一 基础

语法

this.$nextTick

image.png
image.png

三个新的生命周期

路由组件独有的生命周期钩子. 当组件切换时, 两个生命周期钩子将会被激活

1 activated

切换到当前路由时被激活

  1. <script>
  2. export default {
  3. name: "Home",
  4. beforeDestroy() {
  5. console.log("Home 被销毁了")
  6. },
  7. //
  8. activated() {
  9. console.log("路由被激活了")
  10. },
  11. deactivated() {
  12. console.log("路由休眠了")
  13. }
  14. }
  15. </script>

2. deactivated

  1. <script>
  2. export default {
  3. name: "Home",
  4. beforeDestroy() {
  5. console.log("Home 被销毁了")
  6. },
  7. //
  8. activated() {
  9. console.log("路由被激活了")
  10. },
  11. deactivated() {
  12. console.log("路由休眠了")
  13. }
  14. }
  15. </script>

3 this.$nextTick()

this.$nextTick(function(){ // 页面更新后 input 获取焦点 this.$refs.inputTitle.focus() })

全局事件总线

1.定义和作用

  1. 什么是全局事件总线?
  2. 全局事件总线,是实现任意组件(父子,兄弟)之间通信的一种方式,能够往任意方向进行数据的传递
  3. 全局事件总线的要求:
  4. 1.所有组件都能'看到',访问(Vue.prototype.$bus)
  5. 2.具有$on(绑定),$off(解绑),$emit()等函数
  6. 综上所述,vm/vc组件实例对象能够担任全局事件总线,又因为全局事件总线应该在整个应用创建
  7. 后就应该存在,所以在main.js中安装全局事件总线最合适,

2.全局事件总线的安装

  1. // 引入vue
  2. import Vue from 'vue'
  3. // app最近,
  4. import App from './App.vue'
  5. Vue.config.productionTip = false
  6. // 定义/安装全局事件总线
  7. // const Bus = Vue.extend({})
  8. // const bus = new Bus();
  9. // Vue.prototype.$bus = bus
  10. new Vue({
  11. render: h => h(App),
  12. beforeCreate() {
  13. // 安装全局事件总线
  14. Vue.prototype.$bus = this;
  15. }
  16. }).$mount('#app')

3.消息接受者: 总线上绑定/解绑自定义事件

  1. <template>
  2. <div class="demo">
  3. <h3>{{ school }}</h3>
  4. <h3>{{ address }}</h3>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. name: 'School',
  10. data() {
  11. // 这里存放数据",
  12. return {
  13. school: 'SYSUniversity',
  14. address: 'HaiZhu,GuangZhou'
  15. }
  16. },
  17. mounted() {
  18. // 绑定全局事件总线,当sendDataToSchool被数据发送者触发,
  19. // ()=>{} 回调函数即刻执行, student即可收到params
  20. this.$bus.$on('sendDataToSchool', (...params) => {
  21. console.log('school receive message:', params)
  22. })
  23. },
  24. beforeDestroy() {
  25. // 注销捆绑在全局事件总线上的自定义事件,防止事件总线压力过大
  26. // 一定要注意写某个特定的解绑的事件/多个,如果不写,整个应用中的自定义事件都将被解绑
  27. this.$off('sendDataToStudent')
  28. }
  29. }
  30. </script>

4.触发总线上的自定义事件: 消息的发送者

  1. <template>
  2. <div class="demo">
  3. <h3>{{ name }}</h3>
  4. <h3>{{ age }}</h3>
  5. <button @click="sendData">click me to send data</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. // import引入的组件需要注入到对象中才能使用",
  11. name: 'Student',
  12. data() {
  13. // 这里存放数据",
  14. return {
  15. name: 'ManMan@qq.com',
  16. age: 22
  17. }
  18. },
  19. methods: {
  20. sendData() {
  21. // 触发全局事件总线上的sendDataToSchool, 回调函数执行, school即可收到student的信息
  22. this.$bus.$emit('sendDataToSchool', [this.name, this.age])
  23. }
  24. },
  25. }
  26. </script>

5. 事件总线原理

:::danger 如图所示,事件总线上绑定着每一个组件及其对应的自定义事件,例如A组价在总线上绑定(this.$bus.on)绑定了自定义事件test2,当其他组件(b,c,d…)想要给A发送消息(数据),只需要触发(this.$bus.$emit)test2事件,并携带相关参数,A即可收到其他组件传递的消息/数据 ::: image.png

