Vue 知识点整理

前言

本文从 vue 核心特性、原理和使用三个方面整理了相关知识点,并不断更新补充。

Vue 核心特性

数据驱动(MVVM)

MVVM表示的是 Model-View-ViewModel

Model:模型层,负责处理业务逻辑以及和服务器端进行交互;View:视图层:负责将数据模型转化为UI展示出来,可以简单的理解为 HTML 页面;ViewModel:视图模型层,用来连接 Model 和 View,是Model 和 View 之间的通信桥梁。

组件化

组件化就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在Vue中每一个.vue文件都可以视为一个组件。

组件化可以降低整个系统的耦合度,在保持接口不变的情况下,替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现;组件化调试方便,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,以逻辑会比分析整个系统要简单;提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级。

指令

指令是带有 v- 前缀的特殊属性作用,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM,Vue 自带的常用指令有 v-ifv-forv-bindv-onv-model 等,开发者也可以通过 directives 选项创建自定义指令,或者在 <script setup> 中以 v 开头的驼峰式命名的变量用作一个自定义指令。

javascript 复制代码
export default { 
  setup() { /*...*/ }, 
  directives: { 
    // 在模板中启用 v-focus
    focus: { /* ... */ } } 
}

<script setup>
// 在模板中启用 v-focus
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>

自定义指令的常用场景有:通过节流来防止表单重复提交、图片懒加载、权限校验等。

Vue 原理

Vue 实例的挂载过程

new Vue 的时候调用会调用 _init 函数,定义 $set$get$delete$watch 等方法,定义 $on$off$emit$off等事件,定义 _update$forceUpdate$destroy 生命周期;

调用 $mount 进行页面的挂载,挂载的时候主要是通过 mountComponent 方法;

定义 updateComponent 更新函数;

执行render生成虚拟DOM

_update 调用 patch,将 vnode 转换为真实 DOM,并且更新到页面中。

data 属性是函数和是对象的区别

vue 实例的时候定义 data 属性既可以是一个对象,也可以是一个函数。组件中定义 data 属性,只能是一个函数,如果为组件 data 直接定义为一个对象则会得到警告信息。因为对象的内存地址是共用的,函数返回的对象内存地址并不相同,vue 组件可能会有很多个实例,采用函数返回一个全新data 形式,可以使每个实例对象的数据不会受到其他实例对象数据的污染。

双向绑定/响应式原理

1. vue2.x

vue2.x 响应式采用的是软件设计模式中的观察者模式,创建 observer 类观察数据,构造函数中对对象和数组类型分别处理,对象数据通过 Object 构造器上的 api defineProperty 实现,defineProperty 可以在对象上定义/修改一个属性,通过此方法对对象进行监听,通过 get/set 劫持读写,在修改的同时通知视图更新,defineProperty api 有缺点,对象层级深时需要递归到底监听,一次性计算量大,无法监听新增/删除属性;对数组类型,通过 Object.create 方法创建新对象使其原型指向 Array 的 prototype(扩展新属性不会影响数组原型),重写 push 等方法,在更新视图的同时执行真正数组原型上的方法。

2. vue3.x

vue3.x 使用了两个响应式函数,ref()reactive()ref() 可以生成值类型响应式,reactive() 生成引用类型响应式。ref 中定义一个对 value 属性做 getter 和 setter 劫持的对象并返回,get 部分就是执行 track 函数做依赖收集然后返回它的值,set 部分就是设置新值并且执行 trigger 函数派发通知;reactive 中 使用了 new Porxy(target,config) 传入目标对象和代理配置,返回代理对象,与 defineProperty 相比,深度监听是在 get 里,不需要递归到底,只在访问时递归处理。

nextTick 原理

$nextTick 的原理是利用JS 事件循环机制,当监听到数据发生变化的时候不会立即去更新DOM,而是开启一个任务队列,并缓存在同一事件循环中发生的所有数据变更,将多次数据更新合并成一次,减少操作DOM的次数从而减少性能的消耗。$nextTick 会在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,可以获取更新后的 DOM。$nextTick() 会返回一个 Promise 对象,可以使用 async/await 完成相同作用的事情。

