[DAY14]跟 Vue.js 认识的30天 - Vue 模组插槽(`slot`)

最近在 youtube 找到一个学习 webpack 很好用的教学影片,所以正努力的学习 webpack 中,一个不小心就太沉迷在新技术的练习了,导致有点忽略 Vue.js 的笔记,再次跟自己喊话,一定要做完 Vue.js 的系列(之後还有 Vuex 跟 Vuetify 呢!)。

另外说明一下,我笔记的顺序是我自己认为这样的顺序比较好理解,所以会跟文件中的说明顺序有差异。

新增插槽并设定插槽内容

在设定模组时,会透过 template 来决定模组在页面上要显示的内容,而我们可以在 template 中加入 <slot> 标签来设定模组标签内容的位置,并且在网页显示的时候,模组标签的内容会取代该 <slot> 标签。

模组标签里的内容可以是纯文本、 HTML 标签或其他模组等等。

<basic-slot>
  Next
  <i class="fas fa-angle-right"></i>
</basic-slot>
<script>
Vue.component("basic-slot", {
  // <slot></slot>指定外层模组标签内容放置的位置
  // 可放入纯文本、html标签、其他模组
  template: `<div>
    <div>
      <slot></slot>
    </div>
  </div>`
});
</script>

https://ithelp.ithome.com.tw/upload/images/20201121/20127553dEyhheGxI5.png

插槽预设值

直接在 <slot> 标签内写定预设内容,如果模组标签内没有任何内容,那麽该 component 的画面显示就是该预设内容。

<!-- 模组标签没有任何内容 -->
<basic-slot></basic-slot>
<script>
Vue.component("basic-slot", {
  // 在 <slot> 标签中设定模组画面内容的预设值
  template: `<div>
    <div>
      <slot>
        Preview
      </slot>
    </div>
  </div>`
});
</script>

具名插槽

如果要将不同内容的插槽资料分类,就可以透过 <slot name="slotName">v-slot:slotName 的来命名插槽及决定不同内容所放置的位置。

如果 <slot 标签未命名的话,预设的名字是 default ,所以在模组标签可以透过 v-slot:default 或是不写 v-slot:default 来找到那个未命名的 <slot 标签。

须注意 v-slot 只能添加在 <template> 上。除非是只有默认插槽的状况才可写在模组标签上

<name-slot>
  <!-- 为了最後显示时不要有太多标签,所以使用template来避免出现太多元素-->
  <!-- 对应模组内名为 footer 的插槽位置-->
  <template v-slot:footer>
    <h4>Here is footer</h4>
  </template>
  <!-- 对应模组内名为 header 的插槽位置-->
  <template v-slot:header>
    <h4>Here is header</h4>
  </template>
  <!-- 对应模组内名为 container 的插槽位置-->
  <template v-slot:container>
    <h4>Here is container</h4>
  </template>
  <!-- 对应模组内未命名或名为 default 的插槽位置-->
  <template>
    <h4>Here is default</h4>
  </template>
  <!-- <template>
  <h4>Here is default</h4>
  </template> 跟下面意思相同-->
  <template v-slot:default>
    <h4>Here is default</h4>
  </template>
</name-slot>
<script>
Vue.component("name-slot", {
  // 在 <slot> 标签中利用 attribute 来命名插槽,并决定不同名字的插槽内容在画面显示後的位置
  template: `<div>
    <div>
      <slot name="header"></slot>
      <slot name="container"></slot>
      <slot name="footer"></slot>
      <slot></slot>
    </div>
  </div>`
});
</script>

只有下面这种情况才可以将 v-slot:default 写在模组标签上,另外要注意也不能将 default 改成其他名字喔!

<name-slot v-slot:default>
  <h4>Here is default</h4>
</name-slot>
<script>
Vue.component("default-slot", {
  template: `<div>
    <slot></slot>
  </div>`
});
</script>

模组标签(外层)及插槽标签(内层)内容的作用域

