• 非父子组件传值
  • vuex

一、非父子组件传值

基于父子组件通信与传值实现非父子组件传值的示例关键代码:

 <template>
<div>
<!-- 学员展示 -->
<add-student @add="add"></add-student> <!--监听子组件自定义事件add,触发自身add事件-->
<student-list :student-list="studentList"></student-list> <!---基于v-bind建立数据通道-->
</div> </template> <script>
import AddStudent from '@/components/student/AddStudent'
import StudentList from '@/components/student/StudentList' export default {
components:{
AddStudent,
StudentList
},
data(){
return {
studentList:[]
}
},
methods:{
add(name){ //调用由addStudent触发的add事件,实现向父级data中添加数据
this.studentList.push(name);
}
}
}
</script>

父组件Student.vue关键代码

 <template>
<div>
添加学生:
<input type="text" v-model="name"/>
<button @click="add">确认添加</button>
</div>
</template> <script> export default {
data(){
return {
name:''
}
},
methods:{
add(){
this.$emit('add',this.name) //触发父级监听事件add
}
}
}

子组件AddStudent.vue(示图子组件A)

 <template>
<div>
学生列表:
<ul>
<li v-for="(item,index) in studentList"
:key="index+item">
{{item}}
</li>
</ul>
</div> </template> <script>
export default {
props:['student-list']
}
</script>

子组件StudentList.vue(示图子组件B)

通过这个简单的示例看示可以通过上面的方法解决非父子组件传值问题,那么问题来了,实际的业务需求中如果出现了很多个组件依赖一个组件传值呢?而且还可能出现组件嵌套层级更复杂的情况:(示图中的依赖A是表示依赖A传值,不是依赖组件A)

当遇到这种复杂的依赖时,我们会想到它们都是基于父级$emit来监听组件A,然后在通过父级的数据变化来相渲染,那是不是就可以通过一个vue实例在组件A中监听组件A,并且每个依赖A的组件都可以访问得到该vue实例,然后再在每个依赖A的组件中触发监听事件:

事件监听vueObje.$emit:

在vue原型链上绑定实例vue.prototype.vueObje;

在每个依赖A的组件中触发监听事件:this.vueObje.$on('监听事件', 数据 =>{数据渲染})

所以上面的示例可以做以下修改:

Vue.prototype.bus = new Vue(); // 在main.js入口文件中给vue的原型链上添加vue实例bus

Student.vue组件模板修改:

<template>
<div>
<add-student></add-student>
<student-list></student-list>
</div>
</template>

AddStudent.vue组件中触发bus的监听事件:

 methods:{
add(){
this.bus.$emit('add',this.name) //触发父级监听事件add
}
}

StudentList.vue组件中在实例创建完成以后使用钩子函数created()添加bus的监听事件:(注意:需要将props去掉)

 created(){
this.bus.$on('add',name => {
this.studentList.push(name);
})
}

基于单个组件向多个非父子组件传值可以使用vue实例的事件监听机制来实现,但是如果业务需求是多个组件拥有修改数据的权限,并且同时影响多个组件的数据渲染,这种复杂的数据关系那又怎么办?

这时候就可以使用vuex来解决这种相对比较复杂的数据关系,并且vuex有更完善的数据管理模式,进入第二节详细解析vuex的应用:

二、vuex

官方手册:https://vuex.vuejs.org/zh/

安装:vue add vuex

安装完成以后在src文件夹下新增了一个js文件:store.js

 import Vue from 'vue' //引入vue
import Vuex from 'vuex' //引入vuex Vue.use(Vuex) //让vue使用vuex export default new Vuex.Store({ //通过vuex.Store创建vuex实例
state: { },
mutations: { },
actions: { }
})

然后还要再入口文件中引入这个vuex实例:

import store from './store' //main.js中引入vuex实例
 1.state —— 公共数据池

在store.js的state中添加数据字段,然后在组件中使用:

 state: {
//公共数据池
name:'他乡踏雪'
},

