组件通信及vuex原理

组件通信方式

父子组件通信

常用:props/$emit
父传子:
通过props向子组件传值。调用子组件时,可以用 静态传递 或者 使用 v-bind动态传递,子组件用props接受使用。

子传父:
通过$emit方法触发自定义事件。子组件通过this.$emit方法触发父组件上(调用子组件的那个语句)自定义事件,将值传递给自定义事件函数,通过$event获取。

其他:
v-model
子组件中,props中通过value字段来接收
子组件触发this.$emit(‘input’)事件,父组件改变v-model绑定的值
所以在组件中使用的时候,相当于下面的简写:

<custom v-bind:value="something" v-on:input="something = $event.target.value"></custom>

这通常用在子组件是表单元素的情况,例如调用elment-ui中的el-input等组件时。

.sync修饰符
允许props进行双向绑定,以this.$emit(update:PropName,newValue)的模式触发事件。

即:

<text-document v-bind:title.sync="doc.title"></text-document>

相当于:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

this.$emit('update:title', newTitle)

通常用在父子组件都需要改变传入值的情况,比如:在调用el-dialog的时候,visible属性支持.sync修饰符是为了子组件中右上角叉号点击时能改变父组件中visible的绑定值,这样就能关闭dialog。

this.$refs(不推荐使用,容易造成状态混乱)
首先你的给子组件做标记。demo :
然后在父组件中,通过this.$refs.one就可以访问了这个自组件了,包括访问子组件的data里面的数据,调用它的函数。
this.refs如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

无直接关系组件间通信

常用:bus总线事件 原理可以参考:https://blog.csdn.net/bingqise5193/article/details/109406910
与this.$emit原理一致,都是在同一个vue实例内实行的,只不过放在了一个新的vue内,同样是利用了vm.$on/vm.$off/vm.$emit接口来实现的。
1、let bus = new Vue({})
2、Vue.prototype.Bus = bus
3、触发组件内 this.Bus.emit(‘userdifined’, ‘chufa’)
4、接受事件组件内

			mounted () {
				this.Bus.$on('userdifined', res => console.log(res))
			},
			destroyed () {
				this.Bus.$off('userdifined')
			}

简易集中式状态管理器

以上方法适合两个或不多的组件之间传值,如果有以下情况:

  • 多视图依赖同一个状态
  • 不同的视图需要变更同一个状态

用上面的方法虽然可以实现,但是会造成状态管理混乱,这种情况需要一个多组件共享的响应式数据管理中心,在此来实现一个简易版的:

数据共享中心:store.js

export default {
    state: {
        data: 0
    },
    change(r) {
        this.state.data += r
    }
}

调用:

<template>
  <div>
      <h1>componentA</h1>
      <div>{{data.data}}</div>
      <button @click="change(1)">click to change</button>
  </div>
</template>

<script>
import store from '@/state/store'
export default {
    data() {
        return {
            // 应该要给一个对象,不能store.state.data
            // store.state.data为一个基础类型,赋值后,当store.state.data变更时与data并无关系
            // 而store.state是一个对象,赋值为地址赋值,data与store.state指向同一个对象,才能触发响应式
            data: store.state
        }
    },
    methods: {
        change(data) {
            store.change(data)
        }
    }
}
</script>

建立一个状态管理中心,用于管理所有共用的状态,并且如果改变这些状态必须通过中心的事件来更改,这样就能记录更改状态的所有操作。

vuex的使用

vuex是什么

一个集中式存储管理所有组件的状态,当多个组件均需要使用或改变这个状态时,适合用vuex。Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地更新。例如用户信息、菜单信息、购物车信息很适合用vuex来实现

vuex的使用步骤

1. 安装插件: npm i vuex -S
2. 载入插件:import Vuex from 'vuex'
3. 注册插件:Vue.use(Vuex)
4.	实例化存储对象:const store = new Vuex.Store({
  modules: {
    count,
    msg
  }
})
5. 挂载:new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
6. 使用存储对象
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
computed: {
    ...mapState({
      msg: state => state.msg.msg,
      count: state => state.count.count
    }),
    ...mapGetters(['reversedMsg'])
  },
  methods: {
    ...mapMutations(['changeCount']),
    ...mapActions(['changeCountAsync'])
  },

Tips:

  1. 具体的使用请参考vuex官方文档
  2. 更改state的操作必须通过commit mutations中的操作,mutations中只能有同步操作,这样dev tools能够记录回溯每次状态更改的情况,actions中可以有异步操作。
  3. actions中的第一个参数为context,但并不是实例,context对象包括以下:
    在这里插入图片描述

vuex使用实战-购物车例子

具体的项目代码请查看:shop-cart
几个小TIPS:

  • find/findIndex/filter/reduce/every的使用,reduce常用于求和
  • import/export,具体参考:
  • localStorage只能存储字符串,因此存储和取用对象时要注意
	JSON.parse(window.localStorage.getItem('cart-goods'))
    window.localStorage.setItem('cart-goods', JSON.stringify(state.goods))
  • 作用域插槽:父组件只能访问父组件中的值,如果想要访问子组件中的值,则可以通过v-slot来实现:在父组件中引用子组件的行信息
	<el-table-column prop="address"  label="操作">
        <template v-slot="scope">
          <el-button @click="addProducts(scope.row)">加入购物车</el-button>
        </template>
      </el-table-column>
  • 基于其他的值计算并响应式更改,请用vuex中的getters,另外,所有更改state的操作都要通过提交mutations。
  • 如果某个操作需要在每次 mutation 之后调用,可以使用store的plugin,使用如下:
const myPlugin = store => {
  // 当 store 初始化后调用
  store.subscribe((mutation, state) => {
    // 每次 mutation 之后调用
    // mutation 的格式为 { type, payload }
    // localStorage只能存储字符串
    window.localStorage.setItem('cart-goods', JSON.stringify(state.goods))
  })
}
export default new Vuex.Store({
  plugins: [myPlugin],
  state: {}
}
  • 在表单元素中,如果绑定的是vuex中的值,要用:value去绑定,不能用v-model,更改vuex中的值需要通过mutations,因此采用:value+@change等事件去使用,实现双向绑定。对于change等事件,当你传递了自定义参数的时候,还想得到原来那个默认参数,就手动传递一个 $event

vuex原理

简易手写版:

let _Vue = null
class Store {
    constructor(options) {
        const {
            state = {},
            getters = {},
            mutations = {},
            actions = {}
        } = options
        this.state = _Vue.observable(state)
        this.getters = Object.create(null)
        Object.keys(getters).forEach(key => {
            Object.defineProperty(this.getters, key, {
                get: () => getters[key](state)
            })
        })
        this._mutations = mutations,
        this._actions = actions
    }

    commit (type, payload) {
        this._mutations[type](this.state, payload)
    }

    dispatch (type, payload) {
        this._actions[type](this, payload)
    }
}

function install (Vue) {
    _Vue = Vue
    // 挂载$store
    _Vue.mixin({
        beforeCreate() {
            if(this.$options.store){
                // 如果是组件则不需要
                _Vue.prototype.$store = this.$options.store
            }
        }
    })
}

export default {
    Store,
    install
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值