博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
BiuJS[v1.0]说明文档(3):文本编译
阅读量:7232 次
发布时间:2019-06-29

本文共 4493 字,大约阅读时间需要 14 分钟。

logo

BiuJS

BiuJS是一个轻巧的mvvm框架
它实现了数据的双向绑定
并提供一些基本的指令帮助你提升效率,比如
$for
$model
$if
$click
$style
是的,如你所见,以
$开头的指令是它的独特标识
1000行左右的代码量,让应用的开发和加载biu的一瞬完成
:
https://github.com/veedrin/biu

编译

现在我们来看看视图

let app = new Biu({    mount: '#app',    data: {},    action: {}});

这里传进来的mount是BiuJS的挂载点,它的意思是说:id="app"所在的元素以及后代元素现在被BiuJS接管了

这片区域就是我们要编译的范围

function Compiler(mount, vm) {    this.vm = vm;    if (mount) {        let fragment = this.eleToFragment(mount);        this.compile(fragment);        mount.appendChild(fragment);    }}Compiler.prototype.eleToFragment = function(ele) {    let fragment = document.createDocumentFragment();    let child;    while(child = ele.firstChild) {        fragment.appendChild(child);    }    return fragment;};

要给DOM做手术,我们就要先把它抽出来,暂时装在文档碎片里

编译完以后,再整体的插回原来的位置

胡子模板,也叫文本插值,是作为文本节点,而指令是属于元素节点的,所以我们要分开编译

let regBlank = /^\s+$/;Compiler.prototype.compile = function(ele) {    let self = this;    if (ele.childNodes && ele.childNodes.length) {        Array.from(ele.childNodes).forEach((child) => {            if (child.nodeType === 3 && !regBlank.test(child.textContent)) {                self.compileText(child);            } else if (child.nodeType === 1) {                self.compileElement(child);            }            self.compile(child);        });    }};

我们知道文本节点会把元素之间的空隙也算进去,编译它毫无意义,所以排除掉

编译文本

我们先讲文本编译,指令编译会单独抽几个出来在后面文章讲

好,现在假设我们捕捉到了一个文本节点

跟上面一样,编译就是一个分解组装的过程,所以也要用一个文档碎片缓存起来

有一个知识点,exec方法作用在加了全局匹配修饰符的正则表达式上时,需要多次匹配才能获得所有的结果。正则表达式的lastIndex属性就是用来标记匹配到哪里了

举个例子

let str = 'I am biu, I do biu things, I appreciate biu things.';let reg = /biu/g;// 0console.log(reg.lastIndex);// ["biu", index: 5, input: "I am biu, I do biu things, I appreciate biu things."] 8console.log(reg.exec(str), reg.lastIndex);// ["biu", index: 15, input: "I am biu, I do biu things, I appreciate biu things."] 18console.log(reg.exec(str), reg.lastIndex);// ["biu", index: 40, input: "I am biu, I do biu things, I appreciate biu things."] 43console.log(reg.exec(str), reg.lastIndex);// null 0console.log(reg.exec(str), reg.lastIndex);

一个文本节点里可能有好几个胡子模板

我叫{
{name}},我今年{
{age}}岁了

所以我们要用一个循环把它们全抠出来

let regMustache = /\{\{(.*?)\}\}/g;let content = ele.textContent.trim();let fragment = document.createDocumentFragment();let i = 0;let match;let text;while (match = regMustache.exec(content)) {    if (i < match.index) {        text = content.slice(i, match.index);        let element = document.createTextNode(text);        fragment.appendChild(element);    }    i = regMustache.lastIndex;    let exp = match[1];    let element = document.createTextNode('');    let result = execChain(exp, this.vm);    element.textContent = result;    fragment.appendChild(element);    new Watcher(exp, this.vm, (newValue) => {        element.textContent = newValue;    });}if (i < content.length) {    text = content.slice(i);    let element = document.createTextNode(text);    fragment.appendChild(element);}
  1. 如果match.index不是从0开始的,那就说明前面还有文本是匹配失败了。虽然匹配失败,我们还是要原样把它组装回去吧。这就是第一部分
  2. 第二部分就是匹配成功了,我们要把它其中的表达式抠出来,转成实际的值,再组装回去。如此循环,直到结束
  3. 如果while循环结束之后,match.index比字符串的长度要小,那说明后面还有文本匹配失败了。一样的,原样把它组装回去

