【js】 vue 2.5.1 源码学习(五) props directives规范化 props 合并策略
大体思路 (四)
上节回顾:
A: 对于生命周期函数将父子组件的函数放到一个数组里面,特定时间点调用,保证父子组件函数都调用到。
B: 对于directive,filters,components 等的资源选项,父选项将以原型的形势处理,正式因为这样子,在任何地方都可以用到内部自定义指令。
C: 对于watch选项的合并,类似于生命周期,如果父子组件相同的观测字段,将合并为一个数组。
本节内容:
props normalizeProps normalizeDirective 规范化
props 自定义策略
directives 规范化
normalizeProps(child) //规范props
==》while(i--) 找到每个props的元素。 “使用数组语法时 Props成员必须为字符串
==> camelize 将a-a变为aA 将中横线,转化为小驼峰命名。 每个元素 = {type:null}
==> camelizeRE = /-(\w)/g ;
==> 非数组的情况 循环每个建,转为驼峰, 将值传给res[name] = object
==> else 选项porps必须时一个数组或者对象。
mormalizeDirectives // 规范指令的选项
==> directives: {
test1: {
bind: function(){}
upteat: function(){}
},
test2:function(){}
}
strats.props = 策略合并 ==> 如果都存在,就用child覆盖parent。
vue.js 代码如下
1 // 大体思路 (四) 2 // 上节回顾: 3 // A: 对于生命周期函数将父子组件的函数放到一个数组里面,特定时间点调用,保证父子组件函数都调用到。 4 // B: 对于directive,filters,components 等的资源选项,父选项将以原型的形势处理,正式因为这样子,在任何地方都可以用到内部自定义指令。 5 // C: 对于watch选项的合并,类似于生命周期,如果父子组件相同的观测字段,将合并为一个数组。 6 // 本节内容: 7 // props normalizeProps normalizeDirective 规范化 8 // props 自定义策略 9 // directives 规范化 10 // normalizeProps(child) //规范props 11 // ==》while(i--) 找到每个props的元素。 “使用数组语法时 Props成员必须为字符串 12 // ==> camelize 将a-a变为aA 将中横线,转化为小驼峰命名。 每个元素 = {type:null} 13 // ==> camelizeRE = /-(\w)/g ; 14 // ==> 非数组的情况 循环每个建,转为驼峰, 将值传给res[name] = object 15 // ==> else 选项porps必须时一个数组或者对象。 16 // mormalizeDirectives // 规范指令的选项 17 // ==> directives: { 18 // test1: { 19 // bind: function(){ 20 21 // } 22 // upteat: function(){ 23 24 // } 25 // }, 26 // test2:function(){ 27 28 // } 29 // } 30 // strats.props = 策略合并 31 // 如果都存在,就用child覆盖parent。 32 33 34 35 (function(global,factory){ 36 // 兼容 cmd 37 typeof exports === 'object' && module !== 'undefined' ? module.exports = factory(): 38 // Amd 39 typeof define === 'function' && define.amd ? define(factory) : global.Vue = factory(); 40 })(this,function(){ 41 var uip = 0; 42 function warn(string){ 43 console.error('Vue Wran:' + string) 44 } 45 function resolveConstructorOptions(Con){ 46 var options = Con.options; 47 // 判断是否为vm的实例 或者是子类 48 return options 49 } 50 var hasOwnPropeerty = Object.prototype.hasOwnProperty 51 function hasOwn(obj , key){ 52 return hasOwnPropeerty.call(obj,key) 53 } 54 function makeMap(str, expectsLoweraseC){ 55 if(expectsLoweraseC){ 56 str = str.toLowerCase() 57 } 58 var map = Object.create(null) 59 var list = str.split(',') 60 for(var i = 0 ; i < list.length; i++){ 61 map[list[i]] = true 62 } 63 return function(key){ 64 return map[key] 65 66 } 67 } 68 var isbuiltInTag = makeMap('slot,component',true) 69 var isHTMLTag = makeMap( 70 'html,body,base,head,link,meta,style,title,' + 71 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' + 72 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' + 73 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' + 74 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' + 75 'embed,object,param,source,canvas,script,noscript,del,ins,' + 76 'caption,col,colgroup,table,thead,tbody,td,th,tr,' + 77 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' + 78 'output,progress,select,textarea,' + 79 'details,dialog,menu,menuitem,summary,' + 80 'content,element,shadow,template,blockquote,iframe,tfoot' 81 ); 82 var isSVG = makeMap( 83 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' + 84 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' + 85 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view', 86 true 87 ); 88 var ASSET_TYPES = [ 89 'component', 90 'directive', 91 'filter' 92 ]; 93 94 var LIFECYCLE_HOOKS = [ 95 'beforeCreate', 96 'created', 97 'beforeMount', 98 'mounted', 99 'beforeUpdate', 100 'updated', 101 'beforeDestroy', 102 'destroyed', 103 'activated', 104 'deactivated', 105 'errorCaptured' 106 ]; 107 108 var isReservedTag = function(key){ 109 return isHTMLTag(key) || isSVG(key) 110 } 111 function validataComponentName(key){ 112 //检测component 的自定义名称是否合格 113 // 只能是字母开头或下划线,必须是字母开头 114 if(!(/^[a-zA-Z][\w-]*$/g.test(key))){ 115 warn('组件的名称必须是字母或中横线,必须由字母开头') 116 } 117 // 1. 不能为内置对象,2.不能是html ,和avg的内部标签 118 if( isbuiltInTag(key) || isReservedTag(key)){ 119 warn('不能为html标签或者avg的内部标签') 120 } 121 } 122 function checkComonpents(child){ 123 for(var key in child.components){ 124 validataComponentName(key) 125 } 126 } 127 // 配置对象 128 var config = { 129 // 自定义的策略 130 optionMergeStrategies:{} 131 } 132 var strats = config.optionMergeStrategies 133 strats.el = function(parent,child , key , vm){ 134 135 if(!vm){ 136 warn('选项'+key+'只能在vue实例用使用') 137 } 138 return defaultStrat(parent,child , key , vm) 139 } 140 function mergeData(to,form){ 141 // 终极合并 142 if(!form){ 143 return to 144 } 145 // 具体合并。 146 } 147 function mergeDataorFn(parentVal,childVal,vm){ 148 // 合并 parentVal childVal 都是函数 149 if(!vm){ 150 if(!childVal){ 151 return parentVal 152 } 153 if(!parentVal){ 154 return childVal 155 } 156 return function mergeDataFn(parentVal,childVal,vm){//只是一个函数 什么样的情况下调用 加入响应式系统 157 // 合并子组件对应的data 和 父组件对应的data 158 return mergeData( 159 typeof parentVal === 'function' ? parentVal.call(this,this) : parentVal, // -----忘记写 160 typeof childVal === 'function' ? childVal.call(this,this): childVal) // -----忘记写 161 } 162 }else{ // vue实例 163 return function mergeInstanceDataFn(parentVal,childVal,vm){//只是一个函数 什么样的情况下调用 加入响应式系统 164 var InstanceData = typeof childVal === 'function' ? childVal.call(vm,vm): childVal; // -----忘记写 165 var defaultData = typeof parentVal === 'function' ? parent.call(vm,vm): parentVal; // -----忘记写 166 if(InstanceData){ 167 return mergeData(parentVal,childVal) 168 }else{ // -----忘记写 169 defaultData 170 } 171 172 } 173 } 174 } 175 strats.data = function(parent,child , key , vm){ 176 if(!vm){ 177 // console.log(typeof child === 'function') 178 if(child && !(typeof child === 'function')){ 179 warn('data必须返回是一个function') 180 } 181 return mergeDataorFn(parent,child) 182 } 183 return mergeDataorFn(parent,child,vm) 184 } 185 // 生命周期策略的合并,值等于一个function 如果是有两个,放到一个数组里面。 186 function mergeHook(parentVal,childVal,key,vm){ 187 // console.log(key) 188 // console.log(parentVal.concat(childVal) ) 189 return childVal ? parentVal ? parentVal.concat(childVal): 190 Array.isArray(childVal) ? childVal : [childVal] : parentVal 191 } 192 LIFECYCLE_HOOKS.forEach(function(key){ 193 strats[key] = mergeHook 194 }); 195 // 检测是否为object 196 function isPlainObject(obj){ 197 return Object.prototype.toString.call(obj) === '[object Object]' 198 } 199 function assetObjectType(obj){ 200 if(!isPlainObject(obj)){ 201 marn('选项的值'+obj+'无效:必须是一个对象的') 202 } 203 } 204 // 对parent实现链式调用。 205 function extend(to,form){ 206 for(key in form){ 207 208 to[key] = form[key] 209 } 210 return to 211 } 212 // 实现Assets 的策略合并 conmponents filter diretive 213 function mergeAssets(parentVal,childVal,key,vm){ 214 var parent = Object.create(parentVal || null) // 保证子类的每个值的指向都是一个新的object。否则回出现相互引用的现象。 215 if(childVal){ 216 assetObjectType(childVal) 217 return extend(parent,childVal) 218 } 219 return parent 220 } 221 ASSET_TYPES.forEach(function(key){ 222 strats[key+'s'] = mergeAssets 223 }) 224 // 实现watch的策略和并,将相同的属性放到一个数组里面。 225 strats.watch = function(parentVal,childVal , key , vm){ 226 if(!childVal){ 227 return Object.create(parentVal) 228 } 229 var res = {} 230 res = extend(res,parentVal) 231 for(var key in childVal){ 232 var parent = res[key] 233 var child = childVal[key] 234 res[key] = parent ? Array.isArray(parent) ? parent.concat(child) : [parent].concat(child) : 235 Array.isArray(child) ? child : [child] ; 236 } 237 return res 238 } 239 // 实现props指令的合并策略 240 strats.props = function(parentVal,childVal , key , vm){ 241 if(!childVal){ 242 return parentVal 243 } 244 var res = Object.create( null) 245 extend(res,parentVal) 246 if(childVal){ 247 extend(res,childVal) 248 } 249 return res 250 } 251 function defaultStrat(parent,child , key , vm){ 252 return child === undefined ? parent :child ; 253 } 254 var cmalizeRE = /-(\w)/g 255 function camelize(val){ 256 return val.replace(cmalizeRE,function(c,m){ 257 return m ? m.toUpperCase(): "" 258 }) 259 } 260 function normalizeProps(options){ 261 var props = options.props 262 if(!props){ 263 return 264 } 265 var i , val , name 266 var res = {} 267 if(Array.isArray(props)){ 268 i = props.length 269 while(i--){ 270 val = props[i] 271 if(toString.call(val) === '[object String]'){ 272 name = camelize(val) 273 res[name] = {type: null} 274 }else{ 275 warn('使用数组愈发时props成员' +val+ '必须时一个数组') 276 } 277 278 279 } 280 }else if(isPlainObject(props)){ 281 for(var key in props){ 282 val = props[key] 283 name = camelize(key) 284 res[name] = isPlainObject(val) ? val : {type: val} 285 } 286 }else { 287 warn('选项props的值必须是一个对象或者是数组') 288 } 289 options.props = res 290 } 291 function mormalizeDirectives(options){ 292 var dir = options.directives 293 var res = {} 294 if(!dir){ 295 return 296 } 297 if(dir){ 298 for(var key in dir){ 299 var val = dir[key] 300 var name = camelize(key) 301 if(isPlainObject(val)){ 302 res[name] = val 303 } 304 if(toString.call(val) === '[object Function]'){ 305 res[name] = { 306 bind: val, 307 upata: val 308 } 309 } 310 } 311 } 312 options.directives = res 313 314 } 315 function mergeOptions(parent,child,vm){ 316 var options = {} 317 // 检测是component 是否是合法的 318 checkComonpents(child) 319 // 规范props 320 normalizeProps(child) 321 // 规范 dirctives 322 mormalizeDirectives(child) 323 324 // console.log(parent, child) 325 for(key in parent){ 326 magerField(key) 327 } 328 for(key in child){ 329 if(!hasOwn(parent ,key)){ // parent 中循环过地方不进行循环 330 magerField(key) // ----忘记写 331 } 332 333 } 334 // 默认合并策略 335 function magerField(key){ 336 // 自定义策略 默认策略 337 // console.log(key) 338 var result = strats[key] || defaultStrat // ---忘记写 339 options[key] = result(parent[key],child[key] , key , vm) 340 } 341 // console.log(options) 342 return options 343 } 344 function initMinxin(options){ 345 Vue.prototype._init = function(options){ 346 var vm = this 347 // 记录生成的vue实例对象 348 vm._uip = uip++ // //-------忘记写 349 350 vm.$options =mergeOptions(resolveConstructorOptions(vm.constructor),options,vm) 351 } 352 } 353 function Vue(options){ 354 // 安全机制 355 if(!(this instanceof Vue)){ //-------忘记写 356 warn('Vue是一个构造函数,必须是由new关键字调用') 357 } 358 this._init(options) 359 } 360 initMinxin() // 初始化选项1: 规范 2: 合并策略。 361 Vue.options = { 362 components: { 363 transtions: {}, 364 keepAlive:{}, 365 solt:{}, 366 transtionsGroup:{} 367 }, 368 directives:{}, 369 _bash: Vue 370 } 371 function initExend(Vue){ 372 Vue.extend = function(extendOptions){ 373 extendOptions = extendOptions || {} // -----忘记写 374 var Super = this 375 var Child = function VueComponent(options) { 376 this._init(options) 377 } 378 Child.prototype = Object.create(Super.prototype) 379 Child.prototype.constructor = Child // 改变constructor 的指向 380 Child.options = mergeOptions(Super.options,extendOptions) 381 // 子类继承父类的静态方法。 382 Child.extend = Vue.extend 383 // console.log(new Child({})) 384 return Child 385 } 386 } 387 initExend(Vue) 388 return Vue 389 })
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 }, 25 components:{ 26 humle: componentA 27 } 28 29 }) 30 // console.log(Vue) 31 var Parent = Vue.extend({ 32 data: function() {}, 33 props: { 34 'rrr': 1 35 }, 36 components:{ 37 huml: componentA 38 }, 39 created : function(){ 40 41 }, 42 watch: { 43 test: function(){} 44 } 45 }) 46 var Child = Parent.extend({ 47 components:{ 48 humlt: componentA 49 }, 50 created: function(){ 51 52 }, 53 props: { 54 'ddd': 3, 55 "dcdc-vfv": Number 56 }, 57 directives: { 58 test: { 59 bind: function(){} 60 }, 61 test2: function(){} 62 }, 63 watch: { 64 aa: function(){}, 65 test: function(){} 66 } 67 }); 68 // console.log(vm.$options) 69 console.log (Parent.options) 70 console.log(Child.options) 71 </script> 72 </body> 73 </html>
合并完效果
以上为个人学习总结,如有问题,请回复。