之前在学习新框架的时候,只是简单的跟着教程一步一步的敲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;
}
下面来个对比:
vue的v-if
自己实现的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更加深入了解以后,再进行尝试吧。