关于Vue.js 2.0的Vuex 2.0 你需要更新的知识库


Posted in Javascript onNovember 30, 2016

应用结构

实际上,Vuex 在怎么组织你的代码结构上面没有任何限制,相反,它强制规定了一系列高级的原则:

1、应用级的状态集中放在 store 中。

2、改变状态的唯一方式是提交mutations,这是个同步的事务。

3、异步逻辑应该封装在action 中。

只要你遵循这些规则,怎么构建你的项目的结构就取决于你了。如果你的 store 文件非常大,仅仅拆分成 action、mutation 和 getter 多个文件即可。

对于稍微复杂点的应用,我们可能都需要用到模块。下面是一个简单的项目架构:

├── index.html
├── main.js
├── api
│   └── ... # 这里发起 API 请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 组合 modules 、export store
    ├── actions.js        # 根 action
    ├── mutations.js      # 根 mutations
    └── modules
        ├── cart.js       # cart 模块
        └── products.js   # products 模块

关于更多,查看 购物车实例。

Modules

由于使用了单一状态树,应用的所有状态都包含在一个大对象内。但是,随着我们应用规模的不断增长,这个Store变得非常臃肿。

为了解决这个问题,Vuex 允许我们把 store 分 module(模块)。每一个模块包含各自的状态、mutation、action 和 getter,甚至是嵌套模块, 如下就是它的组织方式:

const moduleA = {
 state: { ... },
 mutations: { ... },
 actions: { ... },
 getters: { ... }
}

const moduleB = {
 state: { ... },
 mutations: { ... },
 actions: { ... }
}

const store = new Vuex.Store({
 modules: {
 a: moduleA,
 b: moduleB
 }
})

store.state.a // -> moduleA's state
store.state.b // -> moduleB's state

模块本地状态

模块的 mutations 和 getters方法第一个接收参数是模块的本地状态。

const moduleA = {
 state: { count: 0 },
 mutations: {
 increment: (state) {
  // state 是模块本地的状态。
  state.count++
 }
 },

 getters: {
 doubleCount (state) {
  return state.count * 2
 }
 }
}

相似地,在模块的 actions 中,context.state 暴露的是本地状态, context.rootState暴露的才是根状态。

const moduleA = {
 // ...
 actions: {
 incrementIfOdd ({ state, commit }) {
  if (state.count % 2 === 1) {
  commit('increment')
  }
 }
 }
}

在模块的 getters 内,根状态也会作为第三个参数暴露。

const moduleA = {
 // ...
 getters: {
 sumWithRootCount (state, getters, rootState) {
  return state.count + rootState.count
 }
 }
}

命名空间

要注意,模块内的 actions、mutations 以及 getters 依然注册在全局命名空间内 —— 这就会让多个模块响应同一种 mutation/action 类型。你可以在模块的名称中加入前缀或者后缀来设定命名空间,从而避免命名冲突。如果你的 Vuex 模块是一个可复用的,执行环境也未知的,那你就应该这么干了。距离,我们想要创建一个 todos 模块:

// types.js

// 定义 getter、 action 和 mutation 的常量名称
// 并且在模块名称上加上 `todos` 前缀 
export const DONE_COUNT = 'todos/DONE_COUNT'
export const FETCH_ALL = 'todos/FETCH_ALL'
export const TOGGLE_DONE = 'todos/TOGGLE_DONE'
// modules/todos.js
import * as types from '../types'

// 用带前缀的名称来定义 getters, actions and mutations 
const todosModule = {
 state: { todos: [] },

 getters: {
 [types.DONE_COUNT] (state) {
  // ...
 }
 },

 actions: {
 [types.FETCH_ALL] (context, payload) {
  // ...
 }
 },

 mutations: {
 [types.TOGGLE_DONE] (state, payload) {
  // ...
 }
 }
}

注册动态模块

你可以用 store.registerModule 方法在 store 创建之后注册一个模块:

store.registerModule('myModule', {
 // ...
})

模块的 store.state.myModule 暴露为模块的状态。

