1. Vuex в действии
В последней статье были представлены различные способы связи между компонентами Vue.Среди них vuex является в основном окончательным решением.Это ничего не говорит, просто вставьте код напрямую.
Так называемая структура управления данными основных структур, в принципе, это независимая группа, все головы слишком устали, поэтому Лао Ли устал только от военных, дыма от бомб и почвы, а политический комитет Чжао - это наш VUEX. , данные, совместно используемые группой, будут объединены через Чжао Чжэнчжан.
Мой стиль - б/у вещи, вроде изготовления колес, используя для этого только реальную основу, смотрите кодовое слово не говорите
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state,n=1) {
state.count += n
}
},
actions:{
incrementAsync({commit}){
setTimeout(()=>{
commit('increment',2)
},1000)
}
}
})
// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App),
}).$mount('#app')
<template>
<div id="app">
<div>冲啊,手榴弹扔了{{$store.state.count}}个</div>
<button @click="add">扔一个</button>
<button @click="addAsync">蓄力扔俩</button>
</div>
</template>
<script>
export default {
name: 'app',
methods:{
add(){
this.$store.commit('increment')
},
addAsync(){
this.$store.dispatch('incrementAsync')
}
}
}
</script>
2. Реализуйте свой собственный Vuex
Чтобы реализовать Vue, первое, что нужно реализовать, — это Vue.use (Vuex), который представляет собой механизм Vue для установки плагинов.Vuex должен предоставить метод установки внешнему миру и передать Vue функции установки.Давайте проверим это немного.
3. Реализовать механизм плагинов
Новый zhao (Политический комиссар Чжао).js предоставляет метод установки извне и внутренне монтирует переменную $store в компоненте Vue.
class Store {
constructor() {
this.name = '赵政委'
}
}
function install(Vue) {
Vue.prototype.$store = new Store()
}
export default { Store, install }
// app.vue
<template>
<div id="app">
</div>
</template>
<script>
export default {
name: 'app',
created(){
console.log(this.$store)
}
}
</script>
// output Store {name: "赵政委"}
4. Пройти мимо магазина
Когда он фактически используется, хранилище передается через новый Vue. Нам нужно использовать миксин, чтобы смонтировать его в beforeCreated, чтобы переданное хранилище можно было получить через this.$option
// zhao.js
class Store {
constructor() {
this.name = '赵政委'
}
}
function install(Vue) {
Vue.mixin({
beforeCreate(){
// 这样才能获取到传递进来的store
// 只有root元素才有store,所以判断一下
if(this.$options.store){
Vue.prototype.$store = store
}
}
})
// console.log(this)
}
export default { Store, install }
// store.js
import Vue from 'vue'
import Vuex from './zhao'
Vue.use(Vuex)
export default new Vuex.Store()
5. state
Простой рендеринг данных относительно прост
// zhao.js
class Store {
constructor(options={}) {
// this.name = '赵政委'
this.state = options.state
}
}
6. mutation
Изменять данные и необходимо уведомлять компоненты, для этого требуется, чтобы данные реагировали, нам нужна отзывчивая поддержка Vue, поэтому здесь вы также можете видеть, что Vuex сильно привязан к Vue и не может использоваться независимо от vue.
Так как Vue будет передаваться при установке, мы сохраняем глобальную переменную, поэтому нет необходимости импортировать vue.Если zhao.js выпущен отдельно, размер пакета будет уменьшен
Реализация мутации также относительно проста, запишите функцию мутации и обновите данные при фиксации.
// zhao.js
let Vue
class Store {
constructor(options={}) {
// this.name = '赵政委'
this.state = options.state || {}
this.mutations = options.mutations || {}
}
commit(type,arg){
if(!this.mutations[type]){
console.log('不合法的mutation')
return
}
this.mutations[type](this.state,arg)
}
}
function install(_Vue) {
// 这样store执行的时候,就有了Vue,不用import
// 这也是为啥 Vue.use必须在新建store之前
Vue = _Vue
_Vue.mixin({
beforeCreate(){
// 这样才能获取到传递进来的store
// 只有root元素才有store,所以判断一下
if(this.$options.store){
_Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }
// store.js
import Vue from 'vue'
import Vuex from './zhao'
Vue.use(Vuex)
export default new Vuex.Store({
state:{
count:0
},
mutations:{
increment (state,n=1) {
state.count += n
}
}
})
Счетчик меняется каждый раз, когда вы нажимаете, и страница не отвечает соответствующим образом.
7. Отзывчивое состояние
Если вы хотите оперативно уведомлять страницу, самое главное — использовать механизм отклика Vue, чтобы позволить программе состояния соответствующим образом.
this.state = new Vue({
data:options.state
})
8. action
асинхронные действия,мутации должны выполняться синхронноЭто ограничение? Действие не обязывает! Поскольку есть асинхронные задачи, коммит выполняется отдельно, поэтому вам нужно использовать стрелочные функции, чтобы убедиться, что внутренний this указывает на
let Vue
class Store {
constructor(options={}) {
// this.name = '赵政委'
this.state = new Vue({
data:options.state
})
this.mutations = options.mutations || {}
this.actions = options.actions
}
commit = (type,arg)=>{
this.mutations[type](this.state,arg)
}
dispatch(type, arg){
this.actions[type]({
commit:this.commit,
state:this.state
}, arg)
}
}
bingo
Thief pull mini vuex в основном завершен, есть еще несколько концепций, которые необходимо улучшить.
9. getter
Подобно вычислению, его несложно реализовать.Вы можете использовать Object.defineProperty для проксирования геттера, получения значения внутри геттера и непосредственного выполнения вычисления функции. Установлен на store.getters
handleGetters(getters){
this.getters = {}
Object.keys(getters).forEach(key=>{
Object.defineProperty(this.getters,key,{
get:()=>{
return getters[key](this.state)
}
})
})
}
//store.js
state:{
count:0
},
getters:{
killCount(state){
return state.count * 2
}
},
<div>炸死了{{$store.getters.killCount}}个柜子</div>
10. modules
Vuex поддерживает распаковку, каждый модуль имеет свое состояние, геттер, мутации и действия, поэтому необходимо вводить и устанавливать модули специально, и рекурсивно поддерживает глубокую вложенность, предыдущие handleGetters и тому подобное, каждый модуль должен выполняться один раз
Глубокая вложенность, для состояния нужен геттер-прокси
11. Регистрация модулей
монтировать в корень
register(path, module){
const newModule = {
children: {},
module: module,
state: module.state
}
if(path.length){
// path有值,子模块
const parent = path.slice(0, -1).reduce((module, key) => {
return module.children(key);
}, this.root);
parent.children[path[path.length - 1]] = newModule;
}else{
// 空 就是根目录
this.root = newModule
}
if(module.modules){
this.forEachObj(module.modules,(name,mod)=>{
// console.log(123,name,mod)
this.register([...path,name],mod)
})
}
}
12. Стартовые модули
installModules(state,path,module){
// 安装所有的module的mutation,actions,
if(path.length>0){
const moduleName = this.last(path);
// 默认名字都注册在一个命名空间里
Vue.set(state, moduleName,module.state)
}
this.forEachObj(module.children, (key,child)=>{
this.installModules(state, [...path,key],child)
})
}
constructor(options={}) {
// this.name = '赵政委'
this._vm = new Vue({
data:{
state:options.state
}
})
// 根模块
this.root = null
this.mutations = options.mutations || {}
this.actions = options.actions
this.handleGetters(options.getters)
// 注册一下module,递归,变成一个大的对象 挂载到root
this.register([], options)
this.installModules(options.state, [], this.root)
// this.installModules(options.modules)
}
get state(){
return this._vm._data.state
}
installModules(state,path,module){
// 安装所有的module的mutation,actions,
if(path.length>0){
const moduleName = this.last(path);
// 默认名字都注册在一个命名空间里
Vue.set(state, moduleName,module.state)
}
// 设置上下文,获取state要遍历 path
const context = {
dispatch: this.dispatch,
commit: this.commit,
}
Object.defineProperties(context, {
getters: {
get: () => this.getters
},
state: {
get: () => {
let state = this.state;
return path.length ? path.reduce((state, key) => state[key], state) : state
}
}
})
// 注册mutations 传递正确的state
this.registerMutations(module.module.mutations,context)
// 注册action
this.registerActions(module.module.actions,context)
// 注册getters
this.registerGetters(module.module.getters,context)
// 递归
this.forEachObj(module.children, (key,child)=>{
this.installModules(state, [...path,key],child)
})
}
13. store.js
// zhao.js
let Vue
class Store {
constructor(options={}) {
// this.name = '赵政委'
this._vm = new Vue({
data:{
state:options.state
}
})
// 根模块
this.root = null
this.mutations = {}
this.actions = {}
this.getters = {}
// 注册一下module,递归,变成一个大的对象 挂载到root
this.register([], options)
this.installModules(options.state, [], this.root)
}
get state(){
return this._vm._data.state
}
register(path, module){
const newModule = {
children: {},
module: module,
state: module.state
}
if(path.length){
// path有值,子模块
const parent = path.slice(0, -1).reduce((module, key) => {
return module.children(key);
}, this.root);
parent.children[path[path.length - 1]] = newModule;
}else{
// 空 就是根目录
this.root = newModule
}
if(module.modules){
this.forEachObj(module.modules,(name,mod)=>{
// console.log(123,name,mod)
this.register([...path,name],mod)
})
}
}
forEachObj(obj={},fn){
Object.keys(obj).forEach(key=>{
fn(key, obj[key])
})
}
commit = (type,arg)=>{
this.mutations[type](this.state,arg)
}
dispatch(type, arg){
this.actions[type]({
commit:this.commit,
state:this.state
}, arg)
}
last(arr){
return arr[arr.length-1]
}
installModules(state,path,module){
// 安装所有的module的mutation,actions,
if(path.length>0){
const moduleName = this.last(path);
// 默认名字都注册在一个命名空间里
Vue.set(state, moduleName,module.state)
}
// 设置上下文,获取state要遍历 path
const context = {
dispatch: this.dispatch,
commit: this.commit,
}
Object.defineProperties(context, {
getters: {
get: () => this.getters
},
state: {
get: () => {
let state = this.state;
return path.length ? path.reduce((state, key) => state[key], state) : state
}
}
})
// 注册mutations 传递正确的state
this.registerMutations(module.module.mutations,context)
// 注册action
this.registerActions(module.module.actions,context)
// 注册getters
this.registerGetters(module.module.getters,context)
// 递归
this.forEachObj(module.children, (key,child)=>{
this.installModules(state, [...path,key],child)
})
}
handleGetters(getters){
Object.keys(getters).forEach(key=>{
Object.defineProperty(this.getters,key,{
get:()=>{
return getters[key](this.state)
}
})
})
}
registerGetters(getters, context){
this.forEachObj(getters,(key,getter)=>{
Object.defineProperty(this.getters,key,{
get:()=>{
return getter(
// module的state
context.state,
context.getters,
// 最外层的store
this.state
)
}
})
})
}
registerMutations(mutations, context){
if(mutations){
this.forEachObj(mutations, (key,mutation)=>{
this.mutations[key] = ()=>{
mutation.call(this, context.state)
}
})
}
}
registerActions(actions,context){
if(actions){
this.forEachObj(actions, (key,action)=>{
this.actions[key] = ()=>{
action.call(this, context)
}
})
}
}
}
function install(_Vue) {
// 这样store执行的时候,就有了Vue,不用import
// 这也是为啥 Vue.use必须在新建store之前
Vue = _Vue
_Vue.mixin({
beforeCreate(){
// 这样才能获取到传递进来的store
// 只有root元素才有store,所以判断一下
if(this.$options.store){
_Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }
14. store.js
// store.js
import Vue from 'vue'
import Vuex from './zhao'
Vue.use(Vuex)
const commander = {
state: {
num: 17
},
mutations: {
fire(state) {
state.num -= 1
}
},
getters:{
fireCount(state){
return (17-state.num) *100
},
totalCount(state,getters,rootState){
return getters.fireCount + rootState.count*2
}
},
actions: {
fireAsync({commit}) {
setTimeout(()=>{
commit('fire');
},2000)
}
}
}
export default new Vuex.Store({
modules:{
commander
},
state:{
count:0
},
getters:{
killCount(state){
return state.count * 2
}
},
mutations:{
increment (state,n=1) {
state.count += n
}
},
actions:{
incrementAsync(context){
console.log(context,123)
setTimeout(()=>{
context.commit('increment',2)
},1000)
}
}
})
15. Использование компонентов
<template>
<div id="app">
<p>冲啊,手榴弹扔了{{$store.state.count}}个</p>
<p>炸死了{{$store.getters.killCount}}个柜子</p>
<p>
<button @click="add">扔一个</button>
<button @click="addAsync">蓄力扔俩</button>
</p>
<Battalion></Battalion>
</div>
</template>
<script>
import Battalion from '@/components/Battalion'
export default {
name: 'app',
components:{
Battalion
},
created(){
},
methods:{
add(){
this.$store.commit('increment')
console.log(this.$store.state)
console.log(this.$store.state.getters)
},
addAsync(){
this.$store.dispatch('incrementAsync')
}
}
}
</script>
<style>
div{
border:1px solid red;
margin:20px;
padding:20px;
}
</style>
子组件
<template>
<div>
<p> 意大利炮还有{{$store.state.commander.num}}发炮弹 </p>
<p> 意大利炮炸死了{{$store.getters.fireCount}}个鬼子 和手榴弹一起是{{$store.getters.totalCount}} </p>
<button @click="fire">开炮</button>
<button @click="fireAsync">一会再开炮</button>
</div>
</template>
<script>
export default {
name: 'pageA',
mounted(){
},
methods:{
fire(){
this.$store.commit('fire')
},
fireAsync(){
this.$store.dispatch('fireAsync')
}
}
}
</script>
16. mapState
На самом деле это очень просто, вы можете напрямую вернуть соответствующее значение, вы можете получить его через this.$store внутри вычисляемого, и код готов к выходу.
function mapState(obj){
const ret = {}
Object.keys(obj).forEach((key)=>{
// 支持函数
let val = obj[key]
ret[key] = function(){
const state = this.$store.state
return typeof val === 'function'
? val.call(this, state)
: state[val]
}
})
return ret
}
17. mapMutations
function mapMutations(mutations){
const ret = {}
mutations.forEach((key)=>{
ret[key] = function(){
const commit = this.$store.commit
commit(key)
}
})
return ret
}
Принципы MapGetters и MaPActions не описывают белых.