The rendering principle of Vue source code learning

In the previous template compilation study notes, we know that the template $mountwill be compiled to generate rendera function, and then called mount.call(this, el, hydrating);

// src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
// 重新定义 $mount
Vue.prototype.$mount = function (
    el?: string | Element,
    hydrating?: boolean
): Component {
    
    
    el = el && query(el)
    // ... 其他代码
    // 调用原先 Vue 原型上 $mount 方法
    return mount.call(this, el, hydrating)
}

The above mountis $mountthe method on the Vue prototype;

Vue.prototype.$mount = function (
    el?: string | Element,
    hydrating?: boolean
): Component {
    
    
    el = el && inBrowser ? query(el) : undefined
    return mountComponent(this, el, hydrating)
}

mountComponentIt is the entry function of component instance mounting, because it is related to the life cycle, so the specific code of this method is located in the source code lifecyclefile ;

// src/core/instance/lifecycle.js
export function mountComponent (
    vm: Component,
    el: ?Element,
    hydrating?: boolean
): Component {
    
    
    // 将真实的 el 赋值给实例,为后面虚拟 dom 做铺垫
    vm.$el = el
    // render 函数的判断
    if (!vm.$options.render) {
    
    
        vm.$options.render = createEmptyVNode
        if (process.env.NODE_ENV !== 'production') {
    
    
            if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
                vm.$options.el || el) {
    
    
                warn(
                    'You are using the runtime-only build of Vue where the template ' +
                    'compiler is not available. Either pre-compile the templates into ' +
                    'render functions, or use the compiler-included build.',
                    vm
                )
            } else {
    
    
                warn(
                    'Failed to mount component: template or render function not defined.',
                    vm
                )
            }
        }
    }
    callHook(vm, 'beforeMount')

    let updateComponent
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    
    
        updateComponent = () => {
    
    
            const name = vm._name
            const id = vm._uid
            const startTag = `vue-perf-start:${
      
      id}`
            const endTag = `vue-perf-end:${
      
      id}`

            mark(startTag)
            const vnode = vm._render()
            mark(endTag)
            measure(`vue ${
      
      name} render`, startTag, endTag)

            mark(startTag)
            vm._update(vnode, hydrating)
            mark(endTag)
            measure(`vue ${
      
      name} patch`, startTag, endTag)
        }
    } else {
    
    
        updateComponent = () => {
    
    
            // 实例挂载
            vm._update(vm._render(), hydrating)
        }
    }
   	// 创建一个渲染 watcher 实例,主要执行 updateComponent 的方法
    new Watcher(vm, updateComponent, noop, {
    
    
        before () {
    
    
            if (vm._isMounted && !vm._isDestroyed) {
    
    
                callHook(vm, 'beforeUpdate')
            }
        }
    }, true /* isRenderWatcher */)
    hydrating = false
    // mounted 钩子函数只执行一次
    if (vm.$vnode == null) {
    
    
        vm._isMounted = true
        callHook(vm, 'mounted')
    }
    return vm
}

From the above code, we can see that mountComponentthe method mainly elassigns to the instance, and then vm._update(vm._render(), hydrating)mounts the instance by calling , and we then look at _updateand _render;

// src/core/instance/render.js
export function initRender (vm: Component) {
    
    
    // ... 其他代码
    // 主要参数: tag, data, children
    vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}

export function renderMixin (Vue: Class<Component>) {
    
    
    installRenderHelpers(Vue.prototype)
    Vue.prototype.$nextTick = function (fn: Function) {
    
    
        return nextTick(fn, this)
    }
    Vue.prototype._render = function (): VNode {
    
    
        const vm: Component = this
        // 获取 render 函数
        const {
    
     render, _parentVnode } = vm.$options

        if (_parentVnode) {
    
    
            vm.$scopedSlots = normalizeScopedSlots(
                _parentVnode.data.scopedSlots,
                vm.$slots,
                vm.$scopedSlots
            )
        }
        vm.$vnode = _parentVnode
        let vnode
        try {
    
    
            currentRenderingInstance = vm
            // 执行 render 函数生成 vnode
            vnode = render.call(vm._renderProxy, vm.$createElement)
        } catch (e) {
    
    
            handleError(e, vm, `render`)
            if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
    
    
                try {
    
    
                    vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
                } catch (e) {
    
    
                    handleError(e, vm, `renderError`)
                    vnode = vm._vnode
                }
            } else {
    
    
                vnode = vm._vnode
            }
        } finally {
    
    
            currentRenderingInstance = null
        }
        // ... 其他代码
        return vnode
    }
}

_renderThe function is renderactually vnodethe core of converting the function to , which is generated by executing renderthe function vnode;

// src/core/instance/lifecycle.js
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
    
    
    const vm: Component = this
    const prevEl = vm.$el
    const prevVnode = vm._vnode
    const restoreActiveInstance = setActiveInstance(vm)
    vm._vnode = vnode
    if (!prevVnode) {
    
    
        // 初始化渲染调用
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
    
    
        // 更新时调用
        vm.$el = vm.__patch__(prevVnode, vnode)
    }
    restoreActiveInstance()
    if (prevEl) {
    
    
        prevEl.__vue__ = null
    }
    if (vm.$el) {
    
    
        vm.$el.__vue__ = vm
    }
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
    
    
        vm.$parent.$el = vm.$el
    }
}

_updateIt is the core method to transform virtual DOM into real DOM. It mainly uses patchthe method to create real DOM. Only the parameters passed in are different when initializing rendering and updating. patchThe method can be moved to another study note ;

Summarize:

The process of initial rendering is executed after the template is compiled mountComponent, mountComponentmainly through vm._update(vm._render(), hydrating)the method to mount the instance, and _renderthe method is to execute renderthe function to generate vnode, _updatethe method is to convert the generated virtual DOM into a real DOM, the main core is patchthe method ;

Guess you like

Origin blog.csdn.net/Ljwen_/article/details/124802153