你尚未登录,仅允许查看本站部分内容。请登录使用邀请码注册
前端农民工

前后端模板真的可共用么? 6个回答 问答 专栏 @ 探索

前端农民工 发布于 2 年前

很多人都觉得“只要模板引擎在前端和后端都能运行,就能实现前后端模板共用”,进而引出了一些探索“前后端页面渲染”的思路,我感觉这事应该没那么简单吧。。。所以开个问题,看看有没有团队真的实践过所谓的“前后端共用模板”的方案,如果实践过,最好能举例一二。

我觉得可信的情况是整个项目中前后端有那么一小块模板代码或许可共用,但其余大部分模板都应该是不可共用的。之所以觉得前后端模板共用不靠谱,是因为前后端模板根本就是不同的运行和组织方式,其原因主要有2条:

后端模板大多包含了完整的HTML内容,而前端模板只是局部片段。

比如我们有一个list列表,其后端模板的代码为:

<ul id="list">
    {%foreach $list as $item %}
    <li>{%$item.text%}</li>
    {%/foreach%}
</ul>

前端模板则是:

{%foreach $list as $item %}
    <li>{%$item.text%}</li>
{%/foreach%}

也就说,后端模板用于生成完整的html结构,包含了父级的ul,而前端模板一般用于生成组件的内部内容,比如加载更多列表,都是用这个 不含 ul结构的模板渲染得到的,前后端模板包含的内容范围是不同的。

后端模板是靠【占位-替换】拼装页面内容,而前端模板是靠DOM插入。

比如我们要实现一种组件嵌套,后端模板会这么写:

<!-- parent.tpl -->
<div id="parent">{%include file="child.tpl"%}</div>

<!-- child.tpl -->
<div id="child">I'm child</div>

而前端模板是这么组织的:

var parent = '<div id="parent"></div>';
var child = '<div id="child">I'm child</div>';
$('body').html(parent);
$('#parent').html(child);

区别在于,后端模板会在模板代码中写一个占坑标记,然后通过模板变量或者模板函数插入内容实现组装;而前端模板会在模板中使用html属性(id或者class等)来标记元素,元素内容本身是空的,通过浏览器运行时获取dom来组装html。


以上2条是前后端模板最大区别所在,在这两个区别之下,还有所谓的前后端模板共用么?如果还有,它们是怎样的共用?公共代码怎么编写?后端渲染怎么组织模板?前端渲染怎么组织模板?

