Appearance
Vue
- vue 解决了什么问题
- vue 和 react 的区别
SPA
说说你对 SPA 单页面的理解,它的优缺点分别是什么?
SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
优点
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
MVVM 的理解?与 MVC 有什么不同?
MVC 指的是 Model-View-Controller,即模型-视图-控制器。
使用 MVC 的目的就是将模型与视图分离 MVC 属于单向通信,必须通过 Controller 来承上启下,既必须由控制器来获取数据,将结果返回给前端,页面重新渲染
MVVM 指的是 Model-View-ViewModel,即模型-视图-视图模型,「模型」指的是后端传递的数据,「视图」指的是所看到的页面,「视图模型」是 MVVM 的核心,它是连接 View 与 Model 的桥梁,实现 view 的变化会自动更新到 viewModel 中,viewModel 中的变化也会自动显示在 view 上,是一种数据驱动视图的模型
区别:
MVC 中的 Control 在 MVVM 中演变成 viewModel MVVM 通过数据来显示视图,而不是通过节点操作 MVVM 主要解决了 MVC 中大量的 DOM 操作,使页面渲染性能降低,加载速度慢,影响用户体验的问题
data 为什么要是函数方式的
在 Vue 中,组件都是可复用的,一个组件创建好后,可以在多个地方重复使用,而不管复用多少次,组件内的 data 都必须是相互隔离,互不影响的,如果 data 以对象的形式存在,由于 Javascript 中对象是引用类型,作用域没有隔离,当你在模板中多次声明这个组件,组件中的 data 会指向同一个引用,此时如果对某个组件的 data 进行修改,会导致其他组件里的 data 也被修改。因此 data 必须以函数的形式返回
总结
为了实现每个组件实例可以维护独立的数据拷贝,不会相互影响
响应式数据
Vue 是如何实现数据双向绑定的?
Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据

