菜鸟的Vue-Cli 4.x学习总结
菜鸟的Vue-Cli 4.x学习总结说明一、入门系列(1)vue-cli安装(2)快速原型开发(3)创建vue-cli项目(4)动态路由(1)路由配置(2)简单路由案例说明更新时间:2020/7/16 23:12,更新了整体vue-cli安装、快速原型开发、创建vue-cli项目以及动态路由的(1)(2)点本文主要基于vue-cli官网进行学习,同时进行一些额外的补充,本文会持续更新,不断地扩充本
菜鸟的Vue-Cli 4.x学习总结
说明
更新时间: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对应路由的对应组件的name
this.$router.push({ path: "/user", query: { id } }); //相当于user2/20
},
//name+params,对应post请求,其中不传参时,params可以省略
gotoUser3() {
//这里可以做相应的逻辑处理
//注意:这里只能式name和params搭配,name对应路由的对应组件的name
this.$router.push({ name: "User3", params: { id: "11" } }); //相当于user2/11
},
//通过name+params进行传参,对应post请求
gotoUser03(id) {
//这里可以做相应的逻辑处理
alert("后台传入的id值为:" + id);
//注意:这里只能式name和params搭配,name对应路由的对应组件的name
this.$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.name
next()
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 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() {
//数据获取前先loading
this.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 = true
this.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下的state
msg1: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下的state
age: "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
},
//传参形式:参数为id
getHeight:(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下的state
age: "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
},
//传参形式:参数为id
getHeight:(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模块下的函数
更多推荐
所有评论(0)