3 Vue 基础语法
1:插值表达式又叫小胡子语法
<template>
<div>
{{str}}
{{obj.name}}
{{obj.age>18?"成年":"未成年"}}
</div>
</template>
<script>
export default{
name:'',
data () {
return {
str :'holle Wrod',
obj:{
name:'我是vue语法',
age:5
}
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
2 v-bind动态属性
- 语法:v-bind语法和简写
- 简写: :num="num"
<template>
<div>
<a :href='src'>去百度</a>
</div>
</template>
<script>
export default{
name:'',
data () {
return {
src :'http://www.baidu.com',
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
3 v-on和修饰符
- 语法 v-on:click='addSum'
- 简写:@click="addSum"
- 同时还可以函数传值 @click= "addSum(10)"
- v-on的修饰符 事件修饰
stop阻止事件冒泡pervent阻止默认行为 按键修饰符keydown键盘按下keyup键盘弹起keypress先按下后弹起
<template>
<div>
<!-- v-on用法 -->
<button v-on:click="addSum">sum+5</button>
<!-- v-on的简写 -->
<br>
<button @click="addSum">sum+10</button>
<br>
<!-- v-on的传值 -->
<button @click="addSum1(20)">sum+20</button>
{{sum}}
</div>
</template>
<script>
export default{
name:'',
data () {
return {
sum:0
}
},
created () {
},
computed:{
},
methods:{
addSum(a){
this. sum+=5
this.sum+=a
},
addSum1(a){
this.sum+=a
}
}
}
</script>
<style lang='less' scoped>
</style>
3 v-model和修饰符
- 语法:v-model="Vue数据变量"
- 双向绑定
- 变量变化==>视图自动同步
- 视图变化==》变量自动同步
v-model.修饰符="vue数据变量" - .number 以parseFloat转成数字类型
- .trim 去除首尾空白字符
- .lazy 在change时触发而非inupt时
<template>
<div>
<div>
<!-- (重要)
遇到复选框, v-model的变量值
非数组 - 关联的是复选框的checked属性
数组 - 关联的是复选框的value属性
-->
<span>爱好: </span>
<input type="checkbox" v-model="hobby" value="抽烟">抽烟
<input type="checkbox" v-model="hobby" value="喝酒">喝酒
<input type="checkbox" v-model="hobby" value="写代码">写代码
</div>
<div>
<span>性别: </span>
<input type="radio" value="男" name="sex" v-model="gender">男
<input type="radio" value="女" name="sex" v-model="gender">女
</div>
<div>
<span>自我介绍</span>
<textarea v-model="intro"></textarea>
</div>
</div>
</template>
<script>
export default {
data() {
return {
hobby: [],
sex: "",
intro: "",
};
// 总结:
// 特别注意: v-model, 在input[checkbox]的多选框状态
// 变量为非数组, 则绑定的是checked的属性(true/false) - 常用于: 单个绑定使用
// 变量为数组, 则绑定的是他们的value属性里的值 - 常用于: 收集勾选了哪些值
}
};
</script>
4 v-html和v-text
-
相同点:v-html和v-text都可以设置标签显示的内容不同点:v-html会解析标签,不会输出标签
v-text会把标签当成普通字符串输 -
需要注意一点的是:v-html或v-text会覆盖插值表达式
-
原理:因为插值表达式先被解析,指令晚于插值表达式
<template>
<div>
<p v-html="str"></p><br>
<span v-text="str1"></span>
</div>
</template>
<script>
export default {
data() {
return {
str: "<span>我是一个v-html渲染的标签</span>",
str1:"<span>我是一个v-text渲染的标签</span>"
}
}
}
</script>
5 v-if和v-show 控制标签的显示和隐藏
- 语法:
- v-show="vue变量"
- v-if="vue变量"
- 原理
- v-show 用的display:none隐藏 (频繁切换使用)
- v-if 直接从DOM树上移除
- 高级
- v-else使用
<template>
<div>
<h1 v-show="isOk">v-show的盒子</h1>
<h1 v-if="isOk">v-if的盒子</h1>
<div>
<p v-if="age > 18">我成年了</p>
<p v-else>还得多吃饭</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isOk: true,
age: 15
}
}
}
</script>
5 v-for 语法
- 语法
- v-for="(值变量, 索引变量) in 目标结构"
- v-for="值变量 in 目标结构"
- 目标结构:
- 可以遍历数组 / 对象 / 数字 / 字符串
- 注意: v-for的临时变量名不能用到v-for范围外 同级标签的key值不能重复
遍历数组
<template>
<div id="app">
<!-- v-for 把一组数据, 渲染成一组DOM -->
<!-- 口诀: 让谁循环生成, v-for就写谁身上 -->
<p>学生姓名</p>
<ul>
<li v-for="(item, index) in arr" :key="item">
{{ index }} - {{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
arr: ["小明", "小欢欢", "大黄"]
}
}
</script>
遍历对象
<template>
<div id="app">
<!-- 省略其他 -->
<p>学生详细信息</p>
<ul>
<li v-for="obj in stuArr" :key="obj.id">
<span>{{ obj.name }}</span>
<span>{{ obj.sex }}</span>
<span>{{ obj.hobby }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
// ...省略其他
stuArr: [
{
id: 1001,
name: "张三",
sex: "男",
hobby: "打球",
},
{
id: 1002,
name: "李四",
sex: "男",
hobby: "听音乐",
}
]
}
}
}
</script>
v-for 注意点1 避免v-for和v-if在一起使用
Vue 处理指令时,v-for 比 v-if 具有更高的优先级, 虽然用起来也没报错好使, 但是性能不高, 如果你有5个元素被v-for循环, v-if也会分别执行5次.
v-form 注意点2:
v-for更新监测
- 什么是v-for的就地更新监测呢,就是如果循环这个数组,利用数组的方法对数组进行操作,如果这些方法不 会改变原数组,就算数组数据改了,页面也不会改变的,
所以总结来说,修改数组是否更新页面取决于数组方法是否修改了原数组
例如会修改原数组的方法:push,pop,shift,unshift splice
不会修改原数组:slice/forEach/findeInde/every/map/reduce/indexOf
- 当然在Vue2的解决方法有:
- 1:可以将新数组覆盖给原数组即可解决
- 2:this.$set("数组名","修改哪个值","修改的值")
v-for就地更新
以前原生的for循环渲染的时候在dom页面生成,如果后面添加了一个数据,他会把所有的数据从dom上移除,然后重新for循环,重新生成数据,这时候就会出现一个问题,我每添加一个数据,都会重新销毁在重新生成这个过程,就会引起dom的重绘和回流,大大影响性能。
- 首先我们先回顾一个知识点:
- 重绘是什么:简单来说就是,页面元素的颜色/透明度等信息发生变化后触发重绘
- 回流是什么:页面元素的几何信息(宽高/位置)发生变化时触发回流
- 结论:回流一定会触发重绘,而重绘不一定会触发回流。
- 不建议使用js做动画,将来如果要做动画一定要用css
- 好了现在回归到正题,继续看在Vue中,v-for是怎么来更新dom的。
- 在Vue中v-for更新时, 是如何操作DOM的,然后解决这个问题的 首先了解一个知识点叫虚拟dom:什么是虚拟dom本质是保存节点信息,属性和内容的一个js 对象。
- 其实就是 循环出新的DOM结构, 和旧的DOM结构对比, 尝试复用标签就地更新内容,说白了,就是对比之后如果没改变就复用之前的标签,不做改动,发现有改动地方,就只需要在新虚拟dom 上修改不同的地方就可以了。
v-for中key的作用
在上面对比过程中就可以得知,新旧dom的对比,他是如何准确的找到哪些哪些是改变了,哪些是没有改变的,就通过key来查找,key跟key比较就会有唯一性,能准确查找到哪个地方改变了,如果用index来对比,以索引来对比就等同于没有对比,依旧是采用的就地复用策略,而key就不一样了,key值唯一不重复的字符或者数字在日常开发中,我们把key设置为id
5 动态class和动态style语法
- 给style赋值和class区别是?
- :class="{类名: 布尔值}”, true使用, false不用
- :style="{css属性名: 值}"
<template>
<div>
<button @click="btn">改变颜色</button>
<p :class="{dome:flag}"></p>
</div>
</template>
<script>
export default {
data() {
return {
flag:false
}
},
methods:{
btn(){
this.flag=true
}
}
}
</script>
<style scoped>
.dome{
width: 100px;
height: 100px;
background: red;
}
</style>
:style="{css属性名: 值}"
<template>
<div>
<span :style="{fontSize}">我是span标签</span>
</div>
</template>
<script>
export default {
data() {
return {
fontSize:"120px"
}
},
}
</script>
<style scoped>
.dome{
width: 100px;
height: 100px;
background: red;
}
</style>
6 计算属性 computed语法
- 计算属性名(){} 简单写法
- 必须要有return '值'
- 应用场景:当变量的值,需要通过的变量计算而得来
- 计算属性的最大特点:
计算属性内部依赖的所有数据,只要修改,会自动重新计算并更新页面 - 注意一点:
用法和data中的数据一模一样,他也是数据,所以不能和data中的数据重名 - 思考:在下面的代码中用表达式求和,用函数调用求和,都可以实现一样的功能,但是我们为什么要使用计算属性呢
答案:
是因为计算属性带缓存,如果依赖的变量不变,计算属性第一次计算后,他会进行缓存,下次再用到求和的时候,他会直接从缓存中拿去,假设这个求和功能在页面中用到了100次,这就是大大提高了性能
<template>
<div>
<span>计算属性求和:{{num }}</span><br>
<span>表达式求和{{a+b}}</span><br>
<span>函数调用{{sum(a,b)}}</span>
</div>
</template>
<script>
export default {
data() {
return {
a:10,
b:10
}
},
// 函数调用
methods:{
sum (a,b){
return a+b
}
},
// 计算属性名(){}
// 必须要有return '值'
computed:{
num(){
return this.a+this.b
}
}
}
</script>
<style scoped>
</style>
计算属性的完整写法
计算属性简单写法一般很难满足我们日常的开发,所以要学习计算属性的完整写法get和set
get当计算属性被访问时触发,有缓存, 必须要返回一个值,内部依赖属性被修改时触发, 将赋的值将以参数的形式带给set方法set 方法:当计算属性被修改时触发,将赋的值以参数的形式带个set方法
computed:{
// 计算属性名(){}
// 必须要有return '值'
//
num:{
set(){
},
get(){
return '值'
}
}
}
}
7 侦听器 watch 语法的使用
<template>
<div>
姓名:<input type="text" v-model="name">
</div>
</template>
<script>
export default{
name:'',
data () {
return {
name:'张三'
}
},
created () {
},
computed:{
},
methods:{
},
watch:{
// 侦听的属性名 (value1,value2)
//value1 是新值
//value2 是旧值
name(value1,value2){
console.log(value1,value2,"name被修改了");
}
}
}
</script>
<style lang='less' scoped>
</style>
上面侦听只能侦听值类型的简单数据类型,而复杂类型就需要用到深度侦听 深度侦听有两个参数
- deep:true, 开启深度侦听
- handler,类型为深度侦听
- 注: 深度侦听,Vue无法准确把旧值传递过来,这是Vue设计上的缺陷
<template>
<div>
姓名:<input type="text" v-model="name"><br>
年龄:<input type="text" v-model="obj.age">
</div>
</template>
<script>
export default{
name:'',
data () {
return {
name:'张三',
obj:{
age:18
}
}
},
created () {
},
computed:{
},
methods:{
},
watch:{
// 侦听的属性名 (value1,value2)
name(value1,value2){
console.log(value1+'新值',value2+'旧值',"name被修改了");
},
obj:{
deep:true,
handler(ageVal,ageVal1){
// 深度侦听,Vue无法准确把旧值传递过来,这是Vue设计上的缺陷
console.log(ageVal,ageVal1,"age被修改了");
}
}
}
}
</script>
<style lang='less' scoped>
</style>
8 组件 语法(组件创建,组件通信)
1:为什么要使用组件
- 就是同一个样式或者功能,实现不同地方的复用,各自独立,便于复用
- 组件四个步骤:
- 1:创建组件
- 2:引入组件
- 3:注册组件
- 4:使用组件
<template>
<div>
//使用组件
<Panel></Panel><hr>
<Panel></Panel><hr>
<Panel></Panel>
</div>
</template>
<script>
//引入组件
import Panel from './components/Panel.vue'
export default{
name:'',
//注册组件
components:{
Panel
},
data () {
return {
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
介绍一点注册全局组件的方法: 在main.js中,引入
- import Panel from './components/Panel.vue'
- 注册 Vue.component("Panel",Panel)
8.1组件的scoped的使用和原理
- 什么是scoped
- 在Vue组件内样式,只针对当前组件内标签生效如何做
- style上添加scoped
<style lang='less' scoped> </style>
原理:他做了两件事- 1 scoped给当前组件内标签都被添加了data-v-hash值得属性
- 2:css选择器都被添加了[data-v-hash]的属性选择器。
8.2:组件的通信
父向子传递 props
<template>
<div>
<Panel :str='str'></Panel>
</div>
</template>
============父组件
============子组件
<template>
<div>
<h1>{{str}}</h1>
</div>
</template>
<script>
export default{
name:'',
props:{
str:{
type:String,
default:''
}
},
}
</script>
<style lang='less' scoped>
</style>
8.2.1子向父传值
- 使用自定义事件
- 1:在父组件中给子组件绑定自定义事件
- 2:当子组件执行$emit触发父组件事件时,父组件的事件处理函数会执行。
- 注:this.$emit 方法触发父组件绑定的自定义事件,他有两个参数
- 参数1:事件名
- 参数2:要传递过去的参数
============================子组件
<template>
<div>
<h1>{{str}}</h1>
<button @click="btn">点我修改父组件的值</button>
</div>
</template>
<script>
export default{
name:'',
props:{
str:{
type:String,
default:''
}
},
methods:{
btn(){
this.$emit("dome","大河之水天上来")
}
}
}
</script>
<style lang='less' scoped>
</style>
==============================父组件
<template>
<div>
<Panel :str='str' @dome="upDome"></Panel>
<h1>{{str}}</h1>
</div>
</template>
<script>
import Panel from './components/Panel.vue'
export default{
name:'',
components:{
Panel
},
data () {
return {
str:'窗前明月光'
}
},
created () {
},
computed:{
},
methods:{
upDome(str){
this.str=str
}
}
}
</script>
<style lang='less' scoped>
</style>
8.2.2 单向数据流
- 问题:当我们子组件接收到了父组件传过来的值,能不能不用自定义事件来,我们能否在子组件这边直接修改他的值呢,
- 答案就是:不能
- 原理:从父到子的数据流向,叫单向数据,在Vue中规定,不能直接修改父组件的值,原因就是假设在项目中这个值如果在很多子组件都用到了,各个子组件都在改这个值,一旦这个值出了问题就很排查,数据管理起来很混乱。
9 Vue中的生命周期
9.1 Vue生命周期分为四个周期
1 初始化阶段 2 挂载阶段 3 更新阶段 4 销毁阶段
9.2接下详细讲解这个4个阶段8个方法
初始化 这个阶段Vue做的事情
- beforeCreate:初始化事件和生命周期函数,这个时候生命周期钩子函数被执行了
- created:Vue内部添加了data和methods等实例,实例创建了
挂载阶段 这个阶段Vue做的事情
- 在beforeMount执行之前,
- 1:首先会对template标签进行检查
- 有:编译template返回render渲染函数
- 无:编译el选项对应标签作为template(要渲染的模板)
- 2:虚拟dom挂载成真实dom之前
- 这个时候beforeMount生命周期钩子函数被执行
- 3:Create 把虚拟dom和渲染的数据一并挂载到真实dom上
- 5:真实dom挂载完毕,页面已经出来了
- 6:mounte 生命周期钩子函数被执行,这时候就停在这了
更新阶段 这个阶段Vue做的事情
- 1 当data里数据改变,更新dom之前
- 2 beforeUpdate 生命周期钩子函数被执行
- 3 虚拟dom重新渲染,打补丁到真实dom
- 4 updated 生命周期钩子函数被执行,当data中数据发生变化并更新页面后执行这个钩子函数。
- 5 当有data数据改变,重复这个循环
销毁阶段 这个阶段Vue做的事情
- 1 比如组件dom被移除例如v-if
- 2 beforeDestroy 生命周期钩子函数被执行
- 3 拆卸数据监视器,子组件和事件侦听器
- 4 实例销毁后,最后触发一个钩子函数
- 5 destroyed 生命周期钩子函数被执行了
10 组件的插槽的使用
- 为什么要使用插槽 可以让组件内容自定义,极大的提高组件的灵活性
- 插槽分为
- 匿名插槽
- 具名插槽
- 作用域插槽
10.1 匿名插槽的使用
<slot></slot> 占位符使用
-
1 在组件内找到需要插槽的地方,使用占位
-
2:在使用组件是,将要自定的标签传入组件中
子组件
<template>
<div>
<h1>李白</h1>
<!-- slot占位符 -->
<slot></slot>
</div>
</template>
<script>
export default{
name:'Panel',
data () {
return {
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
父组件
<template>
<div>
<Panel>
<h3>窗前明月光</h3>
<h3>疑是地上</h3>
<h3>举头望明月</h3>
<h3>低头思故乡</h3>
</Panel><hr>
<Panel>
<h3>日照香炉生紫烟</h3>
<h3>遥看瀑布挂前川</h3>
<h3>飞流直下三千尺</h3>
<h3>疑是银河落九天</h3>
</Panel><hr>
<Panel>
<h3>故人西辞黄鹤楼</h3>
<h3>烟花三月下扬州</h3>
<h3>孤帆远影碧空尽</h3>
<h3>唯见长江天际流</h3>
</Panel>
</div>
</template>
<script>
import Panel from './components/Panel.vue'
export default{
name:'',
components:{
Panel
},
data () {
return {
}
},
}
</script>
<style lang='less' scoped>
</style>
10.3 默认内容
- 什么是默认内容
- 就是使用我组件的人,不给slot传标签,就是把slot标签里面的内容当做默认内容 父组件
<template>
<div>
<Panel>
<h3>窗前明月光</h3>
<h3>疑是地上</h3>
<h3>举头望明月</h3>
<h3>低头思故乡</h3>
</Panel><hr>
<Panel>
<h3>日照香炉生紫烟</h3>
<h3>遥看瀑布挂前川</h3>
<h3>飞流直下三千尺</h3>
<h3>疑是银河落九天</h3>
</Panel><hr>
<Panel>
<h3>故人西辞黄鹤楼</h3>
<h3>烟花三月下扬州</h3>
<h3>孤帆远影碧空尽</h3>
<h3>唯见长江天际流</h3>
</Panel>
<Panel></Panel>
</div>
</template>
<script>
import Panel from './components/Panel.vue'
export default{
name:'',
components:{
Panel
},
data () {
return {
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
子组件
<template>
<div>
<h1>李白</h1>
<!-- slot占位符 -->
<slot>
<h1>默认内容</h1>
</slot>
</div>
</template>
<script>
export default{
name:'Panel',
data () {
return {
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
10.2:具名插槽的使用
具名插槽的基本使用
1:在组件内给slot设置name属性
2:在使用组件时,通过template包裹不同区域的代码
3:在template标签上使用v-slot名称,来指定name
4:v-slot:可以简写#
父组件
<template>
<div>
<Panel>
<template v-slot:title>
<h1>静夜思---李白</h1>
</template>
<template #content>
<h3>窗前明月光</h3>
<h3>疑是地上</h3>
<h3>举头望明月</h3>
<h3>低头思故乡</h3>
</template>
</Panel><hr>
<Panel>
<template v-slot:title>
<h2>李白---望庐山瀑布</h2>
</template>
<template #content>
<h3>日照香炉生紫烟</h3>
<h3>遥看瀑布挂前川</h3>
<h3>飞流直下三千尺</h3>
<h3>疑是银河落九天</h3>
</template>
</Panel><hr>
<Panel>
<template v-slot:title>
<h2> 李白---黄鹤楼送孟浩然之广陵</h2>
</template>
<template #content>
<h3>故人西辞黄鹤楼</h3>
<h3>烟花三月下扬州</h3>
<h3>孤帆远影碧空尽</h3>
<h3>唯见长江天际流</h3></template>
</Panel>
<Panel></Panel>
</div>
</template>
<script>
import Panel from './components/Panel.vue'
export default{
name:'',
components:{
Panel
},
data () {
return {
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
子组件
<template>
<div>
<slot name='title'></slot>
<!-- slot占位符 -->
<slot name="content">
<h1>默认内容</h1>
</slot>
</div>
</template>
<script>
export default{
name:'Panel',
data () {
return {
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
10.3:作用域插槽的使用
- 1:在子组件上,在slot动态绑定一个数据
- 2:在使用组件时,template标签 v-slot='自定义变量'一般都默认变量为(scope)接收传递过来的数据
- 3:自定义的变量就是一个对象,会自动收集传递过来的数据,然后使用就可以了 父组件
<template>
<div>
<Panel>
<template v-slot='scope'>
<h1>{{scope.row.name}}</h1>
<h1>{{scope.row.title}}</h1>
<h3>日照香炉生紫烟</h3>
<h3>遥看瀑布挂前川</h3>
<h3>飞流直下三千尺</h3>
<h3>疑是银河落九天</h3></template>
</Panel>
<Panel>
<template v-slot='scope'>
<p>{{scope.row.name}}</p></template>
</Panel>
</div>
</template>
<script>
import Panel from './components/Panel.vue'
export default{
name:'',
components:{
Panel
},
data () {
return {
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
子组件
<template>
<div>
<slot :row='obj'></slot>
</div>
</template>
<script>
export default{
name:'Panel',
data () {
return {
obj:{
name:'李白',
title:'望庐山瀑布'
}
}
},
created () {
},
computed:{
},
methods:{
}
}
</script>
<style lang='less' scoped>
</style>
11 自定义指令的使用
全局注册:
Vue.directive("自定义指令名",{
inserted(el){
//可以对el标签的扩展额外功能
}
})
局部注册
directives:{
"指令名":{
inserted(el){
//el进行操作
}
}
}
11 $refs获取原生DOM
1:目标标签--添加id/ref
<h1 ref='myH1' id="h">ref/id获取原生dom</h1>
2:恰当时机,通过id/通过fef属性获取目标标签
mounted(){
方法1:通过原生的方式来获取dom
document。getElementById("h")
方法2:通过ref属性来获取dom
this.$refs.myH1
}
12 $nextTick处理异步
为什么要用nextTick,因为有时候我们修改数据后并没有立即去更新dom,因为dom的更新是异步的,他的流程:数据修改=>VUe监听到数据变化=>创建一个异步任务(更新DOM)=>继续执行同步代码=>当DOM更新结束后执行updated钩子函数
$this.nextTick(()=>{
//内容代码
})
v-model的本质
1:给元素绑定一个value属性 2:给元素绑定一个input事件 v-model,是实现双向数据绑定的指令,本质是一个语法题,他一共做了两件事, 1:给元素绑定了一个value属性,单向的数据流 2:给元素绑定一个input事件,数据修改时反向同步给父组件。
12 路由 学习
- 12.1 什么是路由:路由就是路径和组件的映射关系
- 12.2 为什么要使用路由:因为Vue组件是单页面应用(SPA):所有的功能在一个html上实现,而前端的路由作用就是实现业务场景的切换,说白了路由就是用来组件切换
- 路由的好处就是:整体不刷新,用户体验更好
- 数据传递容易,开发效率高。
- 缺点:首屏加载会比较慢一点,不利于seo
12.3 vue-router
- 概念:本质就是一个三方包
- 组件分类:页面组件(在项目中我们把页面组件放在src/wiews文件夹),复用组件(src/components文件夹中)
12.3.1vue-router的使用
- 1:下载vue-router模块到当前工程 npm i vue-router@3.5.3
- 2:在main.js引入VueRouter函数 import VueRouter from 'vue-router'
- 3:添加到Vue.use()身上,注册全局的router——link和router——view组件 Vue.use(VueRouter)
- 4:创建路由规则数组 :路径和组件名对应的关系
import Vue from 'vue'
import App from './App.vue'
//第一步下包npm i vue-router@3.5.3指定版本,门前最新的router是vue3的
//第二步 导入路由
import VueRouter from 'vue-router'
//第三步 使用路由插件
Vue.use(VueRouter)
// 第四步创建路由规则
import Find from './views/Find.vue'
import My from './views/My.vue'
import Part from './views/Part.vue'
const routes=[
{
path:'/find',
component:Find
},
{
path:'/my',
component:My
},
{
path:'/part',
component: Part
}
]
// 第五步 创建路由对象 -传入规则
const router=new VueRouter({
routes
})
//第六步关联到vue实例
new Vue({
render: h => h(App),
router
}).$mount('#app')
//第七步占位符占位
<router-view></router-view>
-
5:用规则生成路由对象 const router = new VueRouter({ routes })
-
6:把路由对象注入到new Vue 实例中 const router = new VueRouter({ router })
-
7:components换成router-view,用router-view作为挂载点,切换不同的路由页面
<router-view></router-view>
这里需要注意的是router和routes
声明式导航--跳转
- router提供了一个全局组件 router-link
- router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)
- router-link提供了声明式导航高亮的功能(自带类名)
- 总结: 链接导航, 用router-link配合to, 实现点击切换路由
声明式导航--传参在router-link上的to属性传值, 语法格式如下
-第一种: /path?参数名=值
<!-- 第一种查询字符串传参 -->
<router-link to="/part?name=小明&age=18&sex=男">查询字符串传参</router-link>
-----------------------------
<!-- 对应的组件页面接收$route.query -->
<h1>我是part页面</h1>
<h1>{{$route.query.name}}</h1>
<h1>{{$route.query.age}}</h1>
<h1>{{$route.query.sex}}</h1>
- 第二种: /path/值 – 需要路由对象提前配置 path: “/path/参数名”
动态路劲传参
第一步路径上/传参
<router-link to="/my/小红/28/女">动态路径传参</router-link>
第二步路由规则上配置
{
path:'/my/:name/:age/:sex',
component:My
},
------------------------- <!-- 对应的组件页面接收$route.params -->
<h1>我是my页面</h1>
<h1>{{$route.params.name}}</h1>
<h1>{{$route.params.age}}</h1>
<h1>{{$route.params.sex}}</h1>
编程式导航
就是利用js代码来跳转和传参,因为声明式导航需要route-link标签的支持,所以有一定的局限性
第一种根据路由规则路径来跳转
<template>
<div>
<div class="footer_wrap">
<!--声明式导航 -->
<!-- <router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link> -->
<!-- 编程式导航跳转--根据路径跳转 -->
<span @click="goto('/find')">发现音乐页面</span>
<span @click="goto('/my')">我的音乐页面</span>
<span @click="goto('/part')">我的朋友页面</span>
</div>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
methods:{
goto(path){
// this.$route 路由参数对象,用于获取传递过来的参数
// this.$router 路由对象,进行路由跳转
this.$router.push({
path
})
// 简写--可以直接传字符串
//this.$router.push('/find')
// this.$$router.push(path)
}
}
};
</script>
第二种根据name来跳转
const routes=[
{
path:'/find',
component:Find,
name:'Find'
},
{
path:'/my',
component:My,
name:'My'
},
{
path:'/part',
component: Part,
name:'Part'
},
============================在路由配置name属性
<template>
<div>
<div class="footer_wrap">
<!--声明式导航 -->
<!-- <router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link> -->
<!-- 编程式导航跳转--根据路径跳转 -->
<span @click="goto('Find')">发现音乐页面</span>
<span @click="goto('My')">我的音乐页面</span>
<span @click="goto('Part')">我的朋友页面</span>
</div>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
methods:{
goto(name){
// this.$route 路由参数对象,用于获取传递过来的参数
// this.$router 路由对象,进行路由跳转
this.$router.push({
name
})
}
}
};
</script>
声明式导航传参两种方式无需任何配置
- 注意点1:如果用的是path路径跳转,则不能params来传,因为path会忽略params
- 所以如果用的是path来跳转的,则只能用query来传参
- 注意点2:如果比较隐私的数据用query建议不用query传,因为query在路径上
组合:
path+query传参
name+params 页面刷新后数据会丢失,但是不用担心丢失问题,因为传过去我们已经用了
<template>
<div>
<div class="footer_wrap">
<!--声明式导航 -->
<!-- <router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link> -->
<!-- 编程式导航跳转--根据路径跳转 -->
<span @click="goto">发现音乐页面</span>
<span @click="gotoQuery">我的音乐页面</span>
<span @click="gotoParams">我的朋友页面</span>
</div>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<script>
/* 注意点:如果用的是path路径跳转,则不能params来传,因为path会忽略params
所以如果用的是path来跳转的,则只能用query来传参。*/
/* 总结:组合:path+query传参
name+params 页面刷新后数据会丢失
*/
export default {
methods:{
gotoQuery(){
this.$router.push({
path:'/my',
query:{
name:'假如生活欺骗了你'
}
})
},
========================query传query接
<h1>我是my页面</h1>
<h1>{{$route.query.name}}</h1>
=====================================
gotoParams(){
this.$router.push({
name:'Part',
params:{
name:'不要悲伤,不要心急'
},
query:{
age:18
}
})
},
goto(){
}
}
}
</script>
======================params传params接
<h1>我是part页面</h1>
<h1>{{$route.query.age}}</h1>
<h1>{{$route.params.name}}</h1>
重定向和模式
什么是路由重定向(redirect)
就是匹配path后强制跳转path的路径
const routes = [
{
path: "/", // 默认hash值路径
redirect: "/find" // 重定向到/find
// 浏览器url中#后的路径被改变成/find-重新匹配数组规则
}
]
404页面(*)
- 请求一个不存在页面,我们可以显示404页面
- 可以新建一个组件做404页面,我们为给用户更好的体验,写样式,然后*号配置路由即可
import NotFound from '@/views/NotFound'
const routes = [
// ...省略了其他配置
// 404在最后(规则是从前往后逐个比较path)
{
path: "*",
component: NotFound
}
]
路由模式设置(*)
在实例化路由对象时,传入mode选项和值修改
const router = new VueRouter({
routes,
mode: "history" // 打包上线后需要后台支持, 模式是hash
})
嵌套和守卫
路由嵌children
const routes = [
// ...省略其他
{
path: "/find",
name: "Find",
component: Find,
children: [
{
path: "recommend",
component: Recommend
},
{
path: "ranking",
component: Ranking
},
{
path: "songlist",
component: SongList
}
]
}
// ...省略其他
]
14.Vue路由-声明式导航-类名区别
目标
目标: router-link自带的2个类名的区别是什么
- router-link-exact-active (精确匹配) url中hash值路径, 与href属性值完全相同, 设置此类名
- router-link-active (模糊匹配) url中hash值, 包含href属性值这个路径
15.Vue路由-全局前置守卫
什么是路由守卫(就是让路由有权限判断)
-
- 路由在真正跳转前, 会执行一次beforeEach函数, next调用则跳转, 也可以强制修改要跳转的路由
-
- 在路由对象上使用固定方法beforeEach 3:beforeEach他有三个参数
-
参数1 to: 要跳转到的路由 (路由对象信息) 目标
-
参数2 from: 从哪里跳转的路由 (路由对象信息) 来源
-
参数3 next,是否放行 (true,false)
-
注意一点:导航守卫一旦注册,必须使用next,否则路由系统就会瘫痪。
// 目标: 路由守卫
// 场景: 当你要对路由权限判断时
// 语法: router.beforeEach((to, from, next)=>{//路由跳转"之前"先执行这里, 决定是否跳转})
// 参数1 to: 要跳转到的路由 (路由对象信息) 目标
// 参数2 from: 从哪里跳转的路由 (路由对象信息) 来源
// 参数3 next: 函数体 - next()才会让路由正常的跳转切换, next(false)在原地停留, next("强制修改到另一个路由路径上") 决定是否放行
// 注意: 如果不调用next, 页面留在原地
// 例子: 判断用户是否登录, 是否决定去"我的音乐"/my
const isLogin = true; // 登录状态(未登录)
router.beforeEach((to, from, next) => {
if (to.path === "/my" && isLogin === false) {
alert("请登录")
next(false) // 阻止路由跳转
} else {
next() // 正常放行
}
})
总结: next()放行, next(false)留在原地不跳转路由, next(path路径)强制换成对应path路径跳转
16.Vue路由-组件缓存
组件缓存的好处:不会频繁创建和销毁组件,页面更快的呈现
1. <keep-alive></keep-alive>, 裹切换的挂载点即可
2:用法
<keep-alive>
<router-view></router-view>
</keep-alive>
2:
18.Vue路由-组件缓存-新钩子函数
- 1:上面介绍到了组件缓存,但是如果我只想缓存某个组件或者说我不想缓存某个组件怎么做
- 2:keep-alive的高级用法他有两个属性来解决上面的问题
- 第一步:给组件取个名字
<script>
export default {
name: '组件名'
}
</script>
- 第二步:包含缓存和不包含两种include/exclude
- include 只包含写入到include这个组件,一定要组件名,多个用逗号隔开
<keep-alive include="Find">
<router-view></router-view>
</keep-alive>
include 不包含写入到exclude这个组件,一定要组件名,多个用逗号隔开
<keep-alive exclude="My,Part">
<router-view></router-view>
</keep-alive>
19 组件路由钩子
- 前面学了8个路由钩子,现在扩展两个
- 使用keep-alive后, 新增了2个钩子函数
- 组件不执行销毁/初始化创建的方法了, 如何知道组件被失去激活/激活呢?
- activated --- 组件被激活状态
- deactivated --- 组件被失去激活状态
activated () {
console.log("发现音乐-组件激活");
},
deactivated () {
console.log("发现音乐-组件失去激活");
}