React设计原理—1框架原理

news2024/11/27 2:20:51

阅读前须知

  • 本文是笔者学习卡颂的《React设计原理》的读书笔记,对书中有价值内容以Q&A方式进行呈现,同时结合了自己的理解🤔
  • 阅读时推荐先看问题,想想自己的答案,再和答案比对一下
  • 本文属于前端框架科普,读完快速对前端框架有个概览👍

文章目录

  • 前端框架原理概览
      • 1 react是库还是前端框架?框架和库有啥区别?
      • 2 如何理解前端框架的原理`UI = f(state)`
      • 3 如何同时描述UI和逻辑
      • 4 组件是存放UI和逻辑的松耦合单元,那么组件如何组织UI和逻辑
      • 5 数据如何在组件间传递
      • 6 前端框架分类
      • 7 如何理解reduce和reducer
      • 8 useReducer如何使用,和useState有什么关系区别,如何利用useReducer实现一个useState
      • 9 react hooks中的自变量和因变量
  • 前端框架使用的主流技术
      • 1 useState如何实现state改变时,useEffect重新执行
      • 2 useState如何实现state数值改变之后如何触发UI的更新
      • 3 AOT和JIT有什么区别
      • 4 利用AOT的框架(多适用于模板语法描述UI的框架)solid svelte
      • 5 VDOM框架(jsx描述UI的框架+模板语法描述UI的框架)blockdom inferno
      • 6 前端框架实现原理 🌟
        • 6.1 元素级框架 svelte(AOT)
        • 6.2 Vue3原理(组件级框架)
        • 6.3 React原理(应用级框架)
  • 前端框架总结

前端框架原理概览

1 react是库还是前端框架?框架和库有啥区别?

react本身是构建UI的库,将其称为框架是约定俗称的说法,其实不是框架
在这里插入图片描述

2 如何理解前端框架的原理UI = f(state)

框架f就是一个函数,自变量state是当前数据,因变量是宿主环境的视图
● state: 当前数据
● f:框架内部运行机制
○ 根据自变量的变化计算出UI的变化
○ 根据UI变化执行宿主环境的API
● UI:宿主环境的视图

3 如何同时描述UI和逻辑

在这里插入图片描述

4 组件是存放UI和逻辑的松耦合单元,那么组件如何组织UI和逻辑

state数据:自变量
f函数:是逻辑
UI:因变量
y=f(x) 自变量x的变化,可能会导致依赖x的因变量y的变化

const App = () => {
  const [count, setCount] = useState(0);
  
  // 自变量count改变导致因变量y1改变(纯函数)
  const y1 = useMemo(()=>count*2,[count]);

  // 有副作用
  useEffect(()=>{
    document.title="new title";
    console.log("副作用");
  },[])

  
}

在这里插入图片描述

5 数据如何在组件间传递

在这里插入图片描述

当前组件的自变量或者因变量借助于UI传递给子组件,作为其自变量
子组件自身的自变量称为state
其他组件传递的自变量称为props
在这里插入图片描述

6 前端框架分类

在这里插入图片描述

7 如何理解reduce和reducer

reduce:函数式编程当中的一个术语,reduce操作被称为Fold折叠

// 数组中的reduce函数,第一个参数通常被称为reducer
const sum = [0, 1, 2].reduce((prev, item) => {
  return prev + item;
}, 0)

拿JavaScript来理解。reduce属于一种高阶函数,它将其中的回调函数reducer递归应用到数组的所有元素上并返回一个独立的值。这也就是“缩减”或“折叠”的意义所在了。

reducer:(state, action) => newState
redux中的reducer函数是因为它的入参和返回值都非常类似于arr的reduce中传入的回调函数
// reducer接收两个参数: state=[], action 
// reducer返回值是一个新的state
const todoReducer = (state=[], action) => {
	switch(action){
    case "ADD":
      return [...state, {id: 111}];
    default:
      return state;
  }
}

8 useReducer如何使用,和useState有什么关系区别,如何利用useReducer实现一个useState

