2022你该掌握的Vue知识点
作为前端开发者,我们最熟悉的技术栈莫过于Vue、React, 那么接下来我将分享一些自己对Vue使用的一些笔记
ref应用组件和DOM
ref使用在DOM上
<template>
<div ref="refDom"></div>
</template>
mounted() {
console.log(this.$refs.refDom); // <div></div>
}
ref使用在DOM上,我们可以拿到整个DOM元素
ref使用在组件上
<template>
<div>
<tree ref="refCompoent"></tree>
</div>
</template>
mounted() {
console.log(this.$refs.refCompoent);
}
ref使用在组件上,可以拿到整个组件的实例
v-if和v-show
<template>
<div>
<div class ="show_dom" ref="refDom" v-show="showMe"></div>
</div>
</template>
data () {
return {
showMe: false
}
}
通过 Elements 我们可以看到使用v-show的时候,DOM中是有存在,但是未被渲染,使用display:none; 将其隐藏了,这里补充一下display:none; opacity:0; visibilty:visible; 的区别:
-
display:none会让元素完全从渲染树中消失,渲染时不再占据任何空间,不能点击
-
visibilty:hidden不会让元素从渲染树上消失,渲染元素继续占据空间,只是内容不可见,不能点击
-
opacity:0不会让元素在渲染树中消失,渲染元素继续占据空间,只是内容不可见,可以点击继承
-
display:none和opacity:0; 是非继承属性,子孙节点消失由于子元素从渲染树消失造成,通过修改子孙节点属性无法显示
-
visibilty:hidden; 是继承属性,子孙节点消失由于继承了hidden,通过设置
-
visibilty:visible; 可以让子元素节点显示继承
-
display:none: 修改元素会造成文档重排,读屏器不会读取display:none元素内容,性能消耗较大
- visibilty:hidden: 修改元素只会造成本元素的重绘,读屏器读取visibilty:hidden元素内容,性能优化较少
- opacity:0: 修改元素会造成重绘,性能消耗较少
<template>
<div>
<div class ="show_dom" ref="refDom" v-if="showMe"></div>
</div>
</template>
需要注意到的是使用了v-if是不会有DOM在DOM树中的,会被注释掉,如果我们需要频繁的切换DOM,使用v-show更加合适,反之v-if
父组件和子组件生命周期钩子函数执行顺序
可以分为四个部分,分别是:
首次加载过程
父 beroreCreate
=> 父created
=>父beforeMount
子beroreCreate
=> 子 created
=> 子beforeMount
=> 子mounted
=>父mounted
更新过程
子组件更新过程
父 beforeUpdate
=> 子beforeUpdate
=> 子updated
=> 父 updated
父组件更新过程
父beforeUpdate
=> 父updated
销毁过程
父 beforeDestroy
=> 子beforeDestroy
=> 子 destroyed
=> 父 destroyed
初次渲染过程
- 解析模板为
render
函数 (或在开发环境已完成,vue-loader
) - 触发响应式,监听data属性
getter setter
- 执行render函数,生成
vnode, patch(elem, vnode)
computed和watch的区别
-
computed
-
计算属性,依赖其他属性,当其他属性改变的时候下一次获取
computed
值时也会改变,computed
的值会有缓存 -
watch
-
类似于数据改变后的回调
-
如果想深度监听的话,后面加一个
deep:true
-
如果想监听完立马运行的话,后面加一个
immediate:true
-
如果想在生命周期中使用
watch
this.$watch('someObject', callback, { deep: true, immediate: true }
nextTick
- nextTick:在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,获取更新后的 DOM
- 如果想要在Vue生命周期函数中的created()操作DOM可以使用Vue.nextTick()回调函数
- 在数据改变后要执行的操作,而这个操作需要等数据改变后而改变DOM结构的时候才进行操作,需要用到nextTick
在下次 DOM 更新循环结束之后执行延迟回调,nextTick主要使用了宏任务和微任务,根据执行环境分别尝试采用
Promise
MutationObserver
setImmediate
如果以上都不行则采用setTimeout
定义一个异步方法,多次调用nextTick
会将方法存入队列中,通过这个异步方法清空当前队列
Vue是异步渲染 在data改变之后 DOM不会立刻渲染
$nextTick
会在DOM渲染之后被捕获 以获取最新的DOM节点
路由模式
Vue 路由有三种模式:hash、history 和 abstract
hash: 使用 URL hash 值来作路由,支持所有浏览器,hash不会调用接口
hash模式
hash其实就是指url后面的#号以及后面的字符,这里的#和css里的#是一个道理, hash也称作锚点,本身是用来做页面定位的,它是可以使对应的id元素显示在可视区域内的, 由于hash值变化不会导致浏览器向服务器发出请求,而且hash改变会触发hashchange事件, 同时浏览器的进后退也能对其进行控制,所以在html5的history出现之前,基本都是在使用hash来实现前端路由的
hash的特点在于:hash出现url中,但不会被包含在HTTP请求中,对后端完全没有任何影响,因此改变hash不会重新加载页面
window.addEventListener('hashchange',function(){
//监听hash变化,点击浏览器的前进后退时会触发
})
history模式
HTML5 History 中新增的 pushState()
和 replaceState()
方法(需要特定浏览器支持)
这两个方法应用于浏览器的历史记录栈,在当前已有的 back
、forward
、go
的基础之上,它们提供了对历史记录进行修改的功能,
只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求,
因此可以说,hash
模式和 history
模式都属于浏览器自身的特性
window.addEventListener('popstate',function(){
//监听history变化,点击浏览器的前进后退会触发
})
hash和history的区别
hash模式下,仅hash符号之前的内容会被包含在请求中,如 www.juejin.cn 因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误
history模式下,前端的url必须和实际后端发起请求的url一致,如www.juejin.cn/editor/ 如果后端缺少对/editor/ 的路由处理,将返回404错误,此时需要在后端作出相应的调整
router.query和router.params的区别
类型于get(query) 和 post(params)
query方式传参和接收参数
传参:
this.$router.push({
path:"/xxx"
query:{ id:id}
})
接收参数:this.$router.query.id
params方法传参合接收参数
传参:
this.$router.push({
path:"./xxx"
params:{id:id}
})
接收参数:this.$router.params.id
-
params传参,push里面只能是 name:'xxxx',不能是path:'/xxx',因为params只能用name来引入路由,如果写成了path,接收参数页面会是undefined
-
另外, 二者还有区别,通俗的来讲 query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会在地址栏中显示
-
若刷新当前页面params对象里面数据会消失,而query不会
-
传参是
this.$router
,接收参数是this.$route
$route
和$router
的区别
- $route是路由信息对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
- 而$router是路由实例对象包括了路由的跳转方法,钩子函数等。
虚拟DOM
简洁强大的vdom库
Vue参考它实现的vdom
和diff
推荐学习方式
Mixin
- 数据对象内
mixin的数据对象和组件的数据发生冲突时以组件数据优先
-
钩子函数 同名钩子函数将会混合为一个数组,都将被调用到,但是混入对象的钩子将在组件自身钩子之前调用
-
值为对象的选项
值为对象的选项,例如 methods
, components
和 directives
,将被混合为同一个对象,两个对象键名冲突时,取组件对象的键值对
vue中组件通信的方式
- 父组件向子组件传递
:data="tranData"
,子组件通过props
接收 $ref
获取子组件实例- 子组件使用
$emit
对父组件进行传值 - 父组件通过
$chidren
获取子组件实例 子组件通过$parent
获取父组件实例 vuex
状态管理- 路由传值
- 通过
eventBus
兄弟组件传值 $attrs
包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)/$listeners
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器,它可以通过 v-on="$listeners" 传入内部组件provide
和inject
- localStorage、sessionStorage
组件中data为什么必须是一个函数
复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,不会共享同一个data对象
Proxy与Object.defineProperty的优劣对比
Proxy
的优势如下:
- Proxy可以直接监听对象而非属性
- Proxy可以直接监听数组的变化
- Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的
- Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改
- Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
Object.defineProperty
的优势如下:
- 兼容性好,支持IE9
父组件监听子组件的生命周期
// Parent.vue
<Child @hook:mounted="doSomething"></Child>
doSomething() {
console.log('父组件监听到 mounted 钩子函数 ...')
}
// Child.vue
mounted() {
console.log('子组件触发 mounted 钩子函数 ...');
}
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...
当然 @hook 方法不仅仅是可以监听 mounted
,其它的生命周期事件,例如:created
, updated
等都可以监听
keep-alive
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
- 一般结合路由和动态组件一起使用,用于缓存组件;
- 提供
include
和exclude
属性,两者都支持字符串或正则表达式,include
表示只有名称匹配的组件会被缓存,exclude
表示任何名称匹配的组件都不会被缓存 ,其中exclude
的优先级比 include 高 - 对应两个钩子函数
activated
和deactivated
,当组件被激活时,触发钩子函数activated
,当组件被移除时,触发钩子函数deactivated
生命周期内调用异步请求
可以在钩子函数 created
、beforeMount
、mounted
中进行调用,因为在这三个钩子函数中,data
已经创建,可以将服务端返回的数据进行赋值,推荐在 created
钩子函数中调用异步请求,因为在 created
钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面 loading 时间
- ssr 不支持
beforeMount
、mounted
钩子函数,所以放在created
中有助于一致性
监听数组变化
Object.defineProperty
是不能监听数组变化的
重写定义原型, 重写push
、pop
等方法,实现监听
Proxy
可以原生支持监听数组变化
vm.$set
解决vue视图不及时更新问题
this.$set( target, key, value )
target:要更改的数据源(可以是对象或者数组)
key:要更改的具体数据
value :重新赋的值
路由守卫
全局守卫:beforeEach(to,from,next)和afterEach(to,from)
路由独享守卫:beforeEnter
组件内的守卫:路由进入/更新/离开之前 beforeRouterEnter/update/leave vue的路由守卫钩子,vue的路由方式,vue的路由传参
路由守卫钩子:在路由进入或者离开的执行的方法
beforeEach(to,from,next) :路由的前置守卫,进入该路由执行的方法
参数中包含了路由的一些信息
to: 进入到哪个路由去
from:从哪个路由离开
next:函数,决定是否展示你要看到的路由页面
next(); 顺利进入
next(false); 阻止进入
next('/login'); 进入到其他路由
全局路由守卫的简单用法:
router.beforeEach((to,from,next)=>{
if(to.path == '/login' || to.path == '/register'){
next()
}else{
alert('您还没有登录,请先登录')
next('/login')
}
})
局部路由守卫:
beforeRouteEnter (to, from, next) {
// 这里还无法访问到组件实例,this === undefined
next( vm => {
// 通过 `vm` 访问组件实例
})
}
afterEach(to,from) : 路由的后置守卫,离开该路由执行的方法
vue路由的常用方式
-
动态路由:路由后面:跟不同的参数,跳转同一个组件
-
嵌套路由:一级路由后面跟children子路由
-
编程式路由:使用js在router中push新的path
-
路由传参
-
使用动态路由的特点:后跟参数的方式传递
定义:
{ path: '/user/:id', component: User }
使用:
this.$router.push({
path: `/user/${id}`,
})
获取:this.$route.params.id
- 通过路由属性中的name来确定匹配的路由,通过params来传递参数。
定义:
{
path: '/describe',
name: 'Describe',
component: Describe
}
使用:
this.$router.push({
name: 'Describe',
params: {
id: id
}
})
获取:this.$route.params.id
- 使用path来匹配路由,然后通过query来传递参数,推荐使用
定义:
{
path: '/describe',
name: 'Describe',
component: Describe
}
使用:
this.$router.push({
path: '/describe',
query: {
id: id
}
})
获取:this.$route.query.id
- router-link 中声明params传递
<router-link :to="{name:'Reg',params:{num:888}}">显示注册页面</router-link>
如何在刷新的情况下结合vuex保存登录状态
cookie
localstorage
对用户登录的token记录在本地,在进入登录页面是像后端发送请求验证token的有效性,合法的token怎进入相关页面,否则进入登录页面
-
在vuex中记录userMessage, token
-
在登录时向storage中记录user, token, 登录标识符,同时向vuex派发action,改变vuex相关字段值
-
在刷新页面的时候vuex中的state会被初始化,所以需要判断本地storage中的数据,然后在通过vuex派发事件,改变相关状态 vue路由钩子是在路由跳转过程中拦截当前路由和要跳转的路由的信息
第一种:全局路由钩子
beforeEach(to,from,next) {
//...
}
第二种:路由独享的钩子
beforeEnter(to,from,next) {
// ...
}
第三种:组件内的钩子
beforeRouteEnter(to,from,next) {
//...
}
beforeRouteUpdate(to,from,next) {
//...
}
beforeRouteLeave(to,from,next) {
// ...
}
内部指令
v-model
:用于在表单上创建双向数据绑定。v-text
:等同于JS的text属性v-html
:等同于JS的innerHtml属性v-show
:通过样式display改变显隐v-if
:根据表达式的值的真假条件来渲染元素v-else
:v-else是搭配v-if使用的,它必须紧跟在v-if或者v-else-if后面,否则不起作用v-else-if
:充当v-if的else-if块,可以链式的使用多次。可以更加方便的实现switch语句v-slot
:插槽v-once
:只会渲染一次v-pre
:用来跳过这个元素和它的子元素编译过程v-cloak
:用来保持在元素上直到关联实例结束时进行编译v-for
:用v-for指令根据遍历数组来进行渲染
注意:当v-for和v-if同处于一个节点时,v-for的优先级比v-if更高。这意味着v-if将运行在每个v-for循环中
v-on
:绑定事件,缩写@v-bind
:用来动态的绑定一个或者多个特性绑定变量,缩写:
<div :class="{'is-active':isActive, 'text-danger':hasError}"></div>
<p :class="[{'is-active':activeClass},errorClass]">tg</p>
修饰符
.lazy
<input v-model.lazy="msg">
.number
<input v-model.number="msg">
.trim
<input v-model.trim="msg">
事件修饰符
.native
:绑定事件在自定义组件上时,能执行.stop
阻止事件继续传播.prevent
事件不再重载页面.capture
使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理.self
只当在 event.target 是当前元素自身时触发处理函数.once
事件将只会触发一次.passive
告诉浏览器你不想阻止事件的默认行为
使用修饰符时,序很重要;相应的代码会以同样的顺序产生 因此,用
v-on:click.prevent.self
会阻止所有的点击,而v-on:click.self.prevent
只会阻止对元素自身的点击
强制渲染
vue页面中通过v-for进行数据渲染,如果层级过多,有时候数据发生改变但是视图未发生更新,render
函数没有自动更新,使用this.$forceUpdate()
是重新渲染虚拟DOM,不是重新加载组件,因为this.$forceUpdate
执行后,只会触发beforeUpdate
和updated
这两个钩子函数,并不会触发其他的钩子函数,它仅仅影响实例本身和插入插槽内容的子组件,并不是所有子组件,这个时候便可以通过this.$forceUpdate()
进行手动刷新
this.$forceUpdate()
未完待更新...
转载自:https://juejin.cn/post/7073885282629582862