日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

引言

<<往期回顧>>

  1. vue3源碼分析——rollup打包monorepo
  2. vue3源碼分析——實現(xiàn)組件的掛載流程
  3. vue3源碼分析——實現(xiàn)props,emit,事件處理等

本期來實現(xiàn), slot——插槽,分為普通插槽,具名插槽,作用域插槽,所有的源碼請查看

正文

在 模板中使用插槽的方式如下:

<todo-button>
  Add todo
</todo-button>
復(fù)制代碼

在template中的內(nèi)容最終會被complie成render函數(shù),render函數(shù)里面會調(diào)用h函數(shù)轉(zhuǎn)化成vnode,在vnode的使用方法如下:

render() {
    return h(TodoButton, {}, this.$slots.default)
  },
復(fù)制代碼

看完slots的基本用法,一起來實現(xiàn)個slots,方便自己理解slots的原理哦!

實現(xiàn)基本的用法

使用slots的地方是this.slots,并且調(diào)用的屬性是default,那么slots,并且調(diào)用的屬性是default,那么slots則是一個對象,對象里面有插槽的名稱,如果使用者沒有傳遞,則可以通過default來進(jìn)行訪問

測試用例

attention!!! 由于測試的是dom,需要先寫入html等,在這里需要先創(chuàng)建對應(yīng)的節(jié)點

 let AppElement: Element;
  beforeEach(() => {
    appElement = document.createElement('div');
    appElement.id = 'app';
    document.body.appendChild(appElement);
  })
  afterEach(() => {
    document.body.innerHTML = '';
  })
復(fù)制代碼

本案例的測試正式開始

 test('test basic slots', () => {
 // 子組件Foo
    const Foo = {
      name: 'Foo',
      render() {
        return h('div', { class: 'foo' }, [h('p', {}, this.count), renderSlots(this.$slots)]);
      }
    }

    const app = createApp({
      render() {
        return h('div', { class: 'container' }, [
          h(Foo, { count: 1 }, { default: h('div', { class: 'slot' }, 'slot1') }),
          h(Foo, { count: 2 }, { default: [h('p', { class: 'slot' }, 'slot2'), h('p', { class: 'slot' }, 'slot2')] }),
        ])
      }
    })

    const appDoc = document.querySelector('#app')
    app.mount(appDoc);
    // 測試掛載的內(nèi)容是否正確
    const container = document.querySelector('.container') as HTMLElement;
    expect(container.innerHTML).toBe('<div class="foo"><p>1</p><div><div class="slot">slot1</div></div></div><div class="foo"><p>2</p><div><p class="slot">slot2</p><p class="slot">slot2</p></div></div>'
    )
  })
復(fù)制代碼

需求分析

通過上面的測試用例,可以分析以下內(nèi)容:

  1. 父組件使用子組件傳入插槽的方式是在h的第三個參數(shù),并且傳入的是一個對象,value的值可以是對象,或者是數(shù)組
  2. 子組件中使用插槽的時候,是在this.$slots中獲取的
  3. 并且還實現(xiàn)了一個renderSlot的方法,renderSlot是將this.$slots調(diào)用h轉(zhuǎn)變?yōu)関node

問題解決:

  1. 需要在綁定在this上面,那就在setupStatefulComponent函數(shù)代理中加入判斷,傳入的$slots ;
  2. 判斷$slot是否在組件的代理中,然后代理需要把slots綁定在instance上面并且綁定值的時候需要把傳入的對象統(tǒng)一轉(zhuǎn)成數(shù)組;
  3. renderSlot方法調(diào)用了h函數(shù),把一個數(shù)據(jù)轉(zhuǎn)成vnode

編碼實現(xiàn)

// 需要把$slots綁定在this上面,那就需要在代理里面在加入一個判斷即可
function setupStatefulComponent(instance: any) {
  // 代理組件的上下文
  instance.proxy = new Proxy({  }, {
      get(target,key){
       // 省略其他
       else if(key in instance.slots){
         return instance.slots[key]
       }
      }
  })
}

// 接下里在instance上面加上slots屬性
export function setupComponent(instance) {
  // 獲取props和children
  const { props, children } = instance.vnode

  // 處理props
  
  const slots = {}
  for (const key in children) {
      slots[key] = Array.isArray(children[key]) ? children[key] : [children[key]]
  }
  instance.slots = slots
  
  // ……省略其他
  }
  
  // 最后還需要使用renderSlot函數(shù)
  export function renderSlots(slots) {
    const slot = slots['default']
       if (slot) {
        return createVNode('div', {}, slot)
      }
}
復(fù)制代碼

