{T}

Common 面试题

MVC 和 MVVM 区别

MVC 全名是 Model View Controller,是模型 -视图 -控制器 的缩写,一种软件设计典范

  • Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
  • View(视图):是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
  • Controller(控制器):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据

img

<p style="color:red">MVC 的思想:一句话描述就是 Controller 负责将 Model 的数据用 View 显示出来,换句话说就是在 Controller 里面把 Model 的数据赋值给 View</p>

MVVM 新增了 VM 类,ViewModel 层,做了两件事达到了数据的双向绑定

  • 一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定

  • 二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听

image-20231019181745385

MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View 层显示会自动改变(对应Vue数据驱动的思想)

整体看来,MVVM 比 MVC 精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作 DOM 元素

因为在 MVVM 中,View 不知道 Model 的存在,Model 和 ViewModel 也观察不到 View,这种低耦合模式提高代码的可重用性

注意:Vue 并没有完全遵循 MVVM 的思想 这一点官网自己也有说明

严格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 提供了 $refs 这个属性,让 Model 可以直接操作 View,违反了这一规定,所以说 Vue 没有完全遵循 MVVM

父子组件生命周期执行顺序

加载渲染过程

  • 父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted

子组件更新过程

  • 父 beforeUpdate->子 beforeUpdate->子 updated->父 updated

销毁过程

  • 父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed

Vue 事件绑定原理

原生事件绑定是通过 addEventListener 绑定给真实元素的,组件事件绑定是通过 Vue 自定义的 $on 实现的。如果要在组件上使用原生事件,需要加 .native 修饰符,这样就相当于在父组件中把子组件当做普通 html 标签,然后加上原生事件

onemit 是基于发布订阅模式的,维护一个事件中心,on 的时候将事件按名称存在事件中心里,称之为订阅者,然后 emit 将对应的事件进行发布,去执行事件中心里的对应的监听器

Vue React 等框架的存在价值

VueReact 等框架给前端开发带来了革命性的变化。相比于此前的 jQuery 时代,它们的价值在于

  • 组件化 —— 这不是核心原因。WebComponent 已提出多年,当仍未发展壮大
  • 数据视图分离,数据驱动视图 —— 这才是核心!!!

数据视图分离,开发时只需要关注业务数据(React 的 state,Vue 的 data)即可,不用在实时的修改 DOM —— 这一点和 jQuery 有了本质区别。特别是对于大型的前端项目,将极大的降低开发复杂度,提高稳定性

数据驱动视图,内部借助于 vdom 实现

虚拟 DOM 优缺点

由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产生一定的性能问题。这就是虚拟 Dom 的产生原因。Vue2 的 Virtual DOM 借鉴了开源库 snabbdom 的实现。Virtual DOM 本质就是用一个原生的 JS 对象去描述一个 DOM 节点,是对真实 DOM 的一层抽象。

vdom 是 React 最先提出来的概念。React 的 JSX ,Vue 的 template 其实都是语法糖,它们本质上都是一个函数,成为 render 函数

javascript
// JSX: <p id="p1">hello world</p>
function render(): VNode {
  return createElement('p', { id: 'p1' }, ['hello world'])
}

执行 render 函数返回的就是一个 vdom 对象,一般叫做 vnode(虚拟节点),对应 DOM Node。

每次数据更新(如 React setState)render 函数都会生成 newVnode ,然后前后对比 diff(vnode, newVnode),计算出需要修改的 DOM 节点,再做修改。

优缺点

优点:

  1. 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
  2. 无需手动操作 DOM: 不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,以可预期的方式更新视图,极大提高我们的开发效率;
  3. 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等

缺点:

  1. 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
  2. 首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢

vdom 对比 DOM 操作

使用 vdom 和直接操作 DOM 对比:

  • jquery 时代:直接修改 DOM
  • 框架时代:生成 vdom ,进行 diff 运算 --> 修改 DOM

但凡事都要有一个业务背景。如果页面功能越来越复杂,直接操作 DOM 代码将会难以阅读和维护,大家更希望要“数据视图分离,数据驱动视图”。

在这个前提下,vdom 更快。因为业务复杂、代码混乱,将会导致很多无谓的 DOM 操作 —— DOM 操作是昂贵的

而相比于昂贵的 DOM 操作,JS 运算非常快。所以 JS 多做事情(vdom diff)是更优的选择

  • 直接进行 DOM 操作永远都是最快的(但要目标明确,不能有无谓的 DOM 操作 —— 这很难)
  • 如果业务复杂,要“数据视图分离,数据驱动视图”,无法直接修改 DOM ,那 vdom 就是一个很好的选择

vdom 并不比 DOM 操作更快(反而更慢,它做了 JS 运算),它只是在某个特定的场景下,无法做到精准 DOM 修改时,一个更优的选择

Svelte 不使用 vdom ,它将组件修改,编译为精准的 DOM 操作。和 React 设计思路完全不一样。

img

vue 中使用哪些设计模式

工厂模式 - 传入参数即可创建实例

虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode

单例模式 - 整个程序有且仅有一个实例

vuex 和 vue-router 的插件注册方法 install 判断如果系统存在实例就直接返回掉

发布-订阅模式 (vue 事件机制)

观察者模式 (响应式数据原理)

装饰模式: (@装饰器的用法)

策略模式

策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略

Vue 性能优化

这里只列举针对 Vue 的性能优化,整个项目的性能优化是一个大工程

  • 对象层级不要过深,否则性能就会差
  • 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
  • v-if 和 v-show 区分使用场景
  • computed 和 watch 区分使用场景
  • v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
  • 大数据列表和表格性能优化-虚拟列表/虚拟表格
  • 防止内部泄漏,组件销毁后把全局变量和事件销毁
  • 图片懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 适当采用 keep-alive 缓存组件
  • 防抖、节流运用
  • 服务端渲染 SSR or 预渲染

Vue SSR

SSR 也就是服务端渲染,就是将 Vue 在客户端把标签渲染成 HTML 的工作放在服务端完成,然后再把 html 直接返回给客户端

优点:SSR 有着更好的 SEO、并且首屏加载速度更快

缺点:

  • 开发条件会受到限制,服务器端渲染只支持 beforeCreate 和 created 两个钩子,当需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于 Node.js 的运行环境
  • 服务器会有更大的负载需求