Vuejs之Component slot 插槽详解
Vuejs的component的数据进行了沙箱隔离,除js全局变量如Math, Date之类外无法访问用户自定义的变量,所以使用component写组件或嵌套组件时明白变量的访问非常重要
编译作用域
在看componnent的使用之前,来看下component编译作用域,明白作用域范围才能顺利写出想要的组件
假设我们有一个组件child-component,在父组件中代码如下:
<child-component> {{ message }} </child-component>
编译时message的作用域应该是父组件还是子组件呢,答案是父组件
父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译
Vue.component('child-component', { // 有效,因为是在正确的作用域内 template: '<div v-show="someChildProperty">Child</div>', data: function () { return { someChildProperty: true } } })
slot在component中有单slot,具名slot和作用域slot之分,先来看看最简单的单slot应用
单个slot
直接上代码,其中的name字段会在父组件中初始化并赋值
父组件 <div id="test"> <test-slot> <h3>{{name}}</h3> <p>Something bad happened.</p> </test-slot> </div>
组件 Vue.component("test-slot",{ // 插槽允许有默认内容 template: `<div> <strong>Error!</strong> <slot></slot> </div> `, data:function () { return { name:"perry" } } }); new Vue({ el:"#test" data:{name:"500 error"} }); 结果: <div> <strong>Error!</strong> <h3>500 error</h3> <p>Something bad happened.</p> </div>
具名slot
具名插槽比较有意思,在模板制定时非常好用,比如我们要写一个模板包含头尾和内容部分,希望在模板中定义好一部分公共的东西
具名slot通过name来管理多个slot的解析,其中没有name的slot会被归为default slot放到没有name的节点下面,default slot会无视散落在不同地方的html的位置,都将放到default slot的
模板位置中来
Vue.component("slot-name",{ template: `<div> <header> <slot name="header"></slot> </header> <main> <slot ></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> ` }); <slot-name> <h3>开始</h3>
<p>Default slot内容1</p> <template slot="header"> <ul> <li>主页</li> <li>分诊</li> <li>护理</li> <li>病历</li> </ul> </template> <template slot="footer"> <p>结尾</p> </template> </slot-name>
运行结果:
作用域slot
作用域插槽在解决需要动态生成字符串模板时非常有用,特别针对控件编写者
例如实现一个简单的datagrid控件,控件在页面component中相当于子控件,使用者希望只关注表头和每行数据业务上,直接上代码
控件代码 Vue.component("datagrid",{ props:{ data:null }, template:` <table> <thead> <slot name="headslot"></slot> </thead> <tbody> <tr v-for="item in data"> <slot name="bodyslot" :item="item">{{item.text}</slot> </tr> </tbody> </table> ` }); 在父组件中(页面上)使用如下: <datagrid :data="todos"> <template slot="headslot"> <tr> <td>id</td> <td>text</td> <td>isTrue</td> </tr> </template> <template slot="bodyslot" slot-scope="{item}"> <td>{{item.id}}</td> <td>{{item.text}}</td> <td>{{item.isTrue}}</td> </template> </datagrid>
如上代码,简单的datagrid就实现了,在父组件中只需要在head中指定table的head具体内容,对应的body中tr的每个td的字段绑定,其它交给控件处理
其中数据源是datagrid中的data属性,与slot通信是通过slot-scope来实现数据域传递,这点非常关键
控件中 :item="item" 与父组件slot-scope="{item}" 完成数据访问的传递,其中slot-scope="{item}"语句也可以通过"slot-scope="slotProps"来实现数据传递,slotProps对像相当于当slot对象上
所有props属性的根,通过slotProps对象都能访问到
在js调用如下:
var vm = new Vue({ el:"#app", data:{ todos:[ {text:"A",id:1,isTrue:true}, {text:"B",id:2,isTrue:true}, {text:"C",id:3,isTrue:false}, {text:"D",id:4,isTrue:true}, ] } });
在data中的todos属性已经与页面的table形成了关联,只要todos属性发生变化,页面的table会自动更新tbody中的数据行,这就是数据驱动的精髓