178

vue 状态管理的一点思考 - 知乎专栏

 6 years ago
source link: https://zhuanlan.zhihu.com/p/29237682
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

vue 状态管理的一点思考

前端工程师

最近在写项目的时候碰到一个场景:一个地图应用,有个侧边栏,侧边栏里面放着很多选项,选项改变的时候,应用会根据侧边栏的条件请求数据,然后在地图上放 markers 。页面可以简单抽象成如下结构:

// App.vue

<div>
  <sidebar></sidebar>
  <map></map>
</div>

有很多种方式可以来完成上面的需求,我先介绍 2 种常见的做法,最后再给出一个比较另类但很有趣的方案。

1.直接上 vuex

通常来讲,有多个组件共享状态的时候,把共享的状态丢给 vuex 来处理是个不错的方案。但是在处理上面那个场景的时候,会显示的有点「笨重」,因为侧边栏实际上是一个表单,如果使用 vuex 的话,就需要为每个选项定义一套 mutation,失去了直接使用 v-model 的便利

直接使用组件状态绑定一个选项

// sidebar.vue

<input v-model="message">
// ...
data () {
  return {
    message: ''
  }
} 

当使用 vuex 绑定一个选项时,多了不少「模板」代码

// 定义 state, mutation

state: {
  message: ''
},
mutations: {
  updateMessage (state, message) {
    state.message = message
  }
}

// sidebar.vue

<input :value="message" @input="updateMessage">
// ...
computed: {
  ...mapState({
    message: state => state.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

2.将状态放到父组件上

如果 sidebar 和 map 有一个共同的父级,使用这种方式处理起来会比上面的简单很多。但是当我们的应用越来越大的时候,往往会把 sidebar 和 map 拆成颗粒度更小的组件,那么通过 props 一层层传给子组件也会变的非常麻烦。

3.将组件状态「共享」出来

我们通常把组件内的状态写成下面这种形式:

// ...
data () {
  return {
     message: ''
  }
}

实际上,我们可以把 data () {} 中返回的对象单独提取到外面,作为一个变量,像下面这种写法:

const state = { message: '' }
// ...
data () {
  return state
}

那么在这个组件初始化过程中,state 对象会被 vue 「响应式化」,这会引出一个有趣的事情:任何组件,只要模版中使用了 state.message ,当 state.message 改变时,页面都会被同步更新。

知道了这个之后,我们就可以将侧边栏的状态写成一个独立的文件,作为一个模块引入其他组件中,结构如下:

// state.js
export default { message: '' }


// sidebar.vue
<input v-model="state.message" />

import state from 'path/to/state.js'
// ...
data () {
  return { state }
}


// map.vue
// 在模版中使用侧边栏的状态
<div>{{ state.message }}</div>

import state from 'path/to/state.js'
// ...
data () {
  return { state }
},
created () {
  // 将侧边栏的状态作为参数去请求数据
  axios.get('/xxxx', { params: state })
}

这样做的一个好处是,既可以在 sidebar.vue 中把 state 当作「内部」状态,愉快的使用 v-model 绑定数据,map.vue 中也可以方便的拿到 state 做为参数请求,同时还可以直接在 map.vue 的模版中使用 `state.message`。

更进一步

上面的方案中,state「响应式化」是发生在子组件初始化的过程中,我希望能在应用开始的时候,主动在某个时刻完成这一步。这个时候就可以借助 Vue 构造函数,像下面这样:

// store.js

import state from 'path/to/state.js'

new Vue({
  data: {
    state
  }
})

然后在主文件 main.js 中引入 store.js,state 就会被「响应式化」。

总结

本文阐述了在这个特定场景下,对于如何管理侧边栏状态的一些思考,希望大家看完后有所收获 :)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK