IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 极简hooks实现,与react的思路一样的useState -> 正文阅读

[JavaScript知识库]极简hooks实现,与react的思路一样的useState

使用App函数模拟根组件

 function App() {
        const [num, updateNum] = useState(0);

        console.log(`${isMount ? "mount" : "update"} num: `, num);
        button.textContent = num;
        return {
          click() {
            updateNum((num) => num + 1);
            updateNum((num) => num + 1);
          },
        };
      }

使用app().click模拟点击事件。

全局变量

      let workInProgressHook; //指向当前的hooks对象
      let isMount = true;  //判断当前是mount还是update。
 

App对应的fiber

 // 状态保存在函数组件的fiber上
      // App组件对应的fiber对象
      const fiber = {
        // 保存该FunctionComponent对应的Hooks链表
        memoizedState: null,
        // 指向App函数
        stateNode: App,
      };

useState函数

每个hooks会创建一个hooks对象,然后存放在fiber的memoizedState上形成链表。workInProgressHook就指向当前的hooks对象。

function useState(initialState) {
        //每个hook对应一个hooks对象
        let hook;
        if (isMount) {
          // ...mount时需要生成hook对象
          hook = {
            queue: {
              pending: null,
            },
            memoizedState: initialState, //当前hooks对应的状态
            next: null, //指向下一个hooks对象
          };
          //判断fiber上的链表
          if (!fiber.memoizedState) {
            fiber.memoizedState = hook;
          } else {
            workInProgressHook.next = hook; //连接到fiber的hooks链表上
          }
          workInProgressHook = hook; //workInProgressHook指向最新的hook对象
        } else {
          // update的时候,从workInProgressHook中取出该useState对应的hook
          hook = workInProgressHook;
          // 指针往下移动
          workInProgressHook = workInProgressHook.next;
        }

        let baseState = hook.memoizedState;
        let newState = baseState;
        if (hook.queue.pending) {
          // ...根据queue.pending中保存的update更新state

          // 获取第一个update对象。pending存放着update对象的环状链表,指向的永远是最后一个update
          let firstUpdate = hook.queue.pending.next;
          do {
            //执行update action
            const action = firstUpdate.action; // 假设所有的setState(()=>{})都传递了一个函数
            newState = action(baseState); //每次传入的baseState都是老值
            firstUpdate = firstUpdate.next;
            //最后一个update执行完毕后跳出循环。
          } while (firstUpdate !== hook.queue.pending.next);

          // 清空queue.pending
          hook.queue.pending = null;

          // 将更新后的state重新赋值hooks对象上。
          hook.memoizedState = newState || baseState;
        }

        // dispatchAction的第一个参数就是当前hooks对象的queue
        return [newState, dispatchAction.bind(null, hook.queue)];
      }

如上,setState是dispatchAction.bind(null, hook.queue),他的实现是:
每一个任务更新都会创建一个update对象,他与hooks对象可不同,update对象会通过next指针形成链表,存放在hooks对象queue.pending中,表示需要更新的操作。

 // 调用updateNum实际上调用这个,queue就是当前hooks对应的queue。
      function dispatchAction(queue, action) {
        // 每一个任务对应一个update
        const update = {
          // 更新执行的函数
          action,
          // 与同一个Hook的其他更新形成链表
          next: null,
        };

        // 环状单向链表操作
        if (queue.pending === null) {
          update.next = update;
          // 连向自己
        } else {
          // 形成环状
          update.next = queue.pending.next;
          queue.pending.next = update;
        }
        // 将更新的任务保存在hooks上的queue,下次组件执行的时候获取然后更改state
        queue.pending = update; //永远指向最新的update

        // 模拟React开始调度更新
        schedule();
      }

最后每次调用完setState,都会进入schedule调度阶段。

   // 模拟react调度,默认执行一次
      function schedule() {
        workInProgressHook = fiber.memoizedState;
        const app = fiber.stateNode(); //执行App函数
        isMount = false;
        return app;
      }

每次调度都会重新执行App函数,然后App函数就会执行useState。然后取出存放在hooks.queue中需要更新的action,执行完后获取最新的state并且返回。
在这里插入图片描述

模拟试试
?
在这里插入图片描述
在这里插入图片描述
至此,这就是整个hooks的执行过程。

总结:

每个组件对应着一个fiber节点,每个hook对应一个hooks对象,hooks对象存放在fiiber节点上,而每次执行更新函数,会创建一个update,他通过next指针形成链表,存放在hooks对象上的queue.pending上,是一个环状链表,然后执行调度。调度会重新执行函数组件,然后重新执行useState函数,他会获取存放在hooks对象上的queue上的update链表,然后取出之后执行,获取新的状态state返回。
函数组件的状态存放在hooks对象上的memoizedState上,更新的action存放在hooks对象上queue的pending上,而hooks对象存放在fiber节点上。

与React的区别
        我们用尽可能少的代码模拟了Hooks的运行,但是相比React Hooks,他还有很多不足。以下是他与React Hooks的区别:

        React Hooks没有使用isMount变量,而是在不同时机使用不同的dispatcher。换言之,mount时的useState与update时的useState不是同一个函数。

        React Hooks有中途跳过更新的优化手段。

        React Hooks有batchedUpdates,当在click中触发三次updateNum,精简React会触发三次更新,而React只会触发一次。

        React Hooks的update有优先级概念,可以跳过不高优先的update。

        更多的细节,我们会在本章后续小节讲解。

学习文章出自:https://react.iamkasong.com/hooks/create.html#%E5%9C%A8%E7%BA%BFdemo

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-04 10:57:06  更:2022-02-04 10:57:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:29:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码