title: vue-模板语法
date: 2020-09-16 21:27:17
tags: Vue
categories: 前端
githubBlog:Click Me
1.Vue模板语法
1.1插值操作
mustache语法{{expr}}
expr可以是变量,常量,表达式,会自动根据Vue中的data进行渲染
<div id="app">
<h2>{{message}}</h2>
<h2>{{message+message}}</h2>
<h2>{{v*2}}</h2>
<h2>{{firstName+" "+lastName}}</h2>
<h2>{{firstName}}{{lastName}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: "test",
firstName: "hello",
lastName: "world",
v: 100
}
})
</script>
v-once
此时,数据只从vue中得到数据初始化渲染一次,之后该组件(标签内)的数据不会随着data中的数据变化
<!--v-once作用域是整个标签-->
<div id="app">
<h2 v-once>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: "test",
}
})
</script>
v-html
类似于innerHtml(),把数据进行解析后显示
v-text
类似于innerText(),把原数据显示出来
v-pre
忽略mustache直接显示出两个大括号
v-cloak
在Vue渲染html标签前,让被渲染的元素隐藏,不显示原生的{ {} }等语法
1.2动态绑定属性
v-bind
为标签的属性绑定Vue的data值,绑定后属性值将根据这个data动态改变
v-bind可用冒号:
代替
<div id="app">
<!-- 绑定属性的操作 -->
<img v-bind:src="imgurl" alt="">
<a :href="url">百度一下</a>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
imgurl: 'https://www.baidu.com/img/flexible/logo/pc/result.png',
url: 'www.baidu.com'
}
})
</script>
动态参数
https://cn.vuejs.org/v2/guide/syntax.html#%E5%8A%A8%E6%80%81%E5%8F%82%E6%95%B0
v-bind绑定class
通过对象绑定
<h2 :class={ 类名1:boolean,类名2:boolean}>vue</h2>
<h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
isActive: true,
isLine: false
}
})
</script>
通过一个对象的形式,若类名对应的值(vue中的data)为true值会为标签绑定class,false则不会,可与原生的class共存。若对象过于复杂,可通过methods或计算属性返回一个对象。
通过数组绑定
<h2 class="title" :class="[类名, 类名,···]">Hello World</h2>
<h2 class="title" :class="[active, line]">Hello World</h2>
类名可以是一个变量,常量,表达式,最终代表需要绑定的类名
v-bind绑定style
利用v-bind:style来绑定一些CSS内联样式。
通过对象绑定
<h1 :style="{属性名:属性值,属性名:属性值,···}">{{massage}}</h1>
<h1 :style="{color:colorStyle,'background-color':bkColorStyle,fontSize:fSize+'px'}">{{massage}}</h1>
属性名可以是小驼峰式,或者短横线分隔式(需单引号括起)
属性值可以是常量,变量,表达式
通过数组绑定
<div v-bind:style="[baseStyles, overridingStyles]"></div>
<script>
const vm = new Vue({
el: '#app',
data: {
//可以是驼峰式也可以短横线分隔式
baseStyles: {color:'red','background-color':'gray'},
overridingStyles: {fontSize:'20px'}
}
})
</script>
数组中放的是对象,每个对象代表一个css样式,本质上就是通过对象绑定
1.3方法与计算属性
**方法(methods):**类似于函数,只不过它属于指定的vue对象
**计算属性(computed):**本质上是属性,只是多了getter和setter方法(类似于bean),并且与方法methods相比,具备缓存功能,可以动态监听,当数据改变时才会调用getter
<div id="app">
<h2>{{fullName}}</h2>
<h2>{{fullName2}}</h2>
<!--方法调用必须加上括号(在事件监听时不用)-->
<h2>{{fullName3()} }</h2>
<h2>{{fullName4()} }</h2>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
firstName:'Alan',
lastName:'Turing'
},
computed:{
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
// 当执行vm.fullName = 'John Doe'会被调用
set: function (newValue) {
let names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
},
//当没有set方法时,可简化为如下,并且可省略 :function
fullName2:function() {
return (this.firstName+' '+this.lastName).toUpperCase();
}
},
methods:{
fullName3:function(){
return this.firstName+' '+this.lastName;
},
//可省略 :function
fullName4(){
return this.firstName+' '+this.lastName;
}
}
})
</script>
1.4侦听器和滤波器
侦听器:侦听vue实例中的数据变化,一旦数据发生变化就会执行回调函数
<script>
const vm = new Vue({
el: '#app',
data:{
val=100;
}
watch:{
//如果data中的val发生改变,就会执行下方函数
val:function(newVal,oldVal){
console.log(newVal+' '+oldVal);
}
})
</script>
滤波器:在输出内容时,给内容添加修饰。
<div id="app">
<!-- 最终显示 ¥100.00 -->
<span>总价:{{totalPrice |showPrice } }</span>
</div>
<script>
const vm = new Vue({
el: '#app',
data:{
totalPrice=100;
}
filters:{
showPrice(totalPrice){
return "¥"+totalPrice.toFixed(2);
}
})
</script>
1.5事件监听
v-on
- 作用:绑定事件监听器
- 缩写:@
- 预期:Function | Inline Statement | Object
- 参数:event
v-on:click="func1"
,引号内填写函数名,不带括号时,默认传入一个event,带括号时按正常情况传递参数,当需要传递事件对象时,参数为$event
<div id="app">
<!-- 点击后会输出mouseEvent-->
<button v-on:click="func1">按钮1</button>
<!-- 点击后会输出123 abc-->
<button @click="func2('123','abc')">按钮2</button>
<!-- 点击后会输出mouseEvent undefine-->
<button @click="func2">按钮3</button>
<!-- 点击后会输出123 mouseEvent-->
<button @click="func2('123',$event)">按钮4</button>
</div>
<script>
const vm = new Vue({
el: '#app',
methods:{
func1(param1){
alert("This is btn1:"+param1) ;
},
func2(param1,param2){
alert("This is btn2:"+param1+' '+param2) ;
}
}
})
</script>
v-on修饰符
- .stop - 调用 event.stopPropagation()。
- .prevent - 调用 event.preventDefault()。
- .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
- .native - 监听组件根元素的原生事件。
- .once - 只触发一次回调。
<!-- 阻止事件冒泡 -->
<button @click.stop="doThis">button</button>
<!-- 阻止默认行为发生,可省略doThis -->
<form @submit.prevent="doThis" action="url">
<input type="text">
<input type="submit">
</form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis">button</button>
<!-- 键修饰符,指定别名或键代码 -->
<input type="text" @keyup.enter="doThis" @keydown.10="doThis" >
<!-- 触发一次回调 -->
<button @click.once="doThis">button</button>
关于修饰符的知识还很欠缺,例如不同修饰符顺序引发不同的结果
一些键修饰别名:
.enter
.tab
.delete
(捕获 “删除” 和 “退格” 键).esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
1.6条件渲染
if、else if、else
这三个指令类似于if、else、else if。Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件。
<div id="app">
<h3 v-if="score>=90">Excellent</h3>
<h3 v-else-if="score>=80">Good</h3>
<h3 v-else-if="score>=60">Ok</h3>
<h3 v-else>Fail</h3>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data:{
score:90,
}
})
</script>
v-show
v-show
和v-if
的用法类似,都是决定一个页面是否被渲染。
区别:
v-if
当条件为false时,压根不会有对应的元素在DOM中。
v-show
当条件为false时,仅仅是将元素的display
属性设置为none
而已。
当需要在显示与隐藏之间切片很频繁时,使用`v-show`。
当只有一次切换时,通过使用`v-if`。
1.7列表渲染
v-for
<!--遍历数组-->
<li v-for="value in arr">{{value}}</li>
<li v-for="(value,index) in arr">{{value}}-{{index}}</li>
<!--遍历对象-->
<li v-for="value in obj">{{value}}</li>
<li v-for="(value,key) in obj">{{value}}-{{key}}</li>
<li v-for="(value,key,index) in obj">{{value}}-{{key}}-{{index}}</li>
数组响应式更新
Vue具有数据绑定的功能,因此当vue实例中的数据发生改变时,视图能做出改变响应。
对数组使用如下方法,都会使得视图发生更新
//函数的具体用法有待了解
let arr;
arr.push();
arr.pop();
arr.unshift();
arr.shift();
arr.splice();
arr.sort();
arr.reverse();
唯一需要注意的是使用如下方式1的数组下标赋值的方法,视图并不会发生更新
//方式1
let arr=[{key:value}];
arr[0]={key2:value2}; //视图并不更新
//方式2
let arr2={key:value};
arr2[0].key=value2; //本质上是修改对象的的属性内容,视图发生更新
//解决方式1视图不更新的两种方式
//通过splice方法为数组赋值,具体用法有待了解
//通过vue自带的方法为数组赋值
Vue.set(this.arr,index,value);
1.8两个练手小案例:
点击绑定样式和购物车
1.9v-model表单绑定
你可以用
v-model
指令在表单<input>
、<textarea>
及<select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。此外input中的value属性也支持v-bind动态绑定。
v-model原理
v-model其实是一个语法糖,它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。它的背后本质上是包含两个操作:
- 1.v-bind绑定一个value属性
- 2.v-on指令给当前元素绑定input事件
v-model
会忽略所有表单元素的value
、checked
、selected
attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的data
选项中声明初始值。
v-model
在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用
value
property 和input
事件;- checkbox 和 radio 使用
checked
property 和change
事件;- select 字段将
value
作为 prop 并将change
作为事件。
以下代码等价:
<input type="text" v-model="message">
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
文本
<div id="app">
<p>单行文本:Message is: { { message1 } }</p>
<input v-model="message1" placeholder="edit me">
<br>
<hr>
<span>多行文本: Multiline message is:</span>
<p style="white-space: pre-line;">{{ message2 } }</p>
<br>
<textarea v-model="message2" placeholder="add multiple lines"></textarea>
</div>
<script>
const vm = new Vue({
el: '#app',
data:{
message1:'' ,
message2:''
}
})
</script>
单选按钮
<div id="app">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: { { picked } }</span>
</div>
<script>
const vm = new Vue({
el: '#app',
data:{
picked: '',
}
})
</script>
原生的单选按钮中,不同的按钮需要通过一个相同的name来实现互斥,当使用v-model时,可通过绑定同一个value来实现互斥。
复选框
<h3>单个复选框</h3>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked } }</label>
<h3>多个复选框</h3>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: { { checkedNames } }</span>
<script>
const vm = new Vue({
el: '#app',
data:{
checked:undefined,//初始化时给一个true则默认会被选中,false或其它非布尔值默认不选
checkedNames: [],
}
})
</script>
选择框
<h3>单个选择框</h3>
<div>
<select v-model="option">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: { { option } }</span>
</div>
<h3>多个选择框</h3>
<div>
<select v-model="options" multiple style="width: 50px;">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: { { options } }</span>
</div>
<script>
const vm = new Vue({
el: '#app',
data:{
option:'',
options: [],
}
})
</script>
修饰符
lazy修饰符:
- 默认情况下,v-model默认是在input事件中同步输入框的数据的。
- 也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
- lazy修饰符可以让数据在失去焦点或者回车时才会更新:
number修饰符:
- 默认情况下,如果希望用户输入数字,原生的input提供了type="number"的用法,但是在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
- 因此可通过
<input v-model.number="age" type="number">
- 给
v-model
添加number
修饰符可以让在输入框中输入的内容自动转成数字类型:
trim修饰符:
- 如果要自动过滤用户输入的首尾空白字符,可以给
v-model
添加trim
修饰符:<input v-model.trim="msg">
2.组件化开发
组件是可复用的 Vue 实例,且带有一个名字,并且组件相对于Vue来说多了一些例如template模板等功能,template使得组件内部可封装某些标签数据。
2.1.组件使用流程
基本使用
- 创建组件对象(最新vue手册中已不使用Vue.extend()创建组件对象了)
- 调用Vue.component()方法注册组件
- 在Vue实例的作用范围内使用组件
<div id="app">
<!-- 3.在Vue实例挂载的标签范围内使用组件-->
<cpn></cpn><!-- 此标签最终会由cpnObj中的template进行渲染-->
</div>
<cpn></cpn>
<script>
//1.创建组件对象
//注意template最终必须由一个根标签包含,例如下方根标签为div
//其中template代表该组件对象的模板,该对象还可有data,methods等属性
//这是使用Vue.extend方式(过时)
//通过构造器创建组件对象
// const cpnObj = Vue.extend({
// template:`
// <div>
// <h3>我是标题</h3>
// <div>我是内容</div>
// </div>
// `
// });
//直接使用javascript对象
const cpnObj = {
template:`
<div>
<h3>我是标题</h3>
<div>我是内容</div>
</div>
`
};
//2.将组件对象注册为组件(组件标签,组件对象)
Vue.component('cpn',cpnObj);
const vm = new Vue({
el: '#app',
});
</script>
语法糖
直接在注册时传入对象参数
<script>
//将定义组件对象,并注册为组件(组件标签,组件对象)
Vue.component('cpn',{
template:`
<div>
<h3>我是标题</h3>
<div>我是内容</div>
</div>
`
});
</script>
2.2全局组件和局部组件
全局组件:
凡是以Vue.component()进行注册的都是全局组件,可以在任何挂载了Vue实例的标签内使用
局部组件:
在Vue实例中注册,或在组件中注册为子组件的组件都是局部组件,他们的作用范围限于各自注册的实例(组件)中。
一个组件对象可在不同的作用域中注册为不同的组件
代码实例:
<div id="app">
<cpn2></cpn2>
<cpn1></cpn1>
</div>
<script>
//这是子组件对象,可(同时)在vue实例(或组件)中注册
const cpnObj1 = {
template:`
<div>
<h3>标题1</h3>
</div>
`
};
//第一种:注册成为子组件:
//1.构造组件2对象时,把组件对象cpnObj1代表的组件添加为组件2的子组件
const cpnObj2 = {
template:`
<div style="color: #ff0000">
<h3>标题2</h3>
<cpn1></cpn1>
</div>
`,
components:{
//组件标签名:组件对象
cpn1:cpnObj1 //作用域仅在该组件中,(可用语法糖)
}
};
//2.将组件2对象注册为组件(组件标签,组件对象)
Vue.component('cpn2',cpnObj2); //全局组件:作用域在任何Vue实例中
//第二种:在Vue实例中注册
const vm = new Vue({
el: '#app',
components:{
//组件标签名:组件对象
cpn1:cpnObj1 //作用域仅在该Vue实例中(可用语法糖)
}
})
</script>
2.3分离模板
可将组件的template分离出来,单独写在一个标签中
使用script标签
使用script标签包裹template,并给定id,然后在组件对象中引用template的id即可
<script type="text/x-template" id="myCpn">
<div>
<h3>这是组件</h3>
</div>
</script>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
const cpnObj = Vue.extend()
const vm = new Vue({
el: '#app',
components:{
cpn: {
template:'#myCpn' //引用script中的id
}
}
})
</script>
使用template标签
与script标签相比,不用指定type=“text/x-template”,其余相同
<template id="myCpn">
<div>
<h3>这是组件</h3>
</div>
</template>
<script>
const cpnObj = Vue.extend()
const vm = new Vue({
el: '#app',
components:{
cpn: {
template:'#myCpn'
}
}
})
</script>
2.4组件data
问题引入:组件也是一个Vue实例,意味着组件也可以有自己的data,那么当组件标签使用多次时,每个组件标签引用的是同一个data,这样是明显不合理的,即在组件复用的时候,它们应该引用不同的data对象。
解决方法:Vue强制规定在组件中必须把data写成函数的形式,并且该函数返回一个对象来作为该组件的data,利用函数的作用域来让每个组件标签引用不同的数据对象。
每一次使用组件,function中都会有一个对象被创建并赋值给data
code
<div id="app">
<cpn></cpn>
</div>
<template id="myCpn">
<div>
<h3>这是组件:{{msg}}</h3>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
components:{
cpn:{
//此处的组件对象中的data使用的是函数
data(){
return {
msg:'message'
}
},
template: '#myCpn'
}
}
})
</script>
2.5父子组件的数据传递
子组件是无法访问父组件或Vue实例中的数据的,而在实际的开发中,常常需要把父组件的数据传递给子组件使用,因此Vue提供了父子组件间传递数据的方法。
值得注意的是:当一个组件1在某个组件2内部注册为局部组件后不代表该组件1就是组件2的子组件了,只能说明组件1是作用在组件2内部的局部组件。
判断是否为一个组件的子组件看的是在template标签中是否嵌套使用了组件标签,例如在挂载了vue实例的div中使用组件标签cpn,此时cpn就是vue的子组件
父组件=>子组件 props
父组件(Vue实例)把数据传递给子组件中的props属性。
-
在子组件中的props中声明自己的属性(prop),用于接收父组件传递的数据
-
在使用该子组件标签时,为该子组件的prop属性赋值或动态绑定变量
<div id="app">
<!-- 在使用组件时给attr1 prop赋值-->
1:<cpn attr1="attribute1"></cpn>
<!-- 在使用组件时给attr2 prop动态绑定为vue实例中的val-->
2:<cpn :attr2="val"></cpn>
</div>
<template id="myCpn">
<div>
<h3>这是属性1:{{attr1}}</h3>
<h3>这是属性2:{{attr2}}</h3>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data:{
val:'attribute2'
},
components:{
cpn:{
template: '#myCpn',
//声明属性,有数组和对象两种形式,此处是数组
props:['attr1','attr2'] //属性以字符串形式书写
}
}
});
</script>
当需要对传入子组件中的prop进行类型验证或默认值等操作时,就需要采用对象语法,实例如下
<div id="app">
<!-- 在使用组件时给attr1 prop赋值,类型必须是Number因此必须动态绑定-->
1:<cpn :attr1="num"></cpn>
<!-- 在使用组件时给attr2 prop动态绑定为vue实例中的val-->
2:<cpn :attr2="val"></cpn>
</div>
<template id="myCpn">
<div>
<h3>这是属性1:{{attr1}}</h3>
<h3>这是属性2:{{attr2}}</h3>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data:{
val:'DeliveredParameter',
num:10
},
components:{
cpn:{
template: '#myCpn',
//声明属性:对象形式
props:{
attr1:{
type: Number,
//表示这个属性attr1在使用组件时必须传递参数
//required: true
},
attr2:{
type: String,
default(){ //此处因该使用函数
return 'DefaultString';
}
}
}
}
}
});
</script>
支持的数据验证类型:验证都支持哪些数据类型呢?String,Number,Boolean,Array,Object,Date,Function,Symbol
子组件=>父组件 $emit
子组件通过触发自定义事件传递数据
父组件(Vue实例)则通过v-on监听子组件触发的事件,并从参数中获得数据。
<div id="app">
<!--此处的自定义事件出来函数不应该加括号,vue会自动传递参数,只需在method中接收即可-->
<cpn @my-event="handler"></cpn>
</div>
<template id="myCpn">
<div>
<!--此处是普通的v-on-->
<button @click="BtnClick()">Click me!</button>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data:{
val:'DeliveredParameter',
},
methods:{
handler(val){
console.log('handler is done:'+val);
}
},
components:{
cpn:{
template: '#myCpn',
//声明属性:对象形式
props:{
attr:{
type: String,
default(){ //此处因该使用函数
return 'DefaultString';
}
}
},
methods:{
BtnClick(){
//子组件通过$emit触发自定义事件myEvent,并传递参数attr
this.$emit('my-event',this.attr);
}
}
}
}
});
</script>
2.6父子组件的相互访问
有时候,在父组件内可能会出现引用子组件中某个函数或变量的情况,vue提供了三个变量来实现父子组件间的相互访问。
父组件访问子组件: c h i l d r e n 或 children或 children或refs
子组件访问父组件:$parent
子组件访问根组件:$root
代码示例:
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<template id="tem1">
<div>
<h3>I am son</h3>
<ccpn></ccpn>
<ccpn></ccpn>
</div>
</template>
<template id="tem2">
<div>
<h3>I am grandson</h3>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data:{
val:'I am root',
},
methods:{
func1(){
//this.$children代表当前组件(Vue实例)下的所有子组件(Vue component)
console.log(this.$children[0].val);
}
},
components:{
cpn:{
template: '#tem1',
data(){
return{
val: 'I am son'
}
},
methods:{
func1(){
//this.$parent代表当前组件的父组件Vue
console.log(this.$parent.val);
}
},
//声明属性:对象形式
components: {
ccpn:{
template: '#tem2',
data() {
return {
val: 'I am grandson'
}
},
methods:{
func1(){
//this.$root代表当前组件的根组件Vue实例
console.log(this.$root.val);
}
}
},
}
},
}
});
</script>
采用$children形式得到的是包含所有子组件的数组,并且索引与顺序有关
由于组件标签中同一级别的标签数量和标签可能发生变化,因此这种方法显然在修改时不太容易。
r e f s 就 可 避 免 这 个 问 题 t h i s . refs就可避免这个问题this. refs就可避免这个问题this.refs得到的是一个包含所有子组件的对象, r e f s 使 用 时 只 需 要 为 每 个 子 组 件 指 定 一 个 键 , r e f = " i d " , 然 后 父 组 件 再 引 用 时 采 用 t h i s . refs使用时只需要为每个子组件指定一个键,ref="id",然后父组件再引用时采用this. refs使用时只需要为每个子组件指定一个键,ref="id",然后父组件再引用时采用this.refs.id即可访问子组件
<div id="app"> <cpn ref="first"></cpn> <cpn ref="second"></cpn> </div> <script> const vm = new Vue({ el: '#app', data: { val: 'I am root', }, methods: { func1() { console.log(this.$refs.first); } }, } </script>
2.7slot插槽
基本插槽
在编写组件模板的时候,会出现两个模板高度相似的情况,为了更好的复用模板,vue提出了在组件模板中引入插槽slot。slot标签内表示预留一个位置可供其它html代码或组件插入。提高了组件的复用性和可扩展性。
<div id="app">
<cpn></cpn><!-- 插槽内容为默认的button -->
<cpn><span>自定义内容1</span></cpn><!-- 插槽内容为自定义span -->
</div>
<template id="tem1">
<div>
<h3>我是组件</h3>
<!-- slot标签内容为空,表示不提供默认内容 -->
<slot><button>默认内容</button></slot>
</div>
</template>
若组件cpn的template
中没有包含一个<slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃(即cpn标签内部内容无效)。
该插槽跟模板的其它地方一样可以访问相同的实例 property (也和vue组件拥有相同的“作用域”)
具名插槽
当一个模板中需要有多个插槽的时候,如果在使用的时候vue默认把cpn标签的内容区替换每个v-slot标签,这个时候就需要为每个插槽指定名称,并在使用时注明。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<!-- 一个不带name的<slot>出口会带有隐含的名字“default”-->
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
在向具名插槽提供内容的时候,我们可以在一个
<template>
元素上使用v-slot
指令,并以v-slot
的参数的形式提供其名称:v-slot可缩写为#,使用缩写时必须带上插槽名,例如default
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<!-- 任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。
下面的两个p标签也可包裹在<template v-slot:default><template>标签里
-->
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
作用域插槽
由于组件的编译作用域原因,父组件中无法访问子组件的数据,意味着当父组件向子组件中的slot中插入内容时无法访问到子组件中的数据。
为了解决这一问题,vue提供了作用域插槽,即在为每个实例slot绑定一个function,在把子组件的数据传递到该function的作用域中,实现了每个插槽都有自己单独的作用域。
本质上是:为每个实例的slot标签提供一个作用域,子组件可把数据绑定到slot作用域里,然后父组件在往slot中插入东西时,可以访问slot作用域中的数据。
CODE
<div id="app">
<!-- 没有插入时 插槽显示默认内容-->
<cpn></cpn>
<!-- 将template标签内容作为插槽内容插入 -->
<cpn>
<!-- 此处为该slot命名一个作用域myScope,并根据作用域名访问v-bind:绑定的val(实际上是user的别名)-->
<template v-slot:default="myScope">
<span>自定义显示年龄:{{myScope.val.age}}</span>
</template>
</cpn>
</div>
<template id="tem1">
<div>
<h3>我是组件</h3>
<!-- v-bind:此处把子组件data()中的user绑定到slot实例作用域,并取名为val-->
<slot v-bind:val="user"><span>默认显示子组件data()中的user.name(姓名):{{user.name}}</span></slot>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
components:{
cpn:{
template: '#tem1',
data(){
return{
user: {
name:'Qian',
age:21,
}
}
},
},
}
});
</script>
独占插槽
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用,不需要被template标签包裹。这样我们就可以把 v-slot
直接用在组件上:
<current-user v-slot:default="slotProps">
{ { slotProps.user.firstName } }
</current-user>
<!-- 其中的:default可以省略 -->
当组件内有多个插槽时,为所有的插槽使用完整的基于
<template>
的语法:即每个插槽内容均包裹于template,且指定v-slot:default(othername)
附(一):参考:
阿清博客:https://aqingya.cn/articl/92af846d.html
官网文档:https://cn.vuejs.org/v2/guide/
附(二)快捷键:
"string".log +tab = console.log("string")
div#a + tab = <div id="a"></div>
div.b + tab = <div class="b"></div>