22.自定义指令
22.1 自定义指令基本语法
22.1.2 自定义指令.vue
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--自定义指令
自定义指令:自己定义的指令,可以封装一些dom操作,扩展额外功能
1.全局注册-语法(main.js中定义):
Vue.directive('指令名',{
"inserted"(el){
//可以对el标签,扩展额外功能
el.focus()
}
}
2.局部注册-语法:
directives:{
"指令名":{
inserted(){
//可以对el标签,扩展额外功能
el.focus()
}
}
}
自定义指令的使用:<input v-指令名 type="text">
-->
<!--自定义指令-指令的值
需求:实现一个color指令-传入不同的颜色,给标签设置文字颜色
语法:在绑定指令时,可以通过“等号”的形式为指令 绑定具体的参数值
<div v-color="color">内容</div>
通过binding.value可以拿到指令值,指令值修改会触发update函数
directives:{
color:{
inserted(el,binding){
el.style.color=binding.value
},
update(el,binding){
el.style.color=binding.value
}
}
}
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
22.1.2 App.vue
<template>
<div>
<h1 v-color="color1">自定义指令1</h1>
<h1 v-color="color2">自定义指令2</h1>
<input v-focus ref="inp" type="text">
</div>
</template>
<script>
export default {
//1.全局注册指令
/*mounted(){
this.$refs.inp.focus()
}*/
// 2.局部注册指令
directives:{
//指令名:指令的配置项
focus:{
inserted(el){
el.focus()
}
},
color:{
//1.inserted提供的是元素被添加到页面中时的逻辑
inserted(el,binding){
// console.log(el);
//binding.value就是指令的值
el.style.color=binding.value
},
// 2.update指令的值修改时触发,提供值变化后,dom更新的逻辑
update(el,binding){
console.log("指令的值修改了")
el.style.color=binding.value
}
}
},
data(){
return{
color1:"red",
color2:"orange"
}
}
}
</script>
<style>
</style>
22.2 自定义指令(v-loading指令封装)
22.2.1 自定义指令
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--自定义指令:v-loading指令封装
场景:实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态=>用户体验不好
需求:封装一个v-loading指令,实现加载中的效果
分析:(1)本质loading效果就是一个蒙层,盖在了盒子上;
(2)数据请求中,开启loading状态,添加蒙层;
(3)数据请求完毕,关闭loading状态,移除蒙层;
实现:(1)准备一个loading类,通过伪元素定位,设置宽高,实现蒙层;
(2)开启关闭loading状态(添加移除蒙层),本质只需要添加移除类即可;
(3)结合自定义指令的语法及逆行封装复用;
.loading:before{
content:"";
position:absolute;
left:0;
top:0;
width:100%;
height:100%;
background:#fff url("./loading.gif") no-repeat center;
}
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
22.2.2 App.vue
<template>
<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>
</template>
<!--
接口地址:http://hmajax.itheima.net/api/news
请求方式:get
-->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<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 中,用于页面渲染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;
width: 100%;
height: 100%;
background: #fff url("./img/loading.gif") no-repeat;
}
</style>
23.插槽
23.1 默认插槽
23.1.1 默认插槽
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--插槽-默认插槽
作用:让组件内部的一些结构支持自定义,只能够完成一个定制位置
需求:要在页面中显示一个对话框,封装成一个组件;
问题:组件的内容部分,不希望写死,希望能使用的时候自定义
插槽基本语法:
1.组件内需要定制的结构部门,改用<slot></slot>占位
2.使用组件时,<MyDialog></MyDialog>标签内部,传入结构替换slot;
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
23.1.2 App.vue
<template>
<div>
<!-- 2.使用插槽:在使用标签内,填上插槽要添加的东西(可插入很多东西)-->
<!-- 使用标签时,添加的内容将会填写到调用组件中的slot标签中-->
<MyDialog>确定要删除吗?</MyDialog>
<MyDialog>确定要退出系统吗?</MyDialog>
</div>
</template>
<!--使用组件时
<MyDialog>你确定要退出本系统吗?</MyDialog>
此处的内容(你确定要退出本系统吗)就是要提交到<slot>插槽的位置
-->
<!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
<script>
import MyDialog from "./components/MyDialog"
// import axios from 'axios'
export default {
data() {
return {}
},
components: {
MyDialog
}
}
</script>
<style>
.loading:before {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 100%;
}
</style>
23.1.3 MyDialog.vue
<template>
<div class="dialog">
<!-- 头部-->
<div class="dialog-header">
<h3>友情提示</h3>
<span class="close">X</span>
</div>
<!-- 主体-->
<div class="dialog-content">
<!-- 中间(此处)的内容需要定制-->
<!-- 1.在需要定制的位置,使用slot占位 -->
<slot></slot>
</div>
<!-- 尾部-->
<div class="dialog-footer">
<button>取消</button>
<button>确认</button>
</div>
</div>
</template>
<script>
export default {
name: "MyDialog"
}
</script>
<style scoped>
</style>
23.2 后备内容(插槽默认的内容)
23.2.1 后备内容
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--
通过插槽完成了内容的定制,传什么显示什么,如果不传,则是空白
插槽后备内容:封装组件时,可以为预留的`<slot>`插槽提供后备内容(默认内容)
语法:在<slot>标签内,放置内容,作为默认显示内容
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
23.2.2 App.vue
<template>
<div>
<!-- 2.使用插槽:在使用标签内,填上插槽要添加的东西(可插入很多东西)-->
<!-- 使用标签时,添加的内容将会填写到调用组件中的slot标签中-->
<MyDialog>确定要删除吗?</MyDialog>
<MyDialog>确定要退出系统吗?</MyDialog>
<MyDialog></MyDialog> <!--此处未传入值,则会显示插槽默认的内容-->
</div>
</template>
<!--使用组件时
<MyDialog>你确定要退出本系统吗?</MyDialog>
此处的内容(你确定要退出本系统吗)就是要提交到<slot>插槽的位置
-->
<!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
<script>
import MyDialog from "./components/MyDialog"
// import axios from 'axios'
export default {
data() {
return {}
},
components: {
MyDialog
}
}
</script>
<style>
.loading:before {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 100%;
}
</style>
23.2.3 MyDialog.vue
<template>
<div class="dialog">
<!-- 头部-->
<div class="dialog-header">
<h3>友情提示</h3>
<span class="close">X</span>
</div>
<!-- 主体-->
<div class="dialog-content">
<!-- 中间(此处)的内容需要定制-->
<!-- 1.在需要定制的位置,使用slot占位,在slot占位符中添加的内容就是默认显示内容(当在调用该插槽时,无内容,则将显示该后备内容,否则,显示添加的内容) -->
<slot>后备内容</slot>
</div>
<!-- 尾部-->
<div class="dialog-footer">
<button>取消</button>
<button>确认</button>
</div>
</div>
</template>
<script>
export default {
name: "MyDialog"
}
</script>
<style scoped>
</style>
23.3 具名插槽
23.3.1 具名插槽
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--具名插槽(就是有具体名字的插槽)
需求:一个组件内有多处结构,需要外部传入标签,进行定制
具名插槽语法:
1.多个slot使用name属性区分名字,name对应的数据就相当于每个插槽的具体唯一名字
例如:<div class="dialog-header">
<slot name="head"></slot>
</div>
<div class="dialog-content">
<slot name="content"></slot>
</div>
<div class="dialog-footer">
<slot name="footer"></slot>
</div>
2.template配合v-slot:名字来分发对应标签
例如:<MyDialog>
<template v-slot:head>
大标题
</template>
<template v-slot:content>
内容文本
</template>
<template v-slot:footer>
<button>按钮</button>
</template>
</MyDialog>
3.v-slot:插槽名 可以简化为 #插槽名
<MyDialog>
<template #head>
大标题
</template>
<template #content>
内容文本
</template>
<template #footer>
<button>按钮</button>
</template>
</MyDialog>
-->
<!--
注意:一旦插槽有了名字,就是具名插槽,只支持定向分发
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
23.3.2 App.vue
<template>
<div>
<!-- 2.使用插槽:在使用标签内,填上插槽要添加的东西(可插入很多东西)-->
<!-- 使用标签时,添加的内容将会填写到调用组件中的slot标签中-->
<!-- <MyDialog>确定要删除吗?</MyDialog>
<MyDialog>确定要退出系统吗?</MyDialog>
<MyDialog></MyDialog>--> <!--此处未传入值,则会显示插槽默认的内容-->
<MyDialog>
<!-- 需要通过template包裹分发的结构,包成一个整体-->
<template #head>
<div>大标题</div>
</template>
<template #content>
<div>内容文本</div>
</template>
<template #footer>
<template>
<button>确认</button>
<button>取消</button>
</template>
</template>
</MyDialog>
</div>
</template>
<!--使用组件时
<MyDialog>你确定要退出本系统吗?</MyDialog>
此处的内容(你确定要退出本系统吗)就是要提交到<slot>插槽的位置
-->
<!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
<script>
import MyDialog from "./components/MyDialog"
// import axios from 'axios'
export default {
data() {
return {}
},
components: {
MyDialog
}
}
</script>
<style>
.loading:before {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 100%;
}
</style>
23.3.3 MyDialog.vue
<template>
<div class="dialog">
<!-- 头部-->
<div class="dialog-header">
<slot></slot>
</div>
<!-- 主体-->
<div class="dialog-content">
<slot name="content"></slot>
</div>
<!-- 尾部-->
<div class="dialog-footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script>
export default {
name: "MyDialog"
}
</script>
<style scoped>
</style>
23.4 作用域插槽
23.4.1 作用域插槽
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--作用域插槽:是插槽的一个传参语法-->
<!--作用域插槽:定义slot插槽的同时,是可以传值的,给插槽上可以绑定数据,将来使用组件时可以使用-->
<!--场景:封装表格组件
1.父传子,动态渲染表格内容;
2.利用默认插槽,定制操作列;
3.删除或查看都要用到当前项的id,属于组件内部的数据;
通过作用域插槽传值绑定,进而使用
-->
<!--作用域插槽基本使用步骤:
1.给slot标签,以添加属性的方式传值
<slot :id="item.id" msg="测试文本"></slot>
2.所有添加的属性,都会被收集到一个对象中
{id:3,msg:"测试文本"}
3.在template中,通过'#插槽名=“obj”'接收调用此插槽时发送的数据,默认插槽名为default
<MyTable :list='list'>
<template #default="obj">
<button @click="del(obj.id)">删除</button>
</template>
</MyTable>
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
23.4.2 App.vue
<template>
<div>
<MyTable :data="list">
<!-- 3.通过template #插槽名=“变量名”,接收-->
<!-- obj:就是接收来自MyTable组件的对象数据-->
<template #default="obj">
{{obj}}
<button @click="del(obj.row.id)">删除</button>
</template>
</MyTable>
<MyTabe :data="list2">
<template #default="{row}">
<button @click="show(row)">查看</button>
</template>
</MyTabe>
</div>
</template>
<!--使用组件时
<MyDialog>你确定要退出本系统吗?</MyDialog>
此处的内容(你确定要退出本系统吗)就是要提交到<slot>插槽的位置
-->
<!--<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>-->
<script>
import MyTable from "./components/MyTable"
export default {
data() {
return {
list: [
{id: 1, name: "yuer", age : 18},
{id: 2, name: "yuer2", age : 19},
{id: 3, name:"yuer3", bag :17},
],
list2: [
{id: 1, name: "yuer", age : 18},
{id: 2, name: "yuer2", age : 19},
{id: 3, name:"yuer3", bag :17},
]
}
},
methods:{
del(id){
this.list=this.list.filter(item=>item)
},
show(row){
console.log(row);
}
},
components: {
MyTable
}
}
</script>
<style>
.loading:before {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 100%;
}
</style>
23.4.3 MyTable.vue
<template>
<div>
<tr v-for="(item,index) in data" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<!-- 1.给slot标签,添加属性的方式传值-->
<slot :row="item" msg="测试数据"></slot>
<!-- 2.将所有的属性,添加到一个对象中-->
<!-- {
row:{id:2,name:'yuer',age:18},
msg:{"测试文本"}
}-->
</td>
</tr>
</div>
</template>
<script>
export default {
name: "MyDialog"
}
</script>
<style scoped>
</style>
24.路由
24.1 单页应用程序&路由简单介绍
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--单页应用程序-->
<!--单页应用程序:SPA-Single Page Application:指的是 所有功能 在一个html页面上实现-->
<!--例如:网易云音乐https://music.163.com/(单页面应用:整个导航没变化)-->
<!--例如:京东、淘宝等(多页面应用:导航栏发生变化)-->
<!--单页面应用 VS 多页面应用-->
<!--
开发分类 实现方式 页面性能 开发效率 用户体验 学习成本 首屏加载 SEO(搜索引擎优化)
单页 一个html页面 按需更新性能高 高 非常好 高 慢 差
多页 多个html页面 整页面更新性能低 中等 一般 中等 快 优
-->
<!--单页面、多页面应用的适用:
单页面(vue):系统类网站/内部网站/文档类网站/移动端站点
多页面:公司官网/电商类网站
-->
<!--单页面应用程序,之所以开发效率高,性能高,用户体验的的原因是:页面是按需更新的
要按需更新,首先就需要明确:访问路径和组件的对应关系
访问路径和组件的对应关系如何确定呢?路由
-->
<!--路由:
生活中的路由:设备和ip的映射关系(路由器)
Vue中的路由:路径和组件的映射关系
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
24.2 VueRouter的介绍
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--VueRouter的介绍
目标:1.认识vue提供的路由插件:VueRouter;
2.掌握VueRouter的基本使用步骤;
作用:修改地址栏路径时,切换显示匹配的组件;
说明:Vue官方的一个路由插件,是一个第三方包;
官网:https://v3.router.vuejs.org/zh/
-->
<!--VueRouter的使用步骤(5+2)
1.前5步基础步骤:初始化步骤(固定)
(1)下载:idea打开项目终端下载VueRouter模块到当前工程,版本3.6.5(vue2);
yarn add vue-router@3.6.5 或 pnpm install vue-router@3.6.5(注:查看vue路由版本的指令:pnpm list vue-router)
(2)引入;
import VueRouter from 'vue-router'
(3)安装注册(插件的初始化);
Vue.use(VueRouter)
(注:vue相关插件的使用都需要进行安装注册)
(4)创建路由对象;
const router=new VueRouter()
(注:此时创建的vue实例和任何事件都没有关系)
(5)注入,将路由对象注入到new Vue实例中,建立关联;
new Vue({
render:h=>h(App),
router
}).$mount('#app')
注意:当完成前5步基础步骤时,地址栏中就会自动加一个#/,说明此时,被路由所管理
2.后2步核心步骤
(1)创建需要的组件(views目录---视图),配置路由规则(路径组件的匹配关系);
1)新的.vue文件(需要放在views目录(视图)中)
Find.vue/My.vue/Friend.vue
在main.js中导入该vue文件
(2)配置导航,配置路由出口router-view(路径匹配的组件显示的位置);
-->
<!-- Vue2 和 Vue3 对应的路由插件使用
2(Vue2) 3(VueRouter3.x) 3(Vuex3.x)
3(Vue3) 4(VueRouter4.x) 4(Vuex4.x)
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
24.3 组件存放目录问题
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--
路由相关的组件,为什么要放在views目录中呢?
组件分类:.vue文件分为2类(页面组件 & 复用组件),(注意:.vue文件本质无区别)
(1)页面组件:放在views目录中
(2)复用组件:放在components目录中
组件分类后,更易维护
(1)src/views文件夹
页面组件:页面展示,配合路由使用
(2)src/components文件夹
复用组件:展示数据,常用于封装复用
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
24.4 VueRouter的完整实现步骤
24.4.1 main.js
import Vue from 'vue'
import App from './App.vue'
//路由的使用步骤(5+2)
//5个基础步骤:
//1.下载(版本):项目终端中下载
//2.引入;
import VueRouter from "vue-router";
import Find from "./views/Find.vue"
import My from "./views/My.vue"
import Friend from "./views/Friend.vue"
//3.安装注册 (Vue.use(vue相关的插件))
Vue.use(VueRouter) //将VueRouter 放到use()内,实则会实现VueRouter发生初始化变化
//4.创建路由对象(router可以是其他名字,但要new Vue中与其对应)
// const router=new VueRouter()
const r = new VueRouter({
//核心步骤1:创建需要的组件(views目录),配置路由规则们(多个路由规则)
//routes:路由规则们
//route:一条路由规则{path:路径,component:组件}
routes: [
{path: "/find", component: Find}, //每条规则(地址栏路径(path)和组件(component)的对应)
{path: "/my", component: My},
{path: "/friend", component: Friend},
]
}
)
//5.注入new Vue中,建立关联
Vue.config.productionTip = false
//5.将插件对象注入到new Vue实例中
new Vue({
render: h => h(App),
// router //当路由对象名和router相同时,可简写成router
router: r //当路由对象名和router不同时,需要对应路由对象名
}).$mount('#app')
24.4.2 App.vue
<template>
<div>
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a>
</div>
<div class="top">
<!-- 路由出口: 该标签是用来控制组件所展示的位置-->
<!-- 该标签也是vue内部的标签,当该标签的位置决定着组件的位置(该标签放在哪里,组件就在哪里显示)/-->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {}
</script>
<style>
.footer_wrap {
position: fixed;
left: 0;
top: 0;
display: flex;
width: 100%;
height: 100%;
}
</style>
24.4.3 Find.vue
<template>
<div>
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐</p>
</div>
</template>
<script>
export default {
name: "FindMusic" //此处是因为eslint的命名规范,组件名必须要至少两个单词命名,故此处的name的值是由多个单词组成
}
</script>
<style scoped>
</style>
24.4.4 My.vue
<template>
<div>
<p>我的歌单</p>
<p>我的歌单</p>
<p>我的歌单</p>
</div>
</template>
<script>
export default {
name: "MyMusic" //此处是因为eslint的命名规范,组件名必须要至少两个单词命名,故此处的name的值是由多个单词组成
}
</script>
<style scoped>
</style>
24.4.5 Friend.vue
<template>
<div>
<p>朋友歌单</p>
<p>朋友歌单</p>
<p>朋友歌单</p>
</div>
</template>
<script>
export default {
name: "FriendMusic" //此处是因为eslint的命名规范,组件名必须要至少两个单词命名,故此处的name的值是由多个单词组成
}
</script>
<style scoped>
</style>
总结:
路由的使用步骤(5+2)
5步基础步骤:
(1)项目终端中下载
(2)(3)(4)(5)在main.js文件中实现
2步核心步骤:
(1)创建子组件vue文件,在main.js文件中导入,并将这些文件和路径地址进行匹配;
(2)在根组件中使用这些子组件,App.js文件
25.路由进阶
25.1 路由的封装抽离
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--路由的封装抽离
问题:所有的路由配置都堆在main.js中合适吗?
目标:将路由模块抽离出来;
好处:拆分模块,利于维护;
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
25.1.1 router目录—index.js(路由的封装)
//存放路由规则,在使用路由规则时,只需导入该规则即可
// 核心步骤:(1)创建router目录下的index.js文件;(2)将配置的路由相关内容存放在该文件中,之后在main.js中直接导入该index.js文件即可
//路由的使用步骤(5+2)
//5个基础步骤:
//路由的使用步骤(5+2)
//5个基础步骤:
//1.下载(版本):项目终端中下载
//2.引入;
import Vue from 'vue'
import VueRouter from "vue-router";
//找文件时,推荐使用绝对路径,使用@标识符代表标识路径
//@代表src目录
// import Find from "../views/Find.vue"
// import My from "../views/My.vue"
// import Friend from "../views/Friend.vue"
import Find from "@/views/Find"
import My from "@/views/My"
import Friend from "@/views/Friend"
//3.安装注册 (Vue.use(vue相关的插件))
// eslint-disable-next-line no-undef
Vue.use(VueRouter) //将VueRouter 放到use()内,实则会实现VueRouter发生初始化变化
//4.创建路由对象(router可以是其他名字,但要new Vue中与其对应)
// const router=new VueRouter()
const router = new VueRouter({
//核心步骤1:创建需要的组件(views目录),配置路由规则们(多个路由规则)
//routes:路由规则们
//route:一条路由规则{path:路径,component:组件}
routes: [
{path: "/find", component: Find}, //每条规则(地址栏路径(path)和组件(component)的对应)
{path: "/my", component: My},
{path: "/friend", component: Friend},
]
}
)
//导出router
export default router
25.1.2 main.js 导入封装路由
import Vue from 'vue'
import App from './App.vue'
//导入indext.js中的router
import router from './router/index'
Vue.config.productionTip = false
//5.将插件对象注入到new Vue实例中
new Vue({
render: h => h(App),
router //当路由对象名和router相同时,可简写成router
// router: r //当路由对象名和router不同时,需要对应路由对象名
}).$mount('#app')
25.1.3 App.vue
<template>
<div>
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a>
</div>
<div class="top">
<!-- 路由出口: 该标签是用来控制组件所展示的位置-->
<!-- 该标签也是vue内部的标签,当该标签的位置决定着组件的位置(该标签放在哪里,组件就在哪里显示)/-->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {}
</script>
<style>
.footer_wrap {
position: fixed;
left: 0;
top: 0;
display: flex;
width: 100%;
height: 100%;
}
</style>
25.1.4 Find.vue
<template>
<div>
<p>发现音乐</p>
<p>发现音乐</p>
<p>发现音乐</p>
</div>
</template>
<script>
export default {
name: "FindMusic" //此处是因为eslint的命名规范,组件名必须要至少两个单词命名,故此处的name的值是由多个单词组成
}
</script>
<style scoped>
</style>
25.1.5 Friend.vue
<template>
<div>
<p>朋友歌单</p>
<p>朋友歌单</p>
<p>朋友歌单</p>
</div>
</template>
<script>
export default {
name: "FriendMusic" //此处是因为eslint的命名规范,组件名必须要至少两个单词命名,故此处的name的值是由多个单词组成
}
</script>
<style scoped>
</style>
25.1.6 My.vue
<template>
<div>
<p>我的歌单</p>
<p>我的歌单</p>
<p>我的歌单</p>
</div>
</template>
<script>
export default {
name: "MyMusic" //此处是因为eslint的命名规范,组件名必须要至少两个单词命名,故此处的name的值是由多个单词组成
}
</script>
<style scoped>
</style>
25.2 使用router-link替代a标签实现高亮
25.2.1 使用router-link替代高亮
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--router-link 声明式导航-导航链接
需求:实现导航高亮效果
vue-router提供了一个全局组件router-link(取代a标签)
(1)能跳转,配置to属性指定路径(必须),本质还是a标签,to无需#
(2)能高亮,默认就会提供高亮类名,可以直接设置高亮样式,自带激活时的类名
-->
<!--router-link 声明式导航--默认提供两个类名
说明:我们发现router-link 自动给当前导航添加了两个高亮类名
(1)router-link-exact-active: 精确匹配
to="/my" 只可以匹配 /my
(2)router-link-active: 模糊匹配(用的多)
to="/my" 可以匹配 /my /my/a /my/b ....,,
-->
<!--router-link 声明式导航--默认提供两个类名
说明:router-link的两个高亮类名太长了,我们希望能定制?
在index.js文件中重命名
const router=new VueRouter({
router:[...],
linkActiveClass:"类名1", //类名1和类名2 是自定义的类名,激活时使用的是自定义类名
linkExactActiveClass:"类名2"
})
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
25.2.2 index.js
//存放路由规则,在使用路由规则时,只需导入该规则即可
// 核心步骤:(1)创建router目录下的index.js文件;(2)将配置的路由相关内容存放在该文件中,之后在main.js中直接导入该index.js文件即可
//路由的使用步骤(5+2)
//5个基础步骤:
//路由的使用步骤(5+2)
//5个基础步骤:
//1.下载(版本):项目终端中下载
//2.引入;
import Vue from 'vue'
import VueRouter from "vue-router";
//找文件时,推荐使用绝对路径,使用@标识符代表标识路径
//@代表src目录
// import Find from "../views/Find.vue"
// import My from "../views/My.vue"
// import Friend from "../views/Friend.vue"
import Find from "@/views/Find"
import My from "@/views/My"
import Friend from "@/views/Friend"
//3.安装注册 (Vue.use(vue相关的插件))
// eslint-disable-next-line no-undef
Vue.use(VueRouter) //将VueRouter 放到use()内,实则会实现VueRouter发生初始化变化
//4.创建路由对象(router可以是其他名字,但要new Vue中与其对应)
// const router=new VueRouter()
const router = new VueRouter({
//核心步骤1:创建需要的组件(views目录),配置路由规则们(多个路由规则)
//routes:路由规则们
//route:一条路由规则{path:路径,component:组件}
routes: [
{path: "/find", component: Find}, //每条规则(地址栏路径(path)和组件(component)的对应)
{path: "/my", component: My},
{path: "/friend", component: Friend},
],
//link自定义高亮类名
linkActiveClass: 'active', //模糊匹配类名
linkExactActiveClass:'exact-active' //精确匹配类名
}
)
//导出router
export default router
25.2.3 main.js
import Vue from 'vue'
import App from './App.vue'
//导入indext.js中的router
import router from './router/index'
Vue.config.productionTip = false
//5.将插件对象注入到new Vue实例中
new Vue({
render: h => h(App),
router //当路由对象名和router相同时,可简写成router
// router: r //当路由对象名和router不同时,需要对应路由对象名
}).$mount('#app')
25.2.4 App.vue
<template>
<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">
<!-- 路由出口: 该标签是用来控制组件所展示的位置-->
<!-- 该标签也是vue内部的标签,当该标签的位置决定着组件的位置(该标签放在哪里,组件就在哪里显示)/-->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {}
</script>
<style>
body{
margin:0;
padding:0;
}
.footer_wrap {
position: relative;
left: 0;
top: 0;
display: flex;
width: 100%;
text-align:center;
background-color: #333;
color: #ccc;
}
.footer_wrap a{
flex:1;
text-decoration: none;
padding:20px 0;
line-height: 20px;
background-color: #333;
color:#ccc;
border:1px solid black;
}
/*router-link-active:模糊匹配(常使用)
to="/find" => 地址栏匹配 /find /find/one /find/two
*/
/*.footer_wrap a.router-link-active{
background-color: purple;
}*/
/*对router-link默认自带的router-link-active类进行重命名*/
.footer_wrap a.active{
background-color: purple;
}
/*
router-link-exact-active:精确匹配(不常使用)
to="/find" => 地址栏匹配 /find
*/
/*.footer_wrap a.router-link-exact-active{
background-color: purple;
}*/
/*对router-link默认自带的类router-link-exact-active进行重命名*/
/*.footer_wrap a.exact-active{
background-color: purple;
}*/
.footer_wrap a:hover{
background-color: #555;
}
</style>
子组件与上节相同
25.3 声明式导航-跳转传参
25.3.1 声明式导航-跳转传参
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--
目标:在跳转路由时,进行传值
(1)查询参数传参;
(2)动态路由传参;
-->
<!--(1)查询参数传参
1)语法格式如下:
to="/path?参数名1=值&参数名2=值"
2)对应页面组件接收传递过来的值
$route.query.参数名
-->
<!--(2)动态路由传参
(1)配置动态路由(index.js文件中)
const router=new VueRouter({
routers:[
...,
{
path:"/search/:words",
component: Search
}
]
})
(2)配置导航链接
to="/path/参数值"
(3)对应页面组件接收传递过来的值
$route.params.参数名
-->
<!--查询参数传参 和 动态路由传参之间的异同点
区别:
1.查询参数传参(比较适合传多个参数)
(1)跳转:to="/path?参数名=值&参数名2=值"
(2)获取:$route.query.参数名
2.动态路由传参(优雅简洁,传单个参数比较方便)
(1)配置动态路由:path:"/paht/参数名"
(2)跳转:to="/path/参数名"
(3)获取:$route.params.参数名
-->
<!--动态路由参数可选符
问题:配了路由path:"/search/:words" 为什么按下面步骤操作,会未匹配到组件,显示空白?
原因:/search/:words 表示,必须要传参数,如果不传参数,也希望匹配,可以加个可选符“?”
const router=new VueRouter({
routes=[
{path:"/",redirect:'/home'}
{path:"/home",component:Home]},
{path:"/search/:words?",component:Search}
})
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
25.3.2 router—index.js
import Vue from 'vue'
import VueRouter from "vue-router";
import Home from "@/views/Home"
import Search from "@/views/Search"
Vue.use(VueRouter) //将VueRouter 放到use()内,实则会实现VueRouter发生初始化变化
const router = new VueRouter({
routes: [
{path:"/home",component:Home},
{path:"/search/:words",component:Search}
],
}
)
//导出router
export default router
25.3.3 main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'
Vue.config.productionTip = false
//5.将插件对象注入到new Vue实例中
new Vue({
render: h => h(App),
router //当路由对象名和router相同时,可简写成router
// router: r //当路由对象名和router不同时,需要对应路由对象名
}).$mount('#app')
25.3.4 App.vue
<template>
<div id="app">
<div class="link">
<router-link to="/find">首页</router-link>
<router-link to="/my">搜索页</router-link>
</div>
<!-- 路由出口: 该标签是用来控制组件所展示的位置-->
<router-view></router-view>
</div>
</template>
<script>
export default {}
</script>
<style>
.link{
height:50px;
line-height:50px;
background-color: #495150;
display:flex;
margin:-8px -8px 0 -8px;
margin-bottom: 50px;
}
.footer_wrap {
position: relative;
left: 0;
top: 0;
display: flex;
width: 100%;
text-align: center;
background-color: #333;
color: #ccc;
}
.footer_wrap a {
flex: 1;
text-decoration: none;
padding: 20px 0;
line-height: 20px;
background-color: #333;
color: #ccc;
border: 1px solid black;
}
/*router-link-active:模糊匹配(常使用)
to="/find" => 地址栏匹配 /find /find/one /find/two
*/
/*.footer_wrap a.router-link-active{
background-color: purple;
}*/
/*对router-link默认自带的router-link-active类进行重命名*/
.footer_wrap a.active {
background-color: purple;
}
/*
router-link-exact-active:精确匹配(不常使用)
to="/find" => 地址栏匹配 /find
*/
/*.footer_wrap a.router-link-exact-active{
background-color: purple;
}*/
/*对router-link默认自带的类router-link-exact-active进行重命名*/
/*.footer_wrap a.exact-active{
background-color: purple;
}*/
.footer_wrap a:hover {
background-color: #555;
}
</style>
25.3.5 Home.vue
<template>
<div class="home">
<div class="logo-box"></div>
<div class="search-box">
<input type="text">
<button>搜索一下</button>
</div>
<div class="hot-link">
热门搜索:
<!-- 查询参数传参-->
<!-- <router-link to="/search?key=发现歌单">发现歌单</router-link>-->
<!-- <router-link to="/search?key=我的歌单">我的歌单</router-link>-->
<!-- <router-link to="/search?key=朋友歌单">朋友歌单</router-link>-->
<!-- 动态路由传参-->
<router-link to="/search/发现歌单">发现歌单</router-link>
<router-link to="/search/我的歌单">我的歌单</router-link>
<router-link to="/search/朋友歌单">朋友歌单</router-link>
</div>
</div>
</template>
<script>
export default {
name: "HomeComponent"
}
</script>
<style scoped>
.logo-box{
}
</style>
25.3.6 Search.vue
<template>
<div class="search">
<!-- 查询参数传参-->
<!-- <p>搜索关键字:{{$route.query.key}}</p>-->
<!-- 动态路由传参-->
<p>搜索关键字:{{ $route.params.words }}</p>
<p>搜索结果:</p>
<ul>
<li>..............</li>
<li>..............</li>
<li>..............</li>
<li>..............</li>
<li>..............</li>
<li>..............</li>
</ul>
</div>
</template>
<script>
export default {
name: "SearchComponent",
created() {
// 在created中,获取路由参数,this.$route.query.参数名 获取查询参数
// 查询参数传参
// console.log(this.$route.query.key);
// 在created中,获取路由参数,this.$route.params.参数名 获取动态路由参数
// 动态路由传参
console.log(this.$route.params.words)
}
}
</script>
<style scoped>
.search {
width: 400px;
height: 240px;
padding: 0 20px;
}
</style>
25.4 Vue路由-重定向
25.4.1 Vue路由-重定向
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--Vue路由-重定向
问题:网页打开,url默认是/路径,未匹配到组件时,会出现空白
说明:重定向->匹配path后,强制跳转path路径
语法:{path:匹配路径,redirect:重定向到的路径}
const router=new VueRouter({
routes:[
{path:"/",redirect:'/home'},
{path:"/home",component:Home},
{path:"/search/:words",component:Search}
]
})
-->
<!--vue路由-404
作用:当路径找不到匹配时,给个提示页面
位置:配置在整个路由最后
语法:path:"*"(任意路径)-前面不匹配就命中最后这个
const router=new VueRouter({
routes:[
{path:"/",redirect:'/home'},
{path:"/home",component:Home},
{path:"/search/:words",component:Search},
{path:"*",component:NotFind}
]
})
-->
<!--vue路径-模式设置
问题:路由的路径看起来不自然,有#,能否切成真正路径形式?
(1)hash路由(默认),例如:http://localhost:8080/#/home
(2)history路由(常用),例如:http://localhost:8080/home(以后上线需要服务器端支持)
const router=new VueRouter({
routes,
mode:"history"
})
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
25.4.2 router—index.js
import Vue from 'vue'
import VueRouter from "vue-router";
import Home from "@/views/Home"
import Search from "@/views/Search"
import NotFound from "@/views/NotFound"
Vue.use(VueRouter) //将VueRouter 放到use()内,实则会实现VueRouter发生初始化变化
const router = new VueRouter({
//注意:一旦采用了history模式,地址栏中就没有#,需要后台配置访问规则
// mode: "hash", //vue路径,设置模式(默认)
mode: "history", //vue路径,设置模式(常用)
routes: [
{path: "/", redirect: '/home'},
{path: "/home", component: Home},
{path: "/search/:words", component: Search},
{path: "*", component: NotFound} //vue路由-404
],
}
)
//导出router
export default router
25.4.3 App.vue
<template>
<div id="app">
<div class="link">
<router-link to="/find">首页</router-link>
<router-link to="/my">搜索页</router-link>
</div>
<!-- 路由出口: 该标签是用来控制组件所展示的位置-->
<router-view></router-view>
</div>
</template>
<script>
export default {}
</script>
<style>
.link{
height:50px;
line-height:50px;
background-color: #495150;
display:flex;
margin:-8px -8px 0 -8px;
margin-bottom: 50px;
}
.footer_wrap {
position: relative;
left: 0;
top: 0;
display: flex;
width: 100%;
text-align: center;
background-color: #333;
color: #ccc;
}
.footer_wrap a {
flex: 1;
text-decoration: none;
padding: 20px 0;
line-height: 20px;
background-color: #333;
color: #ccc;
border: 1px solid black;
}
/*router-link-active:模糊匹配(常使用)
to="/find" => 地址栏匹配 /find /find/one /find/two
*/
/*.footer_wrap a.router-link-active{
background-color: purple;
}*/
/*对router-link默认自带的router-link-active类进行重命名*/
.footer_wrap a.active {
background-color: purple;
}
/*
router-link-exact-active:精确匹配(不常使用)
to="/find" => 地址栏匹配 /find
*/
/*.footer_wrap a.router-link-exact-active{
background-color: purple;
}*/
/*对router-link默认自带的类router-link-exact-active进行重命名*/
/*.footer_wrap a.exact-active{
background-color: purple;
}*/
.footer_wrap a:hover {
background-color: #555;
}
</style>
25.4.4 NotFound.vue
<template>
<div>
404
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style scoped>
</style>
其他文件见25.3
25.5 编程式导航
25.5.1 基本跳转
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--
问题一:点击按钮跳转如何实现?
编程式导航:用JS代码来进行跳转
两种语法:(1)path路径跳转(简易方便)
(2)name命名路由跳转
-->
<!--(1)path路径跳转
this.$router.push('路由路径')(简写方式)
this.$router.push({ (完整写法)
path:'路由路径'
})
-->
<!--(2)name命名路由跳转(适合path路径长的场景)
this.$router.push({
name:'路由名'
})
{name:'路由名',path:'/path/xxx',component:xxx}
-->
<!--
问题二:点击搜索按钮,跳转需要传参如何实现?
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
25.5.2 路由传参
<template>
<div id="app">
<!-- v-model相当于:value="XXX" @input="msg=XXX"-->
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
</template>
<!--
问题二:点击搜索按钮,跳转需要传参如何实现?
两种传参方式:查询参数+动态路由传参
两种跳转方式,对于两种传参方式都支持:
(1)path路径跳转传参
a)query传参
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
this.$router.push({
path:'/路径',
query:{
参数名1:'参数值1',
参数名2:'参数值2'
}
})
b)动态路由传参,需要配置路由
this.$router.push('/路径/参数值')
this.$router.push({
path:'/路径/参数值'
})
(2)name命名路由跳转传参
a)查询参数
this.$router.push({
name:'路由名字',
query:{参数名:参数值},
params:{
参数名:‘参数值’
}
})
b)动态路由传参
-->
<script>
export default {}
</script>
<!--样式:-->
<style scoped>
</style>
25.5.3 index.js
import Vue from 'vue'
import VueRouter from "vue-router";
import Home from "@/views/Home"
import Search from "@/views/Search"
import NotFound from "@/views/NotFound"
Vue.use(VueRouter) //将VueRouter 放到use()内,实则会实现VueRouter发生初始化变化
const router = new VueRouter({
//注意:一旦采用了history模式,地址栏中就没有#,需要后台配置访问规则
// mode: "hash", //vue路径,设置模式(默认)
mode: "history", //vue路径,设置模式(常用)
routes: [
{path: "/", redirect: '/home'},
{path: "/home", component: Home},
{name:"search", path: "/search/:words?", component: Search},
{path: "*", component: NotFound} //vue路由-404
],
}
)
//导出router
export default router
25.5.4 main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'
Vue.config.productionTip = false
//5.将插件对象注入到new Vue实例中
new Vue({
render: h => h(App),
router //当路由对象名和router相同时,可简写成router
// router: r //当路由对象名和router不同时,需要对应路由对象名
}).$mount('#app')
25.5.5 Home.vue
<template>
<div class="home">
<div class="logo-box"></div>
<div class="search-box">
<input v-model="inpValue" type="text">
<button @click="goSearch">搜索一下</button>
</div>
<div class="hot-link">
热门搜索:
<!-- 查询参数传参-->
<!-- <router-link to="/search?key=发现歌单">发现歌单</router-link>-->
<!-- <router-link to="/search?key=我的歌单">我的歌单</router-link>-->
<!-- <router-link to="/search?key=朋友歌单">朋友歌单</router-link>-->
<!-- 动态路由传参-->
<router-link to="/search/发现歌单">发现歌单</router-link>
<router-link to="/search/我的歌单">我的歌单</router-link>
<router-link to="/search/朋友歌单">朋友歌单</router-link>
</div>
</div>
</template>
<script>
export default {
name: "HomeComponent",
data() {
return {
inpValue: ""
}
},
methods: {
goSearch() {
//1.通过路径的方式跳转
//(1) this.$router.push("路由路径")(常用)
//this.$router.push('路由路径?参数名=参数值')
// this.$router.push("/search")
// this.$router.push(`/search?key=${this.inValue}`)
//(2)this.$router.push({ (完整写法,更适合传参)
// path:'路由路径'
// query:{
// 参数名:参数值,
// 参数名:参数值
// }
// })
/*this.$router.push({
path:"/search",
query:{
key:this.inpValue
}
})*/
// 2.通过命名路由的方式跳转(在index.js文件中需要给路由起名字)(适合长路径-层级比较深)
/* this.$router.push({
name:'路由名'
})*/
this.$router.push({
name: 'search',
/*query: {
key: this.inpValue
},*/
params: {
words: this.inpValue
}
})
}
}
}
</script>
<style scoped>
.logo-box {
height: 150px;
background: url('@/assets/logo.jpg') no-repeat center;
}
.search-box {
display: flex;
justify-content: center;
}
</style>
25.5.6 Search.vue
<template>
<div class="search">
<!-- 查询参数传参-->
<!-- <p>搜索关键字:{{$route.query.key}}</p>-->
<!-- 动态路由传参-->
<p>搜索关键字:{{ $route.params.words }}</p>
<!-- <p>搜索关键字:{{ $route.query.key}}</p>-->
<p>搜索结果:</p>
<ul>
<li>..............</li>
<li>..............</li>
<li>..............</li>
<li>..............</li>
<li>..............</li>
<li>..............</li>
</ul>
</div>
</template>
<script>
export default {
name: "SearchComponent",
created() {
// 在created中,获取路由参数,this.$route.query.参数名 获取查询参数
// 查询参数传参
// console.log(this.$route.query.key);
// 在created中,获取路由参数,this.$route.params.参数名 获取动态路由参数
// 动态路由传参
console.log(this.$route.params.words)
}
}
</script>
<style scoped>
.search {
width: 400px;
height: 240px;
padding: 0 20px;
}
</style>