Vue-5
(一)动态组件
- 动态组件就是动态切换组件的显示和隐藏:vue提供了一个内置的<component>组件,专门用来实现动态组件的渲染
<!-- 1.component标签是vue内置的,作用:组件的占位符 --> <!-- 2.is属性的值,表示要渲染的组件的名字 --> <!-- 3.is属性的值,应该是组件在components节点下注册的名称 --> <component :is="comName"></component>
- keep-alive缓存组件
<!-- keep-alive可以把内部的组件进行缓存,而不是销毁组件 --> <!-- keep-alive对应的生命周期 --> <!-- 当组件被缓存时,会自动触发组件的deactivted生命周期函数 --> <!-- 当组价被激活时,会自动触发组件的activated生命周期函数 -->
<!-- keep-active属性用来指定:只有名称匹配的组件会被缓存,多个组件名之间用英文的逗号,分隔 --> <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可以把内部的组件进行缓存,而不是销毁组件 --> <!-- keep-alive对应的生命周期 --> <!-- 当组件被缓存时,会自动触发组件的deactivted生命周期函数 --> <!-- 当组价被激活时,会自动触发组件的activated生命周期函数 --> <!-- keep-active属性用来指定:只有名称匹配的组件会被缓存,多个组件名之间用英文的逗号,分隔 --> <!-- keep-alive属性还包含exclude指定不会被缓存的组件 --> <!-- 注意:incude和exclude只能包含一个,不能同时使用 --> <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+1</button> </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的简写为#
<!--子组件 Right.vue--> <div class="right"> <h4>Right组件</h4> <!-- 声明一个插槽 --> <!-- vue官方规定每一个slot插槽,都要有一个name名称 --> <!-- 如果省略了slot的name属性,则有一个默认的名字叫做default --> <slot name="default">这是default插槽的默认内容</slot> </div>
<!-- 父组件 App.vue --> <div class="box"> <Right> <!-- 默认情况下,在使用的组件的时候提供的内容都会被填充到名字为default的插槽中 --> <!-- 1.如果要把内容填充到指定名称的插槽中,需要v-slot: 这个指令 --> <!-- 2.v-slot:后面要跟上插槽的名字 --> <!-- 3.v-slot:指令不能直接用在元素身上,必须用在template标签上 --> <!-- 4.template这个标签,它是一个虚拟的标签,只起到包裹性质的作用,但是,不会被渲染为任何实质性的html元素 --> <!-- 5.v-slot:指令的简写形式是# --> <template #default> <p>我放个p</p> </template> </Right> </div>
- 具名插槽的定义和使用
<!-- 父组件App.vue --> <template #content> <div> <p>锦瑟无端五十弦</p> <p>一弦一柱思华年</p> </div> </template> <!-- 子组件Article.vue --> <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>
<!-- 父组件App.vue --> <!-- 文章的内容 --> <div class="content-box"> <!-- 在封装组件时,为预留的slot提供属性对应的值,这种用法叫做作用域插槽 --> <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地址对应的组件渲染到浏览器中
- 手动模拟简单的前端路由
- 通过<component>标签,结合comName动态渲染组件
<!-- 父组件 App.vue --> <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链接
<!-- 当安装和配置vue-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 }
]
- 嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由
-
- 点击父连接显示模板内容
- 模板内容中又有子级路由链接
- 点击子级路由链接显示子级模板内容
<!-- 子组件Movie.vue --> <template> <div class="container-movie"> <h4>Movie 组件</h4> <router-link to="/movie/bk">bambi</router-link> <router-link to="/movie/zw">deep</router-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中使用英文的冒号来定义路由的 参数项
<!-- About.vue:包含不必要连续路由地址 --> <div class="container-about">
<!-- 获取路由地址的"参数对象" -->
<!-- this.$route是路由的"参数对象" -->
<!-- this.$router是路由的"导航对象" --> <h4>About 组件---{{this.$route.params.id}}</h4> <button @click="showThis">打印this</button> <br /> <router-link to="/about/1">1</router-link> <router-link to="/about/2">2</router-link> <router-link to="/about/3">3</router-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.vue --> <template> <div class="container-about"> <h4>About 组件---{{ id }}</h4> <button @click="showThis">打印this</button> </div> </template> <script> export default { name: 'myAbout', // 接收props数据 props: ['id'], methods: { showThis() { console.log(this) } } } </script>
-
- 拓展query和fullPath
<!-- 父组件 App.vue --> <template> <div id="demoApp"> <h1>App 根组件</h1> <hr /> <!-- 当安装和配置vue-router之后,就可以使用router-link来替代普通的a链接了 --> <div class="choose-bar"> <!-- 注意1:在hash地址中,/后面的参数项叫做路径参数 --> <!-- 在路由“参数对象”中,需要使用this.$route来访问参数路径 --> <!-- 注意2:在hash地址中,?后面的参数项叫做查询参数 --> <!-- 在路由“参数对象”中,需要使用this.$route.query来访问查询参数 --> <!-- 注意3:在this.$route中,path只是路径部分,fullPath是完整的地址 --> <router-link to="/home">首页</router-link> <router-link to="/movie">电影</router-link> <router-link to="/about/1?name=bh&age=20">1</router-link> <router-link to="/about/2">2</router-link> <router-link to="/about/3">3</router-link> </div> <hr> <!-- 只要在项目中安装和配置了vue-router,就可以使用router-view这个组件 --> <!-- 它的作用很单纯:占位符 --> <router-view></router-view> </div> </template>
(七)导航
- 声明式导航和编程式导航
- 在浏览器中,点击链接实现导航的方式,叫做声明式导航。
- 例如:普通网页点击a链接,vue项目中点击<router-link>都属于声明式导航
- 在浏览器中,调用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">跳转到movie</button> <button @click="replaceToMovie">替换到Movie</button> <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

浙公网安备 33010602011771号