Vue 知识点总结

199 阅读8分钟

一、组件通信方式

  1. props/$emit(父子组件通信)
  2. parent(对象)/ parent(对象)/ children (数组)(父子组件通信)
  3. provide/inject (父子/爷孙组件通信):父组件通过 provide 提供变量,子组件通过 inject 注入变量
  4. ref /  $refs (父子组件通信)
  5. eventBus 事件总线,emit 发送事件,emit 发送事件,on 监听/接收事件,$off 移除事件(全局通信,项目大时难以维护)
  6. Vuex(全局通信,F5 刷新页面,值会丢失)
  7. localStorage/sessionStorage
  8. attrs / attrs / listeners(父子/爷孙组件通信)

二、Vue 中 data 为什么是函数,不能是对象

因为组件会被实例化多次,data 是对象(引用类型)会导致实例之间的 data 共享。data 用函数并 return,可以使每个实例都拥有一份组件的 data 的副本

三、计算属性 computed 和侦听器 watch 的区别

  1. 计算属性:复杂逻辑计算时使用,因为计算属性会将它们的响应依赖关系缓存下来,当计算属性依赖的数据变化时,就会触发计算属性更新
  2. 普通方法:每当触发重新渲染,都会执行
  3. 侦听器:数据变化时需要执行异步或者开销较大的操作时使用

四、Vue.nextTick

  1. nextTick 的作用是,将传入的回调延迟到下次 DOM 更新后执行。

  2. nextTick 产生的原因是:Vue DOM 更新是异步执行的,更新数据后,视图并不会马上更新,而是会监听数据变化,并缓存在同一事件循环中(Event Loop:同步代码、异步代码的回调推入任务队列、微任务、宏任务:含 UI Rendering),等同一循环中的数据都变化完后,在统一更新视图。为了确保拿到更新后的 DOM,增加了 nextTick 方法。

  3. nextTick 的使用

    1. 传入回调
    2. nextTick().then()
  4. 应用场景

    1. Vue 的生命周期函数 created() 进行 DOM 操作
    2. 数据变化后,要使用随数据变化而变化的 DOM
  5. 实现思路

    1. 调用 nextTick,会把 nextTick 里的回调函数存进一个任务数组里,再执行这个任务数组之前,nextTick 方法里会先执行一个 Promise.resolve(),再执行回调函数组的方法,这样就确保了 Promise.resolve() 之前已经执行完一个事件循环(包含 DOM 更新)

五、keep-alive

  1. keep-alive 是 vue 的一个内置组件,作用是使被包含的组件保留状态,或避免重新渲染,也就是组件缓存

六、VUE3 Composition API 组合式API

(一)优点

  1. 根据逻辑相关性组织代码,提高可读性和可维护性
  2. 更好地重用逻辑代码,Options API 中 通过 Mixins 重用逻辑代码,容易发生命名冲突且关系不清
  3. 在 VUE3 中,Composition API 是可选的

(二)setup() 生命周期

  1. 使用 Composition API 的入口
  2. 在 beforeCreate 之前调用
  3. 在 setup 中没有 this
  4. 返回对象中的所有属性都可以在模板中使用
  5. setup(props, context) props:组件里的props,context:setup 里的 this

(三)ref

  1. 返回一个响应式的引用
  2. ref('name') ,name 是一个响应式对象,在 setup 中使用 name 的值,需要 name.value,在模板中直接用 name
  3. 用来创建 基本类型 的响应式时,使用的是 Object.defineProperty();
  4. 用来创建 引用类型 时,调用的是 Reactive 生成 proxy

(四)computed 计算属性

 在 setup 中直接给计算属性赋值,是不生效的,需要放在 computed 里的 set 函数里

(五)reactive、toRefs、toRef

  1. reactive 创建一个 深度响应式对象

    1. 一般用来创建引用类型
    2. 无法直接修改整个 reactive 声明的对象
    3. 使用 Proxy
    4. 不能使用 ... 解构,会失去响应
    5. 可以使用 ...toRef() 将响应式对象转换为引用类型,在页面中直接使用对象中的属性
  1. toRefs 将响应式对象转换为普通对象
  2. toRef 针对一个响应式对象的某个属性创建一个 ref ,保持相应及引用关系
  3. 示例
