日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

Vuex-状态管理(24)

發(fā)布時間:2023/12/15 vue 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Vuex-状态管理(24) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Vuex狀態(tài)管理

課程目標(biāo)

  • 組件通信方式回顧
  • Vuex核心概念和基本使用
  • 購物車案例
  • 模擬實(shí)現(xiàn)Vuex

組件內(nèi)的狀態(tài)管理流程

Vue最核心的兩個功能:數(shù)據(jù)驅(qū)動和組件化

組件化開發(fā)給我們帶來了:

  • 更快的開發(fā)效率
  • 更好的可維護(hù)性

每個組件都有自己的狀態(tài)、視圖和行為等組成部分

new Vue({ // statedata () {return {count: 0}}, // viewtemplate: ` <div>{{ count }}</div> `, // actionsmethods: {increment () {this.count++}} })

狀態(tài)管理包含一下幾部分:

  • state,驅(qū)動應(yīng)用的數(shù)據(jù)源
  • view,以聲明方式將state映射到視圖
  • actions,響應(yīng)在view上的用戶輸入導(dǎo)致的狀態(tài)變化

組件間通信方式回顧

大多數(shù)場景下的組件都并不是獨(dú)立存在的,而是相互協(xié)作共同構(gòu)成了一個復(fù)雜的業(yè)務(wù)功能。在 Vue 中為不同的組件關(guān)系提供了不同的通信規(guī)則。

父傳子:Props Down

  • 子組件中通過props接受數(shù)據(jù)
  • 父組件中給子組件通過相應(yīng)屬性傳值

Props

Prop 的大小寫 (camelCase vs kebab-case)

HTML 中的 attribute 名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋為小寫字符。這意味著當(dāng)你使用 DOM 中的模板時,camelCase (駝峰命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名:

Vue.component('blog-post', { // 在 JavaScript 中是 camelCase 的 props: ['postTitle'], template: '<h3>{{ postTitle }}</h3>' }) <!-- 在 HTML 中是 kebab-case 的 --> <blog-post post-title="hello!"></blog-post>

重申一次,如果你使用字符串模板,那么這個限制就不存在了。

Prop 類型

到這里,我們只看到了以字符串?dāng)?shù)組形式列出的 prop:

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

但是,通常你希望每個 prop 都有指定的值類型。這時,你可以以對象形式列出 prop,這些 property 的名稱和值分別是 prop 各自的名稱和類型:

props: {title: String,likes: Number,isPublished: Boolean,commentIds: Array,author: Object,callback: Function,contactsPromise: Promise // or any other constructor }

這不僅為你的組件提供了文檔,還會在它們遇到錯誤的類型時從瀏覽器的 JavaScript 控制臺提示用戶。你會在這個頁面接下來的部分看到類型檢查和其它 prop 驗(yàn)證。

子傳父:Event Up

非父子組件:Event Bus

我們可以使用一個非常簡單的Event Bus來解決這個問題:

eventbus.js

export default new Vue()

然后在需要通信的兩端:

使用$on訂閱:

// 沒有參數(shù) bus.$on('自定義事件名稱', () => {// 執(zhí)行操作 })// 有參數(shù) bus.$on('自定義事件名稱', data => {// 執(zhí)行操作 })

使用$emit發(fā)布:

// 沒有自定義傳參 bus.$emit('自定義事件名稱')// 有自定義傳參 bus.$emit('自定義事件名稱', 參數(shù)數(shù)據(jù))

父直接訪問子組件:通過ref獲取子組件

ref有兩個作用:

  • 如果把它作用到普通HTML標(biāo)簽上,則獲取的是DOM
  • 如果把它作用到組件標(biāo)簽上,則獲取到的是組件實(shí)例

創(chuàng)建base-input

<template><div><h1>ref Child</h1> <input ref="input" type="text" v-model="value"></div> </template> <script>export default {data() {return {value: ''}}, methods: {focus() {this.$refs.input.focus()}} }</script>

在使用子組件的時候,添加ref屬性:

<base-input ref='usernameInput'></base-input>

然后在父組件等渲染完畢后使用$refs訪問:

mounted() {this.$refs.usernameInput.focus() }

$refs只會在組件渲染完成之后生效,并且它們不是響應(yīng)式的。這僅作為一個用于直接操作子組件的“逃生艙”——你應(yīng)該避免在模板或計(jì)算屬性中訪問$refs

簡易的狀態(tài)管理方案

如果多個組件之間要共享狀態(tài)(數(shù)據(jù)),使用上面的方式雖然可以實(shí)現(xiàn),但是比較麻煩,而且多個組件之間互相傳值很難跟蹤數(shù)據(jù)的變化,如果出現(xiàn)問題很難定位問題。

當(dāng)遇到多個組件需要共享狀態(tài)的時候,典型的場景:購物車。我們?nèi)绻褂蒙鲜龅姆桨付疾缓线m,會遇到以下問題:

  • 多個視圖依賴同一狀態(tài)
  • 來自不同視圖的行為需要變更同一狀態(tài)

對于問題一,傳參的方法對于多層嵌套的組件將會非常繁瑣,并且對于兄弟組件間的狀態(tài)傳遞無能為力。

對于問題二,我們經(jīng)常會采用父子組件直接引用或者通過事件來變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會導(dǎo)致無法維護(hù)的代碼。

因此,我們?yōu)槭裁床话呀M件的共享狀態(tài)抽取出來,以一個全局單例模式管理呢?在這種模式下,我們的組件樹構(gòu)成了一個巨大的“視圖”,不管在樹的哪個位置,任何組件都能獲取狀態(tài)或者觸發(fā)行為!

通過定義和隔離狀態(tài)管理中的各種概念并通過強(qiáng)制規(guī)則維持視圖和狀態(tài)間的獨(dú)立性,我們的代碼將會變得更結(jié)構(gòu)化且易維護(hù)。

我們可以把多個組件的狀態(tài),或者整個程序的狀態(tài)放到一個集中的位置存儲,并且可以檢測到數(shù)據(jù)的更改。你可能已經(jīng)想到了 Vuex。

這里我們先以一種簡單的方式來實(shí)現(xiàn)

  • 首先創(chuàng)建一個共享的倉庫 store 對象
export default {debug: true,state: {user: {name: 'xiaomao',age: 18,sex: '男'}},setUserNameAction (name) {if (this.debug) {console.log('setUserNameAction triggered:', name)}this.state.user.name = name} }
  • 把共享的倉庫 store 對象,存儲到需要共享狀態(tài)的組件的 data 中
<template><div><h1>componentA</h1>user name: {{ sharedState.user.name }}<button @click="change">Change Info</button></div> </template><script> import store from './store' export default {methods: {change () {store.setUserNameAction('componentA')}},data () {return {privateState: {},sharedState: store.state}} } </script>

接著我們繼續(xù)延伸約定,組件不允許直接變更屬于 store 對象的 state,而應(yīng)執(zhí)行 action 來分發(fā)(dispatch) 事件通知 store 去改變,這樣最終的樣子跟 Vuex 的結(jié)構(gòu)就類似了。這樣約定的好處是,我們能夠記錄所有 store 中發(fā)生的 state 變更,同時實(shí)現(xiàn)能做到記錄變更、保存狀態(tài)快照、歷史回滾/時光旅行的先進(jìn)的調(diào)試工具。

Vuex回顧

什么是Vuex

Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。Vuex 也集成到 Vue 的官方調(diào)試工具 devtools extension,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級調(diào)試功能。

  • Vuex 是專門為 Vue.js 設(shè)計(jì)的狀態(tài)管理庫
  • 它采用集中式的方式存儲需要共享的數(shù)據(jù)
  • 從使用角度,它就是一個 JavaScript 庫
  • 它的作用是進(jìn)行狀態(tài)管理,解決復(fù)雜組件通信,數(shù)據(jù)共享

什么情況下使用Vuex

官方文檔:

Vuex 可以幫助我們管理共享狀態(tài),并附帶了更多的概念和框架。這需要對短期和長期效益進(jìn)行權(quán)衡。

如果您不打算開發(fā)大型單頁應(yīng)用,使用 Vuex 可能是繁瑣冗余的。確實(shí)是如此——如果您的應(yīng)用夠簡單,您最好不要使用 Vuex。一個簡單的 store 模式就足夠您所需了。但是,如果您需要構(gòu)建一個中大型單頁應(yīng)用,您很可能會考慮如何更好地在組件外部管理狀態(tài),Vuex 將會成為自然而然的選擇。引用 Redux 的作者 Dan Abramov 的話說就是:Flux 架構(gòu)就像眼鏡:您自會知道什么時候需要它。

當(dāng)你的應(yīng)用中具有以下需求場景的時候:

  • 多個視圖依賴于同一狀態(tài)
  • 來自不同視圖的行為需要變更同一狀態(tài)

建議符合這種場景的業(yè)務(wù)使用 Vuex 來進(jìn)行數(shù)據(jù)管理,例如非常典型的場景:購物車。

注意:Vuex 不要濫用,不符合以上需求的業(yè)務(wù)不要使用,反而會讓你的應(yīng)用變得更麻煩。

核心概念回顧

  • Store:倉庫,store是使用Vuex應(yīng)用程序的核心,每一個應(yīng)用僅有一個store。store是一個容器,包含應(yīng)用中的大部分狀態(tài),當(dāng)然我們不能直接改變store中的應(yīng)用狀態(tài),我們需要通過提交mutation的方式改變狀態(tài)。
  • State:就是狀態(tài),保存在store中。因?yàn)閟tore是唯一的,所以state狀態(tài)也是惟一的,稱為單一狀態(tài)樹。但是所有的狀態(tài)都保存在state中的話,會讓程序難以維護(hù),可以通過后續(xù)的模塊解決該問題。
    • 注意:這里的state狀態(tài)是響應(yīng)式的
  • Getter:getter就像是Vuex中的計(jì)算屬性,方便從一個屬性派生出其他的值,它內(nèi)部可以對計(jì)算的結(jié)果進(jìn)行緩存,只有當(dāng)內(nèi)部依賴的state狀態(tài)發(fā)生改變時才會重新計(jì)算。
  • Mutation:state狀態(tài)的變化必須要通過提交mutation來完成
  • Action:action和mutation類似,不同的是action可以進(jìn)行異步的操作,內(nèi)部改變狀態(tài)的時候都需要提交mutation
  • Module:由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象上來,當(dāng)應(yīng)用變得十分復(fù)雜時,,store對象就有可能編的相當(dāng)臃腫。為了解決以上問題,Vuex允許我們將store分隔成模塊,每個模塊擁有自己的state、mutation、action、getter甚至是嵌套的子模塊

示例演示

使用vue create vuex-demo創(chuàng)建包含router和vuex的空項(xiàng)目

基本結(jié)構(gòu)

src/store/index.js

import Vue from 'vue' // 1.導(dǎo)入Vuex import Vuex from 'vuex'// 2.注冊Vuex Vue.use(Vuex)export default new Vuex.Store({state: {},mutations: {},actions: {},modules: {} })

src/main.js

import Vue from 'vue' import store from './store'Vue.config.productionTip = falsenew Vue({store, // 3.注入$store到Vue實(shí)例render: h => h(App) }).$mount('#app')

State

Vuex使用單一狀態(tài)樹,用一個對象就包含了全部的應(yīng)用層級狀態(tài)。

使用mapState簡化State在視圖中的使用,mapState返回計(jì)算屬性

mapState有兩種使用方式:

  • 接收數(shù)組參數(shù)

    // 該方式是Vuex提供的,所以使用前需要先導(dǎo)入 import { mapState } from 'vuex' // mapState返回名稱為count和msg的計(jì)算屬性 // 在模板中直接使用count和msg computed: {...mspState(['count', 'msg']) }

    使用數(shù)組參數(shù)

    <h1>Vuex - Demo</h1> <!-- count: {{ $store.state.count }}<br>--> <!-- msg: {{ $store.state.msg }}-->count: {{ count }}<br> msg: {{ msg }}
  • 接受對象參數(shù)

    如果當(dāng)前視圖中已經(jīng)有了count和msg,如果使用上述方式的話會有命名沖突,解決的方式:

    import {mapState} from 'vuex'export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({num: 'count', message: 'msg'}) // 當(dāng)store中存在count和msg時,使用對象參數(shù)重命名count和msg} }

    使用對象參數(shù)

    <h1>Vuex - Demo</h1> count: {{ num }}<br> msg: {{ message }}

Getter

Getter就是store中的計(jì)算屬性,使用mapGetter簡化視圖中的使用

App.vue

import {mapGetters, mapState} from 'vuex'export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({num: 'count', message: 'msg'}),...mapGetters(['reverseMsg'])} }

使用

<h2>Getter</h2> <!-- reverseMsg: {{ $store.getters.reverseMsg }}--> reverseMsg: {{ reverseMsg }}

src/store/index.js

import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 1,msg: 'Hello Vuex'},getters: {reverseMsg(state) {return state.msg.split('').reverse().join('')}},mutations: {},actions: {},modules: {} })

