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 })
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 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>