1 Star 0 Fork 1

longxiaobaiWJ / fed-e-task-vue

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

Vue

Vue2.x,获取源码git地址:https://github.com/vuejs/vue

1. 调试准备

  1. 在vue-dev的package.json文件添加 --sourcemap,用于生成.map文件

    "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev --sourcemap",
    // "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
  2. 生成vue.js和vue.js.map文件

    yarn install
    yarn run dev
  3. 编辑器配置

{
    "javascript.validate.enable": false,
}
  1. 辅助工具

    https://vue-next-template-explorer.netlify.app/
    https://template-explorer.vuejs.org/
    
    https://astexplorer.net/
    
    recast 
    https://www.npmjs.com/package/recast
    simple-html-parser
    https://www.npmjs.com/package/simple-html-parser
  2. 目录结构

    src
    ├── compiler
    ├── core 
    ├── platforms
       ├── web

2. 代码说明

  1. src\core\instance\index.js

    // 定义Vue函数,即定义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')
      }
      /**
       * 在new Vue({})时
       * 即调用_init函数
       * 该函数被定义时间
       * initMixin(Vue)
       */
      this._init(options) 
    }
    
    /**
     * 注册_init函数:
     * this._init(options)
     * 在new Vue({})时调用,初始化vm实例
     * 处理实例对象数据
     * src\core\instance\init.js
     */
    initMixin(Vue)
    /**
     * 使用Object.defineProperty
     * 特殊处理
     * 在Vue.prototype上挂载以下函数
     * $data、$props
     * $set、$watch、$delete
     */
    stateMixin(Vue)
    /**
     * 混入与事件相关的函数
     * $on、$off、$once、$emit
     * 采用发布-订阅的设计模式
     */
    eventsMixin(Vue)
    /**
     * 混入与生命周期相关的函数
     * _update、$forceUpdate、$destroy
     * _update函数中调用vm.__patch__函数
     * 即进行diff算法,比较新旧vdom节点,
     * 并渲染成真实dom元素
     * 在渲染视图时调用,包括首次渲染、数据更新时
     */
    lifecycleMixin(Vue)
    /**
     * 通过installRenderHelpers(Vue.prototype)函数:
     * 在Vue.prototype上定义许多编译时使用的辅助函数
     * 混入与渲染相关的函数
     * $nextTick、与Vue.nextTick对应
     * _render:
     * 函数体部分代码:
     * const { render, _parentVnode } = vm.$options
     * vnode = render.call(vm._renderProxy, vm.$createElement)
     * 返回值:vnode
     * vm.$createElement,即传入的h函数
     * 在调用this._init(options)时给vm实例上添加的$createElement函数
     * 即./init.js中调用initRender(vm)时添加
     */
    renderMixin(Vue)
    
    /**
     * 以上函数,在Vue.prototype上挂在函数,即定义Vue实例方法
     */
    export default Vue
  2. src\core\index.js

    /**
     * 在Vue上挂载静态方法
     * 例如以下方法:
     * Vue.set、Vue.del、Vue.nextTick、Vue.observable
     * Vue.extend、Vue.mixin、Vue.use、
     * Vue.component、Vue.directive、Vue.filter
     * 在Vue.options.components上挂载组件: keep-alive等
     */
    initGlobalAPI(Vue)
    
    // Object.defineProperty
    Object.defineProperty(Vue.prototype, '$isServer', {
      get: isServerRendering
    })
    
    Object.defineProperty(Vue.prototype, '$ssrContext', {
      get () {
        /* istanbul ignore next */
        return this.$vnode && this.$vnode.ssrContext
      }
    })
    
    // expose FunctionalRenderContext for ssr runtime helper installation
    Object.defineProperty(Vue, 'FunctionalRenderContext', {
      value: FunctionalRenderContext
    })
  3. src\platforms\web\runtime\index.js

    // \platforms\目录,代表与平台相关的代码定义,即与web平台相关
    // 该文件里主要定义与平台相关的方法
    import { mountComponent } from 'core/instance/lifecycle'
    
    // 在Vue.config上挂载与平台相关的方法
    Vue.config.mustUseProp = mustUseProp
    Vue.config.isReservedTag = isReservedTag
    Vue.config.isReservedAttr = isReservedAttr
    Vue.config.getTagNamespace = getTagNamespace
    Vue.config.isUnknownElement = isUnknownElement
    
    
    // 定义与web平台相关的自有指令、组件
    extend(Vue.options.directives, platformDirectives) // 注册和平台相关的全局指令:v-model、v-show
    extend(Vue.options.components, platformComponents) // 注册和平台相关的全局组件:v-transition、v-transition-group
    
    // --install platform patch function
    Vue.prototype.__patch__ = inBrowser ? patch : noop // 把虚拟DOM转换成真实 DOM
    
    /**
     * public mount method
     * 定义$mount函数,挂载方法设置,挂载DOM到页面
     * $mount函数是与平台相关函数
     * 在this._init(options)中调用:
     * vm.$mount(vm.$options.el)
     * _init函数在new Vue({})时调用,
     * 使用initMixin函数挂载到Vue.prototype上
     */
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && inBrowser ? query(el) : undefined
      /**
       * new Watcher(vm, updateComponent, noop, {})
       * updateComponent是关键函数
       * updateComponent = () => {vm._update(vm._render(), hydrating)}
       * 通知视图更新
       * 在lifecycleMixin(Vue)时定义vm._update函数
       * 在renderMixin(Vue)时定义vm._render函数
       * vm.$el = vm.__patch__(prevVnode, vnode)
       */
      return mountComponent(this, el, hydrating)  
    } 
  4. src\platforms\web\entry-runtime-with-compiler.js

    // 从该文件入口依次往上查找,引用关系: 4=>3=>2=>1
    import { compileToFunctions } from './compiler/index'
    
    // 重写$mount函数,判断是否传入render属性; 若没有传入,通过template生成render属性并在options上挂载
    const mount = Vue.prototype.$mount
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && query(el)
    
      const options = this.$options
      // resolve template/el and convert to render function
      if (!options.render) {
        let template = options.template
        if (template) { 
       		// 根据是否定义render属性,执行相应逻辑
        } else if (el) {
          template = getOuterHTML(el) // 通过传入el,将dom转化为template
        }
        if (template) {
          /**
           * 关键步骤、即获取render属性,并在options上挂载
           * 在mount.call(this, el, hydrating)会使用options.render
           * 在new Watcher(vm, updateComponent, noop, {})时调用,获取vnode节点
           * 查看: app.$options.render app.$options.staticRenderFns
           * app.$options.staticRenderFns存放静态根节点的生成函数,
           * 在app.$options.render中使用下标获取相应函数,进行生成相应节点,如: _m(0)
           * 参考src\core\instance\render-helpers\render-static.js -- this.$options.staticRenderFns[index]
           * 使用缓存,减少静态根节点对应的vdom的创建次数
           * 获取vnode: app.$options.render.call(app)
           */
          const { render, staticRenderFns } = compileToFunctions(template, {
            outputSourceRange: process.env.NODE_ENV !== 'production',
            shouldDecodeNewlines,
            shouldDecodeNewlinesForHref,
            delimiters: options.delimiters,
            comments: options.comments
          }, this)
          options.render = render 
        }
      }
      return mount.call(this, el, hydrating) // 执行web\runtime\index.js里定义的mount函数
    }
    
    /**
     * 编译函数:
     * compileToFunctions
     * 返回值: render, staticRenderFns
     * 挂载Vue.compile函数,
     * 传递一个HTML字符串返回render函数
     */
    Vue.compile = compileToFunctions
    
    export default Vue

