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>
复制