使用 Vuex + Vue.js 构建单页应用【新篇】
在去年的七月六号的时候,发布了一篇 使用 Vuex + Vue.js 构建单页应用 的文章,文章主要是介绍
vuex
的基本使用方法,发现对大部分的入门同学有很大的帮助,时至今日还有很多同学不断的点赞与收藏,浏览量最高达到 20.4K 。鉴于前端技术发展更新快速,特此在这里重新整理一篇 vue2.0 版本的vuex
基本使用方法,希望能给更多刚入门或者将要入门的同学带来帮助。
这篇文章主要是介绍最新 vue2.0 API 的使用方法, 和 vue1.x 的一些差异的地方。
阅读建议
1、在阅读这篇文章之前,我希望你已经阅读过我上一篇文章 使用 Vuex + Vue.js 构建单页应用 ,明白我们需要实现的基本需求。
2、希望你阅读并掌握以下知识点
目录层级变化
首先是目录层级的变动,我们看下前后对比:
2.0 版本,vuex 的文件夹改为了 store
├── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── Editor.vue
│ │ ├── NoteList.vue
│ │ └── Toolbar.vue
│ ├── main.js
│ └── store
│ ├── actions.js
│ ├── getters.js
│ ├── index.js
│ ├── mutation-types.js
│ └── mutations.js
└── static
1..0 版本
├── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── Editor.vue
│ │ ├── NotesList.vue
│ │ └── Toolbar.vue
│ ├── main.js
│ └── vuex
│ ├── actions.js
│ ├── getters.js
│ └── store.js
└── static
使用方式的变动
在文件的 main.js
中注入,2.0 的注入方式如下
import Vue from 'vue'
import App from './App'
import store from './store';
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
template: '<App/>',
store,
components: { App }
})
在组件中使用方式
我们来看 Editor.vue
组件内部如何使用 vuex
的
<template>
<div class="note-editor">
<div class="form-group">
<input type="text" name="title"
class="title form-control"
placeholder="请输入标题"
@input="updateNote"
v-model="currentNote.title">
<textarea
v-model="currentNote.content" name="content"
class="form-control" row="3" placeholder="请输入正文"
@input="updateNote"></textarea>
</div>
</div>
</template>
<style>
...
</style>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
export default {
name: 'Editor',
computed: {
...mapGetters([
'activeNote'
]),
currentNote() {
return this.activeNote;
}
},
methods: {
...mapActions({
update: 'updateNote'
}),
updateNote() {
this.update({
note: this.currentNote
});
}
}
}
</script>
由于我们在入口文件 main.js
中已经注入 store
对象,使得我们能够在子组件中获取到它,在这里,我们使用了 vuex
提供的三个扩展方法 mapState
、mapActions
、mapGetters
。
另外一个不同点是在我们的 NodeList.vue
组件中,在 vue2.0
里面已经移除了自带的过滤器函数,官方建议我们使用计算属性,下面是我们更改后的使用方法:
<template>
<div class="notes-list">
<div class="list-header">
<h2>Notes | heavenru.com</h2>
<div class="btn-group btn-group-justified" role="group">
<!-- all -->
<div class="btn-group" role="group">
<button type="button" class="btn btn-default"
@click="toggleShow('all')"
:class="{active: show === 'all'}">All Notes</button>
</div>
<!-- favorites -->
<div class="btn-group" role="group">
<button type="button" class="btn btn-default"
@click="toggleShow('favorite')"
:class="{active: show === 'favorite'}">Favorites</button>
</div>
</div>
<div class="btn-group btn-group-justified" role="group">
<div class="input-group search">
<input type="text" class="form-control" v-model="search" placeholder="Search for...">
<span class="input-group-addon">
<i class="glyphicon glyphicon-search"></i>
</span>
</div>
</div>
</div>
<!-- 渲染笔记列表 -->
<div class="container">
<div class="list-group">
<div
v-for="(note, index) in searchNotes" :key="index"
class="list-group-item"
:class="{active: activeNote === note}"
@click="updateActiveNote(note)">
<h4 class="list-group-item-heading">
{{note.title.trim().substring(0,30)}}
</h4>
</div>
</div>
</div>
</div>
</template>
<style scoped>
...
</style>
<script>
import { mapGetters, mapState, mapActions } from 'vuex';
export default {
name: 'NoteList',
data() {
return {
search: ''
}
},
computed: {
...mapGetters([
'filteredNotes'
]),
// state 内部状态
...mapState([
'show',
'activeNote'
]),
// 计算属性,自定义过滤
searchNotes() {
if (this.search.length > 0) {
return this.filteredNotes.filter((note) => note.title.toLowerCase().indexOf(this.search) > -1);
} else {
return this.filteredNotes;
}
}
},
methods: {
...mapActions([
'toggleListShow',
'setActiveNote'
]),
// 切换列表,全部或者收藏
toggleShow(type) {
this.toggleListShow({ show: type});
},
// 点击列表,更新当前激活文章
updateActiveNote(note) {
this.setActiveNote({ note });
}
}
}
</script>
Q&A
其他的变动,大家自行的查看源码学习:vuex-notes-app2。