Skip to content

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区别

  1. 功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。

  2. 是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。

  3. 是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。

  4. computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)。

  5. 使用场景: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

alt text

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优缺点

优点:

  1. vue是轻量级框架、简单易学、双向数据绑定、组件化、数据和结构的分离、虚拟DOM、运行速度快。
  2. vue是单页面应用,使页面局部刷新,不用每次跳转页面都要请求所有数据和dom,这样大大加快了访问速度和提升用户体验。而且他的第三方ui库很多节省开发时间。

缺点:

  1. 不利于SEO优化。
  2. 首屏加载速度慢。加载时,将所有的css,js文件进行加载。
  3. 不支持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 实例进行渲染。

vue
<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>