Skip to content
On this page

框架与框架 ​

MVVM ​

  • View:用户视图

  • Model:数据

传统的 MVC 架构通常是使用控制器更新模型,视图从模型中获取数据去渲染。当用户有输入时,会通过控制器去更新模型,并且通知视图进行更新

mvc

但是 MVC 有一个巨大的缺陷,控制器承担的责任太大。随着项目愈加的复杂,控制器中的代码也会越臃肿,导致不利于维护。

所以引入 MVVM 模式。添加了 ViewModel 概念。 ViewModel 只关心数据和业务的处理,不关心 View 如何处理数据,在这种情况下, View 和 Model 都可以独立出来,任何一方改变了也不一定需要改变另一方,并且可以将一些可以复用的逻辑放在一个 ViewModel 中,让多个 View 复用这个 ViewModel。

mvvm

以 Vue 来举例,ViewModel 就是组件的实例。View 就是模板,Model 引入 Vuex 的情况下完全可以和组件分离。

对于 MVVM 来说,其实最重要的并不是通过双向绑定或者其他的方式将 View 与 ViewModel 绑定起来,而是通过 ViewModel 将视图中的状态和用户的行为分离出一个抽象,这才是 MVVM 的精髓。

Virtual DOM ​

由于直接操作 DOM 会存在性能缺陷,操作 JS 对象会快很多,所以通过 JS 来模拟 DOM

js
const ul = {
  tag: 'ul',
  props: {
    class: 'list'
  },
  children: {
    tag: 'li',
    children: '1'
  }
}

对应真实的 DOM 结构

html
<ul class='list'>
  <li>1</li>
</ul>

可以通过 JS 对象渲染出对应的 DOM,但是如何判断新旧两个 JS 对象的最小差异且实现局部更新 DOM。

DOM 是一个多叉树结构,如果需要完整的对比两颗树的差异,那么需要的时间复杂度会是 O(n ^ 3),这个复杂度肯定是不能接受的。于是 React 优化了算法,实现 O(n) 的复杂度来对比差异。实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比

  • 先上至下,坐到右遍历对象,树的深度遍历,给每个节点索引,便于最后渲染差异

  • 一旦节点有子元素,就去判断子元素是否有不同

第一步中判断新旧节点 tagName 是否相同,如果不相同就代表节点被替换;如果没有更改,就要判断是否有子元素,存在即进行第二步计算。

在第二步中,需要判断原本的列表中是否有节点被移除,在新的列表中要判断是否有新的节点加入,还需要判断节点是否移动。

例如:页面中有一个列表,对列表中元素进行了变更

js
// 假设这里模拟一个 ul,其中包含了 5 个 li
[1, 2, 3, 4, 5]
// 这里替换上面的 li
[1, 2, 5, 4]

可以看到 ul 中的第三个 li 被移除,四和五替换了位置。

那么在实际的算法中,我们如何去识别改动的是哪个节点呢?

这就引入了 key 属性,大家在列表中都使用过这个属性。这个属性是用来给每一个节点打标记的,用于判断是否是同一个节点,节点的属性是否存在变化。

差异被判断,记录差异,当对比完两颗树后,就可以通过差异去局部更新 DOM,实现性能的最优化。

Virtual DOM 的最大优势:

  • 兼容,实现跨端开发;渲染到其他平台

  • 组件高度抽象

也不是万能的。直接操作 DOM 没有 diff 算法损耗。

前端路由是如何跳转的 ​

前端路由本质是监听 URL 的变化,匹配路由规则,显示相应的页面,无需刷新页面。两种路由实现方式:

  • Hash 模式

  • History 模式

Hash 模式 ​

test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化后,可以使用 hashchange 事件监听 URL 的变化,进行页面跳转,无论哈希值如何变化,服务端收到的 URL 请求一直是 test.com。

js
window.addEventListener('hashchange', () => {
  // code
})

Hash 模式兼容性更好。

History 模式 ​

History 模式是 HTML5 功能,主要使用 history.pushState 和 history.replaceState 监听 URL 变化。

js
// 新增历史记录
history.pushState(stateObject, title, URL)
// 替换当前历史记录
history.replaceState(stateObject, title, URL)

例如:

js
const state = { 'page_id': 1, 'user_id': 5 }
const title = ''
const url = 'hello-world.html'

history.pushState(state, title, url)

当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件

js
window.addEventListener('popstate', e => {
  // e.state 就是 pushState(stateObject) 中的 stateObject
  console.log(e.state)
})

两种模式的差异:

  • Hash 模式只能修改 # 后的内容,History 模式可以通过 API 设置任意同源 URL

  • Hash 模式只能改 # 后的哈希值,History 模式可以添加任意类型数据到历史数据中

  • Hash 模式无需后端配置,兼容性好,History 模式在手动输入地址或刷新页面时会发起 URL 请求,后端需要支持配置 index.html 访问。

React 和 Vue 之间的区别 ​

Vue 双向数据绑定,内部实现了数据的更新,修改数据相对 React 要简单,不用向 React 使用 setState 来改变状态。React 需要手动优化部分操作,而 Vue 的依赖追踪,页面渲染已内部最优。

React 的 JSX,存在上手成本,更加灵活,选择周边工具完全需要靠用户自身对技术的把控。