从头写React-like框架的工程搭建实现

最近在网上看到了 Build your own React 这篇文章,作者从零开始实现了一个简易类 React 框架,虽然没有过多的优化,但 React 中的核心思想 Concurrent Mode,Fiber Reconciler 等都有实现,看完后对理解 React 有很大帮助,因此我想在 Build your own React 的基础上,对代码进行拆分,搭建起自己的框架工程,然后完善教程中没完成的其他功能,代码在 rac 中。

工程搭建

技术栈上我选择用 TypeScript 开发,Rollup 打包, 都是平时用的不多的技术,顺带一起练练手,而且相比 webpack, rollup 配置更简单一些。在工程中创建一个 tsconfig.json 和一个 rollup.config.js, 然后安装一下需要的 rollup 插件,比如 rollup-plugin-typescript2, rollup-plugin-terser。另外准备一个 examples 文件夹,创建一个小型的 demo 工程,使用 tsx 开发

支持 jsx

如果想让 TypeScript 支持 jsx,需要在 tsconfig 中开启 jsx TypeScript 自带了三种模式: preserve, react,和 react-native,我们设置为 react, TypeScript 就会将代码中的 jsx 翻译成 React.createElement,这也是在使用 jsx 时,React 必须要在作用域中的原因。

但是我们要自己实现一个 React-like 框架,完全可以给 React.createElement 换个名字。在 Build your own React 中,作者通过 /** @jsx Didact.createElement */ 注释,告诉编译器将 jsx 的输出函数改为 Didact.createElement,这个方法只对当前文件生效,如果是在工程中使用为每个文件都加一行注释就麻烦了。我们通过另一种办法,在 tsconfig 中通过 jsxFactory 属性指定,我们这里叫 h,除了 React.createEmenent,还有个特殊元素 - Fragment,TypeScript 默认会翻译成 React.Fragment,我们通过 jsxFragmentFactory 直接改为 Fragment。

tsconfig.json:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "moduleResolution": "node",
    "jsx": "react", // enable jsx
    "jsxFactory": "h", // React.createElement => h
    "jsxFragmentFactory": "Fragment", // React.Fragment => Fragment
    "rootDir": "./src",
    "lib": ["dom", "es2015"]
  }
}

Rollup 配置

Rollup 的配置比较简单,除了 input,output,再额外加一些插件就可以了:

const path = require('path')
const typescript = require('rollup-plugin-typescript2')
const { terser } = require('rollup-plugin-terser')
const eslint = require('@rollup/plugin-eslint')

export default {
  input: 'src/index.ts',
  output: [
    { file: 'dist/rac.umd.js', format: 'umd', name: 'rac' }
  ],
  plugins: [
    terser(),
    eslint({
      throwOnError: true,
      include: ['src/**/*.ts']
    }),
    typescript({
      verbosity: 0,
      tsconfig: path.resolve(__dirname, 'tsconfig.json'),
      useTsconfigDeclarationDir: true
    })
  ]
}

Eslint in TypeScript

为了能让 Eslint 支持 TypeScript,需要给 Eslint 一些额外配置:

module.exports = {
  parser: '@typescript-eslint/parser',
  env: {
    es6: true,
    browser: true
  },
  plugins: [
    '@typescript-eslint'
  ],
  extends: [
    'eslint:recommended',
  ],
  parserOptions: {
    sourceType: 'module'
  },
  rules: {
    ...
  }
}

项目结构

React 新的 Fiber 架构有几个核心概念,在 Build your own React 中,作者依照

  • Step I: The createElement Function
  • Step II: The render Function
  • Step III: Concurrent Mode
  • Step IV: Fibers
  • Step V: Render and Commit Phases
  • Step VI: Reconciliation
  • Step VII: Function Components
  • Step VIII: Hooks

这几步逐步实现了一个 mini React,为了提高代码可读性和可维护性,会把这些功能划分到不同的文件中:

.
├── README.md
├── examples  // demo目录
├── package.json
├── rollup.config.js
├── src
│   ├── dom.ts
│   ├── h.ts
│   ├── hooks.ts
│   ├── index.ts
│   ├── reconciler.ts
│   ├── scheduler.ts
│   └── type.ts
└── tsconfig.json
  • dom.ts 中处理 DOM 相关工作
  • h.ts 中是对 jsxFactory, jsxFragmentFactory 的实现
  • hooks.ts 中是对 hooks 的实现
  • reconciler.ts 是 reconcile 阶段和 commit 阶段的实现
  • shceduler.ts 是任务调度器的实现
  • type.ts 是一些类型定义

到这工程就搭建起来了,整个工程的结构和一些代码实现上借鉴了 fre 这个框架。