消息订阅与发布

1.定义

:::info 1.什么是订阅/发布?
类似于全局事件总线,实现任意组件之间消息的通信以及信息的传递,消息的发布者发布消息到特定的频道,消息的订阅者订阅对应的频道获取消息,这就是消息的订阅与发布 :::

2.安装消息订阅发布插件

:::info 消息订阅发布只是vue中支持的一个概念,并没有提供对应的代码实现,所以需要引入/安装第三方订阅/发布库进行消息订阅与发布的开发
例如pubsub.js(还有其他很多的消息订阅/发布库,可以到网上或github中查找)
npm i pubsub-js
引入
import pubsub from ‘pubsub-js’ ::: :::info 关于pubsub.publish()等方法提示无法识别,是因为开发工具没有解析pubsub等第三方库的能力,需要下载能解析对应js的第三方库,解决方案参考如下连接
https://blog.csdn.net/u010142437/article/details/79041404 ::: image.png

3.消息订阅/取消订阅(订阅id取消)

  1. <template>
  2. <div class="demo">
  3. <h3>{{ school }}</h3>
  4. <h3>{{ address }}</h3>
  5. </div>
  6. </template>
  7. <script>
  8. import pubSub from "pubsub-js";
  9. export default {
  10. name: 'School',
  11. data() {
  12. // 这里存放数据",
  13. return {
  14. school: 'SYSUniversity',
  15. address: 'HaiZhu,GuangZhou'
  16. }
  17. },
  18. beforeCreate() {
  19. // 订阅消息, 订阅后会返回一个订阅id,用这个id取消某个频道消息的订阅
  20. this.channelId = pubSub.subscribe('schoolChannel', (channel, message) => {
  21. console.log('school get message:' + message + ' from:' + channel)
  22. })
  23. },
  24. beforeDestroy() {
  25. // 取消订阅
  26. pubSub.unsubscribe(this.channelId)
  27. }
  28. }
  29. </script>

4.消息发布

<template>
  <div class="demo">
    <h3>{{ name }}</h3>
    <h3>{{ age }}</h3>
    <button v-on:click="sendDataToSchool">发布消息到schoolChannel</button>
  </div>
</template>

<script>
// 1.引入pubsub
import pubSub from "pubsub-js";

export default {
  // import引入的组件需要注入到对象中才能使用",
  name: 'Student',
  data() {
    // 这里存放数据",
    return {
      name: 'ManMan@qq.com',
      age: 22
    }
  },
  methods: {
    // 消息发布
    sendDataToSchool() {
      pubSub.publish('schoolChannel', [this.name, this.age])
    }
  }

}
</script>

过渡与动画

:::info vue对css3动画的封装/支持 :::

1.vue动画Animation

<template>
  <div>
    <button @click="isShow =!isShow">显示/隐藏</button>
    <!--    appear让元素在页面打开时就以动画形式进入-->
    <transition appear>
      <h3 v-show="isShow">Vue动画</h3>
    </transition>
  </div>
</template>

<script>
export default {
  name: "Animation",
  data() {
    return {
      isShow: true
    }
  }
}
</script>

<style scoped>
h3 {
  background-color: pink;
  height: 150px;
}

.v-enter-active {
  /*my-animation动画效果进入过程*/
  animation: my-animation 1s linear;
}

.v-leave-active {
  /*my-animation动画效果离开过程*/
  animation: my-animation 1s reverse;
}

/*css 3动画 my-animation为动画效果名*/
@keyframes my-animation {
  /*进来*/
  from {
    transform: translateX(-100%);
  }
  /*离开*/
  to {
    transform: translateX(0px);
  }
}
</style>

2.vue过渡Transition

:::info vue对css过渡的封装和支持 :::

<template>
  <div>
    <h4>Transition</h4>
    <button @click="isShow =!isShow">显示/隐藏</button>
    <!--    appear让元素在页面打开时就以动画形式进入-->
    <transition appear>
      <h3 v-show="isShow">Vue动画</h3>
    </transition>
  </div>
</template>

<script>
export default {
  name: "Animation",
  data() {
    return {
      isShow: true
    }
  }
}
</script>

<style scoped>
h3 {
  background-color: pink;
  height: 150px;
}

/*上学家是起点,放学家是终点*/
.v-enter, .v-leave-to {
  transform: translateX(-100%);
}