3. 响应式

  1. src\core\instance\state.js

    export function initState (vm: Component) {
      /**
       * vm是Vue实例对象,可以通过自定义watch、computed观察vm._watchers数量
       */
      vm._watchers = []
      const opts = vm.$options
      if (opts.props) initProps(vm, opts.props)
      if (opts.methods) initMethods(vm, opts.methods)
      if (opts.data) {
        initData(vm)
      } else {
        observe(vm._data = {}, true /* asRootData */)
      }
      if (opts.computed) initComputed(vm, opts.computed)
      if (opts.watch && opts.watch !== nativeWatch) {
        initWatch(vm, opts.watch)
      }
    }
    
    function initData (vm: Component) {
      // observe data - 响应式数据处理
      observe(data, true /* asRootData */) 
    }
  2. src\core\observer\index.js

    export function observe (value: any, asRootData: ?boolean): Observer | void {
      if (!isObject(value) || value instanceof VNode) {
        return
      }
      let ob: Observer | void
      if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__
      } else if (
        shouldObserve &&
        !isServerRendering() &&
        (Array.isArray(value) || isPlainObject(value)) &&
        Object.isExtensible(value) &&
        !value._isVue
      ) {
        ob = new Observer(value) // 关键函数,数据响应式处理
      }
      if (asRootData && ob) {
        ob.vmCount++
      }
      return ob
    }
    
    export class Observer {
      value: any;
      dep: Dep;
      vmCount: number; // number of vms that have this object as root $data
    
      constructor (value: any) {
        this.value = value
        // 在数据响应式处理前,已调用initRender(vm)函数,创建两个Dep实例,即id:0、1的两个实例
        this.dep = new Dep()
        this.vmCount = 0
        def(value, '__ob__', this) // 挂载__ob__属性
        if (Array.isArray(value)) {
          if (hasProto) {
            protoAugment(value, arrayMethods)
          } else {
            copyAugment(value, arrayMethods, arrayKeys)
          }
          this.observeArray(value)
        } else {
          this.walk(value)
        }
      }
    }
    
    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()
        }
      })
    }
    
    export default class Dep {
        depend () {
            if (Dep.target) {
                Dep.target.addDep(this)
            }
        }
    }
    
    // dep.notify() => watcher.update()
    export default class Watcher {
        update () {
            /* istanbul ignore else */
            if (this.lazy) {
                this.dirty = true
            } else if (this.sync) {
                this.run()
            } else {
                queueWatcher(this)
            }
        }
    }

