Предложение: примеры в блоге размещать вvue_blog_projectВ инженерии рекомендуется сочетать инженерные примеры и блоги, чтобы учиться вместе
Предыдущий блог (Подробная информация о компонентах Vue), представлены различные методы для реализации связи между компонентами, но когда дело доходит до связи между глубоко вложенными и не связанными напрямую компонентами, вы столкнетесьНевозможно отследить данные и отладить проблемы, и vuex был создан для решения таких проблем.
В этом блоге кратко представлены основы использования и лучшие практики vuex, а затем завершится следующая демонстрация.
1. Введение в Vuex
Отказ от ответственности: в этой презентации только Vuex сущность знаний, пожалуйста, обратитесь к более подробным знаниямОфициальный китайский сайт Vuex
1.1 Первое знакомство с Vuex
Vuex — это шаблон управления состоянием, разработанный для приложений Vue.js. Он использует централизованное хранилище для управления состоянием всех компонентов приложения и использует соответствующие правила для обеспечения предсказуемого изменения состояния.
Vuex решен多个视图依赖于同一状态
а также来自不同视图的行为需要变更同一状态
Проблема в том, что разработчики энергии фокусируются на данных, а не на обновлении данных о передаче между компонентами.
1.2 Vuex каждый модуль
(1)state
: используется для хранения данных, в магазинеуникальный источник данных
// 定义
new Vuex.Store({
state: {
allProducts: []
}
//...
})
// 组件中获取
this.$store.state.allProducts
(2)getters
: Как и вычисляемые свойства в vue,Вторичная упаковка на основе данных состояния, обычно используется для фильтрации данных и расчета корреляции нескольких данных
// 定义
getters: {
cartProducts(state, getters, rootState)
=> (getters.allProducts.filter(p => p.quantity))
}
// 组件中获取
this.$store.getters.cartProducts
(3)mutations
: аналогичная функция,Единственный способ изменить данные состояния и не может использоваться для обработки асинхронных событий (важно!!!)
// 定义
mutations: {
setProducts (state, products) {
state.allProducts = products
}
}
// 组件中使用
this.$store.commit('setProducts', {//..options})
(4)actions
: похоже на мутацию,Используется для отправки мутации для изменения состояния, не измененного напрямую, может содержать любую асинхронную операцию
// 定义(shop为api)
actions: {
getAllProducts ({ commit }, payload) {
shop.getProducts((res) => {
commit('setProducts', res)
})
}
}
// 组件中使用
this.$store.dispatch('getAllProducts', {//..payload})
(5)modules
: Подобно пространству имен, оно используется для определения и управления состоянием каждого модуля отдельно в проекте для упрощения обслуживания.
// 定义
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 的状态
store.state.b // -> moduleB 的状态
Уведомление:По умолчанию действия, мутации и геттеры внутри модулей регистрируются в глобальном пространстве имен — это позволяет нескольким модулям реагировать на одну и ту же мутацию или действие, только состояние является локальным.Поэтому геттеры часто используются для обертывания состояния и его вывода, чтобы его можно было напрямую передать черезthis.$store.getters.
способ получить данные без доступа к состоянию под модулем
1.3 Вспомогательные функции
При использовании данных или метода в компоненте, согласно вышеуказанному утверждению, каждый раз, когда у вас естьthis.$store.
Как легко получить?Есть ли простой способ? Вспомогательная функция заключается в решении этой задачи.
// 组件中注册
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
computed: {
// 数组形式,当映射的计算属性的名称与 state 的子节点名称相同时使用
...mapState(['allProducts'])
// 对象形式,可重命名 state 子节点名称
...mapState({
products: state => state.allProducts
})
// 下面为了简便,均以数组形式使用
...mapGetters(['cartProducts'])
},
methods: {
...mapMutations(['setProducts']),
...mapActions(['getAllProducts'])
}
}
// 组件中使用
// 变量
this.allProducts
this.products
// 方法
this.setProducts()
this.getAllProducts()
Как было сказано выше, общепринятой практикой является оборачивать данные в состояние геттером и выводить его, поэтому mapState редко встречается в проекте, а остальные три часто используются, кроме того, есть два примечания и два лучших практики. :
Уведомление:
- Мутация должна следовать правилам ответа Vue.,См. раздел «Мутация» на официальном сайте Vuex.
- Прямые изменения модификации при запуске состояния обработки формывопрос,См. раздел обработки форм на официальном сайте Vuex.
Лучшие практики(Он будет использоваться в следующей демонстрации):
- Используйте константы вместо типов событий Mutation, что позволяет работать таким инструментам, как линтеры, а наличие этих констант в отдельных файлах дает вашим коллегам по коду обзор мутаций, содержащихся во всем приложении.
- хранить конфигурацию, используемую следующим образом
store
├── index.js # 导出 store 的地方
├── state.js # 根级别的 state
├── getters.js # 二次包装state数据
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
├── mutation-types.js # 所有 mutation 的常量映射表
└── modules # 如果有.
├── ...
2. Установка Вьюкса
(1) Установить в проектеVuex
:
npm install vuex --save
(2) Создайте новый в каталоге srcstore/index.js
, где код выглядит следующим образом:
import Vue from 'vue'
import Vuex from 'vuex'
// 修改state时在console打印,便于调试
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
const state = {}
const getters = {}
const mutataions = {}
const actions = {}
export default new Vuex.Store({
state,
getters,
mutataions,
actions,
// 严格模式,非法修改state时报错
strict: debug,
plugins: debug ? [createLogger()] : []
})
(3) В файле вводаmain.js
Добавить:
// ...
import router from './router'
import store from './store'
new Vue({
el: '#app',
router,
store,
// ...
})
Вы можете сравнить способы установки vue-router и vuex: ониОба являются плагинами vue и вводятся при создании экземпляров компонентов. Ко всем компонентам в этом экземпляре можно получить доступ с помощьюthis.$router
а такжеthis.$store
запрос к соответствующему экземпляру подключаемого модуля
3. Практика проекта Vuex
нужно: Завершите функцию анимации, показанную в начале статьи [Примечание:исходный код демо], данные и функции API следующие:
// 商品列表
[
{ 'id': 1, 'title': 'iPad 4 Mini', 'price': 500, 'inventory': 2 },
{ 'id': 2, 'title': 'H&M T-Shirt White', 'price': 10, 'inventory': 10 },
{ 'id': 3, 'title': 'Charli XCX - Sucker CD', 'price': 20, 'inventory': 5 }
]
Функция 1: Изменения инвентаря, список корзины покупок и сумма меняются при увеличении или уменьшении количества товаров.
Особенность 2: При очистке корзины все данные восстанавливаются
анализировать:
Компонентная структура: родительский компонент включает в себя два дочерних компонента, список продуктов и корзину;Аспекты данных: данные списка товаров поступают из API-интерфейса + количество товаров, добавленных в корзину, а список товаров, добавленных в корзину, поступает из фильтрации списка товаров;
Основываясь на приведенном выше анализе, код может быть организован следующим образом.
(1) Код в магазине
const state = {
all: []
}
const getters = {
// 总商品列表
allProducts: state => state.all,
// 购物车商品列表
cartProducts: (state, getters) => (getters.allProducts.filter(p => p.quantity)),
// 购物车商品总价
cartTotalPrice: (state, getters) => {
return getters.cartProducts.reduce((total, product) => {
return total + product.price * product.quantity
}, 0)
}
}
const mutations = {
setProducts (state, products) {
state.all = products
},
clearCartProducts (state) {
state.all.forEach(p => {
p.quantity = 0
})
}
}
const actions = {
// 获取数据后,加入选取数量quantity的标识,以区分是否被加入购物车
getAllProducts ({ commit }) {
shop.getProducts((res) => {
const newRes = res.map(p => Object.assign({}, p, {quantity: 0}))
commit('setProducts', newRes)
})
}
}
(2) Компоненты списка продуктов ProductList.Vue
<template>
<ul class="product-wrapper">
<li class="row header">
<div v-for="(th,i) in tHeader" :key="i">{{ th }}</div>
</li>
<li class="row" v-for="product in currentProducts" :key="product.id">
<div>{{ product.title }}</div>
<div>{{ product.price }}</div>
<div>{{ product.inventory - product.quantity }}</div>
<div>
<el-input-number
:min="0" :max="product.inventory"
v-model="product.quantity"
@change="handleChange">
</el-input-number>
</div>
</li>
</ul>
</template>
<script>
import { mapGetters, mapMutations, mapActions } from 'vuex'
export default {
data () {
return {
tHeader: ['名称', '价格', '剩余库存', '操作'],
currentProducts: []
}
},
computed: {
...mapGetters(['allProducts'])
},
// 为了避免表单直接修改store中的数据,需要使用watch模拟双向绑定
watch: {
allProducts: {
handler (val) {
this.currentProducts = JSON.parse(JSON.stringify(this.allProducts))
},
deep: true
}
},
created () {
this.getAllProducts()
},
methods: {
handleChange () {
this.setProducts(this.currentProducts)
},
...mapMutations(['setProducts']),
...mapActions(['getAllProducts'])
}
}
</script>
(3) Компоненты списка автомобилей для покупок ShoppingCart.Vue
<template>
<div class="cart">
<p v-show="!products.length"><i>Please add some products to cart.</i></p>
<ul>
<li v-for="product in products" :key="product.id">
{{ product.title }} - {{ product.price }} x {{ product.quantity }}
</li>
</ul>
<p>Total: {{ total }}</p>
<el-button @click="clearCartProducts">CLEAR</el-button>
</div>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapGetters({
products: 'cartProducts',
total: 'cartTotalPrice'
})
},
methods: {
...mapMutations(['clearCartProducts'])
}
}
</script>
(4) В сочетании с упомянутой выше передовой практикой оптимизации:
Сначала разделите папку магазина в соответствии с древовидной структурой выше; далее:
новый в магазинеmutation-types.js
документ,
export const SET_PRODUCTS = 'SET_PRODUCTS'
export const CLEAR_CART_PRODUCTS = 'CLEAR_CART_PRODUCTS'
mutations.js
Внесите следующие изменения:
import * as types from './mutation-types'
export default {
[types.SET_PRODUCTS] (state, products) {
state.all = products
},
[types.CLEAR_CART_PRODUCTS] (state) {
state.all.forEach(p => {
p.quantity = 0
})
}
}
actions.js
Внесите следующие изменения:
import shop from '@/api/shop'
import * as types from './mutation-types'
export default {
// 获取数据后,加入选取数量quantity的标识,以区分是否被加入购物车
getAllProducts ({ commit }) {
shop.getProducts((res) => {
const newRes = res.map(p => Object.assign({}, p, {quantity: 0}))
commit(types.SET_PRODUCTS, newRes)
})
},
// 这里将mutation中的方法以action的形式输出,主要是组件中有使用mutation的方法,到时仅需引用mapActions即可,可按实际情况使用
setProducts ({ commit }, products) {
commit(types.SET_PRODUCTS, products)
},
clearCartProducts ({ commit }) {
commit(types.CLEAR_CART_PRODUCTS)
}
}
Кроме того,Часть ссылки на мутацию компонента также должна быть изменена соответствующим образом.
Здесь перечислены только основные части демо, пожалуйста, проверьте полный кодисходный код демо