1.指令
1.1自定义指令
v-html、v-if、v-bind、v-on... 这都是Vue给咱们内置的一些指令,可以直接使用。Vue也支持让开发者,自定义自己的指令。
进行指令注册
//在main.js中全局注册。
Vue.directive('指令名', {
"inserted" (el) { //inserted会在指令所在的元素,被插入到页面时触发。el就是指令所绑定的元素
el.focus() // 可以对el标签,扩展额外功能。这里代表获取焦点。
}
})
//在组件中局部注册,只能在当前组件范围内使用。
directives: {
"指令名": {
inserted () {
el.focus() //可以对el标签,扩展额外功能。这里代表获取焦点。
}
}
}
使用指令
<input type="text" v-指令名/>
1.2.自定义指令的值
自定义指令时,就可以通过“等号”的形式为指令绑定具体的参数值了
<div v-color="color">我是内容</div>
通过 binding.value 可以拿到指令值,指令值修改会触发 update 函数
directives: {
color: {
inserted (el, binding) {
el.style.color = binding.value
},
update (el, binding) { //指令中绑定的参数值发生了改变时。update函数会自动执行
el.style.color = binding.value
}
}
}
1.3.自定义v-loading指令
实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态,用户体验不好。这种情况下,我们就可以封装一个 v-loading 指令,实现加载中的效果。
本质 loading 效果就是一个蒙层,盖在了盒子上。数据请求中,开启loading状态,添加蒙层;数据请求完毕,关闭loading状态,移除蒙层。下面是v-loading指令在组件中的具体实现以及应用。
<template>
<div class="main">
<div class="box" v-loading="isLoading">
<ul>
<li v-for="item in list" :key="item.id" class="news">
<div class="left">
<div class="title">{
{ item.title }}</div>
<div class="info">
<span>{
{ item.source }}</span>
<span>{
{ item.time }}</span>
</div>
</div>
<div class="right">
<img :src="item.img" alt="">
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
// 安装axios => yarn add axios
import axios from 'axios'
// 接口地址:http://hmajax.itheima.net/api/news
// 请求方式:get
export default {
data () {
return {
list: [],
isLoading: true,
isLoading2: true
}
},
async created () {
// 1. 发送请求获取数据
const res = await axios.get('http://hmajax.itheima.net/api/news')
setTimeout(() => {
// 2. 更新到 list 中,用于页面渲染 v-for
this.list = res.data.data
this.isLoading = false
}, 2000)
},
directives: {
loading: {
inserted (el, binding) {
binding.value ? el.classList.add('loading') : el.classList.remove('loading')
},
update (el, binding) {
binding.value ? el.classList.add('loading') : el.classList.remove('loading')
}
}
}
}
</script>
<style>
.loading:before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff url('./loading.gif') no-repeat center;
}
.box {
width: 800px;
min-height: 500px;
border: 3px solid orange;
border-radius: 5px;
position: relative;
}
.news {
display: flex;
height: 120px;
width: 600px;
margin: 0 auto;
padding: 20px 0;
cursor: pointer;
}
.news .left {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-right: 10px;
}
.news .left .title {
font-size: 20px;
}
.news .left .info {
color: #999999;
}
.news .left .info span {
margin-right: 20px;
}
.news .right {
width: 160px;
height: 120px;
}
.news .right img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
2.插槽
插槽可以让通用组件内部的一些 结构 支持 自定义。我们在使用通用组件的时候,如果不想写死组件内的一些结构,这个时候就可以使用插槽。
给插槽传入内容时,可以传入纯文本、html标签、组件。
2.1 默认插槽
公共组件内只有一个地方需要传入外部内容进行定制时,可以使用默认插槽。默认插槽的使用步骤如下:
在通用组件内使用<slot></slot>占位。
<template>
<div class="dialog">
<div class="dialog-header">
<h3>友情提示</h3>
<span class="close">✖️</span>
</div>
<div class="dialog-content">
<slot>您确定要进行删除操作吗?</slot>
</div>
<div class="dialog-footer">
<button>取消</button>
<button>确认</button>
</div>
</div>
</template>
在父组件中,给公共组件传入插槽内容。
<template>
<div>
<MyDialog>你确定要退出本系统吗?</MyDialog>
</div>
</template>
2.2 具名插槽
一个组件内有多处结构,需要外部传入标签,进行定制的话,默认插槽就没用了。这个时候就需要用到具名插槽。比如下面的弹框中有三处不同,但是默认插槽只能定制一个位置。
//公共组件中多个slot使用name属性区分名字
<div class="dialog-header">
<slot name="head"></slot>
</div>
<div class="dialog-content">
<slot></slot>
</div>
<div class="dialog-footer">
<slot name="footer"></slot>
</div>
//父组件的template中配合v-slot:名字来分发对应标签。默认插槽和具名插槽同时存在时,
//无需为默认插槽指定v-slot,内容会自动被传递过去。
<MyDialog>
<template v-slot:head> //v-slot可以简写为#
大标题
</template>
<template>
大标题
</template>
<template v-slot:footer>
<button>按钮</button>
</template>
</MyDialog>
2.3 插槽的默认内容
通过插槽完成了内容的定制,传什么显示什么,但是如果不传,则是空白。这种情况下,可以给插槽设置默认显示内容。
在 <slot> 标签内,放置的内容是默认显示内容。如果父组件传过来内容,则采用父组件的内容;如果父组件没有传过来内容,则采用默认内容。
2.4 作用域插槽
作用域插槽是插槽传参的一个语法,并不属于插槽的分类。作用域插槽就是在定义 slot 插槽的同时,可以给插槽上绑定数据,将来使用组件时可以用。使用步骤如下;
1.给 slot 标签, 以添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
2.所有添加的属性,都会被收集到一个对象中
{ id: 3, msg: '测试文本' }
3.提供给默认插槽的内容需要用 template 标签包裹。然后在 template 标签上通过 #插槽名= "obj" 接收,默认插槽名为 default。这里的<template>标签是为了接收参数而额外加的。
<MyTable :list="list">
<template #default="obj"> //如果是具名插槽。则default替换为插槽的名字。
<button @click="del(obj.id)">删除</button>
</template>
</MyTable>
4.路由
4.1.VueRouter
一般来说,通过Vue开发出的网站就属于单页应用程序。单页应用程序(SPA)是指所有的功能都在一个html页面上实现。它和多页应用程序的主要区别如下。
Vue中的VueRouter是一个路由插件,它明确了路径和组件的对应关系,能够实现单页应用程序的按需更新。
4.2.VueRouter的使用
1. 下载 VueRouter 模块到当前工程
yarn add vue-router@3.6.5
2. main.js中引入VueRouter
import VueRouter from 'vue-router'
3. main.js中进行安装注册
Vue.use(VueRouter)
4. main.js中创建路由对象
const router = new VueRouter()
5. main.js中将路由对象注入到 new Vue 实例中,建立关联
new Vue({
render: h => h(App),
router:router
}).$mount('#app')
当我们配置完以上 5 步之后 就可以看到浏览器地址栏中的路由变成了 /#/ 的形式。表示项目的路由已经被 Vue-Router 管理了。
6. 创建需要的组件,并在main.js的路由对象中配置路由规则
一般创建好的组件是放在views目录,这些组件代表配置路由规则时使用的组件。
import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'
const router = new VueRouter({
routes: [
{ path: '/login', component: Login },
{ path: '/my', component: My},
{ path: '/friend', component: Friend },
]
})
7. 定义导航和路由出口
路由出口就说路径匹配上的组件显示的位置。
<template>
<div>
<!-- 导航栏,用来切换路径。路径切换成功之后,<router-view>处就会展示对应的组件 -->
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a>
</div>
<div class="top">
<!-- 路由出口。放在导航栏下面就代表在它的下面展示 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
4.3.多级路由
在 Vue 中,路由是有多个级别的,可分为以下几级:
一级路由:一级路由通常代表应用的主要页面。但凡是单个页面,独立展示的,都是一级路由。例如,一个网站的主页、登录页、用户页等。
const routes = [
{
path: '/',
component: Home
},
{
path: '/about',
component: About
}
];
二级路由:二级路由通常用于在一个页面内嵌套显示其他页面的内容。可以通过在一个路由的 component 中使用 children 属性来定义子路由。
const routes = [
{
path: '/',
component: Home,
children: [
{
path: 'profile',
component: Profile
},
{
path: 'settings',
component: Settings
}
]
}
];
三级及更深层次的路由:Vue 路由本身支持无限层级的嵌套路由,所以可以继续在子路由下嵌套子路由,从而实现三级或更深的路由。
const routes = [
{
path: '/',
component: Home,
children: [
{
path: 'profile',
component: Profile,
children: [
{
path: 'edit',
component: ProfileEdit
}
]
}
]
}
];
4.4.路由配置的抽离
上述的路由配置都放在了main.js中,这样在配置过多时,非常不利于维护,可以将路由相关的配置单独抽取出来。具体步骤如下:
1.在项目的src目录下新建一个router目录,在目录中创建一个js文件放路由相关的配置
import Vue from 'vue'
import VueRouter from 'vue-router'
import Find from '../views/Find'
import My from '../views/My'
import Friend from '../views/Friend'
Vue.use(VueRouter) // VueRouter插件初始化
const router = new VueRouter({
// routes 路由规则们
// route 一条路由规则 { path: 路径, component: 组件 }
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend },
]
})
export default router
2.在main.js文件中引入VueRouter对象即可
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'
Vue.config.productionTip = false
Vue.use(VueRouter) // VueRouter插件初始化
new Vue({
render: h => h(App),
router
}).$mount('#app')
4.5.声明式导航
也叫导航链接。如果使用 a 标签进行跳转的话,需要给当前跳转的导航加样式,同时要移除上一个a标签的样式,太麻烦。vue-router 提供了一个全局组件 router-link,可以用来取代 a 标签。router-link 本质就是对 a 标签的增强。
增强的内容如下:
能跳转,配置 to 属性指定路径,路径中无需带上 #。
选中哪个<router-link>标签,这个标签就会被添加二个类名(router-link-exact-active和router-link-active),可以根据这二个类名用CSS为其设置样式。
<div>
<div class="footer_wrap">
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/friend">朋友</router-link>
</div>
<div class="top">
<!-- 路由出口 → 匹配的组件所展示的位置 -->
<router-view></router-view>
</div>
</div>
二个类名的区别
router-link-active(模糊匹配):若选中的路径是 /find,则除了 /find 之外,以 /find 开头的<router-link>标签也都会被添加上该类名。比如 /find/one、 /find/two。
router-link-exact-active(模糊匹配):若选中的路径是 /find、则只有 /find 对应的 <router-link> 标签才会被添加上该类名。
修改这二个类名
如果嫌这二个类名太长,可以在router对象中进行配置。
const router = new VueRouter({
// routes 路由规则们
// route 一条路由规则 { path: 路径, component: 组件 }
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend },
]
// router-link标签中的类名修改
router-link-exact-active: 'active', //配置模糊匹配的类名
router-link-active: 'exact-active' //配置精确匹配的类名,前面要加上exact-
})
4.6.路由传参
我们可以通过两种方式,在跳转的时候把所需要的参数传到其它页面中。
1.查询参数传参(适合传递多个参数)
传递参数:
<router-link to="/path?参数名=值"></router-link>
接收参数:
//在 html 标签中
$router.query.参数名
//在 js 代码中
this.$router.query.参数名
2.动态路由传参(适合传递单个参数)
在router对象中配置路径参数
const router = new VueRouter({
routes: [
...,
{
path: '/search/:words', //":words"就是配置的内容
component: Search
}
]
})
传递参数:
<router-link to="to="/path/参数值"></router-link>
接收参数:
//在html标签中
$route.params.参数名
//在js代码中
this.$route.params.参数名
动态路由存在的一个小问题
配了路由 path:"/search/:words" 为什么按下面步骤操作,会未匹配到组件,显示空白?
/search/:words 表示,必须要传参数,如果不传,是无法匹配到组件的。如果不传参数也希望匹配,加个可选符 "?"就好了。
const router = new VueRouter({
routes: [
...
{ path: '/search/:words?', component: Search }
]
})
4.7.路由重定向
路由重定向指的是当访问某个特定的路由时,自动重定向到另一个路由。
当我们访问一个网站时, url 默认路径是" / "," / "这个路径不会匹配到任何组件,页面就会出现空白。这种情况可以利用路由重定向来解决,在匹配到路径" / "后,强制跳转到主页。.
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home'},
...
]
})
4.8.路由跳转
path路径跳转:简易方便
//在标签里进行跳转
<button @click="$router.push('/路径?参数名1=参数值1&参数2=参数值2')">Go to Home</button>
//在js代码里进行跳转
//第一种
//简单写法
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
//完整写法
this.$router.push({
path: '/路径',
query: {
参数名1: '参数值1',
参数名2: '参数值2'
}
})
//跳转后接收参数的方式
$route.query.参数名
//第二种
/简单写法
this.$router.push('/路径/参数值')
//完整写法
this.$router.push({
path: '/路径/参数值'
})
//跳转后接收参数的方式
$route.params.参数值
name命名路由跳转:适合 path 路径长的场景。
//提前在路由对象中配置
const router = new VueRouter({
routes: [
{
name: 'search',
path: '/search/:words',
component: Search
}
]
})
//在标签里进行跳转
<button @click="$router.push('/路径?参数名1=参数值1&参数2=参数值2')">Go to Home</button>
//在js代码里进行跳转
//第一种
//简单写法
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
//完整写法
this.$router.push({
name: '路由名字', //该方式需要定义name属性
query: {
参数名1: '参数值1',
参数名2: '参数值2'
}
})
//跳转后接收参数的方式
$route.query.参数名
//第二种
//简单写法
this.$router.push('/路径/参数值')
//完整写法
this.$router.push({
name: '路由名字',
params: {
参数名: '参数值', //这里的参数名要和路由配置中的路径参数名对应
}
})
//跳转后接收参数的方式
$route.param.参数名
4.9.导航守卫
我们开发的项目中,有些页面是不需要登录就可以访问的;有些页面登录后才可以访问,如果用户未登录,就需要提示用户并跳转到登录页。这个时候就可以用到 Vue Router 的导航守卫。
找到路由对象所在的 js 文件,在文件中调用路由对象的全局前置守卫方法 beforeEach。这样,在浏览器中每访问一个URL,都会经过 beforeEach 的检查。
const authUrl = ['/pay', '/myorder']
// 1. to 往哪里去, 到哪去的路由信息对象
// 2. from 从哪里来, 从哪来的路由信息对象
// 3. next() 是否放行
// 如果next()调用,就是放行
// next(路径) 拦截到某个路径页面
router.beforeEach((to, from, next) => {
const token = store.getters.token
if (!authUrl.includes(to.path)) {
next()
return
}
if (token) {
next()
} else {
next('/login')
}
})
4.10 路由解决的一些小问题
404的解决:当路径找不到匹配时,可以给个提示页面,可以通过如下所示的路由规则跳转到提示页面。该路由规则一般配置在路由规则的最后面。
import NotFind from '@/views/NotFind'
const router = new VueRouter({
routes: [
...
{ path: '*', component: NotFind } //*代表匹配所有,NotFind是404后要跳转的页面(.vue文件)
]
})
路径中#的去除:路由的路径中有"#"看起来不自然,我们可以对路由模式进行切换。
hash路由:例如: http://localhost:8080/#/home。默认使用的就是这一种。
history路由:例如: http://localhost:8080/home。这个模式去掉了"#"。但是,需要服务器端进行对应的支持。
const router = new VueRouter({
mode:'histroy', //默认是hash
routes:[]
})
5.缓存组件
从面经列表点到详情页,再点返回,发现数据重新加载了,而我们希望回到原来的位置。这是因为路由跳转后,原来所看到的组件就被销毁了,重新返回后组件会被重新创建,所以数据也会被重新加载。解决方案:利用keep-alive把原来的组件给缓存下来。
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。同时,减少加载时间及性能消耗,提高用户体验性。
<template>
<div class="h5-wrapper">
<keep-alive :include="['LayoutPage']"> //只有LayoutPage.vue组件会被缓存
<router-view></router-view>
</keep-alive>
</div>
</template>
keep-alive标签中可以配置三个属性
include : 组件名数组,只有匹配的组件会被缓存
exclude : 组件名数组,任何匹配的组件都不会被缓存
max : 最多可以缓存多少组件实例
keep-alive额外的两个生命周期钩子
组件缓存后就不会执行组件的 created, mounted, destroyed 等钩子了,所以其提供了actived 和deactived钩子,keep-alive使用的时候这二个钩子会自动触发,帮我们实现业务需求。
activated:当回到被缓存的组件时触发
deactivated:当离开被缓存的组件时触发
6.ESlint
ESLint:是一个代码检查工具,用来检查你的代码是否符合指定的规则,你和你的团队可以自行约定一套规则。在创建项目时,我们使用的是 JavaScript Standard Style 代码风格的规则。
1.JavaScript Standard Style 规范说明
建议把:JavaScript Standard Style 看一遍,然后在写的时候, 遇到错误就查询解决。下面是这份规则中的一小部分:
字符串使用单引号 – 需要转义的地方除外
关键字后加空格 if (condition) { ... }
函数名后加空格 function name (arg) { ... }
坚持使用全等 === 摒弃 == 一但在需要检查 null || undefined 时可以使用 obj == null
.....
2.代码规范错误
如果你的代码不符合standard的要求,eslint会跳出来刀子嘴,豆腐心地提示你。下面我们在main.js中随意做一些改动:添加一些空行,空格。
import Vue from 'vue'
import App from './App.vue'
import './styles/index.less'
import router from './router'
Vue.config.productionTip = false
new Vue ( {
render: h => h(App),
router
}).$mount('#app')
按下保存代码之后:你将会看在控制台中输出如下错误:
3.手动修正
根据错误提示来一项一项手动修正。
如果你不认识命令行中的语法报错是什么意思,你可以根据错误代码去 ESLint 规则列表中查找其具体含义。打开 ESLint 规则表,使用页面搜索(Ctrl + F)这个代码,查找对该规则的一个释义。
4.自动修正
1.在VSCode中安装ESlint插件
2.在VSCode的设置里进行配置
// 当保存的时候,eslint自动帮我们修复错误
"editor.codeActionsOnSave": {
"source.fixAll": true
},
// 保存代码,不自动格式化
"editor.formatOnSave": false