vue 源码初级学习

// 从Vue的第二个commit来学习数据驱动视图



//Vue里面v-text的实现原理(为了易于理解,以下代码为超简化版)

<div id="test"> <p v-text="date"></p> <p v-text="msg"></p> <span v-text="msg"></span> </div> var bindingMark = "v-text"; //绑定标记 //初始数据 var initData = { date: "2017-05-04", msg: "hello" }; var bindings = {};//内部数据备份 var data = {};//外部数据接口 var root = document.getElementById("test"), //模型根节点 els = root.querySelectorAll("[" + bindingMark + "]");//获取test下 所有的带有 v-test属性的节点 //收集 v-text 里面的值 [].forEach.call(els, function (el) { var variable = el.getAttribute(bindingMark); //获取v-text属性的值 bindings[variable] = {}; }); //数据驱动绑定函数 var bind = function(variable){ bindings[variable].els = root.querySelectorAll('[' + bindingMark + '="' + variable + '"]'); //获取某一 v-text 值的NodeList //删除v-text属性 [].forEach.call(bindings[variable].els, function (e) { e.removeAttribute(bindingMark); }); //添加set get 存取器描述 Object.defineProperty(data, variable, { set: function (newVal) { //数据重新赋值时候 更新dom中相对应的v-text [].forEach.call(bindings[variable].els, function (e) { bindings[variable].value = e.textContent = newVal; }); }, get: function () { return bindings[variable].value; } }) }; //执行绑定 for (var variable in bindings) { bind(variable); } //初始化赋值 if (initData) { for (var variable in initData) { data[variable] = initData[variable]; } } //更新数据函数 var updateData = function(newData){ for(var n in newData){ if(data[n] && (data[n] != newData[n])){ data[n] = newData[n]; } } }; //点击模型元素时 更新数据 然后触发set函数执行,进而驱动视图去改变 root.addEventListener('click', function(){ updateData({ date: "2017-07-02", msg: "world" }); });

本文地址:https://zhuanlan.zhihu.com/p/26744423

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>Title</title>
  6 </head>
  7 <body>
  8 <div id="test">
  9     <p>v-model 绑定的输入框,可同步下面的v-msg文本: <input v-model="msg" placeholder="edit me"></p>
 10     <p>v-msg 文本: <span v-text="msg"></span></p>
 11     <hr>
 12     <p>v-show 显示隐藏 demo: <span v-show="isShow">我是v-show属性绑定元素</span></p>
 13     <p>v-on:click,可以触发上面文本的显示隐藏<a href="javascript:;" v-on:click="changeShow" v-text="btn"></a></p>
 14 </div>
 15 </body>
 16 <script>
 17     /**
 18      * 支持的模板语法
 19      * [Directives description]
 20      * @type {Object}
 21      */
 22     var Directives = {
 23         text: function (el, value) {
 24             el.textContent = value || ''
 25         },
 26         show: function (el, value) {
 27             el.style.display = value ? '' : 'none'
 28         },
 29         on: {
 30             update: function (el, handler, event, directive) {
 31                 if (!directive.handlers) {
 32                     directive.handlers = {}
 33                 }
 34                 var handlers = directive.handlers
 35                 if (handlers[event]) {
 36                     el.removeEventListener(event, handlers[event])
 37                 }
 38                 if (handler) {
 39                     handler = handler.bind(el);
 40                     el.addEventListener(event, handler);
 41                     handlers[event] = handler;
 42                 }
 43             }
 44         },
 45         model: {
 46             bind: function (el, key, directive, seed) {
 47                 el.addEventListener('keyup', function (e) {
 48                     seed.$data[key] = el.value;
 49                 });
 50             },
 51             update: function (el, value) {
 52                 el.value = value;
 53             }
 54         }
 55     }
 56 
 57     /**
 58      * 工具方法
 59      * [Utils description]
 60      * @type {Object}
 61      */
 62     var Utils = {
 63         cloneAttributes: function (attributes) {
 64             return [].map.call(attributes, function (attr) {
 65                 return {
 66                     name: attr.name,
 67                     value: attr.value
 68                 }
 69             })
 70         },
 71         parseDirective: function (attr) {
 72             if (attr.name.indexOf(prefix) === -1) return;
 73 
 74             var noprefix = attr.name.slice(prefix.length + 1),
 75                 argIndex = noprefix.indexOf(':'),
 76                 dirname = argIndex === -1 ? noprefix : noprefix.slice(0, argIndex),
 77                 def = Directives[dirname],
 78                 arg = argIndex === -1 ? null : noprefix.slice(argIndex + 1);
 79 
 80             var exp = attr.value,
 81                 key = exp.trim();
 82 
 83             return def
 84                 ? {
 85                     attr: attr,
 86                     key: key,
 87                     definition: def,
 88                     argument: arg,
 89                     update: typeof def === 'function' ? def : def.update,
 90                     bind: typeof def === 'function' ? null : def.bind ? def.bind : null
 91                 }
 92                 : null;
 93         }
 94     };
 95 
 96     var prefix = 'v',
 97         selector = Object.keys(Directives).map(function (d) {
 98             return '[' + prefix + '-' + d + ']'
 99         }).join();