在模组标签中的内容只能取得该层的资料,而在模组内的 <slot> 标签也只能取得模组内的资料,简单来说就是在哪设定要取资料就是只能取得那一层的资料。

模组标签内容的作用域(外层)

  <scoped-slot name="Michelle">
    外层的 data 资料:{{ user.name }},
    模组内的 props 资料: {{ name }}
    模组内的 data 资料: {{ user2.name }}
  </scoped-slot>
<script>
Vue.component("scoped-slot", {
  template: `<div>
    <div>
      <slot></slot>
    </div>
  </div>`,
  data() {
    return {
      user2: {
        name: "Linda",
        age: 9
      }
    };
  }
});
const vm = new Vue({
  el: "#vm",
  data: {
    user: {
      name: "Celeste",
      age: 18
    }
  }
});
</script>

以上面为例,会发现只要是想取得模组内的 data 或是 props 都会是失败的(会出错),但是取得外层(模组标签那一层)资料是成功的。

插槽标签的作用域(内层)

  <scoped-slot name="Michelle"></scoped-slot>
<script>
Vue.component("scoped-slot", {
  props:[ 'name' ],
  template: `<div>
    <div>
      <slot>
        模组内的 props 资料: {{ name }},
        模组内的 data 资料: {{ user2.name }},
        外层的 data 资料:{{ user.name }}
      </slot>
    </div>
  </div>`,
  data() {
    return {
      user2: {
        name: "Linda",
        age: 9
      }
    };
  }
});
const vm = new Vue({
  el: "#vm",
  data: {
    user: {
      name: "Celeste",
      age: 18
    }
  }
});
</script>

如果是在 <slot> 中设定预设值,会发现在 <slot> 内只能取得该模组内的资料(如 data 或是 props ),如果想要取得外层的 data 资料会出错。

模组标签内容(外层)使用插槽标签动态绑定值(内层)

如上面所说,一般情况下外层无法取得内层的资料,但是可以透过在 <slot name="slotName" v-bind:slotPropName="slotPropValue"> 标签上设定 Attribute 来动态绑定值以将内层的值往外传到外层去,而外层透过 v-slot:slotName="customName" 或是 ES6语法 v-slot:slotName="{ slotPropName }" 来决定适用哪一个作用域。

使用 v-slot:slotName="customName"

设定 customName 来接收从 <slot> 接收的动态绑定值,像是将所有动态绑定的值放入 customName 这个物件中。

<!-- 用 slotProps 来接收从 slot 传进来的值 -->
<v-bind-slot v-slot:default="customName">
  姓{{ slotProps.user.firstName }},
  年龄{{ slotProps.user.age }}
  喜欢水果{{ slotProps.fruit[0] }}
</v-bind-slot>
<script>
Vue.component("v-bind-slot", {
  template: `<div>
    <div>
      <slot :user="user" :fruit="fruit">
      {{ user.lastName }}
      </slot>
    </div>
  </div>`,
  data() {
    return {
      user: {
        lastName: "Linda",
        firstName: "KUO",
        age: 9
      },
      fruit: ["apple", "banana", "orange"]
    };
  }
});
</script>

使用 v-slot:slotName="{ slotPropName }"

透过 ES6 解构语法来将动态绑定值一一传入。

<!-- 用解构语法来接收从 slot 传进来的值 -->
<v-bind-slot v-slot:default="{ user, fruit }">
  姓{{ slotProps.user.firstName }},
  年龄{{ slotProps.user.age }}
  喜欢水果{{ slotProps.fruit[0] }}
</v-bind-slot>
<script>
Vue.component("v-bind-slot", {
  template: `<div>
    <div>
      <slot :user="user" :fruit="fruit">
      {{ user.lastName }}
      </slot>
    </div>
  </div>`,
  data() {
    return {
      user: {
        lastName: "Linda",
        firstName: "KUO",
        age: 9
      },
      fruit: ["apple", "banana", "orange"]
    };
  }
});
</script>

