写点什么

字节前端二面高频 vue 面试题整理

作者:yyds2026
  • 2023-02-24
    浙江
  • 本文字数:9615 字

    阅读完需:约 32 分钟

怎样理解 Vue 的单向数据流

数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样会 防止从子组件意外改变父级组件的状态 ,从而导致你的应用的数据流向难以理解


注意 :在子组件直接用 v-model 绑定父组件传过来的 prop 这样是不规范的写法 开发环境会报警告


如果实在要改变父组件的 prop 值,可以在 data 里面定义一个变量 并用 prop 的值初始化它 之后用$emit 通知父组件去修改


有两种常见的试图改变一个 prop 的情形 :


  1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop用作其初始值


props: ['initialCounter'],data: function () {  return {    counter: this.initialCounter  }}
复制代码


  1. 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性


props: ['size'],computed: {  normalizedSize: function () {    return this.size.trim().toLowerCase()  }}
复制代码

Vue 中如何检测数组变化

前言


Vue 不能检测到以下数组的变动:


  • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue

  • 当你修改数组的长度时,例如:vm.items.length = newLength


Vue 提供了以下操作方法


// Vue.setVue.set(vm.items, indexOfItem, newValue)// vm.$set,Vue.set的一个别名vm.$set(vm.items, indexOfItem, newValue)// Array.prototype.splicevm.items.splice(indexOfItem, 1, newValue)
复制代码


分析


数组考虑性能原因没有用 defineProperty 对数组的每一项进行拦截,而是选择对 7 种数组(push,shift,pop,splice,unshift,sort,reverse)方法进行重写(AOP 切片思想)


所以在 Vue 中修改数组的索引和长度是无法监控到的。需要通过以上 7 种变异方法修改数组才会触发数组对应的 watcher 进行更新


  • 用函数劫持的方式,重写了数组方法,具体呢就是更改了数组的原型,更改成自己的,用户调数组的一些方法的时候,走的就是自己的方法,然后通知视图去更新

  • 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象才能进行观测,观测过的也不会进行观测)


原理


Vuedata 中的数组,进行了原型链重写。指向了自己定义的数组原型方法,这样当调用数组api 时,可以通知依赖更新,如果数组中包含着引用类型。会对数组中的引用类型再次进行监控。



手写简版分析