.v-enter-active, .v-leave-active {
  transition: 0.5s linear;
}

.v-enter-to, .v-leave {
  transform: translateX(0);
}

</style>

3.vue过渡/动画的第三方库

:::info animation.css….,不用我们自己手动书写样式,直接按照要求引入使用第三方css动画/过渡库即可 :::

4.vue动画与过渡总结

image.png
image.png

二 vue脚手架cli

三 vue中的ajax

代理配置

:::info 跨域请求怎么配置?
1.xhr new XMLHttpRequest() xhr.open()/xhr.send()
2.jQuery $.get()/$post()
3.axios 对 xhr的封装 vue推荐使用
4.fetch
:::

1. 同源策略与跨域以及解决跨域的方式

:::info 同源: 协议名, ip, 端口保持一致
跨域: 违背同源策略, 协议名,ip,端口号只要有一个不同,就属于跨域cros

跨域解决方案
1.cor 后端配置跨域
2.jsonp 前后端共同解决跨域
3.代理服务器

注意:当使用代理时, 发送请求请求的资源在本地和目标服务器上都有且路径相同,此时没有进行相关配置,请求首先找寻的是本地vue项目目录下public下的资源 :::

2 代理服务器js

// 命令行 node server1
const express = require('express')
const app = express()

app.use((request,response,next)=>{
    console.log('有人请求服务器1了');
    console.log('请求来自于',request.get('Host'));
    console.log('请求的地址',request.url);
    next()
})

app.get('/students',(request,response)=>{
    const students = [
        {id:'001',name:'tom',age:18},
        {id:'002',name:'jerry',age:19},
        {id:'003',name:'tony',age:120},
    ]
    response.send(students)
})

app.listen(5000,(err)=>{
    if(!err) console.log('服务器1启动成功了,请求学生信息地址为:http://localhost:5000/students');
})
const express = require('express')
const app = express()

app.use((request,response,next)=>{
    console.log('有人请求服务器2了');
    next()
})

app.get('/cars',(request,response)=>{
    const cars = [
        {id:'001',name:'奔驰',price:199},
        {id:'002',name:'马自达',price:109},
        {id:'003',name:'捷达',price:120},
    ]
    response.send(cars)
})

app.listen(5001,(err)=>{
    if(!err) console.log('服务器2启动成功了,请求汽车信息地址为:http://localhost:5001/cars');
})

3.vue-cli配置单个代理服务器

:::info 当我们需要根据端口号的路径名不同代理到不同的服务器,这是单个的配置方式就不再适用 :::

2.1 axios发送请求(安装 引入axios)

<template>
  <div class="app">
    <h3>app</h3>
    <School></School>
    <Student></Student>
    <button @click="getStudentMessage">点击我获取学生信息,查看控制台</button>
  </div>
</template>

<script>
// 引入组件
import Student from "@/components/Student";
import School from "@/components/School";
import axios from "axios";

export default {
  name: "App",
  components: {
    School,
    Student,
  },
  methods: {
    getStudentMessage() {
      axios.get("http://localhost:8080/students").then(
          response => {
            console.log('请求成功了', response.data)
          },
          error => {
            console.log('请求失败了', error.message)
          }
      )
    }
  }
}
</script>

2.2 vue.config.js配置代理

const {defineConfig} = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  //开启代理服务器 方式1 只能配置单个代理
  devServer:{
    proxy:'http://localhost:5000'
  }
})

2.3 原理图

image.png

2.4 方法1小结

image.png

4. vue-cli配置多个代理服务器

3.1 axios发送请求

<template>
  <div class="app">
    <h3>app</h3>
    <School></School>
    <Student></Student>
    <button @click="getStudentMessage">点击我获取学生信息,查看控制台</button>
    <button @click="getCarMessage">点击我获取汽车信息,查看控制台</button>
  </div>
</template>

<script>
// 引入组件
import Student from "@/components/Student";
import School from "@/components/School";
import axios from "axios";

export default {
  name: "App",
  components: {
    School,
    Student,
  },
  methods: {
    // 获取学生信息
    getStudentMessage() {
      axios.get("http://localhost:8080/stu/students").then(
          response => {
            console.log('请求成功了', response.data)
          },
          error => {
            console.log('请求失败了', error.message)
          }
      )
    },
    // 获取汽车信息
    getCarMessage(){
      axios.get("http://localhost:8080/car/cars").then(
          response => {
            console.log('请求成功了', response.data)
          },
          error => {
            console.log('请求失败了', error.message)
          }
      )
    }
  }
}
</script>