Mutation

更改Vuex的store中的狀態(tài)的唯一方法是提交mutation。Vuex中的mutation非常類似于事件:每個mutation都有一個字符串的事件類型(type)一個回調(diào)函數(shù)(handler)。這個回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方,并且它會接受state作為第一個參數(shù)。

使用Mutation改變狀態(tài)的好處是:集中的一個位置對狀態(tài)修改,不管在什么地方修改,都可以追蹤到狀態(tài)的修改。可以實(shí)現(xiàn)高級的time-travel調(diào)試功能

App.vue

import {mapGetters, mapMutations, mapState} from 'vuex'export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({num: 'count', message: 'msg'}),...mapGetters(['reverseMsg']),},methods: {...mapMutations(['increate'])} }

使用

<h2>Mutation</h2> <!-- <button @click="$store.commit('increate', 2)">Mutation</button>--> <button @click="increate(3)">Mutation</button>

src/store/index.js

import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 1,msg: 'Hello Vuex'},getters: {reverseMsg(state) {return state.msg.split('').reverse().join('')}},mutations: {increate(state, payload) {state.count += payload}},actions: {},modules: {} })

Action

Action類似于mutation,不同在于:

  • Action提交的是mutation,而不是直接變更狀態(tài)
  • Action可以包含任意異步操作