let oldArray = Object.create(Array.prototype);['shift', 'unshift', 'push', 'pop', 'reverse','sort'].forEach(method => {    oldArray[method] = function() { // 这里可以触发页面更新逻辑        console.log('method', method)        Array.prototype[method].call(this,...arguments);    }});let arr = [1,2,3];arr.__proto__ = oldArray;arr.unshift(4);
复制代码


源码分析


// 拿到数组原型拷贝一份const arrayProto = Array.prototype // 然后将arrayMethods继承自数组原型// 这里是面向切片编程思想(AOP)--不破坏封装的前提下,动态的扩展功能export const arrayMethods = Object.create(arrayProto) const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]
methodsToPatch.forEach(function (method) { // 重写原型方法 const original = arrayProto[method] // 调用原数组的方法
def(arrayMethods, method, function mutator (...args) { // 这里保留原型方法的执行结果 const result = original.apply(this, args) // 这句话是关键 // this代表的就是数据本身 比如数据是{a:[1,2,3]} 那么我们使用a.push(4) this就是a ob就是a.__ob__ 这个属性就是上段代码增加的 代表的是该数据已经被响应式观察过了指向Observer实例 const ob = this.__ob__
// 这里的标志就是代表数组有新增操作 let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } // 如果有新增的元素 inserted是一个数组 调用Observer实例的observeArray对数组每一项进行观测 if (inserted) ob.observeArray(inserted)
ob.dep.notify() // 当调用数组方法后,手动通知视图更新
return result }) })
this.observeArray(value) // 进行深度监控
复制代码


vue3:改用 proxy ,可直接监听对象数组的变化

Vue 单页应用与多页应用的区别

概念:


  • SPA 单页面应用(SinglePage Web Application),指只有一个主页面的应用,一开始只需要加载一次 js、css 等相关资源。所有内容都包含在主页面,对每一个功能模块组件化。单页应用跳转,就是切换相关组件,仅仅刷新局部资源。

  • MPA 多页面应用 (MultiPage Application),指有多个独立页面的应用,每个页面必须重复加载 js、css 等相关资源。多页应用跳转,需要整页资源刷新。

$nextTick 原理及作用

Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。


nextTick 的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout 的原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列。


nextTick 不仅是 Vue 内部的异步队列的调用方法,同时也允许开发者在实际项目中使用这个方法来满足实际应用中对 DOM 更新数据时机的后续逻辑处理


nextTick 是典型的将底层 JavaScript 执行原理应用到具体案例中的示例,引入异步更新队列机制的原因∶


  • 如果是同步更新,则多次对一个或多个属性赋值,会频繁触发 UI/DOM 的渲染,可以减少一些无用渲染

  • 同时由于 VirtualDOM 的引入,每一次状态发生变化后,状态变化的信号会发送给组件,组件内部使用 VirtualDOM 进行计算得出需要更新的具体的 DOM 节点,然后对 DOM 进行更新操作,每次更新状态后的渲染过程需要更多的计算,而这种无用功也将浪费更多的性能,所以异步渲染变得更加至关重要


Vue 采用了数据驱动视图的思想,但是在一些情况下,仍然需要操作 DOM。有时候,可能遇到这样的情况,DOM1 的数据发生了变化,而 DOM2 需要从 DOM1 中获取数据,那这时就会发现 DOM2 的视图并没有更新,这时就需要用到了nextTick了。


由于 Vue 的 DOM 操作是异步的,所以,在上面的情况中,就要将 DOM2 获取数据的操作写在$nextTick中。


this.$nextTick(() => {    // 获取数据的操作...})
复制代码


所以,在以下情况下,会用到 nextTick:


  • 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的 DOM 结构的时候,这个操作就需要方法在nextTick()的回调函数中。

  • 在 vue 生命周期中,如果在 created()钩子进行 DOM 操作,也一定要放在nextTick()的回调函数中。


因为在 created()钩子函数中,页面的 DOM 还未渲染,这时候也没办法操作 DOM,所以,此时如果想要操作 DOM,必须将操作的代码放在nextTick()的回调函数中。

过滤器的作用,如何实现一个过滤器

根据过滤器的名称,过滤器是用来过滤数据的,在 Vue 中使用filters来过滤数据,filters不会修改数据,而是过滤数据,改变用户看到的输出(计算属性 computed ,方法 methods 都是通过修改数据来处理数据格式的输出显示)。


使用场景:


  • 需要格式化数据的情况,比如需要处理时间、价格等数据格式的输出 / 显示。

  • 比如后端返回一个 年月日的日期字符串,前端需要展示为 多少天前 的数据格式,此时就可以用fliters过滤器来处理数据。


过滤器是一个函数,它会把表达式中的值始终当作函数的第一个参数。过滤器用在插值表达式 {{ }}v-bind 表达式 中,然后放在操作符“ | ”后面进行指示。


例如,在显示金额,给商品价格添加单位:


<li>商品价格:{{item.price | filterPrice}}</li>
filters: { filterPrice (price) { return price ? ('¥' + price) : '--' } }
复制代码

data 为什么是一个函数而不是对象

JavaScript 中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。


而在 Vue 中,更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。


所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当每次复用组件的时候,就会返回一个新的 data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。


参考 前端进阶面试题详细解答

Vue 中封装的数组方法有哪些,其如何实现页面更新

在 Vue 中,对响应式处理利用的是 Object.defineProperty 对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以需要对这些操作进行 hack,让 Vue 能监听到其中的变化。 那 Vue 是如何实现让这些数组方法实现元素的实时更新的呢,下面是 Vue 中对这些方法的封装:


// 缓存数组原型const arrayProto = Array.prototype;// 实现 arrayMethods.__proto__ === Array.prototypeexport const arrayMethods = Object.create(arrayProto);// 需要进行功能拓展的方法const methodsToPatch = [  "push",  "pop",  "shift",  "unshift",  "splice",  "sort",  "reverse"];
/** * Intercept mutating methods and emit events */methodsToPatch.forEach(function(method) { // 缓存原生数组方法 const original = arrayProto[method]; def(arrayMethods, method, function mutator(...args) { // 执行并缓存原生数组功能 const result = original.apply(this, args); // 响应式处理 const ob = this.__ob__; let inserted; switch (method) { // push、unshift会新增索引,所以要手动observer case "push": case "unshift": inserted = args; break; // splice方法,如果传入了第三个参数,也会有索引加入,也要手动observer。 case "splice": inserted = args.slice(2); break; } // if (inserted) ob.observeArray(inserted);// 获取插入的值,并设置响应式监听 // notify change ob.dep.notify();// 通知依赖更新 // 返回原生数组方法的执行结果 return result; });});
复制代码


简单来说就是,重写了数组中的那些原生方法,首先获取到这个数组的__ob__,也就是它的 Observer 对象,如果有新的值,就调用 observeArray 继续对新的值观察变化(也就是通过target__proto__ == arrayMethods来改变了数组实例的型),然后手动调用 notify,通知渲染 watcher,执行 update。

Vue 中 computed 和 watch 有什么区别?

计算属性 computed: (1)支持缓存,只有依赖数据发生变化时,才会重新进行计算函数; (2)计算属性内不支持异步操作; (3)计算属性的函数中都有一个 get(默认具有,获取计算属性)和 set(手动添加,设置计算属性)方法; (4)计算属性是自动监听依赖值的变化,从而动态返回内容。


侦听属性 watch: (1)不支持缓存,只要数据发生变化,就会执行侦听函数; (2)侦听属性内支持异步操作; (3)侦听属性的值可以是一个对象,接收 handler 回调,deep,immediate 三个属性; (3)监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些其他事情

Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?

不会立即同步执行重新渲染。Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。


如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环 tick 中,Vue 刷新队列并执行实际(已去重的)工作。

action 与 mutation 的区别

  • mutation 是同步更新, $watch 严格模式下会报错

  • action 是异步操作,可以获取数据后调用 mutation 提交最终数据

assets 和 static 的区别

相同点: assetsstatic 两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下,这是相同点


不相同点:assets 中存放的静态资源文件在项目打包时,也就是运行 npm run build 时会将 assets 中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在 static 文件中跟着 index.html 一同上传至服务器。static 中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是 static 中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于 assets 中打包后的文件提交较大点。在服务器中就会占据更大的空间。


建议: 将项目中 template需要的样式文件 js 文件等都可以放置在 assets 中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css 等文件可以放置在 static 中,因为这些引入的第三方文件已经经过处理,不再需要处理,直接上传。

Vue template 到 render 的过程

vue 的模版编译过程主要如下:template -> ast -> render 函数


vue 在模版编译版本的码中会执行 compileToFunctions 将 template 转化为 render 函数:


// 将模板编译为render函数const { render, staticRenderFns } = compileToFunctions(template,options//省略}, this)
复制代码


CompileToFunctions 中的主要逻辑如下∶ (1)调用 parse 方法将 template 转化为 ast(抽象语法树)


constast = parse(template.trim(), options)
复制代码


  • parse 的目标:把 tamplate 转换为 AST 树,它是一种用 JavaScript 对象的形式来描述整个模板。

  • 解析过程:利用正则表达式顺序解析模板,当解析到开始标签、闭合标签、文本的时候都会分别执行对应的 回调函数,来达到构造 AST 树的目的。


AST 元素节点总共三种类型:type 为 1 表示普通元素、2 为表达式、3 为纯文本


(2)对静态节点做优化


optimize(ast,options)
复制代码


这个过程主要分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化


深度遍历 AST,查看每个子树的节点元素是否为静态节点或者静态节点根。如果为静态节点,他们生成的 DOM 永远不会改变,这对运行时模板更新起到了极大的优化作用。


(3)生成代码


const code = generate(ast, options)
复制代码


generate 将 ast 抽象语法树编译成 render 字符串并将静态部分放到 staticRenderFns 中,最后通过 new Function(`` render``) 生成 render 函数。

谈谈对 keep-alive 的了解

keep-alive 可以实现组件的缓存,当组件切换时不会对当前组件进行卸载。常用的2个属性 include/exclude ,2个生命周期 activated deactivated

Vue 模板编译原理

Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步


第一步是将 模板字符串 转换成 element ASTs(解析器)第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)
复制代码

MVVM、MVC、MVP 的区别

MVC、MVP 和 MVVM 是三种常见的软件架构设计模式,主要通过分离关注点的方式来组织代码结构,优化开发效率。


在开发单页面应用时,往往一个路由页面对应了一个脚本文件,所有的页面逻辑都在一个脚本文件里。页面的渲染、数据的获取,对用户事件的响应所有的应用逻辑都混合在一起,这样在开发简单项目时,可能看不出什么问题,如果项目变得复杂,那么整个文件就会变得冗长、混乱,这样对项目开发和后期的项目维护是非常不利的。


(1)MVC


MVC 通过分离 Model、View 和 Controller 的方式来组织代码结构。其中 View 负责页面的显示逻辑,Model 负责存储页面的业务数据,以及对相应数据的操作。并且 View 和 Model 应用了观察者模式,当 Model 层发生改变的时候它会通知有关 View 层更新页面。Controller 层是 View 层和 Model 层的纽带,它主要负责用户与应用的响应操作,当用户与页面产生交互的时候,Controller 中的事件触发器就开始工作了,通过调用 Model 层,来完成对 Model 的修改,然后 Model 层再去通知 View 层更新。


(2)MVVM


MVVM 分为 Model、View、ViewModel:


  • Model 代表数据模型,数据和业务逻辑都在 Model 层中定义;

  • View 代表 UI 视图,负责数据的展示;

  • ViewModel 负责监听 Model 中数据的改变并且控制视图的更新,处理用户交互操作;


Model 和 View 并无直接关联,而是通过 ViewModel 来进行联系的,Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新,View 中由于用户交互操作而改变的数据也会在 Model 中同步。


这种模式实现了 Model 和 View 的数据自动同步,因此开发者只需要专注于数据的维护操作即可,而不需要自己操作 DOM。


(3)MVP


MVP 模式与 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中使用观察者模式,来实现当 Model 层数据发生变化的时候,通知 View 层的更新。这样 View 层和 Model 层耦合在一起,当项目逻辑变得复杂的时候,可能会造成代码的混乱,并且可能会对代码的复用性造成一些问题。MVP 的模式通过使用 Presenter 来实现对 View 层和 Model 层的解耦。MVC 中的 Controller 只知道 Model 的接口,因此它没有办法控制 View 层的更新,MVP 模式中,View 层的接口暴露给了 Presenter 因此可以在 Presenter 中将 Model 的变化和 View 的变化绑定在一起,以此来实现 View 和 Model 的同步更新。这样就实现了对 View 和 Model 的解耦,Presenter 还包含了其他的响应逻辑。

Vue 修饰符有哪些

事件修饰符


  • .stop 阻止事件继续传播

  • .prevent 阻止标签默认行为

  • .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理

  • .self 只当在 event.target 是当前元素自身时触发处理函数

  • .once 事件将只会触发一次

  • .passive 告诉浏览器你不想阻止事件的默认行为


v-model 的修饰符


  • .lazy 通过这个修饰符,转变为在 change 事件再同步

  • .number 自动将用户的输入值转化为数值类型

  • .trim 自动过滤用户输入的首尾空格


键盘事件的修饰符


  • .enter

  • .tab

  • .delete (捕获“删除”和“退格”键)

  • .esc

  • .space

  • .up

  • .down

  • .left

  • .right


系统修饰键


  • .ctrl

  • .alt

  • .shift

  • .meta


鼠标按钮修饰符


  • .left

  • .right

  • .middle

对 React 和 Vue 的理解,它们的异同

相似之处:


  • 都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库;

  • 都有自己的构建工具,能让你得到一个根据最佳实践设置的项目模板;

  • 都使用了 Virtual DOM(虚拟 DOM)提高重绘性能;

  • 都有 props 的概念,允许组件间的数据传递;

  • 都鼓励组件化应用,将应用分拆成一个个功能明确的模块,提高复用性。


不同之处 :


1)数据流


Vue 默认支持数据双向绑定,而 React 一直提倡单向数据流


2)虚拟 DOM


Vue2.x 开始引入"Virtual DOM",消除了和 React 在这方面的差异,但是在具体的细节还是有各自的特点。


  • Vue 宣称可以更快地计算出 Virtual DOM 的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。

  • 对于 React 而言,每当应用的状态被改变时,全部子组件都会重新渲染。当然,这可以通过 PureComponent/shouldComponentUpdate 这个生命周期方法来进行控制,但 Vue 将此视为默认的优化。


3)组件化


React 与 Vue 最大的不同是模板的编写。


  • Vue 鼓励写近似常规 HTML 的模板。写起来很接近标准 HTML 元素,只是多了一些属性。

  • React 推荐你所有的模板通用 JavaScript 的语法扩展——JSX 书写。


具体来讲:React 中 render 函数是支持闭包特性的,所以 import 的组件在 render 中可以直接调用。但是在 Vue 中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以 import 一个组件完了之后,还需要在 components 中再声明下。 4)监听数据变化的实现原理不同


  • Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能

  • React 默认是通过比较引用的方式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的 vDOM 的重新渲染。这是因为 Vue 使用的是可变数据,而 React 更强调数据的不可变。


5)高阶组件


react 可以通过高阶组件(HOC)来扩展,而 Vue 需要通过 mixins 来扩展。


高阶组件就是高阶函数,而 React 的组件本身就是纯粹的函数,所以高阶函数对 React 来说易如反掌。相反 Vue.js 使用 HTML 模板创建视图组件,这时模板无法有效的编译,因此 Vue 不能采用 HOC 来实现。


6)构建工具


