VUE2
动态组件
动态组件指的是 动态切换组件的显示与隐藏 。
vue提供了一个内置的 <component>
组件, 专门用来实现动态组件的渲染 。
<button @click="comName = 'Left'">展示 Left</button>
<button @click="comName = 'Right'">展示 Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1. component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2. is 属性的值,表示要渲染的组件的名字 -->
<!-- 3. is 属性的值,应该是组件在 components 节点下的注册名称 -->
<!-- keep-alive 会把内部的组件进行缓存,而不是销毁组件 -->
<!-- 在使用 keep-alive 的时候,可以通过 include 指定哪些组件需要被缓存; -->
<!-- 或者,通过 exclude 属性指定哪些组件不需要被缓存;但是:不要同时使用 include 和 exclude 这两个属性 -->
<keep-alive exclude="MyRight">
<component :is="comName"></component>
</keep-alive>
</div>
export default {
data() {
return {
// comName 表示要展示的组件的名字
comName: 'Left'
}
},
components: {
// 如果在“声明组件”的时候,没有为组件指定 name 名称,则组件的名称默认就是“注册时候的名称”
Left,
Right
}
}
keep-alive
默认情况下,切换动态组件时无法保持组件的状态 。此时可以使用 vue 内置的 <keep keep-alive>
组 件保持动态件的状态。
keep-alive 对应的生命周期函数
- 当组件 被缓存 时,会自动触发组件的 deactivated 生命周期函数。
- 当组件 被激活 时,会自动触发组件的 activated生命周期函数。
- 当组件第一次被创建的时候,既会执行 created 生命周期,也会执行 activated 生命周期
- 当组件被激活的时候,只会触发 activated 生命周期,不再触发 created。因为组件没有被重新创建
keep-alive 的 include 属性
include属性用来指定只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号分隔
include 使该标签作用于所有name属性的值跟此标签 include的属性值一致的vue页面
<!-- 在使用 keep-alive 的时候,可以通过 include 指定哪些组件需要被缓存; -->
<!-- 或者,通过 exclude 属性指定哪些组件不需要被缓存;但是:不要同时使用 include 和 exclude 这两个属性 -->
<keep-alive exclude="MyLeft">
<component :is="comName"></component>
</keep-alive>
...
export default {
name: 'MyLeft',
当提供了 name 属性之后,组件的名称,就是 name 属性的值
- 组件的 “注册名称” 的主要应用场景是:以标签的形式,把注册好的组件,渲染和使用到页面结构之中
- 组件声明时候的 “name” 名称的主要应用场景:结合
<keep-alive>
标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称
插槽
插槽 (SlotSlot)是 vue 为组件的封装者 提供的能力。允许开发者在封装组件时,把不确定的 、希望由用户指定的部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的
在封装组件时,可以通过<slot>
元素 定义插槽 ,从而 为用户预留内容占位符 。
如果在封装组件时 没有预留任何 <slot>
插槽 ,则用户提供的任何 自定义内容 都会被丢弃
<div class="header-box">
<slot name="title"></slot>
</div>
v-slot简写形式是#
后备内容
封装组件时,可以为预留的<slot>
插槽提供 后备内容 (默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。
<!-- 声明一个插槽区域 -->
<!-- vue 官方规定:每一个 slot 插槽,都要有一个 name 名称 -->
<!-- 如果省略了 slot 的 name 属性,则有一个默认名称叫做 default -->
<slot name="default">
<h6>这是 default 插槽的后备内容</h6>
</slot>
具名插槽
如果在封装组件时 需要预留多个插槽节点 ,则需要为每个 <slot>
插槽指定 具体的 name 名称 。这种带有具体名称的插槽叫做“具名插槽”。
<template #author>
<div>作者</div>
</template>
作用域插槽
在封装组件的过程中,可以为预留的<slot>
插槽绑定 props 数据,这种 带有 props 数据的 <slot>
叫做“ 作用 域插槽 ”。
可以使用 v-slot: 的形式,接收作用域插槽对外提供的数据。
<!-- 文章的内容 -->
<div class="content-box">
<!-- 在封装组件时,为预留的 <slot> 提供属性对应的值,这种用法,叫做 “作用域插槽” -->
<slot name="content" msg="hello vue.js" :user="userinfo"></slot>
</div>
...
<template #content="{ msg, user }">
<div>
<p>啊,大海,全是水。</p>
<p>啊,蜈蚣,全是腿。</p>
<p>啊,辣椒,净辣嘴。</p>
<p>{{ msg }}</p>
<p>{{ user.name }}</p>
</div>
</template>
解构插槽Prop
作用域插槽对外提供的数据对象,可以使用解构赋值 简化数据的接收过程。
自定义指令
vue官方提供了 v-text 、v-for 、v-model 、v-if 等常用的指令。除此之外 vue 还允许开发者自定义指令。
vue中的自定义指令分为两类,分别是:
- 私有 自定义指令
- 全局 自定义指令
// 私有自定义指令的节点
directives: {
// 定义名为 color 的指令,指向一个配置对象
/* color: {
// 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
// 形参中的 el 表示当前指令所绑定到的那个 DOM 对象
bind(el, binding) {
console.log('触发了 v-color 的 bind 函数')
el.style.color = binding.value
},
// 在 DOM 更新的时候,会触发 update 函数
update(el, binding) {
console.log('触发了 v-color 的 update 函数')
el.style.color = binding.value
}
} */
color(el, binding) {
el.style.color = binding.value
}
}
私有自定义指令
在每个 vue 组件中,可以在 directives 节点下声明 私有自定义指令。
在使用自定义指令时,需要加上v-前缀。
动态绑定参数值
在 template 结构中 使用自定义指令 时,可以通过等号(=)的方式,为当前指令 动态绑定参数值
通过 binding 获取指令的参数值
在声明自定义指令时,可以通过形参中的第二个参数 ,来接收指令的参数值
update函数
bind函数 只调用 1 次:当指令第一次绑定到元素时调用 当 DOM 更新 时 bind 函数不会被触发 。 update 函 数会在 每次 DOM 更新 时被调用。
函数简写
如果 insert 和update 函数中的 逻辑完全相同 ,则对象格式 的自定义指令可以 简写 成函数格式 :
全局自定义指令
全局共享的自定义指令需要通过 “Vue.directive() directive()”进行声明
// 全局自定义指令
Vue.directive('color', {
bind(el, binding) {
el.style.color = binding.value
},
update(el, binding) {
el.style.color = binding.value
}
})
axios的使用
// 全局配置 axios 的请求根路径
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用
Vue.prototype.$http = axios
// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$http.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!
路由
路由(英文: router)就是 对应关系
前端路由
SPA
SPA指的是一个 web 网站只有唯一的个 HTML 页面, 所有组件的展示与切换 都在这唯一的一个页面内完成。此时,不同组件之间的切换 需要通过 前端路由 来实现。
结论:在 SPA 项目中,不同功能之间的切换 ,要依赖于前端路由 来完成!
工作方式
① 用户 点击了 页面上的 路由链接
② 导致了 URL 地址栏 中的 Hash 值发生了变化
③ 前端路由监听了到 Hash 地址的变化
④ 前 端路由把当Hash 地址对应的组件 渲染都浏览器中
Hash 地址与组件之间的对应关系
实现
created() {
// 只要当前的 App 组件一被创建,就立即监听 window 对象的 onhashchange 事件
window.onhashchange = () => {
console.log('监听到了 hash 地址的变化', location.hash)
switch (location.hash) {
case '#/home':
this.comName = 'Home'
break
case '#/movie':
this.comName = 'Movie'
break
case '#/about':
this.comName = 'About'
break
}
}
}
vue-router
vue-router是vue.js 官方给出的 路由解决方案。它只能结合vue项目进行使用,能够轻松的管理 SPA 项目中组件的切换 。
安装和配置
① 安装 vue -router 包
npm i vue-router@3.5.2 -S
② 创建路由模块
// 1.导入Vue和VueRouter的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 2.把 VueRouter 安装为 Vue 项目的插件,Vue.use() 函数的作用,就是来安装插件的
Vue.use(VueRouter)
// 3.创建实例对象
const router = new VueRouter()
// 4.向外导出实例对象
export default router
③ 导入并挂载路由模块
// 导入路由模块,目的:拿到路由的实例对象
// 在进行模块化导入的时候,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做 index.js 的文件
import router from '@/router'
new Vue({
render: h => h(App),
// 在 Vue 项目中,要想把路由用起来,必须把路由实例对象,通过下面的方式进行挂载
// router: 路由的实例对象
router
}).$mount('#app')
④ 声明路由链接和占位符
// 导入需要的组件
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'
import Tab1 from '@/components/tabs/Tab1.vue'
import Tab2 from '@/components/tabs/Tab2.vue'
import Login from '@/components/Login.vue'
import Main from '@/components/Main.vue'
// 创建路由的实例对象
const router = new VueRouter({
// routes 是一个数组,作用:定义 “hash 地址” 与 “组件” 之间的对应关系
routes: [
// 重定向的路由规则
{ path: '/', redirect: '/home' },
// 路由规则
{ path: '/home', component: Home },
// 需求:在 Movie 组件中,希望根据 id 的值,展示对应电影的详情信息
// 可以为路由规则开启 props 传参,从而方便的拿到动态参数的值
{ path: '/movie/:mid', component: Movie, props: true },
{
path: '/about',
component: About,
// redirect: '/about/tab1',
children: [
// 子路由规则
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
},
{ path: '/login', component: Login },
{ path: '/main', component: Main }
]
})
//=======================================App.vue
<!-- 当安装和配置了 vue-router 后,就可以使用 router-link 来替代普通的 a 链接了 -->
<!-- <a href="#/home">首页</a> -->
<router-link to="/home">首页</router-link>
<!-- 注意1:在 hash 地址中, / 后面的参数项,叫做“路径参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.params 来访问路径参数 -->
<!-- 注意2:在 hash 地址中,? 后面的参数项,叫做“查询参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.query 来访问查询参数 -->
<!-- 注意3:在 this.$route 中,path 只是路径部分;fullPath 是完整的地址 -->
<!-- 例如: -->
<!-- /movie/2?name=zs&age=20 是 fullPath 的值 -->
<!-- /movie/2 是 path 的值 -->
<router-link to="/movie/1">洛基</router-link>
<router-link to="/movie/2?name=zs&age=20">雷神</router-link>
<router-link to="/movie/3">复联</router-link>
<router-link to="/about">关于</router-link>
<!-- 只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了 -->
<!-- 它的作用很单纯:占位符 -->
<router-view></router-view>
常见用法
路由重定向
路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址C 从而展示特定的组件页面。通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向
// 重定向的路由规则
{ path: '/', redirect: '/home' },
// 路由规则
{ path: '/home', component: Home },
嵌套路由
声明子路由链接和子路由占位符
<!-- 子级路由链接 -->
<router-link to="/about">tab1</router-link>
<router-link to="/about/tab2">tab2</router-link>
<hr />
<!-- 子级路由占位符 -->
<router-view></router-view>
通过children属性声明子路由规则
{
path: '/about',
component: About,
// redirect: '/about/tab1',
children: [
// 子路由规则
// 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
}
动态路由匹配
动态路由指的是:把 Hash 地址中 可变的部分 定义为 参数项 ,从而 提高路由规则的复用性 。在 vue-router 中使用 英文的冒号 (:)来定义路由的参数项。
$route.params参数对象
// 需求:在 Movie 组件中,希望根据 id 的值,展示对应电影的详情信息
// 可以为路由规则开启 props 传参,从而方便的拿到动态参数的值
{ path: '/movie/:mid', component: Movie, props: true },
//=======================================
<h3>Movie 组件 --- {{ $route.params.mid }} --- {{ mid }}</h3>
在动态路由 渲染 出来的组件中,可以使用this.$route.params
对象访问到 动态匹配的参数值 。
使用 props 接收路由参数
为了简化路由参数的获取形式,vuevue-router 允许在路由规则中开启 props 传参。
{ path: '/movie/:mid', component: Movie, props: true },
path只是路径部分;fullpath是完整的地址
声明式导航 & 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航 。例如:
普通网页中点击<a>
链接 、vue 项目中点击<router router-link>
都属于声明式 导航
在浏览器中,调用 API 方法 实现 导航的方式,叫做编程式导航。例如:
普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
vue-router 中的编程式导航 API
vue-router 提供了许多编程式导航的 APIAPI,其中最常用 的导航 API 分别是:
① this.$router.push
('hash 地址 ')
跳转到指定 hash 地址,并增加一条历史记录
gotoLk() {
// 通过编程式导航 API,导航跳转到指定的页面
this.$router.push('/movie/1')
},
② this.$router.replace
('hash 地址 ')
跳转到指定的 hash 地址,并替换掉当前的历史记录
gotoLk2() {
this.$router.replace('/movie/1')
}
③ this.$router.go
(数值 n)
实现导航历史前进、后退
goback() {
// go(-1) 表示后退一层
// 如果后退的层数超过上限,则原地不动
this.$router.go(-1)
}
在实际开发中,一般只会前进和后退一层页面。因此
vue-router 提供了如下两个便捷方法:
① $ back()
在历史记录中, 后退 到上一个页面
② $ forward()
在历史记录中, 前进 到下一个页面
导航守卫
导航守卫 可以 控制路由的访问权限 。示意图如下:
全局前置守卫
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制
// 为 router 实例对象,声明全局前置导航守卫
// 只要发生了路由的跳转,必然会触发 beforeEach 指定的 function 回调函数
router.beforeEach(function(to, from, next) {
守卫方法的3个形参
全局前置守卫的回调函数中接收 3 个
- to 表示将要访问的路由的信息对象
- from 表示将要离开的路由的信息对象
- next() 函数表示放行的意思
next 函数的 3 种调用方式
当前用户拥有后台主页的访问权限,直接放行:next()
当前用户没有后台主页的访问权限, 强制其跳转到登录页面 :next(‘/login’)
当前用户没有后台主页的访问权限, 不允许跳转到后台主页 :next(false)
router.beforeEach(function(to, from, next) {
// to 表示将要访问的路由的信息对象
// from 表示将要离开的路由的信息对象
// next() 函数表示放行的意思
// 分析:
// 1. 要拿到用户将要访问的 hash 地址
// 2. 判断 hash 地址是否等于 /main。
// 2.1 如果等于 /main,证明需要登录之后,才能访问成功
// 2.2 如果不等于 /main,则不需要登录,直接放行 next()
// 3. 如果访问的地址是 /main。则需要读取 localStorage 中的 token 值
// 3.1 如果有 token,则放行
// 3.2 如果没有 token,则强制跳转到 /login 登录页
if (to.path === '/main') {
// 要访问后台主页,需要判断是否有 token
const token = localStorage.getItem('token')
if (token) {
next()
} else {
// 没有登录,强制跳转到登录页
next('/login')
}
} else {
next()
}
})