https://ustbhuangyi.github.io/vue-analysis/v2/data-driven/update.html#%E6%80%BB%E7%BB%93

  1. function Vue (options) {
  2. //不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);
  3. //不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype);
  4. //不用 return 临时对象,因为 new 会帮你做;
  5. //不要给原型想名字了,因为 new 指定名字为 prototype。
  6. //instanceof是一个判断右侧是否在左侧原型链上的过程,new做了newObj.__proto__ = constructor
  7. if (process.env.NODE_ENV !== 'production' &&
  8. !(this instanceof Vue)
  9. ) {
  10. warn('Vue is a constructor and should be called with the `new` keyword')
  11. }
  12. this._init(options)
  13. }
  1. //聚合同类定义
  2. var events = {
  3. create: updateDOMListeners,
  4. update: updateDOMListeners
  5. };
  6. //总模块定义聚合
  7. var platformModules = [
  8. attrs,
  9. klass,
  10. events,
  11. domProps,
  12. style,
  13. transition
  14. ];
  15. /* */
  16. // the directive module should be applied last, after all
  17. // built-in modules have been applied.
  18. var modules = platformModules.concat(baseModules);
  19. //聚合需要的钩子
  20. function createPatchFunction (backend) {
  21. var i, j;
  22. var cbs = {};
  23. var modules = backend.modules;
  24. var nodeOps = backend.nodeOps;
  25. for (i = 0; i < hooks.length; ++i) {
  26. cbs[hooks[i]] = [];
  27. for (j = 0; j < modules.length; ++j) {
  28. if (isDef(modules[j][hooks[i]])) {
  29. cbs[hooks[i]].push(modules[j][hooks[i]]);
  30. }
  31. }
  32. }
  33. ...
  34. }
  35. //更新组件的属性(事件,属性等
  36. prepatch: function prepatch (oldVnode, vnode) {
  37. var options = vnode.componentOptions;
  38. var child = vnode.componentInstance = oldVnode.componentInstance;
  39. updateChildComponent(
  40. child,
  41. options.propsData, // updated props
  42. options.listeners, // updated listeners
  43. vnode, // new parent vnode
  44. options.children // new children
  45. );
  46. },
  47. // 更新vnode节点
  48. function patchVnode (
  49. oldVnode,
  50. vnode,
  51. insertedVnodeQueue,
  52. ownerArray,
  53. index,
  54. removeOnly
  55. ) {
  56. if (oldVnode === vnode) {
  57. return
  58. }
  59. if (isDef(vnode.elm) && isDef(ownerArray)) {
  60. // clone reused vnode
  61. vnode = ownerArray[index] = cloneVNode(vnode);
  62. }
  63. var elm = vnode.elm = oldVnode.elm;
  64. if (isTrue(oldVnode.isAsyncPlaceholder)) {
  65. if (isDef(vnode.asyncFactory.resolved)) {
  66. hydrate(oldVnode.elm, vnode, insertedVnodeQueue);
  67. } else {
  68. vnode.isAsyncPlaceholder = true;
  69. }
  70. return
  71. }
  72. // reuse element for static trees.
  73. // note we only do this if the vnode is cloned -
  74. // if the new node is not cloned it means the render functions have been
  75. // reset by the hot-reload-api and we need to do a proper re-render.
  76. if (isTrue(vnode.isStatic) &&
  77. isTrue(oldVnode.isStatic) &&
  78. vnode.key === oldVnode.key &&
  79. (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
  80. ) {
  81. vnode.componentInstance = oldVnode.componentInstance;
  82. return
  83. }
  84. var i;
  85. var data = vnode.data;
  86. // prepatch更新组件例如slot,options prepatch 会执行 updateChildComponents
  87. // hook和prepatch属性可以追溯到createComponent内对于属性的装配
  88. if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
  89. i(oldVnode, vnode);
  90. }
  91. var oldCh = oldVnode.children;
  92. var ch = vnode.children;
  93. // 更新节点的属性
  94. // cbs.update包括
  95. // updateAttrs
  96. // updateClass
  97. // updateDOMListeners
  98. // updateDOMProps
  99. // updateStyle
  100. // update
  101. // updateDirectives
  102. if (isDef(data) && isPatchable(vnode)) {
  103. for (i = 0; i < cbs.update.length; ++i) { cbs.update[i](oldVnode, vnode); }
  104. if (isDef(i = data.hook) && isDef(i = i.update)) { i(oldVnode, vnode); }
  105. }
  106. if (isUndef(vnode.text)) {
  107. if (isDef(oldCh) && isDef(ch)) {
  108. if (oldCh !== ch) { updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly); }
  109. } else if (isDef(ch)) {
  110. if (process.env.NODE_ENV !== 'production') {
  111. checkDuplicateKeys(ch);
  112. }
  113. if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }
  114. addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
  115. } else if (isDef(oldCh)) {
  116. removeVnodes(oldCh, 0, oldCh.length - 1);
  117. } else if (isDef(oldVnode.text)) {
  118. nodeOps.setTextContent(elm, '');
  119. }
  120. } else if (oldVnode.text !== vnode.text) {
  121. nodeOps.setTextContent(elm, vnode.text);
  122. }
  123. if (isDef(data)) {
  124. if (isDef(i = data.hook) && isDef(i = i.postpatch)) { i(oldVnode, vnode); }
  125. }
  126. }