3.2 vue.config.js配置多个代理

const {defineConfig} = require('@vue/cli-service')
module.exports = defineConfig({
    transpileDependencies: true,
    lintOnSave: false,

    //开启代理服务器 方式2
    devServer: {
        proxy: {
            // 所有baseUrl后面的第一个路径为 /stu 的请求
            // http://localhost:8081/stu/students -> http://localhost:5000/students
            '/stu': {
                target: 'http://localhost:5000',
                // 只要是前缀(端口号后的第一个子串)为/stu的,全部转发到http://localhost:5000/.../
                // 路径重写 baseUrl 后面的 /stu全部 替换为 '', + http://localhost:5000
                pathRewrite: {'^/stu': ''},
                ws:true, //是否使用websocket
                changeOrigin: true // 是否告诉目标服务器我来自哪里,ip是什么
            },
            // 所有baseUrl后面的第一个路径为 /car 的请求
            // http://localhost:8081/car/cars -> http://localhost:5000/cars
            '/car': {
                target: 'http://localhost:5001',
                pathRewrite: {'^/car': ''},
                changeOrigin: true
            },
        }
    }
})

3.3 方法小结

image.png

5. vue-resource

:::info vue中发送ajax请求的插件库, 本质上也是对xml的封装, 但是由于维护频率少,所以了解即可, 使用时 npm i vue-resource 下载 vue.use() 即可 :::

插槽slot

1、什么是插槽?它有什么作用?

:::info 数据的展现位置
作用: 让父组件可以向子组件指定的位置插入html, 也是组件间的一种通信方式之一 :::

2 默认插槽

2.1 插槽定义

:::info 只有一个插槽, 插入的元素默认放置该插槽所在位置 :::

<template>
  <div class="category">
    <h3>{{ title }}</h3>
    <ul>
      <li v-for="(item,index) in dataList" :key="index">{{ item }}</li>
    </ul>
    <!--    默认插槽,组件双标签之间传递进来的内容将会被插入此处,如果没有插入内容,默认显示插槽配置的默认信息-->
    <slot>默认插槽:你不给我东西,我就讹你</slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props: ['dataList', 'title']
}
</script>

<style scoped>
h3 {
  /*文字居中*/
  text-align: center;
  background-color: orange;
}

.category {
  background-color: skyblue;
  width: 200px;
  height: 300px;
}
</style>

2.2 插槽使用

<template>
  <div class="app">
    <Category :dataList="films" title="films">
      <!--      插槽内容-->
      <img src="https://s3.ax1x.com/2021/01/16/srJ1q0.jpg" alt="show">
    </Category>
    <Category :dataList="games" title="games">
      <video src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" controls></video>
    </Category>
    <Category :dataList="foods" title="foods"></Category>
  </div>
</template>

<script>
// 引入组件
import Category from "@/components/Category";

export default {
  name: "App",
  components: {Category},
  data() {
    return {
      foods: ['火锅', '烤肉', '烤鱼'],
      games: ['王者荣耀', '和平精英', '植物大战僵尸'],
      films: ['《活着》', '《流浪地球》', '《速度与激情》']
    }
  }
}
</script>

<style scoped>
.app {
  display: flex;
  justify-content: space-around;
}

img, video {
  width: 100%;
}
</style>

2.3 默认插槽理解

image.png

3 具名插槽

:::info 多个插槽时, 插入内容需要指定其具体的插入位置 :::

image.png

4 作用域插槽

:::info 当父组件需要往子组件的插槽放置html结构,但是html的结构/内容由子组件的数据来决定,并且子组件的数据受到限制不能往父组件传,此时就需要作用域插槽进行数据的传递 ::: image.pngimage.png

四 vue-router

1.路由引入

1.1 Single Page Application

单页面应用, 整个应用只有一个完整的页面, 点击页面的导航链接不会刷新页面,只会做页面的局部更新,展示的数据需要发送ajax请求获取

1.2 路由

  1. 一组映射关系, 即 key-value键值对
  2. key为SPA路径处的路径变化,value为与路径key对应的组件

1.3 前端路由和后端路由

  1. 前端路由: 路径key 和 组件 映射
  2. 后端路由: 请求路径 和 controller 映射

2. 路由的基本使用

2.1 npm i vue-router@版本号

