目录
组件结构
- 组件结构:
Vue.component('自定义组件名', {
template:'自定义模板',
data(){
return {
key:value,
...
}
}
})
-
组件中的相关属性:
是可复用的vue实例,vue实例对象拥有的属性,比如data, methods, computed, filter , 生命周期等 组件都有;
除了挂载点el
是vue实例对象特有的属性除外组件中的data 必须是一个函数, 保持数据的独立性(每个组件都有自己的数据,互相不影响)
vue强制刷新组件之$forceupdate、v-if、key属性三种方式的适用
组件的命名规则:
- 驼峰命名: 需要转换为连字符小写形式才能生效 这个转换只限于vue的实例化中使用,后期在单文件组件(index.vue)中或者组件中使用其它的组件不受限制
2. 连字符
组件的模板推荐使用 es6的字符串模板
!!!组件必须只有一个根元素!!!
<body>
<div id="app">
<ul-list></ul-list>
<!-- <divnode></divnode>名字错误 -->
<!-- <d-ivnode></d-ivnode>名字错误 -->
<div-node></div-node>
<zhao></zhao>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
Vue.component('ul-list', {
template: '<h1>hello 组件字符串</h1>'
})
Vue.component('DivNode', {
template: '<h1>hello 组件命名驼峰</h1>'
})
Vue.component('zhao', {
template: `<ul>
<li>
<p>这是标签</p>
<p>100块</p>
</li>
</ul>`
})
const app = new Vue({
el: "#app"
})
</script>
组件的嵌套
template: `<div>
<p>这是一个房间</p>
<bed></bed>
<cat></cat>
</div>`//正确示例
<bed>
<cat></cat>
</bed> //错误示例
<body>
<div id="app">
<room></room>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
//定义组件模板
const Room = {
template: `<div>
<p>这是一个房间</p>
<bed></bed>
<cat></cat>
</div>`
}
const Bed = {
template: `<div>
<p>这是一张床</p>
<cat></cat>
</div>`
}
const Cat = {
template: `<div>
<p>这是一只猫</p>
</div>`
}
//注册组件(全局注册)
Vue.component("Room", Room)
Vue.component("Bed", Bed)
Vue.component("Cat", Cat)
const app = new Vue({
el: "#app"
})
</script>
组件的 /局部注册和/全局注册
<body>
<div id="app">
<p>{{msg}}</p>
<order></order>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const order = {
template: `<div class="order">
<p>order组件-订单</p>
</div>`
}
// Vue.component('order', order)//全局注册
const app = new Vue({
el: '#app',
data: {
msg: 'hello vue'
},
components: { //局部注册
order
}
})
</script>
组件的data参数
面试回答: vue中的data为什么是一个函数?
- 如果data是一个函数的话,
这样每复用一次组件,就会返回一份新的data
(类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据) - Object是
引用数据类型
,里面保存的是内存地址
,单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。
JavaScript只有函数构成作用域(注意理解作用域,只有函数{}构成作用域,对象的{}以及if(){}都不构成作用域)
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
保证deta数据的复用数据相互不受影响
<body>
<div id="app">
<zhao></zhao>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
// Vue.component('zhao', {
// template: `<ul><li v-for='(item,index) in movie' :key='index'>
// <p>{{item.name}}</p>
// <span>{{item.time}}</span>
// <button @click='remove(index)'>删除</button>
// </li></ul>`,
// data() {
// return {
// movie: [{
// name: '刺杀小说家',
// time: '1小时20分钟'
// }, {
// name: '司藤',
// time: '45分钟'
// }, {
// name: '海贼王',
// time: '20分钟'
// }, {
// name: '吐槽大会',
// time: '2小时20分钟'
// }]
// }
// },
// methods: {
// remove(index) {
// this.movie.splice(index, 1)
// }
// }
// })
// 把组件的模板提取出来>
const list = {
template: `<ul><li v-for='(item,index) in movie' :key='index'>
<p>{{item.name}}</p>
<span>{{item.time}}</span>
<button @click='remove(index)'>删除</button>
</li></ul>`,
data() {
return {
movie: [{
name: '刺杀小说家',
time: '1小时20分钟'
}, {
name: '司藤',
time: '45分钟'
}, {
name: '海贼王',
time: '20分钟'
}, {
name: '吐槽大会',
time: '2小时20分钟'
}]
}
},
methods: {
remove(index) {
this.movie.splice(index, 1)
}
}
}
Vue.component('zhao', list)
const app = new Vue({
el: "#app"
})
</script>
</html>
组件的父子传值prop(通信)
步骤:
通过 prop 接受父组件向子组件传递数据
-
需要在组件标签上自定义一个属性
<组件标签名 自定义属性名='value'></组件标签名> <cat col='白色的猫'></cat> 若 1-(1)传递动态的值: 父子通信 ; 在 子组件标签上通过 v-bind 绑定自定义属性 <cat v-bind:自定义属性名='父组件中data中的key'></cat>
-
在组件模板中通过 props选项接受值
const Cat = { template: `<div> <strong>这是一只 {{自定义属性名}}</strong> </div>`, props:['自定义属性名'] // props:['col'] }
<body>
<div id="app">
<!-- 静态的值 默认传递的是字符串类型-->
<!-- 如果需要传递数值类型,数组,对象, 布尔值等需要通过 v-bind 进行绑定,告诉vue这是一个js的表达式而不是字符串-->
<lops col='白色的'></lops>
<!-- 传递动态的的值 父子通信-->
<cat :msg='msg'></cat>
<!-- 本质是这样 :msg='a' 下面父组件中data也可为a-->
</div>
<script src="./js/vue.js"></script>
<script>
const lops = { //从标签获取静态的值
template: `<div>
<p> 一只 {{col}} 猫 </p>
</div>`,
props: ['col']
}
//父子通信
const Cat = {
template: `<div>
<h1> 一条 {{msg}} 狗 </h1>
</div>`,
props: ['msg']
}
//组件的注册
Vue.component('lops', lops);
Vue.component('Cat', Cat);
const app = new Vue({
el: '#app',
data: {
msg: '蓝白色'
}
})
</script>
</body>
组件的子–>父通信(重要)
$emit和 $on
实现子父通信,要先知道一个知识
$emit(‘自定义事件名’, 传递的数据) 触发一个定义事件
$on(‘监听自定义事件’, callback) 监听自定义事件 第二个参数是一个callback (回调函数)
$on(‘监听自定义事件’, (data) => { data 就是接受的数据 })
前提条件: $emit() 和 $on() 必须是同一个对象的
子父通信的实现步骤
- 在子组件的模板中定义原生事件, 触发原生事件 提交一个自定义事件
(1) 在子组件的模板中定义原生事件
template: `<div>
<button @click='sendMsg'>传数据</button>
</div>`,
(2) 触发原生事件 提交一个自定义事件
methods: {
sendMsg() {
this.$emit('sendtoparent', "传递的数据")
// this.$emit('自定义的事件名',传递的数据)
}
}
- 在子组件标签上监听自定义事件, 通过 v-on:自定义事件名=‘父组件methods中的方法名’,触发父组件中methods中的方法
<cat v-on:sendtoparent='getMsg'></cat>
- 在父组件的 methods 中定义方法
methods: {
自定义方法名(与监听事件绑定的方法名一致)
getMsg(data) {
console.log('事件成功传递到了父组件',);
data就是"传递的数据"
}
}
v-on后面的方法名和$emit中的方法名是一致的
<body>
<div id="app">
<cat v-on:sendtoparent='getMsg'></cat>//(3)
<p>它的习性:{{value}} </p>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const Cat = { //这是子组件的模板
template: `<div>
<button @click='sendMsg'>传数据</button>//(1)
</div>`,
//组件的data必须是一个函数
data() {
return {
sleep: "猫喜欢睡觉"
}
},
methods: {
sendMsg() {//(2)
this.$emit('sendtoparent', this.sleep)
// this.sleep 就是传递的数据
}
}
}
//组件的注册
Vue.component('Cat', Cat)
const app = new Vue({ //这是父组件
el: '#app',
data: {
value: '' //存放子组件传过来的数据
},
methods: {
getMsg(data) {//(4)
console.log('事件成功传递到了父组件');
// console.log(data);//猫喜欢睡觉
this.value = data;
//将子组件传过来的数据data放到app的value中
}
}
})
</script>
子父通信标签上进行事件处理例子
实例:每点击一次字体变大一点
<body>
<div id="app">
<p :style="{fontSize:fontSize + 'px'}">hello world</p>
<!-- <child v-on:changefontsize='fontSize += $event'></child> -->
<!--第二种方法 直接在组件标签上进行事件处理 通过 $event 事件对象获取数据 -->
<child v-on:changefontsize='changeFont'></child>
<!-- 第一种方法 在组件标签上 通过 v-on监听自定义事件 `v-on:自定义事件名='父组件中的methods中的方法'`;
父组件中必须存在对应的方法 -->
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component('child', {
template: `
<div>
<button @click='$emit("changefontsize", 1)'>方法字体</button>
</div>
`
})
const app = new Vue({
el: '#app',
data: {
fontSize: 20,
},
methods: {
changeFont(data) {
//console.log(data);//接收到子组件传来的1
this.fontSize += data;
}
}
})
</script>
</body>
.sync子父通信修饰符实现
官网–>深入了解组件–>.sync修饰符
.sync 自定义事件 修饰符 vue2支持
组件通信: 可以简化代码
通常情况下:
自定义事件格式: this.$emit('自定义', '新的数据')
<child :title='title' v-on:change='change'></child>
或者
<child :title='title' v-on:change='title = $event'></child>
如果使用 .sync 修饰符,则可以简化
相当于 把 <child :title='title' v-on:change='change'></child> 整体简化为
<child :title.sync='title'></child>
使用.sync 修饰符时,需要通过固定的形式 `update:绑定的prop属性名` 进行事件的触发,提交的自定义事件的固定格式
this.$emit('update:title', '新的数据')
<body>
<div id="app">
<!--(1) <child :title='title' v-on:change1='change'></child> -->
<!--(2) <child :title='title' v-on:change1='title = $event'></child>-->
<child :title.sync='title'></child>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const child = {
template: `<div>
{{title}}
<button @click='change2'>传递数据</button></div>`,
props: {
title: String
},
// props: ['title'],
methods: {
change2() {
//(1) this.$emit('change1', '新的数据')
this.$emit('update:title', '新的数据')
}
}
}
const app = new Vue({
el: "#app",
data: {
title: '这是给子组件的标题',
},
//(1) methods: {
// change(val) {
// console.log(val);
// this.title = val
// }
// },
components: { //注册组件
child
}
})
</script>
非父子(子父)通信bus总线
bus总线vuecli使用步骤
解决了父子通信嵌套一层的局限
Bus总线: 本质是一个Vue的实例对象 (纯粹只是一个实例化对象,不需要任何设置)
const Bus = new Vue()
非父子通信:依然通过 事件传递: $emit() 和 $on() ; 必须隶属于同一个对象
<body>
<div id="app">
<!-- 非父子通信如下,不用考虑是不是父子 -->
<ul>
<li>
<com1></com1>
</li>
<li>
<com2></com2>
</li>
</ul>
<!-- <com1></com1>
<com2></com2> 他们两个是同一个父亲 -->
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const Bus = new Vue(); //Bus总线
Vue.component('com1', { //com1组件
template: `
<div>
<p> 接收com2传递过来的数据 :{{getmsg}}</p>
<button @click='sendcom2'>c1传递给c2</button>
</div>`,
data() {
return {
com1msg: 'com1的数据',
getmsg: ''
}
},
// $on() 监听通常都是在created()实例创建后 钩子函数中进行
created() {
Bus.$on('sendtocom1', (data) => {
console.log(data);
this.getmsg = data;
})
},
methods: { //1条线 1给2传递数据
sendcom2() {
Bus.$emit('sendtocom2', this.com1msg);
}
}
})
Vue.component('com2', { //com2组件
template: `
<div>
<p> 接收com1传递过来的数据 {{getmsg}}</p>
<button @click='sendcom1'>c2传递给c1</button>
</div>`,
data() {
return {
com2msg: 'com2的数据',
getmsg: '' //存com1发过来的数据
}
},
created() { //1条线 1接受2传过来的数据
Bus.$on('sendtocom2', (data) => {
console.log(data); //data是c1的数据
this.getmsg = data
})
},
methods: {
sendcom1() {
Bus.$emit('sendtocom1', this.com2msg);
}
}
})
const app = new Vue({
el: '#app'
})
//1条线是com1给com2传递数据(数据是com1的)
//同等道理 com2给com1传递数据
</script>
注意:
发送事件时,监听on必须已经存在,不然通信没有内容
弊端:复杂的通信可能会不通
组件之间的通信
Vue2组件通信方式汇总
bus总线传值在复杂情况下容易出问题
组件通信方式(非父子通信:祖孙之间通信):$attrs 和 $listeners 属性实现
官网解析:
$attrs: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。
当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),
并且可以通过 v-bind="$attrs" 传入内部组件
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
如果项目比较复杂(大型项目) 使用 vuex 实现统一的状态管理
- 祖孙传值$attrs
<body>
<!-- 祖给孙传值 -->
<div id="app">
<p>{{msg}}</p>
<parent :title='msg'></parent>
</div>
<script src="./js/vue.js"></script>
<script>
const parent = {
template: `<div>这是父组件
<child v-bind='$attrs' ></child>
</div>`,
}
const child = {
template: `<div> 这是子组件
<span>{{$attrs.title}} </span>
</div>`,
}
Vue.component('parent', parent)
Vue.component('child', child)
const app = new Vue({
el: "#app",
data: {
msg: 'app数据'
}
})
//.$mount('#app')
</script>
</body>
- 孙祖传值$listeners
provide/inject 只能从祖先传递给后代,且慎用
ref访问子组件实例DOM对象
官网->教程->深入了解组件–>访问元素 & 组件
ref 属性:
1. ref 属性绑定到组件上,则可以通过 vm.$refs 属性获取到组件对象
2. ref 属性绑定到普通的标签上,则可以通过 vm.$refs 属性获取到对应的DOM元素
定义: ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:
用法:
<input ref='自定义名字' type="text" placeholder="请输入内容" >
<child ref='自定义名字'></child>
获取:
this.$refs.自定义名字.相关操作
refs 只会在组件渲染完成之后生效,并且它们不是响应式的。
<body>
<div id="app">
<!-- <input ref='ipt' type="text" placeholder="请输入内容"> 标签的情况 -->
<child ref='child'></child> <!--组件的写法-->
<button @click='gain'>获取焦点</button>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const child = {
template: `
<input ref='ipt' type="text" placeholder="请输入内容">`,
methods: {
//方法传递就是将获取dom节点的方法提出来作为一个方法
getFocus() {
this.$refs.ipt.focus();
}
}
}
const app = new Vue({
el: "#app",
components: {
child
},
methods: {
gain() {
// console.log(this.$refs.ipt);//输入框的dom节点
//this.$refs.ipt.focus()
// 链式写法(先找到child组件,然后和上面一样)
// console.log(this.$refs);
// this.$refs.child.$refs.ipt.focus()
// 通过方法传递
this.$refs.child.getFocus()
}
}
})
</script>
封装组件
prop是父子传值放参数的
组件中的参数为什么这么写,回头看基础指令v-bind绑定类名的方法
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<button class="btn btn-danger">按钮</button>
<!-- 这是bootstrap的功能, class有这个属性值就会有样式-->
<div id="app">
<!-- 绑定类名 -->
<base-button classname="danger"></base-button>
<!-- 这里的class中的内容添加属性,不会生效,意味着传参 -->
<br>
</div>
<script src="./js/vue.js"></script>
<script>
const BaseButton = {
template: `
<button :class='["btn",cname]'>按钮</button>
`, //这里面的class属性是生效的地方 //btn是固有属性值 cname是动态类型
props: ['classname'], //prop是接受值这里代表参数danger
//计算属性
computed: {
//计算属性的函数名可以在组件标签或者是html标签中使用
cname() {
return "btn-" + this.classname;
//return的内容就是属性值btn-danger
}
}
}
// 全局注册
// Vue.component('BaseButton', BaseButton)
const app = new Vue({
el: '#app',
components: {
// 局部注册
BaseButton//属性名和属性值一样时,可以只写一个(语法自动补足),ES6的语法糖
}
})
</script>
禁用属性的添加和上面的不太一样
prop 的值类型绑定 传递静态数据时, 只限制于字符串类型,如果传递的是数组,数值类型,布尔类型,对象等
需要通过v-bind指令进行绑定 :需要告诉vue 这是一个 JavaScript 表达式而不是一个字符串
<title>禁用</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<button class="btn btn-warning btn-lg" disabled>官方按钮</button>
//有disabled就会禁用
<hr>
<!-- 这是bootstrap的功能, class有这个属性值就会有样式-->
<div id="app">
<!-- 绑定类名和尺寸 和禁用属性 -->
<!-- <base-button classname="danger" size='lg' forbidden='false'></base-button>//不论真假都生效,所以格式错误 -->
<base-button classname="danger" size='md' :forbidden='false'></base-button><br>
<base-button classname="danger" size='lg' forbidden></base-button>
<!-- 这里的class中的内容添加属性,不会生效,意味着传参 -->
<br>
</div>
<script src="./js/vue.js"></script>
<script>
const BaseButton = {
template: `
<button :class='["btn",cname,btnsize]' :disabled='forbidden'>按钮</button>
`, //这里面的class属性是生效的地方 //btn是固有属性值
props: ['classname', 'size', 'forbidden'], //prop是接受值这里代表参数danger
//计算属性
computed: {
//计算属性的函数名可以在组件标签或者是html标签中使用
cname() {
return "btn-" + this.classname;
//return的内容就是属性值btn-danger
},
btnsize() {
return 'btn-' + this.size
}
}
}
// 全局注册
// Vue.component('BaseButton', BaseButton)
const app = new Vue({
el: '#app',
components: {
// 局部注册
BaseButton
}
})
</script>
组件标签和html标签的区别
-
组件标签 : 定义的组件
组件标签上的添加属性一般都意味着进行
数据传递
, 在组件的内部都需要通过prop进行接受
组件标签上绑定事件 都是自定义事件
(直接用不生效),需要在组件内部通过this.$emit() 进行触发!!!直接在组件上绑定原生的事件,而不是自定义事件,此时可以通过 事件修饰符 .native 实现!!!!!
组件标签内添加内容 默认是会被忽略的,不会显示, 需要通过插槽实现
-
html标签 : html
html标签上的属性就是给标签本身
添加属性
,添加自定义的或是固有的属性
html 标签上绑定事件就是原生的事件
,在vue实例中methods中调用
html标签内添加内容就是标签的内容
<body>
<!-- 这样写组件和html标签的属性和事件才能生效 -->
<div id="app">
<em title="html提示信息" @click='send'>这是html标签</em>
<!-- <child :title="'组件提示信息'" @click='changs'></child> 配合用这个时写到send里面 this.$emit('click')-->
<child :title="'组件提示信息'" @click.native='changs'></child>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
//局部注册组件
components: {
child: {
//组件模块
template: `<p :title="title" @click='send'>这是组件标签</p>`,
props: ['title'],
methods: {
send() {
// this.$emit('click')不用这个时 组件标签这样写@click.native='changs'
}
}
}
},
methods: {
send() {
console.log("html标签事件");
},
changs() {
console.log("组件的事件");
}
}
})
</script>
全局组件的写法
<body>
<!-- 这样写组件和html标签的属性和事件才能生效 -->
<div id="app">
<em title="html提示信息" @click='send'>这是html标签</em>
<!-- <child :title="'组件提示信息'" @click='changs'></child> 配合这个用写到send里面 this.$emit('click')-->
<child :title="'组件提示信息'" @click.native='changs'></child>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
//组件的名称
const child = {
template: `<p :title="title" @click='send'>这是组件标签</p>`,
props: ['title'],
methods: {
send() {
// this.$emit('click')不用这个时 组件标签这样写@click.native='changs'
}
}
}
//全局注册组件
Vue.component('child', child);
const app = new Vue({
el: '#app',
methods: {
send() {
console.log("html标签事件");
},
changs() {
console.log("组件的事件");
}
}
})
</script>
插槽
vue 中插槽: 本质是实现内容分发功能(就是在组件内部插入内容) 在组件标签中 通过 template标签 插入内容
vue提供了一个 内置组件标签
一个组件内可以有多个插槽,并不冲突
插槽的分类:
1. 默认插槽 :
2. 具名插槽 : 给插槽命名
3. 作用域插槽 : 用来传递数据
/定义插槽/
<template>
<div>
<h2>插槽标题</h2>
<slot> 插槽 </slot>
</div>
</template>
/使用插槽/
<template>
<div>
<p>这是标签本来内容</p>
<SlotData>组件标签中的内容</SlotData>
</div>
</template>
<script>
import SlotData from "../components/slotData.vue";
export default {
components: {
SlotData,
},
};
</script>
具名插槽
指令 v-slot 结合插槽使用 ; 可以绑定具名插槽
v-slot 指令 只能用于 template 内置标签上
-
1定义默认插槽:
<slot></slot>
-
2定义具名插件: 需要借助name属性
<slot name='自定义插槽名'></slot>
-
1使用默认插槽:
<组件标签名> <tempalte v-slot:default> 真正插入组件内的内容 </template> </组件标签名>
-
2使用具名插槽:
<组件标签名> <tempalte v-slot:自定义的插槽名> 真正插入组件内的内容 </template> </组件标签名>
插槽的渲染结构: 由模板确定, 不是由插入的内容位置决定
<!-- 具名插槽,模板决定是什么标签,组件标签提供插的位置 -->
//定义插槽
<template>
<div>
<h1>
<slot name="header"></slot>
</h1>
<p>
<slot></slot>
</p>
<h3>
<slot name="footer"></slot>
</h3>
</div>
</template>
//使用插槽
<template>
<div>
<p>原本标签</p>
<SlotData>
<template v-slot:header>
<em>这是头部</em>
</template>
<template v-slot:footer>
<p>这是尾部</p>
</template>
<template>
<!-- 默认插槽可以不提供v-slot 默认插槽 : 如果提供那就是 v-slot:default-->
<p>这是主体</p>
</template>
</SlotData>
</div>
</template>
<script>
import SlotData from "../components/slotData.vue";
export default {
components: {
SlotData,
},
};
</script>
插槽的默认值
当用插槽时,组件标签中具体的值会覆盖插槽模板的内容
可以给插槽提供默认值: 在slot标签内插入内容,就是插槽的默认值
<slot>内容</slot>
如果插槽模板没有提供具体的值,则会显示默认值,如果提供了具体的值, 则会替代默认值
<template>
<div>
<slot> <h2>插槽内容</h2> </slot>
</div>
</template>
//使用插槽
<template>
<div>
<p>原本标签</p>
<SlotData> </SlotData>
</div>
</template>
<script>
import SlotData from "../components/slotData.vue";
export default {
components: {
SlotData,
},
};
</script>
作用域插槽
如果只是内容分发,直接使用v-slot:自定义插槽名即可
如果是作用域插槽, 需要使用 v-slot:自定义插槽名=‘自定义名字’
作用域插槽作用: 作用域插槽是一个带绑定数据的插槽,插槽的样式由父组件决定,内容却由子组件控制。
通过 v-slot进行接收,
传递数据流程:
- 需要在组件模板中的 slot标签上自定义属性并赋值, 属性值就是传递的数据
<template>
<div>
<slot a="插槽"></slot>
<slot name="header" a="头部hello 插槽" b="100"></slot>
</div>
</template>
- 接受数据: 在 组件标签内通过 v-slot 进行接受 ; 接受到的数据是一个对象,包含了 slot标签上传递所有的数据
<template>
<div>
<p>原本标签</p>
<SlotData> v-slot:具名插槽名='自定义名字'
<template v-slot:header="obj">
<p>{{ obj }}</p>
<p>{{ obj.a }}</p>
<p>{{ obj.b }}</p>
</template>
</SlotData>
</div>
</template>
<script>
import SlotData from "../components/slotData.vue";
export default {
components: {
SlotData,
},
};
</script>
v-slot 指令可以进行简写: v-slot: 简写为 #
作用域插槽应用场景: 给开发人员提供的 ; 在实际开发过程中,很多情况下可以避免使用作用域插槽
默认插槽的情况
<template>
<div>
<slot a="hello 插槽" b="hello vue"></slot>
</div>
</template>
//使用插槽
<template>
<div>
<p>原本标签</p>
<SlotData>
<template v-slot="obj">
<p>{{ obj }}</p>
<p>{{ obj.a }}</p>
</template>
</SlotData>
</div>
</template>
组件中有参数的情况
<template>
<div>
<slot a="hello 插槽" :msg="msg"></slot>
</div>
</template>
<script>
export default {
data() {
return {
msg: "插槽的作用域",
};
},
};
</script>
//使用插槽
<template>
<div>
<p>原本标签</p>
<SlotData>
<template v-slot="obj">
<p>{{ obj }}</p>
<p>{{ obj.a }}</p>
<p>{{ obj.msg }}</p>
</template>
</SlotData>
</div>
</template>
v-slot接收的数据是一个对象,可以进行解构
<template>
<div>
<slot name="header" a="hello 插槽" :msg="msg"></slot>
</div>
</template>
<script>
export default {
data() {
return {
msg: "插槽的作用域",
};
},
};
</script>
//使用插槽
<template>
<div>
<p>原本标签</p>
<SlotData>
<template v-slot:header="{ a, msg }">
<p>{{ a }}</p>
<p>{{ msg }}</p>
</template>
</SlotData>
</div>
</template>
vue 的2.6版本(老版本) 具名插槽和作用域插槽引入了一个新的统一的语法 新的指令 v-slot 代替 slot(具名插槽) 和 slot-scope(作用域插槽)
低版本的 slot 和 slot-scope
可以直接把 slot attribute 用在一个普通元素上,其它写法一样
prop属性拓展
在这之前prop都是以数组形式接受所有的值
<body>
<div id="app">
<child :count='count' :msg='msg' title="组件提示信息" :arr='arr'>
<template>
<ul>
<li v-for='item in arr'>{{item}}</li>
</ul>
</template>
<template v-slot:objs>
<strong>{{objs}}</strong>
</template>
</child>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const child = {
template: `<div>
<p >{{count}}-- {{msg}} --{{title}}</P>
<slot></slot>
<slot name='objs'></slot>
</div>`,
// 通过一个数组的形式接受所有的值,不能准确的区分数据类型
props: ['count', 'msg', 'title', 'arr', 'objs'] //(2)接收数据
}
Vue.component('child', child)
const app = new Vue({
el: '#app',
data: {
count: 100,
msg: 'hello vue',
arr: [2, 34, 56, 7], //(1)数组的传值
objs: {
name: "张三",
age: 18
}
}
})
</script>
因为通过一个数组的形式接受所有的值,不能准确的区分数据类型,所以传入一个对象
第一种情况:组件标签直接绑定一个对象
<body>
<div id="app">
<!-- 直接绑定一个对象 -->
<child :person='person'>
{{person.age}}
<!-- 相当于给插槽传入一个具体的值 -->
</child>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const child = {
template: `<div>
<p >{{person.name}}</P>
<slot></slot>
</div>`,
// 通常你希望每个 prop 都有指定的值类型 以对象的形式进行接受,这些 property 的名称和值分别是 prop 各自的名称和类型:
props: {
//绑定一个对象
person: Object
}
}
Vue.component('child', child)
const app = new Vue({
el: '#app',
data: {
person: {
name: '张三',
age: 18
}
}
})
</script>
第二种情况:组件标签传入一个对象的所有属性
<body>
<div id="app">
<!-- 传入一个对象的所有属性 -->
<!-- 传入一个对象所有的属性 ; 在子组件中通过 props 接受时 你接受的对象的每个属性名,而不是整个对象 -->
<child v-bind='person'></child>
<!-- 等价形式 -->
<!-- <child v-bind:name='person.name'></child>
<child v-bind:age='person.age'></child> -->
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const child = {
template: `<div>
<p >{{name}} --{{age}}</P>
</div>`,
props: {
//传入一个对象的所有属性
name: String,
age: Number
}
}
Vue.component('child', child)
const app = new Vue({
el: '#app',
data: {
person: {
name: '张三',
age: 18
}
}
})
</script>
prop设置默认值
<body>
<div id="app">
<!-- <child></child> //基础数据类型测试没有属性时,因为prop设置了默认值,所以为你好,默认值-->
<!-- <child :arr='arr'></child> 123-->
<child></child> // 默认值[100, 200]
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const child = {
template: `<div>
<p>{{arr}} </p>
</div>`,
props: {
// arr: {
// type: String,
// //直接指定默认值,适用于 字符串,数值等基本数据类型
// default: "你好,默认值"
// }
arr: {
type: Array,
default: () => {
return [100, 200]
}
}
}
}
//组件的注册
Vue.component('child', child);
const app = new Vue({
el: '#app',
data: {
arr: [1, 2, 3]
}
})
</script>
prop自定义校验规则 validator
<body>
<div id="app">
<child :count='count'></child>
</div>
<script src="./js/vue.js"></script>
<script>
const child = { //从标签获取静态的值
template: `<div>
<p>{{count}} </p>
</div>`,
props: {
//自定义校验的规则validator
count: {
type: Number,
validator: (value) => {
// console.log(value);
// 通过return 返回校验结果
return value > 100 && value < 200
//当data中参数满足这个条件才输出,否则报错(但仍会显示不在这个条件的结果)
}
}
}
}
//组件的注册
Vue.component('child', child);
const app = new Vue({
el: '#app',
data: {
count: 122
}
})
</script>
帧听器watch
侦听器:
不存在缓存的,
不依赖于data中的数据
watch 可以进行异步操作
watch: {
// 接受两个参数: 第一个参数新值(改变后的值), 第二个参数是旧值(改变之前的值),
msg(newVlue, oldVlue) {
// 监听到对应的数据发生改变,则做出响应的处理
this.msg = '你好,侦听器的值'
}
}
})
侦听器也是一个函数: 完整的写法
watch:{
/ 直接指定事件处理程序
count(newValue, oldValue){
// 事件处理程序
},
/ 使用对象的形式,通过 handler 属性指定事件处理程序
count:{
// 监听到count 改变直接执行handler
handler(){
// 事件处理程序
}
},
/侦听对象的指定属性
"count.a"(newvalue, oldvalue){
// 事件处理程序
},
/ 加载完毕后立即执行一次 通过 immediate:true 属性实现
count:{
handler(newvalue, oldvalue){
console.log('hello123123');
},
immediate:true
},
/ 实现深层次的监听对象的所有属性, 不管嵌套的有多深 通过deep:true 属性实现
person:{
handler(newvalue, oldvalue){
console.log('对象该百年了123123');
},
// immediate:true
deep:true
}
/ 指定监听函数 , 使用函数名(定义在methods方法中)
count:'addCount'
/ 指定多个事件处理程序 使用数组的形式
count:[handler1, handler2 ,....]
}
侦听器的配置项:immediate 效果:页面加载后立即执监听器
<body>
<div id="app">
<!-- 侦听器的例子 当改变app.msg值得时候输出下面设置的100 -->
<p>{{msg}}</p>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg: 'hello vue'
},
//watch 侦听器
watch: {
// 接受两个参数: 第一个参数新值(改变后的值), 第二个参数是旧值(改变之前的值),
msg: {
// handler 是一个回调函数
// !!!!!!!! 侦听器如果是对象的形式:不能使用箭头函数 因为箭头函数的this的指向是window !!!!!!
handler: function(newVlue, oldVlue) {
this.msg = '100'
},
immediate: true
// 页面加载完成后立即执行一次监听器 需要一个 immediate 属性: 是一个布尔类型
}
}
})
</script>
侦听器的配置项:deep 解决函数嵌套太深不触发帧听器的问题
<body>
<div id="app">
<p>{{school}}</p>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
school: {
name: '学校',
classRoom: {
name: '一年级'
}
}
},
watch: {
//函数名对应methods中的方法 把事件处理程序单独独立出来, 存放到methods方法中,在去使用的时候直接用函数名,但是必须添加引号
msg: 'changemag',
school: {
handler: function(newVlue, oldVlue) {
console.log("值被改变了");
},
deep: true // deep:true 该回调会在任何被侦听的对象的属性 改变时被调用,不论其被嵌套多深
}
},
methods: {
changemag(newVlue, oldVlue) {
console.log(newVlue, oldVlue);
}
}
})
</script>
计算属性和侦听器的区别
-
计算属性:computed
1.使用场景:当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性
计算属性 computed 是基于data中数据进行处理的,data数据变化,他也跟着变化 ; 当data中数据没有发生改变时,我们调用computed中函数n次,只会进行缓存(执行一次);
2.用法: 计算属性在调用时需要在模板中渲染,修改计算所依赖元数据 -
侦听器:watch
1.使用场景:数据变化时执行异步或开销较大的操作,可以随时修改状态的变化,不存在缓存 通过监听data中的数据 进行处理,data数据变化,则执行handler处理程序,对data中的元数据进行处理 2.用法:watch在调用时只需修改元数据
<body>
<div id="app">
<!-- 计算属性的例子 页面输出[ 200 ]-->
<p>{{slice}}</p>
<!-- 侦听器的例子 当改变app.msg值得时候输出下面设置的 你好,侦听器 -->
<p>{{msg}}</p>
</div>
</body>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg: 'hello vue'
},
// 计算属性
computed: {
slice() {
return this.count = '200'; //这个只要写了就会执行
}
},
watch: {
// 接受两个参数: 第一个参数新值(改变后的值), 第二个参数是旧值(改变之前的值),
msg(newVlue, oldVlue) {
// 监听到对应的数据发生改变,则做出响应的处理
this.msg = '你好,侦听器的值'
}
}
})
</script>
方法methods和侦听watch、计算属性computed,过滤器filters的区别
watch和computed都是对数据的监听只有数据发生变化时才会触发,但是他们擅长的领域不一样
watch更擅长一对多:就是主要监听一个可以影响多个数据的数据,具备缓存机制
computed擅长多对一:主要监听多个数据影响一个数据的数据,一定要return,具备缓存机制
组合出的那个数据会自动定义不用在data中再次定义,
当然也可以使用watch来监听组成computed所监听数据的每一个数据,但是这样代码的重复率过高
methods和watch、computed的区别
这个很直观,methods是通过事件驱动来执行函数的是被动的
watch、computed是当监听的数据发生变化时主动执行这个函数
methods和computed的区别
区别一个是主动与被动,另一个是methods的运算是没有缓存的,computed运算是有缓存的(只要运算所依赖的数据没有发生变化就会从缓存中取出结果)
计算属性
1、计算属性适合用在单个属性的计算;
2、计算属性只能在单个vue实例中使用;
3、计算属性不能接收参数,只能使用data中定义的变量进行计算;
4、计算属性有缓存机制,可减少调用次数;
5、计算属性相当于定义一个变量
过滤器
1、过滤器适合多个同样计算方法的属性的计算;
2、过滤器可以定义为全局过滤器,在多个vue实例中使用;
3、过滤器可以接收多个参数进行计算;
4、过滤器没有缓存机制,每调用一次都会计算一次;
5、过滤器相当于定义一个特殊的方法
vue生命周期钩子函数
vue 的生命周期钩子函数:
创建前/后 : beforeCreate() created()
挂载前/后 : beforeMount() mounted()
更新前/后 : beforeUpdate() updated()
销毁前/后 : beforeDestroy() destroyed()
vue 的生命周期 组件也都可以使用
这是在script标签直接传值使用,了解即可
<!-- 独立的模板文件 : 不建议使用 -->
<!-- <script id="order" type='text/x-template'>
<p>这是vue 模板 !!!!!!!!!!!!! {{msg}} </p>
</script> -->
组件的生命周期
例子:order组件在child组件上面
控制台输出的顺序:
beforeDestroy() destroyed() 销毁前/后的触发条件 三种情况
beforeDestroy() destroyed() 触发:
1. 需要手动触发vm.$destroy() 销毁方法触发;
2. v-if 触发 切换组件的时候会触发
3. 路由切换时
<div id="app">
<!-- !1手动销毁例子-->
<!-- <order></order> -->
<!--!2 v-if的案例 -->
<order v-if='flag'></order>
<discover v-else></discover>
</div>
<script src="./js/vue.js"></script>
<script>
const order = {
template: ' <p>这是order</p>',
// 组件被销毁时触发
beforeDestroy() {
console.log('order子组件 销毁前');
},
destroyed() {
console.log('order子组件 销毁后');
}
}
const discover = {
template: ' <p>这是discover</p>'
}
const app = new Vue({
el: '#app',
components: {
order,
//es6 之前的写法为 a:order
discover
},
data: {
flag: true
}
})
</script>
</body>
第一种情况图解
路由的切换会触发组件销毁示例
<div id="app">
<router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
const Order = {
template: `<div>
我的订单
</div>`,
created(){
console.log('order 组件创建');
},
destroyed(){
console.log('order 组件被销毁');
}
}
const Pay = {
template: `<div>
代付款页面
</div>`,
created(){
console.log('pay 组件创建');
},
destroyed(){
console.log('pay 组件被销毁');
}
}
const router = new VueRouter({
routes:[
{
path:'/order',
component:Order
},{
path:'/pay',
component:Pay
}
]
})
const app = new Vue({
el:'#app',
router
})
</script>
- 注意:tab页的切换不会触发生命周期,如果业务需要, 可以将组件用v-if做判断
<el-tab-pane label="用户管理" name="first">
<AA v-if="activeName == 'first'"></AA>
</el-tab-pane>
keep-alive 组件缓存
keep-alive是Vue的内置组件,包裹动态组件时,会将不活动的组件实例留在内存中,优化请求,防止DOM重新渲染
比如:在APP.vue中单纯使用keep-alive组件包裹,会直接缓存所有页面
keep-alive用于保存组件的渲染状态
实例
a.vue
<template>
<div class="about">
<h2>小a页面</h2>
<span>组件会缓存</span>
<input type="text" />
</div>
</template>
APP.vue
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/B">About</router-link>
</div>
<keep-alive include="A">
<!-- name 为A的组件将被缓存!-->
<router-view />
</keep-alive>
<!-- <keep-alive exclude="A">
除了 name 为A的组件都将被缓存!
<router-view />
</keep-alive> -->
缓存效果:跳转页面不刷新在跳回来,输入框文字仍然存在
keep-alive是一个抽象组件:
它自身不会渲染一个DOM元素,也不会出现在父组件链中;
使用keep-alive包裹动态组件时,
会缓存不活动的组件实例,而不是销毁它们。
动态组件 通过 :is 属性实现
使用vue 提供的内置组件标签 <component></component> ,绑定is属性实现动态组件的渲染
keep-alive 内置标签 : 进行缓存,会缓存命中的组件
<div id="app">
<!-- 动态组件 -->
<!-- <component is='order'></component> 错误示例-->
<keep-alive>
<component :is='cname'></component>
</keep-alive>
</div>
<script src="./js/vue.js"></script>
<script>
const order = {
template: ' <p>这是order</p>',
beforeCreate() {
console.log('order子组件 创建前 。。。 ');
},
created() {
console.log('order子组件 创建后 。。。 。。。');
},
beforeMount() {
console.log('order子组件 挂载前。。。');
},
mounted() {
console.log('order子组件 挂在后。。。。。。。。。。');
},
// 组件的生命周期(组件残留的钩子函数) 结合 keep-alive 一起使用
activated() {
console.log('order组件被缓存');
},
deactivated() {
console.log('order组件被停用。。');
},
// 组件被销毁时触发
beforeDestroy() {
console.log('order子组件 销毁前');
},
destroyed() {
console.log('order子组件 销毁后');
}
}
const discover = {
template: ' <p>这是discover</p>'
}
const app = new Vue({
el: '#app',
components: {
order,
//es6 之前的写法为 a:order
discover
},
data: {
// 通过组件名动态显示组件 例如将order改为discover
cname: 'order',
}
})
</script>
- is 可以直接传递一个组件
- :is 是查找父组件中的一个赋值,然后找到相应的组件
动态组件中的activated与deactivated钩子函数