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 代碼以下javascript
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>
合併完效果html
以上爲我的學習總結,若有問題,請回復。vue