100 
101     /**
102      * Vue构造函数
103      * [Vue description]
104      * @param {[type]} el   [description]
105      * @param {[type]} opts [description]
106      */
107     function Vue (el, opts) {
108         var self = this,
109             root = self.$el = document.getElementById(el),
110             els  = root.querySelectorAll(selector),
111             _bindings = {};
112 
113         self.$opts = opts || {};
114 
115         self.$data = {}; //对外暴露的数据接口
116 
117         self.processNode(els, _bindings);
118 
119         self.initData(_bindings);
120     }
121 
122     /**
123      * 处理node节点
124      *
125      * [processNode description]
126      * @param  {[type]} els       [description]
127      * @param  {[type]} _bindings [description]
128      * @return {[type]}           [description]
129      */
130     Vue.prototype.processNode = function(els, _bindings){
131         var self = this;
132         [].forEach.call(els, function(el){
133             Utils.cloneAttributes(el.attributes).forEach(function (attr) {
134                 var directive = Utils.parseDirective(attr);
135                 if (directive) {
136                     self.bindDirective(el, _bindings, directive)
137                 }
138             });
139         });
140     }
141 
142     //属性移除 指令绑定
143     Vue.prototype.bindDirective = function(el, _bindings, directive){
144         var self = this;
145 
146         el.removeAttribute(directive.attr.name);
147         var key = directive.key,
148             binding = _bindings[key];
149         if (!binding) {
150             _bindings[key] = binding = {
151                 value: undefined,
152                 directives: []
153             }
154         }
155         directive.el = el;
156         binding.directives.push(directive);
157 
158         if (directive.bind) {
159             directive.bind(el, key, directive, self);
160         }
161         if (!self.$data.hasOwnProperty(key)) {
162             self.bind(key, binding);
163         }
164     }
165 
166     //绑定 赋值拦截 set 方法
167     Vue.prototype.bind = function(key, binding) {
168         var that = this;
169         Object.defineProperty(that.$data, key, {
170             set: function (value) {
171                 binding.value = value;
172 
173                 binding.directives.forEach(function (directive) {
174                     directive.update(
175                         directive.el,
176                         value,
177                         directive.argument,
178                         directive,
179                         self
180                     )
181                 })
182             },
183             get: function () {
184                 return binding.value;
185             }
186         })
187     };
188     //实例初始化 赋值
189     Vue.prototype.initData = function(_bindings) {
190         var self = this;
191         for (var variable in _bindings) {
192             this.$data[variable] = self.$opts[variable];
193         }
194     };
195 
196     /**
197      * 创建vm实例
198      */
199     var vm = new Vue('test', {
200         msg: 'aaa',
201         isShow: true,
202         btn: '点击我',
203         changeShow: function(){
204             vm.$data.isShow = !vm.$data.isShow;
205         }
206     });
207 </script>
208 </html>

 

精品文章

vue学习笔记:http://jiongks.name/blog/vue-code-review/

posted @ 2017-05-11 15:22  _白马非马  阅读(826)  评论(0编辑  收藏  举报