xinglie / xinglie.github.io

blog
https://xinglie.github.io
153 stars 22 forks source link

模板编译与优化 #54

Open xinglie opened 4 years ago

xinglie commented 4 years ago

背景

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:xinglie.lkf@alibaba-inc.com
*/
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

xinglie commented 4 years ago

属性静态化

当模板是这样时

<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属性时,生成的这个对象也只有一份,不会生成值重复的变量

xinglie commented 4 years ago

静态节点变量重用

<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));
}