前言
作为前端开发工程师,咱们今天就来学习总结下主流框架之一的vue,与react都是spa(单页面应用,根html)声明式编程(对应的是原生js命令式编程)。这里我主要总结了一些个人的学习过程中知识点,尽可能的讲通俗易懂的话,更全面更术语更详细的内容还是推荐大家到Vue官网学习。
VUE官方文档
简介
渐进式框架(可以将vue作为项目的一部分嵌入其中,多框架开发一个项目,也叫混合开发),易于上手,有很多三方插件支持vue,反正用vue开发就是好。
MVVM原理
面试可能会经常问到MVVM(M:模型/就是data数据; V:视图/就是页面上的dom;VM:连接数据和视图的桥梁/就是实例化的Vue函数)。这个东西吧,是vue参考了MVVM模型,可不是尤雨曦创造出来的,更不是Vue特有,大佬也相互抄作业。
这个图捏,主要是对比了下MVVM和MVC的区别,能看懂呢最好,看不懂也没事嗷,那谁不说了吗?知道鸡蛋好吃就行,没必要非得看看老母鸡长啥样,反正你不懂这个不会影响你用Vue。但凡事都有例外么,话说回来,这个东西你可以这么去理解,M就是饭店的大厨,V就是到饭店吃饭的客人,VM就是服务员,大厨(M)会做什么菜呢,写个菜单(data数据)让服务员(VM)告诉客人(V)对吧,然后呢,客人有不同口味,喜欢辣呀还是不喜欢辣(V的用户交互行为),告诉服务员(VM),然后呢服务员(VM)告诉大厨(M),客人点什么菜口味怎么样(用户交互行为),大厨做好了(后台处理好的数据)之后服务员再端给客人,客人就吃到了自己想吃的菜。这个个过程就是MVVM的执行过程。
v-指令
1. v-cloak
可以解决插值表达式在网速慢的情况下闪烁的问题(页面出现插值表达式),这里就简单书写用法:
<p v-cloak>{{msg}}</p>
<style>
[v-cloak]{
display:none
}
</style>
2. v-text
另外一种写法就是不用插值表达式来渲染我们的变量,而是用v-text:
<p v-text="msg"></p>
虽然v-text可以自动避免闪烁的问题,但是无法像插值表达式那样实现内容的任意拼接,并且会覆盖标签内容:
<p>这是拼接前面的内容{{msg}}这是拼接后面的内容</p>
<p v-text="msg">这里是v-text会被覆盖的内容</p>
3. v-html
当我们的msg变量中是一个xml表达式时(msg:<h1>我是h1标签</h1>
),插值表达式和v-text都会对其原样输出(容易导致xss攻击,用户表单提交元素上一定要避免使用),这时就要使用v-html:
<p v-html="msg"></p>
4. v-bind
是vue中用于绑定属性的指令,可以直接简写为‘:’,v-bind中可以写合法的js表达式:
/* myTitle:这是一个自定义的标题 */
<input type='button' value='按钮' :title='myTitle+"123"'>
<input type='button' value='按钮' v-bind:title='myTitle'>
/* 第一个标签的title属性为:这是一个自定义的标题123,
第二个标签的title属性为:这是一个自定义的标题 */
这里值得注意的是,通过v-bind绑定的类名vue内部会与class类名进行合并处理。
<p class="text" :class="{active:true, otherName:false}">这是一个p标签</p>
最终p标签上会有两个类名,text和active。
5. v-once:
只渲染数据的默认值,后期数据被更改页面不做响应
<div v-once>就只渲染一次动态属性,后续不会更新{{msg}}</div>
6. v-if:
创建和删除元素,多元素可以外层包裹template标签包裹(配合使用)
7. v-show:
不进行dom操作,只是切换元素的display样式为none
如果项目中涉及到频繁的切换,则尽量使用v-show,以减少对dom的操作,提升性能;如果长期操作不会被用户看到则使用v-if,不能配合template标签使用
8. v-for:
用于数据循环渲染,记得绑定key,可以循环的数据类型有数组、对象、字符串、循环次数。
9. v-else | v-else-if:
配合v-if使用,但结构不能打断
10. v-pre
不做vue语法解析,会将内容原封不动的展示出来<p v-pre>{{count}}</p>
,页面展示为{{count}}
推荐使用场景:纯静态文本没有差值语法和变量参与的标签可以加,用于提升性能,减少vue解析
11. v-on
事件绑定机制,可以简写为‘@’,参数有:click(点击)mouseover(鼠标滑过)…在vue中事件函数要写在methods对象中:
<input type='button' value='按钮' v-on:click='btn' >
<input type='button' value='按钮' @click='btn'>
/* methods:{
btn(){
alert('这是一个按钮')
}
} */
事件中获取元素对象可以在函数中传$event:
<p @click='dianji(canshu1,$event)'>点我啊</p>
@scroll 监听滚动条滚动
@wheel 监听鼠标滚轴滚动
【事件修饰符】
.stop
阻止冒泡
.prevent
阻止默认事件
.capture
添加事件侦听器时使用事件捕获模式(由外而内的触发)
.self
只当事件在该元素本身(不是子元素或父元素)触发时生效
.once
事件只触发一次
.left
左键
.right
右键
.middle
中间滚轮
.lazy
表单元素懒更新(文本域当用户输入一大段话并不实时更新数据,只有当输入框失焦才会更新data数据)
.passive
事件默认行为立即执行,无需等待事件回调执行完毕
事件修饰符可以多个连用前后位置不影响效果,其中self和stop都可以起到阻止冒泡的作用,但区别是.self只会阻止自己身上冒泡行为的触发,并不会真正阻止冒泡行为。
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>
记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
vue提供的别名按键,可以使用按键原始的key值去绑定,但双单词按键要用横线命名(caps-lock),当然,也不是所有的按键都能绑定事件,比如:音量控制键、键盘光以及一些外置个性化的按键。
vue提供全部的按键别名:
.enter
.tab(特殊,必须配合keydown使用,tab本身的功能有切换当前元素焦点)
.delete (捕获 “删除” 和 “退格” 键)
.esc
.space
.up
.down
.left
.right
表单修饰符:
.lazy
搭配v-model使用
.native
将组建绑定的原生事件自动绑定到组建内部第一个跟元素上(如果没有该修饰符,事件将被认为是自定义事件)
以下是系统修饰键(用法特殊):
(1) 配合keyup时,按下修饰键的同时再按下其它键,随后释放其它键,事件才被触发
(2) 配合keydown使用,正常触发事件
.ctrl
.alt
.shift
.meta
不推荐使用keyCode去指定按键(越来越个性化的键盘,上面的keycode未必一致)
可以通过Vue.config.keyCodes.自定义键名 = 键码
去自定义按键别名
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
.number
<!-- 自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值)-->
<input v-model.number="age" type="number">
.trim
<!-- 自动过滤用户输入的首尾空格 -->
<input v-model.trim="msg" >
12. v-model
可以实现表单元素中数据的双向绑定,该指令只能运用在表单元素中,如:input(radio,text,address,email…)、select、checkbox、textarea…
这个方法不可以去绑定props中传过来的值,因为props值不能直接更改,如果props数据是对象类型,则可以绑定对象里的某个值(对象的某个值被更改并不会视作更改对象,内存中的引用地址并未发生改变)
在VM实例中,如果想要获取data上的数据,或者调用methods中的方法,必须通过this.数据属性名 或 this.方法名 来进行访问,这里的this就表示我们new出来的VM实例对象:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>跑马灯</title>
<!-- 引入vue.js-->
<script src="./lib/vue.js"></script>
</head>
<body>
<div id="app">
<button @click="show">浪起来</button>
<button @click="stop">低调</button>
<h3 v-text="message"></h3>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"浩浩爱茜茜~茜茜爱浩浩~",
timer:null
},
methods:{
show(){
if(this.timer != null) return;
this.timer = setInterval(() => {
//获取到头的第一个字符
let start = this.message.substring(0,1);
//获取到后面的所有字符
let end = this.message.substring(1);
this.message = end + start;
},300)
},
stop(){
//清除定时器
clearInterval(this.timer)
//清除定时器之后,需要重新将定时器置为null
this.timer = null
}
}
})
</script>
</body>
</html>
收集表单数据:
若<input type="text"/>
,则v-model收集的是value值,用户输入的就是value值。
若<input type="radio"/>
,则v-model收集的是value值,且要给标签配置value值。
若<input type="checkbox"/>
1. 没有配置input的value属性,那么收集的就是checked(勾选或者未勾选,是布尔值)
2. 配置input的value属性:
a. v-model的初始值是非数组,那么收集的就是checked(勾选或者未勾选,是布尔值)
b. v-model的初始值是数组,那么收集的就是value组成的数组
vue过滤器
filters:在视图里二次处理数据的作用
<div id="app">
{{ message | capitalize }}
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'runoob'
},
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
</script>
过滤器可以串联:
{{ message | filterA | filterB }}
计算属性-computed
应用时机:要用的属性不存在,需要通过已有的属性计算得来
原理:底层借助了Object.defineproperty方法提供的getter和setter
get函数在初次读取和依赖数据发生改变时会被调用。
相比较methods,他有缓存机制,当页面多次调用渲染,非值改变则不会重复调用计算,效率更高。
计算属性最终会出现在vm上,直接读取使用即可,如果计算属性被修改,那必须写set函数去相应修改,且要更改计算的依赖属性数据。
<div id="app">
<p>原始字符串: {{ message }}</p>
<p>计算后反转字符串: {{ reversedMessage }}</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: '这是测试文字'
},
// computed: {
// 计算属性的 getter
// reversedMessage: {
// 详细写法
// get() { // 读取
// return this.message.split('').reverse().join('')
// }
// set( value ) { // 更改
// this.message = value
// }
// }
// }
// 简写
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
</script>
监听-watch
- 当属性变化的时候,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种方法:
// 被监听属性可以是data里的属性,也可以是计算属性
watch:{
被监听属性:{
immediate: true, // 初始化时即监听,否则值改变时开始监听
deep: false, // 深度监视 多嵌套结构
handler(newValue,oldValue){
console.log('新值',newValue,'旧值',oldValue)
}
}
}
// 简写
watch: {
被监听属性(newVal,oldVal) {
}
}
// 一定是字符串类型的属性书写,否则找不到
const vm = new Vue({...})
vm.$watch('被监听属性',{
...
})
// 如果是简写
vm.$watch('被监听属性',function(newVal,oldVal){
})
深度监视:
Vue中的watch默认不监测对象内部值的改变(一层)
配置deep: true可以监测对象内部值的改变(多层)
【vue自身可以监测队形内部值的改变,但vue提供的watch默认不可以,使用时就需要根据数据的具体结构决定是否采用深度监视】
watch&computed区别
computed能完成的功能,watch都可以完成,但computed有缓存,watch中可以进行异步操作。
vue监视数据原理
vue会监视data中所有层次的数据。
- 对象数据
通过setter实现监视,且要在new vue时就传入要监测的数据。
对象中后加的属性,vue默认不做响应式处理。如需后添加的属性做响应式处理,请使用以下方法:
Vue.set(target, propertyName/index, value)
vm.$set(target, propertyName/index, value) - 数据数据
通过包裹数组更新元素的方法实现,本质就是做了两件事:调用原生对应的方法对数组进行更新;重新解析模版,进而更新页面。
在vue中修改某个元素的方法:push(),pop(),shift(),unshift(),splice(),sort(),reverse();Vue.set() 或 vm.$set()
set()不能直接给data(根数据)添加属性!!!
表单元素使用
input 类型为text ,则v-model收集的是value值,用户输入的是value值。
input 类型为radio,则v-model收集的是value值,且要给标签配置value值。
input 类型为CheckBox:
- 若没有配置value值,收集的就是checked(勾选or未勾选, 是布尔值);
- 有配置value值:
a. v-model初始值是非数组,那收集的依然是checked(勾选or未勾选, 是布尔值)
b. v-model初始值是数组,那收集的value值就是value组成的数组
注:
v-model有三个修饰符:lazy: 失去焦点时收集数据; number:输入字符串转为有效数字; trim:输入收尾空格过滤
过滤器
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:
- 注册过滤器: Vue.filter(name,callback) 或 new Vue{filters:{ 过滤器名(管道符【| 就叫管道符】前面的变量,其它参数){ return 页面上要展示的结果} }}
- 使用过滤器:{{ XXX | 过滤器名(其它参数) }} 或 v-bind:属性 = ‘’ XXX | 过滤器名’’
注:
过滤器可以接收额外参数,多个过滤器可以串联,没有改变原本的数据,是产生新的对应的数据
自定义vue指令
自定义指令调用的触发时机:
- 指令与元素成功绑定时
- 指令所在模板被重新解析时(不仅只是对应绑定变量值变更)
语法: - 局部指令
new Vue({
directives: {
指令名:{
bind(){} // 与元素绑定成功时调用
inserted(){} //指令所在元素被插入页面时调用
update(){} // 指令所在模板结构被重新解析时调用
}
}
})
// 简写
new Vue({
directives: {
'指令名'(element, binding){
return
}
}
})
- 全局
Vue.directive('指令名',配置对象)
Vue.directive('指令名',回调函数)
注:
指令定义时不加v-,使用时加v-;指令名如果是多个单词,要使用kebab-case命名方式,不要用小驼峰方式。
生命周期
官网有很详细的介绍和流程图,我在这就不多赘述了,就说两个常用的吧。
mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁vue实例:
销毁后借助vue开发者工具看不到任何信息;自定义事件失效,但原生dom事件依然有效;一般不要在beforeDestroy操作数据,因为即使操作数据,也不会出发更新流程了。
组件
- 定义组件(创建)
使用vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:
el不要写,最终所有的组件都要经过一个vm的管理,由vm中el决定服务哪个容器。
data必须写成函数,避免组件被重复调用时,对象数据存在引用关系。
template可以配置组件结构,但注意根节点。 - 注册组件
局部注册:靠new Vue时候传入的components选项
全局注册:靠Vue.components(‘组件名’,组件) - VueComponent.prototype.proto === Vue.prototype, 让组件实例对象可以访问到Vue原型上的属性、方法。
props
让组件接收外来数据。
// 父组件传值
<ParentComponent propKey='propValue' />
// 子组件接收数据
只接收:
props: ['propKey']
限制类型:
props: {propKey:String(js数据类型)}
限制类型、限制必要性、指定默认值:
props:{propKey:{type:String,required:true,default: '默认值'}}
props是只读,Vue底层会监测对props的修改,如果进行了修改,救护发出警告,如果业务需求确实需要修改,可以复制props的内容到data中一份,然后修改data中的数据。(可以通过v-bind传props值,这样会保持原有数据类型,否则默认都为字符串类型)
插槽
让父组件可以向子组件指定位置插入html结构,也是一种组建通信的方式,适用于父组件 ===>子组件。
分为默认插槽、具名插槽、作用域插槽。
默认插槽:
// 父组件
<Category>
<div>
这里是具体的html内容
</div>
</Category>
// 子组件
<template>
<div>
<slot>插槽默认内容</slot>
</div>
</template>
具名插槽:
// 父组件
<Category>
<div slot='center'>
这里是具体的html内容
</div>
<div v-slot:center>
这里是具体的html内容
</div>
</Category>
// 子组件
<template>
<div>
<slot name='center'>插槽默认内容</slot>
</div>
</template>
作用域插槽:
数据在组件的自身,但是数据生成的结构需要组件的使用者来决定(父组件使用子组件数据来渲染结构)
// 父组件
<Category>
<div slot-scope='{listData}'>
// 第一种slot-scope方式,可解构也可不解构
<ul>
<li v-for="v in listData" :key='v'>{{v}}</li>
</ul>
</div>
</Category>
<Category>
<div scope='scopeData'>
// 第二种scope方式,可解构也可不解构
<ul>
<li v-for="v in scopeData.listData" :key='v'>{{v}}</li>
</ul>
</div>
</Category>
// 子组件
<template>
<div>
<slot :listData='list'>插槽默认内容</slot>
</div>
</template>
<script>
export default {
name:'Catagory',
data(){
return {
list: [11,222,33,14]
}
}
}
</script>
mixin(混入)
功能:可以把多个组件公用的配置提取成一个混入对象。
使用:
定义一个公共js文件用于存储公共逻辑。
// mixin.js文件
export default{
data(){...}
methods:{...}
...
}
局部组件混入:mixins:[‘XXX’]
全局混入:Vue.mixin(XXX),但存在一个弊端就是每个组件都会触发造成滥用,不利于性能优化
插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的自定义数据。
定义插件:
对象.install = function(Vue,options) {
Vue.filter(…) // 全局过滤器
Vue.directive(…) // 全局指令
Vue.mixin(…) // 全局混入
Vue.prototype.
m
y
M
e
t
h
o
d
=
f
u
n
c
t
i
o
n
(
)
.
.
.
V
u
e
.
p
r
o
t
o
t
y
p
e
.
myMethod = function() {...} Vue.prototype.
myMethod=function()...Vue.prototype.myProperty = XXX
}
使用插件: Vue.use()
自定义事件(组件间)
适用于子组件给父组件传递事件
绑定自定义——
第一种方式:
<Children @funHandler='test'/>
<Children v-on:funHandler='test'/>
第二种方式:
<Children ref='childCom'/>
mounted () {
this.$refs.childCom.$on('funHandler',this.test);
}
如果想让自定义事件只触发一次,可以使用.once
修饰符,或$once
方法
触发自定义事件——
this.$emit('funHandler',数据)
解绑自定义事件——
this.$off('funHandler')
注意:通过this.$refs.childCom.$on('funHandler',this.test)
绑定事件,事件如果不是写在methods中则使用箭头函数,否则this指向会出现问题。
全局事件总线
相当于开启了一个全局的第三方组件,所有的组建均可与他进行通信。
new Vue({
router,
store,
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus = this // 开启全局总线
}
}).$mount('#app')
传递数据组件
this.$bus.$emit('busHandler','全局总线事件触发')
读取数据组件
this.$bus.$on('busHandler',(data)=>alert(data))
// 最好在销毁之前解绑自定义事件
beforeDestroy(){
this.$bus.$off('busHandler')
},
transition动画
元素进入样式:
v-enter:进入的起点
v-enter- active:进入的过程中
v-enter-to:进入的终点
元素离开的样式:
v-leave:离开的起点
v-leave-active: 离开的过程中
v-leave-to:离开的终点
使用<transition></transition>
包裹要过度的元素,并配置name属性,如果不配置name属性则为默认的v:
<transition name='hello' appear>
<h1 v-show='isShow'>你好啊!</h1>
</transition>
<style>
.hello-enter,hello-leave-to{
transform: translateX(-100%)
}
.hello-enter-active,.hello-leave-active{
animation: .5s linear;
}
.hello-leave,hello-enter-to{
transform: translateX(0)
}
</style>
配置列表元素动画则使用<transition-group></transition-group>
,每个元素指定key
配置代理
方法一
在vue.config.js中添加如下配置:
devServer: {
proxy: 'http://localhost:5000';
}
优点:配置简单,请求资源时直接发给前端(8080)即可。
缺点:不能配置多个代理,不能灵活控制请求是否走代理。
工作方式:当请求了前端不存在的资源时,该请求会转发给服务器(优先匹配前端资源)
方法二
编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/api1': { // 匹配以api1开头的请求路径
target: 'http://localhost:5000', // 代理目标的基础路径
changeOrigin: true, // 隐藏真实端口
pathRewrite: {'^/api1':''} // 重写路径去除代理标识字段
},
'/api2': { // 匹配以api1开头的请求路径
target: 'http://localhost:5001', // 代理目标的基础路径
changeOrigin: true, // 隐藏真实端口
pathRewrite: {'^/api2':''} // 重写路径去除代理标识字段
},
}
}
}
优点:可以配置多个代理,而且可以灵活的控制请求是否走代理。
缺点:配置略微繁琐,请求资源时必须加前缀。
VUEX模块化+命名空间
目的:让代码更好维护,让多种数据分类更加明确
修改store.js
const countAbout = {
namespaced: true, // 开启命名空间
state: {x:1},
mutations: {...},
actions: {...},
getters: {
bigSum(state){
return state.sum * 10
}
}
}
const personAbout = {
namespaced: true, // 开启命名空间
state: {...},
mutations: {...},
actions: {...}
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
开启命名空间后,组建中读取state数据:
this.$store.state.personAbout.list // 方式一,直接读取
...mapState('countAbout',['sum', 'school', 'subject']) // 方式二,借助mapState读取
开启命名空间后,组建中读取getters数据:
this.$store.getters['personAbout/firstPersonName'] // 方式一,自己直接读取
...mapActions('countAbout',['bigSum'])
开启命名空间后,组建中调用dispatch
this.$store.dispatch('personAbout/addPersonWang'.person)
...mapActions('countAbout',{incerementOdd:'jiaOdd',incerementAwait:'jiaAwait'})
开启命名空间后,组建中调用commit
this.$store.commit('personAbout/ADD_PERSON',person) // 方式一,直接commit
...mapMutations('countAbout',{incerementOdd:'JIA',incerementAwait:'JIAN'})
路由
query传参
传递
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title: '你好'
}
}"
>跳转</router-link>
接收
this.$route.query.id
this.$route.query.title
命名路由
可以简化路由的跳转。
1.给路由命名:
{
path: 'demo',
component: Demo,
children: [
{
path: 'test',
component: Test,
children: [
{
path: 'hello',
name: 'welcome', // 给路由起名字
component: Hello
}
]
}
]
}
2.简化跳转:
<router-link to="/demo/test/hello">跳转</router-link>
<router-link :to="{name:'welcome'}">跳转</router-link>
<router-link :to="{
name:'welcome',
query: {
id=999,
title='你好'
}
}"
>跳转</router-link>
params穿参
1.配置路由,声明接收params参数
{
path: 'demo',
component: Demo,
children: [
{
path: 'test',
component: Test,
children: [
{
path: 'hello/:id/:title', // 动态路由
name: 'welcome', // 给路由起名字
component: Hello
}
]
}
]
}
2.传递参数
<router-link :to="/demo/test/hello/666/你好">跳转</router-link>
<router-link :to="{
name:'welcome',
params: {
id: 666,
title: '你好'
}
}"
>跳转</router-link>
路由携带params参数时若使用to绑定对象值写法,则只能使用name配置!
3.接收参数
this.$route.params.id
this.$route.params.title
路由的props配置
作用:让路由组建更方便的接收到参数
{
name: 'xiangqing',
path: 'detail/:id',
component: Detail,
// 第一种写法,props值为对象,该对象中所有的key-value的组合最终都会通过props传给detail组件
// props: {a:900}
// 第二种写法: props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给detail组件
// props: true
// 第三种写法: props值为函数,该函数返回的对象中每一组key-value都会通过props传给detail组件
props(route){
return {
id:route.query.id,
title: route.query.title
}
}
}
<router-link>
的replace属性
1.作用:控制路由跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
3.如何开启replace模式:<router-link replace to=""></router-link>
编程式路由导航
作用:不借助<router-link>
实现路由跳转,让路由跳转更加灵活
this.$router.push({
name: 'xiangqing',
params: {
id: XXXX,
title: XXXX
}
})
this.$router.replace({
name: 'xiangqing',
params: {
id: XXXX,
title: XXXX
}
})
this.$router.forward() // 前进
this.$router.back() // 后退
this.$router.go() // 正数前进,负数后退,0刷新
缓存路由组件
作用:让不展示的路由组件保持挂载,不被销毁
// include的值是每个组件的组建名 组建内name属性对应的值
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
// 缓存多个组件
<keep-alive include="['News','Message']">
<router-view></router-view>
</keep-alive>
与缓存路由组建相关的两个生命周期钩子
作用: 路由组建所独有的两个钩子,用于捕获路由组建的激活状态。
activated路由组建被激活时触发
deactivated路由组建失活时触发
路由守卫
作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
- 全局守卫:
// 全局前置守卫,初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){ // 判断当前路由是否需要进行权限控制 meta为路由自定义信息配置对象
if(localStorage.getItem('login') === 'true'){ // 权限控制的具体规则
next()
}
else {
next({name: 'login'}) // 未开启权限的其他逻辑
}
}else {
next()
}
})
// 全局路由后置守卫,初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
if(to.meta.title){
doucument.title = to.meta.title // 修改网页标题
}else{
...
}
})
- 独享守卫:
beforeEnter(to,from,next){
... // 基本和全局前置路由首位相似,唯一不同点在于该守卫针对单个路由进行配置,与path、name、meta等参数同级
}
- 组件内守卫:
// 进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){},
// 离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){}
路由器的两种工作模式
页面url里#及其后面的内容就是hash值。
hash值不会包含在http请求中,即:hash值不会带给服务器。
hash模式:
地址中永远带着#,不美观
若以后讲地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
兼容性较好。
history模式:
地址干净、美观。
兼容性和hash模式相比略差。
应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
额外知识点
- eval()会计算字符串并返回结果,但在开发中不推荐使用
console.log(eval('2+3')); // 5
- vue.js与vue.runtime.XXX.js的区别:
vue.js是完整版的vue。包含核心功能+模版解析器。
vue.runtime.XXX.js没有模版解析器,所以不能使用template配置项。需要使用render函数接收到createElement函数去指定具体内容。 - ref属性
被用来给元素或子组件注册引用信息(id的替代者)
应用在html标签上获取的是真实的dom元素,应用在组件标签上是组件实例对象(vc)
使用方式:
<h1 ref="XXX"></h1>
// 获取; 通过this.$refs.XXX
- nextTick
在下一次dom更新结束后执行指定的回调,一般用于当改变数据后,要基于更新后的dom进行某些操作,要在nextTick所指定的回调函数中执行。
this.$nexTick(function(){})
个人开发问题总结
路由配置重定向后导航标签高亮失效
路由表:
const routes = [
{
path: '/home',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
},
{
path: '/',
redirect: '/home',
},
]
const router = new VueRouter({
routes,
mode: 'history',
linkActiveClass: 'active'
})
页面:
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link :to="{name:'keyCode'}">键盘监听</router-link>
</nav>
<router-view/>
</div>
</template>
解决:
将Home导航选项的to属性值与路由表的path对应一致,而不是与重定向路径保持一致
<template>
<div id="app">
<nav>
<router-link to="/home">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link :to="{name:'keyCode'}">键盘监听</router-link>
</nav>
<router-view/>
</div>
</template>