vue-router是vue的一个插件,vue的版本和插件的版本总是需要进行版本的协调,例如vue3对应vue-router@4, vue2只能使用vue-router@3

2.2 定义 引入 use 路由

(1) new 新建路由

// 引入vue
import VueRouter from "vue-router";
import About from "@/components/About";
import Home from "@/components/Home";

export default new VueRouter({
    // routes: 所有的路由配置
    routes: [
        {
            path:'/about', // 路由key
            component: About // 路由value: 组件
        },
        {
            path:'/home',
            component:Home
        }
    ]
})

(2) import use 使用路由

// 引入vue
import Vue from 'vue'
// app最近,
import App from './App.vue'
// 引入vue-router
import VueRouter from "vue-router";
// 安装 VueRouter 插件
import router from "@/router";

Vue.use(VueRouter)
Vue.config.productionTip = false

new Vue({
    router, // 使用路由
    render: h => h(App),
}).$mount('#app')

(3) router-link 跳转, router-view 展现

  1. router-link设置点击此link时,路由跳转到哪里(哪个组件)
  2. router-view 类似于组件的插槽, vue-router找到router-link需要跳转的路由后,将该路由在router-view 处进行展示
<template>
  <div class="app">
    <div>
      <div class="row">
        <div class="col-xs-2 col-xs-offset-2">
          <div class="list-group">
            <!--router-link实现路由切换 to表示要跳转的路由 路径key           -->
            <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
            <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
          </div>
        </div>
        <div class="col-xs-6">
          <div class="panel">
            <div class="panel-body">
              <!--发生路由改变的区域              -->
              <router-view></router-view>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

(4) 过程图

image.png

(5) vue路由的几个注意点

  1. index.js中配置路由后,对应路由的组件实例身上将会被vue加上一个$route属性,这个属性包含本组件对应的路由规则相关信息, 每个组件不同
  2. 组件按照是否为路由组件分类, 分为: 路由组件(pages文件夹) 和 普通组件(component文件夹)
  3. 全局只有一个router, 里面包含 n 个 route 规则

2.3 路由嵌套

(1) 路由配置文件

父路由的path是 ‘/路径’ , 而子路由不能使用 ‘/路径’,而是直接 ‘子路由路径’

// 引入vue
import VueRouter from "vue-router";
import About from "@/pages/About";
import Home from "@/pages/Home";
import News from "@/pages/News";
import Message from "@/pages/Message";

export default new VueRouter({
    // routes: 所有的路由配置
    routes: [
        {
            path: '/about', // 路由key
            component: About // 路由value: 组件
        },
        {
            path: '/home',
            component: Home,
            children: [
                {
                    path: 'news', // 二级路由后 path 使用 news 而不是 /news
                    component: News
                },
                {
                    path: 'message',
                    component: Message
                }
            ]
        }
    ]
})

(2) 页面

router-link的to中 ,需要使用 ‘/父路由/子路由路径’ 方式进行阶梯式配置

<template>
  <div>
    <h2>Home组件内容</h2>
    <div>
      <ul class="nav nav-tabs">
        <li>
          <!-- 嵌套路由: /父路由路径/子路由路径           -->
          <router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
        </li>
        <li>
          <router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
        </li>
      </ul>
    </div>
    <router-view></router-view>
  </div>
</template>

2.4 路由传参 query参数

路由跳转的同时, 跳转路径中携带query参数

(1) to字符串方式

<template>
  <div>
    <ul v-for="message in messages" :key="message.id">
      <li>
         路由传参: query参数 to字符串写法 ` `中间能够夹杂js表达式, 然后与字符串拼接
        <router-link :to="`/home/message/detail?id=${message.id}&title=${message.title}`">
          {{ message.title }}
        </router-link>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Message",
  data() {
    return {
      messages: [
        {id: 1, title: 'message001'},
        {id: 2, title: 'message002'},
        {id: 3, title: 'message003'},
      ]
    }
  }
}
</script>
<template>
  <ul>
    <li>{{$route.query.id}}</li>
    <li>{{$route.query.title}}</li>
  </ul>
</template>

<script>
export default {
  name: "Detail",
}
</script>

(2) to对象方式

<template>
  <div>
    <ul v-for="message in messages" :key="message.id">
      <li>
        <router-link :to="{
          path:'/home/message/detail',
          query:{
            id:message.id,
            title: message.title
          }
        }">
          {{ message.title }}
        </router-link>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Message",
  data() {
    return {
      messages: [
        {id: 1, title: 'message001'},
        {id: 2, title: 'message002'},
        {id: 3, title: 'message003'},
      ]
    }
  }
}
</script>