通過上面的編碼,測試用例就可以完美通關(guān)啦

具名插槽

具名插槽就是,插槽除了可以有多個,并且除了default外,可以加入其他的名字,具體請看測試用例

測試用例

 test('測試具名插槽', () => {
    const Foo = {
      name: 'Foo',
      render() {
        return h('div', { class: 'foo' },
          [
            renderSlots(this.$slots, 'header'),
            h('div', { class: 'default' }, 'default'),
            renderSlots(this.$slots, 'footer')
          ]
        );
      }
    }

    const app = createApp({
      name: 'App',
      render() {
        return h('div', { class: 'container' }, [h(Foo, {}, {
          header: h('h1', {}, 'header'),
          footer: h('p', {}, 'footer')
        })])
      }
    })

    const appDoc = document.querySelector('#app')
    app.mount(appDoc);

    const container = document.querySelector('.container') as HTMLElement

    expect(container.innerHTML).toBe('<div class="foo"><div><h1>header</h1></div><div class="default">default</div><div><p>footer</p></div></div>')
  })
復(fù)制代碼

分析

通過上面測試用例,發(fā)現(xiàn)以下內(nèi)容:

  1. renderSlot傳入第二個參數(shù),然后可以獲取對于的slots

問題解決

直接在renderSlot里面?zhèn)魅氲诙€參數(shù)即可

編碼

  // 最后還需要使用renderSlot函數(shù)
  export function renderSlots(slots, name = 'default') {
    const slot = slots[name]
       if (slot) {
        return createVNode('div', {}, slot)
      }
}
復(fù)制代碼

這一步是不是比較簡單,相對起前面來說,正所謂,前面考慮好了,后面就舒服,接下來實現(xiàn)作用域插槽

作用域插槽

作用域插槽是,每個slot里面可以傳入數(shù)據(jù),數(shù)據(jù)只在當(dāng)前的slot有效,具體請看測試用例

測試用例

test('測試作用域插槽', () => {
    const Foo = {
      name: 'Foo',
      render() {
        return h('div', { class: 'foo' },
          [
            renderSlots(this.$slots, 'header', { children: 'foo' }),
            h('div', { class: 'default' }, 'default'),
            renderSlots(this.$slots, 'footer')
          ]
        );
      }
    }

    const app = createApp({
      name: 'App',
      render() {
        return h('div', { class: 'container' }, [h(Foo, {}, {
          header: ({ children }) => h('h1', {}, 'header ' + children),
          footer: h('p', {}, 'footer')
        })])
      }
    })

    const appDoc = document.querySelector('#app')
    app.mount(appDoc);

    const container = document.querySelector('.container') as HTMLElement

    expect(container.innerHTML).toBe('<div class="foo"><div><h1>header foo</h1></div><div class="default">default</div><div><p>footer</p></div></div>')

  })
復(fù)制代碼

需求分析

通過上面的測試用例,分析出以下內(nèi)容:

  1. 傳入插槽的時候,傳入一個函數(shù),函數(shù)可以拿到子組件傳過來的參數(shù)
  2. renderSlots可以傳入第三個參數(shù)props, 用于接收子組件往父組件傳入的參數(shù)

問題解決:

  1. 問題1: 只需要在傳入插槽的時候進(jìn)行一下判斷,如果是函數(shù)的話,需要進(jìn)行函數(shù)執(zhí)行,并且傳入?yún)?shù)
  2. 問題2: 也是對傳入的內(nèi)容進(jìn)行判斷,函數(shù)做傳入?yún)?shù)處理

編碼

// 在renderSlot里面?zhèn)魅氲谌齻€參數(shù)

export function renderSlots(slots, name = 'default', props = {}) {
  const slot = slots[name];

  if (slot) {
    if (isFunction(slot)) {
      return createVNode('div', {}, slot(props))
    }
    return createVNode('div', {}, slot)
  }
}

// initSlot時候,需要進(jìn)行函數(shù)判斷

 const slots = {}
  // 遍歷children
  for (const key in children) {
  // 判斷傳入的是否是函數(shù),如果是函數(shù)的話,需要進(jìn)行執(zhí)行,并且傳入?yún)?shù)
    if (isFunction(children[key])) {
      slots[key] = (props) => Array.isArray(children[key](props)) ? children[key](props) : [children[key](props)]
    } else {
      slots[key] = Array.isArray(children[key]) ? children[key] : [children[key]]
    }
  }

  instance.slots = slots
復(fù)制代碼

到此,整個測試用例就可以完美通過啦!

分享到:
標(biāo)簽:vue3
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定