鉴于还是有不少的同学搜到这篇文章,而这篇文章又是笔者早期写的,现在回过头来看发现有太多的不足,为避免误人子弟,重新撰写了一篇Vue slot 详解,有需要的同学可以去浏览一下。
百度上已经有很多的关于slot-scope的文章,但我感觉都是那些以前没学好,又回头学的人,他们都使用了.Vue文件,我觉得有点不适合初学者,所以我就写一篇适合初学者的。
先抛例程:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>组件之通过插槽(slot)分发内容</title><script src="你的vue地址,请下载最新版的"></script><script>ponent("test-slot",{// 插槽允许有默认内容template:`<div><strong>Error!</strong><slot></slot></div>`,data:function () {return {name:"perry"}}});// 具名插槽ponent("slot-name",{template:`<div><header><slot name="header"></slot></header><main><slot ></slot></main><footer><slot name="footer"></slot></footer></div>`});// 作用域插槽ponent("todo-list",{inheritAttrs:false,props:{todos:[Array,Object]},template:`<ul><li v-for="todo in todos" :key="todo.id" style="display: block;" ><slot :data="todo">{{todo.text}}</slot></li></ul>`});</script><style>li{list-style: none;display: inline-block;font-size: 25px;}</style></head><body><div id="app"><!--1、插槽内容 slot可以接收任何内容、代码,包括组件--><test-slot>Something bad happened.你是谁啊,我认识你吗</test-slot><test-slot>Something bad happened.<!--允许使用变量,但该变量必须是本作用域的才会生效,这里name的值将会是"岔道口"--><h3>标题{{name}}</h3><img src="img/flower.jfif" alt="图片无法显示"></test-slot><!--2、具名插槽 slo有一个特殊的属性name,可以使插槽具名--><slot-name><h3>劝学</h3><p>少壮不努力,老大徒伤悲</p><p>光阴似箭,日月如梭</p><template slot="header"><ul ><li>主页</li><li>关于时间</li><li>关于人生</li><li>关于未来</li></ul></template><p>你好啊</p><p slot="footer"><p>结尾</p><p>粤ICP备6545646456415</p></p></slot-name><!--使用作用域插槽,通过slot-scope实现--><todo-list :todos="todos"><template slot-scope="slotProps"><span v-if="slotProps.data.isTrue">√</span>{{slotProps.data.text}}</template></todo-list><!--使用ES结构语法--><todo-list :todos="todos"><template slot-scope="{data}"><span v-if="data.isTrue">√</span>{{data.text}}</template></todo-list> </div><script>new Vue({el:"#app",data:{name:"岔道口",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},],// slotProps:""}})</script></body></html>
例程中也写了普通插槽和具名插槽,这个我就不说,我就说一下作用域插槽。首先从“作用域插槽“”这个名字说起,本来你父组件的模板是无法使用到子组件模板中的数据,官网中有一句特别强调的话:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。这句话其实挺难理解,举个例子就知道了,
<test-slot>
Something bad happened.
<!--允许使用变量,但该变量必须是本作用域的才会生效,这里name的值将会是"岔道口"-->
<h3>标题{{name}}</h3>
<img src="img/flower.jfif" alt="图片无法显示">
</test-slot>
这是我上面的例子,也就是说这里name必须是当前组件里的,不能是test-slot这个组件内部的数据,这是它作用域限制,name这个数据属于父组件的作用域,所以只能是父组件的数据。但是slot-scope的出现却实现了父组件调用子组件内部的数据,子组件的数据通过slot-scope属性传递到了父组件
// 作用域插槽ponent("todo-list",{inheritAttrs:false,props:{todos:[Array,Object]},template:`<ul><li v-for="todo in todos" :key="todo.id" style="display: block;" >//这里的:data=“todo”就是讲子组件todo这个数据传给了父组件<slot :data="todo">{{todo.text}}</slot>//todo.text是默认值,父组件会把它覆盖掉</li></ul>`});
<!--使用作用域插槽,通过slot-scope实现--><todo-list :todos="todos"><template slot-scope="slotProps"><span v-if="slotProps.data.isTrue">√</span>{{slotProps.data.text}}</template></todo-list>
使用的时候就是这样,从子组件传过来的名为data值为todo的数据将会被slot-scope接收,需要注意的是,slot-scope接收的是一个对象,这里命名为slotProps,也就是说你传过来的data将会作为slotProps的一个属性,所以需要slotProps.data才能调用到data。前面说了,data的值为todo,todo是什么呢?todo是todos遍历出来的,所以是一个对象。
new Vue({el:"#app",data:{name:"岔道口",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},],// slotProps:""}});
我们已经理解了slot-scope特性,那么他有什么运用场景呢?我们可以试想一下,别人写好了一个组件,该组件已经封装好了,但他觉得,数据的显示风格让用户自己定义,就像我们上面,我们希望列表的样式由用户定义,比如我就加了一个√号。一开始最初的数据是我们传进去的,但数据必须进过子组件处理后,写组件那个人肯定希望显示的是处理后的数据。这时候slot-scope就无比重要了。