其他的 Vue 插件可以为应用的 store 附加一个模块,然后通过动态注册就可以使用 Vuex 的状态管理功能了。例如,vuex-router-sync 库,通过在一个动态注册的模块中管理应用的路由状态,从而将 vue-router 和 vuex 集成。

你也能用 store.unregisterModule(moduleName) 移除动态注册过的模块。但是你不能用这个方法移除静态的模块(也就是在 store 创建的时候声明的模块)。

Plugins

Vuex 的 store 接收 plugins 选项,这个选项暴露出每个 mutation 的钩子。一个 Vuex 的插件就是一个简单的方法,接收 sotre 作为唯一参数:

const myPlugin = store => {
 // 当 store 在被初始化完成时被调用
 store.subscribe((mutation, state) => {
 // mutation 之后被调用
 // mutation 的格式为 {type, payload}。
 })
}

然后像这样使用:

const store = new Vuex.Store({
 // ...
 plugins: [myPlugin]
})

在插件内提交 Mutations

插件不能直接修改状态 - 这就像你的组件,它们只能被 mutations 来触发改变。

通过提交 mutations,插件可以用来同步数据源到 store。例如, 为了同步 websocket 数据源到 store (这只是为说明用法的例子,在实际中,createPlugin 方法会附加更多的可选项,来完成复杂的任务)。

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]
})

生成状态快照

有时候插件想获取状态 “快照” 和状态的改变前后的变化。为了实现这些功能,需要对状态对象进行深拷贝:

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

 // 对比 prevState 和 nextState...

 // 保存状态,用于下一次 mutation
 prevState = nextState
 })
}

** 生成状态快照的插件只能在开发阶段使用,使用 Webpack 或 Browserify,让构建工具帮我们处理:

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

插件默认会被起用。为了发布产品,你需要用 Webpack 的 DefinePlugin 或者 Browserify 的 envify 来转换 process.env.NODE_ENV !== 'production' 的值为 false。

内置 Logger 插件

如果你正在使用 vue-devtools,你可能不需要。

Vuex 带来一个日志插件用于一般的调试:

import createLogger from 'vuex/dist/logger'

const store = new Vuex.Store({
 plugins: [createLogger()]
})

createLogger 方法有几个配置项:

const logger = createLogger({
 collapsed: false, // 自动展开记录 mutation
 transformer (state) {
 // 在记录之前前进行转换
 // 例如,只返回指定的子树
 return state.subTree
 },
 mutationTransformer (mutation) {
 // mutation 格式 { type, payload }
 // 我们可以按照想要的方式进行格式化
 return mutation.type
 }
})

日志插件还可以直接通过 <script> 标签, 然后它会提供全局方法 createVuexLogger 。

要注意,logger 插件会生成状态快照,所以仅在开发环境使用。

严格模式

要启用严格模式,只需在创建 Vuex store 的时候简单地传入 strict: true。

const store = new Vuex.Store({
 // ...
 strict: true
})

在严格模式下,只要 Vuex 状态在 mutation 方法外被修改就会抛出错误。这确保了所有状态修改都会明确的被调试工具跟踪。

开发阶段 vs. 发布阶段

不要在发布阶段开启严格模式! 严格模式会对状态树进行深度监测来检测不合适的修改 —— 确保在发布阶段关闭它避免性能损耗。
跟处理插件的情况类似,我们可以让构建工具来处理:

const store = new Vuex.Store({
 // ...
 strict: process.env.NODE_ENV !== 'production'
})

相关引用