● useRedcuer是useState的替换方案,和useState相比,它更适合state逻辑复杂,或者state是个对象,包含多个子值,或者下一个state依赖于之前的stated的情况。相当于收敛逻辑于reducer函数中进行管理
● useReducer使用

const reducer = (state, action) => {
  switch (action) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
};

function Reducer() {
  const [num, dispatch] = useReducer(reducer, 1);

  return (
    <div>

      <p>
        useStateByReducer :
        <button
          onClick={() => {
            // 区分dispatch函数和reducer函数
            dispatch('DECREMENT');
          }}
        >
          -
        </button>
        {num}
        <button
          onClick={() => {
            dispatch('INCREMENT');
          }}
        >
          +
        </button>
      </p>
    </div>
  );
}
export default Reducer;

● 用useReducer实现一个useState

import { useReducer, useRef } from 'react';

const isFunction = (fn) => {
  return Object.prototype.toString.call(fn) === '[object Function]';
};

export default (initialState) => {
  const reducer = (state, action) => {
    return isFunction(action) ? action(state) : action;
  };
  return useReducer(reducer, initialState);
};

9 react hooks中的自变量和因变量

在这里插入图片描述

前端框架使用的主流技术

1 useState如何实现state改变时,useEffect重新执行

细粒度更新:依赖追踪技术如何实现的
实现useState的state和useEffect的双向绑定
在这里插入图片描述

function useState(value?: any) {
  /**
   * 问题:state绑定的不是回调函数,而是数据结构
   * 解法:创建一个数据结构:effect={execute: () => {}, deps: [subs1, subs2],}
   */
  const subs: Set<Effect> = new Set();

  const getter = () => {
    // 问题:如何实现隐式绑定,如何知道执行getter时,所对应的effect
    // 解法:执行useEffect前,将effect加入堆栈,getter函数获取当前处于栈顶部的effect,即为当前执行的上下文,执行完之后,移出堆栈
    if (effectStack.length > 0) {
      const curEffect = effectStack[effectStack.length - 1];
      subscribe(subs, curEffect);
    }
    return value;
  };
  const setter = (newValue?) => {
    value = newValue;

    // emit
    for (const sub of [...subs]) {
      console.log('setter emit subs');
      // debugger
      sub.execute();
    }
  };

  return [getter, setter];
}

**
 * 创建一个的useEffect
 * 1 不需要显示指定依赖
 * 2 useEffect执行后,回调函数自动执行
 * 3 依赖变化时,useEffect自动执行
 */
function useEffect(fn) {
  const execute = () => {
    // 每次执行,都会先删除依赖
    cleanup(effect);
    effectStack.push(effect);

    // 首次会执行一次回调,来触发“自动依赖收集”
    try {
      fn();
    } finally {
      effectStack.pop();
    }
  };

  const effect = {
    execute,
    deps: new Set<Set<Effect>>(), // III:使用set来存发布订阅的数据结构,而不是list
  };

  execute();
}

/**
 * 双向删除订阅关系
 * @param effect
 */
function cleanup(effect: Effect) {
  // console.log('cleanup', effect);
  // 找到useState的subs,清理该effect(在别人那里,清除自己的痕迹)
  for (const subs of [...effect.deps]) {
    subs.delete(effect);
  }
  // 清理effect的deps
  effect.deps.clear();
}

/**
 * 双向订阅
 * @param subs
 * @param effect
 */
function subscribe(subs, effect) {
  subs.add(effect);

  effect.deps.add(subs);
}
useMemo是useEffect包了一层
function useMemo(fn) {
  const [state, setState] = useState();
  useEffect(() => setState(fn()));

  return state();
}

2 useState如何实现state数值改变之后如何触发UI的更新

在编译阶段:AOT
在运行阶段VDOM:

3 AOT和JIT有什么区别

在这里插入图片描述

4 利用AOT的框架(多适用于模板语法描述UI的框架)solid svelte

用模板语法描述UI的框架可以从AOT中受益,因为模板语法中“静态”部分可以和“动态”部分很轻易的分离
从而减少框架在“自变量变化计算出UI变化”这部分工作量

