亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

                vue3源碼分析-實(shí)現(xiàn)props,emit,事件處理等

                引言

              1. vue3源碼分析——rollup打包monorepo
              2. vue3源碼分析——實(shí)現(xiàn)組件的掛載流程
              3. 本期來(lái)實(shí)現(xiàn),setup里面使用props,父子組件通信props和emit等,所有的源碼請(qǐng)查看

                本期的內(nèi)容與上一期的代碼具有聯(lián)動(dòng)性,所以需要明白本期的內(nèi)容,最后是先看下上期的內(nèi)容哦!

                實(shí)現(xiàn)render中的this

                在render函數(shù)中,可以通過this,來(lái)訪問setup返回的內(nèi)容,還可以訪問this.$el等

                測(cè)試用例

                由于是測(cè)試dom,jest需要提前注入下面的內(nèi)容,讓document里面有app節(jié)點(diǎn),下面測(cè)試用例類似在html中定義一個(gè)app節(jié)點(diǎn)哦

                let appElement: Element; beforeEach(() => { appElement = document.createElement(‘p’); appElement.id = ‘app’; document.body.appendChild(appElement); }); afterEach(() => { document.body.innerHTML = ”; })復(fù)制代碼

                功能的測(cè)試用例正式開始

                test(‘實(shí)現(xiàn)代理對(duì)象,通過this來(lái)訪問’, () => { let that; const app = createApp({ render() { // 在這里可以通過this來(lái)訪問 that = this; return h(‘p’, { class: ‘container’ }, this.name); }, setup() { return { name: ‘123’ } } }); const appDoc = document.querySelector(‘#app’) app.mount(appDoc); // 綁定值后的html expect(document.body.innerHTML).toBe(‘123’); const elDom = document.querySelector(‘#container’) // el就是當(dāng)前組件的真實(shí)dom expect(that.$el).toBe(elDom); })復(fù)制代碼

                分析

                上面的測(cè)試用例

              4. setup返回是對(duì)象的時(shí)候,綁定到render的this上面
              5. $el則是獲取的是當(dāng)前組件的真實(shí)dom
              6. 解決這兩個(gè)需求:

              7. 需要在render調(diào)用的時(shí)候,改變當(dāng)前函數(shù)的this指向,但是需要思考的一個(gè)問題是:this是啥,它既要存在setup,也要存在el,咋們是不是可以用一個(gè)proxy來(lái)綁定呢?在哪里創(chuàng)建呢 可以在處理組件狀態(tài)setupStatefulComponent來(lái)完成改操作
              8. el則是在mountElement中掛載真實(shí)dom的時(shí)候,把當(dāng)前的真實(shí)dom綁定在vnode當(dāng)中
              9. 編碼

                針對(duì)上面的分析,需要在setupStatefulComponent中來(lái)創(chuàng)建proxy并且綁定到instance當(dāng)中,并且setup的執(zhí)行結(jié)果如果是對(duì)象,也已經(jīng)存在instance中了,可以通過instance.setupState來(lái)進(jìn)行獲取

                function setupStatefulComponent(instance: any) { instance.proxy = new Proxy({}, { get(target, key){ // 判斷當(dāng)前的key是否存在于instance.setupState當(dāng)中 if(key in instance.setupState){ return instance.setupState[key] } } }) // …省略其他}// 然后在setupRenderEffect調(diào)用render的時(shí)候,改變當(dāng)前的this執(zhí)行,執(zhí)行為instance.proxyfunction setupRenderEffect(instance: any, vnode: any, container: any) { // 獲取到vnode的子組件,傳入proxy進(jìn)去 const { proxy } = instance const subtree = instance.render.call(proxy) // …省略其他}復(fù)制代碼

                通過上面的操作,從render中this.xxx獲取setup返回對(duì)象的內(nèi)容就ok了,接下來(lái)處理el

                需要在mountElement中,創(chuàng)建節(jié)點(diǎn)的時(shí)候,在vnode中綁定下,el,并且在setupStatefulComponent 中的代理對(duì)象中判斷當(dāng)前的key

                // 代理對(duì)象進(jìn)行修改 instance.proxy = new Proxy({}, { get(target, key){ // 判斷當(dāng)前的key是否存在于instance.setupState當(dāng)中 if(key in instance.setupState){ return instance.setupState[key] }else if(key === ‘$el’){ return instance.vnode.el } } }) // mount中需要在vnode中綁定el function mountElement(vnode: any, container: any) { // 創(chuàng)建元素 const el = document.createElement(vnode.type) // 設(shè)置vnode的el vnode.el = el //…… 省略其他 }復(fù)制代碼

                看似沒有問題吧,但是實(shí)際上是有問題的,請(qǐng)仔細(xì)思考一下,mountElement是不是比setupStatefulComponent 后執(zhí)行,setupStatefulComponent執(zhí)行的時(shí)候,vnode.el不存在,后續(xù)mountelement的時(shí)候,vnode就會(huì)有值,那么上面的測(cè)試用例肯定是報(bào)錯(cuò)的,$el為null

                解決這個(gè)問題的關(guān)鍵,mountElement的加載順序是 render -> patch -> mountElement,并且render函數(shù)返回的subtree是一個(gè)vnode,改vnode中上面是mount的時(shí)候,已經(jīng)賦值好了el,所以在patch后執(zhí)行下操作

                function setupRenderEffect(instance: any, vnode: any, container: any) { // 獲取到vnode的子組件,傳入proxy進(jìn)去 const { proxy } = instance const subtree = instance.render.call(proxy) patch(subtree, container) // 賦值vnode.el,上面執(zhí)行render的時(shí)候,vnode.el是null vnode.el = subtree.el}復(fù)制代碼

                至此,上面的測(cè)試用例就能ok通過啦!

                實(shí)現(xiàn)on+Event注冊(cè)事件

                在vue中,可以使用onEvent來(lái)寫事件,那么這個(gè)功能是怎么實(shí)現(xiàn)的呢,咋們一起來(lái)看看

                測(cè)試用例

                test(‘測(cè)試on綁定事件’, () => { let count = 0 console.log = jest.fn() const app = createApp({ render() { return h(‘p’, { class: ‘container’, onClick() { console.log(‘click’) count++ }, onFocus() { count– console.log(1) } }, ‘123’); } }); const appDoc = document.querySelector(‘#app’) app.mount(appDoc); const container = document.querySelector(‘.container’) as HTMLElement; // 調(diào)用click事件 container.click(); expect(console.log).toHaveBeenCalledTimes(1) // 調(diào)用focus事件 container.focus(); expect(count).toBe(0) expect(console.log).toHaveBeenCalledTimes(2) })復(fù)制代碼

                分析

                在本功能的測(cè)試用例中,可以分析以下內(nèi)容:

              10. onEvent事件是在props中定義的
              11. 事件的格式必須是 on + Event的格式
              12. 解決問題:

                這個(gè)功能比較簡(jiǎn)單,在處理prop中做個(gè)判斷, 屬性是否滿足 /^on[A-Z]/i這個(gè)格式,如果是這個(gè)格式,則進(jìn)行事件注冊(cè),但是vue3會(huì)做事件緩存,這個(gè)是怎么做到?

                緩存也好實(shí)現(xiàn),在傳入當(dāng)前的el中增加一個(gè)屬性 el._vei || (el._vei = {}) 存在這里,則直接使用,不能存在則創(chuàng)建并且存入緩存

                編碼

                在mountElement中增加處理事件的邏輯 const { props } = vnode for (let key in props) { // 判斷key是否是on + 事件命,滿足條件需要注冊(cè)事件 const isOn = (p: string) => p.match(/^on[A-Z]/i) if (isOn(key)) { // 注冊(cè)事件 el.addEventListener(key.slice(2).toLowerCase(), props[key]) } // … 其他邏輯 el.setAttribute(key, props[key]) }復(fù)制代碼

                事件處理就ok啦

                父子組件通信——props

                父子組件通信,在vue中是非常常見的,這里主要實(shí)現(xiàn)props與emit

                測(cè)試用例

                test(‘測(cè)試組件傳遞props’, () => { let tempProps; console.warn = jest.fn() const Foo = { name: ‘Foo’, render() { // 2. 組件render里面可以直接使用props里面的值 return h(‘p’, { class: ‘foo’ }, this.count); }, setup(props) { // 1. 此處可以拿到props tempProps = props; // 3. readonly props props.count++ } } const app = createApp({ name: ‘App’, render() { return h(‘p’, { class: ‘container’, }, [ h(Foo, { count: 1 }), h(‘span’, { class: ‘span’ }, ‘123’) ]); } }); const appDoc = document.querySelector(‘#app’) app.mount(appDoc); // 驗(yàn)證功能1 expect(tempProps.count).toBe(1) // 驗(yàn)證功能3,修改setup內(nèi)部的props需要報(bào)錯(cuò) expect(console.warn).toBeCalled() expect(tempProps.count).toBe(1) // 驗(yàn)證功能2,在render中可以直接使用this來(lái)訪問props里面的內(nèi)部屬性 expect(document.body.innerHTML).toBe(`1123`) })復(fù)制代碼

                分析

                根據(jù)上面的測(cè)試用例,分析props的以下內(nèi)容:

              13. 父組件傳遞的參數(shù),可以給到子組件的setup的第一個(gè)參數(shù)里面
              14. 在子組件的render函數(shù)中,可以使用this來(lái)訪問props的值
              15. 在子組件中修改props會(huì)報(bào)錯(cuò),不允許修改
              16. 解決問題:

                問題1: 想要在子組件的setup函數(shù)中第一個(gè)參數(shù),使用props,那么在setup函數(shù)調(diào)用的時(shí)候,把當(dāng)前組件的props傳入到setup函數(shù)中即可 問題2: render中this想要問題,則在上面的那個(gè)代理中,在加入一個(gè)判斷,key是否在當(dāng)前instance的props中 問題3: 修改報(bào)錯(cuò),那就是只能讀,可以使用以前實(shí)現(xiàn)的api shallowReadonly來(lái)包裹一下既可

                編碼

                1. 在setup函數(shù)調(diào)用的時(shí)候,傳入instance.props之前,需要在實(shí)例上掛載propsexport function setupComponent(instance) { // 獲取props和children const { props } = instance.vnode // 處理props instance.props = props || {} // ……省略其他 } //2. 在setup中進(jìn)行調(diào)用時(shí)作為參數(shù)賦值 function setupStatefulComponent(instance: any) { // ……省略其他 // 獲取組件的setup const { setup } = Component; if (setup) { // 執(zhí)行setup,并且獲取到setup的結(jié)果,把props使用shallowReadonly進(jìn)行包裹,則是只讀,不能修改 const setupResult = setup(shallowReadonly(instance.props)); // …… 省略其他 }}// 3. 在propxy中在加入判斷 instance.proxy = new Proxy({}, { get(target, key){ // 判斷當(dāng)前的key是否存在于instance.setupState當(dāng)中 if(key in instance.setupState){ return instance.setupState[key] }else if(key in instance.props){ return instance.props[key] }else if(key === ‘$el’){ return instance.vnode.el } } })復(fù)制代碼

                做完之后,可以發(fā)現(xiàn)咋們的測(cè)試用例是運(yùn)行沒有毛病的

                組件通信——emit

                上面實(shí)現(xiàn)了props,那么emit也是少不了的,那么接下來(lái)就來(lái)實(shí)現(xiàn)下emit

                測(cè)試用例

                test(‘測(cè)試組件emit’, () => { let count; const Foo = { name: ‘Foo’, render() { return h(‘p’, { class: ‘foo’ }, this.count); }, setup(props, { emit }) { // 1. setup對(duì)象的第二個(gè)參數(shù)里面,可以結(jié)構(gòu)出emit,并且是一個(gè)函數(shù) // 2. emit 函數(shù)可以父組件傳過來(lái)的事件 emit(‘click’) // 驗(yàn)證emit1,可以執(zhí)行父組件的函數(shù) expect(count.value).toBe(2) // 3 emit 可以傳遞參數(shù) emit(‘clickNum’, 5) // 驗(yàn)證emit傳入?yún)?shù) expect(count.value).toBe(7) // 4 emit 可以使用—的模式 emit(‘click-num’, -5) expect(count.value).toBe(2) } } const app = createApp({ name: ‘App’, render() { return h(‘p’, {}, [ h(Foo, { onClick: this.click, onClickNum: this.clickNum, count: this.count }) ]) }, setup() { const click = () => { count.value++ } count = ref(1) const clickNum = (num) => { count.value = Number(count.value) + Number(num) } return { click, clickNum, count } } }) const appDoc = document.querySelector(‘#app’) app.mount(appDoc); // 驗(yàn)證掛載 expect(document.body.innerHTML).toBe(`1`) })復(fù)制代碼

                分析

                根據(jù)上面的測(cè)試用例,可以分析出:

              17. emit 的參數(shù)是在父組件的props里面,并且是以 on + Event的形式
              18. emit 作為setup的第二個(gè)參數(shù),并且可以結(jié)構(gòu)出來(lái)使用
              19. emit 函數(shù)里面是觸發(fā)事件的,事件名稱,事件名稱可以是小寫,或者是 xxx-xxx的形式
              20. emit 函數(shù)的后續(xù)可以傳入多個(gè)參數(shù),作為父組件callback的參數(shù)
              21. 解決辦法: 問題1: emit 是setup的第二個(gè)參數(shù),那么可以在setup函數(shù)調(diào)用的時(shí)候,傳入第二個(gè)參數(shù) 問題2: 關(guān)于emit的第一個(gè)參數(shù),可以做條件判斷,把xxx-xxx的形式轉(zhuǎn)成xxxXxx的形式,然后加入on,最后在props中取找,存在則調(diào)用,不存在則不調(diào)用 問題3:emit的第二個(gè)參數(shù),則使用剩余參數(shù)即可

                編碼

                // 1. 在setup函數(shù)執(zhí)行的時(shí)候,傳入第二個(gè)參數(shù) const setupResult = setup(shallowReadonly(instance.props), { emit: instance.emit });// 2. 在setup中傳入第二個(gè)參數(shù)的時(shí)候,還需要在實(shí)例上添加emit屬性哦export function createComponentInstance(vnode) { const instance = { // ……其他屬性 // emit函數(shù) emit: () => { }, } instance.emit = emit.bind(null, instance); function emit(instance, event, …args) { const { props } = instance // 判斷props里面是否有對(duì)應(yīng)的事件,有的話執(zhí)行,沒有就不執(zhí)行,處理emit的內(nèi)容,詳情請(qǐng)查看源碼 const key = handlerName(capitalize(camize(event))) const handler = props[key] handler && handler(…args) } return instance}復(fù)制代碼

                到此就圓滿成功啦!

                鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
                用戶投稿
                上一篇 2022年6月19日 15:13
                下一篇 2022年6月19日 15:14

                相關(guān)推薦

                • 短視頻策劃內(nèi)容的3個(gè)要點(diǎn)(短視頻策劃內(nèi)容怎么做)

                  短視頻在制作時(shí),內(nèi)容框架非常重要。如果直奔主題,然后結(jié)束,聚卓告訴你,這樣的短視頻已經(jīng)過時(shí)了。現(xiàn)在的短視頻需要框架的,但不是任何框架,它需要一種易于理解和消化的框架。而且,現(xiàn)在大多…

                  2022年11月27日
                • 存儲(chǔ)過程語(yǔ)法(sql server存儲(chǔ)過程語(yǔ)法)

                  今天小編給各位分享存儲(chǔ)過程語(yǔ)法的知識(shí),其中也會(huì)對(duì)sql server存儲(chǔ)過程語(yǔ)法進(jìn)行解釋,如果能碰巧解決你現(xiàn)在面臨的問題,別忘了關(guān)注本站,現(xiàn)在開始吧! oracle存儲(chǔ)過程基本語(yǔ)法…

                  2022年11月26日
                • 全民K歌升級(jí)新版本7.0之后,有哪些隱藏功能?

                  作者:高百烈來(lái)源:知乎 這個(gè)功能,舊版并沒有,要升級(jí)到全新的全民K歌7.0版本才能發(fā)現(xiàn)。 作為朋友圈當(dāng)代K歌之王,我費(fèi)了不少功夫才搶到內(nèi)測(cè)版本。有一說(shuō)一,全民K歌的路子真的很野,新…

                  2022年11月25日
                • 上手Reno8 Pro體驗(yàn)跨屏互聯(lián) 實(shí)在太方便!

                  11月已經(jīng)來(lái)到了月底,在手機(jī)品牌又要推出新一年度的新品手機(jī)之前,我們來(lái)點(diǎn)評(píng)一下今年令人驚喜的產(chǎn)品。如OPPO的Reno8 Pro系列,該系列搭載雙芯影像配置獲得了很多消費(fèi)者的認(rèn)可。…

                  2022年11月25日
                • 小紅書平臺(tái)的一些機(jī)制及玩法詳解(小紅書玩法有哪些)

                  關(guān)于小紅書 一:小紅書平臺(tái)的一些機(jī)制 1. 筆記內(nèi)容的CES評(píng)分機(jī)制 2. 筆記流量入口與長(zhǎng)尾效應(yīng) 二:小紅書優(yōu)質(zhì)筆記的特點(diǎn)(分維度、類型分析) 1.筆記的本身架構(gòu)組成 維度 2.…

                  2022年11月25日
                • 博客營(yíng)銷的3大優(yōu)勢(shì)解析(博客營(yíng)銷怎么做)

                  不知不覺已經(jīng)寫了24篇文章,加上這篇是第25篇了,都是自己這幾年來(lái)用過的營(yíng)銷方法,如果遇到有些不懂的,我會(huì)咨詢我的朋友和同事幫忙,盡量讓每一篇有價(jià)值,哪怕是對(duì)大家有一點(diǎn)點(diǎn)幫助也行,…

                  2022年11月25日
                • 什么是內(nèi)容營(yíng)銷策略如何策劃一套成功的內(nèi)容營(yíng)銷策略

                  很多時(shí)候,營(yíng)銷人員會(huì)在創(chuàng)作營(yíng)銷內(nèi)容時(shí)感到沮喪,這也是很多企業(yè)至今沒用好數(shù)字化營(yíng)銷工具的重要原因之一。 舉個(gè)例子,您可能會(huì)花上數(shù)小時(shí)期待制作一些令人驚嘆的東西,實(shí)際卻是得到很少的受眾…

                  2022年11月25日
                • 直播帶貨詳細(xì)腳本(直播文案策劃怎么寫)

                  短視頻運(yùn)營(yíng)策劃方案怎么寫?涉及哪幾個(gè)方面? 我在網(wǎng)上看到好多千篇一律的文章,關(guān)于【短視頻運(yùn)營(yíng)策劃方案】這一塊,基本都是在講賬號(hào)的內(nèi)容本身。 你內(nèi)容做得再好,卻不掌握算法的規(guī)律,能有…

                  2022年11月25日
                • 閑魚上怎么賣東西快?閑魚賺錢必知技巧(怎么在閑魚里面賣東西)

                  自從閑魚這個(gè)平臺(tái)出現(xiàn)以后,隨著這些年的發(fā)展,閑魚也成為了很多人開店賺錢的重要途徑。一些新人也想在閑魚上試試水,看看能不能賺到錢。如今閑魚上既可以賣二手閑置物品,也可以賣一些新品,那…

                  2022年11月25日
                • 淘寶直播帶貨技巧分享(淘寶直播復(fù)制粘貼技巧)

                  淘寶是大家喜歡的購(gòu)物平臺(tái),不少人會(huì)自己在淘寶選購(gòu),也會(huì)在主播直播間購(gòu)買,價(jià)格更實(shí)惠,現(xiàn)在很多商家都會(huì)開通直播帶貨功能,增加訂單量,那么淘寶直播帶貨有哪些?下面小編為大家?guī)?lái)淘寶直播…

                  2022年11月24日

                聯(lián)系我們

                聯(lián)系郵箱:admin#wlmqw.com
                工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息