作用域

只有在自己那个具名插槽中有绑定的 Attribute 才可以在外层相同名字的 v-slot 作用域中使用。

<v-bind-slot>
  <template v-slot:default="{ user, fruit }">
    姓{{ user.firstName }},
    年龄{{ user.age }}
    喜欢水果{{ fruit[1] }}
    <br/>
  </template>
  <template v-slot:cellphone="{ cellphone, fruit }">
  <!-- user未在名为 cellphone 的插槽中绑定,所以不可以在这个作用域使用 -->
    姓{{ user.firstName }},
    手机{{ cellphone[2] }}
    喜欢水果{{ fruit[1] }}
  </template>
</v-bind-slot>
<script>
Vue.component("v-bind-slot", {
  template: `<div>
    <div>
      <slot name="default" :user="user" :fruit="fruit"></slot>
      <slot name="cellphone" :cellphone="cellphone" :fruit="fruit"></slot>
    </div>
  </div>`,
  data() {
    return {
      user: {
        lastName: "Linda",
        firstName: "KUO",
        age: 9
      },
      fruit: ["apple", "banana", "orange"],
      cellphone: ["iphone", "samsung", "oppo"]
    };
  }
});
</script>

动态插槽名

可以透过 v-slot:[动态插槽名] 来动态可以模组标签内容的位置。
另须注意动态插槽名需全为小写,因为 Vue 会自动将该动态插槽名转为小写,而导致在外层找不到该笔资料( dynamicslotname!==dynamicSlotName )。

<dynamic-slot>
  <!--动态插槽名要全为小写-->
  <!--error-->
  <template v-slot:[dynamicSlotName]>
    I am {{dynamicSlotName}}
  </template>
  <template v-slot:[lowercaseslotname]>
    I am {{lowercaseslotname}}
  </template>
</dynamic-slot>
<script>
Vue.component("dynamic-slot", {
  template: `<div>
      <slot name="header">Here is header</slot>
      <br/>
      <slot name="container">Here is container</slot>
      <br/>
      <slot name="footer">Here is footer</slot>
      <slot></slot>
  </div>`
});
const vm = new Vue({
  el: "#vm",
  data: {
    user: {
      name: "Celeste"
    },
    dynamicSlotName: "header",
    lowercaseslotname: "footer"
  }
});
</script>

v-slot 缩写

可以将以 # 来缩写 v-slot ,像是 v-slot:header 可以缩写成 #headerv-slot:footer={ user } 可以缩写成 #footer={ user } ,另外要注意使用缩写字符时一定要有参数,例如 #footer={ user }footer 即为参数,不可以只有 #={ user } ,这样会出错,如果是预设值的状况可以写成 #default={ user }

Demo:DAY14 | 跟 Vue.js 认识的30天 - Vue 模组插槽(slot)

参考资料:

Vue.js - 插槽


<<:  我遇到的RWD网页难题

>>:  〖WordPress主题〗ASTRA释出「AGENCY BUNDLE」头500名购买只要$149的超级优惠

每个人都该学的30个Python技巧|技巧 20:Python容器—集合(set)(字幕、衬乐、练习)

昨天的字典进阶操作你有没有学会呢?如果还是不熟悉记得要再去复习呀~ 今天又是一个新容器了,集合跟串列...

CSS Of Norton Antivirus By InstallNSetup.Com

Online threats, such as spyware, phishing and iden...

D29-(9/29)-广达(2382)-有肉松之称的电脑代工厂

注:发文日和截图的日期不一定是同一天,所以价格计算上和当日不同,是很正常的。 声明:这一系列文章并无...

Day25:NavigationView

前言 前面两天刻了两个 view, 现在要用 Navigation 来把它们连接起来。 实作 在 R...

Episode 7 - 四则运算

范例档案 GitHub Repo: https://github.com/kaochenlong/...