4. 模板编译

  1. src\platforms\web\entry-runtime-with-compiler.js

    /**
     * 关键步骤、即获取render属性,并在options上挂载
     * 在mount.call(this, el, hydrating)会使用options.render
     * 在new Watcher(vm, updateComponent, noop, {})时调用,获取vnode节点 
     */
    const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
    }, this)
    options.render = render 
    
    /**
        * src\platforms\web\runtime\index.js
        * 执行Vue.prototype.$mount函数
        */
    return mount.call(this, el, hydrating) 
  2. src\compiler\to-function.js

    // 解析compileToFunctions函数,返回render, staticRenderFns
    // src\compiler\to-function.js
    /**
     * compile
     * 将模板字符串编译为对象
     * 包含{ render, staticRenderFns }
     */
    const compiled = compile(template, options)
    
    
    // src\compiler\create-compiler.js 
    // 编译核心函数
    const compiled = baseCompile(template.trim(), finalOptions)
    
    // src\compiler\index.js
    export const createCompiler = createCompilerCreator(function baseCompile (
      template: string,
      options: CompilerOptions
    ): CompiledResult {
      // 编译template为ast抽象语法树
      const ast = parse(template.trim(), options)
      if (options.optimize !== false) {
        /**
         * 优化ast抽象语法树
         * 1. 标记静态节点: 不包含动态内容的节点
         * 2. 标记静态根节点: 不包含动态内容且含义子标签的节点
         */
        optimize(ast, options) 
      }
      /**
       * 生成字符串形式的render函数
       * const code = ast ? genElement(ast, state) : '_c("div")'
       */
      const code = generate(ast, options)
      return {
        ast,
        render: code.render,
        staticRenderFns: code.staticRenderFns
      }
    })
    
    // src\compiler\to-function.js
    // 将字符串形式的代码转化成函数
    res.render = createFunction(compiled.render, fnGenErrors)
    res.staticRenderFns = compiled.staticRenderFns.map(code => {
        return createFunction(code, fnGenErrors)
    })
    
    return (cache[key] = res)
  3. src\core\instance\lifecycle.js

    /**
     * vm._render()
     * 系开发者直接传入,或通过template属性编译生成
     * 调用该函数可获取vnode节点
     * 传入vm._update函数中,用于__patch__函数,比较新旧节点差异
     */
    updateComponent = () => {
        vm._update(vm._render(), hydrating)
    }
    
    // vm._render()
    vnode = render.call(vm._renderProxy, vm.$createElement)
    
    // 在调用getter函数时,完成依赖收集处理,以便在调用setter函数时,通知Watcher,调用其update函数更新视图
    new Watcher(vm, updateComponent, noop, {
        before () {
            if (vm._isMounted && !vm._isDestroyed) {
                callHook(vm, 'beforeUpdate')
            }
        }
    }, true /* isRenderWatcher */)

