vue 框架中,组件的概念贯通始终,我的项目里所有的页面都是一个组件,它有绝对独立的作用域,它们之间如果须要进行数据传递,就要合乎肯定的规定,比方父子组件传值,兄弟组件传值,隔代组件传值(它们之间隔了好几个组件)接下来我就解说一下这些组件之间如何传值:
1. 父组件给子组件传值(props)
新建 src/views/father.vue 文件,做为父组件
father.vue
<template>
<div class="father">
</div>
</template>
<script>
export default {data () {return {}
}
}
</script>
新建 src/components/children.vue 文件作为子组件
children.vue
<template>
<div class="children">
<ul>
<li v-for="(item, index) in userList" :key="index">{{item}}</li>
</ul>
</div>
</template>
<script>
export default {
props: {
userList: {
type: Array,
required: true
}
},
data () {return {}
}
}
</script>
留神到子组件里有一个 props 属性,这里来接管父组件传递过去的值
父组件里援用子组件并且传值给子组件,代码如下:
father.vue
<template>
<div class="father">
<children-item :userList="userList"></children-item>
</div>
</template>
<script>
import childrenItem from '_c/children' // 引入子组件
export default {components: {childrenItem}, // 调用子组件
data () {
return {userList: ['张三', '李四', '王二'] // 传值给子组件
}
}
}
</script>
2. 子组件给父组件传值($emit)
在子组件 children.vue 外面减少一个办法sendData
children.vue
<template>
<div class="children">
<ul>
<li v-for="(item, index) in userList" :key="index">{{item}}</li>
</ul>
<button @click="sendData"> 向父组件传值:3</button>
</div>
</template>
<script>
export default {
props: {
userList: {
type: Array,
required: true
}
},
data () {return {}
},
methods: {sendData () {this.$emit('sendData', 3)
}
}
}
</script>
父组件 father.vue 外面通过事件接管子组件传递过去的值
father.vue
<template>
<div class="father">
<children-item :userList="userList" @sendData="getData"></children-item>
<p>{{value}}</p>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
data () {
return {userList: ['张三', '李四', '王二'],
value: 0
}
},
methods: {getData (val) {
// 接管子组件传递过去的值
this.value = val
}
}
}
</script>
3. 兄弟组件或隔代组件传值($emit/$on)
这种办法通过一个空的 Vue 实例作为地方事件总线(事件核心),用它来触发事件和监听事件, 奇妙而轻量地实现了任何组件间的通信,包含父子、兄弟、跨级。然而咱们的我的项目比拟大时,能够抉择更好的状态治理 vuex。
新建 src/bus/index.js 文件:
import Vue from 'vue'
const Bus = new Vue()
export default Bus
在 main.js 外面引入:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import Bus from './bus/index' // 引入总线
Vue.config.productionTip = false
Vue.prototype.$bus = Bus // 应用总线
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
新建 src/components/brother1.vue 和 brother2.vue 文件做为兄弟组件,让 brother1 给 brother2 传值
brother1.vue
<template>
<div class="brother1">
<h3> 兄弟组件 1 </h3>
<button @click="sendData"> 兄弟组件 1 </button>
</div>
</template>
<script>
export default {data () {return {}
},
methods: {sendData () {this.$bus.$emit('sendData', 1) // 发送
}
}
}
</script>
brother2.vue
<template>
<div class="brother2">
<h3> 兄弟组件 2 </h3>
<p>{{value}}</p>
</div>
</template>
<script>
export default {data () {
return {value: null}
},
methods: { },
mounted () {
this.$bus.$on('sendData', val => { // 接管
console.log(val)
this.value = val
})
}
}
</script>
4. $attrs/$listeners
这种形式我在我的项目里没有用过,它是 Vue2.4 版本中新增的内容,当前能够在我的项目里用一用,接下来的例子我就照着网上给的教程敲一遍,演示一下如何应用:
这里须要新建一个子组件,srccomponentschildren2.vue
建好当前,再来革新下 srcviewsfather.vue 这个组件,还有 srccomponentschildren.vue 这个组件
father.vue
<template>
<div class="father">
<div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">
<h4> 父组件 </h4>
<children-item></children-item>
</div>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
data () {return {}
},
methods: {}}
</script>
<style lang="less" scoped>
.father {
height: 300px;
background: gold;
}
</style>
children.vue
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -1</h4>
<children-item2></children-item2>
</div>
</template>
<script>
const childrenItem2 = () => import('_c/children2')
export default {components: {childrenItem2},
data () {return {}
},
methods: {}}
</script>
children2.vue
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -2</h4>
</div>
</template>
<script>
export default {data () {return {}
},
methods: {}}
</script>
这样做的目标,使它们之间的关系是这样的:
father(a)组件引入 children(b)做为子组件,children(b)组件引入 children2(c)组件做为子组件
father 组件(A)-- 子 --children 组件(B)-- 子 --children2 组件(C)
b 是 a 的子组件,c 又是 b 的子组件,这样造成一个嵌套的关系,当初有一个需要,须要 a 组件把值间接传递给 c 组件,有几种解决办法呢?
- vuex(大材小用)
- a 先通过 props 把值传递给 b,b 再通过 props 将值传递给 c(容易出错)
- 利用下面讲的事件总线 $bus(多人合作开发时,代码维护性较低)
所以倡议应用 $attrs/$listeners
如果在父组件 a 中,有 name1 和 name2 两个值须要传递给子组件 b
a 组件
<template>
<div class="father">
<div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">
<h4> 父组件 </h4>
<children-item :name1="name1" :name2="name2"></children-item>
</div>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
data () {
return {
name1: '张三',
name2: '李四'
}
},
methods: {}}
</script>
子组件 b 拿到值当前通过 $attrs
再传递给 c 组件
b 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">
<h4> 子组件 -1</h4>
{{$attrs}}
<children-item2 v-bind="$attrs"></children-item2>
</div>
</template>
<script>
const childrenItem2 = () => import('_c/children2')
export default {components: {childrenItem2},
inheritAttrs: false,
data () {return {}
},
methods: {}}
</script>
c 组件里这样获取这两个值:
c 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -2</h4>
<span>{{name1}}</span>
<span>{{name2}}</span>
</div>
</template>
<script>
export default {props: ['name1', 'name2'],
inheritAttrs: true,
data () {return {}
},
methods: {}}
</script>
如果 b 组件通过 props 接管了 name1
这个值,那么 c 组件就不会接管到 name1
这个值了
b 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">
<h4> 子组件 -1</h4>
{{$attrs}}
<children-item2 v-bind="$attrs"></children-item2>
</div>
</template>
<script>
const childrenItem2 = () => import('_c/children2')
export default {components: {childrenItem2},
props: ['name1'],
inheritAttrs: false,
data () {return {}
},
methods: {}}
</script>
c 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -2</h4>
<span>{{name2}}</span>
</div>
</template>
<script>
export default {props: ['name2'],
inheritAttrs: true,
data () {return {}
},
methods: {}}
</script>
下面讲的是如何将 a 组件的值传递给 c 组件,这是往下传递的,那么如何将 c 组件的值传递给 a 组件,往上传递呢?
c 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -2</h4>
<span>{{name2}}</span>
<button @click="sendData"> 向 a 组件传值 </button>
</div>
</template>
<script>
export default {props: ['name2'],
inheritAttrs: true,
data () {return {}
},
methods: {sendData () {this.$emit('sendData', 1)
}
}
}
</script>
b 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">
<h4> 子组件 -1</h4>
{{$attrs}}
<children-item2 v-bind="$attrs" v-on="$listeners"></children-item2>
</div>
</template>
<script>
const childrenItem2 = () => import('_c/children2')
export default {components: {childrenItem2},
props: ['name1'],
inheritAttrs: false,
data () {return {}
},
methods: {}}
</script>
a 组件
<template>
<div class="father">
<div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">
<h4> 父组件 </h4>
<span>{{value}}</span>
<children-item :name1="name1" :name2="name2" @sendData="getData"></children-item>
</div>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
data () {
return {
name1: '张三',
name2: '李四',
value: null
}
},
methods: {getData (val) {this.value = val}
}
}
</script>
从以上代码示例,咱们能够看出 $attrs/$listeners 的性能,它就像一个桥梁的作用,负责在 a 和 c 间接传递和接收数据
5. provide/inject
在第四种形式里:$attrs/$listeners,其实也能够看到应用的局限在于,a,b,c 这三个组件,必须是层层嵌套的关系,通过在 b 组件里应用 $attrs/$listeners,让 a 和 c 进行隔代通信
这里讲的 provide/inject,则更加灵便,它是 vue2.2 版本新增内容,不论是 a,b,c 这种嵌套关系,还是 a,b,c,d,e… 更深层的嵌套关系,a 组件通过 provide 分享数据,b,c,d,e… 都能够通过 inject 拿到 a 组件分享的数据
来看一下具体的示例代码:
我还是拿下面的组件来演示:
srcviewsfather.vue -- a 组件
srccomponentschildren.vue -- b 组件
srccomponentschildren2.vue -- c 组件
a 组件
中应用 provide
<template>
<div class="father">
<div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">
<h4> 父组件 </h4>
<children-item></children-item>
</div>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
provide () {
return {
name1: this.name1,
name2: this.name2
}
},
data () {
return {
name1: '张三',
name2: '李四',
}
},
methods: {}}
</script>
b 组件
应用 inject
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">
<h4> 子组件 -1</h4>
{{name1}}
{{name2}}
<children-item2></children-item2>
</div>
</template>
<script>
const childrenItem2 = () => import('_c/children2')
export default {components: {childrenItem2},
inject: ['name1', 'name2'],
data () {return {}
},
methods: {}}
</script>
c 组件
应用 inject
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -2</h4>
<span>{{name1}}</span>
<span>{{name2}}</span>
</div>
</template>
<script>
export default {inject: ['name1', 'name2'],
data () {return {}
},
methods: {}}
</script>
是不是很简略,然而!须要留神:provide 和 inject 绑定并不是可响应的,也就是说, a 组件
里批改一个值,前面的组件并不能拿到批改后的值,如:在 a 组件
里批改 name1 值:
<template>
<div class="father">
<div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">
<h4> 父组件 </h4>
<span>{{name1}}</span>
<span>{{name2}}</span>
<children-item></children-item>
<button @click="changeName1"> 扭转 name1 值 </button>
</div>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
provide () {
return {
name1: this.name1,
name2: this.name2
}
},
data () {
return {
name1: '张三',
name2: '李四',
}
},
methods: {changeName1 () {this.name1 = '旺财'}
}
}
</script>
这 name1 扭转当前,b,c 组件里还是批改之前的值
那么有没有方法实现数据响应式呢?有,看如下代码示例:
a 组件
革新当前相当于将整个 a 组件实例分享进来
<template>
<div class="father">
<div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">
<h4> 父组件 </h4>
<span>{{name1}}</span>
<span>{{name2}}</span>
<children-item></children-item>
<button @click="changeName1"> 扭转 name1 值 </button>
</div>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
provide () {
return {name: this // 将这个组件实例提供给前面的子组件}
},
data () {
return {
name1: '张三',
name2: '李四',
}
},
methods: {changeName1 () {this.name1 = '旺财'}
}
}
</script>
b 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">
<h4> 子组件 -1</h4>
{{name.name1}}
<children-item2></children-item2>
</div>
</template>
<script>
const childrenItem2 = () => import('_c/children2')
export default {components: {childrenItem2},
inject: {
name: {default: () => ({})
}
},
data () {return {}
},
methods: {},}
</script>
c 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -2</h4>
<span>{{name.name1}}</span>
</div>
</template>
<script>
export default {
inject: {
name: {default: () => ({})
}
},
data () {return {}
},
methods: {},}
</script>
以上就能够实现响应式的往子组件传递数据
题外话,我说一下,办法 4 和办法 5,这些都是不罕用的 api,其实在做我的项目的时候,vue 提供给咱们的 api 大略能用到 50% 可能就曾经不错了,然而我想说的是,即便不罕用的也要晓得怎么用,并且做我的项目的时候用一用比拟好
5. $parent/$children/$refs
应用这 3 种形式都会失去组件实例,而后就能够间接调用组件里的办法或者数据
同样还是应用下面的 a,b,c 三个组件做为例子演示一下:
a 组件
<template>
<div class="father">
<div style="width: 300px;height: 300px;border: 1px solid red;margin: 0 auto;">
<h4> 父组件 </h4>
<span>{{name1}}</span>
<span>{{name2}}</span>
<children-item ref="childrenItem"></children-item>
</div>
</div>
</template>
<script>
import childrenItem from '_c/children'
export default {components: {childrenItem},
provide () {
return {name: this}
},
data () {
return {
name1: '张三',
name2: '李四',
}
},
methods: { },
mounted () {console.log(this.$refs.childrenItem.name)
this.$refs.childrenItem.getName()
// 成果雷同
console.log(this.$children[0].name)
this.$children[0].getName()}
}
</script>
b 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 100px);">
<h4> 子组件 -1</h4>
<children-item2 ref="childrenItem2"></children-item2>
</div>
</template>
<script>
const childrenItem2 = () => import('_c/children2')
export default {components: {childrenItem2},
data () {
return {name: '子组件 -1'}
},
methods: {getName () {console.log(this.name)
}
},
mounted () {console.log(this.$parent.name1) // 张三
console.log(this.$refs.childrenItem2.name) // 子组件 -2
console.log(this.$children[0].name) // 子组件 -2
}
}
</script>
c 组件
<template>
<div class="children" style="margin: 30px;border:1px solid black;height: calc(100% - 90px);">
<h4> 子组件 -2</h4>
</div>
</template>
<script>
export default {data () {
return {name: '子组件 -2'}
},
methods: {getName () {console.log(this.name)
}
},
mounted () {console.log(this.$parent.name) // 子组件 -1
}
}
</script>