宝塔服务器面板,一键全能部署及管理,送你10850元礼包,点我领取

菜鸟的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
菜鸟的Vue-Cli 4.x学习总结-风君子博客
输入安装命令,然后静静等待安装完毕即可

npm i @vue/cli -g

安装完输入vue --version查看安装的版本号
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(2)快速原型开发

参考vue-cli官网的快速原型开发,创建第一个项目
菜鸟的Vue-Cli 4.x学习总结-风君子博客

新建空文件夹用于项目文件(cli-01),用vscode打开项目
菜鸟的Vue-Cli 4.x学习总结-风君子博客
在终端输入命令npm install -g @vue/cli-service-global
菜鸟的Vue-Cli 4.x学习总结-风君子博客
创建App.vue,输入测试的模板内容,在终端输入vue serve运行整个项目
菜鸟的Vue-Cli 4.x学习总结-风君子博客
访问结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
注意:运行的命令vue serve默认启动的App.vue,如果是其他vue组件的话需要在vue serve后面加上要启动的组件,例如:vue serve Hello.vue
菜鸟的Vue-Cli 4.x学习总结-风君子博客

具体的使用可以参考官网的介绍。

(3)创建vue-cli项目

  • 输入vue create 项目名称创建vue项目,选择第二个,手动配置选型,第一个是默认配置选项,回车
    菜鸟的Vue-Cli 4.x学习总结-风君子博客

  • 选项如图所示,可以上下移动,按空格进行选择,带*号的都是已选的配置,为了简单可以先选择如图所示的选项即可,然后回车
    菜鸟的Vue-Cli 4.x学习总结-风君子博客
    选项说明
    Babel: 将 ES6 编 译 成 ES5
    TypeScript:使 用 TypeScript
    Router和Vuex:路由和状态管理
    Linter/ Formatter:代码检查工具
    CSS Pre-processors:css预编

  • 选择ESLint with error prevention only菜鸟的Vue-Cli 4.x学习总结-风君子博客
    选项说明
    eslint w… : 只进行报错提醒;
    eslint + A… : 不严谨模式;
    eslint + S…:正常模式;
    eslint + P… :严格模式;

  • Pick additionall intfeatures:代码检查方式, 选择Lint on save保存时检查
    菜鸟的Vue-Cli 4.x学习总结-风君子博客

  • 选择配置信息存放位置:单独存放或者并入package.json ,选择In dedicated configfiles
    菜鸟的Vue-Cli 4.x学习总结-风君子博客

  • 是否保存当前预设,下次构建无需再次配置
    菜鸟的Vue-Cli 4.x学习总结-风君子博客

  • 直接回车
    菜鸟的Vue-Cli 4.x学习总结-风君子博客

  • 项目创建完成
    菜鸟的Vue-Cli 4.x学习总结-风君子博客

  • 运行
    菜鸟的Vue-Cli 4.x学习总结-风君子博客
    菜鸟的Vue-Cli 4.x学习总结-风君子博客
    菜鸟的Vue-Cli 4.x学习总结-风君子博客

关于创建的项目的目录结构说明和运行流程介绍,可以查看我的这一篇博文: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>

运行
菜鸟的Vue-Cli 4.x学习总结-风君子博客
如上图,第一次加载Test01页面,按照顺序触发4个生命周期函数,用于初始化数据
菜鸟的Vue-Cli 4.x学习总结-风君子博客
如上图,点击按钮后,组件数据进行更新,触发运行中的两个生命周期函数
菜鸟的Vue-Cli 4.x学习总结-风君子博客
如上图,点击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)

菜鸟的Vue-Cli 4.x学习总结-风君子博客
新建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-Cli 4.x学习总结-风君子博客

二、路由Vue Router

注意:Vue Router主要根据Vue Router官网进行相应的学习,官网地址:https://router.vuejs.org/zh/guide/advanced/meta.html
上面创建的项目是没有配置路由的,这里简单说一下,页面的路由需要配置路由插件,下面创建新的项目cli-03,进行路由的配置

