通過UI庫深入了解Vue的插槽的使用技巧

Vue官網(wǎng)對于插槽的介紹比較簡略备燃,插槽本身也比較“燒腦”色解,很容易看暈,我就一直沒看懂仅财,直到 使用了element-plus的組件的插槽叶摄。
其實我們可以換一個角度來理解插槽属韧,就會豁然開朗了。

技術(shù)棧

  • vite
  • vue3
  • element-plus

從父子組件的傳值開始

父子組件傳值可以通過 prosp + emit 來實現(xiàn)蛤吓,雖然 props 可以傳遞各種類型宵喂,但是卻不能傳遞組件(包括HTML),這樣靈活度就差了一些会傲。
那么怎么辦呢锅棕?為了提高靈活性,Vue 提供了插槽功能淌山。

插槽可以分為:插槽裸燎、具名插槽、作用域插槽
如果不明所以的話泼疑,可以換一種名稱:匿名插槽德绿、命名插槽、可傳參插槽

匿名插槽

如何理解插槽呢移稳?可以先看看div蕴纳,div是一個容器,里面可以放各種HTML標(biāo)簽个粱,同時也可以放各種組件古毛。

那么我們可以把div內(nèi)部的標(biāo)簽、組件視為插槽內(nèi)容都许,同理稻薇,我們也可以把 select 內(nèi)部的 option 也視為插槽內(nèi)容。

我們可以用匿名插槽的方式梭稚,寫一個my-div的組件颖低。

  • 子組件 ./comp/my-div.vue
  <div style="margin: 10px;padding: 10px; border:1px solid orange;">
    匿名插槽:
    插槽前<br><br>
    <slot>沒有設(shè)置插槽</slot>
    <br><br>
    插槽后
  </div>

子組件設(shè)置一個 slot 標(biāo)簽,slot 可以理解為是一種“插值”弧烤,表示父組件的插槽在這個位置被渲染,然后在其前后可以加入子組件自己的內(nèi)容蹬敲。

slot 里面是“備用內(nèi)容”暇昂,如果父組件沒有設(shè)置插槽的話,“備用內(nèi)容”會被渲染伴嗡,否則會被忽略急波。

  • 父組件

我們看一看在父組件里面的使用情況:

  import myDiv from './comp/my-div.vue'
  匿名插槽<br>
  設(shè)置文本框作為插槽內(nèi)容:
  <my-div>
    <input type="text" placeholder="父組件的插槽">
  </my-div>
  <br>
  沒有設(shè)置插槽內(nèi)容:
  <br>
  <my-div></my-div>
  • 看看效果
匿名插槽

這樣就實現(xiàn)了一個簡單的具有插槽功能的組件,當(dāng)然這個組件是為了插槽而插槽瘪校,并沒有沒有實際意義澄暮。

那么插槽在實際項目里可以有哪些作用呢?我們可以參考一下UI庫的組件阱扬,他們有很多插槽的實際應(yīng)用泣懊,比如 el-input、el-table等麻惶。

具名插槽馍刮。

“具名”是個啥意思?感覺用“命名插槽”更好理解一些窃蹋。

  • 如果一個組件只有一個插槽卡啰,那么不用寫名稱,Vue會使用默認(rèn)名稱:default 警没。
  • 如果一個組件有多個插槽的話匈辱,那么就需要起名來區(qū)分不同的插槽。

el-input 提供了prefix杀迹、suffix亡脸、prepend、append四個插槽,就是采用了命名插槽的方式梗掰。

我們來看一下官網(wǎng)的例子:

    <el-input v-model="input1" placeholder="Please input">
      <template #prepend>Http://</template>
    </el-input>
    <el-input v-model="input2" placeholder="Please input">
      <template #append>.com</template>
    </el-input>
  • # 是 v-lot: 的簡寫形式嵌言,類似于 “v-bind:” 簡寫為 “:”,“v-on:” 簡寫為 “@”
  • prepend 在文本框的前面放置一個插槽及穗,比如 http://
  • append 在文本框的后面方式一個插槽摧茴,比如 .com

這樣可以方便輸入URL地址。其實如果 append 放置一個 el-autocomplete 的話埂陆,可以更靈活的設(shè)置域名后綴苛白。

手寫一個命名插槽

還是手寫一個命名插槽,看一下子組件的實現(xiàn)方式焚虱。

  • 子組件 ./comp/my-div-name.vue
  <div style="margin: 10px;padding: 10px; border:1px solid rgba(61, 67, 155, 0.692);">
    <slot name="header">我來組成頭部</slot>
    插槽中間內(nèi)容
    <slot name="footer">我來組成結(jié)尾</slot>
  </div>

實現(xiàn)具名插槽的方式很簡單购裙,用 name 屬性設(shè)置插槽的名稱即可。

  • 父組件的調(diào)用
  import myDivName from './comp/my-div-name.vue'
  <my-div-name>
    <template v-slot:header>
      <h1>這是頭部</h1>
    </template>
    <template #footer>
      <p>這是結(jié)尾</p>
    </template>
  </my-div-name>