两者都有自己的构建工具:


  • React ==> Create React APP

  • Vue ==> vue-cli


7)跨平台


  • React ==> React Native

  • Vue ==> Weex

说说 Vue 的生命周期吧

什么时候被调用?


  • beforeCreate :实例初始化之后,数据观测之前调用

  • created:实例创建万之后调用。实例完成:数据观测、属性和方法的运算、 watch/event 事件回调。无 $el .

  • beforeMount:在挂载之前调用,相关 render 函数首次被调用

  • mounted:了被新创建的vm.$el替换,并挂载到实例上去之后调用改钩子。

  • beforeUpdate:数据更新前调用,发生在虚拟 DOM 重新渲染和打补丁,在这之后会调用改钩子。

  • updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用改钩子。

  • beforeDestroy:实例销毁前调用,实例仍然可用。

  • destroyed:实例销毁之后调用,调用后,Vue 实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除


每个生命周期内部可以做什么?


  • created:实例已经创建完成,因为他是最早触发的,所以可以进行一些数据、资源的请求。

  • mounted:实例已经挂载完成,可以进行一些 DOM 操作。

  • beforeUpdate:可以在这个钩子中进一步的更改状态,不会触发重渲染。

  • updated:可以执行依赖于 DOM 的操作,但是要避免更改状态,可能会导致更新无线循环。

  • destroyed:可以执行一些优化操作,清空计时器,解除绑定事件。


