一. 创建一个vue项目
1.安装@vue/cli脚手架
- 全局安装
yarn global add @vue/cli
# OR
npm install -g @vue/cli
- 查看vue脚手架版本
vue -V
2. 创建项目
注意: 项目名不能带大写字母, 中文和特殊符号
- 创建项目
# vue和create是命令, vuecli-demo是文件夹名
vue create vuecli-demo
- 选择模板
可以上下箭头选择, 弄错了ctrl+c重来
选择用什么方式下载脚手架项目需要的依赖包
回车等待生成项目文件夹+文件+下载必须的第三方包们
进入项目下, 启动更新本地服务器
yarn serve
#or
npm run serve
看到这个界面就表示项目创建完成
二. 配置项目
vue.config.js 在这里进行一些项目的配置(webpack配置)
/* 覆盖webpack的配置 */
module.exports = {
devServer: { // 自定义服务配置
open: true, // 自动打开浏览器
port: 3000
}
}
三. vue页面分析
<template> //页面结构
<div> //只能有一个 根标签,所有的结构都放在这个根标签内
</div>
</template>
<script>
export default {
//钩子函数
1.初始化
beforeCreate created
2.挂载
beforeMount mounted
3.更新
beforeUpdate updated
4.销毁
beforeDestroy destroyed
5.激活
activated deactivated
//1.接收从父元素传的值
props:{
自定义属性名:{
type:检验数据的类型,
require:是否必填,
default:默认数据,
//自定义校验规则
validator(val){}
//val 就是 父元素传的值
// 校验通过返回 true 否则 false
},
//2.组件注册
components:{
//导入的组件的名字
},
/*1.数据 */
data(){ return {} },
/*2.函数 */
methods:{},
//3.计算属性-依赖data数据的函数计算
computed:{},
/*4.监听数据变化*/
watch:{},
/*5.过滤器*/
filters:{} //2.局部过滤器
//1.自定义指令
directives:{}
}
</script>
<style> // 样式
// scoped 使得当前页面最多能影响子组件最外层的样式
//想要在scoped作用下,是页面能影响子组件深层样式,可以使用深度选择器
// ::v-deep (目前以弃用),建议使用:deep
</style>
3.1. 计算属性-computed
一个变量的值, 需要用另外变量计算而得来
计算属性带有缓存的特性:
- 计算属性对应函数执行后, 会把return值缓存起来
- 依赖项不变, 多次调用都是从缓存取值
- 依赖项值-变化, 函数会"自动"重新执行-并缓存新的值
语法:
computed: {
// 注意: 计算属性和data属性都是变量-不能重名
//只读
计算属性名(){
return 值
}
//既读又写
属性名: {
set(值){},
get() { return "值"}
}
}
总结: 想要给计算属性赋值, 需要使用set方法
3.2.侦听器-watch
可以侦听data/computed属性值改变
语法:
watch:{
//简单类型
变量名(newVal,oldVal){
// newVal: 当前最新值
// oldVal: 上一刻值
// 变量名对应值改变这里自动触发
}
//复杂类型
要侦听的属性名: {
immediate: true, // 立即执行
deep: true, // 深度侦听复杂类型内变化
handler (newVal, oldVal) {
//newVal===oldVal
}
}
//复杂对象的一个属性
'对象.属性'(newVal){
// 变量名对应值改变这里自动触发
}
}
总结: immediate立即侦听, deep深度侦听, handler固定方法触发
3.3过滤器
过滤器就是一个函数, 传入值返回处理后的值
过滤器只能用在, 插值表达式和v-bind表达式
语法:
- 全局—在main.js
//页面中
过滤器使用
语法: {{ 值 | 过滤器名字 }}
<p>{{ msg | reverse }}</p>
----------------------------------
Vue.filter("过滤器名字",function(值){
return 返回处理后的值
})
- 局部—只能在当前vue文件内使用
filters:{
过滤器名字(val){
return 处理后的值
}
}
3.4钩子函数-生命周期函数
3.4.1 初始化
- beforeCreate: 组件的props/data/methods 尚未被创建,都处于不可用状态
- created: 组件的props/data/methods都已创建好,都处于可用状态,但是组件的模板结构尚未生成
3.4.2 挂载
-
beforeMount: 将要把内存中编译好的HTML结构渲染到浏览器,此时浏览器还没有当前组件的DOM元素
-
mounted: 已经把内存中的HTML结构,成功的渲染到了浏览器中,此时浏览器中已经包含了当前组件的DOM结构
3.4.3 更新
-
beforeUpdate: 将要根据变化过后,最新的数据,重新渲染组件的模板结构
-
updated: 已经根据最新的数据,完成了组件DOM结构的重新渲染
3.4.4 销毁
-
beforeDestroy: 将要销毁此组件,此时尚未销毁,组件还处于正常工作的状态
-
destroyed: 组件已经被销毁,此组件在浏览器中对应的DOM结构已经完全移出
3.4.5 激活和非激活
- activated: 激活时触发(组件显示时)
- deactivated: 失去激活状态触发(组件不显示时)
3.5 自定义指令
3.5.1 全局
在main.js用 Vue.directive()方法来进行注册, 以后随便哪个.vue文件里都可以直接用v-fofo指令
// 全局指令 - 到处"直接"使用
Vue.directive("gfocus", {
inserted(el,binding) {
//el: 指令所在的dom元素
//binding: 指令传的值
el.focus() // 触发标签的事件方法
}
})
3.5.2 局部
<!--标签使用自定义指令 v-指令名-->
<!--
inserted方法 - 指令所在标签, 被插入到网页上触发(一次)
update方法 - 指令对应数据/标签更新时, 此方法执行
-->
directives:{
指令名:{
inserted(el,binging){
el: dom元素
binding: 指令传的值
}
}
}
四. vue指令
4.1 v-bind 数据绑定
给标签属性设置vue变量的值
把vue变量的值, 赋予给dom属性上, 影响标签显示效果
<!-- vue指令-v-bind属性动态赋值 -->
1.全写 v-bind:属性名="vue变量"
<a v-bind:href="url">a标签</a>
2.简写 :属性名="vue变量"
<img :src="imgSrc">
<script>
export default {
data(){
return {
imgSrc:'http//www.xxxx',
url:'http://www.xxx1111'
}
}
}
</script>
4.2 v-on 事件绑定
给标签绑定事件
<!-- vue指令: v-on事件绑定-->
1.全写
语法:
1.1 v-on:事件名="要执行的少量代码"
<button v-on:click="count = count + 1">增加
1.2 v-on:事件名="methods中的函数"
<button v-on:click="addFn">增加1个</button>
1.3 v-on:事件名="methods中的函数(实参)"
<button v-on:click="addCountFn(5)">一次加5</button>
2.简写
<button @click="subFn">减少</button>
<script>
export default {
// ...其他省略
methods: {
addFn(e){ // this代表export default后面的组件对象(下属有data里return出来的属性)
this.count++
//事件对象
console.log(e)
},
addCountFn(num,$event){
this.count += num
//事件对象
console.log($event)
},
subFn(){
this.count--
}
}
}
</script>
4.2.1v-on的事件对象
vue事件处理函数中, 拿到事件对象
-
函数—无参
- 通过形参直接接收 addFn(e)
-
函数—有参
- 通过 e v e n t 指代事件对象传给事件处理函数 a d d F n ( n u m , event指代事件对象传给事件处理函数 addFn(num, event指代事件对象传给事件处理函数addFn(num,event)
4.2.2v-on的修饰符
在事件后面.修饰符名 - 给事件带来更强大的功能
语法: @事件名.修饰符=“函数”
① 所有事件
- .stop - 阻止事件冒泡
- .prevent - 阻止默认行为
- .once - 程序运行期间, 只触发一次事件处理函数
② 键盘事件
- @keyup.enter - 监测回车按键
- @keyup.esc - 监测返回按键
4.3 v-model 数据的双向绑定
把value属性和vue数据变量, 双向绑定到一起
语法: v-model=“vue数据变量”
双向数据绑定
- 数据变化 -> 视图自动同步
- 视图变化 -> 数据自动同步
<!-- v-model:是实现vuejs变量和表单标签value属性, 双向绑定的指令 -->
<input type="text" v-model="username" />
<script>
export default {
data() {
return {
username: "",
};
// 总结:
// 特别注意: v-model, 在input[checkbox]的多选框状态
// 变量为非数组, 则绑定的是checked的属性(true/false) - 常用于: 单个绑定使用
// 变量为数组, 则绑定的是他们的value属性里的值 - 常用于: 收集勾选了哪些值
}
};
</script>
注意:
遇到复选框, v-model的变量值
- 非数组 - 关联的是复选框的checked属性
- 数组 - 关联的是复选框的value属性
4.3.1v-model修饰符
语法: v-model.修饰符=“vue变量”
- .number - 以parseFloat转成数字类型
- .trim - 去除首尾空白字符
- .lazy - 在change时触发而非inupt时 (数据改变触发,而不是数据输入)
4.4 v-if 和v-show 元素的隐藏和显示
控制标签的隐藏或出现
- v-show=‘vue变量’ ------原理: display:none 隐藏 (频繁切换使用)
- v-if=‘vue变量’ ------原理: 隐藏 直接从dom树上移除
<template>
<div>
<h1 v-show="isOk">v-show的盒子</h1>
<h1 v-if="isOk">v-if的盒子</h1>
</div>
</template>
<script>
export default {
data() {
return {
isOk: true,
}
}
}
</script>
4.4.1v-if高级用法
v-if以及v-else指令, 方便通过变量控制一套标签出现/隐藏
v-if=“表达式–true显示,false 隐藏”
v-if v-else-if v-else 通常配合使用
<div id="app">
<div v-if="type === 'A'"> A</div>
<div v-else-if="type === 'B'"> B</div>
<div v-else-if="type === 'C'">C</div>
<div v-else>Not A/B/C</div>
</div>
4.5 v-text和v-html 元素文本
更新DOM对象的innerText/innerHTML
v-text把值当成普通字符串显示
v-html把值当做html解析
- v-text=‘vue变量’
- v-html=‘vue变量’
注意: 会覆盖插值表达式
<template>
<div>
<p v-text="str"></p>
<p v-html="str"></p>
</div>
</template>
<script>
export default {
data() {
return {
str: "<span>我是一个span标签</span>"
}
}
}
</script>
4.6 v-for 元素循环渲染
列表渲染, 所在标签结构, 按照数据数量, 循环生成
语法: v-for=" (值,索引) in 目标结构(数组 / 对象 / 数字 / 字符串 "
注意: v-for的临时变量名不能用到v-for范围外
数据 stuArr: [{
id: 1001,
name: "孙悟空",
sex: "男",
hobby: "吃桃子"
}],
<template>
<div id="app">
<!-- v-for 把一组数据, 渲染成一组DOM -->
<!-- 口诀: 让谁循环生成, v-for就写谁身上 -->
<p>学生详细信息</p>
<ul>
<li v-for="obj in stuArr" :key="obj.id">
<span>{{ obj.name }}</span>
<span>{{ obj.sex }}</span>
<span>{{ obj.hobby }}</span>
</li>
</ul>
</div>
</template>
4.6.1v-for目标发生变化
当v-for遍历的目标结构改变, Vue触发v-for的更新
注意:
- 只有数组变更方法导致的v-for更新,才会让页面更新
- 数组的非变更方法, 返回的新数组, 不会导致v-for更新
- 只有采用覆盖数组或this.$set()才可以,更新页面
数组更新方法(七个):
- 数组的增删
push
unshift
pop
shift
splice()
- 数组翻转
reverse
- 数组排序
sort
this.$set()
参数1: 更新目标结构
参数2: 更新位置
参数3: 更新值
4.7 动态class
用v-bind给标签class设置动态的值
<!-- 语法:
:class="{类名: 布尔值}" true--使用类名 false--不使用
:class="['类名']" 通常是三元 表达式 ? '类名' : ''
使用场景: vue变量控制标签是否应该有类名
-->
<p :class="{red_str: bool}">动态class</p>
4.8 动态style
给标签动态设置style的值
<!-- 动态style语法
:style="{css属性名: 值}"
:style="{css属性名: 值}"
-->
colorStr: 'red'
<p :style="{backgroundColor: colorStr}">动态style</p>
五. 组件
5.1 组件通信
5.1.1父组件向子组件通信
父传值—v-bind
数据: arr:[1,2,3]
//传值
<子组件名 :自定义属性名="要传的数据" />
示例:
<son :list="arr" />
子接值—props
//简单--数组形式
props:['父组件绑定的自定义属性名']
//复杂--对象形式
props:{
父组件绑定的自定义属性名:{
type:检验数据的类型,//Array Object Number
require:是否必填,
default:默认数据,
//自定义校验规则
validator(val){
//val 就是 父元素传的值
// 校验通过返回 true 否则 false
}
}
}
注意: props变量本身是只读不能重新赋值
5.1.2子组件向父组件通信
子传值—this.$emit()
this.$emit('自定义事件名',传的值)
父接值—v-on
父组件内
<子组件名 @子组件自定义事件名="父的methods函数" />
------------------------------
<son @addData="onAdd" />
5.1.3 任意两组件通信
事件总线—对象 EventBus
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
传值-eventBus.$emit()
导入EventBus对象
//引入空白vue对象(EventBus)
import eventBus from '../EventBus'
传值
eventBus.$emit("自定义事件", 值) // 跨组件
接值-eventBus.$on()
导入EventBus对象
//引入空白vue对象(EventBus)
import eventBus from '../EventBus'
接值
eventBus.$on('传值方定义的事件',函数体)
5.1.4 vuex 全局共享数据
vuex 使用的配置
-
下载包
`npm i vuex@3.6.2 --save
-
导入vuex
import Vue from 'vue' import Vuex from 'vuex' Vue.use(vuex) const store = new Vuex.Store({ //模块化 modules:{ 模块 } }) export default store --------------------------- main.js 里面导入store new Vue({ el: '#app', store })
定义state
state是放置所有公共状态的属性
// 初始化vuex对象
const store = new Vuex.Store({
state: {
count: 0 // 管理数据
}
})
获取state中的数据
- 在
APP.vue
中直接获取
<div> state的数据:{{ $store.state.count }}</div>
---------------------------------------
script中:this.$store.state.count
- 辅助函数–mapState 获取,延展运算符将导出的状态映射给计算属性
1. 导入mapState
import { mapState } from 'vuex'
2. 采用数组形式引入state属性
computed: {
...mapState(['count'', 'num'])
}
定义mutations
tate数据的修改只能通过mutations,并且mutations必须是同步更新,目的是形成**
数据快照
**
mutations 只能进行同步操作
const store = new Vuex.Store({
state: { count: 0 },
// 定义mutations
mutations: {
// 方法里参数 第一个参数是当前store的state属性
// payload 载荷 运输参数 调用mutaiions的时候 可以传递参数(值)
addCount (state, payload) {
state.count += payload
}
}
})
获取mutations 的方法
- 直接调用
this.$store.commit('addCount', 10)
- 辅助函数–mapMutations
import { mapMutations } from 'vuex'
//方法里
methods: {
...mapMutations(['addCount'])
}
定义actions
actions: {
//context.state 获取状态
//context.commit 来提交mutations,
//context.diapatch 调用其他的action
// ctx 理解为你在组件中用的 this.$store
getAsyncCount (ctx) {
//异步函数,一秒钟之后 要给一个数 去修改state
context.commit('addCount', 123)
}
}
调用actions里面的方法
- 原始调用
this.$store.dispatch('getAsyncCount',123)
- 辅助函数—mapActions
import { mapActions } from 'vuex'
methods: {
...mapActions(['getAsyncCount'])
}
定义getters
除了state之外,有时我们还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters
getters: {
// getters函数的第一个参数是 state
// 必须要有返回值
filterList: state => state.list.filter(item => item > 5)
}
使用getters
- 原始方法
<div>{{ $store.getters.filterList }}</div>
- 辅助函数
computed: {
...mapGetters(['filterList'])
}
<div>{{ filterList }}</div>
5.2 获取组件对象- ref 和 $refs
利用 ref 和 $refs 可以用于获取 dom 元素 和 组件对象
- ref 在dom元素上, $refs 获取的就是ref所在的dom元素
- ref 在组件上, $refs 获取的就是组件对象
//1.dom元素
<h1 ref="myH"> h1 </h1>
-------------------------
this.$refs.myH
//2.组件对象
<Demo ref="de"></Demo>
--------------------------
const demoObj = this.$refs.de
demoObj.fn() //组件对象 调用组件内的函数
5.3 动态组件
多个组件使用同一个挂载点,并动态切换,这就是动态组件
设置挂载点: , 使用is属性来设置要显示哪个组件
<component :is="变量">
// comName 变量 存储着组件的名字
is属性 绑定那个组件名,component就显示那个组件
5.3.1 组件缓存
组件切换会导致组件被频繁销毁和重新创建, 性能不高
使用Vue内置的keep-alive组件, 可以让包裹的组件保存在内存中不被销毁
<keep-alive>
<component :is="comName"></component>
</keep-alive>
注意: 使用keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 但会让开发者无法确定组件的生命周期
5.3.2 补充生命周期
被缓存的组件不再创建和销毁, 而是激活和非激活
- activated - 激活时触发
- deactivated - 失去激活状态触发
5.4 组件插槽
用于实现组件的内容分发, 通过 slot 标签, 可以接收到写在组件标签内的内容
5.4.1 默认插槽
语法口诀:
- 组件内用占位
- 使用组件时夹着的地方, 传入标签替换slot
//主组件
<组件>
插槽的内容
</组件>
//组件
<slot>显示插槽的内容
//默认内容
如果组件内不传内容,
就显示slot内的内容
</slot>
插槽默认内容
如果外面不给传, 想给个默认显示内容
口诀: 夹着内容默认显示内容, 如果不给插槽slot传东西, 则使用夹着的内容在原地显示
5.4.2 具名插槽
当一个组件内有2处以上需要外部传入标签的地方
<组件>
<template v-slot:插槽名字>
</template>
简化写法 v-slot: ==== #
<template #插槽名字>
</template>
</组件>
--------------------------------
<slot name="插槽名字"></slot>
v-slot可以简化成#使用
5.4.3 作用域插槽
子组件里值, 在给插槽赋值时在父组件环境下使用
插槽传值
<slot :自定义属性="值"></slot>
父组件接收
<组件>
<!-- 需求: 插槽时, 使用组件内变量 -->
<!-- scope变量: {自定义属性: 插槽传的值} -->
<template v-slot:插槽名字="变量接收插槽传的值">
</template>
</组件>
总结: 组件内变量绑定在slot上, 然后使用组件v-slot=“变量” 变量上就会绑定slot身上属性和值
六. 路由
使用路由,要先下载vue官方提供的vue-router包
6.1 安装配置路由
- 安装
yarn add vue-router@3.6.5
#根据自己需要下载相对应的版本
- 导入路由
import VueRouter from 'vue-router'
- 使用路由插件
// 在vue中,使用使用vue的插件,都需要调用Vue.use()
Vue.use(VueRouter)
- 创建路由规则数组
const routes = [
{
path: "/find",
component: Find
},
{
path: "/my",
component: Find
},
//默认没有规则能够匹配到,就显示404页面
// 404在最后(规则是从前往后逐个比较path)
{
path: "*",
component: NotFound
}
]
重定向-redirect
匹配path后, 强制切换到目标path上
例如:网页默认打开, 匹配路由"/“, 强制切换到”/find"上
{
path: "/", // 默认hash值路径
redirect: "/find" // 重定向到/find
// 浏览器url中#后的路径被改变成/find-重新匹配数组规则
},
- 创建路由对象 - 传入规则
const router = new VueRouter({
routes
})
路由模式-mode
修改路由在地址栏的模式
- hash - http://localhost:8080/#/home 带 #
- history - http://localhost:8080/home 不带 #
const router = new VueRouter({
routes,
mode: "history" // 打包上线后需要后台支持, 模式是hash
})
- 关联到vue实例
new Vue({
router
})
- components换成router-view
<router-view></router-view> //输出路由
总结: 下载路由模块, 编写对应规则注入到vue实例上, 使用router-view挂载点显示切换的路由
6.2 路由的使用
点击导航,跳转页面
6.2.1 声明式导航
可用全局组件router-link来替代a标签
-
router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)
-
router-link提供了声明式导航
高亮
的功能(自带类名)
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link>
-----------------------------------------
点击相应的导航,显示不同的页面
<router-view></router-view>
6.2.1.1传参
在跳转路由时, 可以给路由对应的组件内传值
在router-link上的to属性传值
查询字符串形式传参
- /path?参数名=值
<router-link to="/part?name=小传">朋友-小传</router-link>
路径形式传参
注意: 这种形式需要路由对象提前配置
path: “/path : 参数名”
const routes = [
{
// 有:的路径代表要接收具体的值
// :username? 加一个? 表示 路由跳转时, username可加可不加
path: "/part:username",
component: Part
}
]
- /path/值
<router-link to="/part/小智">朋友-小智</router-link>
6.2.1.2 接收参数
参数接收,要在对应的页面
查询字符串形式接收
- $route.query.参数名
<p>人名: {{ $route.query.name }}</p>
路径参数形式接收
- $route.params.参数名
<p>人名: {{ $route.params.name }}</p>
总结:
?key=value 用$route.query.key 取值
/值 提前在路由规则/path/:key 用$route.params.key 取值
6.2.2 编程式导航
用JS代码来进行跳转
this.$router.push({
path: "路由路径", // 都去 router/index.js定义
name: "路由名"
})
路由数组里, 给路由起名字
{
path: "/find",
name: "Find",
component: Find
}
6.2.2.1 跳转
语法:
- this.$router.push({path: “路由路径”})
- this.$router.push({name: “路由名”})
虽然用name跳转, 但是url的hash值还是切换path路径值
场景:
- 方便修改: name路由名(在页面上看不见随便定义)
- path可以在url的hash值看到(尽量符合组内规范)
this.$router.push({
path:'/find',
或
name:'Find'
})
6.2.2.2 传参接值
query / params 任选 一个
//推荐: name+query方式传参
this.$router.push({
path: "路由路径"
name: "路由名",
query: {
"参数名": 值
}
params: {
"参数名": 值
}
})
// 对应路由接收 $route.params.参数名 取值
// 对应路由接收 $route.query.参数名 取值
格外注意: 使用path会自动忽略params
6.3 路由嵌套
在现有的一级路由下, 再嵌套二级路由
配置2级路由:
一级路由path从/开始定义
二级路由往后path直接写名字, 无需/开头
嵌套路由在上级路由的children数组里编写路由信息对象
{
path: "/find",
name: "Find",
component: Find,
children: [
{
path: "recommend",
component: Recommend
},
{
path: "ranking",
component: Ranking
},
{
path: "songlist",
component: SongList
}
]
}
总结: 嵌套路由, 找准在哪个页面里写router-view和对应规则里写children
6.4 路由的前置守卫
路由跳转之前, 先执行一次前置守卫函数, 判断是否可以正常跳转
语法:
router.beforeEach(
(to, from, next)=>{
// to: 要跳转到的路由 (路由对象信息) 目标
// from: 从哪里跳转的路由 (路由对象信息) 来源
// next: 函数体 - next()才会让路由正常的跳转切换,
next(false)在原地停留, next("强制修改到另一个路由路径上")
// 注意: 如果不调用next, 页面留在原地
//路由跳转"之前"先执行这里, 决定是否跳转
}
)
尽量符合组内规范)
this.$router.push({
path:'/find',
或
name:'Find'
})
6.2.2.2 传参接值
query / params 任选 一个
//推荐: name+query方式传参
this.$router.push({
path: "路由路径"
name: "路由名",
query: {
"参数名": 值
}
params: {
"参数名": 值
}
})
// 对应路由接收 $route.params.参数名 取值
// 对应路由接收 $route.query.参数名 取值
格外注意: 使用path会自动忽略params
6.3 路由嵌套
在现有的一级路由下, 再嵌套二级路由
配置2级路由:
一级路由path从/开始定义
二级路由往后path直接写名字, 无需/开头
嵌套路由在上级路由的children数组里编写路由信息对象
{
path: "/find",
name: "Find",
component: Find,
children: [
{
path: "recommend",
component: Recommend
},
{
path: "ranking",
component: Ranking
},
{
path: "songlist",
component: SongList
}
]
}
总结: 嵌套路由, 找准在哪个页面里写router-view和对应规则里写children
6.4 路由的前置守卫
路由跳转之前, 先执行一次前置守卫函数, 判断是否可以正常跳转
语法:
router.beforeEach(
(to, from, next)=>{
// to: 要跳转到的路由 (路由对象信息) 目标
// from: 从哪里跳转的路由 (路由对象信息) 来源
// next: 函数体 - next()才会让路由正常的跳转切换,
next(false)在原地停留, next("强制修改到另一个路由路径上")
// 注意: 如果不调用next, 页面留在原地
//路由跳转"之前"先执行这里, 决定是否跳转
}
)