Vue_组件通信

组件通信四大方案

  • 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文档

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的项目基础上添加
vuex目录

导出仓库

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配合actionaction中调用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组件间数据的流动

表格来源Eno Yaovue-tutorial

组件(异步)>dispatch>触发action>commit>触发mutations>修改state的数据
组件(同步)>>>>>commit>触发mutations>修改state的数据
组件>getters>触发getters>获取state的数据

组件之间通过获取改变Vuexstate的值达到通信互动效果

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做出效果。
假设有KatsukiKasami两个页面组件,都含有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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值