运行时

  1. // 定义根节点的complile规则
  2. var createCompiler = createCompilerCreator(function baseCompile (
  3. template,
  4. options
  5. ) {
  6. var ast = parse(template.trim(), options);
  7. if (options.optimize !== false) {
  8. optimize(ast, options);
  9. }
  10. var code = generate(ast, options);
  11. return {
  12. ast: ast,
  13. render: code.render,
  14. staticRenderFns: code.staticRenderFns
  15. }
  16. });
  17. //其中 generate 生成template首次对应的render代码,genElement具体的处理
  18. // render代码的功能
  19. function generate (
  20. ast,
  21. options
  22. ) {
  23. // CodegenState合并配置
  24. var state = new CodegenState(options);
  25. var code = ast ? genElement(ast, state) : '_c("div")';
  26. return {
  27. render: ("with(this){return " + code + "}"),
  28. staticRenderFns: state.staticRenderFns
  29. }
  30. }
  31. function genElement (el, state) {
  32. if (el.parent) {
  33. el.pre = el.pre || el.parent.pre;
  34. }
  35. if (el.staticRoot && !el.staticProcessed) {
  36. return genStatic(el, state)
  37. } else if (el.once && !el.onceProcessed) {
  38. return genOnce(el, state)
  39. } else if (el.for && !el.forProcessed) {
  40. return genFor(el, state)
  41. } else if (el.if && !el.ifProcessed) {
  42. return genIf(el, state)
  43. } else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
  44. return genChildren(el, state) || 'void 0'
  45. } else if (el.tag === 'slot') {
  46. return genSlot(el, state)
  47. } else {
  48. // component or element
  49. var code;
  50. if (el.component) {
  51. code = genComponent(el.component, el, state);
  52. } else {
  53. var data;
  54. if (!el.plain || (el.pre && state.maybeComponent(el))) {
  55. data = genData$2(el, state);
  56. }
  57. var children = el.inlineTemplate ? null : genChildren(el, state, true);
  58. code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")";
  59. }
  60. //根节点是template写法,此时code是_c("App") _c是处理template的createElement
  61. // module transforms
  62. for (var i = 0; i < state.transforms.length; i++) {
  63. code = state.transforms[i](el, code);
  64. }
  65. return code
  66. }
  67. }

