模板引擎不属于特定技术领域,是为了使用户界面与业务数据(内容)分离而产生的,是跨领域跨平台的概念。严格的模板引擎的定义是,输入模板字符串+数据,得到渲染过的字符串(页面)。
后端服务器的资源是有限的,并且对数据的处理是随着用户数量的增加而叠加的,用户的每一次操作,页面渲染都是在消耗服务器资源,少量的用户操作或许不会导致服务器卡顿,但是当出现成千上万甚至更多的用户时,可能仅是网络请求就会让服务器无响应甚至宕机(参照春运)。而如果将页面的渲染放在用户端(前端),用户只有一个,几十毫秒的渲染时间跟请求延迟比起来根本算不上瓶颈,所以既可以提高用户的体验,同时也减轻了服务器的压力。
通常我们将渲染方法设计为render(),参数就是模板路径和数据。render()我们可以将其看成是一个约定接口,接受相同参数,最后返回HTML片段。这样的方法我们都视作实现了这个接口。
如:
形成模板技术的也就如下4个要素。
❑ 模板语言。
❑ 包含模板语言的模板文件。
❑ 拥有动态数据的数据对象。
❑ 模板引擎。
实现上,从正则替换的方式到拼写字符串直接输入,再到AST解析,存在各种输出页面内容的方式,但从定义上来说都是差不多的。
前端模板引擎分为两种,基于字符串的模板引擎和基于JavaScript的模板引擎。
模板引擎主要有以下几个步骤。
❑ 语法分解。提取出普通字符串和表达式,这个过程通常用正则表达式匹配出来
❑ 处理表达式。将标签表达式转换成普通的语言表达式。
❑ 生成待执行的语句。
❑ 与数据一起执行,生成最终字符串。
基于字符串的模板引擎
就是通过字符串替换的方式,来渲染出HTML,再将HTML插入DOM节点中,其代表性框架有Mustache和Handlebars.js。
从软件架构上来看,Mustache提供了多语言的版本,即官方提供了诸如Python、Java、Ruby等语言的模板引擎。因为前后台共用这些模板,它可以使得后台渲染变得相当容易。只需要读取对应的模板文件,就可以将Model填充到数据中。
其基本原理是,全局正则匹配模板关键字,再从传入的data中找到是否有对应的值,如data中存在对象,则使用该值进行替换,否则该值为空。
基于字符串的模板引擎在更新DOM的时候会更新所有DOM节点,这时浏览器需要重新渲染所有的节点。当我们拥有大量的HTML元素或DOM操作时,如果每次都刷新整个DOM节点,显然是不合适的。
基于JavaScript的模板引擎
基于JavaScript的模板引擎的表现形式是,将模板转义为JavaScript,在执行的过程中,再动态更新所需要的DOM节点。相应的逻辑如下:
(1)将模板编译为某种DSL(领域特别语言),比如HyperScript或者JavaScript对象(代码+数据),创建虚拟的树(AST树)。
(2)在使用时,通过这个虚拟树来创建一个DOM节点。
(3)(可选)当发生变更时,通过DOM Diff算法来替换对应的修改结点。
它和基于字符串的模板引擎的区别主要在于第三点,是否全局替换DOM节点。比如Virtual DOM,就是带一个Dom Diff的JavaScript模板引擎,在进行大量DOM操作的时候,由于只对变化的DOM进行替换,可以提高前端应用的性能。
Vue的render实现
template 模板 通过 Compile 编译 得到 render函数。
compile 编译可以分成 parse、optimize 与 generate 三个阶段,最终需要得到 render function。
其实就是Vnode+data
1 | <div id=”app“> |
_c即$createElement 的实现(如何创建Vnode):
vue的整个实现流程简易说明
第一步:解析模板成 render 函数
第二步:响应式开始监听
第三步:首次渲染,显示页面,且绑定依赖
第四步:data 属性变化,触发 rerender
把模板解析为 render 函数
运用 with,模板中的所有信息都被 render 函数包含;模板中用到的 data 中的属性,都变成了 JS 变量;模板中的 v-model v-for v-on 都变成了 JS 逻辑;render 函数返回 vnode
响应式开始监听
Object.defineProperty
将data 的属性代理到 vm 上首次渲染,显示页面且绑定依赖
初次渲染,执行
updateComponent
, 执行vm._render()
;执行 render 函数,会访问到 data 下的数据,被响应式的get
方法监听到;执行 updateComponent,会走到 vdom 的 patch 方法;patch 将 vnode 渲染成 DOM,初次渲染完成data 属性变化,触发 rerender
修改属性,被响应式的
set
监听到;set 中执行 updateComponent;updateComponent 重新执行 vm._render();生成的 vnode 和 prevVnode ,通过 patch 进行对比;渲染到 html 中
一个简明的js实现
1 | var TemplateEngine = function(html, options) { |
从编译角度理解v-if和v-show的区别
v-if编译成了三元运算符
同时还可以解释为什么连用时v-for比v-if优先级高。所以最好在外面嵌套一个template,把v-if放在外层。或者用计算属性先过滤
v-show编译成了指令