• 非父子组件传值
  • 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. SpringCloud(五)之Spring Cloud 中 Feign结合Hystrix断路器开发实战

    1.先讲hystrx(断路器) 在springcloub 中的使用 1.1  加入依赖 注意:网上新旧版本问题,所以要以官网为主,不然部分注解会丢失最新版本 2.0 <dependency> ...

  2. chrome新版本flash无法在http网站上运行的解决办法

    最近遇到一个问题,就是用chrome浏览器打开网站后台以后,使用flash插件上传文件失败,提示flash初始化失败,于是打开chrome的内容设置,准备启用flash功能,打开浏览器,在地址栏中输入 ...

  3. vue指令实现拖动的高级写法

    不熟悉vue自定义指令看这里: https://cn.vuejs.org/v2/guide/custom-directive.html vue指令实现拖动方法很方便也挺简单,但是网上大部分的教程代码, ...

  4. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_3-4.动态Sql语句Mybaties SqlProvider

    笔记 4.动态Sql语句Mybaties SqlProvider     简介:讲解什么是动态sql,及使用 1.             @UpdateProvider(type=VideoSqlP ...

  5. R语言与概率统计(二) 假设检验

    > ####################5.2 > X<-c(159, 280, 101, 212, 224, 379, 179, 264, + 222, 362, 168, 2 ...

  6. Centos安装openjdk

    转载自:https://blog.csdn.net/youzhouliu/article/details/51183115 openjdk在linux各个平台下安装源中可以找到. 命令查找安装源中有什 ...

  7. Spring-Kafka —— 消费后不提交offset情况的分析总结

    最近在使用kafka,过程中遇到了一些疑问,在查阅了一些资料和相关blog之后,关于手动提交offset的问题,做一下总结和记录. 消费端手动提交offset代码如下: /** * 这是手动提交的消费 ...

  8. 购物车实现 <Block实现回调>

    效果图如下: 具体代码实现如下: Model: #import <Foundation/Foundation.h> @interface ShopCarModel : NSObject @ ...

  9. Python 面向对象--继承,实现,依赖,关联,聚合,组合

    一. 继承 继承指的是子类继承父类除私有内容以外的其他所有内容, 并且子类具有增加自己新内容的能力. 举例说明: class Animal: print("吃是动物的本能") cl ...

  10. document.documentElement.clientHeight 和 document.body.clientHeight

    document.documentElement.clientHeight 和 document.body.clientHeight 介绍 在进行一些网页效果处理的时候,经常碰到document.do ...