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