或许有人会提到React,支持服务端渲染。是的,react有类似的demo,不过据我了解其方法是后端渲染之后要把所有渲染用的模板、渲染过的数据和渲染好的html发送到前端进行反射,从而实现模板复用,React的方案(全页面React构建+虚拟DOM构造)似乎可以避开上述2个问题,但后端渲染的页面内容要包括原模板、原数据和后端渲染的完整HTML,这么大的体积不是很好接受吧

  • nimo

    我们是前后端都用 handlebars http://github.com/zordius/lightncandy

    但并没有大规模的让前后端模板都共用,只是为了让前端书写前后端模板时都使用相同的 handlebars 语法以提高效率。

    举一个前后端公用模板的分页例子:Paging: 基于模板生成 HTML

    {{#_hasPaging}}
        <nav>
          <ul class="pagination">
            <li class="{{^_prevPage}}disabled{{/_prevPage}}">
              <{{#_prevPage}}a{{/_prevPage}}{{^_prevPage}}span{{/_prevPage}} href="{{_link}}{{_prevPage}}" aria-label="Previous">
                <span aria-hidden="true">«</span>
              </{{#_prevPage}}a{{/_prevPage}}{{^_prevPage}}span{{/_prevPage}}>
            </li>
            {{^_isFirstPage}}
            <li>
                <a href="{{_link}}1" class="paging-item">1</a>
            </li>
            {{/_isFirstPage}}
            {{#_hasBeforePages}}
            <li>
                <span>...</span>
            </li>
            {{/_hasBeforePages}}
            {{#_beforePages}}
            <li>
              <a href="{{_link}}{{.}}">{{.}}</a>
            </li>
            {{/_beforePages}}
            <li class="active"><a href="{{_link}}{{_currentPage}}">{{_currentPage}}</a></li>
            {{#_afterPages}}
            <li>
                <a href="{{_link}}{{.}}">{{.}}</a>
            </li>
            {{/_afterPages}}
            {{#_hasAfterPages}}
            <li>
                <span>...</span>
            </li>
            {{/_hasAfterPages}}
            {{^_isLastPage}}
            <li><a href="{{_link}}{{_pageCount}}">{{_pageCount}}</a></li>
            {{/_isLastPage}}
            <li class="{{^_nextPage}}disabled{{/_nextPage}}">
                <{{#_nextPage}}a{{/_nextPage}}{{^_nextPage}}span{{/_nextPage}} aria-label="Next" href="{{_link}}{{_nextPage}}">
                    <span aria-hidden="true">»</span>
                </{{#_nextPage}}a{{/_nextPage}}{{^_nextPage}}span{{/_nextPage}}>
            </li>
          </ul>
        </nav>
    {{/_hasPaging}}
    

    整个项目中前后端有那么一小块模板代码或许可共用,但其余大部分模板都应该是不可共用的。

    支持,其实去年就向 fouber 请教过前后端都使用 handlebars 的问题。目前实践了这么长时间最大的感觉是:模板是否共用不重要,如果能做到前后端模板语法完全一致则可以极大提高开发效率(可以 node + handlebars 快速模拟各种后端页面渲染)。


    我会复用模板代码片段,但绝对不会复用前后端模板文件的物理位置。因为一个文件同时给前后端用会导致前后端不分离

    回复
  • 碧青_Kwok

    第一个问题,举的例子没有说服力呀,后端所用的模板为什么一定要包含父级容器呢?

    第二个例子也不太恰当,既然说共用模板,而且假定后端模版要占坑的话,那么前端模板也可以用占坑的写法啊。因为共用模版的基础在于模版引擎无论在前端还是后端,输入(数据)、输出(填充后内容)都是一致的,差异化在于拿到输出后怎么处理。
    说回第二个例子的不当之处,后端代码是模版的写法没错,而前端代码演示的完全是拿到输出后的处理方式啊,不带这么比较的哇~

    回复
    • 2 年前,前端农民工 说:

      第一个例子,是想说明前端模板只是后端模板的一个片段区域。后端模板不包含父容器怎么能在后端直接渲染出完整页面呢?为了能实现“后端渲染完整页面”的需求,后端的模板必然是前端模板内容的超集,否则就无法构成完整HTML内容了。

      第二个例子是要说明,前后端模板使用方式上的差异,前端组织模板的时候并不是简单的数据填充,还包括了组件的DOM装载,甚至说更常见的是DOM装载的形式,我举得第二个例子并不是“输出后的方式”,这是在前端使用模板时的常见做法

      这样讨论可能没有太多效果,你可以尝试写一个可能的“前后端模板复用”例子,看看有没有命中我上面说的两个问题

  • hefangshi

    我在这个问题上一直很迟疑,不光是模板使用场景上的问题,不过我觉得这个问题用 widget 这种概念还是可以一试的。

    主要困扰我的是数据上的问题,后端模板的数据由于是在后端执行,可以处理一些敏感数据,这些数据可能并不适合在前端暴露,更进一步即使数据通用的,数据的处理工作的逻辑要想做到前后端复用还会设计到一个依赖的 shim 问题。

    而如果前后端模板的复用基础如果是建立在数据准备逻辑独立的前提下,我认为很难说服产品线去这样做,这种做法得不偿失。

    所以在我的想法中,前后端模板复用的解决方案最后产出的应该是一个使用上有严格的约束和规范的方案,编写起来应该是符合某种范式的才能够保证使用上的高可靠。

    不过这类约束性很强的方案个人不是特别感冒。。

    另外补充一点,我认为前后端模板复用上,MVVM会是一个非常大的助力,也是一个使用上的突破点,即优化首次渲染,并使后续交互渲染技术栈收敛到前端。

    回复
  • imweb.daniel

    强约束下是可行的,DOM Template原则上是可以向通常使用的handlebars之类的template做转换。接下来是DOM和JS的绑定问题。

    回复
  • igin

    共有的话,是不是说明两个不同的地方做了重复的事情?
    模板语法统一确实有利于提高生产力。

    回复
  • wkylin

    Thymeleaf: http://www.thymeleaf.org/
    自然模板引擎,离前端更近,让视图开发协作更简单,与后端开发解耦,性能是它的短板

    回复
    • 2 年前,前端农民工 说:

      请问你用过吗?在真实的高访问量项目中用过吗?

登录后回复,如无账号,请使用邀请码注册