摘要:鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇下的使用方法,傳送門使用構建單頁應用新篇華麗的分割線原文地址前言在最近學習的時候,看到國外一篇講述了如何使用和來構建一個簡單筆記的單頁應用的文章。
鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇 vue2.0 下的 vuex 使用方法,傳送門:使用 Vuex + Vue.js 構建單頁應用【新篇】
-------------------- 華麗的分割線 --------------------
原文地址:https://coligo.io/learn-vuex-by-building-notes-app/
前言:在最近學習 Vue.js 的時候,看到國外一篇講述了如何使用 Vue.js 和 Vuex 來構建一個簡單筆記的單頁應用的文章。感覺收獲挺多,自己在它的例子的基礎上進行了一些優(yōu)化和自定義功能,在這里和大家分享下學習心得。
在這篇教程中我們將通過構建一個筆記應用來學習如何在我們的 Vue 項目中使用 Vuex。我們將大概的過一遍什么是 Vuex.js,在項目中什么時候使用它,和如何構建我們的 Vue 應用。
這里放一張我們項目的預覽圖片:
項目源碼:vuex-notes-app;有需要的同學可以直接下載源碼查看。
主要知識點Vuex 狀態(tài)管理機制的使用
Vue.js 的基礎 api
Vue-cli 腳手架的安裝及使用
vur-router 的使用
ES6 的語法,這里推薦看下阮一峰的入門教程
Vuex 概述在我們迫不及待的開始項目之前,我們最好先花幾分鐘來了解下 Vuex 的核心概念。
Vuex 是一個專門為 Vue.js 應用所設計的集中式狀態(tài)管理架構。它借鑒了 Flux 和 Redux 的設計思想,但簡化了概念,并且采用了一種為能更好發(fā)揮 Vue.js 數(shù)據(jù)響應機制而專門設計的實現(xiàn)。
state 這樣概念初次接觸的時候可能會感覺到有點模糊,簡單來說就是將 state 看成我們項目中使用的數(shù)據(jù)的集合。然后,Vuex 使得 組件本地狀態(tài)(component local state)和 應用層級狀態(tài)(application state) 有了一定的差異。
component local state:該狀態(tài)表示僅僅在組件內部使用的狀態(tài),有點類似通過配置選項傳入 Vue 組件內部的意思。
application level state:應用層級狀態(tài),表示同時被多個組件共享的狀態(tài)層級。
假設有這樣一個場景:我們有一個父組件,同時包含兩個子組件。父組件可以很容易的通過使用 props 屬性來向子組件傳遞數(shù)據(jù)。
但是問題來了,當我們的兩個子組件如何和對方互相通信的? 或者子組件如何傳遞數(shù)據(jù)給他父組件的?在我們的項目很小的時候,這個兩個問題都不會太難,因為我們可以通過事件派發(fā)和監(jiān)聽來完成父組件和子組件的通信。
然而,隨著我們項目的增長:
保持對所有的事件追蹤將變得很困難。到底哪個事件是哪個組件派發(fā)的,哪個組件該監(jiān)聽哪個事件?
項目邏輯分散在各個組件當中,很容易導致邏輯的混亂,不利于我們項目的維護。
父組件將變得和子組件耦合越來越嚴重,因為它需要明確的派發(fā)和監(jiān)聽子組件的某些事件。
這就是 Vuex 用來解決的問題。 Vuex 的四個核心概念分別是:
The state tree:Vuex 使用單一狀態(tài)樹,用一個對象就包含了全部的應用層級狀態(tài)。至此它便作為一個『唯一數(shù)據(jù)源(SSOT)』而存在。這也意味著,每個應用將僅僅包含一個 store 實例。單狀態(tài)樹讓我們能夠直接地定位任一特定的狀態(tài)片段,在調試的過程中也能輕易地取得整個當前應用狀態(tài)的快照。
Getters:用來從 store 獲取 Vue 組件數(shù)據(jù)。
Mutators:事件處理器用來驅動狀態(tài)的變化。
Actions:可以給組件使用的函數(shù),以此用來驅動事件處理器 mutations
如何你暫時還不太理解這個四個概念,不用著急,我們將在后面的項目實戰(zhàn)中詳細的解釋。
下面這張圖詳細的解釋了 Vuex 應用中數(shù)據(jù)的流向(Vuex 官方圖)
簡單解釋下:
Vuex 規(guī)定,屬于應用層級的狀態(tài)只能通過 Mutation 中的方法來修改,而派發(fā) Mutation 中的事件只能通過 action。
從左到又,從組件出發(fā),組件中調用 action,在 action 這一層級我們可以和后臺數(shù)據(jù)交互,比如獲取初始化的數(shù)據(jù)源,或者中間數(shù)據(jù)的過濾等。然后在 action 中去派發(fā) Mutation。Mutation 去觸發(fā)狀態(tài)的改變,狀態(tài)的改變,將觸發(fā)視圖的更新。
注意事項
數(shù)據(jù)流都是單向的
組件能夠調用 action
action 用來派發(fā) Mutation
只有 mutation 可以改變狀態(tài)
store 是響應式的,無論 state 什么時候更新,組件都將同步更新
環(huán)境安裝這個應用將使用 webpack 來做模塊打包,處理和熱重啟。使用 Vue 官方提供的腳手架 vue-cli。
安裝 vue-clinpm install -g vue-cli
注:Node.js >= 4.x, 5.x 最好
初始化應用vue init webpack vue-notes-app cd vue-notes-app npm install // 安裝依賴包 npm run dev // 啟動服務
初始化一個項目名為vue-notes-app的應用,并選擇使用 webpack 打包方式。在命令行中按照提示選擇初始化配置項。其中在選擇 JSLint 校驗的時候,推薦選擇 AirBNB 規(guī)范。
使用你最喜歡的編輯器打開我們剛剛新建的項目,項目的結構大概如下圖:
components/ 文件夾用來存放我們的 Vue 組件
vuex/ 文件夾存放的是和 Vuex store 相關的東西(state object,actions,mutators)
build/ 文件是 webpack 的打包編譯配置文件
config/ 文件夾存放的是一些配置項,比如我們服務器訪問的端口配置等
dist/ 該文件夾一開始是不存在,在我們的項目經過 build 之后才會產出
App.vue 根組件,所有的子組件都將在這里被引用
index.html 整個項目的入口文件,將會引用我們的根組件 App.vue
main.js 入口文件的 js 邏輯,在 webpack 打包之后將被注入到 index.html 中
功能模塊新增筆記,新增一篇筆記,編輯區(qū)顯示空的筆記內容
刪除筆記,刪除一篇筆記之后,編輯區(qū)域顯示當前筆記類別的第一項
筆記列表切換,分為全部筆記和收藏筆記兩種,在切換之后,編輯區(qū)域顯示當前列表的第一條筆記
收藏筆記,給當前激活的筆記打上收藏的標簽
項目組件劃分在這個項目中,我們將總共使用四個組件:根組件 App.vue,操作欄組件 Toolbar.vue,別表組件 NotesList.vue,筆記編輯組件 Editor.vue。
創(chuàng)建 Vuex Store按照上面我們列出來的功能模塊,我們在 Vuex/ 下面建立一個 store.js 文件。
import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); // 需要維護的狀態(tài) const state = { notes: [], activeNote: {}, show: "" }; const mutations = { // 初始化 state INIT_STORE(state, data) { state.notes = data.notes, state.show = data.show; state.activeNote = data.activeNote; }, // 新增筆記 NEW_NOTE(state) { var newNote = { id: +new Date(), title: "", content: "", favorite: false }; state.notes.push(newNote); state.activeNote = newNote; }, // 修改筆記 EDIT_NOTE(state, note) { state.activeNote = note; // 修改原始數(shù)據(jù) for (var i = 0; i < state.notes.length; i++) { if(state.notes[i].id === note.id){ state.notes[i] = note; break; } }; }, // 刪除筆記 DELETE_NOTE(state) { state.notes.$remove(state.activeNote); state.activeNote = state.notes[0] || {}; }, // 切換筆記的收藏與取消收藏 TOGGLE_FAVORITE(state) { state.activeNote.favorite = !state.activeNote.favorite; }, // 切換顯示數(shù)據(jù)列表類型:全部 or 收藏 SET_SHOW_ALL(state, show){ state.show = show; // 切換數(shù)據(jù)展示,需要同步更新 activeNote if(show === "favorite"){ state.activeNote = state.notes.filter(note => note.favorite)[0] || {}; }else{ state.activeNote = state.notes[0] || {}; } }, // 設置當前激活的筆記 SET_ACTIVE_NOTE(state, note) { state.activeNote = note; } }; export default new Vuex.Store({ state, mutations });創(chuàng)建 Vuex Actions
在 Vuex/ 下面建立一個 action.js,用來給組件使用的函數(shù)。
function makeAction(type) { return ({ dispatch }, ...args) => dispatch(type, ...args); }; const initNote = { id: +new Date(), title: "我的筆記", content: "第一篇筆記內容", favorite: false }; // 模擬初始化數(shù)據(jù) const initData = { show: "all", notes: [initNote], activeNote: initNote }; export const initStore = ({ dispatch }) => { dispatch("INIT_STORE", initData); }; // 更新當前activeNote對象 export const updateActiveNote = makeAction("SET_ACTIVE_NOTE"); // 添加一個note對象 export const newNote = makeAction("NEW_NOTE"); // 刪除一個note對象 export const deleteNote = makeAction("DELETE_NOTE"); export const toggleFavorite = makeAction("TOGGLE_FAVORITE"); export const editNote = makeAction("EDIT_NOTE"); // 更新列表展示 export const updateShow = makeAction("SET_SHOW_ALL");創(chuàng)建 Vuex Getters
在 vuex/ 下面建立一個 getter.js 文件,用來從 store 獲取數(shù)據(jù)。
// 獲取 noteList,這里將會根據(jù) state.show 的狀態(tài)做數(shù)據(jù)過濾 export const filteredNotes = (state) => { if(state.show === "all"){ return state.notes || {}; }else if(state.show === "favorite"){ return state.notes.filter(note => note.favorite) || {}; } }; // 獲取列表展示狀態(tài) : all or favorite export const show = (state) => { return state.show; }; // 獲取當前激活 note export const activeNote = (state) => { return state.activeNote; };
以上就是我們 Vuex 的所有邏輯了,在定下了我們需要完成的功能之后,接下來就是只需要在組件中去調用 action 來實現(xiàn)對應的功能了。
路由配置在這里我們將使用 vue-router 來做路由,引用 bootstrap 樣式。
index.html
vuex-notes-app
所有的入口邏輯我們都將在 main.js 中編寫
main.js
import Vue from "vue"; import App from "./App"; import VueRouter from "vue-router"; import VueResource from "vue-resource"; // 路由模塊和HTTP模塊 Vue.use(VueResource); Vue.use(VueRouter); const router = new VueRouter(); router.map({ "/index": { component: App } }); router.redirect({ "*": "/index" }); router.start(App, "#app");根組件 App.vue
在根組件中引用了三個子組件:Toolbar.vue, NotesList.vue, Editor.vue。
注意:我們在配置里面加入了 vuex 這么一個選項,這里用來將我們 action 里面定義的方法給暴露出來,我們在根組件中只做了一件事情,那就是初始化模擬數(shù)據(jù),因此我們在組件生命周期的 ready 階段調用了 actions 里面的 initStore 來初始化我們的 store 里面的 state
Toolbar.vue在這里,我們用到了 Vuex 的一個案例就是我們需要知道當前的激活的筆記是否是收藏類別的,如果是,我們需要高亮收藏按鈕,那么如何知道呢?那就是通過 vuex 里面的 getters 獲取當前激活的筆記對象,判斷它的 favorite 是否為 true。
始終牢記一個概念,vuex 中數(shù)據(jù)是單向的,只能從 store 獲取,而我們這個例子中的 activeNote 也是始終都在 store.js 中維護的,這樣子就可以給其他組件公用了
// 需要維護的狀態(tài) const state = { notes: [], activeNote: {}, show: "" };NotesList.vue
Notes | heavenru.com
筆記列表組件,主要有三個操作
渲染筆記
切換渲染筆記
點擊列表 title,切換 activeNote
我們通過 getters 中的 filteredNotes 方法獲取筆記列表
// 獲取 noteList,這里將會根據(jù) state.show 的狀態(tài)做數(shù)據(jù)過濾 export const filteredNotes = (state) => { if(state.show === "all"){ return state.notes || {}; }else if(state.show === "favorite"){ return state.notes.filter(note => note.favorite) || {}; } };
可以看到,我們獲取的列表是依賴于 state.show 這個狀態(tài)的。而我們的切換列表操作恰好就是調用 actions 里面的方法來更新 state.show,這樣一來,實現(xiàn)了數(shù)據(jù)列表的動態(tài)刷新,而且我們對樹的操作都是通過調用 actions 的方法來實現(xiàn)的。
我們再看,在切換列表的時候,我們還需要動態(tài)的更新 activeNote??纯次覀冊?store.js 中是如何做的:
// 切換顯示數(shù)據(jù)列表類型:全部 or 收藏 SET_SHOW_ALL(state, show){ state.show = show; // 切換數(shù)據(jù)展示,需要同步更新 activeNote if(show === "favorite"){ state.activeNote = state.notes.filter(note => note.favorite)[0] || {}; }else{ state.activeNote = state.notes[0] || {}; } }
觸發(fā)這些操作的是我們給兩個按鈕分別綁定了我們自定義的函數(shù),通過給函數(shù)傳入不同的參數(shù),然后調用 actions 里面的方法,來實現(xiàn)對數(shù)據(jù)的過濾,更新。
Editor.vue在 Editor.vue 組件中,我們需要能夠實時的更新當前的 activeNote 組件和列表中對應的我們正在修改的筆記對象的內容。
由于我們前面提到過,在組件中是不允許直接修改 store.js在里面的狀態(tài)值的,所以在這里的時候,我們通過一個計算屬性,將 store 里面的狀態(tài)值賦值給一個對象,然后在自定義的 updateNotes() 方法中,去調用 action,同時傳入 currentNote 對象。
在 store.js 中,我們是這么做的,找到對應的 id 的對象,重新賦值,因為前面提到過,我們的數(shù)據(jù)是響應式的,在這里進行了改變,對應的視圖也將刷新改變,這樣一來就實現(xiàn)了實時編輯,實時渲染的功能了。
// 修改筆記 EDIT_NOTE(state, note) { state.activeNote = note; // 修改原始數(shù)據(jù) for (var i = 0; i < state.notes.length; i++) { if(state.notes[i].id === note.id){ state.notes[i] = note; break; } }; },Q&A
在這個項目中,我們并沒有引入 vue-resource 插件,只是自己模擬了部分的數(shù)據(jù),有興趣的同學可以自己去試試。
由于我們的例子相對簡單,沒有涉及到很深入的東西,更深層次的研究需要大家花更多的時間去實踐了。
最后,再說一句,在 action 里面,我們其實可以做的還有更多,比如根據(jù) id 動態(tài)的異步獲取筆記內容等等,這些有興趣的同學可以自己去嘗試,一點點的豐富這個例子。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/111335.html
摘要:使用構建單頁應用新篇在去年的七月六號的時候,發(fā)布了一篇使用構建單頁應用的文章,文章主要是介紹的基本使用方法,發(fā)現(xiàn)對大部分的入門同學有很大的幫助,時至今日還有很多同學不斷的點贊與收藏,瀏覽量最高達到。 使用 Vuex + Vue.js 構建單頁應用【新篇】 在去年的七月六號的時候,發(fā)布了一篇 使用 Vuex + Vue.js 構建單頁應用 的文章,文章主要是介紹 vuex 的基本使用方法...
摘要:鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇下的使用方法,傳送門使用構建單頁應用新篇華麗的分割線原文地址前言在最近學習的時候,看到國外一篇講述了如何使用和來構建一個簡單筆記的單頁應用的文章。 鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇 vue2.0 下的 vuex 使用方法,傳送門:使用 Vuex + Vue.js 構建單頁應用【新篇】 ---------...
摘要:鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇下的使用方法,傳送門使用構建單頁應用新篇華麗的分割線原文地址前言在最近學習的時候,看到國外一篇講述了如何使用和來構建一個簡單筆記的單頁應用的文章。 鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇 vue2.0 下的 vuex 使用方法,傳送門:使用 Vuex + Vue.js 構建單頁應用【新篇】 ---------...
摘要:鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇下的使用方法,傳送門使用構建單頁應用新篇華麗的分割線原文地址前言在最近學習的時候,看到國外一篇講述了如何使用和來構建一個簡單筆記的單頁應用的文章。 鑒于該篇文章閱讀量大,回復的同學也挺多的,特地抽空寫了一篇 vue2.0 下的 vuex 使用方法,傳送門:使用 Vuex + Vue.js 構建單頁應用【新篇】 ---------...
閱讀 1781·2021-11-11 16:55
閱讀 2579·2021-08-27 13:11
閱讀 3641·2019-08-30 15:53
閱讀 2314·2019-08-30 15:44
閱讀 1402·2019-08-30 11:20
閱讀 1049·2019-08-30 10:55
閱讀 953·2019-08-29 18:40
閱讀 3048·2019-08-29 16:13