请各位读者添加一下做者的微信公众号,之后有新的文章,将在微信公众号直接推送给各位,很是感谢。
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日