請各位讀者添加一下做者的微信公衆號,之後有新的文章,將在微信公衆號直接推送給各位,很是感謝。
javascript
若文章中存在內容沒法加載的狀況,請移步做者其餘博客。css
最近在看 Vue 的時候,別人給我安利了一個國外的小案例,經過 Vue 和 Vuex 來實現一個記事本。html
仔細剖析下,發現「麻雀雖小,五臟俱全」,是一個挺適合初學者學習分析的一個案例,因此本身也將本身的學習過程整理,得出本文。vue
國際慣例,首先感謝原文做者。java
參考案例傳送門:node
以後是內容聲明:git
另請注意,不少童鞋一直在問我,爲何粘貼完代碼無效,或者報錯的。es6
請在使用前安裝環境。github
另做者已經將完整程序包放在 Git 上了,請點擊下方連接進行下載,別忘了給我個 Star 呀!笑。
好了,開始正文。
本文中使用瞭如下內容,在閱讀本文前,請保證您對如下內容有了基礎的瞭解。
以前做者寫過一篇關於 Vue 基礎入門的文章。
裏面介紹了一下關於 Vue 的發展前景,以及 Vue 最基礎的使用,你們能夠選擇性的閱讀一下。
首先,若是咱們想要製做一個單頁應用,咱們首先要知道,咱們要作什麼?
那麼,首先來一個草圖。
這時候,咱們一塊兒來分析一下,當前頁面的實現過程。
這時候咱們明確了當前內容至少會涉及到頁面,頁面美化,以及數據處理等。
那咱們就能夠針對特定的需求來進行特定內容的處理了。
可是在正式開始文章前,請先了解一下,關於 Vuex 的基礎知識。
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。
它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
這就是 Vuex 背後的基本思想,借鑑了 Flux、Redux、和 The Elm Architecture。
與其餘模式不一樣的是,Vuex 是專門爲 Vue.js 設計的狀態管理庫,以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。
在這裏有一個須要注意的內容,就是關於 Vuex 中的 Store。
每個 Vuex 應用的核心就是 store(倉庫)。"store" 基本上就是一個容器,它包含着你的應用中大部分的狀態(state)。Vuex 和單純的全局對象有如下兩點不一樣:
- Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地獲得高效更新。
- 你不能直接改變 store 中的狀態。改變 store 中的狀態的惟一途徑就是顯式地提交(commit) mutations。這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。
其實說白了,咱們的 state 就是咱們項目中全部數據的集合,以後經過 Vuex 來區分開實際應用中的 組件本地狀態和應用層級狀態。
這裏須要區分一下,關於 組件本地狀態和應用層級狀態。
若是你明白了上面的內容,那麼接下來,咱們就能夠一塊兒來構建咱們的新項目了。
項目推薦直接使用 Vue 官方提供的腳手架(Vue-cli),因此第一步首先是安裝腳手架。
PS: 做者默認你們是對 Vue 有必定的基礎瞭解以後再看的文本,因此若是有哪些步驟不明確,請參考 Vue - 起手式。
npm install -g vue-cli複製代碼
注意:
-g
是直接安裝在全局環境下,推薦你們也是如此。sudo
sudo npm install -g vue-cli
vue init webpack note複製代碼
webpack
是咱們安裝內容時所默認使用的模板。note
是咱們建立的項目名稱N
cd /Users/lp1/Desktop/notes (你當前的文件目錄)複製代碼
npm install複製代碼
若是不安裝依賴,常常會發生下面這種錯誤。
npm run dev複製代碼
在啓動服務的時候,也有可能會遇到 端口被佔用 的錯誤。
第一種解決方案是進入 Vue 中的 index.js 中修改 默認端口號。
第二種是本身去找到被佔用的端口,kill 掉它(通常 kill node 的就能夠)。
若是這時候頁面中已經彈出一個新的頁面,則證實你當前的服務啓動成功了。
這裏就不單純的介紹項目的內容組成了,具體的能夠參考我以前的文章。
在開始以前,就如咱們上面的分析通常,咱們須要將咱們所要使用的內容進行劃分。
做者留言:
Vue 中最重要的兩個概念,理解了這兩個概念對之後會有很大幫助。
- 模塊化編程
- 數據驅動
根據頁面中的功能,咱們能夠將頁面分紅四個大塊。
首先第一個確定是最外層的父級,咱們通常直接書寫在 App.vue
當中。
其次是左中右三部分的組件,咱們分別命名並統一放在 components
當中。
而最下面的 App.vue 則是全部組件的根。
那咱們如今雖然將不一樣的組件進行了劃分,能夠劃分以後咱們該如何去處理三個組件之間的通訊呢?
這時候其實就該咱們的 Vuex 出馬了,Vuex 做爲一個「數據中心」,咱們能夠提早將咱們想要的內容,進行提早設置。
着重強調:
vuex 中數據是單向的,只能從 store 獲取,而咱們的各類操做也始終都在 store.js 中維護,並以此來給其餘組件公用。
那根據咱們上面所說,咱們須要在 Vuex 文件夾下,建立一個 store.js
文件。
須要注意,這裏使用不少 ES6 的語法,而且採用了原文不一樣的實現方法。
//引入vue及vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//須要維護的狀態
const state = {
/* notes:存儲note項 activeNote:當前正在編輯的note項 */
notes:[],
activeNote:{}
}
const mutations = {
//添加筆記
ADD_NOTE(state){
const newNote = {
/* text:默認文字內容 favorite:收藏 */
text:"new Note",
favorite:false
}
state.notes.push(newNote)
state.activeNote = newNote
},
//編輯筆記
EDIT_NOTE(state,text){
state.activeNote.text = text
},
// 設置當前激活的筆記
SET_ACTIVE_NOTE(state,note){
state.activeNote = note
},
// 切換筆記的收藏與取消收藏
TOGGLE_FAVORITE(state){
state.activeNote.favorite = !state.activeNote.favorite
},
//刪除筆記
DELETE_NOTE(state){
for (var i=0; i<state.notes.length; i++){
if (state.notes[i] == state.activeNote){
state.notes.splice(i, 1)
}
}
state.activeNote = state.notes[0]
}
}
const actions = {
/* actions處理函數接受一個 context 對象 { state, // 等同於 store.state, 若在模塊中則爲局部狀態 rootState, // 等同於 store.state, 只存在於模塊中 commit, // 等同於 store.commit dispatch, // 等同於 store.dispatch getters // 等同於 store.getters } */
addNote({commit}){
commit('ADD_NOTE')
},
editNote({commit},text){
commit("EDIT_NOTE",text)
},
updateActiveNote({commit},note){
commit('SET_ACTIVE_NOTE',note)
},
toggleFavorite({commit}){
commit('TOGGLE_FAVORITE')
},
deleteNote({commit}){
commit('DELETE_NOTE')
}
}
const getters = {
/* Getters 接受 state 做爲其第一個參數 state => state.notes爲箭頭函數等價於: function (state){ return state.notes } */
notes: state => state.notes,
activeNote: state => state.activeNote
}
export default new Vuex.Store({
state,
mutations,
actions,
getters
})複製代碼
記得處理完咱們所須要的數據以後,在 main.js
當中將咱們的 store
添加上去。
import Vue from 'vue'
import App from './App'
import store from '../vuex/store'
Vue.config.productionTip = false
new Vue({
el: '#app',
store,
template: '<App/>',
components: { App }
})複製代碼
對於整個 APP 的根,也就是 App.vue
來講,它須要處理的事情很是簡單,就是在對應的位置去調用對應的組件便可。
<template>
<div id="app">
<toolbar></toolbar>
<note-list></note-list>
<editor></editor>
</div>
</template>
<!--
李鵬 QQ:3206064928
-->
<script>
import Toolbar from './components/Toolbar'
import NoteList from './components/NoteList'
import Editor from './components/Editor'
export default {
components:{
Toolbar,
NoteList,
Editor
}
}
</script>
<style type="text/css">
html, #app {
height: 100%;
}
body {
margin: 0;
padding: 0;
border: 0;
height: 100%;
max-height: 100%;
position: relative;
}
</style>複製代碼
至於調用的組件內部,具體是如何實現的 App.vue
並不關心。
關於 Toolbar.vue
的設置就比較簡單了,咱們只須要調用咱們以前設置好的內容就能夠。
<template>
<div id="toolbar">
<i @click="addOne" class="glyphicon glyphicon-plus"></i>
<i @click="toggleFavorite" class="glyphicon glyphicon-star" v-bind:class="{starred:activeNote.favorite}"></i>
<i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
</div>
</template>
<script>
export default {
computed:{
activeNote(){
return this.$store.getters.activeNote
}
},
methods:{
addOne(){
//經過dispatch分發到actions中的addNote
this.$store.dispatch('addNote')
},
toggleFavorite(){
this.$store.dispatch('toggleFavorite')
},
deleteNote(){
this.$store.dispatch('deleteNote')
}
}
}
</script>
<style type="text/css">
#toolbar {
float: left;
width: 80px;
height: 100%;
background-color: #30414D;
color: #767676;
padding: 35px 25px 25px 25px;
}
#toolbar i {
font-size: 30px;
margin-bottom: 35px;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.5s ease;
}
#toolbar i:hover {
opacity: 1;
}
.starred {
color: #F7AE4F;
}
</style>複製代碼
須要注意,在這裏,我調用了一下 bootstrap
的圖標樣式。
這個是在 index.js
當中調用的。
因爲咱們以前已經將關於數據部分的內容處理過了,因此在這裏,咱們只須要進行一下簡單的判斷,將特定的內容加載便可。
<template>
<div id="notes-list">
<div id="list-header">
<h2>Notes</h2>
<div class="btn-group btn-group-justified" role="group">
<!-- All Notes button -->
<div class="btn-group" role="group">
<button @click="show='all'" type="button" class="btn btn-default" v-bind:class="{active:show=='all'}">
All Notes
</button>
</div>
<!-- Favorites Button -->
<div class="btn-group" role="group">
<button @click="show='favorites'" type="button" class="btn btn-default" v-bind:class="{active:show=='favorites'}">
Favorites
</button>
</div>
</div>
</div>
<!-- render notes in a list -->
<div class="container">
<div class="list-group">
<a v-for="item in notes" class="list-group-item" v-bind:class="{active:activeNote == item}" v-on:click="updateActiveNote(item)" href="#">
<h4 class="list-group-item-heading">
{{item.text}}
</h4>
</a>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
show:'all'
}
},
computed:{
notes(){
if (this.show=='all'){
return this.$store.getters.notes
}else if(this.show=='favorites'){
return this.$store.getters.notes.filter(note=>note.favorite)
}
},
activeNote(){
return this.$store.getters.activeNote
}
},
methods:{
updateActiveNote(note){
console.log(note)
this.$store.dispatch('updateActiveNote',note)
}
}
}
</script>
<style type="text/css">
#notes-list {
float: left;
width: 300px;
height: 100%;
background-color: #F5F5F5;
font-family: 'Raleway', sans-serif;
font-weight: 400;
}
#list-header {
padding: 5px 25px 25px 25px;
}
#list-header h2 {
font-weight: 300;
text-transform: uppercase;
text-align: center;
font-size: 22px;
padding-bottom: 8px;
}
#notes-list .container {
height: calc(100% - 137px);
max-height: calc(100% - 137px);
overflow: auto;
width: 100%;
padding: 0;
}
#notes-list .container .list-group-item {
border: 0;
border-radius: 0;
}
.list-group-item-heading {
font-weight: 300;
font-size: 15px;
}
</style>複製代碼
關於編輯區域,只須要作一件事,就是獲取當前對應內容的文字便可。
<template>
<div id="note-editor">
<textarea v-bind:value="activeNoteText" v-on:input="editNote" class="form-control"></textarea>
</div>
</template>
<script>
export default {
computed:{
activeNoteText(){
return this.$store.getters.activeNote.text
}
},
methods:{
editNote(e){
this.$store.dispatch('editNote',e.target.value)
}
}
}
</script>
<style type="text/css">
#note-editor {
height: 100%;
margin-left: 380px;
}
#note-editor textarea {
height: 100%;
border: 0;
border-radius: 0;
}
</style>複製代碼
本文主要是用於記錄一下本身的分析過程,若是有哪裏出錯了,歡迎你們指出。
謝謝你們。
李鵬(MR_LP)2017年04月17日