[學(xué)習(xí)vue3]準(zhǔn)備階段[一]

調(diào)試環(huán)境準(zhǔn)備

遷出Vue3源碼: git clone https://github.com/vuejs/vue-next.git
安裝依賴: yarn --ignore-scripts
生成sourcemap文件等曼,package.json

"dev": "node scripts/dev.js --sourcemap"

編譯: yarn dev

生成結(jié)果:
packages\vue\dist\vue.global.js
packages\vue\dist\vue.global.js.map

vue3源碼架構(gòu)

vue3初始化過程

createApp()是如何創(chuàng)建vue實(shí)例的;創(chuàng)建的vue實(shí)例執(zhí)行mount()都做了些什么?

//init.html
<div id="app"> 
     <h1>vue3初始化流程</h1>
</div>
<script src="../../dist/vue.global.js"></script>
<script>
const {createApp} = Vue
createApp({}).mount('#app') 
</script>

斷點(diǎn)調(diào)試createApp()
ensureRenderer() => renderer => createApp()
createAppAPI() => createApp
app.mount() => render() => patch() => processComponent() => mountComponent() => setupComponent() => setupRenderEffect()

執(zhí)行流程

createApp() packages/runtime-dom/src/index.ts 創(chuàng)建vue實(shí)例、擴(kuò)展mount方法
createRenderer()/baseCreateRenderer()
packages/runtime-core/src/renderer.ts
創(chuàng)建renderer對象按咒,它對外暴露3個重要方法 render , hydrate , createApp 婆殿,其中 render ,和 hydrate 的實(shí)際使用者是createApp()返回的vue實(shí)例對象潜慎。
createAppAPI(render, hydrate)
packages/runtime-core/src/apiCreateApp.ts 返回生產(chǎn)vue實(shí)例的createApp函數(shù)

render的使用者是vue實(shí)例的mount方法 我們發(fā)現(xiàn)component()/directive()/use()/mixin()這些方法都變成了實(shí)例方法,它們也會返回實(shí)例本身,鏈?zhǔn)秸{(diào)用成為可能
filter方法被移除了

createApp({})
.component('comp', { template: '<div>this is comp</div>' }) .directive('focus', { mounted(el) { el.focus() } }) 
.mount('#app')
  • mount(rootContainer: HostElement, isHydrate?: boolean)
    packages/runtime-core/src/apiCreateApp.ts
    將 createApp(rootComponent) 中傳入的根組件轉(zhuǎn)換為vnode瑟捣,然后渲染到宿主元素rootContainer 中割疾。
  • render(vnode, container) 將傳入vnode渲染到容器container上嚎卫。
  • patch(n1, n2, container)
    將傳入的虛擬節(jié)點(diǎn) n1 跟 n2 進(jìn)行對比,并轉(zhuǎn)換為dom操作宏榕。初始化時(shí) n1 并不存在拓诸,因此操作將是一次dom創(chuàng)建。
  • mount(rootContainer)
    packages/runtime-core/src/apiCreateApp.ts 執(zhí)行根組件掛載麻昼,創(chuàng)建其vnode奠支,并將它render()出來
  • render()
    packages/runtime-core/src/renderer.ts 執(zhí)行補(bǔ)丁函數(shù)patch()將vnode轉(zhuǎn)換為dom。
  • patch(n1, n2, container)
    packages/runtime-core/src/renderer.ts 根據(jù)n2的類型執(zhí)行相對應(yīng)的處理函數(shù)抚芦。對于根組件倍谜,執(zhí)行的是processComponent()
  • processComponent()
    packages/runtime-core/src/renderer.ts 執(zhí)行組件掛載或更新,由于首次執(zhí)行時(shí)n1為空燕垃,因此執(zhí)行組件掛載邏輯mountComponent()
  • mountComponent()
    packages/runtime-core/src/renderer.ts 創(chuàng)建組件實(shí)例枢劝,執(zhí)行setupComponent()設(shè)置其數(shù)據(jù)狀態(tài),其中就包括setup()選項(xiàng)的執(zhí)行

setup()如何生效

在vue3中如果要使用composition-api卜壕,就需要寫在setup()中您旁,它是如何生效并和options-api和諧共處的?

<div id="app"> <h1>setup()如何生效</h1> <p>{{foo}}</p>
</div>
<script src="../../dist/vue.global.js"></script> 
<script>
const { createApp, h, ref } = Vue
  createApp({
    setup() {
const foo = ref('hello, vue3!')
      return { foo }
    }
}).mount('#app') 
</script>

執(zhí)行過程

根組件執(zhí)行掛載mount()時(shí),執(zhí)行渲染函數(shù)render()獲取組件vnode轴捎,然后執(zhí)行補(bǔ)丁函數(shù)patch()將其轉(zhuǎn)換 為真實(shí)dom鹤盒,對于組件類型會調(diào)用processComponent(),這里會實(shí)例化組件并處理其setup選項(xiàng)侦副。
setupComponent() packages/runtime-core/src/component.ts 初始化props侦锯、slots和data

setupStatefulComponent(instance, isSSR) packages/runtime-core/src/component.ts 代理組件實(shí)例上下文,調(diào)用setup()

setup()會接收兩個參數(shù)秦驯,分別是props和setupContext尺碰,可用于獲取屬性、插槽內(nèi)容和派發(fā)事件

createApp({
props: ['bar'], // 屬性依然需要聲明 setup(props) {
// 作為setupResult返回
return { bar: props.bar } }
// 傳入rootProps
}, {bar: 'bar'}).mount('#app')