generate调用路径 //根据ast生成render的方法块
image.png

  1. var ref$1 = createCompiler(baseOptions);
  2. var compile = ref$1.compile;
  3. var compileToFunctions = ref$1.compileToFunctions;
  4. function createCompileToFunctionFn (compile) {
  5. var cache = Object.create(null);
  6. return function compileToFunctions (template,options,vm){
  7. //compile根据代码块生成function
  8. var compiled = compile(template, options);
  9. res.render = createFunction(compiled.render, fnGenErrors);
  10. return res
  11. }
  12. };
  13. //自动执行 auto
  14. var createCompiler = createCompilerCreator(function baseCompile (){...});
  15. function createCompilerCreator(baseCompile){
  16. return createCompiler(){
  17. function compile(){
  18. var compiled = baseCompile(template.trim(), finalOptions);
  19. return compiled;
  20. }
  21. return {
  22. compile: compile,
  23. compileToFunctions: createCompileToFunctionFn(compile)
  24. }
  25. }
  26. };
  27. //上图手动触发顺序调用描述
  28. 1
  29. var ref = compileToFunctions(template, {
  30. outputSourceRange: process.env.NODE_ENV !== 'production',
  31. shouldDecodeNewlines: shouldDecodeNewlines,
  32. shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
  33. delimiters: options.delimiters,
  34. comments: options.comments
  35. }, this);

update&createElement流程
image.png

Watcher在get触发依赖收集的过程中会 保证操作的dep实例收集的类属性 Dep.target是当前组件

  1. Watcher.prototype.get = function get () {
  2. pushTarget(this);
  3. //function pushTarget (target) {
  4. //targetStack.push(target);
  5. //Dep.target = target;
  6. value = this.getter.call(vm, vm);//开始依赖收集,触发set,addDep
  7. }
  8. }

createElm(vue\src\core\vdom\patch.js\patch) vdom->dom 描述 update _patch的主要功能
1,是否是常规标签ns是指它标准的特色标准如svg等
2,递归创建子元素的createElm,从底层向上生成待dom树
3, 调用create钩子invokeCreateHooks。注:接着再调用 invokeCreateHooks 方法执行所有的 create 的钩子并把 vnode push 到 insertedVnodeQueue 中。

  1. function createElm (
  2. vnode,
  3. insertedVnodeQueue,
  4. parentElm,
  5. refElm,
  6. nested,
  7. ownerArray,
  8. index
  9. ) {
  10. if (isDef(vnode.elm) && isDef(ownerArray)) {
  11. // This vnode was used in a previous render!
  12. // now it's used as a new node, overwriting its elm would cause
  13. // potential patch errors down the road when it's used as an insertion
  14. // reference node. Instead, we clone the node on-demand before creating
  15. // associated DOM element for it.
  16. vnode = ownerArray[index] = cloneVNode(vnode)
  17. }
  18. vnode.isRootInsert = !nested // for transition enter check
  19. if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
  20. return
  21. }
  22. const data = vnode.data
  23. const children = vnode.children
  24. const tag = vnode.tag
  25. if (isDef(tag)) {
  26. if (process.env.NODE_ENV !== 'production') {
  27. if (data && data.pre) {
  28. creatingElmInVPre++
  29. }
  30. if (isUnknownElement(vnode, creatingElmInVPre)) {
  31. warn(
  32. 'Unknown custom element: <' + tag + '> - did you ' +
  33. 'register the component correctly? For recursive components, ' +
  34. 'make sure to provide the "name" option.',
  35. vnode.context
  36. )
  37. }
  38. }
  39. // 是否是常规标签ns是指它标准的特色标准如svg等
  40. vnode.elm = vnode.ns
  41. ? nodeOps.createElementNS(vnode.ns, tag)
  42. : nodeOps.createElement(tag, vnode)
  43. setScope(vnode)
  44. /* istanbul ignore if */
  45. if (__WEEX__) {
  46. // in Weex, the default insertion order is parent-first.
  47. // List items can be optimized to use children-first insertion
  48. // with append="tree".
  49. const appendAsTree = isDef(data) && isTrue(data.appendAsTree)
  50. if (!appendAsTree) {
  51. if (isDef(data)) {
  52. invokeCreateHooks(vnode, insertedVnodeQueue)
  53. }
  54. insert(parentElm, vnode.elm, refElm)
  55. }
  56. createChildren(vnode, children, insertedVnodeQueue)
  57. if (appendAsTree) {
  58. if (isDef(data)) {
  59. invokeCreateHooks(vnode, insertedVnodeQueue)
  60. }
  61. insert(parentElm, vnode.elm, refElm)
  62. }
  63. } else {
  64. createChildren(vnode, children, insertedVnodeQueue)
  65. if (isDef(data)) {
  66. invokeCreateHooks(vnode, insertedVnodeQueue)
  67. }
  68. insert(parentElm, vnode.elm, refElm)
  69. }
  70. if (process.env.NODE_ENV !== 'production' && data && data.pre) {
  71. creatingElmInVPre--
  72. }
  73. } else if (isTrue(vnode.isComment)) {
  74. vnode.elm = nodeOps.createComment(vnode.text)
  75. insert(parentElm, vnode.elm, refElm)
  76. } else {
  77. vnode.elm = nodeOps.createTextNode(vnode.text)
  78. insert(parentElm, vnode.elm, refElm)
  79. }
  80. }