http://vuex.vuejs.org/en/plugins.html
http://vuex.vuejs.org/en/strict.html
http://vuex.vuejs.org/en/modules.html
http://vuex.vuejs.org/en/structure.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript Keycode对照表
Oct 24 Javascript
Jquery Ajax学习实例5 向WebService发出请求,返回泛型集合数据的异步调用
Mar 17 Javascript
Grid得到选择行数据的方法总结
Jan 17 Javascript
JavaScript高级程序设计 事件学习笔记
Sep 10 Javascript
Js实现滚动变色的文字效果
Jun 16 Javascript
教你如何在 Javascript 文件里使用 .Net MVC Razor 语法
Jul 23 Javascript
Node.js中使用Buffer编码、解码二进制数据详解
Aug 16 Javascript
基于dropdown.js实现的两款美观大气的二级导航菜单
Sep 02 Javascript
AngularJS模块学习之Anchor Scroll
Jan 19 Javascript
AngularJS基础 ng-if 指令用法
Aug 01 Javascript
每个程序员都需要学习 JavaScript 的7个理由小结
Sep 03 Javascript
mpvue微信小程序多列选择器用法之省份城市选择的实现
Mar 07 Javascript
Vuex2.0+Vue2.0构建备忘录应用实践
Nov 30 #Javascript
BootStrap实现响应式布局导航栏折叠隐藏效果(在小屏幕、手机屏幕浏览时自动折叠隐藏)
Nov 30 #Javascript
JavaScript实现拖拽元素对齐到网格(每次移动固定距离)
Nov 30 #Javascript
jquery.Callbacks的实现详解
Nov 30 #Javascript
javascript中活灵活现的Array对象详解
Nov 30 #Javascript
如何处理JSON中的特殊字符
Nov 30 #Javascript
Angular.JS判断复选框checkbox是否选中并实时显示
Nov 30 #Javascript
You might like
mysql_fetch_row,mysql_fetch_array,mysql_fetch_assoc的区别
2009/04/24 PHP
php自定文件保存session的方法
2014/12/10 PHP
PHP递归遍历多维数组实现无限分类的方法
2016/05/06 PHP
php 7新特性之类型申明详解
2017/06/06 PHP
Thinkphp5行为使用方法汇总
2017/12/21 PHP
Jquery中LigerUi的弹出编辑框(实现方法)
2013/07/09 Javascript
javascript跟随滚动效果插件代码(javascript Follow Plugin)
2013/08/03 Javascript
JS实现闪动的title消息提醒效果
2014/06/20 Javascript
jQuery遍历json中多个map的方法
2015/02/12 Javascript
第一次接触神奇的Bootstrap菜单和导航
2016/08/01 Javascript
BootStrap无限级分类(无限极分类封装版)
2016/08/26 Javascript
Bootstrap时间选择器datetimepicker和daterangepicker使用实例解析
2016/09/17 Javascript
关于List.ToArray()方法的效率测试
2016/09/30 Javascript
完美解决jQuery fancybox ie 无法显示关闭按钮的问题
2016/11/29 Javascript
jQuery实现弹幕效果
2017/02/17 Javascript
js中new一个对象的过程
2017/02/20 Javascript
Angular4开发解决跨域问题详解
2017/08/28 Javascript
Vue打包后出现一些map文件的解决方法
2018/02/13 Javascript
jquery 给动态生成的标签绑定事件的几种方法总结
2018/02/24 jQuery
微信公众平台 发送模板消息(Java接口开发)
2019/04/17 Javascript
js使用cookie实现记住用户名功能示例
2019/06/13 Javascript
[55:47]DOTA2上海特级锦标赛C组小组赛#2 LGD VS Newbee第三局
2016/02/27 DOTA
[06:43]2018DOTA2国际邀请赛寻真——VGJ.Thunder
2018/08/11 DOTA
python通过post提交数据的方法
2015/05/06 Python
详解duck typing鸭子类型程序设计与Python的实现示例
2016/06/03 Python
在Python的Flask框架中构建Web表单的教程
2016/06/04 Python
Python中%r和%s的详解及区别
2017/03/16 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
python3.6 tkinter实现屏保小程序
2019/07/30 Python
pytorch中交叉熵损失(nn.CrossEntropyLoss())的计算过程详解
2020/01/02 Python
plt.figure()参数使用详解及运行演示
2021/01/08 Python
职业道德模范事迹材料
2014/08/24 职场文书
八年级作文之一起的走过日子
2019/09/17 职场文书
「女孩的钓鱼慢活」全新版权绘公布
2022/03/21 日漫
Axios代理配置及封装响应拦截处理方式
2022/04/07 Vue.js
Python使用mitmproxy工具监控手机 下载手机小视频
2022/04/18 Python