Эта глава посвящена объяснению принципа реализации vuex.
Так как в коде много комментариев, код кажется избыточным, поэтому лучше скачать исходники, можно удалить комментарии и посмотреть, на самом деле кода не так много
Закуска
Что такое Векс?
Vuex — это шаблон управления состоянием, разработанный для приложений Vue.js. он принимаетЦентрализованное управление хранилищемсостояние всех компонентов приложения,
Это приложение управления состоянием состоит из следующих частей:
- состояние, источник данных, управляющий приложением;
- представление, которое декларативно сопоставляет состояние представлениям;
- действия, которые представляют собой изменения состояния, вызванные пользовательским вводом в представлении.
Дается простая иллюстрация официальной концепции «одностороннего потока данных»:
Ядром каждого приложения Vuex является магазин. «Хранилище» — это, по сути, контейнер, который содержит большую часть состояния вашего приложения.
Vuex отличается от чистого глобального объекта следующими двумя способами:
- Хранилище состояния Vuex является реактивным. Когда компонент Vue считывает состояние из хранилища, если состояние в хранилище изменяется, соответствующий компонент будет эффективно обновлен соответствующим образом. (также известный как МВВМ)
- Вы не можете напрямую изменить состояние в хранилище. Единственный способ изменить состояние в магазинезафиксировать явно
mutations
.
Посмотрите на картинку, чтобы понять, как это работает:
Если вы понимаете эту картинку, вы можете знать, как работает vuex.
Обратите внимание:
- Единственный способ изменить состояние — зафиксировать
mutations
- Отправка, если асинхронная
actions
, его суть заключается в подачеmutations
- как вызвать
actions
Шерстяная ткань? доступные компонентыVue Components
использоватьdispatch
Или внутренний интерфейс для запуска - представить
mutations
После этого вы можете динамически отображать компонентVue Components
Как вы думаете, чего-то не хватает? Да, этоgetters
Когда будет реализован следующий принцип, он скажет
Принцип реализации
Готов к работе
Во-первых, удалите все ненужные файлы и коды и классифицируйте структуру следующим образом:
App.vue
Код:
<template>
<div>
<!-- vuex 把状态放到一个公共的地方,哪个组件使用,就直接可以从公共的地方获取状态 -->
</div>
</template>
<script>
export default {
name:'app',
}
</script>
main.js
Код:
import Vue from 'vue'
import App from './App.vue'
import store from './store'
import router from 'vue-router'
Vue.config.productionTip = false
new Vue({
name:'main',
router, //封装了 router-view router-link $router $route
store, //写到这里,说明全部的组件都可以使用store
render: h => h(App)
}).$mount('#app')
store.js
Код:
import Vue from 'vue'
//把里面的全删了,自己写
// 引入自己的写的vuex,里面有一个对象{install},当你use时,会自动调用这个方法
//导入vuex {install Store}
import Vuex from './vuex'
Vue.use(Vuex)
//需要创建一个仓库并导出
//当new的时候,给Vuex.js中传入了一堆的东西
export default new Vuex.Store({
state:{
name:'Fan'
},
//getters中虽然是一个方法,但是用时,可以把他当作属性
getters:{ // 说白了,就是vue中data中的computed
},
// 改变状态:异步请求数据 事件
mutations:{
},
actions:{
}
})
vuex.js
Не пишите сначала код в файле, начните писать ниже
реализовать состояние
Вышеуказанные приготовления сделаны, и тогда мы будем реализовывать нашиstate
существуетvuex.js
Напишите в коде следующий код (конкретные инструкции и операции прокомментированы в коде):
//定义一个Vue,让全局都可以使用这个Vue
let Vue;
class Store{
//当new的时候,给Vuex.js中传入了一堆的东西,在这里接收需要用constructor
constructor(options){
// console.log(options); //打印出{state: {…}, getters: {…}, mutations: {…}, actions: {…}},就可以拿到里面的数据了
/*-------------------------------state原理-------------------------------------------------------------*/
//给每个组件的$store上挂一个state,让每个组件都可以用 this.$store.state
this.state = options.state
//在state上面传入一个name:'Fan'打印一下
// console.log(this.state); //打印结果 {name: "Fan"}
/*-------------------------------------------------------------------------------------------------*/
}
}
//install本质上就是一个函数
const install = (_Vue)=>{
// console.log('......'); //测试能不能调到这个方法,经测试可以调到
//把构造器赋给全局Vue
Vue = _Vue;
//混入
Vue.mixin({
beforeCreate() { //表示在组件创建之前自动调用,每个组件都有这个钩子
// console.log(this.$options.name) //this表示每个组件,测试,可以打印出mian.js和App.vue中的name main和app
//保证每一个组件都能得到仓库
//判断如果是main.js的话,就把$store挂到上面
if(this.$options && this.$options.store){
this.$store = this.$options.store
}else{
//如果不是根组件的话,也把$store挂到上面,因为是树状组件,所以用这种方式
this.$store = this.$parent && this.$parent.$store
//在App.vue上的mounted({console.log(this.$store)})钩子中测试,可以得到store ---> Store {}
}
},
})
}
//导出
export default {
install,
Store
}
В этом случае можно использовать все компоненты.this.$store.state
Этот метод
реализовать геттеры
первый вstore.js
серединаgetters
В , которые используются для тестирования, определены два метода:
//getters中虽然是一个方法,但是用时,可以把他当作属性
getters:{ // 说白了,就是vue中data中的computed
myName(state){
return state.name+'Jun'
},
myAge(){
}
},
затем вvuex.js
в файлеStore
Категорияconstructor
Давайте напишем наш код следующим образом:
class Store{
//当new的时候,给Vuex.js中传入了一堆的东西,在这里接收需要用constructor
constructor(options){
// console.log(options); //打印出{state: {…}, getters: {…}, mutations: {…}, actions: {…}},就可以拿到里面的数据了
/*------------------------------------state原理--------------------------------------------------------*/
//给每个组件的$store上挂一个state,让每个组件都可以用 this.$store.state
// this.state = options.state
/*-------------------------------------------------------------------------------------------------*/
/* --------------------------------状态响应式原理---------------------------------------------------------------- */
// 上面那种写法不完美,当改变数据的时候,不能动态的渲染,所以需要把data中的数据做成响应式的
//_s在下面的 get state方法中使用
this._s = new Vue({
data:{
// 只有data中的数据才是响应式
state:options.state
}
})
//在state上面传入一个name:'Fan'打印一下
// console.log(this.state); //打印结果 {name: "Fan"}
/* ------------------------------------------------------------------------------------------------ */
/*---------------------------------getters原理-----------------------------------------------------------*/
//得到仓库中的getters,如果人家不写getters的话,就默认为空
let getters = options.getters || {}
// console.log(getters); //打印出一个对象,对象中是一个方法 {myName: ƒ}
//给仓库上面挂载一个getters,这个getters和上面的那一个getters不一样,一个是得到,一个是挂载
this.getters = {}
//不好理解,因为人家会给你传多个方法,所以使用这个api处理得到的getters,得到一个数组
//把store.js中的getters中再写一个方法myAge,用来测试
// console.log(Object.keys(getters)); //打印出 ["myName", "myAge"]
//遍历这个数组,得到每一个方法名
Object.keys(getters).forEach((getter)=>{
// console.log(getter); //打印出 myName myAge
Object.defineProperty(this.getters,getter,{
//当你要获取getter的时候,会自动调用get这个方法
//一定要用箭头函数,要不然this指向会出现问题
get:()=>{
console.log(this);
return getters[getter](this.state)
}
})
})
/*-------------------------------------------------------------------------------------------------*/
}
get state(){
return this._s.state
}
}
затем вApp.vue
Средний тест:
<template>
<div>
<!-- vuex 把状态放到一个公共的地方,哪个组件使用,就直接可以从公共的地方获取状态 -->
{{this.$store.state.name}}
<!-- 打印出 Fan -->
{{this.$store.getters.myName}}
<!-- 打印出 FanJun -->
</div>
</template>
<script>
export default {
name:'app',
mounted(){
console.log(this.$store);
}
}
</script>
внедрять мутации
Попробуйте сначала с чужим:
существуетApp.vue
определитьadd
метод, кнопка определена выше для запуска этого метода, код:
<template>
<div>
<!-- vuex 把状态放到一个公共的地方,哪个组件使用,就直接可以从公共的地方获取状态 -->
{{this.$store.state.name}}
<!-- 打印出 Fan -->
{{this.$store.getters.myName}}
<!-- 打印出 FanJun -->
<hr>
{{this.$store.state.age}}
<button @click="add()">Add</button>
</div>
</template>
<script>
export default {
name:'app',
mounted(){
console.log(this.$store);
},
methods:{
add(){
//commit一个mutations
this.$store.commit('add',10)
}
}
}
</script>
существуетstore.js
Китайский народ vuex:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state:{
name:'Fan',
age:10
},
//getters中虽然是一个方法,但是用时,可以把他当作属性
getters:{ // 说白了,就是vue中data中的computed
myName(state){
return state.name+'Jun'
},
myAge(){
}
},
// 改变状态:异步请求数据 事件
mutations:{
add(state,payload){
state.age += payload
}
},
})
На этот раз, когда вы нажимаете кнопку «Добавить», вы можете добавить 10.
Тогда напишите сами:
существуетstore.js
написать вmutations
и определите два метода:
// 改变状态:异步请求数据 事件
mutations:{
add(state,payload){
state.age += payload
},
sub(){
}
},
затем вvuex.js
класс вStore
Реализовано в:
class Store{
//当new的时候,给Vuex.js中传入了一堆的东西,在这里接收需要用constructor
constructor(options){
// console.log(options); //打印出{state: {…}, getters: {…}, mutations: {…}, actions: {…}},就可以拿到里面的数据了
/*-------------------------------state原理-------------------------------------------------------------*/
//给每个组件的$store上挂一个state,让每个组件都可以用 this.$store.state
// this.state = options.state
/*----------------------------------------------------------------------------------------------------*/
/* --------------------------------状态响应式原理---------------------------------------------------------------- */
// 上面那种写法不完美,当改变数据的时候,不能动态的渲染,所以需要把data中的数据做成响应式的
//_s在下面的 get state() 方法中使用
this._s = new Vue({
data:{
// 只有data中的数据才是响应式
state:options.state
}
})
//在state上面传入一个name:'Fan'打印一下
// console.log(this.state); //打印结果 {name: "Fan"}
/* ----------------------------------------------------------------------------------------------------------------- */
/* ----------------------------------getters原理------------------------------------------------------------- */
//得到仓库中的getters,如果人家不写getters的话,就默认为空
let getters = options.getters || {}
// console.log(getters); //打印出一个对象,对象中是一个方法 {myName: ƒ}
//给仓库上面挂载一个getters,这个getters和上面的那一个getters不一样,一个是得到,一个是挂载
this.getters = {}
//不好理解,因为人家会给你传多个方法,所以使用这个api处理得到的getters,得到一个数组
//把store.js中的getters中再写一个方法myAge,用来测试
// console.log(Object.keys(getters)); //打印出 ["myName", "myAge"]
//遍历这个数组,得到每一个方法名
Object.keys(getters).forEach((getter)=>{
// console.log(getter); //打印出 myName myAge
Object.defineProperty(this.getters,getter,{
//当你要获取getter的时候,会自动调用get这个方法
//一定要用箭头函数,要不然this指向会出现问题
get:()=>{
// console.log(this);
return getters[getter](this.state)
}
})
})
/* -------------------------------------------------------------------------------------------------- */
/* ---------------------------------------mutatios原理----------------------------------------------------------- */
//和getters思路差不多
//得到mutations
let mutations = options.mutations || {}
// console.log(mutations); //{add: ƒ}
//挂载mutations
this.mutations = {}
//拿到对象中的一堆方法
Object.keys(mutations).forEach((mutation)=>{
// console.log(mutation); //add sub
this.mutations[mutation] = (payload)=>{
mutations[mutation](this.state,payload)
}
})
//打印看一下,正确
// console.log(mutations); //{add: ƒ, sub: ƒ}
//但是他比较恶心,需要实现commit,在下面实现
/* -------------------------------------------------------------------------------------------------- */
}
//给store上挂一个commit,接收两个参数,一个是类型,一个是数据
commit(type,payload){
//{add: ƒ, sub: ƒ}
//把方法名和参数传给mutations
this.mutations[type](payload)
}
get state(){
return this._s.state
}
}
существуетApp.vue
Средний тест:
<template>
<div>
<!-- vuex 把状态放到一个公共的地方,哪个组件使用,就直接可以从公共的地方获取状态 -->
{{this.$store.state.name}}
<!-- 打印出 Fan -->
{{this.$store.getters.myName}}
<!-- 打印出 FanJun -->
<hr>
{{this.$store.state.age}}
<button @click="add()">Add</button>
</div>
</template>
<script>
export default {
name:'app',
mounted(){
// console.log(this.$store);
},
methods:{
add(){
//commit一个mutations
this.$store.commit('add',10)
}
}
}
</script>
код избыточен, поэтому я упростил код, то есть поставил публичный методObject.keys(obj).forEach(key => { callback(key, obj[key]) })
Вытаскивать.
Вы можете скачать исходный код и посмотреть, я не буду здесь много говорить
осуществлять действия
Точно так же вvuex.js
класс вStore
В реализации, потому что я упростил код, поэтому скопируйте его целиком, чтобы увидеть,
положи сюдаdispatch
а такжеcommit
Метод заменен стрелочной функцией для предотвращенияthis
указать на проблему
//定义一个Vue,让全局都可以使用这个Vue
let Vue;
// forEach是用来循环一个对象
const forEach = (obj, callback) => {
// 把数组中的每一个key得到 objc[key]
// key value ----> callback
Object.keys(obj).forEach(key => {
callback(key, obj[key])
})
}
class Store {
//当new的时候,给Vuex.js中传入了一堆的东西,在这里接收需要用constructor
constructor(options) {
// console.log(options); //打印出{state: {…}, getters: {…}, mutations: {…}, actions: {…}},就可以拿到里面的数据了
/*-------------------------------state原理-------------------------------------------------------------*/
//给每个组件的$store上挂一个state,让每个组件都可以用 this.$store.state
// this.state = options.state
/*----------------------------------------------------------------------------------------------------*/
/* ---------------------------------------状态响应式原理--------------------------------------------------------- */
// 上面那种写法不完美,当改变数据的时候,不能动态的渲染,所以需要把data中的数据做成响应式的
//_s在下面的 get state方法中使用
this._s = new Vue({
data: {
// 只有data中的数据才是响应式
state: options.state
}
})
/* ----------------------------------------------------------------------------------------------------------------- */
/* ----------------------------------------getters原理------------------------------------------------------- */
//在state上面传入一个name:'Fan'打印一下
// console.log(this.state); //打印结果 {name: "Fan"}
//得到仓库中的getters,如果人家不写getters的话,就默认为空
let getters = options.getters || {}
// console.log(getters); //打印出一个对象,对象中是一个方法 {myName: ƒ}
//给仓库上面挂载一个getters,这个getters和上面的那一个getters不一样,一个是得到,一个是挂载
this.getters = {}
//不好理解,因为人家会给你传多个方法,所以使用这个api处理得到的getters,得到一个数组
//把store.js中的getters中再写一个方法myAge,用来测试
// console.log(Object.keys(getters)); //打印出 ["myName", "myAge"]
forEach(getters, (getterName, value) => {
Object.defineProperty(this.getters, getterName, {
get: () => {
return value(this.state)
}
})
})
/* -------------------------------------------------------------------------------------------------- */
/* ----------------------------------------mutatios原理---------------------------------------------------------- */
//和getters思路差不多
//得到mutations
let mutations = options.mutations || {}
// console.log(mutations); //{add: ƒ}
//挂载mutations
this.mutations = {}
forEach(mutations, (mutationName, value) => {
this.mutations[mutationName] = (payload) => {
value(this.state, payload)
}
})
//打印看一下,正确
// console.log(mutations); //{add: ƒ, sub: ƒ}
//但是他需要实现commit,在下面实现
/* -------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------actions原理----------------------------------------------------- */
//和上面两种大同小异,不多注释了
let actions = options.actions || {}
this.actions = {};
forEach(actions, (action, value) => {
this.actions[action] = (payload) => {
value(this, payload)
}
})
/* -------------------------------------------------------------------------------------------------- */
}
// type是actions的类型
dispatch = (type, payload) => {
this.actions[type](payload)
}
//给store上挂一个commit,接收两个参数,一个是类型,一个是数据
commit = (type, payload) => {
//{add: ƒ, sub: ƒ}
this.mutations[type](payload)
}
get state() {
return this._s.state
}
}
//install本质上就是一个函数
const install = (_Vue) => {
// console.log('......'); //测试能不能调到这个方法,经测试可以调到
//把构造器赋给全局Vue
Vue = _Vue;
//混入
Vue.mixin({
beforeCreate() { //表示在组件创建之前自动调用,每个组件都有这个钩子
// console.log(this.$options.name) //this表示每个组件,测试,可以打印出mian.js和App.vue中的name main和app
//保证每一个组件都能得到仓库
//判断如果是main.js的话,就把$store挂到上面
if (this.$options && this.$options.store) {
this.$store = this.$options.store
} else {
//如果不是根组件的话,也把$store挂到上面,因为是树状组件,所以用这种方式
this.$store = this.$parent && this.$parent.$store
//在App.vue上的mounted()钩子中测试,可以得到store ---> Store {}
}
},
})
}
//导出
export default {
install,
Store
}
существуетmutations
Добавьте асинхронный метод к:
mutations: {
add(state, payload) {
state.age += payload
},
sub() {
},
asyncSub(state, payload) {
state.age -= payload
}
},
существуетstore.js
написать один вactions
actions: {
asyncSub({commit}, payload) {
setTimeout(() => {
commit("asyncSub", payload)
}, 2000)
}
}
Наконец вApp.vue
Определите тест метода в:
<template>
<div>
<!-- vuex 把状态放到一个公共的地方,哪个组件使用,就直接可以从公共的地方获取状态 -->
{{this.$store.state.name}}
<!-- 打印出 Fan -->
{{this.$store.getters.myName}}
<!-- 打印出 FanJun -->
<hr> {{this.$store.state.age}}
<!-- 同步加 -->
<button @click="add">Add</button>
<!-- 异步减 -->
<button @click="sub">Async Sub</button>
</div>
</template>
<script>
export default {
name: "app",
mounted() {
// console.log(this.$store);
// 是异步的
setTimeout(() => {
this.$store.state.age = 666;
}, 1000);
// 是同步的
console.log(this.$store.state);
},
methods: {
add() {
//commit一个mutations
this.$store.commit("add", 10);
},
sub(){
this.$store.dispatch("asyncSub",10)
}
}
};
</script>
Удаляя комментарииvuex.js
код
Не так много кода
let Vue;
const forEach = (obj, callback) => {
Object.keys(obj).forEach(key => {
callback(key, obj[key])
})
}
class Store {
constructor(options) {
this._s = new Vue({
data: {
state: options.state
}
})
let getters = options.getters || {}
this.getters = {};
forEach(getters, (getterName, value) => {
Object.defineProperty(this.getters, getterName, {
get: () => {
return value(this.state)
}
})
})
let mutations = options.mutations || {}
this.mutations = {};
forEach(mutations, (mutationName, value) => {
this.mutations[mutationName] = (payload) => {
value(this.state, payload)
}
})
let actions = options.actions || {}
this.actions = {};
forEach(actions,(actionName,value)=>{
this.actions[actionName] = (payload)=>{
value(this,payload)
}
})
}
dispatch=(type,payload)=>{
this.actions[type](payload)
}
commit=(type, payload)=>{
this.mutations[type](payload)
}
get state() {
return this._s.state
}
}
const install = _Vue => {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options && this.$options.store) {
this.$store = this.$options.store
} else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default { install, Store }
Обзор
Поскольку комментариев слишком много, это кажется очень сложным, поэтому лучше всего скачать исходный код и попробовать написать его самостоятельно.
Прикрепите адрес исходного кода:Принцип реализации Vuex