vue学习日记05
vuex
例如创建一个min-vuex.js
import Vue from 'vue' const Store = function Store (options = {}) { const {state = {} , mutations ={}} = options this._vm = new Vue ({ data : { $$state: state } })this._mutations = mutations } Store.prototype.commit = function (type, payload) { if(this._mutations[type]) { this._mutations[type](this.state, payload) } } Object.defineProperties(Store.prototype, { state: { get: function() { return this._vm._data.$$state } } }) export default {Store}
import Vue from 'vue' import App from './App' import router from './router' import Vuex from './min-vuex' Vue.config.productionTip = false const store = new Vuex.Store({ state: { count : 0 }, mutations: { increment (state, n) { state.count += n } } }) /* eslint-disable no-new */ Vue.prototype.$store = store new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
https://www.jianshu.com/p/d95a7b8afa06 这篇文章有工作原理解释。
我们在使用vuex的时候,官方还提供了一些辅助函数。如 mapState, mapGetter,mapActions。
mapState:
假如我们在计算属性中返回state里的值,普通用法就需要返回很多个,用辅助函数就可以缩写,以mapState为例
// 先引入 import { mapState, mapActions } from 'vuex' // 普通用法 computed: { products(){ return this.$store.state.products.all } }, // mapState 辅助函数用法 computed: mapState({ products: state => state.products.all, }), //如果state的名称与你计算属性名称相同,可以传一个字符串数组 computed: mapState(['products']), //如果该组件还存在其他计算属性 computed: { ...mapState({ products: state => state.products.all, }), other() { return 'other computed' } }
其他函数也大致相同,目的就是简化一些 this.$store.state之类的操作。
Module
官网例子:
由此可见,每个module都有自己单独的state,mutations,actions,getters。
但是如果,在每个module的mutations或actons中,存在有一样名字的函数,如每个模块中都包含 setName的actions,我们在调用 this.$store.dispatch('setName‘) 的时候,
所有模块中的同名actions都会被执行。因此若要避免这个情况,我们就需要给每个actions单独命名,或者给每个模块使用上命名空间。
// moduleA.js
const state = { ...... } // getters const getters = { ...... } // actions const actions = { ...... } // mutations const mutations = { ...... } export default { namespaced: true, state, getters, actions, mutations }
这个时候我们调用的话,就需要加上命名空间的字段 this.$store.dispatch('moduleA/....'),同时mapState等也有的同样的改变。
computed: { ...mapState("moduleA",{ useName: state => state.useName }), ...mapGetters("moduleA", ["userName"]) },
在使用了命名空间之后,每个模块中的getters会产生第三个参数,rootState作为根数据。数据格式是一个对象,然后每个模块的state都是该对象中的一个属性,值也是一个对象,包含该模块的state数据。
一个具体例子:
目录结构:
// cart.js
import shop from '../../api/shop' import { CART, PRODUCTS } from '../mutations-types' // initial state // shape: [{ id, quantity }] const state = { items: [], checkoutStatus: null } // getters const getters = { cartProducts: (state, getters, rootState) => { return state.items.map(({ id, quantity }) => { const product = rootState.products.all.find(product => product.id === id) console.log(rootState.products.all,'....111.') return { title: product.title, price: product.price, quantity } }) }, cartTotalPrice: (state, getters) => { return getters.cartProducts.reduce((total, product) => { return total + product.price * product.quantity }, 0) }, } // actions const actions = { checkout ({ commit, state }, products) { const savedCartItems = [...state.items] commit(CART.SET_CHECKOUT_STATUS, null) // empty cart commit(CART.SET_CART_ITEMS, { items: [] }) shop.buyProducts( products, () => commit(CART.SET_CHECKOUT_STATUS, 'successful'), () => { commit(CART.SET_CHECKOUT_STATUS, 'failed') // rollback to the cart saved before sending the request commit(CART.SET_CART_ITEMS, { items: savedCartItems }) } ) }, setName ({state,commit,rootState}) { commit('cartSetName',rootState.userInfo) }, addProductToCart ({ state, commit }, product) { commit(CART.SET_CHECKOUT_STATUS, null) if (product.inventory > 0) { const cartItem = state.items.find(item => item.id === product.id) if (!cartItem) { commit(CART.PUSH_PRODUCT_TO_CART, { id: product.id }) } else { commit(CART.INCREMENT_ITEM_QUANTITY, cartItem) } // remove 1 item from stock commit(`products/${PRODUCTS.DECREMENT_PRODUCT_INVENTORY}`, { id: product.id }, { root: true }) } } } // mutations const mutations = { [CART.PUSH_PRODUCT_TO_CART] (state, { id }) { state.items.push({ id, quantity: 1 }) }, [CART.INCREMENT_ITEM_QUANTITY] (state, { id }) { const cartItem = state.items.find(item => item.id === id) cartItem.quantity++ }, [CART.SET_CART_ITEMS] (state, { items }) { state.items = items }, [CART.SET_CHECKOUT_STATUS] (state, status) { state.checkoutStatus = status } } export default { namespaced: true, state, getters, actions, mutations }
// products.js
import shop from '../../api/shop' import {PRODUCTS} from '../mutations-types' // initial state const state = { all: [] } // getters const getters = {} // actions const actions = { getAllProducts ({ commit }) { shop.getProducts(products => { commit(PRODUCTS.SET_PRODUCTS, products) }) } } // mutations const mutations = { [PRODUCTS.SET_PRODUCTS] (state, products) { state.all = products }, [PRODUCTS.DECREMENT_PRODUCT_INVENTORY] (state, { id }) { const product = state.all.find(product => product.id === id) product.inventory-- } } export default { namespaced: true, state, getters, actions, mutations }
// index.js import Vue from 'vue' import Vuex from 'vuex' import cart from './modules/cart' import products from './modules/porducts' Vue.use(Vuex) export default new Vuex.Store({ state: { userInfo: { email: "wl" } }, modules: { cart, products }, })
// mutations-types.js
export const CART = { PUSH_PRODUCT_TO_CART: 'pushProductToCart', INCREMENT_ITEM_QUANTITY: 'incrementItemQuantity', SET_CART_ITEMS: 'setCartItems', SET_CHECKOUT_STATUS: 'setCheckoutStatus', } export const PRODUCTS = { SET_PRODUCTS:'setProducts', DECREMENT_PRODUCT_INVENTORY: 'decrementProductInventory' }
组件代码:
主页:
<template> <div id="app"> <h1>购物车示例</h1> <p>账号: {{email}}</p> <hr> <h2>产品</h2> <ProductList/> <hr> <ShoppingCart/> </div> </template> <script> import { mapState } from 'vuex' import ProductList from './day04_components/products' import ShoppingCart from './day04_components/shoppingCart' export default { computed: mapState({ email: state => state.userInfo.email }), components: { ProductList, ShoppingCart }, } </script>
清单:
<template> <div class="cart"> <h2>清单</h2> <p v-show="!products.length"><i>请添加产品到购物车</i></p> <ul> <li v-for="product in products" :key="product.id"> {{ product.title }} - {{ product.price }} x {{ product.quantity }} </li> </ul> <p>合计: {{ total }}</p> <p><button :disabled="!products.length" @click="checkout(products)">提交</button></p> <p v-show="checkoutStatus">提交 {{ checkoutStatus }}.</p> </div> </template> <script> import { mapGetters, mapState } from 'vuex' export default { computed: { ...mapState({ checkoutStatus: state => state.cart.checkoutStatus }), ...mapGetters('cart', { products: 'cartProducts', total: 'cartTotalPrice' }), // ...mapGetters({ // products: 'cart/cartProducts', // total: 'cart/cartTotalPrice' // }) }, // computed: { // checkoutStatus(){ // return this.$store.state.cart.checkoutStatus // }, // products() { // return this.$store.getters['cart/cartProducts'] // }, // total() { // return this.$store.getters['cart/cartTotalPrice'] // } // }, methods: { checkout (products) { this.$store.dispatch('cart/checkout', products) } }, } </script>
产品列表:
<template> <ul> <li v-for="product in products" :key="product.id"> {{ product.title }} - {{ product.price }} <br> <button :disabled="!product.inventory" @click="addProductToCart(product)"> 加入购物车 </button> </li> </ul> </template> <script> import { mapState, mapActions } from 'vuex' export default { computed: mapState({ products: state => state.products.all, }), // computed: { // products(){ // return this.$store.state.products.all // } // }, methods: mapActions('cart', [ 'addProductToCart' ]), // methods: { // addProductToCart(product){ // this.$store.dispatch('cart/addProductToCart', product) // } // }, created () { this.$store.dispatch('products/getAllProducts') } } </script>
模拟数据:
const _products = [ {"id": 1, "title": "华为 Mate 20", "price": 3999, "inventory": 2}, {"id": 2, "title": "小米 9", "price": 2999, "inventory": 1}, {"id": 3, "title": "OPPO R17", "price": 2999, "inventory": 5} ] export default { getProducts (cb) { setTimeout(() => cb(_products), 100) }, buyProducts (products, cb, errorCb) { setTimeout(() => { // simulate random checkout failure. Math.random() > 0.5 ? cb() : errorCb() }, 100) } }
效果图:
a