经典方法

normalizeArrayChildren 整理生成vdom

  1. function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
  2. const res = []
  3. let i, c, lastIndex, last
  4. for (i = 0; i < children.length; i++) {
  5. c = children[i]
  6. if (isUndef(c) || typeof c === 'boolean') continue
  7. lastIndex = res.length - 1
  8. last = res[lastIndex]
  9. // nested
  10. // 如果是数组,递归调用normalizeArrayChildren
  11. // nestedIndex, 多个重复时的唯一key
  12. if (Array.isArray(c)) {
  13. if (c.length > 0) {
  14. c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
  15. // merge adjacent text nodes 如果才生成的批量节点和当前数组最后一个元素都是文字,归并
  16. if (isTextNode(c[0]) && isTextNode(last)) {
  17. res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
  18. c.shift()
  19. }
  20. //apply的第二个参数是数组,本句功能相当于push(...c)
  21. res.push.apply(res, c)
  22. }
  23. } else if (isPrimitive(c)) {
  24. if (isTextNode(last)) {
  25. // merge adjacent text nodes
  26. // this is necessary for SSR hydration because text nodes are
  27. // essentially merged when rendered to HTML strings
  28. res[lastIndex] = createTextVNode(last.text + c)
  29. } else if (c !== '') {
  30. // convert primitive to vnode
  31. res.push(createTextVNode(c))
  32. }
  33. } else {
  34. if (isTextNode(c) && isTextNode(last)) {
  35. // merge adjacent text nodes
  36. res[lastIndex] = createTextVNode(last.text + c.text)
  37. } else {
  38. // default key for nested array children (likely generated by v-for)
  39. if (isTrue(children._isVList) &&
  40. isDef(c.tag) &&
  41. isUndef(c.key) &&
  42. isDef(nestedIndex)) {
  43. c.key = `__vlist${nestedIndex}_${i}__`
  44. }
  45. res.push(c)
  46. }
  47. }
  48. }
  49. return res
  50. }

observer/initState

vue初始化的构造函数_init内,会执行initState(对props、methods、data、computed和wathcer等属性做了初始化操作),其中initData部分会执行 observe(data, true)->new Observer(value)

  1. function initData (vm: Component) {
  2. let data = vm.$options.data
  3. data = vm._data = typeof data === 'function'
  4. ? getData(data, vm)
  5. : data || {}
  6. if (!isPlainObject(data)) {
  7. data = {}
  8. process.env.NODE_ENV !== 'production' && warn(
  9. 'data functions should return an object:\n' +
  10. 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
  11. vm
  12. )
  13. }
  14. // proxy data on instance
  15. const keys = Object.keys(data)
  16. const props = vm.$options.props
  17. const methods = vm.$options.methods
  18. let i = keys.length
  19. while (i--) {
  20. const key = keys[i]
  21. if (process.env.NODE_ENV !== 'production') {
  22. if (methods && hasOwn(methods, key)) {
  23. warn(
  24. `Method "${key}" has already been defined as a data property.`,
  25. vm
  26. )
  27. }
  28. }
  29. if (props && hasOwn(props, key)) {
  30. process.env.NODE_ENV !== 'production' && warn(
  31. `The data property "${key}" is already declared as a prop. ` +
  32. `Use prop default value instead.`,
  33. vm
  34. )
  35. } else if (!isReserved(key)) {
  36. proxy(vm, `_data`, key)
  37. }
  38. }
  39. // observe data
  40. observe(data, true /* asRootData */)
  41. }