5 VDOM框架(jsx描述UI的框架+模板语法描述UI的框架)blockdom inferno

VDOM是什么:虚拟DOM也是为了描述UI,为了实现“自变量变化计算出UI变化”
VDOM什么用:将元素描述的UI改成VDOM描述的UI,通过比较VDOM的变化,来计算UI的变化

在这里插入图片描述

VDOM好处
● 真实DOM元素冗余属性较多,没必要全部进行比较,减少内存开销
● 描述能力强大??
● 可以多平台渲染

6 前端框架实现原理 🌟

6.1 元素级框架 svelte(AOT)

Q: 如何建立自变量和UI元素的对应关系
编译器编译时绑定update方法和入参
Svelte编译器在编译<App/>时,直接追踪<script>标签中所有变量声明
一旦涉及count++/count=1等变量赋值的语句,该变量就会被提取到instance函数中,instance函数返回值作为组件的入参ctx

Q: 自变量变化时,如何更新UI元素
更新流程开始于元素,编译时已经绑定了具体元素
dirty可以指定自变量和元素的变化关系,set_data可以改dom
自变量变化时,标记dirty,调度更新fragment执行p方法,p方法内的if语句直接和dirty标记对应,执行set_data(), set_data函数会执行具体dom操作

编译时绑定UI和state的关系,触发变化时,标记dirty,更新入参,执行updateUI函数
等代码预先编译为create_fragment函数,创建dom,挂载,更新等
将state作为组件create_fragment的入参
在这里插入图片描述

在这里插入图片描述

function create_fragment(ctx) {
	let h1;
	let t;
	let dispose;

	return {
		c() {
			h1 = element("h1");
			t = text(/*count*/ ctx[0]);
		},
		m(target, anchor) {
			insert(target, h1, anchor);
			append(h1, t);
      // 在mount时候,绑定dom元素事件,回调函数
			dispose = listen(h1, "click", /*update1*/ ctx[1]);
		},
		p(ctx, [dirty]) {
      // count变,元素h1的t变,建立了自变量和元素的对应关系
			if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
		},
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(h1);
			dispose();
		}
	};
}

// ctx 执行上下文 包括state和改变state的行为以及回调函数
function instance($$self, $$props, $$invalidate) {
  let count = 0;

  function update1() {
    // 触发ctx更新,dirty标记
    $$invalidate(0, count++, count);
  }

  setTimeout(
    () => {
      $$invalidate(0, count++, count);
    },
    1000
  );

	return [count, update1];
}

6.2 Vue3原理(组件级框架)

● 如何建立自变量和UI组件的对应关系
○ 细粒度更新
○ 每个组件都有一个watchEffect(类似useEffect),实现state变化时候,自动执行watchEffect中回调函数
● 自变量变化时,如何更新元素(更新流程开始于组件
○ 自变量变化,执行render函数,生成当前组件的VNode,和上一次生成的prevNode一起作为patch函数的入参,返回值为UI中变化的元素
watchEffect的回调函数中包括:render,patch一条龙

在这里插入图片描述

AOT如何帮助Vue3

<div>
  <h1>hello</h1>
  <p>{{name}}</p>
</div>

AOT后(识别模板代码,标记可能发生变化的量)

const render = (_ctx, cache) => {
  return(_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("h1", null, "helllo"),
    // patchFlag 为1 表示变化的元素,为text, 2表示class
    _createElementVNode("p", null, _ctx.name, 1 /* TEXT*/)
  ]))
}
// 生成的VNode大致表示成
const vnode = {
  tag: "div",
  children: [
    {tag: "h1", children: "hello"},
    {tag: "p", children: ctx.name, patchFlag: 1}
  ],
  dynamicChildren:[
    {tag: "p", children: ctx.name, patchFlag: 1} 
  ]
}

之后执行patch时,只需要比对dynamicChildren即可

6.3 React原理(应用级框架)