5. patch

  1. src\core\vdom\patch.js

    // patch => createElm => createComponent => createChildren
    function patch (oldVnode, vnode, hydrating, removeOnly) {
         if (isUndef(oldVnode)) {
          /**
           * empty mount (likely as component), create new root element
           * 将组件实例化为真实dom节点,进入该判断
           * 即.$mount()未传入参数,仅创建组件,没有挂载到页面上
           */
          isInitialPatch = true
          createElm(vnode, insertedVnodeQueue)
        } else {
    		// 新旧节点都存在,进入该判断
          	const isRealElement = isDef(oldVnode.nodeType)
          	if (!isRealElement && sameVnode(oldVnode, vnode)) {
    			// patch existing root node -- 视图更新操作,执行diff算法
            	patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
          	} else {
            	// 判断是否是真实dom节点,即首次渲染,初始化,创建一个空节点
            	if (isRealElement) {
                     oldVnode = emptyNodeAt(oldVnode)
            	}
                const oldElm = oldVnode.elm
                const parentElm = nodeOps.parentNode(oldElm)
                
             	createElm(
                    vnode,
                    insertedVnodeQueue,
                    // extremely rare edge case: do not insert if old element is in a
                    // leaving transition. Only happens when combining transition +
                    // keep-alive + HOCs. (#4590)
                    oldElm._leaveCb ? null : parentElm,
                    nodeOps.nextSibling(oldElm)
                )
        }
    }
    
    // 创建节点
    function createElm(
    vnode,
     insertedVnodeQueue,
     parentElm,
     refElm,
     nested,
     ownerArray,
     index
    ) {
        // 处理组件的情况
        if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
          return
        }
        
         if (isDef(tag)) {
    		// 标签节点逻辑
            vnode.elm = vnode.ns
            ? nodeOps.createElementNS(vnode.ns, tag)
            : nodeOps.createElement(tag, vnode)
          	setScope(vnode)
             
            if (__WEEX__) {
                
            } else {
                // 递归调用createElm函数,创建真实节点
    			createChildren(vnode, children, insertedVnodeQueue)
                if (isDef(data)) {
                  invokeCreateHooks(vnode, insertedVnodeQueue)
                }
                // 子节点创建完毕后,依次插入元素,最后挂载到页面上
                insert(parentElm, vnode.elm, refElm)
            }
             
         } else if (isTrue(vnode.isComment)) {
            // 注释节点逻辑
    		vnode.elm = nodeOps.createComment(vnode.text)
            // 插入元素,挂载到页面上
    		insert(parentElm, vnode.elm, refElm)
         } else { 
    		// 文本节点逻辑
    		vnode.elm = nodeOps.createTextNode(vnode.text)
    		// 插入元素,挂载到页面上
    		insert(parentElm, vnode.elm, refElm)
        }
    }
    
    // createElement => _createElement => vnode = createComponent(Ctor, data, context, children, tag)
    // 在createComponent函数中通过data.hooks初始化组件,进行挂载、new Watcher操作
    function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
        let i = vnode.data
        if (isDef(i)) {
            const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
            if (isDef(i = i.hook) && isDef(i = i.init)) {
                i(vnode, false /* hydrating */)
            }
    
            if (isDef(vnode.componentInstance)) {
                initComponent(vnode, insertedVnodeQueue)
                insert(parentElm, vnode.elm, refElm)
                if (isTrue(isReactivated)) {
                    reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
                }
                return true
            }
        }
    }
    // 一个组件对应一个Watcher实例,Vue实例对应一个Watcher实例,即Vue2.x中是(n+1)个Watcher实例
    
    // 通过以下vm.$createElement将hooks挂载到组件的data上
    vnode = render.call(vm._renderProxy, vm.$createElement)
  2. src\core\vdom\patch.js

    function patchVnode(
    oldVnode,
     vnode,
     insertedVnodeQueue,
     ownerArray,
     index,
     removeOnly
    ) {
        if (isUndef(vnode.text)) {
            // 新节点中没有text属性,即对比子节点
            if (isDef(oldCh) && isDef(ch)) {
                // 新旧节点都有子节点,即updateChildren操作
                if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
            } else if (isDef(ch)) {
                // 新节点有子节点,旧节点没有子节点
                if (process.env.NODE_ENV !== 'production') {
                    checkDuplicateKeys(ch)
                }
                if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
                addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
            } else if (isDef(oldCh)) {
                // 旧节点有子节点,新节点没有子节点
                removeVnodes(oldCh, 0, oldCh.length - 1)
            } else if (isDef(oldVnode.text)) {
                // 旧节点有文本,新节点没有文本
                nodeOps.setTextContent(elm, '')
            }
        } else if (oldVnode.text !== vnode.text) {
            // 新节点中有text属性,更新text属性
            nodeOps.setTextContent(elm, vnode.text)
        }
    }
    
    function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
         let oldStartIdx = 0
         let newStartIdx = 0
         let oldStartVnode = oldCh[0]
         let oldEndVnode = oldCh[oldEndIdx]
         let newStartVnode = newCh[0]
         let newEndVnode = newCh[newEndIdx]
         
    	 while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
             // 四种主要节点比较
             // oldStartVnode-newStartVnode,oldEndVnode-newEndVnode
             // oldStartVnode-newEndVnode,oldEndVnode-newStartVnode
             // 以上四种情况都不满足,newStartVnode与oldCh数组一次比较,++newStartIdx
             if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
            idxInOld = isDef(newStartVnode.key)
              ? oldKeyToIdx[newStartVnode.key]
              : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
             // 后续操作
         }
         if (oldStartIdx > oldEndIdx) {
             refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
             addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
         } else if (newStartIdx > newEndIdx) {
             removeVnodes(oldCh, oldStartIdx, oldEndIdx)
         }
    }
    
    function findIdxInOld(node, oldCh, start, end) {
        for (let i = start; i < end; i++) {
            const c = oldCh[i]
            if (isDef(c) && sameVnode(node, c)) return i
        }
    }

