教程来源:https://www.bilibili.com/video/av39088530/?spm_id_from=333.788.videocard.11
前言:概念
1.什么是Vuex
Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex也集成到Vue的官方调试工具devtools扩展,提供了诸如零配置的time-travel调试,状态快照导入导出等高级调试功能。
2.什么情况下我应该使用 Vuex?
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
一.Vuex快速安装和使用
1.直接下载 / CDN 引用
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.1/dist/vuex.js"></script>
2.NPM
npm install vuex --save
在一个模块化的打包系统中,您必须显式地通过 Vue.use()
来安装 Vuex:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
二.开始
最简单的 Store
1.html中构建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuex的安装</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.1/dist/vuex.js"></script>
</head>
<body>
<script>
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
</script>
</body>
</html>
然后进入浏览器中,打开管理台,输入以下代码
第一个程序成功完成
2.模块化工具中构建
(1).项目创建并初始化项目(创建package.json):创建文件夹、js文件
初始化项目:命令行输入npm init -y
(2).安装vue、vuex:命令行npm install vue vuex
(3).导入vue、vuex。这里有两种导入方式
-
const方式
const Vue = require("vue")
const Vuex = require("vuex")
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
console.log(store.state.count);
store.commit("increment");
console.log(store.state.count);
然后命令行:node index.js
运行结果:
- import方式
首先将index.js文件放入src路径下(如果没有该路径,则创建)
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
console.log(store.state.count);
store.commit("increment");
console.log(store.state.count);
直接在命令行中输入npm run build 编译,此时会报错
此时需要安装webpack进行编译:npm install -D webpack webpack-cli,然后在package.json文件中新建一个脚本
命令行输入:npm run build 完成项目编译,此时项目中出现dist路径,并且main.js
最终运行:node dist/main.js
其中黄色字体WARNING:
其意思就是 配置警告:“模式”选项没有设置,webpack将回退“生产”这个值。将“mode”选项设置为“development”或“production”,以便为每个环境启用默认值。
此时有两种解决方案:
方案一:在package.json文件中添加
之后再重新编译npm run build,就不会出现警告了
方案二:
在package.json同一级目录下创建webpack.config.js文件
module.exports = {
entry:"./src/index.js",
mode:"development"
}
再在package.json中定义一个build2:
最后编译npm run build2,就不会出现警告了
三.使用webpack工程化vuex
1.创建项目结构
2.初始化项目
yarn 命令安装:npm install -g yarn
yarn init -y,此时会产生一个package.json
更改package.json
其中index.js
const store = new Vuex.Store({
state: {
count: 8000,
message:"Hello Vuex!!!"
},
mutations: {
increment (state) {
state.count++
}
}
})
const Counter = {
template:"<div>{{sayHi}}----{{totalData}}----{{count}}----{{message}}</div>",
data(){
return{
dataStart:100
}
},
// 方式一
/*computed:{
count(){
return store.state.count;
}
}*/
// 方式二
/*computed:Vuex.mapState(["count","message"])*/
// 方式三
/*computed:Vuex.mapState({
count:"count",
message:"message"
})*/
// 方式四
computed:{
sayHi(){
return "Hi!!!!"
},
...Vuex.mapState({
count:(state)=>state.count,
message:function (state) {
return state.message
},
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
totalData:function (state) {
return this.dataStart+state.count
}
})
}
}
const app = new Vue({
el:"#app",
store,
components:{Counter},
template: '<div class="app"><counter></counter></div> '
})
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuex</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
安装vue、vuex:yarn add vue vuex
js文件中导入vue、vuex
此时需要安装webpack进行编译:yarn add -D webpack webpack-cli
在package.json同一级目录下创建webpack.config.js文件
module.exports = {
entry:"./src/index.js",
mode:"development"
}
此时目录中有dist文件夹,同时有main.js,但是没有html文件,此时需要依赖webpack的一个插件html-webpack-plugin,安装
yarn add -D html-webpack-plugin
然后在webpack.config.js文件中导入,并定模板
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry:"./src/index.js",
mode:"development",
plugins:[
new HtmlWebpackPlugin({
template:"./src/index.html"
})
]
}
再次编译,最终出现index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vuex的安装</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="main.js"></script></body>
</html>
运行dist/index.html,打开控制台,报错
此时需要在webpack.config.js文件中,用resolve解决一个冲突
重新编译
提高开发效率,安装webpack-dev-server插件yarn add -D webpack-dev-server,并在package.json文件中添加代码
运行yarn dev
此时在浏览器中输入:http://localhost:8080/即可访问
3.代码分离
由于一个工程中代码非常多,所以还得代码分离,意思就是将vuex的代码专门放在一个js文件中
在src目录下,新创建一个store.js并将index.js中的一部分代码添加到store.js中
import Vuex from "vuex"
function creatStore() {
return new Vuex.Store({
state: {
count: 8000,
message:"Hello Vuex,This is webpack!"
},
mutations: {
increment (state) {
state.count++
}
}
})
}
export default creatStore;
在src目录下,新创建一个components文件夹,并在其里面新创建一个counter.js并将index.js中的一部分代码添加到counter.js中
import Vuex from "vuex";
const Counter = {
template:"<div>{{sayHi}}----{{totalData}}----{{count}}----{{message}}</div>",
data(){
return{
dataStart:100
}
},
// 方式一
/*computed:{
count(){
return store.state.count;
}
}*/
// 方式二
/*computed:Vuex.mapState(["count","message"])*/
// 方式三
/*computed:Vuex.mapState({
count:"count",
message:"message"
})*/
// 方式四
computed:{
sayHi(){
return "Hi!!!!"
},
...Vuex.mapState({
count:(state)=>state.count,
message:function (state) {
return state.message
},
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
totalData:function (state) {
return this.dataStart+state.count
}
})
}
}
export default Counter;
index.js中并导入这两个文件
import Vue from "vue"
import Vuex from "vuex"
import creatStore from "./store"
import Counter from "./components/counter"
Vue.use(Vuex)
const store = creatStore()
const app = new Vue({
el:"#app",
store,
components:{Counter},
template: '<div class="app"><counter></counter></div> '
})
最后运行看效果,Perfect!!!
四.Getter
对state中的数据派生出一些状态,例如对数据进行过滤。(可以认为是store中的计算属性),会对state中的变量进行过滤再保存,只要state中的变量发生了改变,它也会发生变化,不变化的时候,读的缓存。
<DOCTYPE !>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vuex的Getter</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.0.1/dist/vuex.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const store = new Vuex.Store({
state: {
todos:[
{id:1, name:"读书1小时", done:false},
{id:2, name:"B站看跟山地人学Vuex",done:true },
{id:3, name:"找山地人加微信",done:true },
{id:4, name:"反馈意见和建议",done:true },
{id:5, name:"跑步1小时",done:false },
]
},
getters:{
doneTodos(state){
return state.todos.filter(todo=>todo.done)
},
doneTodosCount(state,getters){
return getters.doneTodos.length
},
getTodoById:(state)=>{
return (id)=>{
return state.todos.find(todo=>todo.id===id)
}
}
},
mutations: {
increment (state) {
state.count++
}
}
})
const Counter = {
template:'<div><div>当前任务{{curTodo.id}}:{{ curTodo.name }}</div> <p> 今日已完成{{ doneTodosCount }}件事 </p> <ul><li v-for="todo in doneTodos" :key="todo.id">{{todo.name}}</li></ul> </div>',
data(){
return {
start:5,
}
},
// 改造前
/*computed:{
doneTodos(){
return this.$store.getters.doneTodos
},
doneTodosCount(){
return this.$store.getters.doneTodosCount
},
curTodo(){
return this.$store.getters.getTodoById(3)
}
},*/
//改造后 mapGetters
computed:{
...Vuex.mapGetters(["doneTodos","doneTodosCount"]),
curTodo(){
return this.$store.getters.getTodoById(3)
}
}
}
const app = new Vue({
el:"#app",
store,
components:{ Counter },
template:'<div class="app"><counter></counter></div>'
})
</script>
</body>
</html>
五.Mutations
Vue
的视图是由数据驱动的,也就是说state
里面的数据是动态变化的,那么怎么改变呢,切记在Vuex
中store
数据改变的唯一方法就是mutation!
通俗的理解mutations
,里面装着一些改变数据方法的集合,这是Veux
设计很重要的一点,就是把处理数据逻辑方法全部放在mutations
里面,使得数据和视图分离。
六.Action
在mutation中我们讲到,mutation中是存放处理数据的方法的集合,我们使用的时候需要commit。但是commit是同步函数,而且只能是同步执行。在actions中提交mutation,并且可以包含任何的异步操作。actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据(但是还是通过mutation来操作,因为只有它能操作)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Action</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.0.1/dist/vuex.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const Counter = {
/*methods:{
addCount(){
this.$store.dispatch("increment",{count:1})
}
},*/
/*methods:Vuex.mapActions({
addCount:"increment"
}),*/
//与下面的 <button @click="increment">ADD</button> 对应
methods:Vuex.mapActions(["increment"]),
template:`
<div>
<h1>{{ $store.state.count }}</h1>
<!--<button @click="addCount">ADD</button>-->
<button @click="increment">ADD</button>
</div>
`
}
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
}
},
actions:{
increment(context) {
context.commit("increment");
},
incrementAsync({commit}){
setInterval(()=>{
commit('increment')
},1000)
}
}
})
const app = new Vue({
el:"#app",
store,
computed:Vuex.mapState(["count"]),
components:{
Counter
},
template:`
<div>
<h1><counter></counter></h1>
</div>
`
})
/*store.dispatch("incrementAsync")*/
</script>
</body>
</html>
七.Module
在Vue中State使用是单一状态树结构,应该的所有的状态都放在state里面,如果项目比较复杂,那state是一个很大的对象,store对象也将对变得非常大,难于管理。module则可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Module</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.1/dist/vuex.js"></script>
</head>
<body>
<script>
const userModule = {
state:{
user:{
name:"calmLyx",
gender:"male",
age:"22",
tel:"15222222222"
},
products:[],
orders:[]
}
}
const homeModule = {
state:{
cities:[
"四川",
"重庆",
"贵州",
"陕西",
"广东"
],
home:{}
}
}
const shopcartModule = {
state:{
count:0,
shopcart:[
{ id:1,name:"机械师T58-V 全面屏游戏本",count:1},
{ id:2,name:"漫步者(EDIFIER) 游戏头戴式耳机",count:2},
{ id:3,name:"魔炼者 1505 机械键盘",count:5},
{ id:4,name:"银雕 无线无声鼠标",count:8},
{ id:5,name:"金士顿 64G 黑色U盘",count:3}
]
},
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
},
}
const store = new Vuex.Store({
modules:{
user:userModule,
home:homeModule,
shopcart:shopcartModule,
},
mutations: {
increment (state) {
state.count++
}
}
})
</script>
</body>
</html>
八.项目结构
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
-
应用层级的状态应该集中到单个 store 对象中。
-
提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
-
异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:官方实例
九.插件
内置 Logger 插件
Vuex 自带一个日志插件用于一般的调试:
import createLogger from 'vuex/dist/logger'
const store = new Vuex.Store({
plugins: [createLogger()]
})
十.严格模式
开启严格模式,仅需在创建 store 的时候传入 strict: true
:
const store = new Vuex.Store({
// ...
strict: true
})
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
开发环境与发布环境
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。
类似于插件,我们可以让构建工具来处理这种情况:
const store = new Vuex.Store({
// ...
strict: process.env.NODE_ENV !== 'production'
})
十一.对表单的处理
当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model
会比较棘手:
<input v-model="obj.message">
假设这里的 obj
是在计算属性中返回的一个属于 Vuex store 的对象,在用户输入时,v-model
会试图直接修改 obj.message
。在严格模式中,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。
<html>
<head>
<title>课15.Vuex的表单处理</title>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.0.1/dist/vuex.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const store = new Vuex.Store({
strict: true,
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
},
updateCount(state,value){
if(value && value!=""){
state.count= parseFloat(value)
}
}
}
})
const app = new Vue({
el:'#app',
store,
computed:{
count:{
get(){
return this.$store.state.count;
},
set(value){
this.$store.commit("updateCount",value)
}
}
},
methods:{
updateMessage(e){
const value = e.target.value
console.log(value)
this.$store.commit("updateCount",value)
}
},
template:`
<div>
<h1>{{ $store.state.count }}</h1>
<div>
<span>v-model</span>
<input v-model="$store.state.count">
</div>
<div>
<span>:value+@input</span>
<input :value="count" @input="updateMessage">
</div>
<div>
<span>v-model Vuex</span>
<input v-model="count">
</div>
</div>
`
})
</script>
</body>
</html>