(3) 路由命名

简化router-link 中 to 的跳转配置

image.png

2.5路由传参 params参数

  1. path路径中 :变量名 占位
  2. router-link中 to对象式 只能使用name 而不能使用 path
  3. 获取参数 $route.params.参数名 而不是 $route.query.参数名

image.png

2.6 路由props属性

记得组件中 props[] 方式接收数据

image.png

2.7 router-link的replace属性

image.png

3 编程式路由导航

image.png

4. 缓存路由组件

image.png

5. 路由守卫

5.1 全局路由守卫

(1) 前置路由守卫 router.beforeEach

全局前置路由守卫: 在路由跳转/切换前, 进行一定操作(路由鉴权…)

// 全局前置路由守卫, 在路由跳转之前执行一定的操作, 对路由进行鉴权操作...
router.beforeEach((to, from, next) => {
    // 如果 目标路由 的 meta.isAuth 为true, 并且用户名为'BobeLv'
    if (to.meta.isAuth) {
        if (localStorage.getItem("username") === 'BobeLv') {
            console.log("欢迎您")
            // 如果符合要求, 放行
            next()
        } else {
            alert("用户名错误,你无权限访问该区域!")
        }
    } else {
        next()
    }
})

(2) 后置路由守卫 router.afterEach

在路由跳转/切换后, 进行一定操作(路由鉴权…)

// 全局后置路由, 路由跳转成功以后执行的操作
router.afterEach((to, from) => {
    console.log("来自:" + from.name, "去往:" + to.name)
    if (to.meta.title) {
        // 跳转成功后, 改变页签标题
        document.title = to.meta.title
    } else {
        document.title = '通用title'
    }
})

(3) 全局路由守卫小结

// 引入vue
import VueRouter from "vue-router";
import About from "@/pages/About";
import Home from "@/pages/Home";
import News from "@/pages/News";
import Message from "@/pages/Message";
import Detail from "@/pages/Detail";

const router = new VueRouter({
    // routes: 所有的路由配置
    routes: [
        {
            name: 'About',
            path: '/about', // 路由key
            component: About, // 路由value: 组件
            // 前置路由守卫, 单个路由独享
            beforeEnter: (to, from, next) => {
                // 如果 目标路由 的 meta.isAuth 为true, 并且用户名为'BobeLv'
                if (localStorage.getItem("username2") === 'bobe1') {
                    console.log("欢迎您")
                    // 如果符合要求, 放行
                    next()
                } else {
                    alert("只有bobe能够访问此区域")
                }
            }
        },
        {
            name: 'Home',
            path: '/home',
            component: Home,
            children: [
                {
                    name: 'News',
                    path: 'news', // 二级路由后 path 使用 news 而不是 /news
                    component: News,
                    // 路由鉴权所需数据
                    meta: {
                        isAuth: true, // 当前路由需要鉴权
                        title: "news area"
                    }
                },
                {
                    name: 'Message',
                    path: 'message',
                    component: Message,
                    meta: {
                        isAuth: true, // 当前路由需要鉴权
                        title: "message area"
                    },
                    children: [
                        {
                            name: 'Detail',// 路由命名, 当路由层级较深, 使用命名可简化跳转配置
                            path: 'detail/:id/:title', // 路由传参 param 方式, :id 占位
                            component: Detail
                        }
                    ]
                }
            ]
        }
    ]
})
// 全局前置路由守卫, 在路由跳转之前执行一定的操作, 对路由进行鉴权操作...
router.beforeEach((to, from, next) => {
    // 如果 目标路由 的 meta.isAuth 为true, 并且用户名为'BobeLv'
    if (to.meta.isAuth) {
        if (localStorage.getItem("username") === 'BobeLv') {
            console.log("欢迎您")
            // 如果符合要求, 放行
            next()
        } else {
            alert("用户名错误,你无权限访问该区域!")
        }
    } else {
        next()
    }
})

// 全局后置路由, 路由跳转成功以后执行的操作
router.afterEach((to, from) => {
    console.log("来自:" + from.name, "去往:" + to.name)
    if (to.meta.title) {
        // 跳转成功后, 改变页签标题
        document.title = to.meta.title
    } else {
        document.title = '通用title'
    }
})

