React设计原理—1框架原理

阅读前须知

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

前端框架原理概览

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等

前端框架总结

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 学习 React 框架原理的一个好方法是从官方文档开始。React 官方网站提供了一些教程和文档,可以帮助您了解 React 的基础概念和用法。 首先,您可以阅读 React 的官方教程,了解 React 如何工作,包括它的基本概念、组件、状态和生命周期等。这将有助于您熟悉 React 的基础用法。 其次,您可以学习 React 的官方文档,深入了解 React 的各种功能和用法。React 官方文档中包含了大量的详细信息,可以帮助您了解 React 的详细原理。 此外,您还可以通过实践来加深对 React 的理解。您可以尝试使用 React 构建一些小型应用或者尝试在现有应用中使用 React。通过不断实践和探究,您可以进一步加深对 React 的理解。 另外,您还可以参加一些在线课程或者报名参加 React 相关的培训,从专业人士那里学习 React 的知识和技巧。这些方式可以帮助您更快地掌握 React,并且能够得到专业的指导和帮助。 ### 回答2: 学习React这门框架原理可以按照以下步骤进行: 1. 学习JavaScript基础:React是基于JavaScript语言的,因此首先需要熟悉JavaScript的基本语法、函数、对象等概念。了解JavaScript的基础知识会有助于理解React的工作原理。 2. 学习React的核心概念:React的核心概念包括组件、虚拟DOM、状态和属性等。通过阅读React官方文档或相关教程,在编写简单的React组件时要了解这些概念的作用和用法。 3. 掌握React的生命周期:React组件具有生命周期,包括组件的创建、更新和销毁等阶段。掌握React的生命周期方法,比如`componentDidMount`、`componentDidUpdate`等,可以更好地理解组件是如何工作的。 4. 理解React的虚拟DOM:React通过虚拟DOM来实现高效的页面更新。了解虚拟DOM的概念和原理,以及与真实DOM的区别,对于优化性能和理解React工作原理很有帮助。 5. 阅读React源码:阅读React源码是学习其原理的最佳途径之一。通过查看React的源码,可以深入理解其内部实现机制,例如组件渲染、事件处理、状态更新等。 6. 实践项目:通过实践项目来应用和巩固学习的知识。可以尝试创建简单的React应用、组件库或其他前端项目,通过实践来加深对React原理的理解。 7. 参与社区:加入React社区,与其他开发者交流并学习。可以参与讨论、提问问题,获得更多关于React原理方面的经验和见解。 在学习React原理过程中,需要保持持续的学习和实践,并且不断关注最新的React版本和更新,以保持与时俱进。 ### 回答3: 学习React框架原理主要分为以下几个步骤: 1. 掌握基础知识:学习React的基础概念,包括组件、虚拟DOM、状态和属性等。理解React的思想和设计模式,学习React相关的JavaScript语法和ES6+特性。 2. 实践项目:选择一个小型的React项目,通过实际的编码过程来理解React的使用和原理。可以先从快速搭建React开发环境开始,然后逐步添加组件、处理事件和状态等。通过项目实践,深入理解React的工作原理。 3. 深入研究源码:阅读React的源代码是学习React原理的重要一环。可以从React的官方文档中找到源码地址,阅读和分析React的核心模块。重点关注虚拟DOM的实现以及组件的生命周期等。可以通过调试源码、查阅相关资料和参考优秀的开源实现等方式来加强理解。 4. 学习相关技术栈:React通常会和其他技术栈一起使用,如Webpack、Babel、Redux等。学习这些相关的技术可以更好地理解React原理及其与其他技术的结合。可以参考相关文档、教程和实践项目来学习这些技术。 5. 参与社区和交流:积极参与React的相关社区和交流平台,如官方论坛、GitHub等,与其他开发者交流、分享经验和学习资源。参与开源项目,审阅他人的代码,给出自己的建议和改进。通过与社区的互动,不断提升自己的技术水平。 总之,学习React原理需要结合理论和实践,通过实际的项目和阅读源码来加深对其原理的理解。同时,学习相关技术栈和积极参与社区交流也是提高React技术水平的重要途径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值