Observer构造方法部分

  1. constructor (value: any) {
  2. this.value = value
  3. this.dep = new Dep()
  4. this.vmCount = 0
  5. def(value, '__ob__', this) //把当前实例defineProperty到data的__ob__上
  6. if (Array.isArray(value)) {
  7. if (hasProto) {
  8. protoAugment(value, arrayMethods)
  9. } else {
  10. copyAugment(value, arrayMethods, arrayKeys)
  11. }
  12. this.observeArray(value) //遍历数组再次调用 observe 方法
  13. } else {
  14. this.walk(value)
  15. }
  16. }
  17. //walk
  18. walk (obj: Object) {
  19. const keys = Object.keys(obj)
  20. for (let i = 0; i < keys.length; i++) {
  21. defineReactive(obj, keys[i]) //开始正式的依赖收集
  22. }
  23. }

设置gettter/setter 依赖收集/派发更新

  1. export function defineReactive (
  2. obj: Object,
  3. key: string,
  4. val: any,
  5. customSetter?: ?Function,
  6. shallow?: boolean
  7. ) {
  8. const dep = new Dep()
  9. const property = Object.getOwnPropertyDescriptor(obj, key)
  10. if (property && property.configurable === false) {
  11. return
  12. }
  13. // cater for pre-defined getter/setters
  14. const getter = property && property.get
  15. const setter = property && property.set
  16. if ((!getter || setter) && arguments.length === 2) {
  17. val = obj[key]
  18. }
  19. //childOb用于递归当前value(对象/数组)进行observe,当设置一个新值时,也能派发更新
  20. let childOb = !shallow && observe(val)
  21. Object.defineProperty(obj, key, {
  22. enumerable: true,
  23. configurable: true,
  24. get: function reactiveGetter () {
  25. const value = getter ? getter.call(obj) : val
  26. if (Dep.target) {
  27. dep.depend()
  28. if (childOb) {
  29. childOb.dep.depend()
  30. if (Array.isArray(value)) {
  31. dependArray(value)
  32. }
  33. }
  34. }
  35. return value
  36. },
  37. // ...
  38. })
  39. }

