组件
Vue中最强大的莫过于组件了吧,但是组件中的作用域是独立的,所以组件的数据没办法互相引用。为了解决这一问题,组件间通信应运而生。
父向子传值
在子组件中写props来接收数据
//父组件
<template>
<div>
<children :menus="menus"/>
</div>
</template>
<script>
import Children from '@/components/children.vue';
export default {
"children": Children,
props: {},
data() {
return {
menus: ['首页','菜单一','菜单二']
};
},
watch: {},
computed: {},
methods: {},
created() {},
mounted() {}
};
</script>
<style lang="less" scoped>
</style>
//子组件
<template>
<div>
<ul>
<li v-for="menu in menus">{{menu}}</li>
</ul>
</div>
</template>
<script>
export default {
components: {},
props: {
//父组件标签名字
menus: {
type: Array,
required: true
}
},
data() {
return {
}
},
watch: {},
computed: {},
methods: {},
created() {},
mounted() {}
};
</script>
<style lang="less" scoped>
</style>
父组件通过props向下传递给子组件,组件的数据形式有三种:data,props,computed
子向父传值
子组件传值给父组件,需要通过事件
//父组件
<template>
<div>
//通过自定义事件接收子组件传的值
<children @getMsg="getData"/>
<p>{{msg}}</p>
</div>
</template>
<script>
import Children from '@/components/children.vue';
export default {
"children": Children,
props: {},
data() {
return {
msg: ""
};
},
watch: {},
computed: {},
methods: {
getData(data) {
this.msg = data
}
},
created() {},
mounted() {}
};
</script>
<style lang="less" scoped>
</style>
//子组件
<template>
<div>
<button @click="sendMsg">子组件传值给父组件</button>
</div>
</template>
<script>
export default {
components: {},
data() {
return {
msg: '给父组件传的值'
}
},
watch: {},
computed: {},
methods: {
sendMsg() {
this.$emit('getMsg',this.msg);
}
},
created() {},
mounted() {}
};
</script>
<style lang="less" scoped>
</style>
子组件通过事件给父组件发消息,把自己的数据发送给子组件
通过VueX传值
vueX的数据是响应式的,不会被保存,刷新就会没有,所以要保存数据要拷贝一份到localStorage中,刷新之后,在localStorage中取出来替换state
let defaultCity = "上海"
try { // 用户关闭了本地存储功能,此时在外层加个try...catch
if (!defaultCity){
defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
}
}catch(e){}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
changeCity(state, city) {
state.city = city
try {
window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
// 数据改变的时候把数据拷贝一份保存到localStorage里面
} catch (e) {}
}
}
})
由于VueX中保存的是数组,而localStorage只支持字符串,所以要用JSON转换
JSON.stringify(state.defaultCity); // array -> string
JSON.parse(window.localStorage.getItem("defaultCity")); // string -> array
EventBus(非父子组件)
新建一个.js文件或者在main.js中注册
//新建.js文件
import Vue from 'vue'
export default new Vue()
//main.js中注册
Vue.prototype.$EventBus = new Vue()
//发送方
<template>
<div class="add-task" :class="{'ishide':isAdding}" @click="addtask()">
<i class="fa fa-plus-circle" aria-hidden="true"></i>
添加任务
</div>
</template>
<script>
import Bus from '@/bus'
export default {
methods: {
props: ['index'],
data () {
return {
isAdding: false
}
},
addtask () {
this.isAdding = true
Bus.$emit('adding-task', this.isAdding, this.index) // 这里触发的事件是'adding-task',
传递了两个参数,分别是this.isAdding和this.index
this.$emit('addtask')
}
}
}
//接收方
export default{
created () {
// 这里使用on监听了adding-task事件,接收到两个参数。所以一旦上面的组件中的adding-task事件触发,这里就会监听到。
Bus.$on('adding-task', (state, index) => {
if (this.index === index) {
this.isShow = state
}
})
}
}
因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。
provide 与 inject
假设有两个组件,B组件是A的子组件
//B组件
export default {
inject:['age'],
mouted() {
console.log(this.age) //18
}
}
//A组件
export default {
provide: {
age: 18
}
}
ref
ref用于给元素或子组件注册引用信息,引用信息将会注册在父组件的ref获取在子组件上定义的属性和方法,通过调用方法向子组件传递数据
父组件向子组件传值
//父组件
<template>
<div>
<Child ref="child"></Child>
</div>
</template>
<script>
import Child from "@/components/Child"
export default {
data() {
return {
msg: "父组件传给子组件的值"
}
},
mounted() {
//父组件通过ref属性调用子组件的方法
this.$refs.child.getMsg(this.msg)
},
components: {
Child
}
}
</script>
<style scoped>
</style>
//子组件
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
data() {
return {
msg: "
}
},
methods: {
//子组件获取父组件值的方法
getMsg(val) {
this.msg = val
}
}
}
</script>
<style scoped>
</style>
$attrs和$listeners
- attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
- listeners" 传入内部组件
// index.vue
<template>
<div>
<h2>demo</h2>
<child-com1
:foo="foo"
:boo="boo"
:coo="coo"
:doo="doo"
title="前端"
></child-com1>
</div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
components: { childCom1 },
data() {
return {
foo: "Javascript",
boo: "Html",
coo: "CSS",
doo: "Vue"
};
}
};
</script>
// childCom1.vue
<template class="border">
<div>
<p>foo: {{ foo }}</p>
<p>childCom1的$attrs: {{ $attrs }}</p>
<child-com2 v-bind="$attrs"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
components: {
childCom2
},
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
props: {
foo: String // foo作为props属性绑定
},
created() {
console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端" }
}
};
</script>
// childCom2.vue
<template>
<div class="border">
<p>boo: {{ boo }}</p>
<p>childCom2: {{ $attrs }}</p>
<child-com3 v-bind="$attrs"></child-com3>
</div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue");
export default {
components: {
childCom3
},
inheritAttrs: false,
props: {
boo: String
},
created() {
console.log(this.$attrs); // {"coo": "CSS", "doo": "Vue", "title": "前端" }
}
};
</script>
// childCom3.vue
<template>
<div class="border">
<p>childCom3: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
props: {
coo: String,
title: String
}
};
</script>
listeners 是两个对象,listeners里存放的是父组件中绑定的非原生事件。
$parent和$children
父向子传值用$parent
//父组件
<template>
<div>
<Child ref="child"></Child>
</div>
</template>
<script>
import Child from "@/components/Child"
export default {
data() {
return {
msg: "父组件传给子组件的值"
}
},
mounted() {
//父组件通过$children[0]访问对应子组件
this.$children[0].msg = this.msg
},
components: {
Child
}
}
</script>
<style scoped>
</style>
//子组件
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
data() {
return {
msg: ""
}
}
}
</script>
<style scoped>
</style>
子向父传值$parent
//子组件
<template>
<div>
<button @click="sendMsg">子组件传值给父组件</button>
</div>
</template>
<script>
export default {
data() {
return {
msg: "子组件传给父组件的值"
}
},
methods: {
//子组件通过$parent访问父组件
sendMsg() {
this.$parent.msg = this.msg
}
}
}
</script>
<style scoped>
</style>
//父组件
<template>
<div>
<Child></Child>
<p>{{msg}}</p>
</div>
</template>
<script>
import Child from "@/components/Child";
export default {
data() {
return {
msg: ""
}
}
components: {
Child
}
}
</script>
<style scoped>
</style>
父向子传值($children)
children为当前组件的直接子组件,是一个无序的数组,父组件通过 children 访问子组件并传递数据,$ children 并不保证顺序,也不是响应式的,如果能清楚的知道子组件的顺序,可以使用下标来操作对应的子组件
//父组件
<template>
<div>
<Child ref="child"></Child>
</div>
</template>
<script>
import Child from "@/components/Child"
export default {
data() {
return {
msg: "父组件传给子组件的值"
}
},
mounted() {
//父组件通过$children[0]访问对应子组件
this.$children[0].msg = this.msg
},
components: {
Child
}
}
</script>
<style scoped>
</style>
//子组件
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
data() {
return {
msg: ""
}
}
}
</script>
<style scoped>
</style>
作用域插槽
//父组件
<template>
<div>
<child :propName="items"> <!--传值到子组件-->
<!--写法1 -->
<li
<!--作用域插槽也可以具名 绑定slot name="slotName"-->
slot="slotName"
<!--把子组件插槽看作一个对象, 赋给scopeName-->
slot-scope="scopeName">
<!-- dos="item.do" (子组件中)-->
{{scopeName.dos}}
</li>
</child>
<!--写法2 es6 的解构写法 推荐!!!-->
<child :propName="items">
<li slot="slotName" slot-scope="{item}">
{{item.do}}
</li>
</child>21 </div>
</template>
<script>
import child from "./components/child.vue"
export default{
components:{child},
data(){
return{
items:[
{do:'play'},
{do:'eat'},
{do:'sleep'},
{do:'play'},
{do:'eat'},
{do:'sleep'}
]
}
}
}
</script>
//子组件
<template>
<ul>
<slot name="slotName" <!--作用域插槽也可以具名!-->
v-for="item in items"
:dos="item.do" <!--取循环中的项作为属性 方便父组件调用!-->
></slot>
</ul>
</template>
<script>
export default{
props:['items'] <!--父级items 传过来的值!-->
}
</script>
-
父子通信: 父向子传递数据是通过 props,子向父是通过 events(parent / attrs/$listeners
-
兄弟通信: Bus,Vuex
-
跨级通信: Bus,Vuex,provide / inject API,listeners