Vue-5
(一)动态组件
- 动态组件就是动态切换组件的显示和隐藏:vue提供了一个内置的
组件,专门用来实现动态组件的渲染
<component :is="comName">component>
- keep-alive缓存组件
<keep-alive include="Left"> <component :is="comName">component> keep-alive>
- 组件的name
export default{ //当提供了name属性后,组件的名字就是name属性的值 //对比: //1.组件的“注册名称”的主要应用场景,以标签的形式,把注册好的组件,渲染和使用到页面结构之中 //2.组件声明时候的“name”名称的主要应用场景,结合<keep-alive>标签实现组件的缓存功能,以及在测试工具中看到的组件name名称 name: 'MyLeft', }
- 案例
- App.vue
<template> <div class="app-container"> <h1>App根组件h1> <hr> <button @click='comName="Left"'>显示Left组件button> <button @click="comName='Right'">显示right组件button> <hr> <div class="box"> <keep-alive include="Left"> <component :is="comName">component> keep-alive> div> div> template> <script> import Left from '@/components/Left.vue' import Right from '@/components/Right.vue' export default { data(){ return { // 要展示的组件的名字comName comName: 'Left' } }, methods: { }, components: { Left, Right } } script> <style lang="less"> .app-container { overflow: hidden; padding-left: 10px; padding-right: 10px; background-color:#ECECEC; } style>
-
- Left.vue
<template> <div class="left"> <h4>Left组件---{{count}}h4> <button @click="count++">count+1button> div> template> <script> export default{ data(){ return { count: 0 } }, created() { console.log("Left组件被创建了"); }, // 当组件第一次被创建时,既会执行created生命周期,又会执行activated生命周期 // 但是,当组件被激活时,只会触发activated生命周期,不再触发created activated(){ console.log("组件被激活了"); }, deactivated() { console.log("组件被缓存了"); }, destroyed(){ console.log("Left组件被销毁了"); } } script> <style> .left { height: 300px; padding-left: 10px; background-color: orange; overflow: hidden; } style>
-
- Right.vue
<template> <div class="right"> <h4>Right组件h4> div> template> <script> export default{ } script> <style> .right { height: 300px; background-color: royalblue; overflow: hidden; } h4 { margin-left: 10px; } style>
(二)插槽
插槽(Slot)是vue为组件的封装者提供的能力,运行开发者在封装组件时,把不确定的,希望由用户指定的部分定义为插槽
- 插槽的基本语法格式:v-slot的简写为#
<div class="right"> <h4>Right组件h4> <slot name="default">这是default插槽的默认内容slot> div>
<div class="box"> <Right> <template #default> <p>我放个pp> template> Right> div>
- 具名插槽的定义和使用
<template #content> <div> <p>锦瑟无端五十弦p> <p>一弦一柱思华年p> div> template> <div class="content-box"> <slot name="content">slot> div>
- 作用域插槽的定义和使用
<template #content="{msg, user}"> <div> <p>锦瑟无端五十弦p> <p>一弦一柱思华年p> <p>{{msg}}p> <p>{{user.age}}p> div> template>
<div class="content-box">
<slot name="content" msg="只是当时已惘然" :user="userinfo">slot>
div>
<script>
export default{
data(){
return{
userinfo:{
name: '张三',
age: 20
}
}
}
}
script>
(三)自定义指令
- vue中的自定义指令分为两种
- 私有自定义指令
在每个vue组件中,可以在directives节点下声明私有自定义节点
-
-
- bind函数
-
//私有自定义指令的节点
directives:{
//定义名为color的自定义指令,指向一个配置对象
color: {
//当指令第一次被绑定到元素身上的时候,会立即触发bind函数,只调用一次,当DOM更新是并函数不会触发
//形参中的el表示当前指令所绑定到的那个DOM对象
bind(el,binding){
el.style.color = binding.value;
console.log(binding);
}
}
}
-
-
- update函数
-
//bind函数只调用一次,当指令第一次绑定到元素时调用,当DOM更新时bind函数不会被触发,
//update函数会在每次DOM更新时被调用
update(el,binding){
el.style.color = binding.value;
}
-
-
- 简写
-
//如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式
color(el,binding){
el.style.color = binding.value;
}
-
- 全局自定义指令
全局共享的自定义指令需要通过Vue.directive()进行声明
//main.js
//全局自定义属性
Vue.directive('color',{
bind(el,binding){
el.style.color = binding.value;
},
update(el,binding){
el.style.color = binding.value;
}
})
(四)eslint
| 序号 | 规则名字 | 规则约束/默认约束 |
| 1 | quotes | 默认:字符串需要使用单引号包裹 |
| 2 | key-spacing | 默认:对象的属性和值之间,需要有一个空格分割 |
| 3 | comma-dangle | 默认:对象或数组的末尾,不允许出现多余的逗号 |
| 4 | no-multiple-empty-lines | 不允许出现多个空行 |
| 5 | no-trailing-spaces | 不允许在行尾出现多余的空格 |
| 6 | eol-last | 默认:文件的末尾必须保留一个空行 |
| 7 | spaced-comment | 在注释中的//或/*后强制使用一致的间距 |
| 8 | indent | 强制一致的缩进 |
| 9 | import/first | import导入模块的语句必须声明在文件的顶部 |
| 10 | space-before-function-paren | 方法的形参之前是否需要保留一个空格 |
(五)将axios挂载到vue
//main.js import Vue from 'vue' import App from './App.vue' import axios from 'axios' Vue.config.productionTip = false // 全局配置axios请求根路径 axios.defaults.baseURL = 'http://www.liulongbin.top:3006' // 将axios挂载到Vue的原型对象上,供每个.vue实例直接使用 // 今后在每个vue组件中发起请求,直接调用this.$http Vue.prototype.$http = axios new Vue({ render: h => h(App), a() {} }).$mount('#app')
- 缺点:无法实现API接口的复用
(六)路由
路由(router):就是对应关系(Hash地址与组件的对应关系)
- SPA与前端路由
- SPA指的是一个web网站只有唯一的一个HTML页面,所有组件的显示和切换都在这唯一的页面内完成。此时,不同组件之间的切换需要通过前端路由来实现。
- 前端路由的工作方式
- 用户点击了页面上的路由链接
- 导致URL地址栏中的Hash值发生了变化
- 前端路由监听到了Hash地址的变化
- 前端路由把当前Hash地址对应的组件渲染到浏览器中
- 手动模拟简单的前端路由
- 通过
标签,结合comName动态渲染组件
- 通过
<template> <div id="demoApp"> <h1>App 根组件h1> <hr /> <div class="choose-bar"> <a href="#/home">首页a> <a href="#/movie">电影a> <a href="#/about">关于a> div> <hr /> <component :is="comName"> component> div> template> <script> import Home from '@/components/Home.vue' import Movie from '@/components/Movie.vue' import About from '@/components/About.vue' export default { data() { return { // 在动态组件的位置要展示的组建的名字必须要是字符串 comName: 'Home' } }, components: { Home, Movie, About }, created() { location.hash = '#/home' // 只要当前的app组件一被创建,就立即监听window对象的onhashchange事件 window.onhashchange = () => { switch (location.hash) { case '#/home': this.comName = 'Home' break case '#/movie': this.comName = 'Movie' break case '#/about': this.comName = 'About' break } } } } script> <style> #demoApp { min-height: 300px; padding: 0px 8px 8px 8px; background-color: #ececec; overflow: hidden; } .choose-bar a { display: inline-block; margin-right: 10px; height: 30px; line-height: 30px; } style>
- 安装和配置路由
vue-router是由vue.js官方给出的路由解决方案,它只能结合vue项目进行使用,能够轻松管理SPA项目中组件的切换
-
- vue-router安装和配置的步骤
- 安装vue-router包:npm i vue-router@3.5.2 -s
- src/router/index.js:创建路由模块
- 导入并挂载路由模块
// src/router/index.js 就是当前项目的路由模块 // 1.导入vue和vue-router包 import vue from "vue" import VueRouter from "vue-router" // 2.调用Vue.use()函数,把VueRouter安装为Vue插件 vue.use(VueRouter) // 3.创建路由的实例对象 const router = new VueRouter // 4.向外共享路由的实例对象 export default router
-
- 声明路由链接和占位符
// main.js // 导入路由模块,拿到路由的实例对象 import router from './router/index.js' new Vue({ render: h => h(App), // 在Vue项目中,要想把路由用起来,必须把路由实例对象,通过下面的方式进行挂载 // router:路由的实例对象 router }).$mount('#app')
-
- 在路由模块中声明路由的对应关系
// src/router/index.js 就是当前项目的路由模块 // 1.导入vue和vue-router包 import vue from 'vue' import VueRouter from 'vue-router' import Home from '../components/Home.vue' import Movie from '../components/Movie.vue' import About from '../components/About.vue' // 2.调用Vue.use()函数,把VueRouter安装为Vue插件 vue.use(VueRouter) // 3.创建路由的实例对象 const router = new VueRouter({ // routes是一个数组,作用:定义hash定制与组件之间的对应关系 routes: [ { path: '/home', component: Home }, { path: '/movie', component: Movie }, { path: '/about', component: About } ] }) // 4.向外共享路由的实例对象 export default router
-
- 使用router-link代替a链接
<div class="choose-bar"> <router-link to="/home">首页router-link> <router-link to="/movie">电影router-link> <router-link to="/about">关于router-link> div>
-
- redirect重定向
路由重定向指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。通过路由规则指定的redirect属性,制 定一个新的路由地址,可以方便地设置路由的重定向
routes: [
{ path: '/', redirect: Home }
]
- 嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由
-
- 点击父连接显示模板内容
- 模板内容中又有子级路由链接
- 点击子级路由链接显示子级模板内容
<template> <div class="container-movie"> <h4>Movie 组件h4> <router-link to="/movie/bk">bambirouter-link> <router-link to="/movie/zw">deeprouter-link> <hr> <router-view >router-view> div> template>
// index.js const router = new VueRouter({ // routes是一个数组,作用:定义hash定制与组件之间的对应关系 routes: [ { path: '/', redirect: Home }, { path: '/home', component: Home }, { path: '/movie', component: Movie, // 默认子路由 // redirect: '/movie/bk', // Movie页面的路由规则(父级路由规则) // 通过声明children属性,嵌套声明子级路由规则 children: [ // 子路由规则 // 默认子路由 如果 children数组中,某个路由规则的path为空字符串,则这条路由规则叫做默认子路由 { path: '', component: baekHyun }, { path: 'bk', component: baekHyun }, { path: 'zw', component: zWei } ] }, { path: '/about', component: About } ] })
- 动态路由
动态路由指的是:把Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性。在vue-router中使用英文的冒号来定义路由的 参数项
<div class="container-about">
<h4>About 组件---{{this.$route.params.id}}h4> <button @click="showThis">打印thisbutton> <br /> <router-link to="/about/1">1router-link> <router-link to="/about/2">2router-link> <router-link to="/about/3">3router-link> div>
// index.js:统一定义模板路由链接对应关系 { path: '/about', component: About, children: [ { path: '/about/:id', component: About } ] }
-
- 为路由开启props传参
// index.js { path: '/about/:id', component: About, // 可以为路由规则开启props传参,从而方便地拿到动态参数的值 props: true }About 组件---{{ id }}
-
- 拓展query和fullPath
<template> <div id="demoApp"> <h1>App 根组件h1> <hr /> <div class="choose-bar"> <router-link to="/home">首页router-link> <router-link to="/movie">电影router-link> <router-link to="/about/1?name=bh&age=20">1router-link> <router-link to="/about/2">2router-link> <router-link to="/about/3">3router-link> div> <hr> <router-view>router-view> div> template>
(七)导航
- 声明式导航和编程式导航
- 在浏览器中,点击链接实现导航的方式,叫做声明式导航。
- 例如:普通网页点击a链接,vue项目中点击
都属于声明式导航 - 在浏览器中,调用API实现导航的方式,叫做编程式导航
- 例如:在普通网页调用location.href跳转到新页面的方式,属于编程式导航
- vue-router中的编程式导航API
- this.$router.push('hash地址')
- 跳转到指定的hash地址,并增加一条历史记录
- this.$router.replace('hash地址')
- 跳转到指定的hash地址,并替换掉当前的历史记录
- this.$router.go(数值n)
- 可以在浏览历史中前进或后退
- 便捷写法:
- $router.back():后退
- $router.forward():前进
- this.$router.push('hash地址')
<template> <div class="container-home"> <h4>Home 组件h4> <button @click="goToMovie">跳转到moviebutton> <button @click="replaceToMovie">替换到Moviebutton> <button @click="goNext">前进button> <button @click="goPre">后退button> div> template> <script> export default { name: 'myHome', methods: { goToMovie() { // 通过编程式导航 API,导航跳转到指定的页面 this.$router.push('/movie') }, replaceToMovie() { this.$router.replace('/movie/zw') }, goNext() { this.$router.go(1) }, goPre() { this.$router.go(-1) } } } script> <style> .container-home { min-height: 200px; background-color: lightsalmon; overflow: hidden; } style>
- 导航守卫
导航守卫可以控制路由的访问权限
-
- 全局前置守卫:每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限控制
// src/router/index.js 就是当前项目的路由模块 // 1.导入vue和vue-router包 import vue from 'vue' import VueRouter from 'vue-router' import Home from '../components/Home.vue' import Movie from '../components/Movie.vue' import About from '../components/About.vue' import baekHyun from '../components/baekHyun.vue' import zWei from '../components/zWei.vue' import Main from '../components/Main.vue' import Login from '../components/Login.vue' // 2.调用Vue.use()函数,把VueRouter安装为Vue插件 vue.use(VueRouter) // 3.创建路由的实例对象 const router = new VueRouter({ // routes是一个数组,作用:定义hash定制与组件之间的对应关系 routes: [ { path: '/', redirect: Home }, { path: '/home', component: Home }, { path: '/movie', component: Movie, // 默认子路由 // redirect: '/movie/bk', // Movie页面的路由规则(父级路由规则) // 通过声明children属性,嵌套声明子级路由规则 children: [ // 子路由规则 // 默认子路由 如果 children数组中,某个路由规则的path为空字符串,则这条路由规则叫做默认子路由 { path: '', component: baekHyun }, { path: 'bk', component: baekHyun }, { path: 'zw', component: zWei } ] }, { path: '/about/:id', component: About, // 可以为路由规则开启props传参,从而方便地拿到动态参数的值 props: true }, { path: '/login', component: Login }, { path: '/main', component: Main } ] }) // 调用路由实例对象的beforeEach方法,即可声明"全局前置守卫" // 每次发生路由跳转的时候,都会自动触发fn这个回调函数 // 全局前置守卫的回调函数包含3个形参 router.beforeEach(function(to, from, next) { // to是将要访问的路由信息对象 // from是将要离开的路由信息对象 // next是一个函数,调用next()表示放行,允许这次路由导航 console.log(to) console.log(from) // next的三种调用方式 // 1.当用户拥有后台主页的访问权限,直接放行:next() // 2.当前用户没有后台主页的访问权限,强制跳转到登录页面:next('/login') // 3.当前用户没有后台主页的访问权限,不允许登录到后台主页:next(false) // 分析 // 1.要拿到用户将要访问的hash地址 // 2.判断hash地址是否等于/mian,如果等于/mian,证明需要登陆之后才能访问成功,如果不等于/mian,则不需要登录直接放行next() // 3.如果访问的地址是/mian,则要读取localStorage,如果有token则放行,如果没有token,则强制跳转到/login登录页面 if (to.path === '/main') { // 要访问后台主页,需要判断是否有token const token = localStorage.getItem('token') if (token) { next() } else { next('/login') } } else { next() } }) // 4.向外共享路由的实例对象 export default router