watcher部分

  1. // expOrFn用于执行然后getter变量触发依赖收集。cb用于$watch,如expOrFn值变动,watch会调用run,其中会执行watch的回调方法cb。
  2. constructor (
  3. vm: Component,
  4. expOrFn: string | Function,
  5. cb: Function,
  6. options?: ?Object,
  7. isRenderWatcher?: boolean
  8. )
  9. updateComponent = () => {
  10. vm._update(vm._render(), hydrating)
  11. }
  12. new Watcher(vm, updateComponent, noop, {
  13. before () {
  14. if (vm._isMounted) {
  15. callHook(vm, 'beforeUpdate')
  16. }
  17. }
  18. }, true /* isRenderWatcher */)
  19. //用newDepIds,depIds两个set来维护新旧依赖,便于在不同阶段调用/懒加载。新加入的依赖统一在
  20. //newDeps系列里,然后等合适的机会,diff并固化为实际的depIds系列,然后本身置空等待下次收集。
  21. //mountComponent的调用
  22. updateComponent = () => {
  23. vm._update(vm._render(), hydrating)
  24. }
  25. new Watcher(vm, updateComponent, noop, {
  26. before () {
  27. if (vm._isMounted) {
  28. callHook(vm, 'beforeUpdate')
  29. }
  30. }
  31. }, true /* isRenderWatcher */)
  32. constructor ( //构造方法
  33. vm: Component,
  34. expOrFn: string | Function,
  35. cb: Function,
  36. options?: ?Object,
  37. isRenderWatcher?: boolean
  38. ) {
  39. this.vm = vm
  40. if (isRenderWatcher) {
  41. vm._watcher = this
  42. }
  43. vm._watchers.push(this)
  44. ...
  45. // parse expression for getter
  46. if (typeof expOrFn === 'function') {
  47. this.getter = expOrFn // this.getter = updateComponent
  48. } else {
  49. this.getter = parsePath(expOrFn)
  50. if (!this.getter) {
  51. this.getter = noop
  52. process.env.NODE_ENV !== 'production' && warn(
  53. `Failed watching path: "${expOrFn}" ` +
  54. 'Watcher only accepts simple dot-delimited paths. ' +
  55. 'For full control, use a function instead.',
  56. vm
  57. )
  58. }
  59. }
  60. this.value = this.lazy
  61. ? undefined
  62. : this.get()
  63. }
  64. get(){
  65. pushTarget(this) //当前Dep.target设置为this watcher,并缓存。
  66. let value
  67. const vm = this.vm
  68. value = this.getter.call(vm, vm) // vm._update(vm._render(), hydrating)
  69. // render会生成VNode,过程中访问数据从而触发getter,此时dep.depend->Dep.target.addDep(this)
  70. // 交给watcher执行(进行一些关于dep的判断),最后交给dep.addSub,只负责收集行为。
  71. if (this.deep) {
  72. traverse(value) //遍历触发依赖收集
  73. }
  74. }
  75. cleanupDeps () {
  76. let i = this.deps.length
  77. while (i--) {
  78. const dep = this.deps[i]
  79. if (!this.newDepIds.has(dep.id)) {
  80. //如旧的依赖id,新的里面没有了,则remove掉
  81. dep.removeSub(this)
  82. }
  83. }
  84. let tmp = this.depIds
  85. this.depIds = this.newDepIds
  86. this.newDepIds = tmp
  87. this.newDepIds.clear()
  88. tmp = this.deps
  89. this.deps = this.newDeps
  90. this.newDeps = tmp
  91. this.newDeps.length = 0
  92. }
  93. //宏观理解下observer index是data数据reactive的逻辑,dep是一个data依赖的抽象或者说依赖的实体,维护了watchers,职责单一,如负责通知依赖的组件watcher update;watcher维护一个依赖的关系,然后负责具体组件的更新,取消订阅依赖等。

