三、vuex状态管理 ---kkb
render函数
一些场景中需要 JavaScript 的完全编程能力,这时可以用渲染函数,它比模板更接近编译器
render(h) {
return h(tag, {...}, [children])
}
createElement 函数
{ // 与 `v-bind:class` 的 API 相同, // 接受⼀一个字符串串、对象或字符串串和对象组成的数组 'class': { foo: true, bar: false }, // 与 `v-bind:style` 的 API 相同, // 接受⼀一个字符串串、对象,或对象组成的数组 style: { color: 'red', fontSize: '14px' }, // 普通的 HTML 特性 attrs: { id: 'foo' }, // 组件 prop props: { myProp: 'bar' }, // DOM 属性 domProps: { innerHTML: 'baz' }, // 事件监听器器在 `on` 属性内, // 但不不再⽀支持如 `v-on:keyup.enter` 这样的修饰器器。 // 需要在处理理函数中⼿手动检查 keyCode。 on: { click: this.clickHandler } }
插件
// 插件定义 MyPlugin.install = function (Vue, options) { // 1. 添加全局⽅方法或属性 Vue.myGlobalMethod = function () { // 逻辑... } // 2. 添加全局资源 Vue.directive('my-directive', { bind(el, binding, vnode, oldVnode) { // 逻辑... } // ... }) // 3. 注⼊入组件选项 Vue.mixin({ created: function () { // 逻辑... } //... }) // 4. 添加实例例⽅方法 Vue.prototype.$myMethod = function (methodOptions) { // 逻辑... } } // 插件使⽤用 Vue.use(MyPlugin)
例如:移动 $bus 到插件
// plugins/bus.js class Bus { constructor(){} emit(){} on(){} } Bus.install = function(Vue){ Vue.prototype.$bus = new Bus() }
export default Bus // main.js import Bus from './plugins/bus.js' Vue.use(Bus)
组件混入:mixin
混入(mixin)提供了一种分发Vue组件中可复用功能的灵活方式
// 定义⼀一个混⼊入对象 var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin!') } } } // 定义⼀一个使⽤用混⼊入对象的组件 var Component = Vue.extend({ mixins: [myMixin] })
例如:移动dispatch 和 broadcast到mixins
// mixins/emmiter.js // mixin 其实是一个对象 export default { methods: { dispatch(eventName, data) { let parent = this.$parent // 查找父元素 while (parent) { // 父元素用$emit触发 parent.$emit(eventName, data) // 递归查找父元素 parent = parent.$parent } }, boardcast(eventName, data) { boardcast.call(this, eventName, data) } } } function boardcast(eventName, data) { this.$children.forEach(child => { // 子元素触发$emit child.$emit(eventName, data) if (child.$children.length) { // 递归调用,通过call修改this指向child boardcast.call(child, eventName, data) } }) }
上面的混入(mixin),哪个组件想用的话,加入下面的代码就行了
import emmiter from '@/mixin/emmiter.js' // mixins 与 data、props、methods同级 mixins: [emmiter]
Vuex数据管理
Vuex是一个专为Vue.js应用开发的状态管理模式,集中式存储管理应用所有组件的状态。
Vuex遵循“单向数据流” 理念,易于问题追踪以及提高代码可维护性。
Vue中多个视图依赖于同一状态时,视图间传参和状态同步比较困难,Vuex能够很好解决该问题。
安装:
vue add vuex
核心概念
- state 状态、数据
- mutations 更改状态的函数
- actions 异步操作
- sotre 包含以上概念的容器
状态和状态变更
state 保存数据状态,mutations 用于修改状态
// store.js export default new Vuex.Store({ state: { count: 0 }, mutations: { increment(state, n=1) { state.count += n } } })
使用状态
// test.vue <template> <div class="home"> <h3>vuex test</h3> <p>count: {{$store.state.count}}</p> <button @click="addHandler">add</button> </div> </template> export default { methods: { addHandler(){ this.$store.commit('increment', 2) } } }
派生状态 --- getters
从state派生出新状态,类似计算属性
export default new Vuex.Store({ getters: { score(state){ return 'score记录:' + state.count } } })
使用方式
<span>{{$store.getters.score}}</span>
动作 --- actions
复杂业务逻辑,类似于controller
export default new Vuex.Store({ state: { count: 0 }, mutations: { increment(state, n=1) { state.count += n } }, actions: { incrementAsync({ commit }){ setTimeout(()=>{ commit('increment', 2) }, 1000) } } })
使用actions
<template> <div class="home"> <p>count: {{$store.state.count}}</p> <button @click="addAsync">add</button> </div> </template> <script> export default { methods: { addAsync(){ this.$store.dispatch('incrementAsync') } } } </script>
模块化
按模块化的方式编写代码
// store.js // 这里可以提出另一个文件,来进行单独管理 const count = { namespaced: true, state: { count: 0 }, mutations: { increment(state, n=1) { state.count += n } }, getters: { score(state){ return 'score记录:' + state.count } }, actions: { incrementAsync({ commit }){ setTimeout(()=>{ commit('increment', 2) }, 1000) } } } export default new Vuex.Store({ modules: { a: count } })
使用方式发生变化
<template> <div class="home"> <h3>vuex test</h3> <p>count: {{$store.state.a.count}}</p> <p>{{$store.getters['a/score']}}</p> <button @click="addAsync">add</button> </div> </template> <script> export default { methods: { addAsync(){ this.$store.dispatch('a/incrementAsync') } } } </script>
mapState,mapGetter,mapMutations 好处在于和$store 脱耦了
Vuex原理解析
功能实现:
- 维护状态state
- 修改状态commit
- 业务逻辑控制dispatch
- 状态派发getter
- 实现state响应式
- 插件
- 混入
// kvue.js let Vue function install(_Vue) { Vue = _Vue // 这样store执行的时候,就有了vue,不用import // 这也是为什么Vue.use必须在新建store之前 // 混入:把store选项指定到Vue原型上 Vue.mixin({ beforeCreate() { // 这样才能获取到传递进来的store // 只有root元素才有store,判断一下 if (this.$options.store) { Vue.prototype.$store = this.$options.store } }, }) } class Store { // options: {state:{count:0}, mutations:{...}} constructor(options = {}) { // 利用Vue的数据响应式---和Vue的紧耦合 this.state = new Vue({ data: options.state }) // 初始化mutations this.mutations = options.mutations || {} } // 触发mutations,需要实现commit // 注意这里用箭头函数形式,后面actions实现时会有作用 commit = (type, arg) => { // this指向Store实例 const fn = this.mutations[type] // 获取变更函数 fn(this.state, arg) } } // 这个就是Vuex,Vuex.Store 就是可以拿到store实例了 export default { Store, install }
实现dispatch
class Store { constructor(options = {}) { this.actions = options.actions || {} } commit = (type, arg) => { const fn = this.mutations[type] fn(this.state, arg) } dispatch(type, arg) { const fn = this.actions[type] return fn({ commit: this.commit, state: this.state }, arg) } }
实现getter
class Store { constructor(options = {}) { options.getters && this.handleGetters(options.getters); } handleGetters(getters) { this.getters = {}; // 定义this.getters // 遍历getters选项,为this.getters定义property // 属性名就是选项中的key,只需定义get函数保证其只读性 Object.keys(getters).forEach(key => { // 这样这些属性都是只读的 Object.defineProperty(this.getters, key, { get: () => { // 注意依然是箭头函数 return getters[key](this.state); } }); }); } }