Vue的一点小知识🤏
Vue响应式原理
使用了defineProperty
- Object.defineProperty只能对单个属性进行控制,而Proxy可以对整个对象进行控制。这意味着Proxy可以拦截更多的操作,比如has、deleteProperty、ownKeys等。而且Proxy不需要遍历对象的每个属性来定义拦截器函数,更加方便和高效。
- Object.defineProperty会直接修改原始对象,而Proxy会在原始对象之上创建一个代理层。这意味着使用Object.defineProperty时,我们可以直接操作原始对象来触发拦截器函数,而使用Proxy时,我们必须操作代理对象才能触发拦截器函数。这也导致了一个问题:如果我们在代码中混用了原始对象和代理对象,可能会造成一些混乱和不一致。
- Object.defineProperty有一些兼容性问题,在低版本浏览器中可能无法正常工作。而Proxy是ES6中引入的新特性,在高版本浏览器中才支持。因此,在实际开发中需要考虑到目标环境是否支持这两种方式。
- 在Vue框架中就使用了Object.defineProperty来实现数据双向绑定(Vue2)和Proxy来实现数据响应式(Vue3)
INFO
vue进行编译时,就是将挂载目标的所有子节点劫持到DocumentFragment中,经过一番处理之后,再将DocumentFragment整体返回插入挂载目标。
watch和computed区别
功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)。
使用场景:computed----当一个属性受多个属性影响的时候,使用computed-----用户名展示、列表展示、购物车商品结算。watch–当一条数据影响多条数据的时候,使用watch-----搜索框。
computed
- 支持缓存,只有依赖数据发生改变,才会重新进行计算;
- 不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化;
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的。也就是基于 data 中声明过或者父组件传递的 props 中的数据通过计算得到的值;
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性是一个多对一或者一对一,一般用computed;
- 如果 computed 属性值是函数,那么默认会走 get 方法,函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个 set 方法,当数据变化时,调用 set 方法;
优点:
- 当改变 ref 或者 reactive 响应式变量值时,整个应用会重新渲染,vue 会被数据重新渲染到 dom 中。这时,如果我们模板中使用了 methods 中的fullNameFun函数,或者使用了组合式return的函数。随着渲染,方法也会被调用。但是 如果computed中所依赖的变量没有发生改变,则不会进行重新的计算,从而性能开销比较小。当新的值需要大量计算才能得到,缓存的意义就非常大;
- 如果 computed 所依赖的数据发生改变时,计算属性才会重新计算,并进行缓存;当改变其他数据时,computed 属性 并不会重新计算,从而提升性能;
- 当拿到的值需要进行一定处理使用时,就可以使用 computed;
watch
- 完全等同于vue2 中的watch;
- 不支持缓存,数据变化,直接会触发相应的操作;
- watch 支持异步操作;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作,一对多;
INFO
监听数据必须是 data 中声明过或者组合式api中声明的响应式值或者父组件传递过来的 props 中的数据。当数据变化时触发其他操作,函数有两个参数:
- immediate:组件加载立即触发回调函数执行;
- deep: 深度监听;为了发现对象内部值的变化,复杂类型的数据时使用,例如:数组中的对象内容的改变,注意:监听数组的变动不需要这么做。
注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到;
WARNING
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的,这是和 computed 最大的区别。
nextTick
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
所以就衍生出了这个获取更新后的DOM的Vue方法。所以放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操作的 js代码;
理解:nextTick(),是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数,
WARNING
Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。
Vue.nextTick(callback) 使用原理:
原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
Vuejs 3.3
Vue3 vs Vue2
- 更灵活的响应式系统:Vue 2.x 中响应式系统的核心是 Object.defineProperty,劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式。Vue 3.x 中使用 Proxy对象重写响应式系统。
- 更快的渲染速度:Vue3 的编译器生成的渲染函数比 Vue2 生成的更高效。
- 编译阶段:Vue 2.x 通过标记静态节点,优化 diff 的过程。Vue 3.x中标记和提升所有的静态节点,diff的时候只需要对比动态节点内容。
- 更小的体积:Vue3 将源码拆分为多个独立的模块,这样就可以按需导入所需的模块,从而减小了整个库的体积。
- 更好的 TypeScript 支持:Vue3 对 TypeScript 的支持更加友好,内部使用了更先进的 TypeScript 特性,并为其提供了更好的声明文件。
- 更好的组件系统:比如,Vue3中引入了一个新的 Fragment 组件,它可以替代原来的 template 标签作为根节点
- 新增了setup组合式API
vue优缺点
优点:
- vue是轻量级框架、简单易学、双向数据绑定、组件化、数据和结构的分离、虚拟DOM、运行速度快。
- vue是单页面应用,使页面局部刷新,不用每次跳转页面都要请求所有数据和dom,这样大大加快了访问速度和提升用户体验。而且他的第三方ui库很多节省开发时间。
缺点:
- 不利于SEO优化。
- 首屏加载速度慢。加载时,将所有的css,js文件进行加载。
- 不支持IE678。
seo是什么,怎么解决seo不友好
seo是搜索引擎优化。在搜索引擎自然排名机制的基础上,对网站进行内部及外部的调整优化,改进网站在搜索引擎中的关键词自然排名,获得更多的流量。
单页面的内容是根据路由变化动态生成并展示出来的,很多页面的内容是通过ajax异步获取的,网络抓取工具并不会等待异步请求完成后再行抓取页面内容。
解决方式可以使用SSR(服务端渲染)或者是预渲染。
vue生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
- beforeCreate:组件实例被创建之初,组件的属性生效之前
- created:组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
- beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用
- mounted:el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
- beforeUpdate:组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
- update:组件数据更新之后
- activited:keep-alive 专属,组件被激活时调用
- deactivated:keep-alive 专属,组件被销毁时调用
- beforeDestory:组件销毁前调用
- destoryed:组件销毁后调用
pinia & vuex
vue router
vue3
Vue 2:主要使用data、methods、computed和watch等选项来组织代码逻辑。defineproperty Vue 3:引入了Composition API,允许开发者更灵活地组织代码逻辑,使用setup函数来替代,具体可替代data、methods、生命周期。proxy
响应式原理
用track函数把所有依赖于某一个变量x的effect函数都收集起来,放在dep里(dep是Set以去重)。搜集起来之后,以后只要变量x一改变,就执行trigger函数通知dep里所有依赖变量x的effect函数执行,实现依赖变量的更新。
watcher:当调用变量x的时候,dep本身不知道是谁在使用自己
ref和reactive区别
keep-alive
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。
在 created 函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode 实例进行渲染。
<template>
<div id="app">
// 1. 将缓存 name 为 test 的组件
<keep-alive include='test'>
<router-view/>
</keep-alive>
// 2. 将缓存 name 为 a 或者 b 的组件,结合动态组件使用
<keep-alive include='a,b'>
<router-view/>
</keep-alive>
// 3. 使用正则表达式,需使用 v-bind
<keep-alive :include='/a|b/'>
<router-view/>
</keep-alive>
// 5.动态判断
<keep-alive :include='includedComponents'>
<router-view/>
</keep-alive>
// 5. 将不缓存 name 为 test 的组件
<keep-alive exclude='test'>
<router-view/>
</keep-alive>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>