● 如何建立自变量和UI元素的对应关系
○ 不需要建立
● 自变量变化时,如何更新元素
○ 只要自变量改变,就从根节点开始,重新遍历应用,找到diff,执行dom
在这里插入图片描述

● react每次自变量变化都会从根节点遍历应用,会不会性能差?
不会
○ 内部有优化机制
○ 提供一些优化api,减少不必要的遍历: shouldComponentUpdate,memo,PureComponent等

前端框架总结

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/336133.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

68. Python的相对路径

68. Python的相对路径 文章目录68. Python的相对路径1. 知识回顾2. 什么是相对路径3. 相对路径的语法4. 查看相对路径的方法5. 写出所有txt文件的相对路径5.1 同目录5.2 上级目录6. 用相对路径读取txt文件6.1 读取旅游.txt6.2 读取旅游经费.txt6.3 读取笔记.txt和new.txt6.4 读…

微服务调用组件Feign

目录 JAVA 项目中如何实现接口调用&#xff1f; Httpclient Okhttp HttpURLConnection RestTemplate WebClient 什么是Feign 优势 Spring Cloud Alibaba快速整合OpenFeign 引入依赖 编写调用接口FeignClient注解 调用端在启动类上添加EnableFeignClients注解 发起调…

【送书活动】学Vue核心技术和uni-app跨平台实战项目就来看看这本书

本节目录1、书籍介绍2、推荐理由2.1 有充足的配套资源图书页内展示。2.2 PPT示例代码讲解演示2.3 内容由浅入深&#xff0c;渐进式学习3、参与方式1、书籍介绍 本书共分为14个章节&#xff0c;包括Vue.js核心基础、Vue.js高级进阶、Axios发送HTTP请求&#xff0c;Vuex状态管理…

牛客 面试必刷TOP101 题解(3、二叉树)

23 二叉树的前序遍历 /*** struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* };*/ #include <vector> class Solution { public:vector<int> ans;void show(TreeNode…

腾讯架构师极力推荐:Java多线程与Socket实战微服务框架

在这个技术不断更新的年代&#xff0c;跟不上时代变化的速度就会被刷掉&#xff0c;特别是咱们程序员这一群体&#xff0c;技术不断更新的同时也要同时进步&#xff0c;不然长江后浪推前浪&#xff0c;前浪… 一个程序员从一个什么都不懂的小白在学到有一定的Java基础的时候肯…

WebDAV之葫芦儿·派盘+可达漫画

可达漫画 支持WebDAV方式连接葫芦儿派盘。 这是一款专为阅读你的漫画收藏而设计的阅读器。 热爱漫画的你肯定收藏了不少各种类型的漫画,它们可能有各种各样的格式,zip,rar,cbz,cbr,epub, mobi 或 pdf,也可能只是单纯的文件夹。 可达漫画支持「流式阅读」

数据推荐 | 手势识别训练数据集

多样化&#xff0c;有按键、语音等&#xff0c;也由于整个疫情的大环境下&#xff0c;一种更方便更卫生更符合人们的非接触式交互方式—手势识别正大步发展&#xff0c;极富图像化和具备行动性的手势操作将会与人们的生活息息相关。 手势识别应用场景广泛&#xff0c;常见应用…

