背景知识:Vue.js的作用域插槽在第三方组件开发等用到的比较多。要理解作用域插槽,先要理解插槽,插槽说白了是父组件向子组件传值的一种方式。父组件向子组件传值的另一种方式是属性传值。
1 属性传值
最简单的例子,先看代码:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<title>Document</title>
<script src='lib/vue.js'></script>
</head>
<body>
<div id='app'>
<!-- 在父组件中使用子组件 -->
<!-- 2、元素(子组件)socom的绑定属性message获取父组件的msg变量值 -->
<soncom :message="msg"></soncom>
</div>
<script>
var soncom = {
// 3、子组件模板中显示属性message的值
template:'<div>{{message}}</div>',
// 1、子组件定义绑定属性message
props:['message']
};
var vm = new Vue({
el:'#app',
data:{
msg:'hello soncom!'
},
methods:{},
components:{
soncom
}
})
</script>
</body>
</html>
注意:
子组件名称最好不要使用驼峰命名,因为子组件作为html元素使用,元素名自动转为小写,会报未知的自定义元素错误。如果非得用驼峰命名子组件名称,在子组件作为html元素使用时,要把名称转换为短线连接符写法。
示例:
写法1:
<div id='app'>
<soncom :message="msg"></soncom>
</div>
...
var sonCom = {
// 3、子组件模板中显示属性message的值
template:'<div>{{message}}</div>',
// 1、子组件定义绑定属性message
props:['message']
};
components:{
soncom:sonCom
}
写法2:
<div id='app'>
<son-com :message="msg"></son-com>
</div>
...
var sonCom = {
// 3、子组件模板中显示属性message的值
template:'<div>{{message}}</div>',
// 1、子组件定义绑定属性message
props:['message']
};
components:{
sonCom
}
代码分析:
2 组件插槽
理解了属性传值,组件插槽传值就更简单了,可以理解为对属性传值的封装或抽象。
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<title>Document</title>
<script src='lib/vue.js'></script>
</head>
<body>
<div id='app'>
<!--
与一般用法不同,父组件在子组件标签间放了数据,这个数据会出现在子组件插槽中,
即slot占位符的位置。这种更加简单的传值方式,可以理解为是对属性传值的封装或者简化。
-->
<soncom>{{ msg }}</soncom>
</div>
<script>
var soncom = {
// 组件插槽<slot></slot>是子组件的占位符,相当于在子组件挖了一个坑
template:'<div>来自父组件的消息:<slot></slot></div>'
};
var vm = new Vue({
el:'#app',
data:{
msg: 'Hello,soncom!'
},
methods:{},
components:{
soncom
}
})
</script>
</body>
</html>
代码分析:
3 具名插槽
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<title>Document</title>
<script src='lib/vue.js'></script>
</head>
<body>
<div id='app'>
<soncom>
<h1 slot="header">标题信息</h1>
<p>主体信息1</p>
<p>主体信息2</p>
<p slot="footer">底部信息</p>
</soncom>
</div>
<script>
var soncom = {
template:`
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
}
var vm = new Vue({
el:'#app',
data:{},
methods:{},
components:{
soncom
}
})
</script>
</body>
</html>
代码解析:
显示结果:
实际上,通过插槽,可以隐藏子组件的细节,只是把传递数据的接口暴露给父组件,然后由vue把父组件传递的数据放到子组件固定的位置,最终由浏览器把子组件渲染到DOM中。
通过插槽可以把父组件传递的信息固定地显示在子组件定义好的插槽位置上,即使在父组件使用子组件时打乱顺序:
<div id='app'>
<soncom>
<!-- <div slot="header">
<p>标题信息1</p>
<p>标题信息2</p>
</div> -->
<h1 slot="header">标题信息</h1>
<p>主体信息1</p>
<p>主体信息2</p>
<p slot="footer">底部信息</p>
<p>其它信息</p>
</soncom>
</div>
最终渲染结果,也会把数据填充到对应的插槽中:
功能扩展:
通过以下方式,也可以让具名插槽显示多行信息:
<div id='app'>
<soncom>
<div slot="header">
<p>标题信息1</p>
<p>标题信息2</p>
</div>
<p>主体信息1</p>
<p>主体信息2</p>
<p slot="footer">底部信息</p>
</soncom>
</div>
可以使用Vue提供的临时标签:
<div id='app'>
<soncom>
<template slot="header">
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主体信息1</p>
<p>主体信息2</p>
<p slot="footer">底部信息</p>
</soncom>
</div>
4 作用域插槽
与普通插槽的区别:
通俗点讲:
普通插槽,子组件定义的坑里没内容,由父组件向坑里填内容;作用域插槽,子组件的坑里有内容,父组件可以看到坑里的内容,并可以对内容的显示外观施加影响,甚至改变具体内容。
作用域插槽,把子组件内容更多地封装在了子组件内部,仅需要父组件通过子组件暴露的接口,对子组件内容及外观施加有限影响,而不需要关心子组件的细节,这更能体现封装的特点。
作用域插槽,为第三方组件的开发,提供了很大的便利。
具体区别:
1、作用域不同:
普通插槽是父组件向子组件传递数据;作用域插槽是子组件把自己的数据暴露给父组件,父组件可以操纵子组件的数据,两种插槽的作用域不同。
2、内容定义和显示方式不同:
普通插槽显示内容在父组件中定义,子组件只有空的插槽标签,标签间即使有内容,也会被覆盖;
作用域插槽显示内容在子组件的插槽标签间定义,父组件只能间接对内容或显示样式施加影响。
3、使用方式不同:
子组件的插槽标签需要定义绑定属性;父组件使用子组件要在template标签中通过指定slot-scop属性,引入子组件插槽的绑定属性。
父组件改变子组件的数据示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<title>Document</title>
<script src='lib/vue.js'></script>
</head>
<body>
<div id='app'>
<soncom>
<template slot-scope="datafromson">
{{ datafromson.sonpro = '来自父组件的数据' }}
</template>
</soncom>
</div>
<script>
var soncom = {
template:`
<div>
<slot :sonpro="sonData">{{ sonData }}</slot>
</div>
`,
data(){
return {
sonData:'来自子组件的数据'
}
}
}
var vm = new Vue({
el:'#app',
data:{},
methods:{},
components:{
soncom
}
})
</script>
</body>
</html>
代码分析:
显示结果:
父组件影响子组件内容外观的示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<title>Document</title>
<script src='lib/vue.js'></script>
</head>
<body>
<div id='app'>
<soncom>
<template slot-scope="datafromson">
<!-- 通过对子组件绑定属性的修饰,改变子组件属性关联数据的显示外观 仅影响子组件插槽标签内的显示 -->
<strong style="color:red">{{ datafromson.sonpro }}</strong>
</template>
</soncom>
</div>
<script>
var soncom = {
template:`
<div>
<!-- 父组件仅影响到这里slot标签内的显示外观 -->
<slot :sonpro="sonData">{{ sonData }}</slot>
<!-- 不会影响到子组件其它元素的显示 -->
<p v-text="sonData"></p>
</div>
`,
data(){
return {
sonData:'来自子组件的数据'
}
}
}
var vm = new Vue({
el:'#app',
data:{},
methods:{},
components:{
soncom
}
})
</script>
</body>
</html>
更复杂一点的示例:结合属性传值使用
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<title>Document</title>
<script src='lib/vue.js'></script>
</head>
<body>
<div id='app'>
<soncom :son-fruit-list="fruitList">
<template slot-scope="sonPro">
<!-- <strong style="color:red">{{ sonPro.fruit.name }}</strong> -->
<!-- 也可以通过条件判断精细控制列表中的某一行 -->
<strong v-if="sonPro.fruit.id == 2" style="color:red">{{ sonPro.fruit.name }}</strong>
</template>
</soncom>
</div>
<script>
var soncom = {
template:`
<div>
<ul>
<li :key="item.id" v-for="item in sonFruitList">
<slot :fruit="item">{{ item.name }}</slot>
</li>
</ul>
</div>
`,
// 属性名最好不要驼峰命名,否则在元素标签内作为属性名使用时要修改为连接符命名
props:['sonFruitList']
}
var vm = new Vue({
el:'#app',
data:{
fruitList:[
{ id:1,name:'apple' },
{ id:2,name:'orange' },
{ id:3,name:'banana' }
]
},
methods:{},
components:{
soncom
}
})
</script>
</body>
</html>