Vue原始碼學習(六):(支線)渲染函數中with(),call()的使用以及一些思考

2023-09-15 15:00:44

好傢伙,

 

昨天,在學習vue原始碼的過程中,看到了這個玩意

嘶,看不太懂,研究一下
 

1.上下文

這段出現vue模板編譯的虛擬node部分
export function renderMixin(Vue) {
    Vue.prototype._c = function () {
        //建立標籤
        return createElement(...arguments)
    }
    Vue.prototype._v = function (text) { //文字
        return createText(text)
    }
    Vue.prototype._s = function (val) {
        return val == null?"":(typeof val ==='object')?JSON.stringify(val):val
    }
    Vue.prototype._render = function () { //render函數變成 vnode
        let vm = this
        let render = vm.$options.render
        console.log(render,'||this is render')
        let vnode = render.call(this)
        // console.log(vnode)
        return vnode
    }
}
//vnode只可以描述節點

//建立元素
function createElement(tag,data={},...children){
    return vnode(tag,data,data.key,children)
}
//建立文字
function createText(text){
    return vnode(undefined,undefined,undefined,undefined,text)
}
//建立vnode
function vnode(tag,data,key,children,text){
    return {
        tag,
        data,
        key,
        children,
        text
    }
}

 

 

我實在是看不懂這個_render方法在幹什麼,所以我們開始研究

 

2.凍手嘗試

2.1.方法返回方法

寫一個簡易版本,在一個空白頁

 (顯然這會失敗,方法返回的方法未定義)

 

2.2.加上方法定義

_c = function () {
    //建立標籤
    return createElement(...arguments)
}
_v = function (text) { //文字
    return createText(text)
}
_s = function (val) {
    return val == null ? "" : (typeof val === 'object') ? JSON.stringify(val) : val
}

function createElement(tag, data = {}, ...children) {
    return vnode(tag, data, data.key, children)
}
//建立文字
function createText(text) {
    return vnode(undefined, undefined, undefined, undefined, text)
}
//建立vnode
function vnode(tag, data, key, children, text) {
    return {
        tag,
        data,
        key,
        children,
        text
    }
}

function test() {
    return _c('div', _v("張三"))
}

test()

成功執行

 

2.3.回到專案

現在再回到我們的專案
我們知道,渲染函數的_c,_v,_s等方法被定義在Vue的prototype上的

不可能像上述案例這樣直接定義在全域性

我們在寫一個例子,這裡用上with()

 

同樣,執行成功了

 

 所以,我們能看到,正如mdn檔案所說,

在這個例子中with()方法拓展了一個test()方法的作用域鏈

於是,到這裡,最難的問題已經解決了

 

3.程式碼分析

 

在這裡我們知道this指向Vue範例

 

 來看這串程式碼

console.log(this,"||this is this")
let vnode = render.call(this)

在 JavaScript 中,.call() 方法可以用於呼叫函數,並且可以顯式地指定函數執行時的作用域(即 this 值)。

 

於是,一切都通暢了

這一大段的程式碼無非做了這麼幾件事

1.在Vue的原型上定義_c,_v等節點處理方法

2.(  render.call(this)  )將render方法的作用域指定為this,即Vue範例本身

3.(  with(this)  )此處 with(this) 塊中的 this 則指向渲染函數 render 執行時的上下文,也是 Vue 範例

4.隨後,_c,_v等方法執行建立虛擬節點,返回