watch的更新

  1. update () {
  2. /* istanbul ignore else */
  3. if (this.computed) {
  4. // A computed property watcher has two modes: lazy and activated.
  5. // It initializes as lazy by default, and only becomes activated when
  6. // it is depended on by at least one subscriber, which is typically
  7. // another computed property or a component's render function.
  8. if (this.dep.subs.length === 0) {
  9. // In lazy mode, we don't want to perform computations until necessary,
  10. // so we simply mark the watcher as dirty. The actual computation is
  11. // performed just-in-time in this.evaluate() when the computed property
  12. // is accessed.
  13. this.dirty = true
  14. } else {
  15. // In activated mode, we want to proactively perform the computation
  16. // but only notify our subscribers when the value has indeed changed.
  17. this.getAndInvoke(() => {
  18. this.dep.notify()
  19. })
  20. }
  21. } else if (this.sync) {
  22. this.run()
  23. } else {
  24. queueWatcher(this) //一般逻辑
  25. }
  26. export function queueWatcher (watcher: Watcher) {
  27. const id = watcher.id
  28. if (has[id] == null) { //
  29. has[id] = true
  30. if (!flushing) { //flush: 冲刷
  31. queue.push(watcher)
  32. } else {
  33. // if already flushing, splice the watcher based on its id
  34. // if already past its id, it will be run next immediately.
  35. let i = queue.length - 1
  36. while (i > index && queue[i].id > watcher.id) {
  37. i--
  38. }
  39. //id蕴含优先级信息,遍历插入 splice第二个参数是删除个数,0表示只新增
  40. queue.splice(i + 1, 0, watcher)
  41. }
  42. // queue the flush
  43. if (!waiting) {
  44. waiting = true
  45. nextTick(flushSchedulerQueue) //一般执行nextTick中的
  46. }
  47. }
  48. }

开始flush冲刷

  1. function flushSchedulerQueue () {
  2. flushing = true
  3. let watcher, id
  4. // Sort queue before flush.
  5. // This ensures that:
  6. // 1. Components are updated from parent to child. (because parent is always
  7. // created before the child)
  8. // 2. A component's user watchers are run before its render watcher (because
  9. // user watchers are created before the render watcher)
  10. // 3. If a component is destroyed during a parent component's watcher run,
  11. // its watchers can be skipped.
  12. queue.sort((a, b) => a.id - b.id)
  13. // do not cache length because more watchers might be pushed
  14. // as we run existing watchers
  15. for (index = 0; index < queue.length; index++) {
  16. watcher = queue[index]
  17. if (watcher.before) {
  18. watcher.before()
  19. }
  20. id = watcher.id
  21. has[id] = null
  22. watcher.run() //在此过程中可能会发生queueWatcher插入的逻辑,所以遍历flag动 态获取length
  23. // in dev build, check and stop circular updates.
  24. if (process.env.NODE_ENV !== 'production' && has[id] != null) {
  25. circular[id] = (circular[id] || 0) + 1
  26. if (circular[id] > MAX_UPDATE_COUNT) {
  27. warn(
  28. 'You may have an infinite update loop ' + (
  29. watcher.user
  30. ? `in watcher with expression "${watcher.expression}"`
  31. : `in a component render function.`
  32. ),
  33. watcher.vm
  34. )
  35. break
  36. }
  37. }
  38. }
  39. //run
  40. run () {
  41. if (this.active) {
  42. const value = this.get() //触发vm._update(vm._render(), hydrating),重新渲染和收集依赖
  43. if (
  44. value !== this.value ||
  45. // Deep watchers and watchers on Object/Arrays should fire even
  46. // when the value is the same, because the value may
  47. // have mutated.
  48. isObject(value) ||
  49. this.deep
  50. ) {
  51. // set new value
  52. const oldValue = this.value
  53. this.value = value
  54. if (this.user) {
  55. try {
  56. this.cb.call(this.vm, value, oldValue) // $watch第二个参数的cb
  57. } catch (e) {
  58. handleError(e, this.vm, `callback for watcher "${this.expression}"`)
  59. }
  60. } else {
  61. this.cb.call(this.vm, value, oldValue)
  62. }
  63. }
  64. }
  65. }

set方法

  1. function set (target: Array<any> | Object, key: any, val: any): any {
  2. if (process.env.NODE_ENV !== 'production' &&
  3. (isUndef(target) || isPrimitive(target))
  4. ) {
  5. warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  6. }
  7. if (Array.isArray(target) && isValidArrayIndex(key)) {
  8. target.length = Math.max(target.length, key)
  9. target.splice(key, 1, val)
  10. return val
  11. }
  12. if (key in target && !(key in Object.prototype)) {
  13. target[key] = val
  14. return val
  15. }
  16. const ob = (target: any).__ob__
  17. if (target._isVue || (ob && ob.vmCount)) {
  18. process.env.NODE_ENV !== 'production' && warn(
  19. 'Avoid adding reactive properties to a Vue instance or its root $data ' +
  20. 'at runtime - declare it upfront in the data option.'
  21. )
  22. return val
  23. }
  24. if (!ob) {
  25. target[key] = val
  26. return val
  27. }
  28. defineReactive(ob.value, key, val)
  29. //进入这里说明是对响应式对象,添加新属性;
  30. //这里ob是observer实例,执行let childOb = !shallow && observe(val),observe,如果已经添加过,直 //接返回ob,然后getter准备收集依赖,
  31. ob.dep.notify()
  32. //notify最终执行watch.run执行value = this.get()触发vm.update(vm.render()),render会重新收集依赖
  33. //最后update渲染
  34. return val
  35. }

nextTick

  1. // 上面依次判断环境支持的api,对timerFunc赋值,
  2. let timeFunc
  3. if (typeof Promise !== 'undefined' && isNative(Promise)) {
  4. const p = Promise.resolve()
  5. timerFunc = () => {
  6. p.then(flushCallbacks)
  7. if (isIOS) setTimeout(noop)
  8. }
  9. isUsingMicroTask = true //当前的循环基于microTask还是macroTask
  10. }
  11. else{
  12. timerFunc = () => {
  13. setTimeout(flushCallbacks, 0)
  14. }
  15. }
  16. export function nextTick (cb?: Function, ctx?: Object) {
  17. let _resolve
  18. //推入队列一个闭包的function
  19. callbacks.push(() => {
  20. if (cb) {
  21. try {
  22. cb.call(ctx)
  23. } catch (e) {
  24. handleError(e, ctx, 'nextTick')
  25. }
  26. } else if (_resolve) {
  27. _resolve(ctx)
  28. }
  29. })
  30. if (!pending) {
  31. pending = true
  32. timerFunc()
  33. }
  34. // $flow-disable-line
  35. if (!cb && typeof Promise !== 'undefined') {
  36. return new Promise(resolve => {
  37. _resolve = resolve
  38. })
  39. }
  40. }
  41. //callback 执行某次flush
  42. function flushCallbacks () {
  43. pending = false
  44. const copies = callbacks.slice(0)
  45. callbacks.length = 0
  46. for (let i = 0; i < copies.length; i++) {
  47. copies[i]()
  48. }
  49. }

还有一段有趣的任务执行代码

  1. let counter = 1
  2. //观察dom各项属性的突变
  3. const observer = new MutationObserver(flushCallbacks)
  4. const textNode = document.createTextNode(String(counter))
  5. observer.observe(textNode, {
  6. characterData: true
  7. })
  8. //触发方式是执行dom内部text的变化,然后执行observer的监听
  9. timerFunc = () => {
  10. counter = (counter + 1) % 2
  11. textNode.data = String(counter)
  12. }
  13. isUsingMicroTask = true

validateProp

  1. function validateProp (
  2. key: string,
  3. propOptions: Object,
  4. propsData: Object,
  5. vm?: Component
  6. ): any {
  7. const prop = propOptions[key]
  8. const absent = !hasOwn(propsData, key)
  9. let value = propsData[key]
  10. // boolean casting
  11. const booleanIndex = getTypeIndex(Boolean, prop.type)
  12. if (booleanIndex > -1) {
  13. if (absent && !hasOwn(prop, 'default')) {
  14. value = false
  15. } else if (value === '' || value === hyphenate(key)) {
  16. // only cast empty string / same name to boolean if
  17. // boolean has higher priority
  18. //在type里如果boolen在string前,或者无string,则将视作
  19. const stringIndex = getTypeIndex(String, prop.type)
  20. if (stringIndex < 0 || booleanIndex < stringIndex) {
  21. value = true
  22. }
  23. }
  24. }
  25. // check default value
  26. if (value === undefined) {
  27. value = getPropDefaultValue(vm, prop, key)
  28. // since the default value is a fresh copy,
  29. // make sure to observe it.
  30. const prevShouldObserve = shouldObserve
  31. toggleObserving(true)
  32. observe(value)
  33. toggleObserving(prevShouldObserve)
  34. }
  35. if (
  36. process.env.NODE_ENV !== 'production' &&
  37. // skip validation for weex recycle-list child component props
  38. !(__WEEX__ && isObject(value) && ('@binding' in value))
  39. ) {
  40. assertProp(prop, key, value, vm, absent)
  41. }
  42. return value
  43. }