在组件a中使用公共数据池的数据:

 <template>
<div>
{{storeName}}
</div>
</template>
//js数据
data(){
return {
storeName:this.$store.state.name
}
},

在组件b中使用公共数据池的数据:

 <template>
<div>
{{$store.state.name}}
</div>
</template>

然后在组件c中通过点击事件修改公共数据池的数据:

 <button @click="ceshi">确认添加</button>
...
methods:{
ceshi(){
this.$store.state.name += 1;
}
}

在没有触发点击事件之前,在页面上能看正确的渲染结果“他乡踏雪”,但是当触发ceshi点击事件后,只能看到组件b中的数据更新了“他乡踏雪1”,这个问题并不在vuex,而是组件a中的逻辑出现了错误,因为在data中赋值的是字符串,storeName的栈内存是“他乡踏雪”,而并非$store.state.name的引用依赖,所以当$store.state.name被修改后并不能实现a组件的storeName的修改,这是模板语法中计算属性的相关内容:https://www.cnblogs.com/ZheOneAndOnly/p/11003014.html

所以,在组件a中应该使用计算属性来实现数据绑定:

 computed:{
storeName(){
return this.$store.state.name
}
}

按照上面这种直接使用this.$store.state.name的方式获取数据没有什么错误,但是如果有非常多的数据需要获取还是这么写吗?看下面的操作:

 //store.js
state: {
//公共数据池
name:'他乡踏雪',
age:18
}
//组件a
<script>
import {mapState} from 'vuex' xport default {
data(){
name:''
},
computed:mapState(['name','age'])
}

这时后在页面上你能正确的看到{{age}}的渲染数据,但是发现{{name}}没有正确渲染,原因又处在模板语法中优先使用了data中的数据,与计算属性computed中获取到的name冲突了,这种情况我们只能考虑给计算属性中的数据做别名处理,毕竟修改data中的数据名称在实际业务中不大可能,不用但是vuex还提供了键值对的方式获取数据池中的数据:

 computed:mapState({
storeName:state => state.name,
storeAge: state => state.age
})

显然上面的写法还有一个问题,使用将mapState直接赋值给了计算属性字段computed,那如果需要使用计算属性定义组件自身的数据呢?mapState整体返回的是一个对象,但对象内部的每一项都是一个函数,与computed的结构一样,所以就可以使用ES6的写法展开这个对象:(获取数据池中的最后的正确写法)

 computed:{
...mapState({
storeName:state => state.name,
storeAge: state => state.age
}),
a (){
return 'aaaa'
}
}
 2.getters —— store的计算属性

如果在一个应用中出现了数据的派生模式,而且还有多个组件依赖同一个派生模式,这时候我们一定会想到计算属性,在vuex中也有类似computed的属性getters:

 state: {
//公共数据池
studentList:[]
},
getters:{
// 相当于计算属性
newStudent(state){
return state.studentList.map((item,index) => {
if(index == 0){
return '**' + item
}else if(index == 1){
return item + '**'
}else{
return '++' + item + '++'
}
})
}
}

然后在组件中获取这个getters的派生数据:

 computed:{
newStudent(){
return this.$store.getters.newStudent //获取store中的getters的派生数据
}
}

当然同样也可以使用map方法来获取数据的对象模式:

 import { mapState, mapGetters } from 'vuex'; //通过map方式获取数据
//
computed:{
...mapGetters(['newStudent'])
}

getters的数据别名就不需要state参数来获取了,其实state中的数据也可以不用,但是为了区分state和getters中的数据,当然还有防止命名冲突问题:

...mapGetters({student:'newStudent'})

最后,getters在构建派生数据时,还可以传入第二个参数,这个数据就是自身getters,然后通过自生可以在构建派生数据时引用自身的数据:

 newA(state,getters){
return state.studentList.map(item => {
return item + getters.newB
})
},
newB(){
return 'B'
}
 3.修改数据:Mutation、Actiony以及严格模式