handleSetupResult(instance, setupResult, isSSR) packages/runtime-core/src/component.ts 處理setup返回結(jié)果,如果是函數(shù)則作為組件的渲染函數(shù)亲桥,如果是對象則對其做響應(yīng)化處理洛心。

自定義渲染器

可以自定義渲染器,將獲取到的vnode轉(zhuǎn)換為特定平臺的特定操作

范例:利用canvas畫圖

第一步:我們創(chuàng)建一個渲染器题篷,需要給它提供節(jié)點(diǎn)和屬性的操作

const { createRenderer } = Vue
// 創(chuàng)建一個渲染器词身,給它提供節(jié)點(diǎn)和屬性操作
const nodeOps = {}
const renderer = createRenderer(nodeOps);

第二步:創(chuàng)建畫布,我們通過擴(kuò)展默認(rèn)createApp做到這一點(diǎn)

// 保存畫布和其上下文 let ctx;
let canvas;
// 擴(kuò)展mount番枚,首先創(chuàng)建一個畫布元素
function createCanvasApp(App) {
    const app = renderer.createApp(App);
    const mount = app.mount;
    app.mount = function (selector) {
        canvas = document.createElement('canvas');
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        document.querySelector(selector).appendChild(canvas);
        ctx = canvas.getContext('2d');
        mount(canvas);
    }
    return app;
}
// 創(chuàng)建app實(shí)例 createCanvasApp({}).mount('#app')

此時(shí)已經(jīng)可以看到canvas法严,但是會報(bào)一個錯誤,是因?yàn)槲覀兩厦娼M件是空的葫笼,vue想要創(chuàng)建一個 comment元素導(dǎo)致

第三步:添加模板

<script type="text/x-template" id="chart">
      <bar-chart :data="chartData"></bar-chart>
</script>
<div id="app"></div>
createCanvasApp({
    template: '#chart', data() {
        return {
            chartData: [
                { title: "?銅", count: 200, color: "brown" },
                { title: "磚石", count: 300, color: "skyblue" },
                { title: "星耀", count: 100, color: "purple" },
                { title: "王者", count: 50, color: "gold" }
            ]
        }
    }
}
)

第四步:節(jié)點(diǎn)操作實(shí)現(xiàn)


// 保存canvas實(shí)例和上下文 let ctx, canvas
const nodeOps = {
    createElement: (tag, isSVG, is) => {
        // 創(chuàng)建元素時(shí)由于沒有需要創(chuàng)建的dom元素深啤,只需返回當(dāng)前元素?cái)?shù)據(jù)對象 return {tag}
    },
    insert: (child, parent, anchor) => {
        // 我們重寫了insert邏輯,因?yàn)樵谖覀僣anvasApp中不存在實(shí)際dom插入操作 // 這里面只需要將元素之間的父子關(guān)系保存一下即可
        child.parent = parent
        if (!parent.childs) {
            parent.childs = [child]
        } else {
            parent.childs.push(child)
        }
        // 只有canvas有nodeType渔欢,這里就是開始繪制內(nèi)容到canvas 
        if (parent.nodeType === 1) {
            draw(child)
        }
    },
    patchProp(el, key, prevValue, nextValue) {
        el[key] = nextValue;
    }
}

第四步:繪圖邏輯


const draw = (el, noClear) => {
    if (!noClear) {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
    }
    if (el.tag == 'bar-chart') {
        const { data } = el;
        const barWidth = canvas.width / 10,
            gap = 20,
            paddingLeft = (data.length * barWidth + (data.length - 1) * gap) / 2, paddingBottom = 10;
        // x軸
        // 柱狀圖
        data.forEach(({ title, count, color }, index) => {
            const x = paddingLeft + index * (barWidth + gap)
            const y = canvas.height - paddingBottom - count
            ctx.fillStyle = color
            ctx.fillRect(x, y, barWidth, count)
            // text
        });
    }
    // 遞歸繪制子節(jié)點(diǎn)
    el.childs && el.childs.forEach(child => draw(child, true));
}

配置自定義組件白名單:
app.config.isCustomElement = tag => tag === 'bar-chart'

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墓塌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奥额,更是在濱河造成了極大的恐慌,老刑警劉巖访诱,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垫挨,死亡現(xiàn)場離奇詭異,居然都是意外死亡触菜,警方通過查閱死者的電腦和手機(jī)九榔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涡相,“玉大人哲泊,你說我怎么就攤上這事〈呋龋” “怎么了切威?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長丙号。 經(jīng)常有香客問我先朦,道長,這世上最難降的妖魔是什么犬缨? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任喳魏,我火速辦了婚禮,結(jié)果婚禮上怀薛,老公的妹妹穿的比我還像新娘刺彩。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布创倔。 她就那樣靜靜地躺著三热,像睡著了一般。 火紅的嫁衣襯著肌膚如雪三幻。 梳的紋絲不亂的頭發(fā)上就漾,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音念搬,去河邊找鬼抑堡。 笑死,一個胖子當(dāng)著我的面吹牛朗徊,可吹牛的內(nèi)容都是我干的首妖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爷恳,長吁一口氣:“原來是場噩夢啊……” “哼有缆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起温亲,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棚壁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后栈虚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袖外,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年魂务,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了曼验。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡粘姜,死狀恐怖鬓照,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孤紧,我是刑警寧澤豺裆,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站坛芽,受9級特大地震影響留储,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咙轩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一获讳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧活喊,春花似錦丐膝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽偎肃。三九已至,卻和暖如春浑此,著一層夾襖步出監(jiān)牢的瞬間累颂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工凛俱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留紊馏,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓蒲犬,卻偏偏與公主長得像朱监,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子原叮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容