如何使用Vuex+Vue.js构建单页应用
1个回答
展开全部
按照上面我们列出来的功能模块,我们在 Vuex/ 下面建立一个 store.js 文件
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// 需要维护的状态
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;
// 修改原始数据
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;
},
// 切换显示数据列表类型:全部 or 收藏
SET_SHOW_ALL(state, show){
state.show = show;
// 切换数据展示,需要同步更新 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
});
创建 Vuex Actions
在 Vuex/ 下面建立一个 action.js,用来给组件使用的函数
function makeAction(type) {
return ({ dispatch }, ...args) => dispatch(type, ...args);
};
const initNote = {
id: +new Date(),
title: '我的笔记',
content: '第一篇笔记内容',
favorite: false
};
// 模拟初始化数据
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');
创建 Vuex Getters
在 vuex/ 下面建立一个 getter.js 文件,用来从 store 获取数据
// 获取 noteList,这里将会根据 state.show 的状态做数据过滤
export const filteredNotes = (state) => {
if(state.show === 'all'){
return state.notes || {};
}else if(state.show === 'favorite'){
return state.notes.filter(note => note.favorite) || {};
}
};
// 获取列表展示状态 : all or favorite
export const show = (state) => {
return state.show;
};
// 获取当前激活 note
export const activeNote = (state) => {
return state.activeNote;
};
以上就是我们 Vuex 的所有逻辑了,在定下了我们需要完成的功能之后,接下来就是只需要在组件中去调用 action 来实现对应的功能了。
路由配置
在这里我们将使用 vue-router 来做路由,引用 bootstrap 样式。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vuex-notes-app</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
所有的入口逻辑我们都将在 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
<template>
<div id="app" class="app">
<toolbar></toolbar>
<notes-list></notes-list>
<editor></editor>
</div>
</template>
<style>
html, #app {
height: 100%;
}
body {
margin: 0;
padding: 0;
border: 0;
height: 100%;
max-height: 100%;
position: relative;
}
</style>
<script>
import Toolbar from './components/Toolbar';
import NotesList from './components/NotesList';
import Editor from './components/Editor';
import store from './vuex/store';
import { initStore } from './vuex/actions';
export default {
components: {
Toolbar,
NotesList,
Editor
},
store,
vuex: {
actions: {
initStore
}
},
ready() {
this.initStore()
}
}
</script>
在根组件中引用了三个子组件:Toolbar.vue, NotesList.vue, Editor.vue。
注意:我们在配置里面加入了 vuex 这么一个选项,这里用来将我们 action 里面定义的方法给暴露出来,我们在根组件中只做了一件事情,那就是初始化模拟数据,因此我们在组件生命周期的 ready 阶段调用了 actions 里面的 initStore 来初始化我们的 store 里面的 state
Toolbar.vue
<template>
<div id="toolbar">
<i class="glyphicon logo"><img src="../assets/logo.png" width="30" height="30"></i>
<i @click="newNote" class="glyphicon glyphicon-plus"></i>
<i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNote.favorite}"></i>
<i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
</div>
</template>
<script>
import { newNote, deleteNote, toggleFavorite } from '../vuex/actions';
import { activeNote } from '../vuex/getters';
export default {
vuex: {
getters: {
activeNote
},
actions: {
newNote,
deleteNote,
toggleFavorite
}
}
}
</script>
<style lang="scss" scoped>
#toolbar{
float: left;
width: 80px;
height: 100%;
background-color: #30414D;
color: #767676;
padding: 35px 25px 25px 25px;
.starred {
color: #F7AE4F;
}
i{
font-size: 30px;
margin-bottom: 35px;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.5s ease;
&:hover{
opacity: 1;
}
}
}
</style>
在这里,我们用到了 Vuex 的一个案例就是我们需要知道当前的激活的笔记是否是收藏类别的,如果是,我们需要高亮收藏按钮,那么如何知道呢?那就是通过 vuex 里面的 getters 获取当前激活的笔记对象,判断它的 favorite 是否为 true。
始终牢记一个概念,vuex 中数据是单向的,只能从 store 获取,而我们这个例子中的 activeNote 也是始终都在 store.js 中维护的,这样子就可以给其他组件公用了
// 需要维护的状态
const state = {
notes: [],
activeNote: {},
show: ''
};
NotesList.vue
<template>
<div id="notes-list">
<div id="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>
<!-- 渲染笔记列表 -->
<div class="container">
<div class="list-group">
<a v-for="note in filteredNotes"
class="list-group-item" href="#"
:class="{active: activeNote === note}"
@click="updateActiveNote(note)">
<h4 class="list-group-item-heading">
{{note.title.trim().substring(0,30)}}
</h4>
</a>
</div>
</div>
</div>
</template>
<script>
import { updateActiveNote, updateShow } from '../vuex/actions';
import { show, filteredNotes, activeNote } from '../vuex/getters';
export default {
vuex: {
getters: {
show,
filteredNotes,
activeNote
},
actions: {
updateActiveNote,
updateShow
}
},
methods: {
toggleShow(show) {
this.updateShow(show);
}
}
}
</script>
笔记列表组件,主要有三个操作
渲染笔记
切换渲染笔记
点击列表 title,切换 activeNote
我们通过 getters 中的 filteredNotes 方法获取笔记列表
// 获取 noteList,这里将会根据 state.show 的状态做数据过滤
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 这个状态的。而我们的切换列表操作恰好就是调用 actions 里面的方法来更新 state.show ,这样一来,实现了数据列表的动态刷新,而且我们对树的操作都是通过调用 actions 的方法来实现的。
我们再看,在切换列表的时候,我们还需要动态的更新 activeNote 。 看看我们在 store.js 中是如何做的:
// 切换显示数据列表类型:全部 or 收藏
SET_SHOW_ALL(state, show){
state.show = show;
// 切换数据展示,需要同步更新 activeNote
if(show === 'favorite'){
state.activeNote = state.notes.filter(note => note.favorite)[0] || {};
}else{
state.activeNote = state.notes[0] || {};
}
}
触发这些操作的是我们给两个按钮分别绑定了我们自定义的函数,通过给函数传入不同的参数,然后调用 actions 里面的方法,来实现对数据的过滤,更新。
Editor.vue
<template>
<div id="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>
<script>
import { editNote } from '../vuex/actions';
import { activeNote } from '../vuex/getters';
export default {
vuex: {
getters: {
activeNote
},
actions: {
editNote
}
},
computed: {
// 通过计算属性得到的一个对象,这样子我们就能愉快的使用 v-model 了
currentNote: activeNote
},
methods: {
// 为什么这么做? 因为在严格模式中不允许直接在模板层面去修改 state 中的值
updateNote() {
this.editNote(this.currentNote);
}
}
}
</script>
在 Editor.vue 组件中,我们需要能够实时的更新当前的 activeNote 组件和列表中对应的我们正在修改的笔记对象的内容。
由于我们前面提到过,在组件中是不允许直接修改 store.js在里面的状态值的,所以在这里的时候,我们通过一个计算属性,将 store 里面的状态值赋值给一个对象,然后在自定义的 updateNotes() 方法中,去调用 action,同时传入 currentNote 对象。
在 store.js 中,我们是这么做的,找到对应的 id 的对象,重新赋值,因为前面提到过,我们的数据是响应式的,在这里进行了改变,对应的视图也将刷新改变,这样一来就实现了实时编辑,实时渲染的功能了。
// 修改笔记
EDIT_NOTE(state, note) {
state.activeNote = note;
// 修改原始数据
for (var i = 0; i < state.notes.length; i++) {
if(state.notes[i].id === note.id){
state.notes[i] = note;
break;
}
};
},
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// 需要维护的状态
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;
// 修改原始数据
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;
},
// 切换显示数据列表类型:全部 or 收藏
SET_SHOW_ALL(state, show){
state.show = show;
// 切换数据展示,需要同步更新 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
});
创建 Vuex Actions
在 Vuex/ 下面建立一个 action.js,用来给组件使用的函数
function makeAction(type) {
return ({ dispatch }, ...args) => dispatch(type, ...args);
};
const initNote = {
id: +new Date(),
title: '我的笔记',
content: '第一篇笔记内容',
favorite: false
};
// 模拟初始化数据
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');
创建 Vuex Getters
在 vuex/ 下面建立一个 getter.js 文件,用来从 store 获取数据
// 获取 noteList,这里将会根据 state.show 的状态做数据过滤
export const filteredNotes = (state) => {
if(state.show === 'all'){
return state.notes || {};
}else if(state.show === 'favorite'){
return state.notes.filter(note => note.favorite) || {};
}
};
// 获取列表展示状态 : all or favorite
export const show = (state) => {
return state.show;
};
// 获取当前激活 note
export const activeNote = (state) => {
return state.activeNote;
};
以上就是我们 Vuex 的所有逻辑了,在定下了我们需要完成的功能之后,接下来就是只需要在组件中去调用 action 来实现对应的功能了。
路由配置
在这里我们将使用 vue-router 来做路由,引用 bootstrap 样式。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vuex-notes-app</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
所有的入口逻辑我们都将在 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
<template>
<div id="app" class="app">
<toolbar></toolbar>
<notes-list></notes-list>
<editor></editor>
</div>
</template>
<style>
html, #app {
height: 100%;
}
body {
margin: 0;
padding: 0;
border: 0;
height: 100%;
max-height: 100%;
position: relative;
}
</style>
<script>
import Toolbar from './components/Toolbar';
import NotesList from './components/NotesList';
import Editor from './components/Editor';
import store from './vuex/store';
import { initStore } from './vuex/actions';
export default {
components: {
Toolbar,
NotesList,
Editor
},
store,
vuex: {
actions: {
initStore
}
},
ready() {
this.initStore()
}
}
</script>
在根组件中引用了三个子组件:Toolbar.vue, NotesList.vue, Editor.vue。
注意:我们在配置里面加入了 vuex 这么一个选项,这里用来将我们 action 里面定义的方法给暴露出来,我们在根组件中只做了一件事情,那就是初始化模拟数据,因此我们在组件生命周期的 ready 阶段调用了 actions 里面的 initStore 来初始化我们的 store 里面的 state
Toolbar.vue
<template>
<div id="toolbar">
<i class="glyphicon logo"><img src="../assets/logo.png" width="30" height="30"></i>
<i @click="newNote" class="glyphicon glyphicon-plus"></i>
<i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNote.favorite}"></i>
<i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
</div>
</template>
<script>
import { newNote, deleteNote, toggleFavorite } from '../vuex/actions';
import { activeNote } from '../vuex/getters';
export default {
vuex: {
getters: {
activeNote
},
actions: {
newNote,
deleteNote,
toggleFavorite
}
}
}
</script>
<style lang="scss" scoped>
#toolbar{
float: left;
width: 80px;
height: 100%;
background-color: #30414D;
color: #767676;
padding: 35px 25px 25px 25px;
.starred {
color: #F7AE4F;
}
i{
font-size: 30px;
margin-bottom: 35px;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.5s ease;
&:hover{
opacity: 1;
}
}
}
</style>
在这里,我们用到了 Vuex 的一个案例就是我们需要知道当前的激活的笔记是否是收藏类别的,如果是,我们需要高亮收藏按钮,那么如何知道呢?那就是通过 vuex 里面的 getters 获取当前激活的笔记对象,判断它的 favorite 是否为 true。
始终牢记一个概念,vuex 中数据是单向的,只能从 store 获取,而我们这个例子中的 activeNote 也是始终都在 store.js 中维护的,这样子就可以给其他组件公用了
// 需要维护的状态
const state = {
notes: [],
activeNote: {},
show: ''
};
NotesList.vue
<template>
<div id="notes-list">
<div id="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>
<!-- 渲染笔记列表 -->
<div class="container">
<div class="list-group">
<a v-for="note in filteredNotes"
class="list-group-item" href="#"
:class="{active: activeNote === note}"
@click="updateActiveNote(note)">
<h4 class="list-group-item-heading">
{{note.title.trim().substring(0,30)}}
</h4>
</a>
</div>
</div>
</div>
</template>
<script>
import { updateActiveNote, updateShow } from '../vuex/actions';
import { show, filteredNotes, activeNote } from '../vuex/getters';
export default {
vuex: {
getters: {
show,
filteredNotes,
activeNote
},
actions: {
updateActiveNote,
updateShow
}
},
methods: {
toggleShow(show) {
this.updateShow(show);
}
}
}
</script>
笔记列表组件,主要有三个操作
渲染笔记
切换渲染笔记
点击列表 title,切换 activeNote
我们通过 getters 中的 filteredNotes 方法获取笔记列表
// 获取 noteList,这里将会根据 state.show 的状态做数据过滤
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 这个状态的。而我们的切换列表操作恰好就是调用 actions 里面的方法来更新 state.show ,这样一来,实现了数据列表的动态刷新,而且我们对树的操作都是通过调用 actions 的方法来实现的。
我们再看,在切换列表的时候,我们还需要动态的更新 activeNote 。 看看我们在 store.js 中是如何做的:
// 切换显示数据列表类型:全部 or 收藏
SET_SHOW_ALL(state, show){
state.show = show;
// 切换数据展示,需要同步更新 activeNote
if(show === 'favorite'){
state.activeNote = state.notes.filter(note => note.favorite)[0] || {};
}else{
state.activeNote = state.notes[0] || {};
}
}
触发这些操作的是我们给两个按钮分别绑定了我们自定义的函数,通过给函数传入不同的参数,然后调用 actions 里面的方法,来实现对数据的过滤,更新。
Editor.vue
<template>
<div id="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>
<script>
import { editNote } from '../vuex/actions';
import { activeNote } from '../vuex/getters';
export default {
vuex: {
getters: {
activeNote
},
actions: {
editNote
}
},
computed: {
// 通过计算属性得到的一个对象,这样子我们就能愉快的使用 v-model 了
currentNote: activeNote
},
methods: {
// 为什么这么做? 因为在严格模式中不允许直接在模板层面去修改 state 中的值
updateNote() {
this.editNote(this.currentNote);
}
}
}
</script>
在 Editor.vue 组件中,我们需要能够实时的更新当前的 activeNote 组件和列表中对应的我们正在修改的笔记对象的内容。
由于我们前面提到过,在组件中是不允许直接修改 store.js在里面的状态值的,所以在这里的时候,我们通过一个计算属性,将 store 里面的状态值赋值给一个对象,然后在自定义的 updateNotes() 方法中,去调用 action,同时传入 currentNote 对象。
在 store.js 中,我们是这么做的,找到对应的 id 的对象,重新赋值,因为前面提到过,我们的数据是响应式的,在这里进行了改变,对应的视图也将刷新改变,这样一来就实现了实时编辑,实时渲染的功能了。
// 修改笔记
EDIT_NOTE(state, note) {
state.activeNote = note;
// 修改原始数据
for (var i = 0; i < state.notes.length; i++) {
if(state.notes[i].id === note.id){
state.notes[i] = note;
break;
}
};
},
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询