Mixin

本质其实就是一个 js 对象,它可以包含我们组件中任意功能选项,如datacomponentsmethodscreatedcomputed等等,需要注意的是有不同的合并策略:替换型策略有propsmethodsinjectcomputed,就是将新的同名参数替代旧的参数;合并型策略是data, 通过set方法进行合并和重新赋值;队列型策略有生命周期函数和watch,原理是将函数存入一个数组,然后正序遍历依次执行;叠加型有componentdirectivesfilters,通过原型链进行层层的叠加。

Vue 中 key 的原理

key 是给每一个 vnode 的唯一id,也是 dif f的一种优化策略,可以根据 key,更准确, 更快的找到对应的 vnode 节点。当我们在使用 v-for 时,需要给单元加上 key,如果不用 key,Vue 会采用就地复地原则:最小化 element 的移动,并且会尝试尽最大程度在同适当的地方对相同类型的 element,做 patch 或者 reuse。如果使用了 key,Vue 会根据 keys 的顺序记录 element,曾经拥有了 key 的 element 如果不再出现的话,会被直接 remove 或者 destoryed。

keep-alive 原理

keep-alivevue 中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOMkeep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们 keep-alive可以设置以下props属性: include - 字符串或正则表达式。只有名称匹配的组件会被缓存;exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存;max - 数字。最多可以缓存多少组件实例。

keep-alivemounted 钩子函数中观测 includeexclude 的变化,通过 cache 对象存储需要缓存的组件,通过 render 函数渲染组件,在 render 函数中,首先获取组件的 key 值,拿到key值后去 cache 对象中去寻找是否有该值,如果没有则以组件 vnode 为值,存入 cache 对象中,如果有则表示该组件有缓存,直接从缓存中拿 vnode 的组件实例。

虚拟 DOM

1. 什么是虚拟 DOM

虚拟 DOM (Virtual DOM )是一层对真实 DOM 的抽象,在 Javascript 对象中,虚拟 DOM 表现为一个 Object 对象。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的命名可能会有差别。创建虚拟 DOM 就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟 DOM 对象的节点与真实 DOM 的属性一一照应。

2. 虚拟 DOM 有哪些作用

用传统的原生 api 或 jQuery 去操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程。比如在一次操作时,需要更新10个 DOM 节点,浏览器收到第一个更新 DOM 请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程,而通过 VNode,同样更新10个DOM 节点,虚拟 DOM 不会立即操作DOM,而是将这10次更新的 diff 内容保存到本地的一个js 对象中,最终将这个 js 对象一次性 attach DOM 树上,避免大量的无谓计算

虚拟 DOM 优势一个是 diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗。另一个是抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种 GUI。

diff 算法

diff 算法是一种通过同层的树节点进行比较的高效算法,其有两个特点:比较只会在同层级进行, 不会跨层级比较;在 diff 比较的过程中,循环从两边向中间比较。

diff 算法在很多场景下都有应用,在 vue 中,作用于虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较,diff 整体策略为:深度优先,同层比较。在 vue2.x 中采用双端比较,新旧 VDOM 分别设置 startIndex 和 endIndex 向中间比较,直到双端相遇。在 vue3.x 中先双端比较,然后寻找最长递增子序列,这个子序列是不用变更的。

vue-router 原理

vue-router 有三种路由模式,分别为 hash 模式、history 模式、 memoryHistory 模式。

1. hash 模式