App.vue

import {mapActions, mapGetters, mapMutations, mapState} from 'vuex'export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({num: 'count', message: 'msg'}),...mapGetters(['reverseMsg']),},methods: {...mapMutations(['increate']),...mapActions(['increateAsync'])} }

使用

<h2>Action</h2> <!-- <button @click="$store.dispatch('increateAsync', 5)">Action</button>--> <button @click="increateAsync(5,1)">Action</button>

src/store/index.js

import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 1,msg: 'Hello Vuex'},getters: {reverseMsg(state) {return state.msg.split('').reverse().join('')}},mutations: {increate(state, payload) {state.count += payload}},actions: {increateAsync(context, payload) {console.log(payload)setTimeout(() => {context.commit('increate', payload)}, 2000)}},modules: {} })

Module

由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時,store 對象就有可能變得相當(dāng)臃腫。

為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊。在案例中體會 Module 的使用。

目錄結(jié)構(gòu):

嚴(yán)格模式

之前在介紹核心概念時說過,所有的狀態(tài)變更必須通過提交mutation,但是這僅僅是一個約定。如果你想的話,可以在組建中隨時獲取$store.state.msg,對它進(jìn)行修改。從語法層面來說,這是沒有問題的,但是這樣操作破壞了Vuex的約定。如果在組件中直接修改state,那在dev-tools中無法追蹤狀態(tài)的變更。開啟嚴(yán)格模式之后,如果在組件中直接修改state狀態(tài),會拋出錯誤。演示如下:

store/index.js中添加strict: true

在App.vue中添加如下代碼,點(diǎn)擊按鈕直接修改store中state.msg的值

打開瀏覽器進(jìn)行測試,發(fā)現(xiàn)$store.state.msg的值確實(shí)被修改了,但是console中會拋出異常。

需要注意的是:不要再生產(chǎn)模式下開啟嚴(yán)格模式,嚴(yán)格模式會深度檢查狀態(tài)樹,來檢查不合規(guī)的狀態(tài)改變,會影響性能。可以再開發(fā)環(huán)境中啟用嚴(yán)格模式,在生產(chǎn)模式下關(guān)閉嚴(yán)格模式。調(diào)整后的代碼:

  • 當(dāng)npm run serve時,process.env.NODE_ENV為development開發(fā)環(huán)境;
  • 當(dāng)npm run build時,process.env.NODE_ENV是production。這樣就可以根據(jù)環(huán)境來動態(tài)的設(shè)置嚴(yán)格模式。

購物車案例

接下來我們通過一個購物車案例來演示 Vuex 在項(xiàng)目中的使用方式,首先把購物車的項(xiàng)目模板下載下來。

模板地址

案例演示

server.js,在訪問數(shù)據(jù)時,必須先使用node server.js啟動server接口

const express = require('express') const cors = require('cors') const app = express()app.use(cors())const hostname = '127.0.0.1' const port = 3000const _products = [{ id: 1, title: 'iPad Pro', price: 500.01 },{ id: 2, title: 'H&M T-Shirt White', price: 10.99 },{ id: 3, title: 'Charli XCX - Sucker CD', price: 19.99 } ]app.use(express.json())app.get('/products', (req, res) => {res.status(200).json(_products) })app.post('/checkout', (req, res) => {res.status(200).json({success: Math.random() > 0.5}) })app.listen(port, hostname, () => {console.log(`Server is running at http://${hostname}:${port}/`) })

功能列表

  • 商品列表組件
  • 商品列表中彈出框組件(購物車彈出框)
  • 購物車列表組件

商品列表

商品列表功能

  • Vuex中創(chuàng)建兩個模塊,分別用來記錄商品列表和購物車的狀態(tài),stroe的結(jié)構(gòu):

    store--modulescart.jsproducts.jsindex.js
  • products模塊,store/modules/products.js