export default router;

5.2 独享路由守卫

(1) 路由内路由守卫 beforeEnter()

当进入当前路由前, 执行的操作

 {
            name: 'About',
            path: '/about', // 路由key
            component: About, // 路由value: 组件
            // 前置路由守卫, 单个路由独享
            beforeEnter: (to, from, next) => {
                // 如果 目标路由 的 meta.isAuth 为true, 并且用户名为'BobeLv'
                if (localStorage.getItem("username2") === 'bobe1') {
                    console.log("欢迎您")
                    // 如果符合要求, 放行
                    next()
                } else {
                    alert("只有bobe能够访问此区域")
                }
            }
        },

(2) 组件内路由守卫 beforeRouteEnter

 // 组件内路由守卫, 进入本组件前
  beforeRouteEnter(to, from, next) {
    console.log("进入detail组件前", to.name, from.name)
    next()
  },

(3) 组件内路由守卫 beforeRouteLeave

  beforeRouteLeave(to,from,next){
    console.log("离开detail组件之前",to.name,from.name)
    next()
  }

(4) 组件内路由守卫case

<template>
  <ul>
    <li>{{ $route.params.id }}</li>
    <li>{{ $route.params.title }}</li>
  </ul>
</template>

<script>
export default {
  name: "Detail",
  beforeDestroy() {
    console.log("Detail 被销毁了")
  },
  // 组件内路由守卫, 进入本组件前
  beforeRouteEnter(to, from, next) {
    console.log("进入detail组件前", to.name, from.name)
    next()
  },
  // 组件内路由守卫, 进入本组件前
  beforeRouteLeave(to,from,next){
    console.log("离开detail组件之前",to.name,from.name)
    next()
  }
}
</script>

<style scoped>

</style>

6 路由的两个工作模式

image.png

6.1 是什么?

6.2 hash哈希模式

6.3 history历史模式

五 vuex

1 定义

:::info 专门在vue中实现 集中式 状态(数据) 管理的一个 插件(Vue.use()), 对 vue应用中多个组件的 共享状态 进行集中式管理(读/写/计算…) ,也是 组件之间进行通信的一种方式, 适用于任意组件通信
项目: https://github.com/vuejs/vuex

  1. 集中式: 所有组件共享的数据及对数据的操作, 都放置在一个地方进行统一管理
  2. 状态: 数据, vue组件所需要/依赖的数据
  3. vuex: 一种插件, 一种通信方式 :::

    2 用途和作用

    :::info

  4. 多组件共享/依赖同一个状态: 网站访问数: 所有人都需要能够看到并且能够修改(登录行为修改)

  5. 来自不同组件的行为需要变更共享的状态(同一状态) ::: image.png

    3 vuex原理图详解(重点)

    image.png

    4. vuex使用

    4.1 npm安装

    :::info vue3使用的是vuex4, vuex3只支持vue2, 在没有指定版本号时, 默认安装vuex4, 所以一定要注意版本依赖问题
    npm i vuex@3 :::

    4.2 创建

    ```javascript // 1.引入Vue,因为你要Vue.use() import Vue from “vue”; // 2.引入Vuex,你才能使用构造函数创建 import Vuex from “vuex”;

// 2.1安装插件 Vue.use(Vuex) // 3.vuex 的 state, 保存公共状态 const state = {} // 4.vuex 的 actions, 保存对公共状态的操作 const actions = {} // 5.vuex 的 mutations, 公共数据/状态的改变 const mutations = {} // 6. 暴露定义的 Store,以供vue应用引入 export default new Vuex.Store( { state, actions, mutations } )

<a name="gYI5k"></a>
### 4.3 使用
```javascript
// 引入vue
import Vue from 'vue'
// app最近,
import App from './App.vue'

Vue.config.productionTip = false
// 引入store
import store from './vuex/store'
// 创建vm时传入store
new Vue({
    store,
    render: h => h(App),
    mounted() {
        console.log(this)
    }
}).$mount('#app')

4.4 定义的注意点

image.png

5 vuex的执行流程

:::info dispatch(actionName,val) ——> commit(mutationName,val) ——>mutation
为了区分执行的动作是actions中还是mutations的, 我们通常将actions中的actionName小写, 而mutations中对应的mutationName大写 ::: image.png

5.1 vuex的actions