hash 模式下使用 createWebHashHistory 配置,使用 hash 模式下会在浏览器网页路由当中使用哈希字符(#)对 url 进行切割并且匹配哈希字符后的字符进行判断路由匹配与跳转处理。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。改变 hash 可以监听到 hashchange 事件,vue-router 在其中处理逻辑。

2. history 模式

history 模式下使用 createWebHistory 配置,这个模式下路由的 url 与以前传统的一个网页文件一个访问路径类似,但是应用本质上还是一个 SPA。需要注意服务器的资源匹配方式。使用 window.onpopstate 对浏览器地址进行监听。对浏览器 history api 中的 pushState()replaceState() 进行封装,当方法调用,会对浏览器的历史栈进行修改。从而实现 URL 的跳转而无需加载页面。

3. memoryHistory 模式

memoryHistory 模式是提供在 SSR 情况下的服务端使用,通过 createMemoryHistory 能够创建一个基于内存的历史记录。这个历史记录的主要目的是处理 SSR。它在一个特殊的位置开始,这个位置无处不在。如果用户不在浏览器上下文中,它们可以通过调用 router.push() 或 router.replace() 将该位置替换为启动位置。

axios 原理

axios 是一个基于 promiseHTTP 库,支持 promise 所有的 API,可以从浏览器中创建 XMLHttpRequests 或者从 node.js 创建 http 请求。简单来说,axios的基本原理就是:通过 XMLHttpRequest实现一个 ajax,再通过 promise 对象来对结果进行处理。

vuex 原理

vuex 是一个专为 vue 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

vuex 的功能是依靠 vue 的,vuex 在每个 vue 实例上添加 $store 属性,可以让每个实例访问到 vuex 数据信息;在每个 vue 实例的 data 属性上添加上 state,这样 state 就是响应式的;收集 options 中传入的所有的 gettersmutaionsactions 存放到对象中;当 dispatch 的时候去匹配到 store 类中存放的 actions 方法名字,然后去执行;当 commit 的时候去匹配到 store 类中存放的 mutations 方法名字,然后去执行;这其实就是一个发布订阅模式。

Vue 使用

生命周期

Vue 生命周期总共可以分为8个阶段:创建前后、挂载前后、更新前后、销毁前后,以及一些特殊场景的生命周期。在组合式 api 中,setup() 代替了 created 和 beforeCreated。

生命周期 (Options API) 生命周期 (Composition API) 描述
beforeCreate setup() 在实例初始化完成、props 解析之后、data()computed 等选项处理之前立即调用,通常用于插件开发中执行一些初始化任务
created setup() 组件实例已经完全创建,此时挂载阶段还未开始,因此 $el 属性仍不可用,常用于异步数据获取
beforeMount onBeforeMount() 组件挂载之前,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点
mounted onMounted() 组件挂载到实例上去之后调用,可用于获取访问数据和dom元素
beforeUpdate onBeforeUpdate() 组件数据发生变化,DOM 更新之前调用
updated onUpdated() 组件的任意 DOM 更新后被调用
beforeUnmount onBeforeUnmount() 组件实例卸载之前
unmounted onUnmounted() 组件实例卸载之后,可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接
activated onActivated() keep-alive 缓存的组件激活时调用
deactivated onDeactivated() keep-alive 缓存的组件被移除时调用
errorCaptured onErrorCaptured() 在捕获了后代组件传递的错误时调用

v-if 和 v-for 的优先级

v-for 优先级比 v-if 高,所以永远不要把 v-ifv-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)。如果避免出现这种情况,则在外层嵌套 template(页面渲染不生成 dom 节点),在这一层进行v-if判断,然后在内部进行v-for循环;如果条件出现在循环内部,可通过计算属性 computed 提前过滤掉那些不需要显示的项。

提高 SAP 应用首屏加载速度

减少首屏渲染时间的方法有很多,总的来讲可以分成两大部分 :资源加载优化和页面渲染优化。

1. 减小入口文件体积

常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加。在 vue-router 配置路由的时候,采用动态加载路由的形式,以函数的形式加载路由,这样就可以把各自的路由文件分别打包,只有在解析给定的路由时,才会加载路由组件。

2. 静态资源本地缓存

后端返回资源采用 HTTP 缓存,设置 Cache-ControlLast-ModifiedEtag 等响应头或者采用 Service Worker离线缓存。

前端合理利用 localStorage

3. UI 框架按需加载

在日常使用 UI 框架,例如 element-UIantd,根据文档配置按需引入。

4. 资源压缩

图片资源虽然不在编码过程中,但它却是对页面性能影响最大的因素,对于所有的图片资源,我们可以进行适当的压缩。对页面上使用到的icon,可以使用在线字体图标,或者雪碧图,将众多小图标合并到同一张图上,用以减轻http请求压力。项目拆完包之后,可以再用 gzip 做一下压缩。

