vue数据双向绑定原理,和简单的实现,本文将实现mvvm的模板指令解析器
1)vue数据双向绑定原理-observer
2)vue数据双向绑定原理-wather
3)vue数据双向绑定原理-解析器 complie
vue数据双向绑定原理,和简单的实现,本文将实现mvvm的模板指令解析器
上一步实现了简单数据绑定,最后实现解析器,来解析v-model,v-on:click等指令,和{{}}模板数据。解析器compile实现步骤:
解析模板指令,并替换模板数据,初始化视图
将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器
为了解析模板,首先需要获取到dom元素,然后对含有dom元素上含有指令的节点进行处理,因此这个环节需要对dom操作比较频繁,所有可以先建一个fragment片段,将需要解析的dom节点存入fragment片段里再进行处理:
function node2fragment(el) { var fragment = document.createdocumentfragment(), child; // 将原生节点拷贝到fragment while ((child = el.firstchild)) { fragment.appendchild(child); } return fragment;}
接下来渲染'{{}}'模板
//compilefunction compile(el, vm) { this.$vm = vm; this.$el = this.iselementnode(el) ? el : document.queryselector(el); if (this.$el) { this.$fragment = this.node2fragment(this.$el); this.init(); this.$el.appendchild(this.$fragment); }}compile.prototype = { init: function () { this.compileelement(this.$fragment); }, node2fragment: function (el) { //... }, //编译模板 compileelement: function (el) { var childnodes = el.childnodes, self = this; [].slice.call(childnodes).foreach(function (node) { var text = node.textcontent; var reg = /{{(.*)}}/; //表达式文本 //按元素节点方式编译 if (self.iselementnode(node)) { self.compile(node); } else if (self.istextnode(node) && reg.test(text)) { self.compiletext(node, regexp.$1); } //遍历编译子节点 if (node.childnodes && node.childnodes.length) { self.compileelement(node); } }); }, iselementnode: function (node) { return node.nodetype == 1; }, istextnode: function (node) { return node.nodetype == 3; }, compiletext: function (node, exp) { var self = this; var inittext = this.$vm[exp]; this.updatetext(node, inittext); new watcher(this.$vm, exp, function (value) { self.updatetext(node, value); }); }, updatetext: function (node, value) { node.textcontent = typeof value == "undefined" ? "" : value; },};
处理解析指令对相关指令进行函数绑定。
compile.prototype = { ...... isdirective: function(attr) { return attr.indexof('v-') == 0; }, iseventdirective: function(dir) { return dir.indexof('on:') === 0; }, //处理v-指令 compile: function(node) { var nodeattrs = node.attributes, self = this; [].slice.call(nodeattrs).foreach(function(attr) { // 规定:指令以 v-xxx 命名 // 如 <span v-text="content"></span> 中指令为 v-text var attrname = attr.name; // v-text if (self.isdirective(attrname)) { var exp = attr.value; // content var dir = attrname.substring(2); // text if (self.iseventdirective(dir)) { // 事件指令, 如 v-on:click self.compileevent(node, self.$vm, exp, dir); } else { // 普通指令如:v-model, v-html, 当前只处理v-model self.compilemodel(node, self.$vm, exp, dir); } //处理完毕要干掉 v-on:, v-model 等元素属性 node.removeattribute(attrname) } }); }, compileevent: function(node, vm, exp, dir) { var eventtype = dir.split(':')[1]; var cb = vm.$options.methods && vm.$options.methods[exp]; if (eventtype && cb) { node.addeventlistener(eventtype, cb.bind(vm), false); } }, compilemodel: function(node, vm, exp, dir) { var self = this; var val = this.$vm[exp]; this.updatermodel(node, val); new watcher(this.$vm, exp, function(value) { self.updatermodel(node, value); }); node.addeventlistener('input', function(e) { var newvalue = e.target.value; if (val === newvalue) { return; } self.$vm[exp] = newvalue; val = newvalue; }); }, updatermodel: function(node, value, oldvalue) { node.value = typeof value == 'undefined' ? '' : value; },}
最后再关联起来
function vue(options) { ..... observe(this.data, this); this.$compile = new compile(options.el || document.body, this) return this;}
来尝试下效果
<!--html--><div id="app"> <h2>{{name}}</h2> <input v-model="name" /> <h1>{{name}}</h1> <button v-on:click="test">click here!</button></div><script> new vue({ el: "#app", data: { name: "chuchur", age: 29, }, methods: { test() { this.name = "my name is chuchur"; }, }, });</script>
ok. 基本完善了
推荐学习:vue.js教程
以上就是浅析vue中complie数据双向绑定原理(代码详解)的详细内容。
