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

但是 MVC 有一个巨大的缺陷,控制器承担的责任太大。随着项目愈加的复杂,控制器中的代码也会越臃肿,导致不利于维护。
所以引入 MVVM 模式。添加了 ViewModel 概念。 ViewModel 只关心数据和业务的处理,不关心 View 如何处理数据,在这种情况下, View 和 Model 都可以独立出来,任何一方改变了也不一定需要改变另一方,并且可以将一些可以复用的逻辑放在一个 ViewModel 中,让多个 View 复用这个 ViewModel。

以 Vue 来举例,ViewModel 就是组件的实例。View 就是模板,Model 引入 Vuex 的情况下完全可以和组件分离。
对于 MVVM 来说,其实最重要的并不是通过双向绑定或者其他的方式将 View 与 ViewModel 绑定起来,而是通过 ViewModel 将视图中的状态和用户的行为分离出一个抽象,这才是 MVVM 的精髓。
Virtual DOM
由于直接操作 DOM 会存在性能缺陷,操作 JS 对象会快很多,所以通过 JS 来模拟 DOM
const ul = {
tag: 'ul',
props: {
class: 'list'
},
children: {
tag: 'li',
children: '1'
}
}对应真实的 DOM 结构
<ul class='list'>
<li>1</li>
</ul>可以通过 JS 对象渲染出对应的 DOM,但是如何判断新旧两个 JS 对象的最小差异且实现局部更新 DOM。
DOM 是一个多叉树结构,如果需要完整的对比两颗树的差异,那么需要的时间复杂度会是 O(n ^ 3),这个复杂度肯定是不能接受的。于是 React 优化了算法,实现 O(n) 的复杂度来对比差异。实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比
先上至下,坐到右遍历对象,树的深度遍历,给每个节点索引,便于最后渲染差异
一旦节点有子元素,就去判断子元素是否有不同
第一步中判断新旧节点 tagName 是否相同,如果不相同就代表节点被替换;如果没有更改,就要判断是否有子元素,存在即进行第二步计算。
在第二步中,需要判断原本的列表中是否有节点被移除,在新的列表中要判断是否有新的节点加入,还需要判断节点是否移动。
例如:页面中有一个列表,对列表中元素进行了变更
// 假设这里模拟一个 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。
window.addEventListener('hashchange', () => {
// code
})Hash 模式兼容性更好。
History 模式
History 模式是 HTML5 功能,主要使用 history.pushState 和 history.replaceState 监听 URL 变化。
// 新增历史记录
history.pushState(stateObject, title, URL)
// 替换当前历史记录
history.replaceState(stateObject, title, URL)例如:
const state = { 'page_id': 1, 'user_id': 5 }
const title = ''
const url = 'hello-world.html'
history.pushState(state, title, url)当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件
window.addEventListener('popstate', e => {
// e.state 就是 pushState(stateObject) 中的 stateObject
console.log(e.state)
})两种模式的差异:
Hash模式只能修改#后的内容,History模式可以通过API设置任意同源URLHash模式只能改#后的哈希值,History模式可以添加任意类型数据到历史数据中Hash模式无需后端配置,兼容性好,History模式在手动输入地址或刷新页面时会发起URL请求,后端需要支持配置index.html访问。
React 和 Vue 之间的区别
Vue 双向数据绑定,内部实现了数据的更新,修改数据相对 React 要简单,不用向 React 使用 setState 来改变状态。React 需要手动优化部分操作,而 Vue 的依赖追踪,页面渲染已内部最优。
React 的 JSX,存在上手成本,更加灵活,选择周边工具完全需要靠用户自身对技术的把控。
前端小册