<template>
	<div>
  	<p>姓名:{{ name }}</p>
    <p>年龄:
    	<button @click="changeAge(-1)">-</button>
      <span>{{ age }}</span>
      <button @click="changeAge(1)">+</button>
    </p>
    <p>出生年份:
    	<button @click="changeYear(-1)">-</button>
    	{{ year }}
      <button @click="changeYear(1)">+</button>
    </p>
  </div>
</template>

<script>
import { ref, computed, reactive, toRefs, watch } from 'vue';

export default {
	props: [
  	titleString
  ],
	setup () {
  	// const name = ref('Aubrey')
    const data = reactive({
    	name'Aubrey',
      age18,
      yearcomputed({
      	get() => {
        	return 2022 - data.age
        },
        setval => {
        	data.age = 2022 - val
        }
      })
    });
    
    function changeAge (val) {
    	data.age += value
      console.log(props.title// props 参数
    };
    
    function changeYear (val) {
    	data.year += val
    };
    
    watch(() => props.title(newTitle, oldTitle) => {
    	console.log(newTitle, oldTitle)
      context.emit('titleChange', newTitle) // context 参数 就是 this
    });
    
    return { ...toRefs(data), changeAge, changeYear };
  }
}
</script>

七、虚拟 DOM(VDOM)

虚拟 DOM,其实是一个普通的 JavaScript 对象,包含 tag、props、children 三个属性。

虚拟 DOM 如何提升性能:DOM发生变化时,通过 diff 算法比对 JavaScript 原生对象,计算出需要变更的 DOM,然后只对变化的 DOM 进行操作,而不是更新整个视图。

DOM 节点 通过 h 函数,转换为虚拟 DOM 对象,再 通过 render 渲染函数,渲染到视图。

虚拟 DOM 优势:

  1. 性能优化(JS 计算代码性能,比 DOM 渲染性能消耗少)
  2. 跨平台

八、diff 算法 

diff 算法,对比新老 vdom 的变化,再通过 patch 方法,将变化更新到视图上。

diff 函数接收两个参数,旧 vdom 对象,新 vdom 对象,返回一个 patches (补丁)。

// h 函数的参数说明
// 第一个: tag 标签名
// 第二个:props 属性
// 第三个:children 子节点(如果是文本节点,则就是 String,
// 否则就是个继续嵌套 tag、props、children 的对象)

const before = h('div', {}, 'before text');
const adter = h('div', {}, 'after text');

const patches = diff(before, after);

1、深度优先遍历算法:子节点顺序变化,diff 算法会认为所有子节点都需要进行 replace,重新将所有子节点的虚拟 DOM 转换成真实的 DOM(全部重新渲染),这种操作十分消耗性能。

优化方式:找到新旧虚拟 DOM 对应的位置,然后进行移动,尽量减少 DOM 操作。通过对子节点添加 key 值,通过 key 值对比,来判断子节点是否移动了。

九、key 的作用

  1. key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果

  2. 在简单模版遍历中,不使用 key,会使新旧虚拟节点跳过 key 值对比,速度更快,节点(标签)会 就地复用,只变更改变的部分内容;

  3. key是给每一个vnode的唯一id,可以依靠key,更准确, 更快的拿到oldVnode中对应的vnode节点。

    1. 更准确

因为带key就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。

  1. 更快

利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。(这个观点,就是我最初的那个观点。从这个角度看,map会比遍历更快。)

4.     使用场景:

(1)想要完整地触发组件的声明周期时,如给一个复用的页面的 绑定:key="router.path"

(2)触发过渡效果

(3)v-for 循环列表

5.    不能用 index 作为 key

(1)改变列表数据顺序时,会增加 DOM 更新

(2)结构中含有表单输入元素时,会导致渲染错误

十、Vue3 生命周期

beforeCreate(setup 没有这个)

Created(setup 没有这个)

beforeMount

mounted

beforeUpdate

updated

beforeUnmount

unmounted

activated(复用组件激活)

deactivated(复用组件失活)

errorCaptured

renderTracked

renderTriggered

页面和组件生命周期触发顺序:

十一、Vue Router 

  1. 完整的导航解析流程

    1. 导航被触发
    2. 在失活的组件里调用 beforeRouteLeave
    3. 调用全局的 beforeEach
    4. 在重用的组件里调用 beforeRouteUpdate
    5. 调用路由配置的 beforeEnter
    6. 解析异步路由组件
    7. 在被激活的组件里调用 beforeRouteEnter
    8. 调用全局的 beforeResolve
    9. 导航被确认
    10. 调用全局的 afterEach
    11. 触发 DOM 更新
    12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数
  2. history 和 hash 

    1. hash(window.location.hash)

      1. url 的 # 后的就是 hash,hash变化会触发 onHashChange 事件,但不会触发页面刷新
    2. history(window.history)

      1. 监听window.onpopstate 方法,通过history.pushState()、history.replaceState()控制跳转
      2. 需要服务器配置将所有路由都重定向到根页面

十二、Vue3 对比 Vue2 的变化

  1. 生命周期

    1. setup 替换 beforeCreate 和created
    2. beforeUnmount/unMounted 替换 beforeDestory/destoryed
  2. 数据劫持的使用的方法

    1. vue2:Object.defineProperty
    2. vue3:Proxy
  3. 组合式 API (Composition API)实现逻辑关注点分离

  4. 支持多个根节点

  5. 组件可将部分 DOM 移到 Vue app 之外

  6. suspense 异步组件

    1. #fallback 里显示异步组件加载完成前兜底渲染的内容(如:loading)
    2. #default 渲染异步组件  
  7. 虚拟 DOM / diff 算法

    1. 新增 patchFlag 标明节点的类型,如 -1 静态节点,diff 算法比较时忽略其子节点
    2. patchFlag 简化 diff 算法对比过程
  8. 事件缓存:cacheHandler,如静态节点上的事件,会随节点一起缓存,不会被重新创建

  9. tree-shaking 打包优化:一些之前绑定在 Vue 实例上的 API,改为需要通过 import/export 导入/导出,如 nextTick、set、delete

  10.  TypeScript 重写

十三、双向绑定原理 

VUE 采用数据劫持加订阅-发布模式。主要包含这几个模块:

  1. Observer 劫持属性,初始化时,给每个属性添加 getter 和 setter,getter 里为每一个属性添加一个订阅器数组,订阅器数组里用来存放这个属性的所有 watcher,setter 里通过 notify 方法通知订阅器里每个 watcher 执行 update 方法
  2. Watcher 给每个属性添加 update 方法,执行添加的回调函数
  3. Compiler 模板编译器,初始化解析 DOM 时,为每个使用属性的地方生成一个 watcher,同时添加属性变化后的回调函数 

十四、Vue 项目性能优化

  1. 路由懒加载:() => import()

  2. 图片懒加载:vue-lazyload

  3. 按需引入第三方插件:babel-plugin-component

  4. 无限加载列表虚拟滚动:vue-virtual-scroller

  5. 服务端渲染 SSR

  6. webpack 相关优化

    1. 预渲染:插件 prerender-spa-plugin
    2. 提取 chunks 中的公共代码:内置插件 webpack.optimize.splitChunks
    3. 单独打包 CSS 文件:插件 extract-text-webpack-plugin
    4. 压缩 CSS:插件 optimize-css-assets-webpack-plugin
    5. source-map:根据不同环境配置 devtools
    6. 生产环境的包分析工具:webpack-bundle-analyzer
    7. 代码懒加载:() => import ()
    8. prefetch:预获取(浏览器空余时)

 import ( /* webpackPrefetch: true */ './imgDec').then()

  1. preload:预加载 

 import ( /* webpackPreload: true */ './imgDec').then() 10. 利用浏览器缓存(强缓存、协商缓存)