import axios from 'axios' const state = {products: [] } const getters = {} const mutations = {} const actions = {async getProducts ({ commit }) {const { data } = await axios({method: 'GET',url: 'http://127.0.0.1:3000/products'})commit('setProducts', data)} }export default {namespaced: true,state,getters,mutations,actions }
  • store/index.js中注冊products.js模塊
import Vue from 'vue' import Vuex from 'vuex' import products from './modules/products' import cart from './modules/cart'Vue.use(Vuex)const myPlugin = store => {store.subscribe((mutation, state) => {if (mutation.type.startsWith('cart/')) {window.localStorage.setItem('cart-products', JSON.stringify(state.cart.cartProducts))}}) }export default new Vuex.Store({state: {},mutations: {},actions: {},modules: {products,cart},plugins: [myPlugin] })
  • views/products.vue中實(shí)現(xiàn)商品列表的功能
<template><div><el-breadcrumb separator="/"><el-breadcrumb-item><a href="#/">首頁</a></el-breadcrumb-item><el-breadcrumb-item><a href="#/">商品列表</a></el-breadcrumb-item></el-breadcrumb><el-table:data="products"style="width: 100%"><el-table-columnprop="title"label="商品"></el-table-column><el-table-columnprop="price"label="價格"></el-table-column><el-table-columnprop="address"label="操作"><!-- <template slot-scope="scope"> --><template v-slot="scope"><el-button @click="addToCart(scope.row)">加入購物車</el-button></template></el-table-column></el-table></div> </template><script> import { mapState, mapActions, mapMutations } from 'vuex' export default {name: 'ProductList',computed: {...mapState('products', ['products'])},methods: {...mapActions('products', ['getProducts']),},created () {this.getProducts()} } </script><style></style>

添加購物車

  • cart 模塊實(shí)現(xiàn)添加購物車功能,store/modules/cart.js
