Prop 常用
$emit 组件封装用的较多
.sync 语法糖 (较少)
$attrs和$listeners(组件封装用的较多)
provide和inject(高阶组件/组件库用的较多)
其他方式通信
1 Prop
我们用的最多方式,可以通过Prop向子组件传递数据。
用一个形象的比喻来说,父子组件之间的数据传递相当于自上而下的下水管子,只能从上往下流,不能逆流。这也正是Vue的设计理念之单向数据流。而Prop正是管道和管道之间的一个衔接口,这样(水)数据才能向下流.
看代码:
</head>
<body>
<script src="js/vue.js"></script>
<div id="app">
<!-- 父子组件直接的通信
很多时候我们需要从顶层向底层发送数据
发送数据的方法一般有两种
1.通过props向子组件传递数据
2.通过事件向父组件发送消息 -->
<!-- props的值有两种方式
1.方式1:字符串数组,数组中的字符串就是传递时的名称
2.方式2:对象,对象可以设置传递时的类型,也可以设置默认值等 -->
<cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<!-- {{cmovies}} -->
{{cmessage}}
</div>
</template>
<script>
//父传子props
const cpn = {
template: '#cpn',
// props:['cmovies','cmessage']
props: {
//类型的限制
// cmovies:Array,
// cmessage:String
//2.提供一些默认值
cmessage: {
type: String,
default: 'aaaaa',
required: true
},
//类型是对象或者数组时,默认值必须是一个函数
cmovies: {
type: Array,
default() {
return []
}
}
}
}
const app = new Vue({
el: '#app',
data: {
message: 'hello',
movies: ['火影忍者', '海贼王', '海尔兄弟']//这里是定义在vue实例化对象里的数据就是cpn的父组件
//使用props的方法让其在子组件的页面中显示
},
methods: {
},
components: {
cpn
}
})
</script>
</body>
</html>
父组件传给子组件数值,子组件不要对其进行修改,而是需要找个变量将父组件传递的数值给赋值过来然后对变量进行操作.
2 $emit
触发当前实例上的事件。附加参数都会传给监听器回调。
参数:
{string} eventName
[...args]
- 1
- 2
这个方法是用来子组件向父组件传递数值的,和上面的Porp正好相反.
下面代码:
<!--
父组件传给子组件数值,子组件不要对其进行修改,而是需要找个变量将父组件传递的数值给赋值过来
然后对变量进行操作
-->
<body>
<div id="app">
<counter @inc="addnumber"></counter>
</div>
<script>
Vue.component('counter', {
template: '<div @click="add">点击一下</div>',
methods: {
add: function() {
this.$emit('inc', '温清夜')
}
},
})
let vm = new Vue({
el: "#app",
data: {
total: 5
},
methods: {
addnumber: function(step) {
alert(step)
}
}
})
</script>
</body>
上面就是$emit的用法,在页面中我点击 '点我一下’就会触发上面绑定的事件add,在add里面有个监听事件inc,因此inc监听事件就会被触发(其实inc事件在代码中就可以看出来,直接在父组件上 @inc=“addnumber”,但是是无效的,因为你需要知道inc是个怎样的东西,到底在哪里。所以在子组件你绑定点击事件,来告诉浏览器inc是个啥东西)
该事件相当于
vm.$on('inc',function addnumber(step){
alert(step)
})
vm.$emit('inc','疫情快快消失')
3 .sync修饰符
这个东西曾经作为双向绑定功能存在,后来Vue2.0移出,然后在Vue2.3版本引入.
它只是作为一个编译时的语法糖存在,它会被扩展为一个自动更新父组件属性的v-on监听器。说白了就是然我们手动进行更新父组件的值,从而使数据改动来源更加明显。官方用语:
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。
既然作为一个语法糖,肯定是某种写法的简写形式:看代码
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
语法糖的写法:
<text-document @:title.sync="doc.title"></text-document>
注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。取而代之的是,你只能提供你想要绑定的属性名,类似 v-model。
<!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>.sync</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
</head>
<body>
<div id="app">
<login :name.sync="userName"></login> {{ userName }}
</div>
<script>
let Login = Vue.extend({
template: `
<div class="input-group">
<label>姓名:</label>
<input v-model="text">
</div>
`,
props: ['name'],
data() {
return {
text: ''
}
},
watch: {
text(newVal) {
this.$emit('update:name', newVal)
}
}
})
let vm = new Vue({
el: '#app',
data: {
userName: ''
},
components: {
Login
}
})
</script>
</body>
</html>
代码中的一行代码:
this.$emit('update:name', newVal)
1
官方语法:
我们推荐以 update:myPropName 的模式触发事件取而代之。举个例子,在一个包含 title prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:
this.$emit('update:title', newTitle)
- 1
myPropName表示要更新的prop值,如果你不用.sync语法糖使用第二种$emit也可以
4 a t t r s 和 attrs和 attrs和listeners
$attrs官网的解释
包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用
$listeners官网的解释
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
我觉得 a t t r s 和 attrs和 attrs和listeners属性像两个收纳箱,一个负责收纳属性,一个负责收纳事件,都是以对象的形式来保存数据
<!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="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
</head>
<body>
<div id="app">
<child :foo="foo" :bar="bar" @one.native="triggerOne" @two="triggerTwo">
<child>
</div>
<script>
let Child = Vue.extend({
template: '<h2>{{ foo }}</h2>',
props: ['foo'],
created() {
console.log(this.$attrs, this.$listeners)
// -> {bar: "parent bar"}
// -> {two: fn}
// 这里我们访问父组件中的 `triggerTwo` 方法
this.$listeners.two()
// -> 'two'
}
})
new Vue({
el: '#app',
data: {
foo: 'parent foo',
bar: 'parent bar'
},
components: {
Child
},
methods: {
triggerOne() {
alert('one')
},
triggerTwo() {
alert('two')
}
}
})
</script>
</body>
</html>
代码中两个属性个两个方式,区别是,属性是一个prop声明,事件是一个.native修饰器.
我们通过 a t t r s 和 attrs和 attrs和listeners进行数据传递,在需要的地方调用和处理还是比较方便的。
当我们在组件上赋予一个非Prop声明时,编译之后的代码会把这些属性都当成原始属性对待,添加到HTML原生标签上,上面的代码编译后
<h2 bar="parent bar">parent foo</h2>
1
标签里面的bar属性是什么鬼?如果去掉,这正是inheritAttrs属性的用武之地!给组件加上这个属性就行了,一般是配合$attrs使用:
let Child=Vue.extend({
inheritAttrs:false;//默认是true
})
1
2
3
再次编译
<h2>parent foo</h2>
5 provide/inject
这二个是组合,也就是相互配合使用,缺一不可
官方描述
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
1
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。如果你熟悉 React,这与 React 的上下文特性很相似。
<!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">
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<son></son>
</div>
<script>
let Son = Vue.extend({
template: '<h2> son {{house}}--{{car}} ---{{money}}</h2>',
inject: {
house: {
default: '没房'
},
car: {
default: '没车'
},
money: {
//长大工作了虽然有点钱
// 仅供生活费,需要向父母要
default: '¥4500'
}
},
created() {
console.log(this.house, this.car, this.money)
// -> '房子', '车子', '¥10000'
}
})
new Vue({
el: '#app',
provide: {
house: '父亲给的房子',
car: '父亲给的车子',
money: '父亲给的¥10000'
},
components: {
Son
}
})
</script>
</body>
</html>
代码很容易看懂,这个二个东西,一个在子组件 inject
另一个在父组件provide
Vuex
官方推荐,VueX是一个专门为Vue.js应用程序开发的状态管理模式
$root
当前组件树的根Vue实例,如果当前实例没有父实例,此实例将会是自己。通过访问根组件也能进行数据之间的交互,但极小情况下回直接修改父组件中的数据.
$parent
父实例,如果当前实例有的话,通过访问父实例也能进行数据之间的交互,但极小情况下回直接修改父组件中的数据