浅尝vue

浅尝vue

rollup 与 webpack
umd,esm,commonjs
weex
shared
export const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]

export const LIFECYCLE_HOOKS = [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed',
  'activated',
  'deactivated',
  'errorCaptured',
  'serverPrefetch'
]
理一下代码的执行思路哈
// 代码必然从构造函数开始执行的。
<body>
    <div id="app">
        <span>
            {{aa}}
        span>
    div>
    <script>
        new Vue({
            data() {
                return {
                    aa: 'aa'
                }
            }
        }).$mount('#app')
    script>
body>

【1.寻找Vue constructor阶段】

// 寻找的过程中顺便发现的
Vue.prototype.$mount
Vue.prototype.__patch__ = inBrowser ? patch : noop
initGlobalAPI(Vue)---->>>
initUse(Vue)  Vue.use
initMixin(Vue)  Vue.mixin
initExtend(Vue)  Vue.extend
initAssetRegisters(Vue)  Vue[type] 
//  注意这句代码 this.options[type + 's'][id] = definition

【2.prototype预处理阶段】

找到了Vue构造器,但是发现除了上边的预处理,代码还做Vue原型prototype做了很多预处理,这个阶段称为prototype处理阶段

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
prototype处理阶段(1)initMixin
 方法。
  Vue.prototype._init = function () {
 	initLifecycle(vm)
	 initEvents(vm)
	 initRender(vm)
	 callHook(vm, 'beforeCreate')
	 initInjections(vm) // resolve injections before data/props
	 initState(vm)
	 initProvide(vm) // resolve provide after data/props
	 callHook(vm, 'created')
 }

prototype处理阶段(2)stateMixin
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch
prototype处理阶段(3)initRender
Vue.prototype.$on
Vue.prototype.$once
Vue.prototype.$off
Vue.prototype.$emit
prototype处理阶段(4)lifecycleMixin
Vue.prototype._update
Vue.prototype.$forceUpdate
Vue.prototype.$destroy

prototype处理阶段(5)renderMixin

Vue.prototype.$nextTick
Vue.prototype._render

【3.vm处理阶段】

以上是prototype的处理阶段,或者说new Vue之前的处理,那么new Vue之后做了什么,这个阶段可以称为(vm处理阶段)
也就是执行_init方法

vm处理阶段initLifecycle
parent.$children.push(vm)
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm处理阶段 initEvents
把父组件绑定的event映射到子组件。
updateListeners
vm处理阶段 initRender
 vm.$slots = resolveSlots(options._renderChildren, renderContext)
 vm.$scopedSlots = emptyObject
 vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
 vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
vm处理阶段 initInjections
vm处理阶段 initState
处理 opts.data  (核心observe(data)) 这一块Observer阶段我们稍后详细的看下。
处理 opts.computed
处理 opts.watch
vm处理阶段initProvide

【4.Observer阶段】

vue中我们最关心的是如何实现mvvm的,data和dom是如何进行绑定的。到底对opt.data做了什么?

Observer阶段之new Observer。
// 记录下observer阶段
class Observer{
	this.dep = new Dep()
	def(value, '__ob__', this)
	如果是数组那么...
	如果是其他那么walk...,然后执行defineReactive
}
Observer阶段之数组处理
对数组操作的7个方法进行劫持,先执行这7个方法原本的操作,然后进行this.__ob__.dep.notify()
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
Observer阶段之defineReactive

遍历所有的key进行劫持。
每一个key 都会const dep = new Dep()
通过Object.defineProperty的get进行依赖收集,通过set进行触发更新。

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })
}
其实到这里initState中的Observer阶段就完成了。有人说,我什么都么看到哈? 看后边的代码执行$mount

【5.mountComponent阶段】

执行 mountComponent(vm: Component,el: ?Element, hydrating?: boolean)
一个vue组件执行一次mountComponen注册一个watcher
二个vue就会执行两次。

mountComponent () {
	updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
    new Watcher(vm, updateComponent, noop, {
	    before () {
	      if (vm._isMounted && !vm._isDestroyed) {
	        callHook(vm, 'beforeUpdate')
	      }
	    }
	 }, true /* isRenderWatcher */)
	return vm;
}

【6.dom diff和 dom更新阶段】

最终执行vm._update(vnode, hydrating)
dom打补丁,然后更新真实dom

vm._vnode = vnode
// 这里就是进行dom diff 然后打补丁的地方
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)

【7.从数据更新到Dom更新过程】

浅尝vue_第1张图片

数据更新后,在更新dom。问题来了: dom事件触发是如何绑定到dom上面的呢?

【8.dom事件触发数据更新过程】

我们发现_update中传入的dom虚拟节点已经是把数据处理过的。
所以从数据到dom的映射过程在_render完成的。
_render调用的是我们的render函数。
render函数通过compileToFunctions生成。compileToFunctions调用createCompiler函数。
三个步骤。parse,optimize,generate.
	const ast = parse(template.trim(), options)
	if (options.optimize !== false) {
		optimize(ast, options)
	}
	const code = generate(ast, options)
	return {
		ast,
		render: code.render,
		staticRenderFns: code.staticRenderFns
	}

这样子编译的过程就把我们的data属性编译到了vNode上边去,并且如果如果触发事件会更新vm的data。
这个过程就不详细说了。

dom diff的过程我们就不说了。

看一下vue 的生命周期哪里执行的。
vue的指令的编译方式。
 ASSET_TYPES.forEach(type => {
    Vue[type] = function (
      id: string,
      definition: Function | Object
    ): Function | Object | void {
      if (!definition) {
        return this.options[type + 's'][id]
      } else {
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== 'production' && type === 'component') {
          validateComponentName(id)
        }
        if (type === 'component' && isPlainObject(definition)) {
          definition.name = definition.name || id
          definition = this.options._base.extend(definition)
        }
        if (type === 'directive' && typeof definition === 'function') {
          definition = { bind: definition, update: definition }
        }
        this.options[type + 's'][id] = definition
        return definition
      }
    }
  })

function genDirectives (el: ASTElement, state: CodegenState): string | void {
  const dirs = el.directives
  if (!dirs) return
  let res = 'directives:['
  let hasRuntime = false
  let i, l, dir, needRuntime
  for (i = 0, l = dirs.length; i < l; i++) {
    dir = dirs[i]
    needRuntime = true
    const gen: DirectiveFunction = state.directives[dir.name]
    if (gen) {
      // compile-time directive that manipulates AST.
      // returns true if it also needs a runtime counterpart.
      needRuntime = !!gen(el, dir, state.warn)
    }
    if (needRuntime) {
      hasRuntime = true
      res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
        dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''
      }${
        dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ''
      }${
        dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''
      }},`
    }
  }
  if (hasRuntime) {
    return res.slice(0, -1) + ']'
  }
}

你可能感兴趣的:(点滴-vue-目录)