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中的数据行,这就是数据驱动的精髓

 

posted on 2018-10-12 11:24  晴天的故事  阅读(9058)  评论(0编辑  收藏  举报