【微电网】并网微电网运行经济性研究(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

面试官问我:说说你对JMM内存模型的理解?为什么需要JMM?

点个关注&#xff0c;必回关 随着CPU和内存的发展速度差异的问题&#xff0c;导致CPU的速度远快于内存&#xff0c;所以现在的CPU加入了高速 缓存&#xff0c;高速缓存一般可以分为L1、L2、L3三级缓存。基于上面的例子我们知道了这导致了缓存一致 性的问题&#xff0c;所以加入…

字符串常量池

1.创建对象的思考下面两种创建字符串的方式一样吗&#xff1f;public static void main(String[] args) {//两者一样吗String s1 "hello";String s2 "hello";String s3 new String("hello");String s4 new String("hello");System…

11 Advanced CNN

文章目录GoogLeNetInception Module1x1 Conv计算效果代码实现总结ResNet (残差网络)问题引入梯度消失与传统神经网络的比较代码实现课程来源&#xff1a; 链接对于前篇中所提到问题&#xff0c;设计出的是一种类似于LeNet5的线性结构&#xff0c;而对于大多数问题&#xff0c;简…

postgresql 数据库小版本更新

postgresql 数据库小版本更新 记录一次pg小版本更新 文章目录postgresql 数据库小版本更新前言一.下载最新的pg版本 pg11&#xff08;11.18&#xff09;二 pg11.5 升级 pg11.18过程1. 查看当前数据库版本&#xff1a;2. 关闭pg11.5数据库3. 安装pg11.184.修改 配置文件5. 重新…

【yolov5】yolov5训练自己的数据集全流程----包含本人设计的快速数据处理脚本

关于yolo应用时能用到的脚本集合&#xff0c;推荐收藏&#xff1a; https://chenlinwei.blog.csdn.net/article/details/127299428 1. 工程化快速yolo训练流程指定版&#xff08;无讲解&#xff09; 1.1 抽样数据集xml转txt输出量化分析 python make_dataset.pymake_dataset…

Little Fighter:旺角——NFT 系列来袭!

《小朋友齐打交 2 (LF2) 》是一款流行的格斗游戏&#xff0c;由 Marti Wong 和 Starsky Wong 于 1999 年创作。这是一款非常容易上瘾的游戏&#xff0c;具有多种游戏模式、横向卷轴格斗系统以及 24 个具有复杂动作和连击的不同角色。这款游戏在世界范围内非常受欢迎&#xff0c…

Debain安装命令

目录 一、安装sudo命令 二、安装jdk8 三、更换软件源 四、Debian 安装 yum 五、安装zip、unzip、curl、lrzsz、NUMA 六、安装Maven 五、问题 一、安装sudo命令 1&#xff09;执行sudo命令&#xff0c;提示 -bash: sudo: command not found的解决方法 apt-get install s…

图像分割方法与问题思考

前言图像分割就是预测图像中每一个像素所属的类别或者物体。图像分割有两个子问题&#xff0c;一个是只预测类别层面的分割&#xff0c;对每个像素标出一个位置。第二个是区分不同物体的个体。应用场景&#xff0c;比如自动驾驶&#xff0c;3D 地图重建&#xff0c;美化图片&am…

Docker-compose 容器集群的快速编排

目录 一、Docker-compose 简介 二、compose 部署 2.1、Docker Compose 环境安装 三、YAML 文件格式及编写注意事项 四、Docker Compose配置常用字段 五、Docker compose 常用命令 六、案例&#xff1a;编写Docker Compose搭建lnmp论坛 6.1、配置nginx 6.2、配置mysql …

Linux查看JVM FULL GC频率

查看系统的full gc频率&#xff0c;可以使用jstack命令一、采用top命令定位进程登录服务器&#xff0c;执行top命令&#xff0c;查看CPU占用情况&#xff0c;找到进程的pid二、使用jstack命令统计垃圾回收jstat -gc pid 5000即会每5秒一次显示进程号为pid的java进程的GC情况以上…

PyTorch学习笔记:nn.ReLU——ReLU激活函数

PyTorch学习笔记&#xff1a;nn.ReLU——ReLU激活函数 torch.nn.ReLU(inplaceFalse)功能&#xff1a;逐元素应用ReLU函数对数据进行激活 函数方程&#xff1a; ReLU(x)(x)max⁡(0,x)ReLU(x)(x)^\max(0,x) ReLU(x)(x)max(0,x) 输入&#xff1a; inplace&#xff1a;是否改变输…

C++004-C++选择与分支1

文章目录C004-C选择与分支1条件表达式条件表达式条件表达式返回值条件语句C实现单if语句题目描述 判断奇数还是偶数题目描述 判断是否及格题目描述 判断是否为0if else语句连续if与if else的差别题目描述 输出绝对值逻辑运算符题目描述 判断程序的输出题目描述 真值表判断题目描…