ajax 放在哪个生命周期?:一般放在 mounted 中,保证逻辑统一性,因为生命周期是同步执行的, ajax 是异步执行的。单数服务端渲染 ssr 同一放在 created 中,因为服务端渲染不支持 mounted 方法。 什么时候使用 beforeDestroy?:当前页面使用 $on ,需要解绑事件。清楚定时器。解除事件绑定, scroll mousemove

子组件可以直接改变父组件的数据吗?

子组件不可以直接改变父组件的数据。这样做主要是为了维护父子组件的单向数据流。每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。如果这样做了,Vue 会在浏览器的控制台中发出警告。


Vue 提倡单向数据流,即父级 props 的更新会流向子组件,但是反过来则不行。这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解,导致数据流混乱。如果破坏了单向数据流,当应用复杂时,debug 的成本会非常高。


只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。

虚拟 DOM 的优劣如何?

优点:


  • 保证性能下限: 虚拟 DOM 可以经过 diff 找出最小差异,然后批量进行 patch,这种操作虽然比不上手动优化,但是比起粗暴的 DOM 操作性能要好很多,因此虚拟 DOM 可以保证性能下限

  • 无需手动操作 DOM: 虚拟 DOM 的 diff 和 patch 都是在一次更新中自动进行的,我们无需手动操作 DOM,极大提高开发效率

  • 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等


缺点:


  • 无法进行极致优化: 在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化,比如 VScode 采用直接手动操作 DOM 的方式进行极端的性能优化


用户头像

yyds2026

关注

还未添加个人签名 2022-09-08 加入

还未添加个人简介

评论

发布
暂无评论
字节前端二面高频vue面试题整理_Vue_yyds2026_InfoQ写作社区