【js】vue 2.5.1 源码学习 (七) 初始化之 initState 响应式系统基本思路

大体思路(六)
本节内容:
一、生命周期的钩子函数的实现
  ==》 callHook(vm , 'beforeCreate')
  beforeCreate 实例创建之后 事件数据还未创建
二、初始化initState
  ==>initState(vm) // 初始化数据
    ==> initProps(vm,opts.props) 待续
    ==> initMethods 待续
    ==> initComputed 待续
    ==> if data initData else { observer(vm._data={},true)}
      initData
        ==> getData(data,vm) 如果是一个函数直接调用这个函数
        ==> data methods props 里面属性名称不能相同
        ==> proxy(vm,data,key) 双向数据绑定的地方 defineProperty(vm,key,sharedProperty)
        ==> sharedProperty // 公共访问器对象
vue.js代码如下
  1 //  大体思路(六)
  2 //  本节内容:
  3 //  生命周期的钩子函数的实现 
  4 //  ==》 callHook(vm , 'befroeCreate')
  5 // beforeCreate   实例创建之后  事件数据还未创建
  6 //   ==>initState(vm) // 初始化数据
  7 //         ==> initProps(vm,opts.props)  待续
  8 //         ==> initMethods 待续
  9 //         ==> initComputed  待续
 10 //         ==> if data initData else  { observer(vm._data={},true)}
 11 //             initData 
 12 //                 ==> getData(data,vm) 如果是一个函数直接调用这个函数
 13 //                 ==> data methods props 里面属性名称不能相同  
 14 //                 ==> proxy(vm,data,key)  双向数据绑定的地方    defineProperty(vm,key,sharedProperty)
 15 //                 ==> sharedProperty   // 公共访问器对象  
 16 // 响应式系统
 17 
 18 
 19 (function(global,factory){
 20     // 兼容 cmd  
 21     typeof exports === 'object'  && module !== 'undefined' ? module.exports = factory():   
 22     // Amd
 23     typeof define  === 'function' && define.amd ?  define(factory) : global.Vue = factory();
 24 })(this,function(){
 25     var uip = 0;
 26     function warn(string){
 27         console.error('Vue Wran:' + string)
 28     }
 29     function warnNonpresent(target,key){
 30         warn('属性方法'+ key + '未在实例对象上定义,渲染功能正在尝试访问这个不存在的属性!')
 31     }
 32     function resolveConstructorOptions(Con){
 33         var options = Con.options;
 34         // 判断是否为vm的实例 或者是子类
 35           return options
 36     }
 37     var hasOwnPropeerty = Object.prototype.hasOwnProperty
 38     function hasOwn(obj , key){
 39         return hasOwnPropeerty.call(obj,key)
 40     }
 41     function makeMap(str, expectsLoweraseC){
 42         if(expectsLoweraseC){
 43             str = str.toLowerCase()
 44         }
 45         var map = Object.create(null)
 46         var list = str.split(',')
 47         for(var i = 0 ; i < list.length; i++){
 48             map[list[i]] = true
 49         }
 50         return function(key){
 51             return map[key]
 52         
 53         }
 54     }
 55     var  isbuiltInTag = makeMap('slot,component',true)
 56     var isHTMLTag = makeMap(
 57         'html,body,base,head,link,meta,style,title,' +
 58         'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
 59         'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
 60         'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
 61         's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
 62         'embed,object,param,source,canvas,script,noscript,del,ins,' +
 63         'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
 64         'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
 65         'output,progress,select,textarea,' +
 66         'details,dialog,menu,menuitem,summary,' +
 67         'content,element,shadow,template,blockquote,iframe,tfoot'
 68     );
 69     var isSVG = makeMap(
 70         'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
 71         'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
 72         'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
 73         true
 74     );
 75     var ASSET_TYPES = [
 76         'component',
 77         'directive',
 78         'filter'
 79     ];
 80 
 81     var LIFECYCLE_HOOKS = [
 82         'beforeCreate',
 83         'created',
 84         'beforeMount',
 85         'mounted',
 86         'beforeUpdate',
 87         'updated',
 88         'beforeDestroy',
 89         'destroyed',
 90         'activated',
 91         'deactivated',
 92         'errorCaptured'
 93     ];
 94     var noop = function (){}
 95     var isReservedTag = function(key){
 96         return isHTMLTag(key) || isSVG(key) 
 97     }
 98     // 检测data的属性名称是否为 _ 或者$ 开始的,这些的话是vue的其他属性的值。
 99     function isReserved(key) {
100         var c = key.charCodeAt(0)
101         return c === 0x24 || c === 0x5F
102     }
103     function validataComponentName(key){
104         //检测component 的自定义名称是否合格 
105         // 只能是字母开头或下划线,必须是字母开头
106         if(!(/^[a-zA-Z][w-]*$/g.test(key))){
107            warn('组件的名称必须是字母或中横线,必须由字母开头')
108         }
109         // 1. 不能为内置对象,2.不能是html ,和avg的内部标签
110         if( isbuiltInTag(key) || isReservedTag(key)){
111             warn('不能为html标签或者avg的内部标签')
112         } 
113     }
114     function checkComonpents(child){
115         for(var key in child.components){
116             validataComponentName(key)
117         }
118     }
119    // 配置对象
120     var config = {
121         // 自定义的策略
122         optionMergeStrategies:{}
123     }
124     var strats = config.optionMergeStrategies
125     strats.el = function(parent,child , key , vm){
126        
127           if(!vm){
128               warn('选项'+key+'只能在vue实例用使用')
129           }
130           return defaultStrat(parent,child , key , vm)
131     }
132     function mergeData(to,form){
133         // 终极合并
134         if(!form){
135             return to
136         }
137         // 具体合并。  
138     }
139     function mergeDataorFn(parentVal,childVal,vm){
140         // 合并 parentVal childVal  都是函数
141         if(!vm){
142             if(!childVal){
143                 return parentVal
144             }
145             if(!parentVal){
146                 return childVal
147             }
148            return  function mergeDataFn(parentVal,childVal,vm){//只是一个函数   什么样的情况下调用 加入响应式系统 
149                // 合并子组件对应的data 和   父组件对应的data
150                return mergeData( 
151                    typeof parentVal === 'function' ? parentVal.call(this,this) : parentVal,    // -----忘记写
152                    typeof childVal === 'function' ? childVal.call(this,this): childVal)      // -----忘记写
153            }
154         }else{ // vue实例
155             return function mergeInstanceDataFn(){//只是一个函数   什么样的情况下调用 加入响应式系统 
156                 var InstanceData = typeof childVal === 'function' ? childVal.call(vm,vm): childVal;   // -----忘记写
157                 var defaultData = typeof parentVal === 'function' ? parentVal.call(vm,vm): parentVal;   // -----忘记写
158                 if(InstanceData){
159                     return mergeData(InstanceData,defaultData)
160                 }else{                // -----忘记写
161                     return defaultData
162                 }
163                 
164             }
165         }
166     }
167     strats.data = function(parent,child , key , vm){
168         if(!vm){
169           // console.log(typeof child === 'function')
170             if(child && !(typeof child === 'function')){
171                 warn('data必须返回是一个function')
172             }
173            return mergeDataorFn(parent,child)
174         }
175         return mergeDataorFn(parent,child,vm)
176     }
177     // 生命周期策略的合并,值等于一个function 如果是有两个,放到一个数组里面。
178     function mergeHook(parentVal,childVal,key,vm){
179         // console.log(key)
180         // console.log(parentVal.concat(childVal) )
181         return childVal ?  parentVal ? parentVal.concat(childVal):
182                 Array.isArray(childVal) ?  childVal : [childVal] : parentVal
183     }
184     LIFECYCLE_HOOKS.forEach(function(key){
185         strats[key] = mergeHook
186     });
187     // 检测是否为object
188     function isPlainObject(obj){
189         return Object.prototype.toString.call(obj) === '[object Object]'
190     }
191     function assetObjectType(obj){
192         if(!isPlainObject(obj)){
193             warn('选项的值'+obj+'无效:必须是一个对象的')
194         }
195     }
196     // 对parent实现链式调用。
197     function extend(to,form){
198         for(key in form){
199 
200             to[key] = form[key]
201         }
202         return to
203     }
204     // 实现Assets 的策略合并  conmponents  filter  diretive   
205     function mergeAssets(parentVal,childVal,key,vm){
206         var parent = Object.create(parentVal || null)   // 保证子类的每个值的指向都是一个新的object。否则回出现相互引用的现象。
207         if(childVal){
208             assetObjectType(childVal)
209             return extend(parent,childVal)
210         }    
211         return parent
212     }
213     ASSET_TYPES.forEach(function(key){
214         strats[key+'s'] = mergeAssets
215     })
216     // 实现watch的策略和并,将相同的属性放到一个数组里面。
217     strats.watch = function(parentVal,childVal , key , vm){
218         if(!childVal){
219             return Object.create(parentVal)
220         }
221         var res = {}
222         res =  extend(res,parentVal) 
223         for(var key in childVal){
224             var parent = res[key]
225             var child  = childVal[key]
226             res[key] = parent ? Array.isArray(parent) ? parent.concat(child) : [parent].concat(child) : 
227             Array.isArray(child) ? child : [child] ;
228          }
229         return res
230     }
231     // 实现props指令的合并策略
232     strats.props = function(parentVal,childVal , key , vm){
233         if(!childVal){
234             return parentVal
235         }
236         var res = Object.create( null)
237         extend(res,parentVal)
238         if(childVal){
239              extend(res,childVal)
240         }
241         return res
242     }
243     function defaultStrat(parent,child , key , vm){
244         return child === undefined ? parent :child ;
245     }
246     var cmalizeRE = /-(w)/g
247     function camelize(val){
248         return val.replace(cmalizeRE,function(c,m){
249             return m ? m.toUpperCase(): ""
250         })   
251     }
252     function normalizeProps(options){
253         var props = options.props
254         if(!props){
255             return 
256         }
257         var i , val , name
258         var res = {}
259         if(Array.isArray(props)){
260             i = props.length
261             while(i--){
262                 val = props[i]
263                 if(toString.call(val) === '[object String]'){
264                     name  = camelize(val)
265                     res[name] =  {type: null}
266                 }else{
267                     warn('使用数组愈发时props成员' +val+ '必须时一个数组')
268                 }
269                 
270 
271             }
272         }else if(isPlainObject(props)){
273             for(var key in props){
274                 val = props[key]
275                 name = camelize(key)
276                 res[name] = isPlainObject(val) ? val : {type: val}
277             }
278         }else {
279             warn('选项props的值必须是一个对象或者是数组')
280         }
281         options.props = res
282     }
283     function mormalizeDirectives(options){
284         var dir = options.directives
285         var res = {}
286         if(!dir){
287             return 
288         }
289         if(dir){
290             for(var key in dir){
291                 var val = dir[key]
292                 var name = camelize(key)
293                 if(isPlainObject(val)){
294                     res[name] = val
295                 }
296                 if(toString.call(val) === '[object Function]'){
297                     res[name] = {
298                         bind: val,
299                         upata: val
300                     }
301                 }
302             }
303         }
304         options.directives = res
305 
306     }
307     function mergeOptions(parent,child,vm){
308         var options = {}
309         // 检测是component 是否是合法的  
310         checkComonpents(child)
311         // 规范props 
312         normalizeProps(child)
313         // 规范 dirctives 
314         mormalizeDirectives(child)
315         
316         // console.log(parent, child)
317         for(key in parent){
318             magerField(key)
319         }
320         for(key in child){
321             if(!hasOwn(parent ,key)){  // parent 中循环过地方不进行循环
322                 magerField(key)  // ----忘记写
323             }
324               
325         }
326         // 默认合并策略
327         function magerField(key){  
328             // 自定义策略  默认策略 
329             // console.log(key)
330             var result = strats[key] || defaultStrat        // ---忘记写
331             options[key] = result(parent[key],child[key] , key , vm)
332         }
333         // console.log(options)
334         return options
335     }
336    
337     var allowedGlobals = makeMap(
338         'Infinity,undefined,NaN,isFinite,isNaN,' +
339         'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
340         'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
341         'require' // for Webpack/Browserify
342     );
343     function isNative(Ctor){
344         return typeof Ctor !== undefined && /native code/.test(toString.call(Ctor))
345     }
346     var hasproxy = typeof Proxy !== undefined && isNative(Proxy)
347     var hasHeadler = {
348         has: function(target,key){
349             var val = key in target
350             // key 是否是全局对象 或者内置方法 _
351             var isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_')
352             if(!val && !isAllowed){
353                 warnNonpresent(target,key)
354             }
355             return val || !isAllowed 
356         }
357     }
358     var getHeadler = {
359         get: function(target,key){
360             if( typeof key === 'string'  && !(key in target)){
361                 warnNonpresent(target,key)
362             }
363             return target[key]
364         }
365     }
366 
367     // 数据代理
368     function initProxy(vm){
369         var options = vm.$options
370         // 判断是否是es6 是否存在Proxy
371         if(hasproxy){
372             // 渲染函数拦截那些操作。 1. has 查询 2. get 或者
373             var headler = options.render && options.render._withStripeed ? 
374             getHeadler:
375             hasHeadler;
376             vm._renderPorxy= new proxy(vm,headler)
377         }else{
378             // 如果不支es6  Proxy
379             vm._renderPorxy = vm
380         }
381     }
382 
383     
384      // 初始化当前实例的$children 和$parent 的指向
385     function initLifeCycle(vm){
386         var options = vm.$options
387         // 当前组件 父实例 
388         var parent  = options.parent  // 组件的实例对象
389         // 是否抽象组件 
390         if(parent && !parent.abstrat){
391             while(parent.$options.abstrat && parent.$parent){
392                parent = parent.$options.parent
393             }
394             parent.$children.push(vm)
395         }
396         vm.$parent = parent
397         vm.$root = parent ? parent.$root : vm;
398 
399         vm.$children = [];
400         vm.$refs = {};
401 
402         vm._watcher = null;
403         vm._inactive = null;
404         vm._directInactive = false;
405         vm._isMounted = false; // 是否挂载 
406         vm._isDestroyed = false;  // 是否销毁
407         vm._isBeingDestroyed = false; // 是否正在销毁
408 
409     }
410     function callHook(vm,hook){
411         var options = vm.$options
412         var obj = options[hook]
413         if(obj){
414             for(var i =0 ; i < obj.length ; i++){
415                 obj[i].call(vm)
416             }
417         }
418         
419     }
420     function getData(data,vm){
421         return  data.call(vm,vm)
422     }
423   
424     //共享的访问器对象
425     var sharedProperty = {
426         enumerable:true,
427         configurable:true,
428         get:noop,
429         set:noop
430     };
431     function proxy(vm,data,key){
432        
433         sharedProperty.get = function(){
434             console.log("我监听到你访问了我")
435                return this[data][key]
436             },
437         sharedProperty.set =function(newVal){
438             console.log("我设置了data的值"+key+"==" +newVal)
439             this[data][key] = newVal
440              
441         }
442         Object.defineProperty(vm,key,sharedProperty)
443     }
444     function initData(vm){
445         var opts = vm.$options
446         var data = vm.$options.data
447         // 通过之前strats 里面合成好的数据,data是一个function ,为了独立数据调用的空间。拿到data的值。
448         data = vm._data =  typeof data === 'function' ? getData(data,vm) : data || {}
449         if(!isPlainObject(data)){
450             data = {}
451             warn('data选项应该是object对象')
452         }
453         // data methods props 里面属性名称不能相同
454         var props = opts.props
455         var methods = opts.methods
456         for(let key in data){
457             if(props && hasOwn(props,key)){
458                 warn("props " + key + "选项已经定义为data的属性.")
459             }else if(methods && hasOwn(methods,key)){
460                 warn("methods: " + key + "选项已经定义为data的属性.")
461             }else if(!isReserved(key)){
462                 proxy(vm,"_data",key)
463             }
464             
465         }
466         
467     }
468     function observer(){
469         // 相应试系统
470     }
471     function initState(vm){
472         var opts = vm.$options
473         // 初始化props
474         if(opts.props){
475             initProps(vm,opts.props)
476         }
477         // 初始化methods
478         if(opts.methods){
479             initMethods(vm,opts.methods)
480         }
481         // 初始化computed
482         if(opts.computed){
483             initComputed(vm,opts.computed)
484         }
485         // 初始化data 如果存在就initData
486         if(opts.data ){
487             initData(vm)
488         }else{
489             // 放在响应式系统里面
490             observer(vm._data={},true)
491         }
492     }
493     function initMinxin(options){
494         Vue.prototype._init = function(options){
495             var vm = this 
496             // 记录生成的vue实例对象 
497             vm._uip =  uip++ //   //-------忘记写
498             //合并选项
499             vm.$options =mergeOptions(resolveConstructorOptions(vm.constructor),options,vm)
500              // // 初始化数值代理
501             initProxy(vm)
502             // 初始化当前实例的$children 和$parent 的指向
503             initLifeCycle(vm)
504             // 调用beforeCreate 的钩子函数
505             callHook(vm, 'beforeCreate')
506             // 初始化数据
507             initState(vm)
508         }
509     }
510     function Vue(options){
511         // 安全机制
512         if(!(this instanceof Vue)){     //-------忘记写
513             warn('Vue是一个构造函数,必须是由new关键字调用')  
514         }
515         this._init(options)
516     }
517     initMinxin()  //  初始化选项1: 规范 2: 合并策略。
518     Vue.options = {
519         components: {
520             transtions: {},
521             keepAlive:{},
522             solt:{},
523             transtionsGroup:{}
524         },
525         directives:{},
526         _bash: Vue
527     }
528     function initExend(Vue){
529         Vue.extend = function(extendOptions){
530             extendOptions = extendOptions || {}   // -----忘记写
531             var Super = this 
532             var Child = function VueComponent(options) {
533                 this._init(options)
534             }
535             Child.prototype = Object.create(Super.prototype)
536             Child.prototype.constructor = Child   // 改变constructor 的指向
537             Child.options = mergeOptions(Super.options,extendOptions)
538             // 子类继承父类的静态方法。
539             Child.extend = Vue.extend
540             // console.log(new Child({}))
541             return Child
542         }
543     }
544     initExend(Vue)
545     return Vue
546 })
View Code

dome.html 代码如下

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 7     <title>第五课</title>
 8 </head>
 9 <body>
10     <div id="app">
11         <huml></huml>
12     </div>
13     <script src="vue.js"></script>
14     <!-- <script src="vue2.5.1.js"></script> -->
15     <script type="text/javascript">
16         var componentA = {
17             el: "#app"
18         }
19         var vm = new Vue({
20             el:"#app",
21             data: {
22                 message: "hello Vue",
23                 key: "wodow",
24                 test: 1
25             },
26             beforeCreate: function(){
27                 console.log('我钩子函数beforeCreate')
28             },
29             components:{
30                 humle: componentA
31             }
32             
33         })
34        
35         vm.test = 2;
36         console.log(vm)
37     </script>
38 </body>
39 </html>
View Code
原文地址:https://www.cnblogs.com/yeujuan/p/10998577.html