到此这篇关于从头写React-like框架的工程搭建实现的文章就介绍到这了,更多相关React-like搭建内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React三大属性之Refs的使用详解

    refs是React中用来取得某个JSX组件或者某个DOM中的一些状态值的时候,用来获取节点的方法.在React官方的解释中,它的适用范围如下: 管理焦点,文本选择或媒体播放. 触发强制动画. 集成第三方 DOM 库. React文档中再三强调,请不要过度使用refs,所以当我们可以用dom原生对象解决时,尽量不要使用refs 依照之前的写法,首先是给出类组件和函数组件中refs的写法 类组件 在类中,refs有三种方式,目前最常用的是回调的形式使用,分别进行演示 //直接定义refs,已废弃

  • React使用高德地图的实现示例(react-amap)

    pc版React重构,使用到了高德地图.搜了资料,发现有一个针对React进行封装的地图插件react-amap.官方网址:https://elemefe.github.io/react-amap/components/map,有兴趣的可以看下里面的API. react-amap 安装 1.使用npm进行安装,目前是1.2.8版本: cnpm i react-amap 2.直接使用sdn方式引入 <script src="https://unpkg.com/react-amap@0.2.5

  • react项目如何运行在微信公众号

    说明:项目是create-react-app结合antd-mobile写的H5,在微信公众号里运行. 1.使用a标签预览或下载PDF.写法如下,手机上点击无反应,电脑上点击网页崩溃. <a href='pdf或图片路径'> PDF或图片名称 </a> 原因是浏览器监测到访问非安全访问,给拦截下来了.于是根据报错提示,新增target和rel两个属性,写法如下: <a href='pdf或图片路径' target='_blank' rel="noreferrer&qu

  • 如何用react优雅的书写CSS

    1.内联样式 优点:这种方式较为简单,一目了然,给标签添加style属性. 缺点: 这种方式可以造成项目结构较为臃肿,造成css命名冲突. import React, { Component } from 'react' import PropTypes from 'prop-types' export default class index extends Component { static propTypes = { title: PropTypes.string } render() {

  • react中的虚拟dom和diff算法详解

    虚拟DOM的作用 首先我们要知道虚拟dom的出现是为了解决什么问题的,他解决我们平时频繁的直接操作DOM效率低下的问题.那么为什么我们直接操作DOM效率会低下呢? 比如我们创建一个div,我们可以在控制台查看一下这个div上自带或者继承了很多属性,尤其是我们使用js操作DOM的时候,我们的DOM本身就很复杂,js的操作也会占用很多时间,但是我们控制不了DOM元素本身,因此虚拟DOM解决的是js操作DOM这一层面,其实解决的是减少了操作dom的次数 简单实现虚拟DOM 虚拟DOM,见名知意,就是假

  • React useEffect的理解与使用

    React16.8新增的useEffec这个hook函数就是处理副作用的. 所谓的"副作用",举个通俗一点的例子,假如感冒了本来吃点药就没事了,但是吃了药发现身体过敏了,而这个"过敏"就是副作用. 放到React中,本来只是想渲染DOM展示到页面上,但除了DOM之外还有数据,而这些数据必须从外部的数据源中获取,这个"获取外部数据源"的过程就是副作用. useEffect怎么用可以参考官网给出的例子,这里主要针对使用useEffect过程中遇到的问

  • react显示文件上传进度的示例

    Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 在使用react, vue框架的时候, 如果需要监听文件上传可以使用axios里的onUploadProgress. react上传文件显示进度 demo 前端 快速安装react应用 确保有node环境 npx create-react-app my-app //当前文件夹创建my-app文件 cd my-app //进入目录 npm install antd //安装antd UI组件 npm

  • 封装一个最简单ErrorBoundary组件处理react异常

    前言 从 React 16 开始,引入了 Error Boundaries 概念,它可以捕获它的子组件中产生的错误,记录错误日志,并展示降级内容,具体 官网地址 错误边界避免一个组件错误导致整个页面白屏不能使用等情况,使用优雅降级的方式呈现备用的 UI,错误边界可以在渲染期间.生命周期和整个组件树的构造函数中捕获错误.自 React 16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载 ErrorBoundary 意义 某些 UI 崩溃,不至于整个 webapp 崩溃 在浏

  • react如何用懒加载减少首屏加载时间

    最近在写一个react-ant-admin的集成框架用于快速搭载中后台项目.其中遇到很多问题,最重要的应该是访问速度了.我就想 react 可不可以和 vue 一样用路由懒加载来减少首页渲染所花费的时间. 于是找到了一个很好用的轮子:@loadable/component 使用 安装 npm install @loadable/component -D # or use yarn yarn add @loadable/component -D 如何在路由中使用? 在src/router/inde

  • React Fiber结构的创建步骤

    React Fiber的创建 当前React版本基于V17.0.2版本,本篇主要介绍fiber结构的创建. 一.开始之前 个人理解,如有不对,请指出. 首先需要配置好React的debugger开发环境,入口在这里:github 执行npm run i,安装依赖,npm start运行环境. 二.从React.render开始 通过在项目入口处调用React.render,打上Debug,查看React调用栈. const root = document.getElementById('root

随机推荐