什么行業(yè)做網(wǎng)站深圳網(wǎng)絡(luò)推廣有幾種方法
render
- 在 $mount 時,會調(diào)用 render 方法
- 在寫 template 時,最終也會轉(zhuǎn)換成 render 方法
- Vue 的 _render 方法是實例的一個私有方法,它用來把實例渲染成一個虛擬 Node
- 它的定義在 src/core/instance/render.js 文件中,它返回的是一個vnode
- 這里看下它的實現(xiàn)
Vue.prototype._render = function (): VNode {const vm: Component = this// 從 $options 中拿到這個 render 函數(shù)// 這個 render 函數(shù), 可以是用戶自己寫,也可以通過編譯生成const { render, _parentVnode } = vm.$options// 跳過// reset _rendered flag on slots for duplicate slot checkif (process.env.NODE_ENV !== 'production') {for (const key in vm.$slots) {// $flow-disable-linevm.$slots[key]._rendered = false}}// 跳過if (_parentVnode) {vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject}// 跳過// set parent vnode. this allows render functions to have access// to the data on the placeholder node.vm.$vnode = _parentVnode// render selflet vnodetry {// call的第一個參數(shù)是當前上下文, vm._renderProxy 在生產(chǎn)環(huán)境下,就是 VM,也就是this本身, 在開發(fā)環(huán)境它可能是一個proxy對象 // 這個 vm.$createElement 也是一個函數(shù),會在下面來說// 總體來說就是生成 vnodevnode = render.call(vm._renderProxy, vm.$createElement)} catch (e) {// 捕獲并處理錯誤,嘗試再次生成 vnodehandleError(e, vm, `render`)// return error render result,// or previous vnode to prevent render error causing blank component/* istanbul ignore else */if (process.env.NODE_ENV !== 'production') {if (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}} else {vnode = vm._vnode}}// return empty vnode in case the render function errored outif (!(vnode instanceof VNode)) {// 如果拿到的 vnode 是個數(shù)組,說明模板有多個根節(jié)點,這個在后續(xù)vue3中支持,在vue2中會報下面的警告// 在vue2 中 vnode 是一個 vdom, if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {warn('Multiple root nodes returned from render function. Render function ' +'should return a single root node.',vm)}vnode = createEmptyVNode()}// set parentvnode.parent = _parentVnodereturn vnode }
- 注意,上面的 vm.$createElement 在 initRender 函數(shù)中被初始化定義
- 而 initRender 是在一開始 new Vue 的時候,會調(diào)用 _init 函數(shù)中被執(zhí)行
// initRender 函數(shù)中 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)
- 看到上面兩個函數(shù)區(qū)別,就是最后一個參數(shù)不一樣
_c
這個函數(shù)它實際上是被編譯生成的render函數(shù)所使用的一個方法$createElement
是給我們手寫render函數(shù)提供了一個創(chuàng)建vnode的一個方法var app = new Vue({el: '#app',// 手寫 render 函數(shù)render(createElement) {return createElement('div', {attrs: {id: 'app2' // 這些寫后,這里掛載的 div 元素,會替換掉之前的 #app 容器}}, this.message)},data() {return {message: 'Hello'}} })
- 這里,使用render和在html中寫是不一樣的,它沒有一個把從差值變換的這個過程
- 之前的寫法是在HTML里面定義了這個差值
// 之前的寫法 <div id="app">{{ message }} </div>
- 它在不執(zhí)行vue的時候,它會先把這個東西渲染出來
- 然后在new vue之后, 執(zhí)行 mount 的時候,再把它從差值的那個message給替換成真實的數(shù)據(jù)
- 在這里是通過純 render 函數(shù),當它執(zhí)行完畢以后,直接掛載到頁面上,這樣的話體驗會更好一點
- 因為這里手寫了 render 函數(shù),所以, 整個渲染流程中,就不會再執(zhí)行把 template 轉(zhuǎn)換 render 函數(shù)那一步了
- 所以就相當于直接就達到了一樣的效果
vm._renderProxy
的定義,也是發(fā)生在 src/core/instance/init.js 中- 在 _init 函數(shù)中,有一個判斷
/* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') {initProxy(vm) } else {vm._renderProxy = vm }
- 當前如果說是生產(chǎn)環(huán)境,
vm._renderProxy = vm
- 開發(fā)階段開發(fā)階段,則執(zhí)行
initProxy(vm)
定義在 src/core/instance/proxy.js 中// 報錯提示函數(shù) const warnNonPresent = (target, key) => {warn(`Property or method "${key}" is not defined on the instance but ` +'referenced during render. Make sure that this property is reactive, ' +'either in the data option, or for class-based components, by ' +'initializing the property. ' +'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',target) } // const hasHandler = {has (target, key) {const has = key in targetconst isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)) // 全局方法或者私有方法,或不在$data中// 屬性不在 target 下,并且不被允許,則報錯提示,比如:使用了 一個沒有在 data 中定義的變量,就會報錯if (!has && !isAllowed) {if (key in target.$data) warnReservedPrefix(target, key)else warnNonPresent(target, key)}return has || !isAllowed} }// 判斷當前瀏覽器是否支持 proxy,Proxy作用是對對象的訪問做一個劫持 const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)initProxy = function initProxy (vm) {if (hasProxy) {// determine which proxy handler to useconst options = vm.$optionsconst handlers = options.render && options.render._withStripped? getHandler: hasHandlervm._renderProxy = new Proxy(vm, handlers) // 在這里 handlers 是 hasHandler} else {vm._renderProxy = vm} }
- 這里可以看到,支持 Proxy 則
vm._renderProxy = new Proxy(vm, handlers)
- 當前如果說是生產(chǎn)環(huán)境,
- 通過以上分析可知,vm._render 最終是通過執(zhí)行 createElement 方法并返回的是 vnode
- 而返回的vnode 它是一個虛擬 Node