https://www.processon.com/vie...

https://www.processon.com/vie...

指标

  1. 把握源码学习办法
  2. vue初始化原理解剖
  3. 深刻了解数据响应式

配置环境

  1. 拷贝代码: git clone https://github.com/vuejs/vue.git
  2. npm install
  3. 装置打包工具rollup npm i -g rollup
  4. 批改package.json 中dev的打包配置"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
  5. 执行脚本 npm run dev
  6. 在examples文件夹中增加test文件

文件目录

vue├──dist #公布目录├──examples #范例,测试代码在这里├──flow #试代码├──packages #外围代码之外的独立库├──scripts #构建脚本├──src #源码├──test #ts类型申明,下面flow└──types #对flow的类型申明 
  src # 源码    ├──compiler  # 编辑器相干  ├──core  # 外围代码  ├──────components # 通用组建如 keep-alive  ├──────global-api # 全局API  ├──────instance # 构建函数等  ├──────observer # 响应式相干  ├──────vdom # 虚构DOM相干  └──────platforms # 平台独特的代码 代码扩充

vue源码源码剖析-初始化流程

入口

dev脚本中 scripts/config.js 配置文件
web-full-dev 示意执行时的配置项

'web-full-dev': {    // 入口    entry: resolve('web/entry-runtime-with-compiler.js'),    // 进口    dest: resolve('dist/vue.js'),    // 格局    format: 'umd',    env: 'development',    alias: { he: './entity-decoder' },    banner  },
入口文件

门路src/platforms/web/entry-runtime-with-compiler.js

首先是扩大$mount办法, 解决template或者是el选项

// 扩大$mountconst mount = Vue.prototype.$mountVue.prototype.$mount = function (el?: string | Element,hydrating?: boolean): Component {el = el && query(el)  if (el === document.body || el === document.documentElement) {    process.env.NODE_ENV !== 'production' && warn(      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`    )    return this  }  // 用户配置的选项  const options = this.$options  // 首先判断render是否存在  if (!options.render) {    // 获取template选项    let template = options.template    // 判断template是否存在 如果template不存在判断el是否存在    if (template) {          } else if (el) {      template = getOuterHTML(el)    }    if (template) {      // 获取到html模板字符之后  执行编译过程    }  }  // 执行挂载  return mount.call(this, el, hydrating)}
Vue.prototype.$mount的起源

门路: src/platforms/web/runtime/index.js

// 实现了一个patch函数 次要用于初始化和更新Vue.prototype.__patch__ = inBrowser ? patch : noop// 实现了$mountVue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {  el = el && inBrowser ? query(el) : undefined  // 定义$mount 挂载组件, 将vnode转化为node  return mountComponent(this, el, hydrating)}
查找vue

门路: src/core/index.js

// 初始化全局api 比方component/ fitter/ directive/ use/ mixin/ util/ extendinitGlobalAPI(Vue)
查找vue引入

门路: src/core/instance/index.js
申明了Vue构造函数

function Vue (options) {  if (process.env.NODE_ENV !== 'production' &&    !(this instanceof Vue)  ) {    warn('Vue is a constructor and should be called with the `new` keyword')  }  // 初始化办法  this._init(options)}// 实例属性  实例办法initMixin(Vue)  // 混入了_init()办法stateMixin(Vue) // $data/$props/ $set() $delete()  $watch()eventsMixin(Vue)  // $emit() / $on() $off()  $once()  事件相干办法lifecycleMixin(Vue)  // _update / $forceUpdate/ $destory  生命周期相干办法renderMixin(Vue) // $nextTick()/ _render() // 渲染相干

initMixin办法
门路: src/core/instance/init.js

// 实现一个_init办法 _init的目标是供外部应用Vue.prototype._init = function (options?: Object) {        //做选项合并 将传入的选项和vue的默认选项进行合并    if (options && options._isComponent) {          } else {      vm.$options = mergeOptions(        resolveConstructorOptions(vm.constructor),        options || {},        vm      )    }    // 初始化外围代码    vm._self = vm    initLifecycle(vm)  // $parent $root  $children  $refs    initEvents(vm)  // 自定义事件监听    initRender(vm) // $slots/ $createElement  定义$attrs 和$listeners响应式    // 下面办法在beforeCreate之前, 进行初始筹备    callHook(vm, 'beforeCreate')  // 派发beforeCreate, 在beforeCreate中能够拜访下面三个内容    initInjections(vm) // resolve injections before data/props    initState(vm)  // props/ methods/ data/ computed /watch 数据初始化    initProvide(vm) // resolve provide after data/props    callHook(vm, 'created')    // 当设置了el选项时, 主动调用$mount    if (vm.$options.el) {      vm.$mount(vm.$options.el)    }  }
整体流程:

new Vue() => 创立vue实例
_init() => 初始化数据, 属性, 事件
$mount() => 执行挂载, 将虚构dom转化成实在dom

mountComponent()=>    将虚构dom转化成实在domnew Watcher() => 创立组件渲染watcherupdateComponent() => 执行初始化或更新_update() => 初始化或更新, 将虚构dom转化成实在dom_render()  渲染组件,获取vdom~~~~

Vue源码剖析 数据响应式

vue2实现双向绑定,应用了js对象的Object.defineProperty()对data的setter/getter办法进行拦挡, 联合公布订阅模式,在getter中进行订阅.在setter中进行公布告诉,让所有订阅者实现响应
具体实现是在初始化Vue时, 会调用initState(vm)办法

initState(vm)

门路: src/core/instance/state.js

export function initState (vm: Component) {.....      // 判断data是否存在     if (opts.data) { //如果data存在,走这里,因为要看数据的双向绑定,所以找到initData         initData(vm)       } else {          observe(vm._data = {}, true /* asRootData */)       }  }
initData(vm)
function initData (vm: Component) {....      // 传进来的data可能是函数,或者对象,目标是避免数据净化;     let data = vm.$options.data     data = vm._data = typeof data === 'function'     ? getData(data, vm)     : data || {}    ....     //代理、反复判断   const keys = Object.keys(data)     while (i--) {....        //属性和办法不能是反复的判断      if (process.env.NODE_ENV !== 'production') {}        if (props && hasOwn(props, key)) {}      }      // 对data数据响应式解决     observe(data, true /* asRootData */)  }
对data数据响应式解决

门路: src/core/observer/index.js

export function observe (value: any, asRootData: ?boolean): Observer | void {....      // 为传进来的对象value,创立一个observer实例;任何一个对象都随同一个observer实例     // 返回一个observer实例     // 用observer实例,来判断类型 以及外部响应式解决       // 如果做过响应式,那么_ob_ 存在,就间接返回     if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {          ob = value.__ob__       } else if (...){...}else{...         // 如果未解决,就创立一个新的实例 ob实例        ob = new Observer(value)       }  }
new Observer(value)

门路: src/core/observer/index.js

export class Observer {      constructor (value: any) {         // 能够了解为小管家:比方在data中的数据是对象包对象的存在, data:{obj:{foo:foo}},对对象来说,是对对象新             增和删除的告诉工作;由小管家来告诉的。        // 当须要批改obj外面的属性,是在肚子里产生的事件,所以须要小管家来进行解决        // 如果obj是一个数组,选项的删除也是须要小管家        // 对象属性变动或者数组元素变动须要小管家告诉更新        this.dep = new Dep() //小管家     //判断传入的value类型,做相应的解决       if (Array.isArray(value)) { //当是Array时的解决              //笼罩数组实例的原型         }else{              //对象的解决            this.walk(value)         }      }      walk (obj: Object) {....           const keys = Object.keys(obj)           //遍历以后的所有keys          for (let i = 0; i < keys.length; i++) {              //对每个key进行拦挡             defineReactive(obj, keys[i])           }      }  }
defineReactive
//做递归解决   Object.defineProperty(obj, key, {        enumerable: true,        configurable: true,        get: function reactiveGetter () {            // 依赖收集           const value = getter ? getter.call(obj) : val            // Dep.target 能够了解为watcher的实例,每次触发watcher实例的时候,会手动触发,get事件一下获取以后                 值,将实例放在Dep.target上;           if (Dep.target) {                 // dep和watcher是n对n的关系,除了页面渲染{{name}}会触发watcher以外;还有可能是computed 或者w                    atch触发的事件监听;                // 双向增加两者关系                 dep.depend()                 // 若存在子ob                if (childOb) {                 // 把以后的watcher和子ob中的dep建设关系                childOb.dep.depend()                 if (Array.isArray(value)) {                    dependArray(value)                 }             }          }          return value        },        set: function reactiveSetter (newVal) {}        }  }
dep.depend

门路:src/core/observer/dep.js

depend () {      if (Dep.target) {      //执行的是以后watcher的addDep     Dep.target.addDep(this)      }  }
Dep.target.addDep

找到watcher的addDep事件
门路:src/core/observer/watcher.js

addDep (dep: Dep) { //dep和wacter互相保留的关系     const id = dep.id      if (!this.newDepIds.has(id)) { // 是否和以后的dep建设关系         // 没有就创立dep和watcher的关系         this.newDepIds.add(id)          this.newDeps.push(dep)          if (!this.depIds.has(id)) {              // 创立dep和watcher             dep.addSub(this)          }      }  } 
dep.addSub

src/core/observer/dep.js

export default class Dep {      //往本人的sub 外面去push      addSub (sub: Watcher) {          this.subs.push(sub)      }  }