技术实现详解
magix-combine工具主要针对magix文件做处理,通常一个完整的view由js、html、css组成。
所以插件目录下以js打头的处理js文件,以tmpl打头的处理html文件,以css打头的处理css文件
而html和css又依附于js文件存在,所以统一入口在js.js插件里
我们用以下简单的模板进行解释,主要用到的是magix-combine中的tmpl模块
在tmpl.js中可以看到具体的每一步的处理流程,接下来会说到重点步骤
当我们拿到这样的模板后,除了处理html注释,就是识别html中的js代码了,即模板命令(控制)语句
这块主要由tmpl-vars.js处理
在tmpl-vars.js中,我们要把前面的模板字符串变成合法的js语句,使用的是es6中的标签模板,即上述模板经简单处理后变成如下的字符串
`
加上反撇号(`),这一步使用正则即可完成。
变成上面的字符串后,它本身就是合法的js代码了。我们为了识别局部变量和全局变量,使用acorn这个工具进行语法分析
然后对语法树遍历Identifier和VariableDeclarator,找到变量使用和变量声明的地方,这时候要自己做一下处理。因为从外到内,从上到下遍历语法树,如果当前出现的变量未进行声明,则认为是全局变量,反之则认为局部变量
我们为了后续的其它分析,在这个tmpl-vars.js中做了入侵修改,即识别完后我们对全局变量和局部变量做了“手脚“,我们在全局变量前增加了 \u0003. 这2个字符,在使用变量的地方增加了\u0001这个字符,在变量声明的地方增加了 \u0002 这个字符。这样在其它插件中做其它分析时会比较方便。
处理后即为
`
这时候已经不是合法的js代码了,不过这已经不重要了。我们再把反撇号(`)去掉,则最终为
至此tmpl-vars的任务就完成了。
接下来为了要分析上面的模板,我们要把它变成合法的html标签,所以我们使用了另外一个模块tmpl-cmd.js。对模板命令进行移除,移除后的结果为
&0
&1
&2
我们把命令移除,同时使用特殊的占位符占位,因为我们还要把命令还原回去,所以我们还要知道每个占位符对应的模板命令。我们用一个对象来记录每个占位符对应的原始模板内容
此时用于记录占位符对应模板命令的对象如下
{
'&0':'',
'&1':'',
'&2':''
}
这个时候剩余的html就是合法的html了。
接下来把这段html交给tmpl-guid.js模块进行添加guid的操作。
进入tmpl-guid模块后,上来不管三七二十一,先给每个节点都添加一个guid,当然除了特别的节点如:没有模板命令的自闭合标签
添加完guid后变成为:
&0
&1
&2
然后再次遍历,对不符合要求的guid进行移除。
移除保留规则如下:
1.如果移除子节点后,属性和剩余的内容中不存在模板命令,则移除guid
2.如果剩余内容+属性中的模板命令,变量声明和变量使用不配对,则删除guid
3.如果剩余内容+属性中的模板命令,变量声明和使用配对,则保留guid
即上述带guid的html第一次被处理时
对外层的div移除配对的子标签后(为什么要先移除子标签?因为我们要把局部刷新做到最近的节点上),在本例中把子标签span移除
&0
&2
这时候我们把这个html片断还原模板命令语句,即把&0和&2还原回旧样子
然后对这段代码进行变量声明和变量使用分析。因为每个变量前都有特殊的前缀,所以我们可以很方便的用正则识别出来:
在当前范围内声明的变量有 i 使用的局部变量有 i,当然还有一个全局变量list,全局变量我们不用管它,因为它在整个模板中都可以访问到。
我们可以看出声明和使用的变量是配对的。即:没有出现使用的局部变量未声明的情况
那么这个div标签的guid则可以保留
然后再分析子标签,这时候进行第二次的处理
子标签为
&1
同样命令还原
同样变量识别,我们发现当前范围内使用到了局部变量i,但在当前范围内并未找到声明它的地方,则说明这段html不能被单独存在,只能做为其它标签的一部分。所以span上的guid要移除。
经tmpl-guid.js处理后,最终添加的guid如下
&0
&1
&2
在tmpl-guid.js的最后,会对存在模板命令的对象进行处理,删除其中的\u0002、\u0001占位符,最终存放模板命令的对象中存放的结果如下
{
'&0':'',
'&1':'',
'&2':''
}
这时候就进入tmpl-partial.js进行子模板分析了。
经过了tmpl-guid的打guid标识后,到tmpl-partial只需要识别带mx-guid的标签即可。
所到在tmpl-partial进行子模板拆分分析时,只需要匹配带mx-guid的标签即可,因为只有这些节点才能做为局部刷新的容器节点,识别出相应的标签和内容,再从模板命令中提取出根节点对应的数据key,即完成了整个识别内容
当然在这一步还要考虑嵌套刷新等细节,比如
当数据x,y都发生改变时,mx-guid="g1"的不需要更新,因为x的变化会让g1变到最新
当y变化时,只需要更新g1即可
细节处理
关于\u0003在tmpl-partial恢复命令的时候被替换成了$,是因为我们改造了模板引擎,去掉了with语句,这个$就是变量的根节点
关于以下代码
这段代码从div的角度看,确实变量声明和使用都是配对的,但也会被移除mx-guid,凡是不带mx-guid标识的节点都不会被局部刷新。局部刷新的首要条件是当前代码片断中要包含全局变量
再比如这段代码
这段代码只有span会被识别为局部刷新
再比如这样的情况
外层缺少包括的标签,这种情况magix-combine会自动添加一个div,详见tmpl-guid.js插件的实现