通过前面的state和getters解决了数据获取的统一方式,但是数据写入和修改呢?如果有多个组件有同样的数据写入和修改操作,还是要到每个组件中去重复的写吗?这明显不符合程序的设计思想,所以vuex提供了Mutation和Actiony来解决数据写入操作:

而且在vuex的store实例中有一个strict字段,这个字段如果被赋值为true时,表示该store采用严格模式,不能在外部写入和修改数据,不然会报错:

也就说合理的修改数据方式应该定义在Mutation中:

 export default new Vuex.Store({
strict:true,
state: {
//公共数据池
studentList:[]
},
getters:{},
mutations: {
changeStudent(state,name){
state.studentList.push(name);
}
},
actions: {}
})

然后在组件重调用这个方法:【通过$store.commit()调用mutations中的方法】

 <button @click="add">确认添加</button>
...
methods:{
add(){
this.$store.commit('changeStudent',this.name)
}
}

需要注意的是,commit只能接收两个参数,第一个参数是要调用mutations中的方法名称,第个参数是执行方法需要的参数,所以如果有多个参数就需要使用对象的键值对方式来传值(上面的代码可以修改为):

 //mutations
changeStudent(state,{name}){ //使用键值对的方式接收
state.studentList.push(name);
}
//组件中的方法传值也同样采用键值对的方式
add(){
this.$store.commit('changeStudent',{name:this.name})
}

接着新的问题又出现了,一般情况下的写入和修改可以符合严格模式的要求,如果有异步的指令则使用mutations来操作数据的话由于this指向了全局同样会导致不符合严格模式的编程规范:

 mutations: {
changeStudent(state,{name}){
setTimeout(() => {
state.studentList.push(name);
},1000)
}
}

这样的写法同样会导致前面一样的问题出现,而且还会导致一些其他辅助性工作也不好做,比如使用vue.js devtools没有办法准确追踪执行栈:

解决store中的异步数据写入操作就是使用Actiony来解决:

 mutations: {
changeStudent(state,{name}){
state.studentList.push(name);
}
},
actions: {
changeStudent(ctx,{name}){ //ctx表示数据操作的执行上下文
setTimeout(() => {
ctx.commit('changeStudent',{name}) //在异步程序中通过ctx.commit调用mutations中的数据操作方法
},1000)
}
}
//在组件方法中调用actions中的异步数据操作方法:
methods:{
add(){
this.$store.dispatch('changeStudent',{name:this.name}) //调用actions中的方法要使用dispatch方法来调用,语法与commit一致
}
},

到了这里解决了实际一些问题以后我么又会发现调用同各国this.$store.dispatch()调用actions中的方法有必要在组件实例中的methods中写一个方法来调用吗?可不可以和mapState、mapGetters一样,使用map的方法直接引入store中的方法,这个当然也是可行的,语法也基本一致:

 <button @click="changeStudent">确认添加</button>
...
import {mapState, mapActions} from 'vuex'
...
methods:{
...mapActions(['changeStudent']) //同样使用键值对的方式实现别名
}
 3.store模块化

未完待续:

vue进阶:vuex(数据池)的更多相关文章

  1. id,is的用法,小数据池的概念及编码知识进阶

    一:id 查询内存地址 name = 'alex' print(id(name)) li = [1,2,3] print(id(li)) 二:is  判断的是内存地址 name1 = 'alex@' ...

  2. python之路day06--python2/3小区别,小数据池的概念,编码的进阶str转为bytes类型,编码和解码

    python2#print() print'abc'#range() xrange()生成器#raw_input() python3# print('abc')# range()# input() = ...

  3. 十、Vue:Vuex实现data(){}内数据多个组件间共享

    一.概述 官方文档:https://vuex.vuejs.org/zh/installation.html 1.1vuex有什么用 Vuex:实现data(){}内数据多个组件间共享一种解决方案(类似 ...

  4. Vue之Vuex

    一.什么是vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.简单来说就是一个数据统一 ...

  5. 页面刷新vuex数据消失问题解决方案 之 vuex中间件

    之前我写了一篇用ES6 Proxy方案解决数据同步的文章 页面刷新vuex数据消失问题解决方案. 今天和同事沟通这个vuex数据还原问题,我说我的方法很奇异.聊着聊着,同事咋不用  store.sub ...

  6. Vue中Vuex的详解与使用(简洁易懂的入门小实例)

    怎么安装 Vuex 我就不介绍了,官网上有 就是 npm install xxx 之类的.(其实就是懒~~~哈哈) 那么现在就开始正文部分了 众所周知 Vuex 是什么呢?是用来干嘛的呢? Vuex ...

  7. vue+vue-router+vuex实战

    shopping vue + vue-router + vuex实现电商网站 效果展示 install 下载代码: git clone https://github.com/chenchangyuan ...

  8. 15.vue动画& vuex

    Vue.config.productionTip = false; ==是否显示提示信息== ==import/export== export xxx 必须跟跟对象或者和定义一起 对象: export ...

  9. 深入浅出的webpack4构建工具--webpack4+vue+route+vuex项目构建(十七)

    阅读目录 一:vue传值方式有哪些? 二:理解使用Vuex 三:webpack4+vue+route+vuex 项目架构 回到顶部 一:vue传值方式有哪些? 在vue项目开发过程中,经常会使用组件来 ...

随机推荐

  1. jwt扩展

    1.新建扩展类 package com.ireciting.uaaservice.config; import com.ireciting.uaaservice.pojo.TUser; import ...

  2. Android系统服务 —— WMS与AMS

    “可以毫不夸张的说,Android的framework层主要是由WMS.AMS还有View所构成,这三个模块穿插交互在整个framework中,掌握了它们之间的关系和每一个逻辑步骤,你对framewo ...

  3. @Transactional(事务讲解)和springboot 整合事务

    概述 事务在编程中分为两种:声明式事务处理和编程式事务处理 编程式事务处理:编码方式实现事务管理,常与模版类TransactionTemplate(推荐使用) 在业务代码中实现事务. 可知编程式事务每 ...

  4. <JavaScript>“浏览器模式”和“文档模式”之间的区别

    只有IE浏览器中才会有“浏览器模式”和“文档模式”,兼容性视图涉及两个重要的功能便是“浏览器模式[browser mode]”和“文档模式[document mode]”,在IE8/IE9中按F12键 ...

  5. 快速解决设置Android 23.0以上版本对SD卡的读写权限无效的问题

    快速解决设置Android 23.0以上版本对SD卡的读写权限无效的问题 转 https://www.jb51.net/article/144939.htm 今天小编就为大家分享一篇快速解决设置And ...

  6. 机器学习之保存与加载.pickle模型文件

    import pickle from sklearn.externals import joblib from sklearn.svm import SVC from sklearn import d ...

  7. 监控web80端口

    判断本机的80端口是否开启着,如果开启着什么都不做,如果发现端口不存在,那么重启一下httpd服务,并发邮件通知你自己. #! /bin/bashmail=123@123.comif netstat ...

  8. 九十:CMS系统之项目结构

    目录结构 cms模块 from flask import Blueprint bp = Blueprint('cms', __name__, url_prefix='/cms') @bp.route( ...

  9. 基于request的爬虫练习

    引言 概述 概念:基于网络请求的模块 作用:用来模拟浏览器发请求,从而实现爬虫 通用爬虫 步骤: 指定url 请求发送:get返回的是一个响应对象 获取响应数据: text返回的是字符串形式的响应数据 ...

  10. 编译安装了的nginx 添加http_ssl_module模块

    1.看下编译安装nginx的时候,都编译安装的哪些模块. [root@zabbix ~]# /usr/local/nginx/sbin/nginx -V nginx version: nginx/1. ...