:::info 动作和动作管理
当actions中没有过多的逻辑, 你可以直接commit到mutations中而不用经过dispatch到actions再到mutations,
同时,在action中你需要再dispatch到另一个action, 可以使用context提供的dispatch完成需求, 此时context中的内容能够让你的某个action不至于过于复杂
注意: 如果直接在action中进行状态state的修改而不是在mutations中进行状态的修改, vue的开发者工具将无法捕获state到在mutations以外的地方发生的变化 :::

// 4.vuex 的 actions, 保存对公共状态的操作
const actions = {
    // dispatch() 后, 进行对应动作
    // actionName(context上下文,值)
    increment(context, value) {
        // commit() 给 mutations
        context.commit("INCREMENT", value)
    },
    decrement(context, value) {
        context.commit("DECREMENT", value)
    },
    multiple(context, value) {
        context.commit("MULTIPLE", value)
    },
    divide(context, value) {
        context.commit("DIVIDE", value)
    },
    reset(context, value) {
        context.commit("RESET", value)
    },
    // 其他的再一次dispatch到这里, 然后打印日志
    log(context, value,mutationname){
      console.log("log info")
      context.commit(mutationname,value)
    }
}

5.2 vuex的mutations

:::info 状态管理 :::

// 5.vuex 的 mutations, 公共数据/状态的改变
const mutations = {
    // mutation name
    INCREMENT(state, value) {
        state.sum += value
    },
    DECREMENT(state, value) {
        state.sum -= value
    },
    MULTIPLE(state, value) {
        state.sum *= value
    },
    DIVIDE(state, value) {
        state.sum /= value
    },
    RESET(state) {
        state.sum = 0
    }
}

5.3 vuex的state

:::info 状态存储 :::

// 3.vuex 的 state, 保存公共状态
const state = {
    sum: 0
}

5.4 vuex的getters

:::info 类似vm中的data数据项和computed, 一个是原始数据来源, 另一个是被处理过后的数据 :::

// vuex 的 getters
const getters = {
    // vuex 的 state
    processedSum(state) {
        // 一定要return
        return state.sum + 1
    }
}
<template>
  <div class="app">
    <h2>SUM:{{ $store.state.sum }}</h2>
    <!-- vuex 中的 getters中 保存的处理过后的 sum    -->
    <h2>SUM+1:{{ $store.getters.processedSum }}</h2>
...
</template>

5.5 mapState

:::info 方便组件获取vuex的state的数据, 将对应的变量+值 映射为 data的计算属性 :::

(1) 计算属性获取state中的变量及其值

    sum() {
      return this.$store.state.sum
    },
    caseName(){
      return this.$store.state.caseName
    },
    caseTime(){
      return this.$store.state.caseTime
    },

(2) mapState对象写法

:::info 获取state中的变量及其值, 当 计算属性名和state变量名不一致时 :::

    // mapState写法1 对象写法 : 将vuex 的 state 中的变量及其值 映射 成计算属性, 
    // 类似于 上面我们手写的计算属性
    ...mapState({sum: 'sum', caseName: 'caseName', caseTime: 'caseTime'})

(3) mapState数组写法

:::info 获取state中的变量及其值, 当 state中的变量名作为映射后的计算属性的名字 :::

5.6 mapGetters

和mapState一样的规则, 不多赘述

    // 手写mapGetters
    // processedSum() {
    //   return this.$store.getters.processedSum
    // },
    // mapGetters : 将vuex 的 getters 中的 变量及其值映射为 组件的 computed属性
    ...mapGetters(['processedSum'])

5.6 mapActions

  methods: {
    // increment() {
    //   // 1.dispatch(actionName,value)
    //   this.$store.dispatch("increment", this.step)
    // },
    // decrement() {
    //   this.$store.dispatch("decrement", this.step)
    // },
    // multiple() {
    //   this.$store.dispatch("multiple", this.step)
    // },
    // divide() {
    //   this.$store.dispatch("divide", this.step)
    // },
    // reset() {
    //   this.$store.dispatch("reset")
    // }
    ...mapActions(['increment', 'decrement', 'multiple',
      'divide', 'reset']),
  }

image.png

5.7 mapMutations

直接commit不用经过actions(没有ajax等其他逻辑需要再执行时使用

  methods: {
     ...mapMutations(['increment', 'decrement', 'multiple', 'divide', 'reset'])
  }

5.8 vuex的模块化编码+namespace命名空间(你很懒,记得补哟)

image.png
image.png

六 element-ui的使用

七 vue3