经过这三部分,一个文本节点就算编译完成了

表达式求值

因为BiuJS的表达式并不是真正的表达式,它不支持计算(或者暂时不支持)

所以表达式求值实际上指的是:求嵌套对象的属性的值

{
{aa.bb.cc}}或者{
{aa["bb"].cc}}或者{
{aa['bb']['cc']}}
{
{aa + 3}}

先把这些对象或属性名抽取出来,放到一个数组里面

然后再用递归一步一步的向里求值,就可以获得aa.bb.cc的值了

let regChain = /[\[\]\.'"]/;function splitChain(exp) {    let arr = exp.split(regChain);    if (arr.length === 1) {        return arr;    }    let chain = [];    for (let i = 0, len = arr.length; i < len; i++) {        arr[i] && chain.push(arr[i]);    }    return chain;}function execChain(exp, vm) {    let chain = splitChain(exp);    let temp;    function recursion(obj, i) {        let prop = obj[chain[i]];        if (prop !== undefined) {            temp = prop;            i < chain.length && recursion(temp, i + 1);        }    }    recursion(vm.$data, 0);    return temp;}

action方法集合没有嵌套,所以直接取就可以了

这就是表达式转成实际的值的过程

订阅器

上面的文本编译部分有一个new Watcher(),还有印象吗?

这就是传说中的订阅器

这个订阅器的作用是什么呢?其实也很简单

  1. 把模板里抠出来的表达式、BiuJS的实例、还有回调函数打包在一起
  2. 通过触发getter将包裹送到订阅数组里面
function Watcher(exp, vm, cb) {    this.exp = exp;    this.vm = vm;    this.update = cb;    this.trigger();}Watcher.prototype.trigger = function() {    Dep.target = this;    execChain(this.exp, this.vm);    Dep.target = null;};

打包物品,送到目的地,这不就是快递公司么

之前的文章我们还埋了一个点:“订阅者是什么时候挂到Dep.target上的?”

就是在这个时候挂上去的

不过要马上清空,因为它其实挺忙的,要接不少客

写在后面

以上就是文本编译的过程

欢迎到: https://github.com/veedrin/biu了解详情

更欢迎StarFork

转载地址:http://bkvfm.baihongyu.com/

你可能感兴趣的文章
《H3C路由器配置与管理完全手册》(第二版)前言和目录
查看>>
《.NET最佳实践》与Ext JS/Touch的团队开发
查看>>
虚拟磁盘工具vmkfstools的使用
查看>>
职场思想分享005 | 别让背后抱怨说别人坏话成为聊天习惯
查看>>
oracle11gR2 DataGuard switchover切换的两个错误状态解决
查看>>
不登陆数据库执行mysql命令小结
查看>>
SQL Server 2014 许可证(一)版本区别
查看>>
话里话外:成功CEO的用人之道——按需激励
查看>>
使用Visual Studio迁移远程网站到Micorosft Azure
查看>>
Dr.Elephant mysql connection error
查看>>
Tomcat网络输出数据流图
查看>>
Cloudera CDH 离线安装与使用
查看>>
安装 SQL Server 客户端驱动程序
查看>>
<北京青年>--思考
查看>>
Linux Bash Shell高级重定向操作--深入了解标准错误输出和标准输出
查看>>
HP LaserJet Pro P1106网络打印机64位驱动安装
查看>>
JDK和JAXB的对应
查看>>
Numpy快速入门
查看>>
Nginx查看 并发连接数
查看>>
Hyper-V虚拟机快照占用磁盘空间过多,导致虚拟机不能启动怎么办
查看>>