5. 使用SSR

SSR(Server side ),也就是服务端渲染,组件或页面通过服务器生成html字符串,再发送到浏览器,vue 应用可以使用 Nuxt.js 实现服务端渲染。

在 Vue2.x 中给对象添加新属性页面不刷新

vue2 是通过 Object.defineProperty 实现数据响应式,当访问对象属性或者设置属性值的时候都能够触发 settergetter,但为对象添加新属性的时,却没有经过Object.defineProperty 设置成响应式数据,也就无法触发拦截。

可采取下面三种解决方案实现数据与视图同步更新:

  1. Vue.set( target, propertyName/index, value )

  2. Object.assign() 创建一个新的对象,合并原对象和混入对象的属性

  3. $forcecUpdated 强制 vue 实例重新渲染

组件通信

整理 vue 中8种常规的通信方案

1. 通过 props 传递

适用于父组件传递数据给子组件,子组件设置 props 属性或者使用 defineProps(),定义接收父组件传递过来的参数,父组件在使用子组件标签时通过字面量来传递值。

2. 通过 emit 触发自定义事件

适用于子组件传递数据给父组件,子组件通过 $emit / defineEmits 声明自定义事件,第一个参数作为事件名称,第二个参数作为传递数值。父组件绑定监听器获取到子组件传递过来的参数。

3. 使用 ref

父组件在使用子组件的时候设置ref,父组件通过 $ref/ ref().value 来获取子组件实例,通过子组件实例拿到对应的数据。

4. EventBus

适用于兄弟组件传值,创建一个中央事件总线 EventBus 挂载到 vue 实例上,兄弟组件通过$emit 触发自定义事件,$emit第二个参数为传递的数值,另一个兄弟组件通过 $on 监听自定义事件。

5. $parent$root

通过共同父组件 $parent 或者根组件 $root 搭建通信桥连,使用 onemit 进行兄弟组件之间的传值,类似 EventBus

6. attrslisteners

适用于祖先传递数据给子孙,$attrs 是一个包含了组件所有透传 attributes 的对象,包括父级作用域中不作为 prop 被识别且获取的特性绑定。子孙组件中这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问,如果子孙组件里有多个元素时,可以通过 v-bind="$attrs" 显式的绑定到需要的元素上。在 vue3.x$listeners 已经被合并到 $attr 中。

7. ProvideInject

在祖先组件定义 provide 属性,返回传递的值,在后代组件通过 inject 接收组件传递过来的值。

8. Vuex

适用于复杂关系的组件数据传递,Vuex作用相当于一个用来存储共享变量的容器,state用来存放共享变量的地方,getter 可以增加一个 getter 派生状态,(相当于store中的计算属性),用来获得共享变量的值,mutations 用来存放修改 state 的方法,actions 也是用来存放修改state 的方法,不过 action 是在 mutations 的基础上进行。常用来做一些异步操作。

Slot 插槽

slot 本质上是返回 VNode 的函数。通过 slot 插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用,比如布局组件、表格列、下拉选、弹框显示内容等。slot 可以分为以下三种:默认插槽,具名插槽,作用域插槽。

Vue 常用的修饰符

vue 中,修饰符处理了许多 DOM 事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。vue 中修饰符分为以下五种:表单修饰符、事件修饰符、鼠标按键修饰符、键值修饰符、v-bind 修饰符。

相关推荐
533_30 分钟前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
ZBY520311 小时前
【Vue】 npm install amap-js-api-loader指南
javascript·vue.js·npm
计算机毕设指导61 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
木子02043 小时前
前端VUE项目启动方式
前端·javascript·vue.js
运维-大白同学4 小时前
将django+vue项目发布部署到服务器
服务器·vue.js·django
星星会笑滴5 小时前
vue+node+Express+xlsx+emements-plus实现导入excel,并且将数据保存到数据库
vue.js·excel·express
Backstroke fish5 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
临枫5415 小时前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
RAY_CHEN.5 小时前
vue3 pinia 中actions修改状态不生效
vue.js·typescript·npm
酷酷的威朗普5 小时前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5