前端框架原理概览

2025 年 4 月 26 日 星期六(已编辑)
/ ,
81

前端框架原理概览

前端框架原理

现代前端框架几乎都是基于描述自变量UI 视图之间的关系建立的,即“数据驱动视图”

UI=f(state)UI=f(state)

前端经过长时间的发展,对于 UI 的描述语言逐渐衍生出两种解决方案

  • JSX(代表框架:React):类 XML 语法的 ES 的语法糖
  • 模版语法(代表框架:Vue,Svelte)

前端框架分类的依据

对于公式 UI = f(state) 在“自变量与因变量”理论中,state 的本质是自变量,自变量通过直接或者间接(自变量导致因变量变化)的方式来改变 UI。“被改变的 UI”仅仅是”对宿主环境 UI 的描述“,并不是实际宿主环境的 UI。

所以,**UI = f(state) **可以进一步概括为两步:
  1. 根据自变量(state)变化来计算出 UI 的变化
  2. 根据 UI 的变化执行具体宿主环境的 API
至此我们可以根据**前端框架与自变量建立的对应关系的抽象层级**来划分前端框架
  • 元素级:Svelte
  • 组件级:Vue
  • 应用级:React

细粒度更新

React 中定义因变量时候需要显式的指定出依赖项,而 VueMobx 不需要

// React
const y = useMemo(() => x * 2 + 1,[x]);
// Vue
const y = computed(() => x.value * 2 + 1);
// Mobx
const y = computed(() => x.data * 2 + 1);

在 Vue 和 Mobx 中使用的“能自动追踪依赖的技术”被称为“细粒度更新”,它同时是许多前端框架剪力“自变量变化到 UI 变化”的底层原理。

实现 React 中的一些 Hooks

此处不管是 effect.deps 还是 state 对应的 subs 都使用了 Set 利用了集合不会重复添加数据的特性,用细粒度更新实现的 hooks 不需要去显式的指出依赖

4.1 useState

function useState(value){
  // 订阅该state变化的effect
  const subs = new Set();

  const getter = () => {
    // 获取当前上下文的effect
    const effect = effectStack[effectStatck.length-1];
    if(effect){
      subscribe(effect,subs);
    }
    return value;
  }

  const setter = (nextValue) => {
    value = nextValue;

    // 通知所有订阅该state变化的effect执行
    for(const effect of [...subs]){
      effect.execute();
    }
  } 
  return [getter,setter];
}
function subscribe(effect,subs){
  subs.add(effect);
  // 依赖关系建立
  effects.deps.add(subs);
}

4.2 useEffect

const [count,setCount] = useState(0);

useEffect(() => {
  console.log('count is:',count());
})
setCount(2);

useEffect 实现的关键在于建立 useState 和 useEffect 之间的订阅关系

  1. 在 useEffect 回调中执行 useState 的 getter 时,该 Effect 会订阅该 state 的变化
  2. useState 的 setter 在执行时,会向所有订阅了该 state 变化的 effect 发布通知
const effect = {
  // 用于执行useEffect回调函数
  execute,
  // 保存该useEffect依赖的state对应的subs的集合
  deps: new Set()
}
function useEffect(callback){
  const execute = () => {
    // 重置依赖
    cleanup(effect);
    // 将当前effect推入栈顶
    effectStack.push(effect);

    try{
      // 执行回调
      callback();
    }finally{
      // effect出栈
      effectStatck.pop();
    }
  }

  const effect = {
    execute,
    deps: new Set();
  }

  execute(); // 自动收集依赖
}
function cleanup(effect){
  // 从该effect订阅的所有state对应subs移除该effect
  for(const subs of effect.deps){
    subs.delete(effect);
  }
  effect.deps.clear();
}

4.3 useMemo

function useMemo(callback){
  const [s,set] = useState();
  // 首次执行callback后,建立回调中state的订阅发布关系
  useEffect(() => set(callback()));
  return s;
}

AOT 与 JIT

所有现代前端框架都需要 “编译” 这一环节,而 “编译” 可以选择两个时机去执行:

  1. AOT(Ahead Of Time,提前编译或预编译),宿主环境获取编译后的代码
  2. JIT(Just In Time,即时编译),代码在宿主环境中编译并执行

在 Angular 中同时提供了这两种编译方案:

import {Component} from "@angular/core";

@Component({
  selector: "app-root",
  template: "<h3>{{getTitle()}}</h3>"
})

export class AppComponent {
  public getTitle() {
    return "Hello World";
  }
}

渲染结果:

<app-root>
  <h3>Hello World</h3>
</app-root>

如果将使用的 getTitle 方法换成一个未被定义的函数:

  1. AOT:立即报错
ERROR occurs in the template of component AppComponent.
  1. JIT:代码在编译时候不会报错,在浏览器运行的时候报错
ERROR TypeError: _co.getTitleXXX is not a function

除此之外还有这些区别:

  1. JIT 的应用在首次加载的时候慢于 AOT 的应用,因为其需要先编译代码;而使用 AOT 的应用已经在构建的时候编译完成,可以直接执行代码
  2. 使用 JIT 的应用体积可能大于使用 AOT 的应用,因为其在运行时会增加编译器代码

因此 Angular 一般在开发环境下使用 JIT,生产环境下使用 AOT

VirtualDOM(虚拟 DOM)

又称 VDOM 是目前根据自变量变化计算出 UI 变化的一种主流技术

Vue 中使用模版语法来描述 UI,模版语法编译为 render 函数:

  1. render函数执行后返回“VNode 描述的 UI”,这一步骤中 Vue 中被称为 render
  2. 将变化前后“VNode 描述的 UI”进行对比,计算出 UI 中变化的部分,这一部分在 Vue 中被称为 patch

React 使用 JSX 来描述 UI,JSX 编译为 createElement 方法:

  1. createElement 方法执行后返回“React Elemet 描述的 UI”
  2. “将 React Eleme 描述的 UI”与变化前“Fiber Node 描述的 UI”进行比较,计算出 UI 变化的部分,同时生成本次更新“Fiber Node 描述的 UI”

VDOM 的优点

  1. 相较于 DOM 的体积优势:除了“比较 UI 变化需要必要的属性”,DOM 还包含了大量冗余的属性,使用 VDOM 可以减少内存的开销
  2. 相较于 AOT 更强大的描述能力:比如要在“使用模版语法的前端框架”中实现 Reactchildren 属性,需要实现 Slot Component,对 children 的简单操作也变成了对 Slot Component 的繁琐操作
  3. 多平台渲染能力:根据 UI=f(state)UI=f(state)中 f 的工作原理可以概括为
    1. 根据自变量变化计算出 UI 变化
    2. 根据 UI 变化执行具体的宿主 API

因此在浏览器,Node 宿主环境下使用 ReactDOM 包,在 Native 宿主环境下使用 ReactNative 包,在 CanvasSVG 宿主环境下使用 ReactArt 包。

React 实现原理

React 被称为应用级框架的原因在于--其每次更新流程都是从应用的根结点开始,遍历整个应用。同理,Vue 起始于组件,Svelte 起始于元素。


使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...