(1)路由配置

项目的创建基本和上面的创建流程一样,唯一不同的是在手动配置的时候,把路由的配置也选上,如下图所示
菜鸟的Vue-Cli 4.x学习总结-风君子博客
选上Router路由配置,其他的创建方式不变,创建成功后可以在项目的根目录下的package.json里面查看项目的配置信息,如下图所示,路由已经配置
菜鸟的Vue-Cli 4.x学习总结-风君子博客
关于创建的项目的目录结构说明和运行流程介绍,可以查看我的这一篇博文:Vue-Cli 4.x目录结构学习总结,建议先看一下,如果连目录结构都不知道的话,就更别说使用该项目了。

(2)简单路由

对应项目cli-03
需求:通过多个自定义组件,组成一个新的组件,并且添加到路由里面

components包存放公共组件
菜鸟的Vue-Cli 4.x学习总结-风君子博客
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包存放页面组件
菜鸟的Vue-Cli 4.x学习总结-风君子博客
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包下注册路由
菜鸟的Vue-Cli 4.x学习总结-风君子博客

在App.vue根节点添加导航
菜鸟的Vue-Cli 4.x学习总结-风君子博客
注意:< router-view/>表示路由的添加,不加上去的话将不会有路由的页面跳转。

运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
通过这个例子可以大概体会到vue的组件开发的模式,将功能写成一个个的组件,需要的,通过组件的方式进行开发,即所谓的组件式开发,上面的例子涉及到知识点有,组件的创建,组件的赋值,组件间的传值等。

(3)动态路由

对应项目cli-03
所谓动态路由匹配:就是将不确定的参数进行路由映射到同一个组件上去;比如经典的:user?id=5,这个 5 就是动态的数值,最终路径:user/5,可以看一下官网给出的例子,链接地址:https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#响应路由参数的变化
菜鸟的Vue-Cli 4.x学习总结-风君子博客
动态路由案例
这里给出动态路由的两种方法,$ route.query和$ route.params,更多的方法可以查看router的api,地址:router的api
菜鸟的Vue-Cli 4.x学习总结-风君子博客
$ route.query
在views包下新建User.vue,代码如下

<template><div>url传递的用户参数id = {{$route.query.id}}  </div></template><script>
export default {name:"User"}
</script><style></style>

添加路由
菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客

$ route.params
在views包下新建User2.vue,代码如下

<template><div>url传递的用户id = {{$route.params.id}}</div>
</template><script>
export default {name:"User2"}
</script><style></style>

添加路由
菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
路由对象属性除了上面的两个之外还有$ route.hash等,具体可以查看官网

通配符*
"*"可以匹配所有的路径
菜鸟的Vue-Cli 4.x学习总结-风君子博客
如是上图所示,输入任意路径都会跳转到About页面
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(4)嵌套路由

对应项目cli-03
实际应用场景中,可能存在多种嵌套组件的应用界面,类似栏目分类; 比如:新闻板块下有国内新闻、国外新闻、体育新闻、音乐新闻等; 音乐板块下有流行音乐、古典音乐等;下面是嵌套路由的匹配方式,第一种固定路由,二三两种动态路由;

模式 匹配路径
/ music / : id/ popMusic / music / 5 /popMucsic (注意,这里假设id为5)
/ music / : id / classicalMusic / music / 5 / classicalMusic (注意,这里假设id为5c)

也可以查看官网的说明
菜鸟的Vue-Cli 4.x学习总结-风君子博客
嵌套路由案例

在views包下新建一个Music文件夹,用于存放音乐板块,里面新建Music.vue、PopMusic.vue和ClassicalMusic.vue三个组件
菜鸟的Vue-Cli 4.x学习总结-风君子博客
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>

注册路由
菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
注意:注册路由时,子路由需要通过children来进行注册。

(5)编程式导航

对应项目cli-03
< router-link>组件标签可以直接进行导航,但缺乏编程性,无法各种逻辑判断;插件提供了编程式的导航,让我们自行定义我们的导航的方法; 比如,我们自行创建一个按钮,通过执行函数的方式,去导航,可编程性就提高了

可以看一下官方给出的解释
菜鸟的Vue-Cli 4.x学习总结-风君子博客
编程式导航案例
使用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>

运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
为什么说一个对应get,一个对应post呢,先点击get的两个按钮
菜鸟的Vue-Cli 4.x学习总结-风君子博客
从上图可以看到这种传参的方式是直接在url后面拼接字符串实现的传参,跟get对应。最后点击post对应的按钮
菜鸟的Vue-Cli 4.x学习总结-风君子博客
从上图可以看到这种传参的方式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
是视图标签,组件通过这个标签来渲染组件的内容;上面的所有内容都通过这个标签加载组件的模版内容; 但如果,我们在不同的区域做了不同的设计,不同的组件需要在不同的区域渲染;这个时候,就需要通过命名视图来指定渲染的组件了;

来看一个例子
菜鸟的Vue-Cli 4.x学习总结-风君子博客
这种情况下,对应的路径下的组件被渲染了3次
菜鸟的Vue-Cli 4.x学习总结-风君子博客
菜鸟的Vue-Cli 4.x学习总结-风君子博客
命名路由案例
需求:加载about的时候,还需要另外加载 Head.vue 组件和 Footer.vue 组件

在跟组件修改几行代码,有3个路由视图,如图
菜鸟的Vue-Cli 4.x学习总结-风君子博客

在about路由注册那里增加点东西
菜鸟的Vue-Cli 4.x学习总结-风君子博客
其中Head.vue和Foot.vue是自定义组件
菜鸟的Vue-Cli 4.x学习总结-风君子博客

当然更多的玩法可以查看官网。

(8)重定向

先看一下官方的说明
菜鸟的Vue-Cli 4.x学习总结-风君子博客
这里重新开一个项目cli-04进行演示,重定向的存操作跟java还是挺像的
菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
查看控制台还可以查看代码中的to是什么
菜鸟的Vue-Cli 4.x学习总结-风君子博客
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(9)打包部署

首先明确两种路由的模式history模式和hash模式,先来看history模式,这是创建项目时默认的模式
history模式
菜鸟的Vue-Cli 4.x学习总结-风君子博客
在该模式下执行打包命令:npm run build,会自动生成一个dist文件,里面是打包好的文件
菜鸟的Vue-Cli 4.x学习总结-风君子博客
打包后,我们想要在静态服务器上测试,先要安装静态服务器

 npm i serve -g //安装 serve dist -s //运行dist 目录,注意-s不能省略,不然后面会出现404错误

菜鸟的Vue-Cli 4.x学习总结-风君子博客菜鸟的Vue-Cli 4.x学习总结-风君子博客
访问链接,各种功能都正常
菜鸟的Vue-Cli 4.x学习总结-风君子博客

hash模式
菜鸟的Vue-Cli 4.x学习总结-风君子博客
接下来的命令都差不多的,唯一不同的就是运行dist目录时不需要-s

 npm run build//大包npm i serve -g //安装 serve dist //运行dist 目录,注意这里不需要-s

菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行结果,一切正常
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(10)导航守卫

导航守卫的作用就是跳转或取消跳转的导航功能,比如登录跳转方案,导航守卫还包括前置导航守卫和后置导航守卫等,直接上官网瞄一眼
菜鸟的Vue-Cli 4.x学习总结-风君子博客

前置导航守卫
按照官方的说法,在路由下新建一个前置导航守卫
菜鸟的Vue-Cli 4.x学习总结-风君子博客
代码

//模拟登录状态 
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路径时自动跳转到首页
菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行flag为false,未登录状态,一切正常访问,不管输入什么路径都会跳转到登录页面
菜鸟的Vue-Cli 4.x学习总结-风君子博客

守卫的参数参考官网给出的说明
菜鸟的Vue-Cli 4.x学习总结-风君子博客

后置导航守卫
菜鸟的Vue-Cli 4.x学习总结-风君子博客
代码

//全局后置导航守卫
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-Cli 4.x学习总结-风君子博客
运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
过渡动效
过渡效果,字面意思就是基础 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;
}

菜鸟的Vue-Cli 4.x学习总结-风君子博客
这样在路由跳转时会有一个转场的特效,这里是缓慢转场(大概是这个意思)

(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>

运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客
貌似没啥问题,但是一旦点击其他页面后再跳回计数页面,发现按钮的次数就又变为了0,有没有办法让按钮的次数不变呢,这就要用的Vuex了

(2)Store 模式

对应项目cli-06
store 模式支持极少的共享数据,按照官网给出的说法,如果不是大型的单页面应用,使用 Vuex 会繁琐冗余; 也就是说,你的应用特别简单,不需要真么重的插件,可以使用 store 模式; store 模式支持你在极少的共享数据中,就好比你只近视 50 度,可以不戴眼镜

在src目录下新建store/index.js文件用于存储共享数据
菜鸟的Vue-Cli 4.x学习总结-风君子博客

修改上一节的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了
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(3)Vuex的安装与基本使用

对应项目cli-06
上面的Store的方式是通过自己手动新建一个store/index.js的方式进行数据的共享,这种方式适合少量数据的共享,如果大量数据的话需要安装Vuex,使用配置好的Vuex

安装
安装的方式也简单,只需要在新建项目的时候,勾上Vuex的选项即可
菜鸟的Vue-Cli 4.x学习总结-风君子博客
安装完之后发现,在src下多了一个store文件夹,里面有一个index.js文件,里面有配置好一些数据
菜鸟的Vue-Cli 4.x学习总结-风君子博客
简单认识一下里面的配置
菜鸟的Vue-Cli 4.x学习总结-风君子博客

使用
还是以上面的计数的例子,在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模式实现的一样
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(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>

运行结果,信息都正确显示,住址跟着输入框改变而改变
菜鸟的Vue-Cli 4.x学习总结-风君子博客
这种传统的获取和修改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>

运行结果,这种方式也可以实现上面的功能,而且写法更简洁
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(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: {}
})

菜鸟的Vue-Cli 4.x学习总结-风君子博客

在原来的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>

菜鸟的Vue-Cli 4.x学习总结-风君子博客
运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(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>

运行结果菜鸟的Vue-Cli 4.x学习总结-风君子博客

(7)Actions

上节课我们说 Mutations 只能是同步函数,那异步操作可以交给Actions,直接上例子
在store/index.js中的mutations里面写要异步执行的代码,然后在Actions异步调用,为了模拟异步效果,可以在state里面设置一个状态值info
菜鸟的Vue-Cli 4.x学习总结-风君子博客
新建一个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>

运行结果
菜鸟的Vue-Cli 4.x学习总结-风君子博客

(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里面
菜鸟的Vue-Cli 4.x学习总结-风君子博客
引入后就可以直接使用了,新建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变量
菜鸟的Vue-Cli 4.x学习总结-风君子博客

菜鸟的Vue-Cli 4.x学习总结-风君子博客

下面演示调用模块的函数

修改module01.js的代码

export default{namespaced: true,//为了隔离主状态和模块状态名称冲突,需要设置一个命名空间//状态值state: {info: 'module01下的初始值'},//修改状态值mutations: {setInfo(state, value) {console.log("这是模块module01.js")state.info = value}},modules: {}
}

菜鸟的Vue-Cli 4.x学习总结-风君子博客
修改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模块下的函数
菜鸟的Vue-Cli 4.x学习总结-风君子博客