父組件需要用 template 限定具名插槽內(nèi)容的范圍鹃栽,我們來看看效果:

具名插槽

作用域插槽

插槽是父組件的躏率,不是子組件的,父組件可以完全操作插槽里的組件民鼓。
但是子組件只能規(guī)定插槽的渲染位置薇芝,其他的就不能操作了,這樣的話還是有些不夠靈活丰嘉,于是出現(xiàn)了作用域插槽夯到。

作用域插槽的目的是解決父組件、子組件饮亏、插槽之間的數(shù)據(jù)通訊的問題耍贾。

還是看看UI庫組件 el-table 的插槽 。

父組件設(shè)置列表數(shù)據(jù)路幸,傳遞給子組件荐开,子組件渲染 table 表格。
為了更靈活劝赔,組件提供了自定義列的功能誓焦,采用的就是作用域的插槽。

看一下官網(wǎng)示例:

<el-table :data="tableData" style="width: 100%">
    <el-table-column label="Date" width="180">
      <template #default="scope">
        <span style="margin-left: 10px">{{ scope.row.date }}</span>
      </template>
    </el-table-column>
</table>
  • scope 就是子組件傳遞出來的數(shù)據(jù)集合着帽,包含row杂伟、column、$index等屬性仍翰。
  const tableData = reactive([
        {
          date: '2016-05-03',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-02',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-04',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-01',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        }
  ])

  • tableData:父組件定義數(shù)據(jù)列表赫粥,通過 data 屬性傳遞給子組件。

這里的 scope 的數(shù)據(jù)流程是這樣的:父組件 =》子組件 =》插槽予借。

為啥要繞一圈呢越平?雖然父組件可以直接給插槽設(shè)置值频蛔,但是由于 tr 是循環(huán)出來的,父組件無法獲知循環(huán)到哪一行了秦叛,所以需要子組件告知循環(huán)行數(shù)晦溪,這個信息就是通過作用域插槽來實現(xiàn)的,我們可以做一個簡單的示例挣跋。

手?jǐn)]一個簡單的作用域插槽

  • 子組件 ./comp/my-table.vue
  <div>
    <table>
      <tr>
        <th>標(biāo)題一</th>
        <th>標(biāo)題二</th>
        <th>自定義</th>
      </tr>
      <tr v-for="(item, index) in data"
        :key="index"
      >
        <td>{{item.t1}}</td>
        <td>{{item.t2}}</td>
        <td>
          <slot name="td"
            :row="item"
            :$index="index"
          ></slot>
        </td>
      </tr>
    </table>
  </div>

第三列設(shè)置一個具名插槽三圆,通過row、$index 傳遞數(shù)據(jù)避咆。

  const props = defineProps({
    data: Array
  })

設(shè)置一個屬性舟肉,接收列表數(shù)據(jù)。

  • 父組件調(diào)用
  import myTable from './comp/my-table.vue'

  const data = reactive([
    { t1: '11', t2: '12', t3: '13' },
    { t1: '21', t2: '22', t3: '23' },
    { t1: '31', t2: '32', t3: '33' }
  ])
  <my-table :data="data">
    <template #td="scope">
      自定義列:{{scope}}
    </template>
  </my-table>

可以看到數(shù)據(jù)的傳遞查库。

子組件的插槽路媚,先起個名字,就叫做“td”好了樊销,不要糾結(jié)名稱整慎,俺有起名困難癥。

然后用 row 屬性傳遞行的數(shù)據(jù)现柠,用 $index 傳遞遍歷到第幾行的數(shù)據(jù)院领。

這樣一個簡單的作用域插槽就搞定了。當(dāng)然只是一個示例够吩,還是沒有啥實際意義。

那么有實際意義的是什么樣子的呢丈氓?還記得標(biāo)題嗎周循?我可不是標(biāo)題黨,彩蛋馬上就來万俗。

片尾彩蛋

現(xiàn)在流行用 json 來渲染組件湾笛,還是用 el-table 舉例,我們可以定義一個 json闰歪,來描述表格列的情況嚎研,比如:

{
  "itemMeta": [
    {
      "prop": "name",
      "label": "姓名",
      "width": 140,
      "align": "center",
      "header-align": "center"
    },
    {
      "prop": "age",
      "label": "年齡",
      "width": 140,
      "align": "center",
      "header-align": "center"
    },
    {
      "prop": "mobile",
      "label": "電話",
      "width": 140,
      "align": "center",
      "header-align": "center"
    },
    {
      "prop": "url",
      "label": "URL",
      "width": 140,
      "align": "center",
      "header-align": "center"
    }
  ]
}

然后遍歷 el-table-colmun 設(shè)置屬性,這樣就可以實現(xiàn)動態(tài)渲染 table 的功能库倘。
這樣雖然很方便临扮,但是自定義列呢?如果不支持插槽的話教翩,那么靈活性就差了一些杆勇。

魚和熊掌能不能兼得呢?既然都寫到這里了饱亿,那么肯定可以兼得蚜退。

做一個默認(rèn)規(guī)則

