自定义指令
全局注册指令
- 文件路径:src/main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 全局注册指令
Vue.directive('myFocus', {
// inserted 会在指令所在的元素,被插入到页面中时触发
inserted(el) {
el.focus() // el 就是指令所绑定的元素
}
})
new Vue({
render: c => c(App)
}).$mount('#app')
- 文件路径:src/App.vue
<template>
<div>
<h1>自定义指令</h1>
<input v-myFocus type="text" />
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
局部注册指令
<template>
<div>
<h1>自定义指令</h1>
<input v-myFocus type="text" />
</div>
</template>
<script>
export default {
// 局部注册指令
directives: {
// 指令名称: 指令的配置项
myFocus: {
inserted(el) {
el.focus();
},
},
},
};
</script>
<style>
</style>
指令的值
<template>
<div>
<h1 v-myColor="color1">指令的值</h1>
<h1 v-myColor="color2">指令的值</h1>
</div>
</template>
<script>
export default {
directives: {
// 指令名称 : 指令的配置项
myColor: {
// 1. inserted 提供的是元素被添加到页面中时的逻辑
inserted(el, binding) {
// binding.value 即可获取"指令的值"
el.style.color = binding.value;
},
// 2. update 指令的值修改的时候触发,提供的值变化后,dom 更新的逻辑
update(el, binding) {
el.style.color = binding.value;
},
},
},
data() {
return {
color1: "red",
color2: "blue",
};
},
};
</script>
<style>
</style>
实现加载效果
目的:实现自定义指令——v-loading
指令封装
实现:
- 准备一个 loading 类,通过伪元素定位,设置宽高,实现蒙层
- 开启关闭 loading 状态(添加移除蒙层),本质只需要添加移除类即可
- 结合自定义指令的语法进行封装复用
代码:
<template>
<div v-loading="isLoading">
<ul v-for="item in list" :key="item.id">
<hr />
<li><img :src="item.img" alt="" /></li>
<li>{{ item.title }}</li>
<li>{{ item.source }}</li>
<li>{{ item.time }}</li>
</ul>
<hr />
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
list: [],
isLoading: true,
};
},
async created() {
// 1. 发送请求,获取数据
const res = await axios.get("http://hmajax.itheima.net/api/news");
setTimeout(() => {
// 2. 更新到list中,用于页面渲染
this.list = res.data.data;
// 3. 移除loading效果
this.isLoading = false;
}, 3000); // 定时3秒,模拟网速慢
},
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("https://pic4.zhimg.com/80/v2-d048608d543b55df997675195552f887_720w.webp")
no-repeat center;
}
</style>
插槽
作用:让组件内部的一些结构支持自定义
默认插槽
默认插槽:组件内定制一处结构
- 文件路径:src/components/MyDialog.vue
<template>
<div>
<hr />
<h3>温馨提示</h3>
<p>
<!-- 1. 在需要定制的地方,使用 slot 占位 -->
<slot>我是后备内容</slot>
</p>
<hr />
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
- 文件路径:src/App.vue
<template>
<div>
<!-- 2. 使用组件时,组件标签内填入内容 -->
<MyDialog>你确认要取消收藏吗?</MyDialog>
<MyDialog>你确认要注销账户吗?</MyDialog>
</div>
</template>
<script>
import MyDialog from "./components/MyDialog.vue";
export default {
components: {
MyDialog,
},
};
</script>
<style>
</style>
具名插槽
具名插槽:组件内定制多处结构
注意:v-slot:插槽名
可以简化为 #插槽名
- 文件路径:src/components/MyDialog.vue
<template>
<!-- 1. 在需要定制的地方,使用 slot 占位 -->
<div>
<hr />
<slot name="title">我是后备内容</slot>
<p><slot name="content">我是后备内容</slot></p>
<hr />
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
- 文件路径:src/App.vue
<template>
<div>
<!-- 2. 使用组件时,组件标签内填入内容 -->
<MyDialog>
<template v-slot:title>
<h3>友情提示</h3>
</template>
<template #content>
<span>>你确认要取消收藏吗?</span>
</template>
</MyDialog>
</div>
</template>
<script>
import MyDialog from "./components/MyDialog.vue";
export default {
components: {
MyDialog,
},
};
</script>
<style>
</style>
作用域插槽
功能:给插槽绑定数据,将来使用组件时可以用
具体演示:
- 文件路径:src/App.vue
<template>
<div>
<MyTable :data="list1">
<!-- 3. 通过template #插槽名=变量名 接收对象 -->
<template #default="obj">
<button @click="del(obj.row.id)">删除</button>
</template>
</MyTable>
<MyTable :data="list2">
<template #default="obj">
<button @click="show(obj.row)">查看</button>
</template>
</MyTable>
</div>
</template>
<script>
import MyTable from "./components/MyTable.vue";
export default {
data() {
return {
list1: [
{ id: 1, name: "李小萌", age: 16 },
{ id: 2, name: "王非明", age: 37 },
{ id: 3, name: "任天风", age: 22 },
],
list2: [
{ id: 1, name: "江海燕", age: 28 },
{ id: 2, name: "楼王嘿", age: 35 },
{ id: 3, name: "疆梦中", age: 41 },
],
};
},
methods: {
del(id) {
this.list1 = this.list1.filter((item) => item.id !== id);
},
show(row) {
alert(`姓名: ${row.name}; 年龄: ${row.age}`);
},
},
components: {
MyTable,
},
};
</script>
<style>
</style>
- 文件路径:src/components/MyTable.vue
<template>
<table class="my-table">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>年龄</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<!-- 1. 给slot标签,添加属性的形式进行传值 -->
<slot :row="item" msg="测试文本"></slot>
<!-- 2. 它底层会自动将所有的属性,添加到一个对象中 -->
<!--
{
row: { id: 2, name: '王非明', age: 27},
msg: '测试文本'
}
-->
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
data: Array,
},
};
</script>
<style>
.my-table {
width: 450px;
text-align: center;
border: 1px solid #ccc;
font-size: 24px;
margin: 30px auto;
}
.my-table head {
background-color: #1f74ff;
color: #fff;
}
</style>
单页应用程序
基本概念
-
单页应用程序:SPA(Single Page Application)
-
单页面应用——所有功能在一个 html 页面上实现
优点与缺点
单页面应用:系统类网站 || 内部网站 || 文档类网站 || 移动端站点
多页面应用:公司官网 || 电商类网站
VueRouter
基本认识
VueRouter:它是一个 Vue.js 官方提供的路由管理插件
官网:https://v3.router.vuejs.org/zh/
使用步骤
基本步骤:
- 下载
npm i vue-router@3.6.5
- 引入
import VueRouter from 'vue-router'
- 安装注册
Vue.use(VueRouter)
- 创建路由对象
const router = new VueRouter()
- 建立关联
new Vue({ render:h => h(App), router }).$mount('#app')
- 文件路径:src/main.js
// 1. 下载插件
// 在终端运行命令: npm i vue-router@3.6.5
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router' // 2. 导入插件
Vue.config.productionTip = false
Vue.use(VueRouter) // 3. 插件初始化
const myRouter = new VueRouter() // 4. 创建一个路由对象
new Vue({
render: h => h(App),
router: myRouter // 5. 建立关联
}).$mount('#app')
核心步骤:
- 创建需要的组件(建议放到 views 目录下)并配置路由规则
- 配置导航,配置路由出口(路由匹配的组件显示的位置)
- 文件路径:src/main.js
// 1. 下载插件
// 在终端运行命令: npm i vue-router@3.6.5
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router' // 2. 导入插件
Vue.config.productionTip = false
Vue.use(VueRouter) // 3. 插件初始化
// 6.1 导入资源
import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'
// 4. 创建一个路由对象
const myRouter = new VueRouter({
// 6.2 配置 router 路由规则
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend },
]
})
new Vue({
render: h => h(App),
router: myRouter // 5. 建立关联
}).$mount('#app')
- 文件路径:src/App.vue
<template>
<div>
<div>
<a href="#/find">发现音乐</a><br />
<a href="#/find">我的音乐</a><br />
<a href="#/find">我的朋友</a><br />
</div>
<div>
<!-- 路由出口=>匹配的组件所展示的位置 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {};
</script>
<style>
</style>
- 文件路径:src/views/Find.vue
<template>
<div>
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐</p>
</div>
</template>
<script>
export default {
name: "FindMusic",
};
</script>
<style>
</style>
- 文件路径:src/views/My.vue
<template>
<div>
<p>我的音乐</p>
<p>我的音乐</p>
<p>我的音乐</p>
</div>
</template>
<script>
export default {
name: "MyMusic",
};
</script>
<style>
</style>
- 文件路径:src/views/Friend.vue
<template>
<div>
<p>我的朋友</p>
<p>我的朋友</p>
<p>我的朋友</p>
</div>
</template>
<script>
export default {
name: "MyFriend",
};
</script>
<style>
</style>
路由模块封装
- 文件路径:src/router/index.js
// 导入模块
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入资源
import Find from '../views/Find.vue'
import My from '../views/My.vue'
import Friend from '../views/Friend.vue'
// 初始化
Vue.use(VueRouter)
// 创建路由对象
const myRouter = new VueRouter({
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend },
]
})
// 导出路由对象
export default myRouter
- 文件路径:src/main.js
import Vue from 'vue'
import App from './App.vue'
import myRouter from "./router/index.js";
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router: myRouter // 建立关联
}).$mount('#app')
声明式导航
router-link
需求:实现导航高亮效果
**vue-router 模块 **提供了一个全局组件 router-link 取代了 a 标签,它有两个功能
- 能跳转(它本质还是 a 标签)
- 能高亮(配置颜色样式即可)
原来的 a 标签:
<div>
<a href="#/find">发现音乐</a> <br />
<a href="#/my">我的音乐</a> <br />
<a href="#/friend">我的朋友</a> <br />
</div>
现在的 router-link 标签
<div>
<router-link to="/find">发现音乐</router-link> <br />
<router-link to="/find">我的音乐</router-link> <br />
<router-link to="/find">我的朋友</router-link> <br />
</div>
自定义匹配类名
const myRouter = new VueRouter({
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend },
],
// link自定义高亮
linkActiveClass: 'active', // 配置模糊匹配的类名
linkExactActiveClass: 'exact-active', // 配置精确匹配的类名
})
跳转传参
- 查询参数传参
<router-link to="/find?name=for you">发现音乐</router-link>
<template>
<div>
<!-- 查询参数传参,获取命令:$route.query.参数名 -->
<p>音乐名: {{ $route.query.name }}</p>
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐</p>
</div>
</template>
<script>
export default {
name: "FindMusic",
created(){
// 在created中,通过下面命令获取路由参数
// this.$route.query.参数名
alert(this.$route.query.name)
}
};
</script>
<style>
</style>
- 动态路由传参
<router-link to="/find/什么是音乐">发现音乐</router-link> <br />
// 导入模块
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入资源
import Find from '../views/Find.vue'
import My from '../views/My.vue'
import Friend from '../views/Friend.vue'
// 初始化
Vue.use(VueRouter)
// 创建路由对象
const myRouter = new VueRouter({
routes: [
{ path: '/find/:words', component: Find }, // "发现音乐"部分
{ path: '/my/:words', component: My },
{ path: '/friend/:words?', component: Friend }, // :words? 与 :words 的区别是加了问号后可以不传参
],
})
// 导出路由对象
export default myRouter
<template>
<div>
<!-- 动态路由参数,获取命令:$route.params.参数名 -->
<p>音乐名: {{ $route.params.words }}</p>
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐</p>
</div>
</template>
<script>
export default {
name: "FindMusic",
created(){
// 在created中,通过下面命令获取"动态路由参数"
// this.$route.params.参数名
alert(this.$route.params.words)
}
};
</script>
<style>
</style>
重定向与404
// 导入模块
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入资源
import Find from '../views/Find.vue'
import My from '../views/My.vue'
import Friend from '../views/Friend.vue'
import NotFind from '../views/NotFind.vue'
// 初始化
Vue.use(VueRouter)
// 创建路由对象
const myRouter = new VueRouter({
routes: [
{ path: '/', redirect: '/my' }, // 路由重定向
{ path: '/find', component: Find },
{ path: '/my/:words', component: My },
{ path: '/friend/:words', component: Friend },
{ path: '*', component: NotFind }, // 404页面
]
})
// 导出路由对象
export default myRouter
路由模式设置
路由模式默认是采用"hash路由",有个#号,看起来不好看,如何解决?
- hash 路由---------(默认) 例如:http://127.0.0.1:8080/#/home
- history 路由----- (常用) 例如:http://127.0.0.1:8080/home
// 导入模块
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入资源
import Find from '../views/Find.vue'
import My from '../views/My.vue'
import Friend from '../views/Friend.vue'
import NotFind from '../views/NotFind.vue'
// 初始化
Vue.use(VueRouter)
// 创建路由对象
const myRouter = new VueRouter({
routes: [
{ path: '/', redirect: '/my' }, // 路由重定向
{ path: '/find', component: Find },
{ path: '/my/:words', component: My },
{ path: '/friend/:words', component: Friend },
{ path: '*', component: NotFind }, // 404页面
],
mode: 'history' // 路由模式设置为'history'
})
// 导出路由对象
export default myRouter
编程式导航
概念简诉
所谓的编程式导航,就是指用 JS 代码来进行跳转
跳转与路由参数
- path 路径跳转与路由参数
this.$router.push('路由路径')
this.$router.push({
path: '路由路径'
})
<template>
<div>
<button @click="gotoFriend">我的朋友</button>
<p>发现音乐</p>
<p>发现音乐</p>
</div>
</template>
<script>
export default {
name: "FindMusic",
methods: {
// (1) 简单写法
// gotoFriend(){
// this.$router.push('/friend?key=123456')
// }
// (2) 普通写法
gotoFriend() {
this.$router.push({
path: "/friend",
query: {
key: 123456
},
});
},
},
};
</script>
<style>
</style>
- name 命名路由跳转与路由参数
const myRouter = new VueRouter({
routes: [
{ path: '/find/:words?', component: Find },
{ path: '/my/:words?', component: My },
{ name: 'friend', path: '/friend/:words?', component: Friend },
],
mode: 'history'
})
<template>
<div>
<button @click="gotoFriend">我的朋友</button>
<p>发现音乐</p>
<p>发现音乐</p>
</div>
</template>
<script>
export default {
name: "FindMusic",
methods: {
gotoFriend() {
this.$router.push({
name: 'friend',
// query: {
// key: 123456
// },
params: {
words: 123456
},
})
},
},
};
</script>
<style>
</style>
二级路由
const myRouter = new VueRouter({
routes: [
{ path: '/', component: Layout, children: [ { path: '/article', component: Article } ] }, // 通过 children 配置项,可以嵌套子路由
{ path: '/my/:words?', component: My }
],
mode: 'history'
})
组件缓存
keep-alive 是什么:
- keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们
- keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中
keep-alive 的优点:
- 在组件切换过程中,把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性
keep-alive 的三个属性:
include : 组件名数组,只有匹配的组件会被缓存
exclude : 组件名数组,匹配成功的组件都不会被缓存,而除此之外的组件都缓存
max : 最多可以缓存多少组件实例
<script>
export default {
// 组件名:如果没有配置 name,才会找文件名作为组件名
name: 'LayoutPage'
}
</script>
<template>
<div>
<!--
只希望 Layout 被缓存,include 需要配置成 :include="组件名数组"
-->
<keep-alive :include=['LayoutPage']>
<router-view></router-view>
</keep-alive>
</div>
</template>