props/$emit
1、父组件通过props向子组件传值
<!-- 父组件 -->
<template>
<div>
<item @click="getSonString" v-bind:users="users" :test="test" ref="item"/>
</div>
</template>
<script>
import Item from './item'
export default {
data() {
return {
users:['lee', 'he', 'mei']
}
},
methods: {
test(){ //将父组件的方法传给子组件调用
}
},
components: {
Item
}
}
</script>
<!-- 子组件 -->
<template>
<div>
<ul>
<li v-for="user in users">{{ user }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
};
},
props: {
users: {
},
test: {
type: Function,
default: null
}
}
};
</script>
2、子组件使用$emit触发事件向父组件传值
<!-- 父组件 -->
<template>
<div>
<item @click="getSonString"/>
<div>{{ title }}</div>
</div>
</template>
<script>
import Item from './item'
export default {
data() {
return {
title: '',
users:['lee', 'he', 'mei'],
paper: '父组件的一个值',
}
},
components: {
Item
},
methods: {
getSonString(title) {
this.title = title
}
}
}
</script>
<!-- 子组件 -->
<template>
<div>
<button @click="toFatherString">
用$emit事件触发将子组件的值传递给父组件
</button>
<!-- <ul>
<li v-for="user in users">{{ user }}</li>
</ul> -->
<!-- <div>{{ this.$parent.paper }}</div> -->
</div>
</template>
<script>
export default {
data() {
return {
title: "来自子组件的值"
};
},
props: {
users: {
},
},
methods: {
toFatherString() {
// 触发父组件的click事件,参数为title
this.$emit("click", this.title);
},
},
};
</script>
使用$parent / $children与 ref
this.$parent.XXX
在子组件中读取父组件的值
ref是给DOM元素或组件自定义名称,便于引用
ref父组件访问子组件,子组件标签设ref,使用this.$refs.item.XXX
获取
<!-- 父组件 -->
<template>
<div>
<item ref="item"/>
</div>
</template>
<script>
mounted() {
// 读取第一个子组件数据,不推荐,你并不知道哪个是第一个
console.log(this.$children[0].clips)
//读取命名子组件数据,对应子组件的元素要设ref='item'
console.log(this.$refs.item.clips)
//从根组件查找组件数据
console.log(this.$root.$children[0].name) // 入口APP
console.log(this.$root.$children[0].$children[0].paper) // 第一个父组件,这里是自己
console.log(this.$root.$children[0].$children[0].$children[0].clips)// 第一个子组件,这里是item
}
</script>
<!-- 子组件 -->
<template>
<div>
<div>{{ this.$parent.paper }}</div>
</div>
</template>
<script>
export default {
data() {
return {
title: "来自子组件的值",
clips: "来自子组件的一个值"
};
}
};
</script>
使用$emit/$on
通过一个空的Vue实例作为中央事件总线,用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。
关键代码
var Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {});
// 发送端代码:
methods: {
send() {
event.$emit('data-a',this.title)
}
},
//接收端代码:
mounted() {
event.$on("data-a", (title) => {
this.title = title; //箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
});
}
案例:
event.js
import Vue from 'vue'
export default new Vue()
// 子组件item
<template>
<div>
<div>{{ title }}</div>
</div>
</template>
<script>
import event from './event'
export default {
data() {
return {
title: "item自己的数据"
};
},
props: {
users: {},
},
mounted() {
event.$on("data-a", (title) => { // 接收其他组件的title值
this.title = title; //箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
});
},
};
</script>
// 子组件itemB
<template>
<div>
<button @click="send">itemB将数据发送给item</button>
</div>
</template>
<script>
import event from './event'
export default {
data() {
return {
title: '来自于子组件itemB的数据'
}
},
methods: {
send() {
event.$emit('data-a',this.title) // 将title值通过自定义事件发送出去
}
},
}
</script>
// 子组件itemC
<template>
<div>
<button @click="send">itemC将数据发送给item</button>
</div>
</template>
<script>
import event from './event'
export default {
data() {
return {
title: '来自于子组件itemC的数据'
}
},
methods: {
send() {
event.$emit('data-a',this.title)
}
},
}
</script>
// 父组件
<template>
<div>
<ItemB @click="getSonString" />
<ItemC @click="getSonString" />
<item @click="getSonString" v-bind:users="users" ref="item"/>
<div>{{ title }}</div>
</div>
</template>
<script>
import Item from './item'
import ItemB from './itemB'
import ItemC from './itemC'
import event from './event'
export default {
data() {
return {
title: '',
users:['lee', 'he', 'mei'],
paper: '父组件的一个值',
}
},
components: {
Item,
ItemB,
ItemC
},
methods: {
getSonString(title) {
this.title = title
}
},
mounted() {
event.$on("data-a", (title) => {
this.title = title; //箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
});
},
beforeDestroy() {
// 及时销毁自定义事件,否则可能造成内存泄露
event.$off('data-a', this.title)
}
}
</script>
Vuex
Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享。
上面传递数据的方法都要一次又一次复杂的传递,没有一个集中传递的地方,Vuex就是解决这个问题,他将所有状态(数据)都存在store中,其他组件只有从store中取即可。
优点:
1、能够集中管理数据,便于开发和后期维护
2、能够高效地实现组件间的数据共享,提高开发效率
3、存在Vuex中的数据都是响应式的,能够实时保持数据与页面的同步
什么情况使用:一般只有需要组件之间共享的数据才有必要存储到Vuex中,对于组件中的私有数据,依旧存储在组件自身的data中即可。
用法:
store.js文件
// 使用vuex插件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 创建store实例
export const store = new Vuex.Store({
// 数据定义,唯一公共数据源,所有共享数据都有放在这
state: {
lists: null
},
// 类似于computed计算属性
getters:{
},
// 数据修改,同步触发,类似于methods,
mutations: {
},
// 异步操作的数据,只能调用mutations
actions: {
}
})
export default store
同时要挂载到vue实例中,main.js:
import Vue from 'vue'
import store from './vuex'
import App from './App'
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
components: { App },
template: '<App/>'
})
如果你的需要共享数据的组件多,项目大,也可以每个组件单独一个store文件,最后归到一个js文件管理
<!--dialog_store.js-->
export default {
state:{
},
// 类似于computed计算属性
getters:{
},
// 类似于methods,
mutations:{
},
// 执行多个 mutations 就需要用 action ,异步
actions:{
}
}
// index.js
// 这个文件可以管理多个store文件
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);
import dialog_store from './dialog_store.js';//引入某个store对象
export default new vuex.Store({
modules: {
dialog: dialog_store // 管理多个store对象
}
})
state
数据定义,存储数据,类似于data
组件访问state数据的两种方式
第一种:$store.state
// store没被拆分
{{ $store.state.XXX }}
如果有单独的组件store文件,要加上store对象的名称
// store被拆分,要加store对象的名称
{{ $store.state.dialog.XXX }}
第二种:mapState(State的集合)
// store没被拆分
<template>
<div class="hello">
<p>{{ name }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
props: {
},
computed: {
...mapState(['name'])
}
}
</script>
// store被拆分
<template>
<div class="hello">
<p>{{ name }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
props: {
},
computed: {
...mapState({
'name':state=>state.Hello.name
})
}
}
</script>
Mutation
Mutation用于变更Store中的数据,而且只能通过Mutation变更Store数据,不可直接操作Store中的数据。
这样可以集中监控所有数据的变化。
Mutation中不能写异步代码,不然数据会不一致,异步代码要写在actions中
调用Mutation的第一种方式this.$store.commit():
export default{
state: {
count: 0
},
mutations: {
add(state) {
state.count++
}
},
actions: {
},
modules: {
}
}
<!-- 组件中 -->
<template>
<div class="hello">
<button @click="handle1">增加</button>
{{ $store.state.Hello.count }}
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
computed: {
},
methods: {
handle1() {
this.$store.commit('add')
}
}
}
</script>
调用Mutation的第二种方式:mapMutations(Mutations的集合)
<!-- 组件中 -->
<template>
<div>
<button @click="handle1">点击加一</button>
<p>{{ $store.state.Hello.count }}</p>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
methods: {
...mapMutations(['add']),
handle1() {
this.add()
}
},
}
</script>
<!-- 某个被拆分的store.js -->
export default{
state: {
name:'你好',
count: 0
},
mutations: {
add(state) {
state.count++
}
},
actions: {
},
modules: {
}
}
Mutation传递参数的方式:
<!-- 名字为Hello的store.js -->
export default{
state: {
name:'你好',
count: 0
},
mutations: {
add(state) {
state.count++
},
addN(state, step) {
// 对应下面普通提交
state.count += step
},
addN(state, payload) { // 这里的第二个参数是一个对象,保存着type和step
// 第二种特殊提交,对应下面特殊提交
state.count += payload.count // 要这样调用
}
},
actions: {
},
modules: {
}
}
<template>
<div>
<button @click="handle1">点击加10</button>
<p>{{ $store.state.Hello.count }}</p>
<button @click="handle2(10)">点击加10第二种办法</button>
<p>{{ count }}</p>
</div>
</template>
<script>
import { mapState, mapMutations } from "vuex";
export default {
methods: {
...mapMutations(["add", "addN"]),
// mapMutations调用
handle1() {
this.addN(10);
},
// 普通调用
handle2(step) {
// 1、普通提交
this.$store.commit("addN", step);
// 2、特殊提交
this.$store.commit({
type: 'addN',
step
})
},
},
computed: {
...mapState({
'count': (state) => state.Hello.count,
})
},
};
</script>
使用常量替代 Mutation 事件类型
使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
Action异步操作
异步执行 mutations 就需要用 action
Action只能调用mutations的函数,不能操作数据,只有mutations才有权利操作state中的数据
export default{
state: {
name:'你好',
count: 0
},
mutations: {
add(state) {
state.count++
},
addN(state, step) {
state.count += step
}
},
actions: {
addAsync(context) { // 异步,延迟1000毫秒触发mutations里的add函数
setTimeout(() => {
context.commit('add')
}, 1000);
}
},
modules: {
}
}
<template>
<div>
<button @click="handle1">点击加一</button>
<p>{{ $store.state.Hello.count }}</p>
<button @click="handle2">点击加一第二种办法</button>
<p>{{ count }}</p>
<button @click="handle3">异步点击加一</button>
<p>{{ count }}</p>
</div>
</template>
<script>
import { mapState, mapMutations } from "vuex";
export default {
methods: {
...mapMutations(["add", "addN"]),
handle1() {
this.addN(4);
},
handle2() {
this.$store.commit("addN", 4);
},
handle3() { // 触发addAsync异步函数
this.$store.dispatch('addAsync')
}
},
computed: {
...mapState({
'count': (state) => state.Hello.count,
})
},
};
</script>
触发action异步任务时带参数:
export default{
state: {
name:'你好',
count: 0
},
mutations: {
add(state) {
state.count++
},
addN(state, step) {
state.count += step
}
},
actions: {
addAsync(context, step) { // 带参数,任意加多少
setTimeout(() => {
context.commit('addN', step)
}, 1000);
}
},
modules: {
}
}
export default {
methods: {
handle4() {
this.$store.dispatch('addAsync', 4) // 带参数
}
}
};
this.$store.dispatch()是触发actions的第一种方式
触发actions的第二种方式:
没错,又是map,mapActions
<!-- 组件中 -->
<template>
<div>
<button @click="addAsync(4)">异步带参数点击加一</button> <!-- 直接使用addAsync函数 -->
<p>{{ count }}</p>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions } from "vuex"; // 引入mapActions
export default {
methods: {
...mapMutations(['add', 'addN']),
...mapActions(['addAsync']), // 映射actions中的addAsync函数,映射为自己的函数
},
computed: {
...mapState({
'count': (state) => state.Hello.count,
})
},
};
</script>
Getter
Getter对store中已有的数据处理之后形式新的数据,类似于computed计算属性
store中的数据发生变化,Getter的数据也会跟着变化
定义Getter
export default{
state: {
name:'你好',
count: 0,
students: [
{ name:"lili", age: 23 },
{ name:"lin", age: 12 },
{ name:"zh", age: 65 },
{ name:"xue", age: 16 },
{ name:"he", age: 14 }
]
},
mutations: {
add(state) {
state.count++
},
addN(state, step) {
state.count += step
}
},
actions: {
addAsync(context, step) {
setTimeout(() => {
context.commit('addN', step)
}, 1000);
}
},
// 计算属性
getters: {
showNum(state) {
return `当前数字为` + state.count
},
// 第二个参数为getters,可以调用getters中的方法
showNumLength(state, getters) {
return getters.showNum.length
},
// 返回年龄大于20的学生
more20stu(state) {
return state.students.filter(s => s.age > 20)
},
// getters传参数:还可以返回函数,返回大于age的学生,调用:$store.getters.moreAgeStu(18)
moreAgeStu(state) {
return function (age){
return state.students.filter(s => s.age > age)
}
}
}
}
使用:
{{ $store.getters.showNum }}
第二种使用,没错,同样有map,mapGetters
<template>
<div>
<p>{{ showNum }}</p> <!-- 直接使用 -->
</div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from "vuex"; // 引用
export default {
methods: {
},
computed: {
...mapState({
'count': (state) => state.Hello.count,
}),
...mapGetters(['showNum']) // 映射
},
};
</script>