组件通信四大方案
- Vuex(同页面下==同路由下,任何形式下的组件都可以进行通信) 同路由下的组件之间的通信
$emit
和$on
事件总线,观察者模式 任意阶层传
https://cn.vuejs.org/v2/guide/components-custom-events.html- props父子孙数据集成
https://cn.vuejs.org/v2/guide/components-props.html - 本地存储和cookie,把数据放到url上
//直接设置url方式,最无赖
location.href = `${location.href}?id=${this.id}`
Vuex安装
Vuex插件安装
npm
npm install vuex --save
yarn
yarn add vuex
yarn官网,具体使用查看api
在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装 Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
Vuex状态管理 === 管理组件数据流动 === 全局数据管理
Vue的全局数据池,在这里它存放着大量的复用或者公有的数据,然后可以分发给组件
Vuex是一个前端非持久化的数据库中心,Vuex其实是Vue的重要选配,一般小型不怎么用,大型项目运用比较多,所以页面刷新,Vuex数据池会重置
组件数据来源
- ajax请求后端
- 组件自身定义默认数据
- 从vuex拿
路由 => 管理的是组件
Vuex => 管理的是数据
Vuex五大概念
State 池 (数据源)
Getter 查 (获取数据源的数据)
Mutation 改 (真正修改的动作)
Action 触发修改行为
Module 可以拥有多个数据源(数据池)
以上篇博文Vue_router
的项目基础上添加
导出仓库
store.js
// 状态管理
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 实例化Vuex,创建一个仓库
const store = new Vuex.Store({
// 状态
// 该库存数据的地方
state: {
// 状态项
count: 0,
author: 'katsuki'
},
// 修改数据的方法
mutations: {
increment(state) {
state.count++
}
}
})
// 暴露store仓库到main.js根容器里面
export default store
挂载仓库
main.js
import Vue from 'vue'
// 引入路由模块
import router from './configs/router.js'
// 引入仓库
import store from './configs/store.js'
//微信样式
import 'weui'
//全局样式
import './styles/app.css'
// 引入ajax库
import axios from 'axios'
// 第三方库挂载使用原型链方式
Vue.prototype.$axios = axios
// Root容器
new Vue({
//挂载修改为router-view
render: h => h('router-view'),
// Vue官方组件挂载使用的方式
// 挂载路由
router,
// 挂载仓库
store
}).$mount('#app')
获取数据源的数据
直接获取
Xheader.vue
<template>
<header v-text="title"></header>
</template>
<script>
export default {
data() {
return {
title: "katsuki"
};
},
vuex仓库数据可以在任意生命周期函数获取,因为不是组件自身数据
beforecreate(){
// 来自vuex仓库
console.log(this.$store.state.author); // katsuki
// 来自组件定义
console.log(this.title); // undefined
}
};
</script>
Getter方式
先定义store
中的getters
再通过属性访问
Getter 会暴露为store.getters
对象,以属性的形式访问这些值,这种方式能对返回结果进行处理
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0,
author: 'katsuki'
},
mutations: {
increment(state) {
state.count++
}
},
// 定义获取数据的方法
getters: {
getAuthor(state) {
return state.author + 'chan';
}
}
})
export default store
Xheader.vue
<script>
export default {
beforecreate(){
console.log(this.$store.getters.getAuthor); // katsukichan
}
};
</script>
修改数据的方法
方案1:先定义store
中的mutations
,然后在组件触发$store.commit
事件
方案2:定义store
中的mutations
配合action
,action
中调用commit
,组件中用$store.dispatch
方法触发action
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0,
author: 'katsuki'
},
// 修改数据的方法 真正改数据的操作
mutations: {
editCount(state) {
state.count++
},
// 修改仓库中state中的author
editAuthor(state, data) {
state.author = data
}
},
getters: {
getAuthor(state) {
return state.author + 'chan';
}
},
// 异步的方法放这里
// 触发多个数据的改变才使用
// 触发mutations,其实就是把刚才commit从组件放出来,换个地方放到actions
actions: {
setAuthor(context, data) {
context.commit('editAuthor', data)
// context.commit('editCount') 可以进行多个修改
}
}
})
Xheader.vue
// 点击header,触发修改
<template>
<header @click="editAuthor" v-text="title"></header>
</template>
</body>
export default {
data() {
return {
// title: "katsuki"
};
},
methods: {
methods:{
editAuthor(){
// 触发store里面的mutations,把store里面的author改为kasami
this.$store.commit('editAuthor','kasami');
// 直接触发action 间接触发mutations
this.$store.dispatch('setAuthor', "kasami");
}
},
computed: {
// 从仓库里面去取值
title(){
return this.$store.getters.getAuthor;
}
}
};
</script>
Module
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 类似数据库的表
const moduleA = {
state: {
age: '18'
}
}
const moduleB = {
state: {
age: '17'
}
}
const store = new Vuex.Store({
// 该库存数据的地方
state: {
count: 0,
author: 'katsuki',
// module写入
modules: {
moduleA,
moduleB
}
}
})
export default store
Vuex组件间数据的流动
组件(异步) | > | dispatch | > | 触发action | > | commit | > | 触发mutations | > | 修改state 的数据 |
---|---|---|---|---|---|---|---|---|---|---|
组件(同步) | > | > | > | > | > | commit | > | 触发mutations | > | 修改state 的数据 |
组件 | > | getters | > | 触发getters | > | 获取state 的数据 |
组件之间通过获取改变Vuex
中state
的值达到通信互动效果
loading案例
上面项目的概念案例的基础上添加
在components文件夹
中添加Xloading.vue
组件
Xloading
组件添加到Katsuki
页面组件
Katsuki.vue
<template>
<div>
<Xheader />
<Xsearch />
<Xpanel />
<Xloading />
</div>
</template>
<script>
// 引用组件
import Xheader from '../components/Xheader.vue'
import Xsearch from '../components/Xsearch.vue'
import Xpanel from '../components/Xpanel.vue'
import Xloading from '../components/Xloading.vue'
export default {
components: {
Xheader,
Xsearch,
Xpanel,
Xloading
}
}
</script>
store.js
中对应添加部分
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
// 该库存数据的地方
state: {
count: 0,
author: 'katsuki',
// loading判断值,为true显示,为false隐藏
loadingStatus: false
},
// 修改数据的方法
mutations: {
editLoading(state, bool){
state.loadingStatus = bool
}
},
getters: {
getLoading(state){
return state.loadingStatus
}
}
})
export default store
Xloading.vue weui加载样式调用
<template>
<div id="loadingToast" v-show="loadingStatus">
<div class="weui-mask_transparent"></div>
<div class="weui-toast">
<i class="weui-loading weui-icon_toast"></i>
<p class="weui-toast__content">数据加载中</p>
</div>
</div>
</template>
<script>
export default {
computed: {
loadingStatus(){
// 直接获取方式
return this.$store.state.loadingStatus
// getters获取方式
return this.$store.getters.getLoading
}
}
};
-------------mapState法-------------
遍历仓库中State中的内容,全部取出
import { mapState } from "vuex";
export default {
computed: {
// mapState方法,从公有数据取出绑定到组件上
...mapState({
loadingStatus: state => state.loadingStatus
})
}
};
</script>
观察者模式(发布订阅模式)
必须先订阅后发布顺序
class Observer {
constructor(){
//空的队列
this.list ={
// 执行下方obs.on结果
// 'katsuki': [()=>{
// console.log('katsui1')
// }]
}
}
// 监听 订阅者
on(key,fn){
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
}
// 触发 发布者 观察者
emit(key,parmas){
// 把所有存着回调函数的数组给取出来
let fns = this.list[key];
// 如果数组队列为空,则返回
if (!fns || fns.length === 0) {
return false;
}
// 如果不为空,遍历所有的函数执行
fns.forEach(fn => {
fn(parmas);
});
}
}
// vue中叫事件总线
var obs = new Observer();
// 订阅者,一般监听一次
// 存着一套技能
obs.on('katsui',()=>{
console.log('katsui1')
})
obs.on('katsui',()=>{
console.log('katsui2')
})
obs.on('kasami',()=>{
console.log('kasami')
})
obs.on('click',(data)=>{
console.log(data)
})
console.log(obs);
// 触发订阅者技能
obs.emit('katsui');
obs.emit('click', 'katsukichan');
obs.emit('kasami');
搜索框显示对应内容案例
比如一个vue页面组件中有Xsearch
搜索框和Xcontent
内容展示组件,进行组件之间通信,搜索框组件输入内容,内容展示组件显示对应内容
observer.js
class Observer {
constructor() {
// 空的队列
// 事件队列,发布者和订阅者的供需关系来去决定去向
this.list = {}
}
// 监听 订阅者
on(key, fn) {
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
}
// 触发 发布者
emit(key,params) {
// 把所有存着回调函数的数组给取出来
let fns = this.list[key];
// 如果数组队列为空,则返回
if (!fns || fns.length === 0) {
return false;
}
// 如果不为空,遍历所有的函数执行
fns.forEach(fn => {
fn(params);
});
}
}
export default new Observer;
Xsearch.vue
<template>
<!-- 文件中输入标签 -->
<input v-model="searchInput" type="search" placeholder="搜索">
</template>
<script>
import observer from '../libs/observer.js'
export default {
data() {
return {
// 搜索框的值
searchInput:""
};
},
watch: {
searchInput(){
// 发布事件 要求执行observer.js中名称为search的函数
observer.emit('search',this.searchInput)
}
}
};
</script>
Xcontent.vue
<script>
import observer from '../libs/observer.js'
export default {
data() {
return {
// 获取到Xsearch输入的值,这就是实现了组件通信
searchInput:""
};
},
created() {
// 订阅者 等待被执行
// 往observer.js中this.list = {}推入函数
observer.on('search',(data)=>{
this.searchInput = data
})
}
};
</script>
props父子孙数据集成
用于同一组件呈现不同状态
案例
我们日常使用的微信,点击底部的按键会进行页面的路由切换,头部显示对应不同的文字,用props做出效果。
假设有Katsuki
和Kasami
两个页面组件,都含有Xheader
头部组件,能点击底部栏Xfooter
组件进行路由切换,做上面描述效果。若不清楚路由切换,可参考上篇Vue_router
博文
Katsuki.vue
<template>
<div>
<Xheader :name="name"/>
</div>
</template>
<script>
import Xheader from '../components/Xheader.vue'
export default {
data(){
return {
name: "katsuki"
}
},
components: {
Xheader
}
}
</script>
Kasami.vue
<template>
<div>
<Xheader name="kasami"/>
</div>
</template>
<script>
import Xheader from '../components/Xheader.vue'
export default {
components: {
Xheader
}
}
</script>
Xheader.vue
<template>
<header v-text="name"></header>
</template>
<script>
export default {
// 获取父组件传过来的属性值,让子组件呈现对应的状态
// 上面的页面组件中Xheader的name部分和这里对应
// 举例: <Xheader katsuki="666" kasami="777"/>
// props:['katsuki','kasami']
// v-text="katsuki"拿到666 v-text="kasami"拿到777
props:['name']
};
</script>
<style scoped>
header {
height: 50px;
line-height: 50px;
width: 100%;
text-align: center;
color: pink;
background-color: gray;
}
</style>