vue匹配模板简单实现

之前在学习新框架的时候,只是简单的跟着教程一步一步的敲demo,从来没有想过这个框架是如何实现的,简单的功能我能不能实现。最近打算开始学习Vue,觉得还是尝试一下能不能自己简单的实现其中的某些功能吧。

感觉模板引擎后端同学用的多一些,像jade、FreeMarker等,这个通常是在客户端发出HTTP请求之后,应用层的控制器(Controller)接收,通过应用层的服务器(service)访问数据库,然后封装到模型层(Model),再跳转到视图层(View),通过模板引擎生成HTML代码,最后返回给客户端浏览器。

但是Vuejs也是有模板的,他的模板引擎是在客户端浏览器的内存中处理生成的。所以我们用模板的时候,显示出来的是要渲染的数据。

Vue官网的第一个demo

<div id="app">
    {{ message }}
</div>
var app = new Vue({
    el: '#app',
    data: {
        message: 'Hello Vue!'
    }
});

看到这个部分,感觉应该可以通过正则来简单的实现一下:
html部分不变。

正则部分:

首先想到的是匹配两边的大括号,/\{\{\}\}/,注意转译;
然后我们需要匹配大括号中间的内容,[\w]+;
这里需要注意一个问题,在匹配的内容前后可能会有空格,所以这里内容部分我们需要修改成\s*[\w]+\s*;
但是如果需要替换的话,使用正则的分组用法$1会比较轻松的解决这个问题;
所以正则的最终版就变成了/(\{\{\s*([\w]+)\s*\}\})/g;

js部分:
function vue(options) {
    var context = document.querySelector(options.el);
    var html = context.innerHTML;
    var regex = /(\{\{\s*([\w]+)\s*\}\})/g;
    while(regex.test(html)) {
        html = html.replace(RegExp.$1, options.data[RegExp.$2]);
    }
    context.innerHTML = html;
}

$1是最先匹配到的{% raw %}{{}}{% endraw %}
包括其内的部分,$2是匹配的要替换的对象。
使用方法跟vue的使用方法一样。
这样第一个demo我们就简单的实现了。当然相比vue有非常多的缺陷,比如不可以通过app.message来修改数据…

第二个demo,匹配v-bind:

<div id="app-2">
    <span v-bind:title="message">
        Hover your mouse over me for a few seconds
        to see my dynamically bound title!
    </span>
</div>
var app2 = new Vue({
    el: '#app-2',
    data: {
        message: 'You loaded this page on ' + new Date()
    }
})
正则部分

第二个demo换成了匹配v-bind:,所以正则就变成了
/(v\-bind:([\w]+)=\"([\w]+)\")/g;

js部分
function Vue(options) {
    var context = document.querySelector(options.el),
        html = context.innerHTML,
        regex = /(v\-bind:([\w]+)=\"([\w]+)\")/g;
    while(regex.exec(html)) {
        html = html.replace(RegExp.$1,RegExp.$2+"='"+options.data[RegExp.$3]+"'");
    }
    context.innerHTML = html;
}

第三个demo,匹配v-if

<div id="app-3">
    <p v-if="seen">Now you see me</p>
</div>
var app3 = new Vue({
    el: '#app-3',
    data: {
        seen: true
    }
})
正则部分

替换v-if相对于v-bind:这个还简单一些:
/(v\-if=\"([\w]+)\")/g;

js部分
function Vue(options) {
    var context = document.querySelector(options.el),
        html = context.innerHTML,
        regex = /(v\-if=\"([\w]+)\")/g;
    while(regex.exec(html)) {
        html = html.replace(RegExp.$1,options.data[RegExp.$2] ? 'style="display:block"' : 'style="display:none"');
    }
    context.innerHTML = html;
}

下面来个对比:
v-if
vue的v-if
my-vue-show
自己实现的v-if
在显示效果上虽然是一样的,如果v-if中的布尔值为false时,在界面上都不会显示出来。但是,我们打开控制台,查看demo结构我们可以发现,vue实现的v-if,如果布尔值为false时,是直接将这个元素移除DOM树(DOMElement.remove());而我们自己实现的是将这个元素的display属性设为none。这个效果实际上跟vue的v-show是一样的。

最后将这三个demo合并起来

function Vue(options) {
    var context = document.querySelector(options.el),
        html = context.innerHTML,
        regex = /(\{\{\s*([\w]+)\s*\}\})/g;
    while(regex.exec(html)) {
        html = html.replace(RegExp.$1, options.data[RegExp.$2]);
    }
    regex = /(v\-bind:([\w]+)=\"([\w]+)\")/g;
    while(regex.exec(html)) {
        html = html.replace(RegExp.$1,RegExp.$2+"='"+options.data[RegExp.$3]+"'");
    }
    regex = /(v\-if=\"([\w]+)\")/g;
    while(regex.exec(html)) {
        html = html.replace(RegExp.$1,options.data[RegExp.$2] ? 'style="display:block"' : 'style="display:none"');
    }
    context.innerHTML = html;
}

虽然实现了这几个效果,但是这个跟vue的实现方式是不一样的。比如之后的双向绑定,如果继续通过上面的代码,可能就会出现一些问题,或者实现起来会比较乱。这个还需要对vue更加深入了解以后,再进行尝试吧。

附:
vue官网
正则表达式-语法
在线正则表达式测试

发表评论

邮箱地址不会被公开。 必填项已用*标注