const mutations = {addToCart (state, product) {// 1. cartProducts 中沒有該商品,把該商品添加到數(shù)組,并增加 count,isChecked,totalPrice// 2. cartProducts 有該商品,讓商品的數(shù)量加1,選中,計(jì)算小計(jì)const prod = state.cartProducts.find(item => item.id === product.id)if (prod) {prod.count++prod.isChecked = trueprod.totalPrice = prod.count * prod.price} else {state.cartProducts.push({...product,count: 1,isChecked: true,totalPrice: product.price})}} }
  • store/index.js 中注冊 cart 模塊
import Vue from 'vue' import Vuex from 'vuex' import products from './modules/products' import cart from './modules/cart'Vue.use(Vuex)const myPlugin = store => {store.subscribe((mutation, state) => {if (mutation.type.startsWith('cart/')) {window.localStorage.setItem('cart-products', JSON.stringify(state.cart.cartProducts))}}) }export default new Vuex.Store({state: {},mutations: {},actions: {},modules: {products,cart},plugins: [myPlugin] })
  • view/products.vue 中實(shí)現(xiàn)添加購物車功能
methods: {...mapMutations('cart', ['addToCart'])},
  • 測試,通過 vue-devtools 觀察數(shù)據(jù)的變化

商品列表-彈出購物車窗口

購物車列表

  • components/pop-cart.vue中展示購物車列表
<template><el-popoverwidth="350"trigger="hover"><el-table :data="cartProducts" size="mini"><el-table-column property="title" width="130" label="商品"></el-table-column><el-table-column property="price" label="價格"></el-table-column><el-table-column property="count" width="50" label="數(shù)量"></el-table-column><el-table-column label="操作"><template v-slot="scope"><el-button @click="deleteFromCart(scope.row.id)" size="mini">刪除</el-button></template></el-table-column></el-table><div><p>共 {{ totalCount }} 件商品 共計(jì)¥{{ totalPrice }}</p><el-button size="mini" type="danger" @click="$router.push({ name: 'cart' })">去購物車</el-button></div><el-badge :value="totalCount" class="item" slot="reference"><el-button type="primary">我的購物車</el-button></el-badge></el-popover> </template><script> import { mapState, mapGetters, mapMutations } from 'vuex' export default {name: 'PopCart',computed: {...mapState('cart', ['cartProducts']),...mapGetters('cart', ['totalCount', 'totalPrice'])},methods: {...mapMutations('cart', ['deleteFromCart'])} } </script><style></style>

刪除

  • cart 模塊實(shí)現(xiàn)從購物車刪除的功能,store/modules/cart.js
deleteFromCart (state, prodId) {const index = state.cartProducts.findIndex(item => item.id === prodId)index !== -1 && state.cartProducts.splice(index, 1) }
  • components/pop-cart.vue 中實(shí)現(xiàn)刪除功能
<el-table-column label="操作"><template v-slot="scope"><el-button @click="deleteFromCart(scope.row.id)" size="mini">刪除</el-button></template> </el-table-column> methods: {...mapMutations('cart', ['deleteFromCart']) }

小計(jì)

  • cart 模塊實(shí)現(xiàn)統(tǒng)計(jì)總數(shù)和總價,store/modules/cart.js
const getters = {totalCount (state) {return state.cartProducts.reduce((count, prod) => {return count + prod.count}, 0)},totalPrice (state) {return state.cartProducts.reduce((count, prod) => {return count + prod.totalPrice}, 0).toFixed(2)} }
  • components/pop-cart.vue 中顯示徽章和小計(jì)
<div><p>共 {{ totalCount }} 件商品 共計(jì)¥{{ totalPrice }}</p><el-button size="mini" type="danger" @click="$router.push({ name: 'cart' })">去購物車</el-button> </div> <el-badge :value="totalCount" class="item" slot="reference"><el-button type="primary">我的購物車</el-button> </el-badge> computed: {...mapState('cart', ['cartProducts']),...mapGetters('cart', ['totalCount', 'totalPrice']) },

購物車

購物車列表

<template><el-popoverwidth="350"trigger="hover"><el-table :data="cartProducts" size="mini"><el-table-column property="title" width="130" label="商品"></el-table-column><el-table-column property="price" label="價格"></el-table-column><el-table-column property="count" width="50" label="數(shù)量"></el-table-column><el-table-column label="操作"><template v-slot="scope"><el-button @click="deleteFromCart(scope.row.id)" size="mini">刪除</el-button></template></el-table-column></el-table><div><p>共 {{ totalCount }} 件商品 共計(jì)¥{{ totalPrice }}</p><el-button size="mini" type="danger" @click="$router.push({ name: 'cart' })">去購物車</el-button></div><el-badge :value="totalCount" class="item" slot="reference"><el-button type="primary">我的購物車</el-button></el-badge></el-popover> </template><script> import { mapState, mapGetters, mapMutations } from 'vuex' export default {name: 'PopCart',computed: {...mapState('cart', ['cartProducts']),...mapGetters('cart', ['totalCount', 'totalPrice'])},methods: {...mapMutations('cart', ['deleteFromCart'])} } </script><style></style>

全選功能

  • cart 模塊實(shí)現(xiàn)更新商品的選中狀態(tài),store/modules/cart.js
const mutations = {addToCart(state, product) {// 1. cartProducts 中沒有該商品,把該商品添加到數(shù)組,并增加 count,isChecked,totalPrice// 2. cartProducts 有該商品,讓商品的數(shù)量加1,選中,計(jì)算小計(jì)const prod = state.cartProducts.find(item => item.id === product.id)if (prod) {prod.count++prod.isChecked = trueprod.totalPrice = prod.count * prod.price} else {state.cartProducts.push({...product,count: 1,isChecked: true,totalPrice: product.price})}},deleteFromCart(state, prodId) {const index = state.cartProducts.findIndex(item => item.id === prodId)index !== -1 && state.cartProducts.splice(index, 1)}, updateAllProductChecked(state, checked) {state.cartProducts.forEach(prod => {prod.isChecked = checked}) }, updateProductChecked(state, {checked,prodId }) {const prod = state.cartProducts.find(prod => prod.id === prodId)prod && (prod.isChecked = checked) },updateProduct(state, {prodId,count}) {const prod = state.cartProducts.find(prod => prod.id === prodId)if (prod) {prod.count = countprod.totalPrice = count * prod.price}} }
  • views/cart.vue,實(shí)現(xiàn)全選功能
    • 使用事件拋出一個值
<el-table-columnwidth="55"><template v-slot:header><el-checkbox v-model="checkedAll" size="mini"></el-checkbox></template><!--@change="updateProductChecked" 默認(rèn)參數(shù):更新后的值@change="updateProductChecked(productId, $event)" 123, 原來那個默認(rèn)參數(shù)當(dāng)你傳遞了自定義參數(shù)的時候,還想得到原來那個默認(rèn)參數(shù),就手動傳遞一個 $event--><template v-slot="scope"><el-checkboxsize="mini":value="scope.row.isChecked"@change="updateProductChecked({prodId: scope.row.id,checked: $event})"></el-checkbox></template> </el-table-column> export default {name: 'Cart',computed: {...mapState('cart', ['cartProducts']),...mapGetters('cart', ['checkedCount', 'checkedPrice']),checkedAll: {get () {return this.cartProducts.every(prod => prod.isChecked)},set (value) {this.updateAllProductChecked(value)}}},methods: {...mapMutations('cart', ['updateAllProductChecked','updateProductChecked','updateProduct'])} }

數(shù)組文本框

  • cart 模塊實(shí)現(xiàn)更新商品數(shù)量,store/modules/cart.js
updateProduct(state, {prodId,count}) {const prod = state.cartProducts.find(prod => prod.id === prodId)if (prod) {prod.count = countprod.totalPrice = count * prod.price}}
  • views/cart.vue,實(shí)現(xiàn)數(shù)字文本框功能
<el-input-number :value="scope.row.count" @change="updateProduct({prodId: scope.row.id,count: $event})" size="mini"> </el-input-number> methods: {...mapMutations('cart', ['updateAllProductChecked','updateProductChecked','updateProduct']) }

刪除

小計(jì)

  • cart 模塊實(shí)現(xiàn)統(tǒng)計(jì)選中商品價格和數(shù)量,store/modules/cart.js
checkedCount(state) {return state.cartProducts.reduce((sum, prod) => {if (prod.isChecked) {sum += prod.count}return sum}, 0) }, checkedPrice(state) {return state.cartProducts.reduce((sum, prod) => {if (prod.isChecked) {sum += prod.totalPrice}return sum}, 0) }
  • views/cart.vue,實(shí)現(xiàn)小計(jì)
<div><p>已選 <span>{{ checkedCount }}</span> 件商品,總價:<span>{{ checkedPrice }}</span></p><el-button type="danger">結(jié)算</el-button> </div> computed: {...mapState('cart', ['cartProducts']),...mapGetters('cart', ['checkedCount', 'checkedPrice']),checkedAll: {get() {return this.cartProducts.every(prod => prod.isChecked)},set(value) {this.updateAllProductChecked(value)}} }

本地存儲

Vuex插件

  • Vuex的插件就是一個函數(shù)
  • 這個函數(shù)接受一個store的參數(shù)
  • 這個函數(shù)內(nèi)可以注冊一個函數(shù),讓它可以在mutaions之后再執(zhí)行
  • 就像在axios中的過濾器,在所有請求之后統(tǒng)一完成一件事

  • mutation的結(jié)構(gòu)
    • 如果想在cart模塊中的mutation之行結(jié)束之后再來調(diào)用調(diào)用,product模塊中不需要,可以使用mutation

  • 注冊插件

  • 最終實(shí)現(xiàn)

    import Vue from 'vue' import Vuex from 'vuex' import products from './modules/products' import cart from './modules/cart'Vue.use(Vuex)const myPlugin = store => {// 當(dāng)store初始化后調(diào)用// subscribe的作用是用來訂閱store中的mutation,會在每個mutation完成之后調(diào)用// 參數(shù):mutation、state// 如果想在cart模塊中的mutation之行結(jié)束之后再來調(diào)用調(diào)用,product模塊中不需要,可以使用mutationstore.subscribe((mutation, state) => {// 每次調(diào)用mutation之后調(diào)用// mutation的格式為 { type, payload }if (mutation.type.startsWith('cart/')) {//記錄到localStoragewindow.localStorage.setItem('cart-products', JSON.stringify(state.cart.cartProducts))}}) }export default new Vuex.Store({state: {},mutations: {},actions: {},modules: {cart,products},plugins: [myPlugin] })

Vuex模擬實(shí)現(xiàn)

回顧基礎(chǔ)示例,自己模擬實(shí)現(xiàn)一個Vuex實(shí)現(xiàn)同樣的功能

import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex) export default new Vuex.Store({state: {count: 0,msg: 'Hello World'},getters: {reverseMsg(state) {return state.msg.split('').reverse().join('')}},mutations: {increate(state, payload) {state.count += payload.num}},actions: {increate(context, payload) {setTimeout(() => {context.commit('increate', {num: 5})}, 2000)}} })

實(shí)現(xiàn)思路

  • 實(shí)現(xiàn)install方法
    • Vuex是Vue的一個插件,所以和模擬VueRouter類似,縣實(shí)現(xiàn)Vue插件約定的install方法
  • 實(shí)現(xiàn)Store類
    • 實(shí)現(xiàn)構(gòu)造函數(shù),接受options對象參數(shù)
    • state的響應(yīng)式處理
    • getter的實(shí)現(xiàn)
    • commit、dispatch方法

install方法

let _Vue = null function install (Vue) {_Vue = Vue_Vue.mixin({beforeCreate () {if (this.$options.store) {Vue.prototype.$store = this.$options.store}}}) }

Store類

let _Vue = nullclass Store {constructor(options) {const {state = {},getters = {},mutations = {},actions = {}} = optionsthis.state = _Vue.observable(state)// 此處不直接 this.getters = getters,是因?yàn)橄旅娴拇a中要方法 getters 中的 key// 如果這么寫的話,會導(dǎo)致 this.getters 和 getters 指向同一個對象// 當(dāng)訪問 getters 的 key 的時候,實(shí)際上就是訪問 this.getters 的 key 會觸發(fā) key 屬性的getter// 會產(chǎn)生死遞歸this.getters = Object.create(null)Object.keys(getters).forEach(key => {Object.defineProperty(this.getters, key, {get: () => getters[key](state)})})this._mutations = mutationsthis._actions = actions}commit(type, payload) {this._mutations[type](this.state, payload)}dispatch(type, payload) {this._actions[type](this, payload)}}// install方法可以接受兩個參數(shù),一個是Vue構(gòu)造函數(shù),另外一個是額外的選項(xiàng),這里只需要Vue構(gòu)造函數(shù) function install(Vue) {_Vue = Vue_Vue.mixin({beforeCreate() {// 首先判斷當(dāng)前Vue實(shí)例的$options中是否有store,如果是組件實(shí)例的話沒有store選項(xiàng),就不需要做這件事if (this.$options.store) {// 這里注冊插件的時候會混入beforeCreate,當(dāng)創(chuàng)建根實(shí)例的時候就會把$store注入到Vue實(shí)例上_Vue.prototype.$store = this.$options.store}}}) }export default {Store,install }

使用自己實(shí)現(xiàn)的Vuex

src/store/index.js 中修改導(dǎo)入 Vuex 的路徑,測試

import Vuex from '../myvuex' // 注冊插件 Vue.use(Vuex)

總結(jié)

以上是生活随笔為你收集整理的Vuex-状态管理(24)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲一级色 | 国产精品天干天干 | 操操操综合 | 神马午夜51 | 精品一区二区三区在线免费观看 | 免费不卡毛片 | 人人插人人草 | 国产精品九九热 | 四虎免费网址 | 一级一级黄色片 | 超级砰砰砰97免费观看最新一期 | 国产免费a视频 | 中文字幕123 | 久久影视大全 | 久久久久网 | 大乳村妇的性需求 | 国产欧美在线观看视频 | 精产国产伦理一二三区 | 绿帽人妻精品一区二区 | 色婷婷国产精品综合在线观看 | 超碰在线天天 | 久久久久久久亚洲精品 | 亚洲国产www| 嫩草国产在线 | 欧美xxxxx自由摘花 | 亚洲AV无码乱码国产精品牛牛 | 西西人体高清44rt·net | 中文字幕有码视频 | 午夜精品一区二区三区在线播放 | 天堂最新资源在线 | 久久久久久久久影院 | 波多野结衣一本 | 黄色一级片在线免费观看 | av大片在线免费观看 | 福利社区一区二区 | 欧美色图第一页 | 伊人快播 | 亚洲黄色一级 | 激情久久婷婷 | 色999在线 | 91美女啪啪 | 亚洲人成色777777精品音频 | 色综合久久网 | 激情婷婷色 | 久久99精品久久只有精品 | 免费a网站 | 永久免费未满 | 老妇女av| 中文字幕乱码一区二区 | 97久久精品| 国产午夜视频在线 | 女同性做受全过程动图 | 国产精品观看 | 免费网站www在线观看 | 日韩精品一区二区视频 | 中文字幕88| 日韩中文字幕一区 | 手机免费av| 国产人成在线观看 | 天堂影视av| 欧美一级鲁丝片 | 国产成人综合欧美精品久久 | 午夜寂寞影视 | 午夜亚洲aⅴ无码高潮片苍井空 | 日韩在线观看视频一区二区三区 | 91黄色视屏 | 亚洲天堂伊人网 | 放荡的美妇在线播放 | 色爱综合区 | 五月婷婷狠狠干 | 色葡萄影院 | 一本久道视频一本久道 | 麻豆av电影在线观看 | 污污视频网站免费观看 | 三上悠亚中文字幕在线播放 | 欧美精品激情视频 | 国产高清视频一区 | 亚洲一区二区影院 | 欧美人与性动交g欧美精器 国产在线视频91 | 欧美自拍偷拍第一页 | 成人免费毛片色戒 | 日本www免费 | 超碰碰碰 | 亚洲色图另类 | 一级黄色片免费看 | 婷婷久久伊人 | 一级片免费播放 | 日韩18p | 国产精品一区二区白浆 | 歪歪6080| 国产精品视频全国免费观看 | 国产人久久人人人人爽 | 日韩在线观看免费网站 | www色| 国产高中女学生第一次 | 美女又爽又黄又免费 | 天堂在线视频网站 | 色一情一乱一伦一区二区三区 | 国产精品二区视频 |