菜鸟的Vue-Cli 4.x学习总结
- 说明
- 一、入门系列
-
- (1)vue-cli安装
- (2)快速原型开发
- (3)创建vue-cli项目
- (4)Vue的生命周期
- (5)Axios
- 二、路由Vue Router
-
- (1)路由配置
- (2)简单路由
- (3)动态路由
- (4)嵌套路由
- (5)编程式导航
- (6)命名路由
- (7)命名视图
- (8)重定向
- (9)打包部署
- (10)导航守卫
- (11)元信息和过渡动效
- (12)路由数据获取
- 三、Vuex
-
- (1)入门引子
- (2)Store 模式
- (3)Vuex的安装与基本使用
- (4)State状态设置
- (5)Getter
- (6)Mutations
- (7)Actions
- (8)module
说明
更新时间:2020/9/10 11:03,更新了对应的项目名
更新时间:2020/7/19 22:11,更新了Vuex剩下的相关知识
更新时间:2020/7/18 21:26,更新了Vue Router的剩下的内容和部分Vuex的知识,修改了整体排版
更新时间:2020/7/17 22:46,更新了动态路由到导航守卫的相关内容
更新时间:2020/7/16 23:12,更新了整体vue-cli安装、快速原型开发、创建vue-cli项目以及动态路由的(1)(2)点
本文主要基于vue-cli官网进行学习,同时综合了网上的很多相关教程,本文会持续更新,不断地扩充
本文仅为记录学习轨迹,如有侵权,联系删除
注意:本文主要基于vue-cli官网进行学习,地址:https://cli.vuejs.org/zh/guide/prototyping.html
一、入门系列
建议在观看这篇文章前先花几个小时了解一下Vue的基础语法和部分ES6相关的知识,也可以看一下本人的相关博文菜鸟的Vue基础快速入门和菜鸟的ES6与JavaScript学习总结
当然没有Vue的基础和ES6也基本上能看懂这篇博文,里面的代码都有详细贴出来,玩一下就大概上手了
(1)vue-cli安装
在安装之前需要先确保有安装node和npm
输入安装命令,然后静静等待安装完毕即可
npm i @vue/cli -g
安装完输入vue --version
查看安装的版本号
(2)快速原型开发
参考vue-cli官网的快速原型开发,创建第一个项目
新建空文件夹用于项目文件(cli-01),用vscode打开项目
在终端输入命令npm install -g @vue/cli-service-global
创建App.vue,输入测试的模板内容,在终端输入vue serve
运行整个项目
访问结果
注意:运行的命令vue serve
默认启动的App.vue,如果是其他vue组件的话需要在vue serve后面加上要启动的组件,例如:vue serve Hello.vue
具体的使用可以参考官网的介绍。
(3)创建vue-cli项目
-
输入
vue create 项目名称
创建vue项目,选择第二个,手动配置选型,第一个是默认配置选项,回车
-
选项如图所示,可以上下移动,按空格进行选择,带*号的都是已选的配置,为了简单可以先选择如图所示的选项即可,然后回车
选项说明
Babel: 将 ES6 编 译 成 ES5
TypeScript:使 用 TypeScript
Router和Vuex:路由和状态管理
Linter/ Formatter:代码检查工具
CSS Pre-processors:css预编 -
选择ESLint with error prevention only
选项说明
eslint w… : 只进行报错提醒;
eslint + A… : 不严谨模式;
eslint + S…:正常模式;
eslint + P… :严格模式; -
Pick additionall intfeatures:代码检查方式, 选择Lint on save保存时检查
-
选择配置信息存放位置:单独存放或者并入package.json ,选择In dedicated configfiles
-
是否保存当前预设,下次构建无需再次配置
-
直接回车
-
项目创建完成
-
运行
关于创建的项目的目录结构说明和运行流程介绍,可以查看我的这一篇博文:Vue-Cli 4.x目录结构学习总结
(4)Vue的生命周期
对应项目cli-07
其实这个知识点应该记录在我的另一篇Vue基础那一篇博文上,不过在这里记录也可以。
生命周期
生命周期 | 说明 |
---|---|
(初始化)beforeCreate | 整个页面创建之前触发 |
(初始化)created | 实例创建完之后触发 |
(初始化)beforeMount | 开始挂载之前触发 |
(初始化)mounted | 挂载成功之后触发 |
(运行中)beforeUpdate | 组件数据更新前触发 |
(运行中)updated | 组件数据更新后触发 |
(销毁)beforeDestroy | 组件销毁前触发 |
(销毁)destroy | 组件销毁后触发 |
直接上例子说明
随便新建组件(Test01.vue),并且在方法里面将上面的生命周期相关的方法全写上
<template><div><div>{{data}}</div><button @click="change">点击更新组件数据</button></div>
</template><script>
export default {methods: {change(){this.data = "修改后的测试数据,"}},data() {return {data: "测试数据"};},//整个页面创建之前触发beforeCreate() {console.log("beforeCreate");},//实例创建完之后触发created() {console.log("created");},//开始挂载之前触发beforeMount() {console.log("beforeMount");},//挂载成功之后触发mounted() {console.log("mounted");},//组件数据更新前触发beforeUpdate() {console.log("beforeUpdate");},//组件数据更新后触发updated() {console.log("updated");},//组件销毁前触发beforeDestroy() {console.log("beforeDestroy");},//组件销毁后触发destroyed() {console.log("destroyed");}
};
</script><style>
</style>
运行
如上图,第一次加载Test01页面,按照顺序触发4个生命周期函数,用于初始化数据
如上图,点击按钮后,组件数据进行更新,触发运行中的两个生命周期函数
如上图,点击About跳转页面,触发销毁的两个生命周期函数
(5)Axios
对应项目cli-08
参考网址:http://www.axios-js.com/zh-cn/docs/index.html
Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post等请求,当然这也可以用jq来实现这些请求,实际上,Axios跟jq也挺类似的。
下面直接上案例
安装
npm install --save axios vue-axios
配置,将下面这些代码加入主入口文件
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'Vue.use(VueAxios, axios)
新建Test01.vue
<template><div><table border="1" style="margin:0 auto"><tr><th>id</th><th>名字</th><th>性别</th><th>出生年月</th></tr><tr><td>{{user.id}}</td><td>{{user.name}}</td><td>{{user.age}}</td><td>{{user.bir}}</td></tr></table><button @click="addUser">添加用户</button><p>{{msg}}</p></div>
</template><script>
export default {data() {return {user: {id: null,name: null,age: null,bir: null},msg: null};},methods: {//发送post请求addUser() {this.$http.post("http://rap2.taobao.org:38080/app/mock/253047/user/add", {name: "张三",age: 10,bir: "2012-12-12"}).then(resp => {this.msg = resp.data;}).catch(error => {alert(error);});},//发送get请求findUser() {this.$http.get("http://rap2.taobao.org:38080/app/mock/253047/user/findOne?id=3").then(resp => {this.user = resp.data;}).catch(error => {alert(error);});}},created() {this.findUser();}
};
</script><style>
</style>
运行结果
二、路由Vue Router
注意:Vue Router主要根据Vue Router官网进行相应的学习,官网地址:https://router.vuejs.org/zh/guide/advanced/meta.html
上面创建的项目是没有配置路由的,这里简单说一下,页面的路由需要配置路由插件,下面创建新的项目cli-03,进行路由的配置
(1)路由配置
项目的创建基本和上面的创建流程一样,唯一不同的是在手动配置的时候,把路由的配置也选上,如下图所示
选上Router路由配置,其他的创建方式不变,创建成功后可以在项目的根目录下的package.json里面查看项目的配置信息,如下图所示,路由已经配置
关于创建的项目的目录结构说明和运行流程介绍,可以查看我的这一篇博文:Vue-Cli 4.x目录结构学习总结,建议先看一下,如果连目录结构都不知道的话,就更别说使用该项目了。
(2)简单路由
对应项目cli-03
需求:通过多个自定义组件,组成一个新的组件,并且添加到路由里面
components包存放公共组件
Head.vue代码如下
<template><div>头部信息:{{data}}</div>
</template><script>
export default {name:"Head",props:{data:String}}
</script><style></style>
Content.vue代码如下
<template><ul class="list"><li v-for="item in link" :key="item"><a href="#">{{item}}</a></li></ul>
</template>
<script>
export default {name: "Content",data() {return { link: ["首页", "资讯", "图文", "关于"] };}
};
</script>
<style scoped>
.list a {color: brown;
}
</style>
Foot.vue代码如下
<template><div>版权所有</div>
</template>
<script>
export default {name: "Foot"
};
</script>
<style scoped>
</style>
view包存放页面组件
Test.vue代码如下
<template>
<div><Head data="Hello"></Head><!--使用子组件Head,并且向Head传值--><Content></Content><!--使用子组件Content--><Foot></Foot><!--使用子组件Foot值-->
</div>
</template><script>
// 引入子组件(公共组件)
import Head from '../components/Head'
import Content from '../components/Content'
import Foot from '../components/Foot'export default {name:"Test",components:{//组成引入的公共组件Head,Content,Foot}
};
</script><style>
</style>
在Rount包下注册路由
在App.vue根节点添加导航
注意:< router-view/>表示路由的添加,不加上去的话将不会有路由的页面跳转。
运行结果
通过这个例子可以大概体会到vue的组件开发的模式,将功能写成一个个的组件,需要的,通过组件的方式进行开发,即所谓的组件式开发,上面的例子涉及到知识点有,组件的创建,组件的赋值,组件间的传值等。
(3)动态路由
对应项目cli-03
所谓动态路由匹配:就是将不确定的参数进行路由映射到同一个组件上去;比如经典的:user?id=5,这个 5 就是动态的数值,最终路径:user/5,可以看一下官网给出的例子,链接地址:https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#响应路由参数的变化
动态路由案例
这里给出动态路由的两种方法,$ route.query和$ route.params,更多的方法可以查看router的api,地址:router的api
$ route.query
在views包下新建User.vue,代码如下
<template><div>url传递的用户参数id = {{$route.query.id}} </div></template><script>
export default {name:"User"}
</script><style></style>
添加路由
运行结果
$ route.params
在views包下新建User2.vue,代码如下
<template><div>url传递的用户id = {{$route.params.id}}</div>
</template><script>
export default {name:"User2"}
</script><style></style>
添加路由
运行结果
路由对象属性除了上面的两个之外还有$ route.hash等,具体可以查看官网
通配符*
"*"可以匹配所有的路径
如是上图所示,输入任意路径都会跳转到About页面
(4)嵌套路由
对应项目cli-03
实际应用场景中,可能存在多种嵌套组件的应用界面,类似栏目分类; 比如:新闻板块下有国内新闻、国外新闻、体育新闻、音乐新闻等; 音乐板块下有流行音乐、古典音乐等;下面是嵌套路由的匹配方式,第一种固定路由,二三两种动态路由;
模式 | 匹配路径 |
---|---|
/ music / : id/ popMusic | / music / 5 /popMucsic (注意,这里假设id为5) |
/ music / : id / classicalMusic | / music / 5 / classicalMusic (注意,这里假设id为5c) |
也可以查看官网的说明
嵌套路由案例
在views包下新建一个Music文件夹,用于存放音乐板块,里面新建Music.vue、PopMusic.vue和ClassicalMusic.vue三个组件
PopMusic.vue代码如下
<template><div>这是流行音乐</div>
</template><script>
export default {
name:"PopMusic"
}
</script><style></style>
ClassicalMusic.vue代码如下
<template><div>这是古典音乐</div>
</template><script>
export default {name: "ClassicalMusic"
};
</script><style>
</style>
Music.vue代码如下
<template><div><h1>音乐模块</h1><br><router-link to="/music/5/popMusic">流行音乐</router-link> |<router-link to="/music/5/classicalMusic">古典音乐</router-link><router-view/></div>
</template><script>
export default {name:"Music"}
</script><style></style>
注册路由
运行结果
注意:注册路由时,子路由需要通过children来进行注册。
(5)编程式导航
对应项目cli-03
< router-link>组件标签可以直接进行导航,但缺乏编程性,无法各种逻辑判断;插件提供了编程式的导航,让我们自行定义我们的导航的方法; 比如,我们自行创建一个按钮,通过执行函数的方式,去导航,可编程性就提高了
可以看一下官方给出的解释
编程式导航案例
使用router.push方法,主要有如下两种方式,分别对应get和post
//对应get请求,其中不传参时,query可以省略
router.push({ path: '/register', query: { plan: 'private' }})//对应post请求,其中不传参时,params可以省略
router.push({ name: 'User', params: { userId: '123' }})
新建Navigation.vue组件,代码如下
<template><div><!--通过path+query的方式:get --><span>通过path+query的方式(get):</span><button @click="gotoAbout()">跳转到About页面</button><button @click="gotoUser(30)">跳转到User页面(传参:30)</button><br /><br /><!--通过name+params的方式:post --><span>通过name+params的方式(psot):</span><button @click="gotoUser3()">跳转到User3页面</button><button @click="gotoUser03(20)">跳转到User3页面(传参:20)</button></div>
</template><script>
export default {name: "Navigation",methods: {//path+query,对应get请求,其中不传参时,query可以省略gotoAbout() {//这里可以做相应的逻辑处理this.$router.push({ path: "/about" });},//通过path+query进行传参,对应get请求gotoUser(id) {//这里可以做相应的逻辑处理alert("后台传入的id值为:" + id);//注意:这里只能式name和params搭配,name对应路由的对应组件的namethis.$router.push({ path: "/user", query: { id } }); //相当于user2/20},//name+params,对应post请求,其中不传参时,params可以省略gotoUser3() {//这里可以做相应的逻辑处理//注意:这里只能式name和params搭配,name对应路由的对应组件的namethis.$router.push({ name: "User3", params: { id: "11" } }); //相当于user2/11},//通过name+params进行传参,对应post请求gotoUser03(id) {//这里可以做相应的逻辑处理alert("后台传入的id值为:" + id);//注意:这里只能式name和params搭配,name对应路由的对应组件的namethis.$router.push({ name: "User3", params: { id } }); //相当于user2/20}}
};
</script><style>
</style>
运行结果
为什么说一个对应get,一个对应post呢,先点击get的两个按钮
从上图可以看到这种传参的方式是直接在url后面拼接字符串实现的传参,跟get对应。最后点击post对应的按钮
从上图可以看到这种传参的方式url后面没有拼接字符串,参数也跟着传过去了,跟post对应。
其余方法
编程式导航还有其他方法,像router.replace(location, onComplete?, onAbort?)和this.$router.go()等,具体可以查看官网,地址:https://router.vuejs.org/zh/guide/essentials/navigation.html
(6)命名路由
对应项目cli-03
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。
这个相对简单,直接上代码
<router-link v-bind:to="{ name: 'User3', params: { id: '11' } }">User3</router-link> |<router-link v-bind:to=" { path: '/user', query: { id:'21' } }">User</router-link>
这个跟上面的编程式导航的功能是一样的。
(7)命名视图
对应项目cli-03
是视图标签,组件通过这个标签来渲染组件的内容;上面的所有内容都通过这个标签加载组件的模版内容; 但如果,我们在不同的区域做了不同的设计,不同的组件需要在不同的区域渲染;这个时候,就需要通过命名视图来指定渲染的组件了;
来看一个例子
这种情况下,对应的路径下的组件被渲染了3次
命名路由案例
需求:加载about的时候,还需要另外加载 Head.vue 组件和 Footer.vue 组件
在跟组件修改几行代码,有3个路由视图,如图
在about路由注册那里增加点东西
其中Head.vue和Foot.vue是自定义组件
当然更多的玩法可以查看官网。
(8)重定向
先看一下官方的说明
这里重新开一个项目cli-04进行演示,重定向的存操作跟java还是挺像的
运行结果
运行结果
查看控制台还可以查看代码中的to是什么
(9)打包部署
首先明确两种路由的模式history模式和hash模式,先来看history模式,这是创建项目时默认的模式
history模式
在该模式下执行打包命令:npm run build
,会自动生成一个dist文件,里面是打包好的文件
打包后,我们想要在静态服务器上测试,先要安装静态服务器
npm i serve -g //安装 serve dist -s //运行dist 目录,注意-s不能省略,不然后面会出现404错误
访问链接,各种功能都正常
hash模式
接下来的命令都差不多的,唯一不同的就是运行dist目录时不需要-s
npm run build//大包npm i serve -g //安装 serve dist //运行dist 目录,注意这里不需要-s
运行结果,一切正常
(10)导航守卫
导航守卫的作用就是跳转或取消跳转的导航功能,比如登录跳转方案,导航守卫还包括前置导航守卫和后置导航守卫等,直接上官网瞄一眼
前置导航守卫
按照官方的说法,在路由下新建一个前置导航守卫
代码
//模拟登录状态
const flag = true//全局前置导航守卫
router.beforeEach((to,from,next)=>{//to: Route: 即将要进入的目标 路由对象//from: Route: 当前导航正要离开的路由//.next:这是个钩子函数console.log('开始loading...')if(flag){//已经登录// 如果是已经登录状态,再访问登录页面直接跳转到首页if(to.name === 'Login'){next('/')//跳转到首页}else{next()//直接放行}}else{//未登录//next('/login')//如果直接这样写会出现无限递归错误if(to.name === 'Login'){//如果是已经跳转到登录页面的话直击放行next()}else{//如果跳转页面不是登录页面则继续跳转登录页面next('/login')}}
})
运行flag为true,已登录状态,一切正常访问,访问login路径时自动跳转到首页
运行flag为false,未登录状态,一切正常访问,不管输入什么路径都会跳转到登录页面
守卫的参数参考官网给出的说明
后置导航守卫
代码
//全局后置导航守卫
router.afterEach(()=>{console.log("关闭loading...")
})
也可以传参进去,进行相应的操作,参数跟前置导航守卫的参数一致
路由独享的守卫
你可以在路由配置上直接定义 beforeEnter 守卫:
const router = new VueRouter({routes: [{path: '/foo',component: Foo,beforeEnter: (to, from, next) => {// ...}}]
})
组件内的守卫
const Foo = {template: `...`,beforeRouteEnter (to, from, next) {// 在渲染该组件的对应路由被 confirm 前调用// 不!能!获取组件实例 `this`// 因为当守卫执行前,组件实例还没被创建},beforeRouteUpdate (to, from, next) {// 在当前路由改变,但是该组件被复用时调用// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。// 可以访问组件实例 `this`},beforeRouteLeave (to, from, next) {// 导航离开该组件的对应路由时调用// 可以访问组件实例 `this`}
}
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {next(vm => {// 通过 `vm` 访问组件实例})
}
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
beforeRouteUpdate (to, from, next) {// just use `this`this.name = to.params.namenext()
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
beforeRouteLeave (to, from, next) {const answer = window.confirm('Do you really want to leave? you have unsaved changes!')if (answer) {next()} else {next(false)}
}
(11)元信息和过渡动效
元信息
可以给路由配置一个 meta 属性,这个属性一般设置对象即可;这个属性是固定的,设置别的属性比如:abc是无法获得内容的;
运行结果
过渡动效
过渡效果,字面意思就是基础 Vue 课程中的转场特效,支持路由切换;
将要再如的视图组件标签使用< transition>包裹住实现特效,标签以css样式的首个字符串命名
<transition name="fade"> <router-view/> </transition>
拷贝转场动效的 CSS3
.fade-enter-active,
.fade-leave-active {transition: opacity 5s;
}
.fade-enter,
.fade-leave-to {opacity: 0;
}
.fade-leave-to {display: none;
}
这样在路由跳转时会有一个转场的特效,这里是缓慢转场(大概是这个意思)
(12)路由数据获取
获取数据,可以有两种方式:导航完成后获取和导航完成前获取;
导航完成后获取
<template><div class="post"><div v-if="loading">Loading...</div><div v-if="post"><h2>{{post.title}}</h2><p>{{post.body}}</p></div></div>
</template>
<script>
export default {name: "Test02",data() {return {loading: false,post: null};},//当组件创建完毕后执行created() {this.getData();},//方法methods: {getData() {//数据获取前先loadingthis.loading = true;//模拟获取的延迟过程setTimeout(() => {this.loading = false;this.post = { title: "标题", body: "内容..." };}, 2000);}}
};
</script>
导航完成前获取
<template><div class="post"><div v-if="post"><h2>{{post.title}}</h2><p>{{post.body}}</p></div><div v-if="flag"><h1>{{text}}</h1></div></div>
</template><script>export default {name: "Test03",data() {return {post : null,flag:false,text:''}},//导航完成之前beforeRouteEnter(to, from, next) {setTimeout(() => {next(vm => {vm.getData()})}, 2000);},methods: {//获取数据getData() {this.post = {title : '标题',body : '内容...'}this.flag = truethis.text='文本'}}}
</script><style scoped></style>
三、Vuex
注意:这一部分的关于Vuex主要根据Vuex官网进行学习,官网地址:https://vuex.vuejs.org/zh/
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
Vuex 主要功能是用于解决组件与组件之间共享的状态,集中存储管理所有组件状态
(1)入门引子
对应项目cli-06
来看一个计数的例子,也是官网给出的例子
<template><div><button @click="Add">按钮的次数:{{msg}}</button></div>
</template><script>
export default {name:'Count',data() {return {msg:0}},methods: {Add(){this.msg++}},}
</script><style></style>
运行结果
貌似没啥问题,但是一旦点击其他页面后再跳回计数页面,发现按钮的次数就又变为了0,有没有办法让按钮的次数不变呢,这就要用的Vuex了
(2)Store 模式
对应项目cli-06
store 模式支持极少的共享数据,按照官网给出的说法,如果不是大型的单页面应用,使用 Vuex 会繁琐冗余; 也就是说,你的应用特别简单,不需要真么重的插件,可以使用 store 模式; store 模式支持你在极少的共享数据中,就好比你只近视 50 度,可以不戴眼镜
在src目录下新建store/index.js文件用于存储共享数据
修改上一节的Count.vue文件,通过将store引入并且使用里面index.js的共享数据的方式进行数据的操作
<template><div><button @click="Add1">(not store)按钮的次数:{{msg1}}</button><button @click="Add2">(store)按钮的次数:{{storeState.msg}}</button></div>
</template><script>
//引入store
import store from "@/store"export default {name: "Count",data() {return {storeState: store.state,//使用store下的statemsg1:0};},methods: {Add1() {this.msg1++},Add2() {store.Add();//调用store下的Add()方法}}
};
</script><style>
</style>
运行结果,同样增加到10次,点击Home页面后再跳回Count页面发现使用了store的按钮次数还是10,没有使用store的则直接被清0了
(3)Vuex的安装与基本使用
对应项目cli-06
上面的Store的方式是通过自己手动新建一个store/index.js的方式进行数据的共享,这种方式适合少量数据的共享,如果大量数据的话需要安装Vuex,使用配置好的Vuex
安装
安装的方式也简单,只需要在新建项目的时候,勾上Vuex的选项即可
安装完之后发现,在src下多了一个store文件夹,里面有一个index.js文件,里面有配置好一些数据
简单认识一下里面的配置
使用
还是以上面的计数的例子,在store下的index.js增加状态值和修改状态值的方法
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({//状态值state: {msg: 0},//修改状态值mutations: {Add(state) {state.msg++}},actions: {},modules: {}
})
新建Count.vue组件,添加相应的路由
<template><div><button @click="Add1">(not store)按钮的次数:{{msg1}}</button><!--通过$store.state.msg调用里面的msg数据--><button @click="Add2">(store)按钮的次数::{{$store.state.msg}}</button></div>
</template><script>
export default {name: "Count",data() {return {msg1:0};},methods: {Add1() {this.msg1++},Add2() {//注意::$store 是插件挂载在 Vue 实例上的,在实例内访问用 this.$store,否则会出错this.$store.commit('Add')//修改状态执行mutation 里的下的Add()方法}}
};
</script><style>
</style>
运行的效果跟上面用store模式实现的一样
(4)State状态设置
对应项目cli-06
这个模块主要解决设置多个状态在赋值取值繁琐的问题。
传统的State多状态设置
在store/index.js里面设置name,age等多个状态值
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({//状态值state: {//设置多个状态值msg: 0,name: '灰太狼',age: 3,sex: '雄',place: '狼堡'},//修改状态值mutations: {Add(state) {state.msg++},SetPlace(state, value) {state.place = value}},actions: {},modules: {}
})
创建User01.vue组件,代码如下
<template><div><!--除了用$store.state.属性的方式获取外,还可以用下面的计算属性的方式获取--><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>性别:{{sex}}</p><p>住址:{{place}}</p><input type="text" v-model="place" /></div>
</template><script>
export default {name: "User01",//计算属性computed: {name() {return this.$store.state.name;},age() {return this.$store.state.age;},sex() {return this.$store.state.sex;},place: {//get方法用于获取值get() {return this.$store.state.place;},//set用于修改值set(value) {this.$store.commit("SetPlace", value);}}}
};
</script><style>
</style>
运行结果,信息都正确显示,住址跟着输入框改变而改变
这种传统的获取和修改state状态值的方式在状态值多的时候就会显得代码很冗余,重复代码量会增多,于是又了下面的简洁版的写法
辅助函数写法
实现同样的功能,这种方式显得更简洁
新建一个User02.vue组件库,store里面的index.js的写法不变,主要区别就是组件里面状态值的获取方式和修改方式
<template><div><!--除了用$store.state.属性的方式获取外,还可以用下面的计算属性的方式获取--><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>性别:{{sex}}</p><p>住址:{{place}}</p><input type="text" @input="SetPlace" :value="place" /><p>测试:{{test}}</p></div>
</template><script>
//导入辅助函数
import { mapState } from "vuex";export default {name: "User02",computed: {//其余计算属性操作test() {return "这是一个测试";},//辅助函数操作store.state...mapState({name: "name", //等于name:state=>state.name,state表示store下的stateage: "age",sex: "sex",place(state) {return state.place;}})},methods: {SetPlace(e) {this.$store.commit("SetPlace", e.target.value);}}
};
</script><style>
</style>
运行结果,这种方式也可以实现上面的功能,而且写法更简洁
(5)Getter
在获取 state 状态时,有时我们需要对这个原生的值进行处理;处理完之后再从store中返回给组件使用, 处理采用计算属性或辅助 mapState 均可,但每个组件都要进行处理就繁琐了 ;我们可以使用 getters 派生来设置需要处理的 state 状态
先在store里面新增两个状态值,爱好和身高,用来做不传参和传参的使用,同时新建getters
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({//状态值state: {//设置多个状态值msg: 0,name: '灰太狼',age: 3,sex: '雄',place: '狼堡',hobby: '抓羊',height:'3米'},getters: {//无传参形式getHobby:(state)=>{//可以进行对原值的逻辑操作return '灰太狼:'+state.hobby},//传参形式:参数为idgetHeight:(state)=>(id)=>{//可以进行对原值的逻辑操作return state.height+id}},//修改状态值mutations: {Add(state) {state.msg++},SetPlace(state, value) {state.place = value}},actions: {},modules: {}
})
在原来的User02.vue组件上进行操作
<template><div><!--除了用$store.state.属性的方式获取外,还可以用下面的计算属性的方式获取--><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>性别:{{sex}}</p><p>住址:{{place}}</p><!--Getter--><p>爱好:{{hobby}}</p><p>身高:{{getHeight('--巨人')}}</p><input type="text" @input="SetPlace" :value="place" /><p>测试:{{test}}</p></div>
</template><script>
//导入辅助函数和mapGetters辅助函数
import { mapState, mapGetters } from "vuex";export default {name: "User02",computed: {//其余计算属性操作test() {return "这是一个测试";},//mapGetters辅助函数...mapGetters({hobby: 'getHobby',getHeight:'getHeight'}),//辅助函数操作store.state...mapState({name: "name", //等于name:state=>state.name,state表示store下的stateage: "age",sex: "sex",place(state) {return state.place;}})},methods: {SetPlace(e) {this.$store.commit("SetPlace", e.target.value);}}
};
</script><style>
</style>
运行结果
(6)Mutations
关于Mutations的使用在上面计数的那个例子里面已经使用过了,下面用辅助函数的方式进行操作
还是以计数为例,在store/index.js中增加状态值msg2,同时在Mutations中增加AddMsg2方法
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({//状态值state: {//设置多个状态值msg2:0,msg: 0,name: '灰太狼',age: 3,sex: '雄',place: '狼堡',hobby: '抓羊',height:'3米'},getters: {//无传参形式getHobby:(state)=>{//可以进行对原值的逻辑操作return '灰太狼:'+state.hobby},//传参形式:参数为idgetHeight:(state)=>(id)=>{//可以进行对原值的逻辑操作return state.height+id}},//修改状态值mutations: {Add(state) {state.msg++},SetPlace(state, value) {state.place = value},AddMsg2(state){state.msg2++}},actions: {},modules: {}
})
在Count.vue中增加按钮,同时导入mapMutations辅助函数
<template><div><button @click="Add1">(not store)按钮的次数:{{msg1}}</button><!--通过$store.state.msg调用里面的msg数据--><button @click="Add2">(store)按钮的次数:{{$store.state.msg}}</button><button @click="addMsg2">(store 辅助函数)按钮的次数:{{$store.state.msg2}}</button></div>
</template><script>
//导入辅助函数
import {mapMutations} from 'vuex'export default {name: "Count",data() {return {msg1:0};},methods: {...mapMutations({addMsg2:'AddMsg2'}),Add1() {this.msg1++},Add2() {//注意::$store 是插件挂载在 Vue 实例上的,在实例内访问用 this.$store,否则会出错this.$store.commit('Add')//修改状态执行mutation 里的下的Add()方法}}
};
</script><style>
</style>
运行结果
(7)Actions
上节课我们说 Mutations 只能是同步函数,那异步操作可以交给Actions,直接上例子
在store/index.js中的mutations里面写要异步执行的代码,然后在Actions异步调用,为了模拟异步效果,可以在state里面设置一个状态值info
新建一个User03.vue组件异步调用信息
<template>
<div>
<h1>{{$store.state.info}}</h1>
<button @click="setInfo('异步:hello')">点击执行异步方法</button>
</div>
</template><script>
//导入辅助函数
import { mapActions } from 'vuex'export default {
methods: {//传统使用异步方法// setInfo(){// this.$store.dispatch('changeInfo')// },//采用辅助函数的方式使用异步方法...mapActions({setInfo:"changeInfo"})},
}
</script><style></style>
运行结果
(8)module
当项目越来越大的时候,如果所有的共享状态都存放在一个index.js文件里的时候,整文件就会显得很臃肿,于是就可以用模块化的方式来进行共享状态,直接上例子
在store文件下新建一个module文件,用来存放各种模块,再新建一个module01.js
export default{namespaced: true,//为了隔离主状态和模块状态名称冲突,需要设置一个命名空间//状态值state: {info: 'modules下的初始值'},//修改状态值mutations: {//等待被异步调用的方法setInfo(state, value) {state.info = value}},modules: {}
}
然后将该模块导入store/index.js里面
引入后就可以直接使用了,新建User04.vue组件
<template>
<div>
<h1>info = {{$store.state.module01.info}}</h1>
<button @click="getStore">获取$store.state</button>
</div>
</template><script>
export default {
methods: {getStore(){console.log(this.$store.state)}
},
}
</script><style></style>
运行的时候打开控制台,查看$store.state变量
下面演示调用模块的函数
修改module01.js的代码
export default{namespaced: true,//为了隔离主状态和模块状态名称冲突,需要设置一个命名空间//状态值state: {info: 'module01下的初始值'},//修改状态值mutations: {setInfo(state, value) {console.log("这是模块module01.js")state.info = value}},modules: {}
}
修改User04.vue演示函数调用
<template>
<div>
<h1>info = {{$store.state.module01.info}}</h1>
<button @click="getStore">获取$store.state</button>
<button @click="setInfo('你好')">修改info</button>
</div>
</template><script>
import {mapMutations} from "vuex"export default {
methods: {getStore(){console.log(this.$store.state)},...mapMutations({setInfo:'module01/setInfo'})
},
}
</script><style></style>
运行结果,可以看见调用的确实module01模块下的函数