输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。 Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。 其中,View 变化更新 Data ,可以通过事件监听的方式来实现,所以 Vue 的数据双向绑定的工作主要是如何根据 Data 变化更新 View。
Vue 主要通过以下 4 个步骤来实现数据双向绑定的: 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
Vue2.x 响应式数据原理
Vue 在初始化数据时,会使用 Object.defineProperty 重新定义 data 中的所有属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件的 watcher)如果属性发生变化会通知相关依赖进行更新操作(发布订阅)。
vue2.x 中如何监测数组变化
使用了函数劫持的方式,重写了数组的方法,Vue 将 data 中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组 api 时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
nextTick 知道吗,实现原理是什么?
nextTick 指在下次 DOM 更新循环结束之后执行延迟回调。nextTick 主要使用了宏任务和微任务。根据执行环境分别尝试采用 Promise.then → MutationObserver 的回调函数 → setImmediate → setTimeout 是否存在(微任务 -> 宏任务),找到存在的就去使用它,以此来确定回调函数队列是以哪个 api 来异步执行
在 nextTick 函数接受到一个 callback 的时候,先不去调用它,而是把它 push 到一个全局的 queue 队列,等待下一个任务队列的时候再一次性把这个 queue 里的函数依次执行 这个队列可能是 microTask 队列,页可能是 macroTask 队列,前两个 api 属于微任务队列,后两个 api 属于宏任务队列
作用:在下次 dom 更新循环结束后执行延迟回调,当我们修改数据之后立即使用 nextTick()来获取最新更新的 Dom
- 对于同步组件,微任务将在组件渲染后执行,所以 nextTick 执行没有问题。
- 对于异步组件,内部也有异步任务,这个时候就取决于任务队列的执行顺序,所以无法保证 nexTick 达到预期效果
在你的组件中,包含一个异步组件。因为异步组件一定不会第一时间实例化,自然无法保证子组件节点挂载。
生命周期
Vue 的生命周期
beforeCreate 是 new Vue()之后触发的第一个钩子,在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问。
created 在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发 updated 函数。可以做一些初始数据的获取,在当前阶段无法与 Dom 进行交互,如果非要想,可以通过 vm.$nextTick 来访问 Dom。
数据观测(data observer)
属性和方法的运算
watch/event 事件回调
但这里还没有$el
beforeMount 发生在挂载之前,在这之前 template 模板已导入渲染函数编译。而当前阶段虚拟 Dom 已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发 updated。
mounted 在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom 节点,使用$refs 属性对 Dom 进行操作。
beforeUpdate 发生在更新之前,也就是响应式数据发生更新,虚拟 dom 重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
updated 发生在更新完成之后,当前阶段组件 Dom 已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
beforeDestroy 发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
destroyed 发生在实例销毁之后,这个时候只剩下了 dom 空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
- 可以执行一些优化操作,例如清空定时器,清理缓存,解除事件绑定等
生命周期调用顺序
组件的调用顺序都是先父后子,渲染完成的顺序是先子后父。 组件的销毁操作是先父后子,销毁完成的顺序是先子后父。
- 加载渲染过程
父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount- >子 mounted->父 mounted
- 子组件更新过程
父 beforeUpdate->子 beforeUpdate->子 updated->父 updated
- 父组件更新过程
父 beforeUpdate -> 父 updated
- 销毁过程
父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed
父组件可以监听到子组件的生命周期吗?
比如有一个父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理、
方式一
js
// Parent.vue
<Child @mounted='doSomething' />
// Child.vue
mounted(){
this.$emit('mounted')
}方式二(更简单 - 通过@hook 来监听)
@hook 可以监听其他生命周期
js
// Parent.vue
<Child @hook:mounted='doSomething' />Computed 和 Watch
- Computed
Computed 本质是一个具备缓存的 watcher,依赖的属性发生变化就会更新视图。
适用于计算比较消耗性能的计算场景。当表达式过于复杂时,在模板中放入过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中处理。
- Watch
Watch 没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。当我们需要深度监听对象中的属性时,可以打开 deep:true 选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用 unWatch 手动注销哦。
运用场景
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
watch 中的 deep:true 是如何实现的?
当用户指定了 watch 中的 deep:true 时,如果当前监控的值是数组类型(对象类型),会对对象中的每一项进行求值,此时会将当前 watcher 存入到对应属性的依赖中,这样数组中的对象发生变化也会通知数据进行更新
本质上是因为 Vue 内部对设置了 deep 的 watch,会进行递归的访问(只要此属性也是响应式属性),而再此过程也会不断发生依赖收集
缺点
由于需要对每一项都进行操作,性能会降低,不建议多次使用 deep:true
为什么 v-if 与 v-for 不建议连在一起使用?
v-for 优先级高于 v-if,如果连在一起使用的话会把 v-if 给每一个元素都添加上,重复运行于每一个 v-for 循环中,会造成性能浪费
keep-alive 在 vue 中的作用是什么?
包裹在<keep-alive>里组件,在切换时会保存其组件的状态,使其不被销毁,防止多次渲染
一般结合路由和动态组件一起使用,用于缓存组件
keep-alive 拥有两个独立的生命周期(activated | deactivated),使 keep-alive 包裹的组件在切换时不被销毁,而是缓存到内存中并执行 deactivated 钩子,切换回组件时会获取内存,渲染后执行 activated 钩子
提供 include 和 exclude 属性,两者都支持字符串或正则表达式
- include 表示只有名称匹配的组件才会被缓存
- exclude 表示任何名称匹配的组件都不会被缓存
- exclude 优先级高于 include
虚拟 DOM
虚拟 Dom 以及 key 属性的作用
由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产生一定的性能问题。这就是虚拟 Dom 的产生原因。
Vue2 的 Virtual DOM 借鉴了开源库 snabbdom 的实现。
Virtual DOM 本质就是用一个原生的 JS 对象去描述一个 DOM 节点。是对真实 DOM 的一层抽象。(也就是源码中的 VNode 类,它定义在 src/core/vdom/vnode.js 中。)
VirtualDOM 映射到真实 DOM 要经历 VNode 的 create、diff、patch 等阶段。
「key 的作用是尽可能的复用 DOM 元素。」
新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。
需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。key 也就是 children 中节点的唯一标识。
优点
保证性能下限:框架的虚拟 Dom 需要适配任何上层 API 可能产生的操作,它的一些 Dom 操作的实现必须是普遍适用的,所以它的性能并不是最优的,但比起粗暴的 Dom 操作要好很多,因此保证了性能的下限
无需手动操作 Dom:框架会根据虚拟 Dom 和数据的双向绑定,帮我们更新视图,提高开发效率
跨平台:虚拟 Dom 本质上是 JavaScript 对象,而真实 Dom 与平台相关,相比下虚拟 Dom 可以更好地跨平台操作
缺点
- 无法进行极致优化
编译
Vue 模版编译原理知道吗,能简单说一下吗?
简单说,Vue 的编译过程就是将 template 转化为 render 函数的过程。会经历以下阶段:
- 生成 AST 树
- 静态树优化(markStatic)
- codegen
首先解析模版,生成 AST 语法树(一种用 JavaScript 对象的形式来描述整个模板)。
使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。
Vue 的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的 DOM 也不会变化。那么优化过程就是深度遍历 AST 树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。
编译的最后一步是将优化后的 AST 树转换为可执行的代码。
Diff
Vue2.x 和 Vue3.x 渲染器的 diff 算法分别说一下
简单来说,diff 算法有以下过程
同级比较,再比较子节点先判断一方有子节点一方没有子节点的情况(如果新的 children 没有子节点,将旧的子节点移除)比较都有子节点的情况(核心 diff)递归比较子节点
正常 Diff 两个树的时间复杂度是 O(n^3),但实际情况下我们很少会进行跨层级的移动 DOM,所以 Vue 将 Diff 进行了优化,从 O(n^3) -> O(n),只有当新旧 children 都为多个子节点时才需要用核心的 Diff 算法进行同层级比较。
Vue2 的核心 Diff 算法采用了双端比较的算法,同时从新旧 children 的两端开始进行比较,借助 key 值找到可复用的节点,再进行相关操作。相比 React 的 Diff 算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。
Vue3.x 借鉴了ivi 算法和 inferno 算法
在创建 VNode 时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个基础之上再配合核心的 Diff 算法,使得性能上较 Vue2.x 有了提升。(实际的实现可以结合 Vue3.x 源码看。)
该算法中还运用了动态规划的思想求解最长递归子序列。
通信
Vue2.x 组件通信有哪些方式
- 父子组件通信
- 父->子 props,子->父 $on、$emit
- 获取父子组件实例 $parent、$children
- Ref 获取实例的方式调用组件的属性或者方法
- Provide、inject 官方不推荐使用,但是写组件库时很常用
- 兄弟组件通信
- Event Bus 实现跨组件通信 Vue.prototype.$bus = new Vue
- Vuex
- 跨级组件通信
- Vuex
- $attrs、$listeners
- Provide、inject
Vue3.0
对于即将到来的 vue3.0 特性你有什么了解的吗?
Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:
- 监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:
- 只能监测属性,不能监测对象
- 检测属性的添加和删除;
- 检测数组索引和长度的变更;
- 支持 Map、Set、WeakMap 和 WeakSet。
新的 observer 还提供了以下特性:
- 用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
- 默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
- 更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。 不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。
- 更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染。
- 模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。
- 对象式的组件声明方式
vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。
此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。
- 其它方面的更改
vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:
- 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
- 支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
- 基于 treeshaking 优化,提供了更多的内置功能。
Vue3.x 响应式数据原理
1. Object.defineProperty => Proxy
Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?
判断当前 Reflect.get 的返回值是否为 Object,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次 get/set,那么如何防止触发多次呢?
我们可以判断 key 是否为当前被代理对象 target 自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行 trigger。
Proxy 的优势
- Proxy 可以直接监听对象而非属性;
- Proxy 可以直接监听数组的变化;
- Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
- Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
- Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;
Object.defineProperty 的优势
- 兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
2. 响应式逻辑更新
- Vue2
在 Vue2 其使用的 Observer 、dep 和 Watch 的方式去实现发布订阅功能的
- Vue3
将整个响应式的逻辑抽离出,从而与 Vue 核心功能分离。
其整体使用的是 effect(副作用)去管理整个响应式功能,通过 effect.track(target)进行依赖的收集,通过 effect.trigger(target)进行派发的流程。
具体如下: 在初始化组件实例对象的时候会创建一个 SetupResultEffect 副作用,然后在组件 setup 或者初始化的时候对遇到响应式的对象,在初始化响应式的对象的时候会通过 Proxy 去劫持响应式对象,
在 render 的时候遇到响应式对象的访问,然后执行 Proxy 的 get 方法进而执行 渲染 effect.track 的过程,将 updateComponent 回调函数添加到渲染 effect 对应 dep 中。
然后遇到 set 的时候,会触发 effect.trigger() 派发更新的过程,这时候从 effectWeakMap 对象中找到 Key 的 effect
Vue3 与 Vue2 静态提升的区别
说明
- Vue2
编译阶段: 对静态节点进行标记(node.static | node.staticRoot ),只是通过深度优先遍历的方式去判断组件子孙节点中是否存在响应式内容,在向上收集的过程中去标记其是否是静态节点或者是静态根节点。 Patch 阶段: 如果是静态节点 那么在 Update 阶段就不会重新执行直接复用
- Vue3
主要提升了以下方面:
- 区别静态节点、静态属性 这样如果静态节点的子孙节点都是静态的,只是其本身包含了响应式的属性内容,那么在更新阶段就不需要重新渲染生成,直接执行节点的属性更新即可
- 缓存了静态节点(
const _hoisted_2 = /*#__PURE__*/ _createElementVNode("div",null,[/*#__PURE__*/_createElementVNode("span", null, "这是静态节点")],-1 /* HOISTED */))、静态属性(const _hoisted_1 = ["data-attr"]) - 通过二进制对节点的类型进行标记 (是否存在 文本插槽 、 响应式 class、响应式 style 等),并在向上收集的过程中收集到父节点中(跟 React.flag, subFlags 逻辑),这样在判断的时候可以直接通过判断
节点.patchFlag判断节点响应式类型
Vue3 与 2 插槽的优化
- Vue 2
每当一个插槽被渲染时,都会生成一个函数来创建对应的 VNode 节点。这意味着即使插槽内容保持不变,每次渲染时仍然会重新生成相同的 VNode 节点。
- Vue 3
通过使用新的编译器,对插槽的处理方式进行了改进。Vue 3 编译器会根据插槽内容的稳定性,将其分为静态插槽和动态插槽:
静态插槽(Static Slots):如果插槽的内容在组件跟踪更新时是稳定的,即不会发生变化,那么这个插槽内容就会被标记为静态插槽。对于静态插槽,Vue 3 编译器会在首次渲染时将其生成为一个常量,并且在后续更新中直接复用该常量。
动态插槽(Dynamic Slots):如果插槽的内容可能随着组件更新而变化,那么这个插槽就会被标记为动态插槽。对于动态插槽,Vue 3 编译器会生成动态的渲染函数,以确保在每次更新时能够正确地处理插槽内容的变化。
Vue3 中的 Teleport(传送门) 是什么?它的作用是什么?
Vue3 中的 Teleport 是控制渲染位置的新指令。它的作用是在 DOM 中移动一个组件的内容而不改变组件的父级。
其提供了一个 to 的属性(它接受一个 css query selector 作为参数, 这个选择器参数就是代表要把这个组件渲染到哪个 dom 元素中),从而在维护组件 VNode 的树结构的情况下,可以将其子节点插入到对应的 DOM 元素下
Vue3 中的 Suspense(异步组件) 是什么?它的作用是什么?
Vue3 中的 Suspense 是 Vue3 中新增的一个组件,它的作用是实现延迟加载和错误处理。在组件中加入 Suspense,可以让异步组件可以渲染出加载状态,并且如果异步组件加载时出现错误,也能够处理这些错误。
Vue3 中的 Fragment 是什么?它的作用是什么?
碎片节点
v-if 和 v-for 的优先级哪个高?
- Vue2
v-for 的优先级更高
- Vue2
v-if 的优先级更高
watch 和 watchEffect 的区别?
Vue3 和 Vue2 的指令的区别
vue2.0 中和 vue3.0 中 自定义指令的原理是一致的,只是生命周期 钩子函数不同
- vue2 中绑定的钩子函数为
- bind - 指令绑定到元素后发生。只发生一次。
- inserted - 元素插入父 DOM 后发生。
- update - 当元素更新,但子元素尚未更新时,将调用此钩子。
- componentUpdated - 一旦组件和子级被更新,就会调用这个钩子。
- unbind - 一旦指令被移除,就会调用这个钩子。也只调用一次。
- vue3.0
将钩子函数的命名与生命周期的钩子函数命名相一致
- bind → beforeMount
- inserted → mounted
- beforeUpdate:新的!这是在元素本身更新之前调用的,很像组件生命周期钩子。
- componentUpdated → updated
- beforeUnmount:新的!与组件生命周期钩子类似,它将在卸载元素之前调用。
- unbind -> unmounted、
shallowReactive 与 shallowRef 的区别?
readonly 与 shallowReadonly
ref 和 reactive 区别
优化
- 编码阶段
- 尽量减少 data 中的数据,data 中的数据都会增加 getter 和 setter,会收集对应的 watcher
- v-if 和 v-for 不能连用如果需要使用 v-for 给每项元素绑定事件时使用事件代理
- SPA 页面采用 keep-alive 缓存组件
- 在更多的情况下,使用 v-if 替代 v-show
- key 保证唯一
- 使用路由懒加载、异步组件
- 防抖、节流
- 第三方模块按需导入
- 长列表滚动到可视区域动态加载
- 图片懒加载
- SEO 优化
- 预渲染
- 服务端渲染 SSR
- 打包优化
- 压缩代码
- Tree Shaking/Scope Hoisting
- 使用 cdn 加载第三方模块
- 多线程打包 happypack
- splitChunks 抽离公共文件
- sourceMap 优化
- 用户体验
- 骨架屏
- PWA
SSR
Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。 即:SSR 大致的意思就是 vue 在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的 html 片段直接返回给客户端这个过程就叫做服务端渲染。
服务端渲染 SSR 的优缺点如下: (1)服务端渲染的优点:
更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
服务端渲染的缺点:
更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
