Vueソースコード学習のレンダリング原理

以前のテンプレート コンパイルの研究ノートでは、テンプレート$mountがコンパイルされてrender関数が生成され、呼び出されることがわかっています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)
}

上記mount$mountVue プロトタイプのメソッドです。

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

mountComponentこれは、コンポーネント インスタンス マウントのエントリ関数であり、ライフ サイクルに関連しているため、このメソッドの特定のコードはソース コードlifecycleファイル

// 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
}

mountComponent上記のコードから、メソッドは主にインスタンスにel割り当て、vm._update(vm._render(), hydrating)次に_updateわかります_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
    }
}

_render関数は、render実際にはvnode、テンプレート コンパイルによって生成されたrender関数を実行することによって生成される関数です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
    }
}

_update仮想 DOM を実際の DOM に変換するコア メソッドです. 主にpatchこのメソッド実際の DOM を作成します. レンダリングと更新の初期化時に渡されるパラメータのみが異なります.patchメソッドは別の研究ノートに移動できます;

要約:

初期レンダリングのプロセスは、テンプレートがコンパイルされた後に実行されmountComponentmountComponent主にインスタンスをマウントするvm._update(vm._render(), hydrating)メソッドを実行され、_renderメソッドはテンプレートのコンパイルによって生成されたrender関数をvnode_updateメソッドは生成された仮想 DOM を実際の DOM に変換します。 、主なコアはpatchメソッド。

おすすめ

転載: blog.csdn.net/Ljwen_/article/details/124802153