首页 > 编程笔记 > JavaScript笔记
Vue slot插槽完全攻略
项目中有很多组件显示的内容,在不同的地方使用的时候,有些内容需要变化,这时候可以用插槽内容分发功能实现。
例如,开发人员可以定义一个组件,专门用来显示提示信息。只是显示的信息包含在插槽 slot 元素中,满足在不同的地方,提示的内容不一样。
MessageAlert 组件中包含了 slot 插槽,运行的时候,就会将组件元素包含的内容动态地渲染到视图中,代码如下:
当然,分发到 slot 中的内容,可以包含动态内容,此时动态的变量只能使用父组件里面的 property,代码如下:
示例代码如下:
开发人员可以在 slot 元素中,使用 v-bind 指令将对象绑定后,传递到组件外面,如:slot v-bind:userAttr="user".../slot 就是将子元素中的 user 数据,通过 v-bind 指令绑定到 userAttr 属性上。
userAttr 属性又称为 slot property,在父组件中,可以在 template 元素中使用指定的插槽属性变量操作 userAttr 绑定的 user 数据,代码如下:
v-slot:header 可以被重写为 # header,代码如下:
例如,开发人员可以定义一个组件,专门用来显示提示信息。只是显示的信息包含在插槽 slot 元素中,满足在不同的地方,提示的内容不一样。
MessageAlert 组件中包含了 slot 插槽,运行的时候,就会将组件元素包含的内容动态地渲染到视图中,代码如下:
... <div id="app"> <message-alert>普通</message-alert> <message-alert>警告</message-alert> <message-alert>错误</message-alert> </div> <script> // 定义MessageAlert组件 const MessageAlert = { template: ` <div> <strong>消息:</strong> <slot></slot> </div> ` }; // 注册全局MessageAlert组件 Vue.component('message-alert', MessageAlert); // 创建Vue实例并挂载到#app元素 const vm = new Vue({ el: '#app' }); </script> ...
插槽的缺省内容和编译作用域
在实战中,slot 中往往希望有缺省值,实现起来很简单,直接在子元素的 slot 之间包含缺省值即可,调用的时候,如果有新值,则自动会将缺省值覆盖,如 slot 缺省值 /slot。当然,分发到 slot 中的内容,可以包含动态内容,此时动态的变量只能使用父组件里面的 property,代码如下:
... <div id="app"> <!-- 使用缺省值 --> <slot-dft-content></slot-dft-content> <!-- 使用静态内容 --> <slot-dft-content>提交</slot-dft-content> <!-- 编译作用域 --> <!-- parentValue是在父组件vm的data中定义的 --> <slot-dft-content>{{parentValue}}</slot-dft-content> <!-- 如果放开下面这行,会报异常,因为sonValue是在子组件SlotDftContent的data中定义的 --> <!-- <slot-dft-content>{{sonValue}}</slot-dft-content> --> </div> <script> // 注册SlotDftContent组件 Vue.component('slot-dft-content', { template: ` <button type="submit"> <slot>提交</slot> </button> `, data: function() { return { sonValue: 'sonContent' } } }); // 创建Vue实例并挂载到#app元素 const vm = new Vue({ el: '#app', data: { parentValue: 'parentContent' } }); </script> ...
具名插槽
有时候在一个子元素中需要定义多个插槽,使用子元素的时候,将不同内容分发到不同插槽中去。这时候,开发人员可以使用 slot 的 name 属性,给每个 slot 命名(没有命名的为 default),使用子组件时,用 template 元素包含要分发到每个 slot 的内容,使用“v-slot:目标插槽的名称”来指定对应的分发插槽。示例代码如下:
... <div id="app"> <base-layout> <!-- 具名插槽:header --> <template v-slot:header>头</template> <!-- 具名插槽:footer --> <template v-slot:footer>尾</template> <!-- 默认插槽内容 --> <p>默认内容</p> </base-layout> </div> <script> // 注册BaseLayout组件 Vue.component('base-layout', { template: ` <div class="container"> <header> <!-- header具名插槽 --> <slot name="header"></slot> </header> <main> <!-- 默认插槽 --> <slot></slot> </main> <footer> <!-- footer具名插槽,当前为空 --> <slot name="footer"></slot> </footer> </div> ` }); // 创建Vue实例并挂载到#app元素 const vm = new Vue({ el: '#app' }); </script> ...
作用域插槽
有时候,需要获取子元素的数据,进行整理后再分发给插槽,这时候可以使用作用域插槽。开发人员可以在 slot 元素中,使用 v-bind 指令将对象绑定后,传递到组件外面,如:slot v-bind:userAttr="user".../slot 就是将子元素中的 user 数据,通过 v-bind 指令绑定到 userAttr 属性上。
userAttr 属性又称为 slot property,在父组件中,可以在 template 元素中使用指定的插槽属性变量操作 userAttr 绑定的 user 数据,代码如下:
<div id="app"> <!-- 使用作用域插槽 --> <range-slot> <template v-slot:default="slotProps"> {{ slotProps.userAttr.userName }}整理好了 </template> </range-slot> </div> <script> // 定义RangeSlot组件 Vue.component('range-slot', { template: ` <div> <!-- 作用域插槽,向父组件传递userAttr属性 --> <slot v-bind:userAttr="user"></slot> </div> `, data: function() { return { user: { name: '李四', userName: 'lisi' } } } }); // 创建Vue实例并挂载到#app元素 const vm = new Vue({ el: '#app' }); </script> ...
动态插槽名
动态指令参数也可以用在 v-slot 上,用来定义动态的插槽名。特别要注意的是,这里不支持驼峰命名的变量名,语法如下:<range-slot> <template v-slot:[动态的插槽名称]> ... </template> </range-slot>
具名插槽的缩写
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容(v-slot:)替换为字符 #。v-slot:header 可以被重写为 # header,代码如下:
<base-layout> <!-- Header template --> <template #header> <h1>Here might be a page title</h1> </template> <!-- Main content --> <p>A paragraph for the main content.</p> <p>And another one.</p> <!-- Footer template --> <template #footer> <p>Here's some contact info</p> </template> </base-layout>然而,和其他指令一样,该缩写只在有参数的时候才可用,这意味着以下语法是无效的:
<!-- 这样会触发一个警告 --> <current-user #="{ user }"> {{ user.firstName }} </current-user>如果开发人员希望使用缩写,则必须始终以明确的插槽名取而代之,代码如下:
<current-user #default="{ user }"> {{ user.firstName }} </current-user>