易忘,但常问的面试题(四)

21、 Vue响应式的原理

什么是数据劫持

数据劫持比较好理解,通常我们利用Object.defineProperty劫持对象的访问器,在属性值发生变化时我们可以获取变化,从而进行进一步操作。

发布者模式 / 订阅者模式

在软件架构中,发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需知道订阅者是否存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需知道发布者是否存在。

这里很明显了,区别就在于,不同于观察者和被观察者,发布者和订阅者是互相不知道对方的存在的,发布者只需要把消息发送到订阅器里面,订阅者只管接受自己需要订阅的内容

响应式原理

Vue响应式的原理就是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty() 来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤:

  • Observe(被劫持的数据对象)
  • Compile(vue的编译器)
  • Wather(订阅者)
  • Dep(用于收集Watcher订阅者们)
  1. 需要给 Observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter 这样的属性,修改这个对象的某个属性,就会触发 setter,那么就能监听到了数据变化。

  2. Compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

  3. Watcher 订阅者是 ObserverCompile 之间通信的桥梁,主要做的事情是: ①在自身实例化时往属性订阅器(Dep)里面添加自己; ②自身必须有一个 update() 方法,待属性变动dep.notice() 通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调。

  4. MVVM作为数据绑定的入口,整合 ObserverCompileWatcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 ObserverCompile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

22、 MVVM、MVC、MVP的区别

MVC

  • M: model数据模型,
  • V:view视图模型,
  • C: controller控制器,

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

MVP

  • M: model数据模型,
  • V: view视图模型,
  • P: Presenter 控制器,

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 还包含了其他的响应逻辑。

MVVM

MVVM 分为 Model、View、ViewModel:

  • Model代表数据模型,数据和业务逻辑都在Model层中定义;
  • View代表UI视图,负责数据的展示;
  • ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;

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

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

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

对象为引用类型,当复用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题。

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

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

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

25、 动态给vue的data添加一个新的属性时会发生什么?

会出现,数据虽然更新了, 但是页面没有更新。

  • vue2是通过Object.defineProperty实现数据响应式,当我们访问定义的属性或者修改属性值的时候都能够触发settergetter
  • 但是我们为obj添加新属性的时候,却无法触发事件属性的拦截,原因是一开始obj的要定义的属性被设成了响应式数据,而新增的属性并没有通过Object.defineProperty设置成响应式数据。

解决方案:

  1. Vue.set()

    • 通过Vue.set向响应式对象中添加一个property,并确保这个新 property同样是响应式的,且触发视图更新
  2. Object.assign()

    • 直接使用Object.assign()添加到对象的新属性不会触发更新
    • 应创建一个新的对象,合并原对象和混入对象的属性

26、 Vue的template模版编译原理

vue中的模板template无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的HTML语法,所有需要将template转化成一个JavaScript函数,这样浏览器就可以执行这一个函数并渲染出对应的HTML元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。模板编译又分三个阶段,解析parse,优化optimize,生成generate,最终生成可执行函数render。

  • 解析阶段:使用大量的正则表达式对template字符串进行解析,将标签、指令、属性等转化为抽象语法树AST。
  • 优化阶段:遍历AST,找到其中的一些静态节点并进行标记,方便在页面重新渲染时,直接跳过这一些静态节点(静态节点不进行,diff算法比较),优化runtime的性能。
  • 生成阶段:将最终的AST转化为render函数字符串。

27、SPA单页面应用优缺点

概念:

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

单页应用优缺点

  1. 优点:

    • 具有桌面应用的即时性、网站的可移植性和可访问性
    • 用户体验好、快,内容的改变不需要重新加载整个页面
  2. 缺点:

    • 首次渲染加载js、css相关资源过大,速度相对较慢
    • 不利于搜索引擎的抓取

28、 $route$router 的区别

route对象 包含当前路由的信息,包括路由的地址、参数、名称等。它可以通过route访问,或者在 Vue 组件中通过route 属性访问。

router对象 则是模块的实例,它提供了用于操作和管理路由的方法。它可以通过router访问,或者在 Vue 组件中通过router 属性访问。

$route 对象提供了以下属性:

  • path:当前路由的地址。
  • name:当前路由的名称,如果有的话。
  • params:当前路由的参数,是一个对象。
  • query:当前路由的查询参数,是一个对象。

$router 对象提供了以下方法:

  • push():向 history 栈中推送一个新的路由。
  • replace():替换当前路由,而不是向 history 栈中添加新的路由。
  • go():导航到指定的路由。
  • back():导航到上一个路由。
  • forward():导航到下一个路由。
  • beforeEach():在导航到每个路由之前执行的钩子函数。
  • afterEach():在导航到每个路由之后执行的钩子函数。

29、 什么Vuex

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

  • 首先vuex的出现是为了解决web组件化开发的过程中,各组件之间传值的复杂和混乱的问题
  • 将我们在多个组件中需要共享的数据放到state中,
  • 要获取或格式化数据需要使用getters,
  • 改变state中的数据,可以使用mutation,但是只能包含同步的操作,在具体组件里面调用的方式this.$store.commit('xxxx')
  • Action也是改变state中的数据,不过是提交的mutation,并且可以包含异步操作,在组件中的调用方式this.$store.dispatch('xxx'); 在actions里面使用的commit('调用mutation')

30、 ## vuex中有几个核心属性,分别是什么?

  • state唯一数据源,Vue 实例中的 data 遵循相同的规则

  • mutation更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,非常类似于事件,通过store.commit 方法触发

  • actionaction 类似于 mutation,不同在于action 提交的是 mutation,而不是直接变更状态,action 可以包含任意异步操作

  • module 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。

  • getters 可以认为是 store 的计算属性,就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值.

相关推荐
小白学习日记41 分钟前
【复习】HTML常用标签<table>
前端·html
john_hjy1 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd1 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
丁总学Java1 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele1 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
It'sMyGo2 小时前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
懒羊羊大王呀2 小时前
CSS——属性值计算
前端·css
睡觉然后上课2 小时前
c基础面试题
c语言·开发语言·c++·面试
DOKE2 小时前
VSCode终端:提升命令行使用体验
前端
xgq2 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试