Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于主流前端框架的本质区别 #12

Open
li-jia-nan opened this issue Oct 15, 2022 · 0 comments
Open

关于主流前端框架的本质区别 #12

li-jia-nan opened this issue Oct 15, 2022 · 0 comments

Comments

@li-jia-nan
Copy link
Owner

前言

前段时间,StackOverflow发布了2021调查报告Svelte作为一个非主流框架,技压ReactVue等众多前端框架,成为了web领域最受欢迎的框架,你听说过Svelte吗?

111.png

从React15到17,再到即将到来的React18,再从Vue2到Vue3,会不会有种前端框架发展太快?学不动了的感觉?

那么我们来探讨一下,这些主流框架之间的区别吧

tips:本文借鉴了卡颂老师在B站发布的视频,本文发布已征得本人同意,点击查看原视频:『货很干』主流前端框架的实现原理,懂完了你

组件化

主流前端框架都遵循组件化的开发模式,具体来说,框架开发的应用分为三级抽象:

应用 => 组件 => 节点

44.png

就拿掘金App举例来说,掘金是一个应用,这个应用由header组件导航栏组件列表组件footer组件四个部分构成,而这些组件还能继续拆分,比如列表组件由多个列表项组件构成,导航栏组件由多个导航组件构成,导航组件内部,由描述视图的节点加上业务逻辑构成。

  • 这里描述视图的节点可能是一个被<ul>标签包裹的<li>节点
  • 这里的业务逻辑是,当用户点击<li>节点时,需要跳转到对应的地方,并且更新列表组件中的数据

总结一下:框架中最小的单位是节点,节点加业务逻辑构成组件,多个组件构成应用,这就是前端框架的抽象层级划分。

那么前端框架的工作原理是什么呢?我们可以用一个公式概括:UI = F(state)

  • UI - 视图
  • state - 状态
  • F - 框架内部的运行机制

总结起来就是:框架内部的运行机制根据状态渲染视图

说到这里大家可能会有疑惑,既然主流前端框架的工作原理都是一样的,那它们有什么区别呢?

答案是:更新粒度的区别

刚才讲过,框架有三级抽象:应用组件节点,同样的,主流前端框架也分为三种更新粒度,应用级组件级节点级,接下来我们看看这些框架内部都由哪些技术构成:

节点级更新框架:Svelte

我们从最细的粒度,节点级更新的框架Svelte开始讲起(如果想了解这个框架,可以点击这里查看官网)

46544.png

首先定义一个计数器组件,先定义组件逻辑:

  • 定义变量count,初始值为0
  • 定义函数handleClick,每次点击时count + 1
  • 然后定义一个渲染组件的<button>节点,将点击事件绑定为<button>节点的点击回调函数
  • 最后定义一个<p>节点,内容为一段文本

经过渲染,视图上的数字初始值为0,每次点击都会加1

节点级更新框架的原理分为三步:

  1. 状态变化可能导致的节点变化编译为具体方法
  2. 监听状态变化
  3. 当交互导致状态变化后,直接调用具体方法,改变对应视图

在我们上面的例子中:

  1. 变量count变化可能导致<button>节点中的文本节点发生变化,由于<p>节点不包含状态变化,所以它不会出现在编译后的update方法中(这种将状态变化可能导致的节点变化编译为具体方法的技术,被称为预编译,网站中实际运行的代码,都是预编译之后的代码)

  2. 监听状态变化,具体方案是采用了发布订阅设计模式,当创建一个状态后,会为状态维护一张订阅了该状态变化的表,所有需要监听变化的回调函数都在表中进行了注册,这就是发布订阅中的订阅部分

  3. 每当状态变化,会发布这张表,将状态变了这一消息发布出去,每个订阅该状态变化的回调函数都会收到通知并执行,这就是发布订阅中发布的部分

  4. 通过以上这种方式,框架能对每个状态化做出反应,这种精确到状态的更新被称为细粒度更新,这也就是节点级更新框架的工作原理。

应用级更新框架:React

讨论到应用级更新框架,这种框架会采用虚拟DOM的技术,我们通过 React 来举例,首先,我们用 React 重写刚才的 Counter 组件:

import React, { useState } from 'react';

const Counter: React.FC = () => {
  const [count, setCount] = useState<number>(0);
  const handleClick = () => setCount(count + 1);
  return <button onClick={handleClick}>{count}</button>;
};

export default () => (
  <>
    <Counter />
    <Counter />
  </>
);
  • 在父组件内使用两个 <Counter /> 子组件,这两个 Counter 子组件可以独立计数

  • 首屏渲染时,React会根据应用的树结构,生成一颗对应的虚拟DOM树

465454.png

  • 当点击 <button> 节点后,调用handleClick,触发状态更新,react会重新生成一颗完整的虚拟DOM树

57867.png

  • 新生成的每个节点会与之前的虚拟DOM树的同级节点进行比较,如果存在差异,记录该差异

ghgh.png

  • 在上面的例子中,进行到左边的<Counter />组件的<button>节点时,发现前后两个button节点的内容不一致,就会记录下来,等新的虚拟DOM生成完毕后,将记录的差别统一渲染到视图上

总结

刚才讲过,节点级更新的框架需要监听状态的变化,与节点级框架不同,应用级框架不关心是哪个状态发生了变化,应用中的任何组件中的任何一个状态变化,都会从root根节点创建一颗完整的虚拟DOM树,然后通过前后两棵虚拟DOM树的比较,找到变化的部分,最终将变化的部分更新到视图,主流的应用级更新框架就是React

提问

你可能会问:为什么状态变化后,要生成一颗完整的虚拟DOM树,而不是只为改变的状态对应的组件生成对应的虚拟DOM树?这样一来,就能节省很多虚拟DOM操作的开销,从而降低内存的开销,达到性能优化的目的

  • 如果你这么想,那么恭喜你,你发明了Vue

组件级更新框架:Vue

作为组件级更新框架的代表,Vue2采用虚拟DOM + 细粒度更新实现,Vue3则在此基础上引入了预编译,并升级了细粒度更新的实现。

复习一下吧

最后总结一下:主流前端框架的工作原理是 框架的运行机制根据状态渲染视图,他们的区别是更新粒度不同,三种更新粒度的框架分别对应不同的技术点:

更新粒度 对应技术 代表作品
应用级 虚拟DOM React
组件级 细粒度更新、预编译、虚拟DOM Vue2、Vue3
节点级 细粒度更新、预编译 Svelte
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant