模板编译与优化
背景
HTML、CSS和Javascript是前端的三大基础 React通过jsx把HTML变成直接可以用js描述的结构对象,CSS也被变成可供import的模块,被webpack打包进js中,js一统一天下。
小程序、快应用和我负责研发的Magix,在开发阶段,HTML,CSS和Javascript是分离的文件,通过编译的功能把html和css编译到js文件中,最终和react一样,仅输出js文件
今天就聊聊模板,也就是html被编译到js中时,一些优化方法
模板编译优化
在Magix中,js使用如下的语法占位符表示最终html出现在哪个位置
/*
author:[email protected]
*/
import Magix from 'magix';
export default Magix.View.extend({
tmpl: '@index.html',
assign(data) {
this.set(data);
return true;
},
render() {
this.digest();
}
});
html中我们使用的语法可以参考这里:https://github.com/thx/magix-composer/issues/1
当我们的index.html是静态内容时
<div>
<span>a</span>
b
</div>
编译出的js文件内容如下
let $quick___0_static_node;
Magix.View.extend({
tmpl: ($$, $_create,$_viewId)=> {
let $_temp,$vnode_0=[],
$vnode_1,
$vnode_2,
$text;
if($quick___0_static_node){
$vnode_0.push($quick___0_static_node);
}else{
$vnode_2=[$_create(0,0,'a')];
$vnode_1=[$_create('span',0,$vnode_2),$_create(0,0,'b')];$vnode_0.push($quick___0_static_node=$_create('div',{'_': '_',},$vnode_1));
}
return $_create($_viewId,0,$vnode_0); }
在这份编译结果中,有2个优化点
静态化不变的节点
通过原始模板,我们可以知道这个html在接下来的并不会变化,我们会通过类似$quick___0_static_node在js代码中,反复使用同一个对象来进行运行时的速度提升
给静态节点打上标记
我们可以看到静态div节点被打上{'_': '_',},这样的标记,这样的标记告诉运行时的diff模块,这个节点下的内容不会变化,会加快运行时的diff
当模板是这样时
<div>
<span>a</span>
b
</div>
{{if true}}
<div>
<span>a</span>
b
</div>
{{/if}}
编译出的js结果如下
let $quick___0_static_node;
Magix.View.extend({
tmpl: ($$, $_create,$_viewId)=> {
let $_temp,$vnode_0=[],
$vnode_1,
$vnode_2,
$text,
$vnode_3;
if($quick___0_static_node){
$vnode_0.push($quick___0_static_node);
}else{
$vnode_2=[$_create(0,0,'a')];
$vnode_1=[$_create('span',0,$vnode_2),$_create(0,0,'b')];$vnode_0.push($quick___0_static_node=$_create('div',{'_': '_',},$vnode_1));
}
if(true){
if($quick___0_static_node){
$vnode_1=[$quick___0_static_node];
}else{
$vnode_3=[$_create(0,0,'a')];
$vnode_2=[$_create('span',0,$vnode_3),$_create(0,0,'b')];
$vnode_1=[$quick___0_static_node=$_create('div',{'_': '_',},$vnode_2)];
}
$vnode_0.push(...$vnode_1);
}
return $_create($_viewId,0,$vnode_0); }
编译器会自动识别重复的同样的html片断,最终它们将会使用同一个变量,同一个静态标记,来减少运行时的内容和加速diff
属性静态化
当模板是这样时
<div class="a b" name="div">
{{=name}}
</div>
编译出的js代码如下
let $quick___0_static_attr={'class': 'a b','name': 'div',};
Magix.View.extend({
tmpl: ($$, $_create,$_viewId,$n)=> {
let $_temp,$vnode_0=[],
{
name,}=$$,
$vnode_1,
$text;
$vnode_1=[$_create(0,0,$n(name))];$vnode_0.push($_create('div',$quick___0_static_attr,$vnode_1));
return $_create($_viewId,0,$vnode_0); } ,
我们可以看到html中的属性被编译成对象并以变量提取到最外边,生成重复使用的变量
同时当有相同的静态html属性时,生成的这个对象也只有一份,不会生成值重复的变量
静态节点变量重用
<div a="b">
<div>
<div>bb</div>
</div>
</div>
{{=f}}
<div a="b1">
<div>
<div>bb</div>
</div>
</div>
编译出的js代码如下
if ($quick___0_static_node) {
$vnode_0.push($quick___0_static_node);
}
else {
$vnode_3 = [$_create(0, 0, 'bb')];
$vnode_2 = [$_create('div', 0, $vnode_3)];
$vnode_1 = [$_create('div', 0, $vnode_2)];
$vnode_0.push($quick___0_static_node = $_create('div', { '_': '_', 'a': 'b', }, $vnode_1));
}
$vnode_0.push($_create(0, 0, ($__line = 6, $__art = '{{=f}}', $__ctrl = '<%=f%>', $n(f))));
if ($quick___1_static_node) {
$vnode_0.push($quick___1_static_node);
}
else {
$vnode_3 = [$_create(0, 0, 'bb')];
$vnode_2 = [$_create('div', 0, $vnode_3)];
$vnode_1 = [$_create('div', 0, $vnode_2)];
$vnode_0.push($quick___1_static_node = $_create('div', { '_': 'a', 'a': 'b1', }, $vnode_1));
}
我们会发现不同静态节点下的
<div>
<div>bb</div>
</div>
是一样的,但是编译出的js代码中,却要反复创建相同的节点。
针对这样的情况,我们可以对这样的节点进行变量重复利用,达到提升性能的目的 上述情况优化后生成的js代码如下
if ($quick___0_static_node) {
$vnode_0.push($quick___0_static_node);
}
else {
if ($inline$___) {
$vnode_1 = [$inline$___];
}
else {
if ($inline$__a) {
$vnode_2 = [$inline$__a];
}
else {
$vnode_3 = [$_create(0, 0, 'bb')];
$vnode_2 = [$inline$__a = $_create('div', 0, $vnode_3)];
}
$vnode_1 = [$inline$___ = $_create('div', 0, $vnode_2)];
}
$vnode_0.push($quick___0_static_node = $_create('div', { '_': '_', 'a': 'b', }, $vnode_1));
}
$vnode_0.push($_create(0, 0, ($__line = 6, $__art = '{{=f}}', $__ctrl = '<%=f%>', $n(f))));
if ($quick___1_static_node) {
$vnode_0.push($quick___1_static_node);
}
else {
if ($inline$___) {
$vnode_1 = [$inline$___];
}
else {
if ($inline$__a) {
$vnode_2 = [$inline$__a];
}
else {
$vnode_3 = [$_create(0, 0, 'bb')];
$vnode_2 = [$inline$__a = $_create('div', 0, $vnode_3)];
}
$vnode_1 = [$inline$___ = $_create('div', 0, $vnode_2)];
}
$vnode_0.push($quick___1_static_node = $_create('div', { '_': 'a', 'a': 'b1', }, $vnode_1));
}