自定義列的插槽名稱格式:td_{字段名稱}闰靴。
也就是說 td_開頭的視為自定義列的插槽,加上前綴可以避免和 el-table 自帶的具名插槽沖突钻注。

然后封裝一下 el-table

建立一個組件 ./comp/my-table-json.vue

  import { useSlots } from 'vue'
  
  const props = defineProps({
    colInfo: Object,
    data: Array
  })
 
  // 獲取插槽信息
  const slots = useSlots()
  // 獲取列的描述信息
  const colInfo = props.colInfo

  // 檢查插槽蚂且,設(shè)置名稱
  colInfo.forEach(col => {
    const _slotName = 'td_' + col.prop
    if (typeof slots[_slotName] === 'function') {
      // 有插槽
      col.slotName = _slotName
    } else {
      // 沒有插槽
      col.slotName = ''
    }
  })

定義屬性,接收數(shù)據(jù)和列的描述幅恋。
然后獲取插槽的信息杏死,設(shè)置列是否需要加載插槽。

  <el-table :data="data" style="width: 100%">
    <template
      v-for="(item, index) in colInfo"
      :key="index"
    >
      <!--不帶插槽的列-->
      <el-table-column
        v-if="item.slotName == ''"
        v-bind="item"
      >
      </el-table-column>
      <!--帶插槽的列-->
      <el-table-column
        v-else
        v-bind="item"
      >
        <template #default="scope">
          <slot :name="item.slotName" v-bind="scope"></slot>
        </template>
      </el-table-column>
    </template>
  </el-table>

遍歷列的描述信息佳遣,判斷是否需要加載插槽识埋,如果需要插槽的話,設(shè)置插槽并且傳遞 scope 數(shù)據(jù)零渐。

父組件的調(diào)用

父組件就簡單多了窒舟。

  UI庫的 table 的二次封裝
  不用自定義列:
  <my-table-json :data="data" :colInfo="colInfo">
  </my-table-json>

  使用自定義列:
  <my-table-json :data="data" :colInfo="colInfo">
    <template #td_url="{ row }">
      <a :href="row.url" target="blank">{{row.name}}</a>
    </template>
    <template #td_mobile="scope">
      手機(jī):{{scope.row.mobile}}
    </template>
  </my-table-json>

不需要自定義列的話,代碼可以更簡潔诵盼;
需要自定義列的話惠豺,也支持用插槽的方式實現(xiàn)。

  import myTableJson from './comp/my-table-json.vue'
  import meta from './grid.json'

  const colInfo = reactive(meta.itemMeta)

  const data = reactive([
    {
      name: '阿蒙',
      age: 18,
      mobile: '1399999991',
      url: 'https://naturefw.gitee.io/nf-rollup-ui-controller'
    },
    {
      name: '小李',
      age: 18,
      mobile: '1399999992',
      url: 'https://naturefw.gitee.io/nf-rollup-ui-controller/meta-base'
    },
    {
      name: '路飛',
      age: 18,
      mobile: '1399999993',
      url: 'https://naturefw.gitee.io/nf-rollup-ui-controller/meta-base'
    }
  ])

這樣就不用手?jǐn)] el-table-column 了风宁,交給子組件即可洁墙,同時還可以滿足自定義列的需求。

是不是即簡潔又靈活戒财。這個彩蛋還滿意吧热监。

看看效果:

json渲染 + 作用域插槽

在線演示

https://naturefw.gitee.io/nf-rollup-ui-controller/test-slot

源碼

https://gitee.com/naturefw/nf-rollup-ui-controller/tree/master/src/views/test/slot

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市饮寞,隨后出現(xiàn)的幾起案子孝扛,更是在濱河造成了極大的恐慌,老刑警劉巖幽崩,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苦始,死亡現(xiàn)場離奇詭異,居然都是意外死亡慌申,警方通過查閱死者的電腦和手機(jī)陌选,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹄溉,“玉大人咨油,你說我怎么就攤上這事±噻停” “怎么了臼勉?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長餐弱。 經(jīng)常有香客問我宴霸,道長囱晴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任瓢谢,我火速辦了婚禮畸写,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氓扛。我一直安慰自己枯芬,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布采郎。 她就那樣靜靜地躺著千所,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒜埋。 梳的紋絲不亂的頭發(fā)上淫痰,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音整份,去河邊找鬼待错。 笑死,一個胖子當(dāng)著我的面吹牛烈评,可吹牛的內(nèi)容都是我干的火俄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼讲冠,長吁一口氣:“原來是場噩夢啊……” “哼瓜客!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起竿开,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤忆家,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后德迹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡揭芍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年胳搞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片称杨。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡肌毅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出姑原,到底是詐尸還是另有隱情悬而,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布锭汛,位于F島的核電站笨奠,受9級特大地震影響袭蝗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜般婆,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一到腥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蔚袍,春花似錦乡范、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宇整,卻和暖如春瓶佳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背没陡。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工涩哟, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盼玄。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓贴彼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親埃儿。 傳聞我的和親對象是個殘疾皇子器仗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

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