基础知识请查阅 vue学习 +
介绍
用于将元素或组件的内容只渲染一次,而不会随着数据的变化而重新渲染。这在某些情况下可以提高性能,因为不需要在数据更新时重新渲染这部分内容
当使用 v-once
指令时,Vue.js 会将该元素或组件的内容标记为静态内容,不会再根据数据的变化重新计算和更新。这意味着即使数据发生变化,被 v-once
标记的内容也不会重新渲染
<template>
<div>
<div v-once @click="setMsg">只生效一次:{{sayHi}}</div>
<div>持续变化:{{sayHi}}</div>
</div>
</template>
<script>
export default {
data() {
return {
sayHi: "HELLO WORLD"
};
},
methods:{
setMsg(){
alert('hello')
this.sayHi = "学 HTML 死路一条"
}
}
};
</script>
父级通过使用 #插槽名 = “行参” 来接收参数
<template>
<div>
<test>
<template #title="row">
<p>无名喵站</p>
</template>
<test>
</div>
</template>
<script>
import Pannel from './插槽.vue';
export default {
components: {
test,
}
}
</script>
<template>
<div>
<p>子组件</p>
<slot name="title" :row="arr"></slot>
</div>
</template>
当子组件想直接修改父组件通过 props 传入的值时。会抛出一个报错提示:
这个Vue报错提示是因为你在子组件中直接修改了一个父组件传递给它的prop值。这是不推荐的做法,
因为父组件在重新渲染时会覆盖子组件中的修改。
<template>
<div class="switch">
<p>子级组件</p>
<div>
<p>{{type}}</p>
<div @click="changeFn" class="swDiv">
<div :class="{circle:true, off:type}"></div>
</div>
<button @click="changeFn">改变状态</button>
</div>
</div>
</template>
<script>
export default {
props: ["type"],
methods: {
changeFn() {
this.type = !this.type
}
}
};
</script>
效果展示
因此,一般情况下,子组件只作为展示的工作,不提供数据操作的能力。若非要操作父级页面的数据,
可以通过事件传递方式实现。如果需要实现操作数据能力,也是通过事件的方式委派给父级操作
<template>
<div class="switch">
<p>子级组件</p>
<div>
<p>{{type}}</p>
<div @click="changeFn" class="swDiv">
<div :class="{circle:true, off:type}"></div>
</div>
<button @click="changeFn">改变状态</button>
</div>
</div>
</template>
<script>
export default {
props: ["type"],
methods: {
changeFn() {
this.$emit("updateType", !this.type);
}
}
};
</script>
<template>
<div class="app">
<demo :type="type" @updateType="fn"></demo>
<p>父级状态</p>
<div class="swDiv">
<div :class="{circle:true, off:type}"></div>
</div>
</div>
</template>
<script>
import demo from "./components/demo.vue";
export default {
components: {
demo
},
data() {
return {
type: true
};
},
methods: {
fn(e) {
this.type = e;
}
}
};
</script>
效果展示
sync
官方提供了一种双向绑定的方案。通过 sync 修饰符,即可实现上述操作。其原理实际与上方相似;
<template>
<div class="app">
<demo :type.sync="type"></demo>
<p>父级状态</p>
<div class="swDiv">
<div :class="{circle:true, off:type}"></div>
</div>
</div>
</template>
<script>
import demo from "./components/demo.vue";
export default {
components: {
demo
},
data() {
return {
type: true
};
}
};
</script>
<template>
<div class="switch">
<p>子级组件</p>
<div>
<p>{{type}}</p>
<div @click="changeFn" class="swDiv">
<div :class="{circle:true, off:type}"></div>
</div>
<button @click="changeFn">改变状态</button>
</div>
</div>
</template>
<script>
export default {
props: ["type"],
methods: {
changeFn() {
this.$emit("update:type", !this.type);
}
}
};
</script>
效果展示
在 Vue 2 中,父组件可以通过使用 v-model
指令将一个变量的值传递给子组件,并实现双向绑定。
下面是一种常见的方法:
<input type="text" v-model="text" />
解析 v-mode 的原理,实际上是进行了一个如下的操作:
<input type="text" :value="text" @input="text = $event" />
若要自定义子组件实现父组件所谓的 v-model 的双向绑定,则需要如下操作
在父组件中,使用 v-model
指令将一个变量绑定到子组件上。假设你要将变量 parentValue
传递给子组件,并实现双向绑定,可以这样写:
<template>
<div>
<child-component v-model="parentValue"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
parentValue: ''
};
}
};
</script>
在子组件中,需要声明一个名为 value
的 prop,并在组件内部使用 model
选项将其绑定到子组件的一个内部变量上。
然后,通过在适当的时候触发 input
事件来更新该值。子组件的代码如下所示:
<template>
<div>
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
required: true
}
}
};
</script>
父组件和子组件之间就建立了双向绑定,当子组件的值发生变化时,父组件的 parentValue
也会相应地更新
效果展示
ESLint 会检查代码语法中是否符合 [ESLint语法规范],如若违反规范会抛出一些错误。
配置自动修正
基于 vscode 插件 ESLint 高亮错误,通过修改 vscode 配置项自动帮助修复错误。
写入对应配置,完成保存时自动修正代码规范的操作。
{
// 当保存的时候,eslint 自动帮我们修复错误
"editor.codeActionsOnSave": {
"source.fixAll": true
},
// 保存代码 不自动格式化
"editor.formatOnSave": false
}
效果展示
Vue可通过 全局注册 或者 局部注册 来 定义自定义指令
当 vue 内置指令满足不了业务需求时,我们可以使用 directive 方法或对象成员来实现
描述
- 全局指令 需在 main.js 入口文件 使用 Vue.directive() 注册
- 局部指令 需在 export default 中定义成员 directive 中注册
- 使用全局指令时,只需要在标签中 v-自定义指令名 即可,会执行自定义指令的方法
在 main.js 入口文件中使用 Vue.directive() 方法注册
声明在全局的指令,其他 vue单文件 都可以直接使用
语法介绍
- 此声明方式 作用于全局Vue项目
- Vue.directive( ‘自定义指令名’ , { inserted( Dom对象 , 标签传入的参数 ){ … } }
- inserted 是一个回调函数,指的是自定义指令所在的标签插入到页面时 触发 (仅响应一次)
注册全局自定义指令
- 演示自定义指令 使 表单组件 自动聚焦
// 全局自定义指令 自动对焦
Vue.directive('focus', {
inserted(el) {
el.focus();
}
})
<template>
<div>
<input v-if="isopen" type="text" v-focus />
<button v-else @click="isopen=true">打开输入框</button>
</div>
</template>
<script>
export default {
data(){
return{
isopen:false,
}
}
}
</script>
效果演示
在单 .vue 文件中的 export default 中声明的 directives 成员中注册
声明在单文件的自定义指令,仅可在该文件下使用
语法介绍
- 此声明方式 作用于单 vue文件内
- directive: { 自定义指令名 : { inserted(el) { … } } }
- inserted 是一个回调函数,指的是自定义指令所在的标签插入到页面时 触发 (仅响应一次)
注册局部自定义指令
- 演示自定义指令 使 表单组件 自动聚焦
<template>
<div>
<input v-if="isopen" type="text" v-focus />
<button v-else @click="isopen = true">打开输入框</button>
</div>
</template>
<script>
export default {
directives: {
focus: {
inserted(el) {
el.focus();
}
}
},
data() {
return {
isopen: false,
}
}
}
效果演示
标签在 v-自定义指令名=”值” 的方式传值,自定义指令在函数中 第二个参数中接收 值
无论在全局自定义指令或是在局部自定义指令中,都可以对自定义组件在不同场合时 传值
语法介绍
- 事件回调的参数1为 引用指令的dom对象,参数2为标签传入的值 例如 ( inserted( el,val ){ … } )
- 传入的参数被包裹在对象中,需要使用 对象的方式 取到实际的值
接收的参数成员
打印 接收的参数 val,可以看到如下对象信息
下面是对各个成员存值的描述
参数成员 | 描述 |
---|---|
{def} | 配置对象 |
{expression} | 传入值的完整结构 |
{modifiers} | – |
{name} | 自定义指令的名字 |
{rawName} | 自定义指令的全名 |
{value} | 被传入指令的值 |
演示传值
- 通过 data中的 myColor 传入 ‘red’ 参数到自定义指令中,自定义指令通过 val.value 取到实际的值
- update() 会在指令对应的 值/标签 更新时触发,并在下方演示中重复执行一轮新的操作,样式得到同步更新
<template>
<div>
<div v-myColor="colorStr"></div>
<button @click=" colorStr = 'blue'">打开输入框</button>
</div>
</template>
<script>
export default {
directives: {
myColor: {
inserted(el, val) {
console.log(val);
el.style.backgroundColor = val.value;
},
update(el, val) {
el.style.backgroundColor = val.value;
}
}
},
data() {
return {
colorStr: 'red',
}
}
}
</script>
效果演示
自定义指令 通过 事件驱动方法,并可在事件时传值 来实现各种操作
- 被绑定的标签使用 v-自定义指令名=”值” 来传递参数 (例如 <p v-color=”red”>我是红色</p>)
- 自定义指令使用 参数1 接收 dom对象 ,参数2 接收 传入的值 (例如 color: { inserted:(el,val){ el.style.color=val.value } })
回调函数 | 描述 |
---|---|
inserted | 指令所在标签插入到网页上 |
update | 指令所在标签对应数据/标签发生更新 |
路由指的是一种 映射关系
例如 node.js 中 接口 与 业务 的映射关系,设备 和 ip 的映射关系
通过路由的方式,可以关联两个 项目 的业务,也可说是种 一对一 的绑定关系
路由可以制作 单页面应用(SPA)
单页面应用指的是所有功能在一个html页面上实现,使用路由的方式进行业务场景切换
优点
- 整体不刷新页面,用户体验更好
- 数据传递容易,开发效率高
缺点
- 开发成本较高(需要学习专门知识)
- 首次加载会比较缓慢,不利于seo
组件可分为 页面组件 与 复用组件
页面组件一般为构成页面展示的组件,复用组件一般为重复使用和展示数据组件,例如商品列表中的小项
说明
- vue文件本质无区别,为方便学习和理解,分解成两种类型的组件
- 页面组件 一般放在 src/views 文件夹下,配合路由切换使用
- 复用组件 一般放在 src/components 文件夹下,嵌套在页面组件中。重复渲染结构一样的标签
Vue 路由通过 哈希值 来实现 组件的切换
通过 网站地址/#/哈希值,可在特定标签中自由切换到对应的组件内容
vue-router 本质上是一个第三方包
官网 https://router.vuejs.org/zh/
模块包优势
- 它和 Vue.js 深度集成
- 可以定义 – 视图表(映射规则)
- 符合模块化开发理念
- 提供两个内置全局组件
- 声明式导航自定激活的 CSS class 的链接
- … 更多优势
下载包
注意:最新版本的 vue-router 包存在兼容性问题,需要进行包的降级操作 相关资料
- Vue CLI 4.5以下,对应的是Vue2
- Vue CLI 4.5及以上,对应的是Vue3,也可以手动选择Vue2 vue
- 3.0以下兼容的是element-ui前端组件库;
- vue 3.0兼容的是element-plus前端组件库.
- vue2搭配vue-router3
- vue3搭配vue-router4
# npm 下载
npm install vue-router@3.6.5
# yarn 下载
yarn add vue-router@3.6.5
配置 vue-router 到 Vue实例 并在 App.vue 使用
- 引入 vue-router包 到 main.js 中
- 使用 Vue.use() 注册 vue-router 到全局组件
- 定义变量 routes 撰写路由规则 { part,component }
- 实例化 VueRouter 至变量 router 并导入 routes 路由规则
- 将完成配置的路由对象 router 注入到 vue实例中
配置路由
// @ 是 vue 目录中 src 的绝对路径
import Find from '@/view/Find.vue';
import My from '@/view/My.vue';
import Part from '@/view/Part.vue';
// 1. 引入 vue-router 包
import VueRouter from 'vue-router'
// 2. 注册全局组件
Vue.use(VueRouter);
// 3. 撰写 规则数组 引入组件
const routes = [
{
path: '/find',
component: Find,
},
{
path: '/my',
component: My,
},
{
path: '/part',
component: Part,
}
]
// 4. 生成路由对象
const router = new VueRouter({
// 与上方数组名字相同 ES6语法允许简写
routes,
});
// 5. 路由对象注入到 vue实例 中,this可以访问 $route 和 $router
new Vue({
router,
// 准备渲染到 APP
render: h => h(App),
})
// 渲染到 index.html 文件中 id 叫 app 的标签上
.$mount('#app');
路由配置参数
在上方代码演示中 routes 路由配置项的每一个对象,常用属性和描述
成员 | 类型 | 描述 |
---|---|---|
path | String | 路由的路径 根路由的路径需要加 ‘/’ |
name | String | 路由的名称,方便路由传值 |
component | value | 路由对应的组件 |
redirect | String | 路由响应后 执行重定向的路径 |
children | Arrary | 嵌套的子组件 |
beforeEnter | Arrary | function | 路由独享的守卫,可传入多个函数(不加括号) |
VueRouter 类导入的 routes 配置项,可使用 /#/路由名 渲染到指定位置
跳转到对应的路由,会直接在挂载点 <router-view></router-view> 中替换内容
使用方式
- 在对应的 <a>标签中 href 属性设置路由规则上的哈希值 例如 #/find
- 设置挂载点,用 <router-view></router-view> 挂载
- 当url的 hash值 路径切换时,在 router-view 标签中会显示路由规则中的内容
案例模拟
- main.js 使用上个案例的 路由配置
- 实现效果:路由的基础配置和切换
<template>
<div>
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/part">朋友</a>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {}
</script>
<style scoped>
.footer_wrap{
position: fixed;
left: 0;
right: 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;
}
.footer_wrap a:hover{
background-color: #555;
}
.top{
padding-top: 62px;
}
</style>
<template>
<div>
<li>推荐</li>
<li>排行榜</li>
<li>歌单</li>
</div>
</template>
<template>
<div>
<li>我的收藏</li>
<li>我的历史</li>
</div>
</template>
<template>
<div>
<li>关注明星</li>
<li>发现精彩</li>
<li>寻找伙伴</li>
<li>加入我们</li>
</div>
</template>
效果展示
使用 <router-link> 替代 <a> 标签执行跳转,会在选中时自动添加对应类名
router-link 会使用户执行跳转路由时,会给自身增加类样式,方便用于CSS实现选中时候的作 高亮判断,
说明
- vue-router 提供了一个全局组件 <router-link>
- router-link 实际上最终会渲染成 <a> 标签,它的 to 属性等价于 href 属性 (to无需 # )
- <router-link> 自带高亮的类名(选中时的类名): router-link-exact-active、router-link-active
实际操作
- 使用 <router-link> 特性,替换所有 <a> 标签,href属性 改为 to属性,(路径前面省略 #)
- 修饰高亮状态 router-link-exact-active 类的样式,vue其他内容复用上一个演示代码
案例模拟
- 实现效果:使用 router-link-exact-active 类为激活的路由呈现自定义的效果
/* 选中时触发 注意类的权重 */
.footer_wrap .router-link-active{
color: white;
background: black;
}
<template>
<div>
<div class="footer_wrap">
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
效果演示
router-link 可在 跳转路由时,给路由 对应的组件内传值
在 <router-link> 上的 to 属性中路径后面以 ?参数名=值 方式传值
说明
- 普通传值:在 router-link 上的 to 属传值,语法如下:
- /path?参数名=值 方式传值
- 普通接收:对应路由中接收的 页面组件
- 使用 $route.query.参数名 获取传递的值
- 动态传值:在 router-link 上的 to 属传值,语法如下:
- /path/值 (需要路由对象提前配置 path:”/path/参数名” 可传递多个)
- 动态接收:对应路由中接收的 页面组件
- 使用 $route.params.参数名 获取传递的值
普通传值
- <router-link> 使用 /path?参数名=值 方式 传值
- 路由对应的组件 使用 $route.query.参数名 接收
- vue其他内容复用上一个演示代码
- 实现效果:为每一个路由交互 query参数
<template>
<div>
<div class="footer_wrap">
<router-link to="/find?name=菊花台">发现音乐</router-link>
<router-link to="/my?name=七里香">我的音乐</router-link>
<router-link to="/part?name=隔壁老王">朋友</router-link>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
<template>
<div>
<li>推荐</li>
<li>排行榜</li>
<li>歌单</li>
<li>推荐:{{$route.query.name}}</li>
</div>
</template>
<template>
<div>
<li>我的收藏</li>
<li>我的历史</li>
<li>我喜欢的:{{$route.query.name}}</li>
</div>
</template>
<template>
<div>
<li>关注明星</li>
<li>发现精彩</li>
<li>寻找伙伴</li>
<li>加入我们</li>
<li>有人关注你:{{$route.query.name}}</li>
</div>
</template>
效果演示
动态传值
- 在 main.js 中的 路由配置项里修饰,path:”/:变量名/” 书写的内容会被当成 动态参数
- 在 <router-link> 的 to 属性中 以 /路径/参数 的方式 进行传值
- 接收方以 $route.params.变量名 获取值。vue其他内容复用上一个演示代码
- 实现效果:为每一个路由交互 params 动态参数
const routes=[
{
path:'/find/:music',
component:Find,
},
{
path:'/my/:music',
component:My,
},
{
path:'/part/:user/:id',
component:Part,
},
];
<template>
<div>
<div class="footer_wrap">
<router-link to="/find/菊花台">发现音乐</router-link>
<router-link to="/my/七里香">我的音乐</router-link>
<router-link to="/part/夜夜酱/10032">朋友</router-link>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
<template>
<div>
<li>推荐</li>
<li>排行榜</li>
<li>歌单</li>
<li>推荐:{{$route.params.music}}</li>
</div>
</template>
<template>
<div>
<li>我的收藏</li>
<li>我的历史</li>
<li>我喜欢的:{{$route.params.music}}</li>
</div>
</template>
<template>
<div>
<li>关注明星</li>
<li>发现精彩</li>
<li>寻找伙伴</li>
<li>加入我们</li>
<li>你关注的人:[ID {{ $route.params.id }}] {{ $route.params.user }}</li>
</div>
</template>
效果展示
在路由配置中 “/” 是 默认路径,加载页面时候 <router-view> 首先匹配该路径
网页打开 url 默认的 hash 值是 / 路径
redirect 属性可以设置 路由响应后 需要 重定向 到哪个路由路径
在路由配置的每项路由中,redirect 需要传入一个字符串值的路由,且该传入的路由在 路由配置 中 已经声明
案例模拟
- 实现效果:设置一个网站打开默认转向的路由
const routes = [
{
// 默认路由
path: '/',
// 路由响应后 执行重定向
redirect: '/find',
},
...
{
path: '/find',
component: Find,
},
];
效果演示
在路由配置中 “*” 是 匹配任意路由,常用于匹配 无对应路由 所显示的 404页面
该路由一般 放置在路由配置对象的末尾,用于 返回路径无命中任意路由 最后返回的 提示页面
案例模拟
- 在 main.js 中的路由配置对象 中,最末端添加 ” * ” 路由配置
- ” * ” 路由绑定的组件为 404页面组件
- 实现效果:当路由无任何命中时,跳转到 404页面 组件
// 引入 404页面组件
import NotFound from '@/views/NotFound.vue';
const routes = [
{
// 默认路由
path: '/',
redirect: '/find',
},
...
{
// 任意路由
path:'*',
component:NotFound,
}
];
<template>
<div>
<div class="footer_wrap">
<router-link to="/find/菊花台">发现音乐</router-link>
<router-link to="/my/七里香">我的音乐</router-link>
<!-- 该路径未在路由声明 -->
<router-link to="/play">游戏</router-link>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
<template>
<img src="../assets/404.png" alt="">
</template>
效果演示
Vue路由有两种模式,分别为 hash路由 与 history路由
使用 hash路由时,路径后面的 # 显得有些突兀,如若为了解决这个问题,可尝试在路由配置中切换模式
区别
- hash 路由:http://localhost:8080/#/home
- history 路由:http://localhost:8080/home (需要服务器端支持,否则这样的写法只是寻找域名下的文件夹)
切换路由
- 在全局配置中,找到 路由实例化的配置项中,修改 mode 的默认值
- 路由配置项的 mode 的值默认为 hash,修改完成后,url路径中去掉了 # 号占位
案例模拟
- 实现效果:使用 history路由 的方式,去掉 url 中的 # 号
const router = new VueRouter({
routes,
mode:'history',
})
效果展示
使用 JavaScript代码 来 控制路由 的跳转,叫编程式导航
不使用 <a> 标签的 href 来进行路由的切换,而是使用 JS 的 this.$router.push({}) 进行路由的切换。
需传入 path 或 name 对应参数
说明
- 使用 编程式导航时,目标路由 需已在 路由配置 中已经声明
this.$router.push({
// path 或者 name 任选一个
path: '路由路径',
name: '路由名'
})
案例模拟 – 使用 path
- 使用 span 标签绑定对应菜单,并给每个 span 绑定点击事件
- 给每个绑定的点击事件传入对应 路由url参数
- 在 methods 中定义 编程式导航 方法 this.$router.push() ,并使用传入的参数
- 实现效果 在 this.$router.push({}) 方式跳转路由时,依据 path路径 跳转
<template>
<div>
<div class="footer_wrap">
<span @click="btn('/find')">发现音乐</span>
<span @click="btn('/my')">我的音乐</span>
<span @click="btn('/part')">游戏</span>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
methods: {
btn(targrtPath) {
this.$router.push({
path: targrtPath
});
}
}
}
</script>
效果展示
案例模拟 – 使用 name
- 在 mian.js 入口文件中,为路由配置中每一个路由设置 name 的值,作为跳转的索引
- 使用 span 标签绑定对应菜单,并给每个 span 绑定点击事件
- 给每个绑定的点击事件传入对应 路由的 name 值
- 在 methods 中定义 编程式导航 方法 this.$router.push() ,并使用传入的参数
- 实现效果 在 this.$router.push({}) 方式跳转路由时,依据 name路由名 跳转
const routes = [
{
path: '/find',
name:'Find',
component: Find,
},
{
path: '/my',
name:'My',
component: My,
},
{
path: '/part',
name:'Part',
component: Part,
},
];
<template>
<div>
<div class="footer_wrap">
<span @click="btn('Find')">发现音乐</span>
<span @click="btn('My')">我的音乐</span>
<span @click="btn('Part')">朋友</span>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
methods: {
btn(targrtPath) {
this.$router.push({
name: targrtPath
});
}
}
}
</script>
效果展示
# name 方法跳转适用于组内的规范,因为 path 是会在 url中看到,但是 name 在 url 中无法查看 #
直观的说就是:
name 可以随便命名,
path 的命名会影响显示的 url,
因此 name 可以统一 路由组的风格
在 this.router.push({}) 跳转路由时,使用 $route 属性中 query 或 params 来对路由进行传参
- 传入 query:{“参数名”:值} 等同于 声明式的 /path?参数名 = 值
- 传入 params:{“参数名”:值} 等同于 声明式的 动态传参 /path/值
说明
- query 或者 params 任选一个
- 推荐使用 name + quey 或 name + params 方式传参
注意事项
- 使用 path 会忽略 params,因此 params 只能使用 name
- 如果在当前路由中重复跳到当前路由并传参数提示冗余问题报错
“NavigationDuplicated: Avoided redundant navigation to current location…”,不会跳转路由
this.$router.push({
path: '路由名称',
name: '路由名',
query: {
"参数名": 值
},
params: {
"参数名": 值
}
});
案例模拟
- 在 main.js入口文件 中的路由配置里 定义动态参数的 /:参数名 用于 params 接收参数
- 对应组件内 使用 $router.params.val 或 $router.query.val 按需接收对应传入的参数
- App.vue 在 this.router.push({}) 方法中撰写 params 或 query,并传入对应 key 和 值
- 实现效果:使用 this.$router.push({}) 方式跳转路由时,并传递参数
const routes = [
{
path: '/my',
name:'My',
component: My,
},
{
path: '/part/:user/:id',
component: Part,
},
];
<template>
<div>
<li>我的收藏</li>
<li>我的历史</li>
<li v-if="$route.query.music">{{ $route.query.music }}</li>
</div>
</template>
<template>
<div>
<li>关注明星</li>
<li>发现精彩</li>
<li>寻找伙伴</li>
<li>加入我们</li>
<li v-if="$route.params.id">你关注的人:[ID {{ $route.params.id }}] {{ $route.params.user }}</li>
</div>
</template>
<template>
<div>
<span @click="oneBtn">朋友A</span>
<span @click="twoBtn">我的音乐</span>
</div>
<div class="top">
<!-- 设置挂载点 -->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
methods: {
oneBtn() {
this.$router.push({
name: 'Part',
params: {
id: '233',
user: '夜夜'
}
})
},
twoBtn() {
this.$router.push({
name: 'My',
query: {
music: '菊花台'
}
})
}
}
}
</script>
效果展示
$router 与 $route 区别
- $route 表示 (当前路由信息对象) 用以获取当前路由的 path, name, params, query 等属性
- this.$router 在 vue 实例内部,通过 this.$router 访问路由实例,实现例如 编程式 跳转等方法
配置对象的 children 属性可以嵌套 子路由对象,子路由url 前面加上 父级url 进行切换
在 路由配置 中定义的 childern 传入的是一个 子路由配置 的对象数组,可使用 /path/path 进行跳转
说明
- 一级路由 path 从 / 开始定义
- 二级路由往后 path 直接写名字,无需 / 开头
- 嵌套路由 在上级路由的 childern 数组里编写路由信息对象
- 子级路由 同 父级一样也是通过 <router-view></router-view> 设置路由的挂载点
案例模拟
- 定义二级路由文件 Recommend.vue、Ranking.vue、SongList.vue
- 在 main.js 入口文件的 路由配置 中,为 指定路由增加 childern 属性,引入组件 并 配置对应 子级路由
- 修改 Find.vue 文件,使用 <router-link> 的 to 路由切换,使用 <router-view></router-view> 挂载点
- 实现效果:二级嵌套路由的铺设和跳转
<template>
<div>推荐内容</div>
</template>
<template>
<div>推荐内容</div>
</template>
<template>
<div>歌单 - 发现音乐内容</div>
</template>
import Recommend from '@/views/Second/Recommend.vue';
import Ranking from '@/views/Second/Ranking.vue';
import SongList from '@/views/Second/SongList.vue';
...
const routes = [
...
{
path: '/find',
name:'Find',
component: Find,
// 嵌套的路由前面无需添加 /
children:[
{
path:'recommend',
component:Recommend,
},
{
path:'ranking',
component:Ranking,
},
{
path:'songlist',
component:SongList,
},
]
},
...
];
<template>
<div class="findBox">
<div class="menu">
<router-link to="/find/recommend">推荐</router-link>
<router-link to="/find/ranking">排行榜</router-link>
<router-link to="/find/songlist">歌单</router-link>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
效果展示
使用 <router-link> 时,会根据 url 的 精确/模糊 匹配模式 赋予指定类名
- 在 url 的 hash 完全匹配下 路由对应的 a 标签会赋予类 router-link-exact-active 和 router-link-active
- 在 url 的 hash 模糊匹配下 路由对应的 a 标签会赋予类 router-link-active
说明
- router-link-exact-active 类的添加条件:url的hash值和href完全匹配
- router-link-active 类的添加条件:url的hash值包含href路径值匹配
案例模拟
- 实现效果:使完全匹配下的嵌套子级路由呈现选中样式
<template>
<div class="findBox">
<div class="menu">
<router-link to="/find/recommend">推荐</router-link>
<router-link to="/find/ranking">排行榜</router-link>
<router-link to="/find/songlist">歌单</router-link>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
/* 为 menu 下的 a 标签赋予选中效果 */
.menu a.router-link-exact-active.router-link-active{
transform: scale(1.1);
background-color: rgb(204, 65, 65);
}
效果展示
路由跳转之前,会触发 router.beforeEach() 函数,可用作跳转前验证、阻止跳转
该函数常用于跳转前 验证用户是否登录、用户是否有权限等判断。详细文档
const routes = [
...
];
const router = new VueRouter({
routes,
})
//未登录
let isLogin = false;
router.beforeEach((to, from, next) => {
if (to.path === '/my' && isLogin === false) {
alert('请登录');
//阻止路由跳转
next(false);
} else {
next();
}
});
效果演示
语法
- router.beforeEach((to,from,next) => {} ); to 是跳转方对象 from是跳转目标 next是执行跳转
- 场景:当你要对路由进行权限判断时,需要用到 router.beforeEach()
- 跳转:next() 路由正常跳转 | next(false) 在原地停留 | next(‘/path’) 强制跳转到传入参数的指定 /path 路径上
成员 | 描述 |
---|---|
to.path | 路由要跳转的hash路径目标 |
to.params | 路由跳转携带的params参数 |
to.name | 路由要跳转的路由名目标 |
from.path | 路由跳转前的hash路径 |
from.name | 路由跳转前的路由名 |
next | 函数体,不传值为执行跳转,传入false表示不跳转 |
在路由配置上每项单独定义的 beforeEnter 守卫
beforeEnter
守卫 只在进入路由时触发,不会在 params
、query
或 hash
改变时触发。
( 例如,从 /users/2
进入到 /users/3
或者从 /users/2#info
进入到 /users/2#projects
它们只有在 从一个不同的 路由导航时,才会被触发 )
详细内容参考 官方文档
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// 不进行跳转
return false
},
},
]
你也可以将一个函数数组传递给 beforeEnter
这在为不同的路由重用守卫时很有用
function removeQueryParams(to) {
if (Object.keys(to.query).length)
return { path: to.path, query: {}, hash: to.hash }
}
function removeHash(to) {
if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash],
},
{
path: '/about',
component: UserDetails,
beforeEnter: [removeQueryParams],
},
]
Vant 是一个轻量、可靠的移动端 Vue组件库,开箱即用
支持移动项目中大多数使用场景的 Vue组件库,包含了组件所需的 js 和 css
相关文档:https://www.w3cschool.cn/vantlesson/
Vant组件库:gitee地址
特点
- 提供60多个高质量组件,覆盖移动端各类场景
- 性能极佳,组件平均体积不到1kb
- 完善的中英文文档示例
- 支持 Vue2 & Vue3
- 支持按需引入和主题定制
导入所有 Vant组件
vant 支持一次性导入所有组件,引入所有组件会增加代码包体积,因此不推荐这种做法
tips: 配置按需导入后,将不允许直接导入所有组件
下载包
# yarn 安装 vue2 版本的 vant
yarn add vant@latest-v2
# npm 安装 vue2 版本的 vant
npm install vant@latest-v2
引用包
import Vue from 'vue';
// 引入 vant 所有组件
import Vant from 'vant';
import 'vant/lib/index.css';
// 全局注册 vant组件
Vue.use(Vant);
使用 vant组件
查看官网文档,使用 vant提供的标签 制作样式
<template>
<div>
<van-button type="primary">主要按钮</van-button>
<van-button type="success">成功按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
</div>
</template>
效果展示
手动引入使用 vant 的某个组件
手动单独引入,快速了解:官方文档 – 按需引入
说明
- 引入vent组件 的完整包过于臃肿,我们在使用vant组件时,首先建议按需引入
- 在 .vue 单文件下引入时,使用 import 用变量命名方式接收导入,并用 components 成员引入
- 在 .vue 单文件下引入时,要注意使用驼峰命名方式对应标签的 横线命名 例如(<van-button> 等于 VanButton组件)
使用 按需引入 的 vant组件
- 在 components 引入组件时候,例如 Button组件 时也可以采用 [Button.name]:Button 的方式
- 下方演示中 [Button.name]:Button 与 VanButton:Button 是等价的。相当于使用它自己提供的标签命名
<template>
<div>
<van-button type="primary">主要按钮</van-button>
<van-button type="success">成功按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
</div>
</template>
<script>
// button组件
import Button from 'vant/lib/button';
// button样式
import 'vant/lib/button/style';
export default {
// 引入组件
components: {
// 对应上面标签名 采用横线转驼峰的方式
VanButton: Button
}
}
</script>
效果展示
使用 babel 的插件 babel-plugin-import ,实现自动按需引入 vant组件
babel-pugin-import 是一款babel插件,他会在编译过程中将 import 的写法自动转换成按需引入的方式
安装插件
# 安装插件
npm i babel-plugin-import -D
配置插件
在 babel.config.js 上进行配置插件后,实现 vant组件 自动按需导入的效果
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}
使用插件
- 直接在 main.js 全局引入需要的 Vant组件,(重启服务器)
- .vue 单文件 会根据代码中使用的组件 自动转换成 按需引入形式
// 使用 babel-plugin-import 后会自动按需引入 Vant组件
import { Button } from 'vant';
// Button组件 进行全局注册
Vue.use(Button);
<template>
<div>
<van-button type="primary">主要按钮</van-button>
<van-button type="success">成功按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
</div>
</template>
<script>
export default {}
</script>
效果展示
Vant组件 中的 弹出框样式,用于取代 HTML 自带的 alert() 弹出框
Vant组件的 弹出模态框,常用于消息提示、消息确认,或在当前页面内完成特定的交互操作。
支持组件调用和函数调用两种方式。相关文档
引入
通过 全局 或 局部 的方式引入 vant弹出框
全局
import { Dialog } from 'vant';
// 原型链方式注入
Vue.prototype.$dialog = Dialog;
<template>
<div>
<van-button type="primary" @click="btn">提示</van-button>
</div>
</template>
<script>
export default {
methods:{
btn(){
this.$dialog({message:'你好Vue!'});
}
}
}
</script>
效果展示
局部
<template>
<div class="box">
<van-button type="primary" @click="btn">提示</van-button>
</div>
</template>
<script>
import { Dialog } from 'vant';
export default {
methods:{
btn(){
Dialog({message:'你好Vue!'});
}
}
}
</script>
效果展示
参数介绍
Dialog 可以对其配置项的成员对象传参,呈现不同效果
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
title | 标题 | string | – |
width | 弹窗宽度,默认单位为 px |
number | string | 320px |
message | 文本内容,支持通过 \n 换行 |
string | () => JSX.ELement | – |
messageAlign | 内容对齐方式,可选值为 left right |
string | center |
theme | 样式风格,可选值为 round-button |
string | default |
className | 自定义类名 | string | Array | object | – |
showConfirmButton | 是否展示确认按钮 | boolean | true |
showCancelButton | 是否展示取消按钮 | boolean | false |
confirmButtonText | 确认按钮文案 | string | 确认 |
confirmButtonColor | 确认按钮颜色 | string | #ee0a24 |
confirmButtonDisabled v3.5.0 |
是否禁用确认按钮 | boolean | false |
cancelButtonText | 取消按钮文案 | string | 取消 |
cancelButtonColor | 取消按钮颜色 | string | black |
cancelButtonDisabled v3.5.0 |
是否禁用取消按钮 | boolean | false |
overlay | 是否展示遮罩层 | boolean | true |
overlayClass | 自定义遮罩层类名 | string | Array | object | – |
overlayStyle | 自定义遮罩层样式 | object | – |
closeOnPopstate | 是否在页面回退时自动关闭 | boolean | true |
closeOnClickOverlay | 是否在点击遮罩层后关闭弹窗 | boolean | false |
lockScroll | 是否锁定背景滚动 | boolean | true |
allowHtml | 是否允许 message 内容中渲染 HTML | boolean | false |
beforeClose | 关闭前的回调函数,返回 false 可阻止关闭,支持返回 Promise |
(action: string) => boolean | Promise<boolean> | – |
transition | 动画类名,等价于 transition 的 name 属性 |
string | – |
teleport | 指定挂载的节点,等同于 Teleport 组件的 to 属性 | string | Element | body |