FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

Vuex那些事儿 #106

Open FrankKai opened 5 years ago

FrankKai commented 5 years ago

用vuex也有一段时间了,但仅仅是很基础的用,没有学习高级特性,更没有深入过源码 由于手上负责的聊天项目较为复杂,深入学习vuex更显得很有必要,从而优化聊天部分的前端实现

基于以上目的,我将在这里记录自己在实践过程中的一些总结。

FrankKai commented 5 years ago

mapState与mapGetter的区别是什么?

我们都知道,这是我们从vuex查询数据的两种方式,那这两者到底有什么区别呢? 直接看二者的定义:

// https://vuex.vuejs.org/api/#mapstate
mapState(namespace?: string, map: Array<string> | Object<string | function>): Object
// https://vuex.vuejs.org/api/#mapgetters
mapGetters(namespace?: string, map: Array<string> | Object<string>): Object

如果看定义看不懂的话,可以移步TypeScript入门,或者到TypeScript官网自己学习,了解TypeScript中的Optional Property和strictNullChecks。 二者的第一个参数都是可选的命名空间,第二个是数组或者对象的map。

mapState:

mapState(['bar','baz']) // 相当于state.bar
mapState({
    foo: 'foo',
    bar: state => state.bar,
    baz(state) {
        return state.baz + 'hhh'; // 用做计算
    }
})
mapState('foo',['bar','baz']) // 这种形式不常用

mapGetters:

mapGetters(['bar', 'baz']) // 直接映射
mapGetters({
    barBuddy: 'bar',  // 重命名映射
    bazBuddy: 'baz',
})

其实mapState和mapGetters的真正区别在于:

FrankKai commented 5 years ago

Vuex的plugins有什么用?

暴露每个mutation的hooks,只接收一个store(初始化好的store)为参数。

最简单的开发和注入:

const myPlugin = store => {
  store.subscribe((mutation, state) => { })
}
const store = new Vuex.Store({
  plugins: [myPlugin]
})

又见subscribe,这仍然是基于事件的"发布订阅者"编程模型,关于发布订阅者模型,可以参考webhook到底是个啥?

Plugin内部提交mutation

Plugins不能直接修改状态--这和我们的组件一样,可以通过commit mutation触发更改。 通过commit mutation,插件可以同步数据到store。例如sync一个websocket数据源到store,这个contrived example里, 函数里可以执行很多更加复杂的任务:

export default function createWebSocketPlugin (socket) {
  return store => {
    socket.on('data', data => {
      store.commit('receiveData', data)
    })
    store.subscribe(mutation => {
      if (mutation.type === 'UPDATE_DATA') {
        socket.emit('update', mutation.payload)
      }
    })
  }
}
const plugin = createWebSocketPlugin(socket)

const store = new Vuex.Store({
  state,
  mutations,
  plugins: [plugin]
})

获取状态快照

state的"snapshots"指的是,对mutation的状态做保留,例如当前的post-mutation state和pre-mutation state做对比。可以通过深拷贝实现:

const myPluginWithSnapshot = store => {
  let prevState = _.cloneDeep(store.state)
  store.subscribe((mutation, state) => {
    let nextState = _.cloneDeep(state)

    // compare `prevState` and `nextState`...

    // save state for next mutation
    prevState = nextState
  })
}

snapshot适合在development阶段使用。当结合webpack或者Browserify,我们可以让构建工具处理:

const store = new Vuex.Store({
    plugins: process.env.NODE_ENV !=='production' ? [myPluginWithSnapshot] : []
})

此处的环境变量的获取,需要用到webpack的DefinePlugin 这个webpack内置插件。关于DefinePlugin,可以参考Webpack Plugin那些事儿

Built-in Logger插件

用于在控制台输出store的mutation信息,其实也是一种state snapshot。 信息内容包括:

相比vue-devtools,有以下优势:

/store/logger.js

import createLogger from 'vuex/dist/logger';

const subscribedMutations = ['USER_INIT', 'DEVICE_EXPEND_STATUS'];

const logger = createLogger({
  collapsed: false,
  filter: (mutation) => subscribedMutations.includes(mutation.type),
  transformer: (state) => state,
  mutationTransformer: (mutation) => mutation, 
  logger: console,
});
export default logger;

/store/index.js

import logger from './logger';
export default new Vuex.Store({
    plugins: [logger],
})

日志输出就像下面这样: image

注意:logger plugin仅仅用于development环境,因为它也本身属于state snapshot。

FrankKai commented 5 years ago

this.$store.commit()和mapMutation()哪种更好?

当然是mapMutation,可以统一查看当前组件有多少mutation类型,但是需要注意命名不能和组件方法冲突。

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 不带payload映射,map `this.increment()` to `this.$store.commit('increment')`
      'incrementBy' // 带payload映射,map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 重命名: map `this.add()` to `this.$store.commit('increment')`
    })
  }
}

直接使用this.$store.commit():

methods: {
   foo() {
       this.$store.commit('USER_INIT'); 
   }
   ...
   ...
   bar() {
         this.$store.commit('USER_UPDATE'); 
    }
}

当业务逐渐变复杂时,foo和bar之间会相隔很远,很难一目了然看清当前组件用到了哪些mutation。 使用mapMutatinos:

methods: {
   ...mapMutations([
       'USER_INIT',
       'USER_UPDATE',
   ]),
   foo() {
        this['USER_INIT']();
   }
   ...
   bar() {
        this['USER_UPDATE']();
   }
}

很明显,这样很清晰。

FrankKai commented 5 years ago

mutation与action区别是什么?

mutation可以结构多个入参数吗?

当然,加个判断就好了。

FrankKai commented 4 years ago

Modules的namespacing是什么操作?

上述内容完全可以满足需求,其他内容暂时不用涉猎。

FrankKai commented 3 years ago

一次vuex的状态变更经历哪些步骤?

组件内的方法 commit (1.判断是否当前类型的mutation2.若有触发mutation)

Vuex是如何与Vue框架做双向数据绑定的?

使用了Vue实例作为全局状态树_vm($$state) deep watch所有属性