6. 代码流程

  1. 数据响应式处理

    new Vue({})
    this._init(options)
    
    // src\core\instance\init.js部分代码
    /**
     * 依次对传入参数进行初始化处理并挂载到vm上
     * props、methods、data、computed、watch
     * 在该函数中亦对数据进行响应式处理
     * 初始化vm._watchers,vm._watchers = []
     * src\core\instance\state.js
     */
    initState(vm)
    
    /**
     * 判断vm.$options.el
     * 进行创建dom节点,最终挂载dom元素操作
     * src\platforms\web\entry-runtime-with-compiler.js
     * 执行web平台下定义的$mount函数
     */
    if (vm.$options.el) {
        vm.$mount(vm.$options.el)
    }
  2. 模板编译

    // src\platforms\web\entry-runtime-with-compiler.js
    const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
    }, this)
    options.render = render
    options.staticRenderFns = staticRenderFns
    
    /**
     * src\platforms\web\runtime\index.js
     * 执行Vue.prototype.$mount函数
     */
    return mount.call(this, el, hydrating)
    
    // compileToFunction函数部分代码
    function baseCompile (
      template: string,
      options: CompilerOptions
    ): CompiledResult {
      // 编译template为ast抽象语法树
      const ast = parse(template.trim(), options)
      if (options.optimize !== false) {
        /**
         * 优化ast抽象语法树
         * 1. 标记静态节点: 不包含动态内容的节点
         * 2. 标记静态根节点: 不包含动态内容且含义子标签的节点
         */
        optimize(ast, options) 
      }
      /**
       * 生成字符串形式的render函数
       * const code = ast ? genElement(ast, state) : '_c("div")'
       */
      const code = generate(ast, options)
      return {
        ast,
        render: code.render,
        staticRenderFns: code.staticRenderFns
      }
    }
  3. new Watcher

    // src\core\instance\lifecycle.js
    // return mountComponent(this, el, hydrating)
    updateComponent = () => {
        vm._update(vm._render(), hydrating)
    }
    
    // 创建Vue实例与Vue组件时,均会实例化Watcher
    new Watcher(vm, updateComponent, noop, {
        before() {
            if (vm._isMounted && !vm._isDestroyed) {
                callHook(vm, 'beforeUpdate')
            }
        }
    }, true /* isRenderWatcher */)
  4. 页面渲染

    // vm._update(vm._render(), hydrating)
    
    if (!prevVnode) {
        // initial render
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
        // updates
        vm.$el = vm.__patch__(prevVnode, vnode)
    }
The MIT License (MIT) Copyright (c) 2013-present, Yuxi (Evan) You Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

learn vue,通读Vue2.x源码,并做注释 展开 收起
JavaScript
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
JavaScript
1
https://gitee.com/frontend_site/fed-e-task-vue.git
git@gitee.com:frontend_site/fed-e-task-vue.git
frontend_site
fed-e-task-vue
fed-e-task-vue
master

搜索帮助