服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服务器之家 - 编程语言 - JavaScript - React - 都 2022 年了,手动搭建 React 开发环境很难吗?

都 2022 年了,手动搭建 React 开发环境很难吗?

2022-04-19 21:37DYBOY小东同学 React

作为一名前端工程师,总是用一些脚手架来快速搭建新项目的基本结构,因此今天尝试着一步步搭建一个 React 的项目环境,看看需要处理哪些问题,查漏补缺!

都 2022 年了,手动搭建 React 开发环境很难吗?

工厂流水线生产的东西用久了,总想着自己手工是否也能做出来,就如同工艺品和艺术品一般,虽然效果相似,但艺术品往往比工艺品更有韵味。

作为一名前端工程师,总是用一些脚手架来快速搭建新项目的基本结构,因此今天尝试着一步步搭建一个 React 的项目环境,看看需要处理哪些问题,查漏补缺!

一、需求分析

首先分析我们的诉求:

  • 应用级别的项目,是需要支持打包构建。
  • 需要考虑兼容性,支持代码 pollyfill。
  • 支持 React 框架下的开发环境。
  • 支持代码类型提示。
  • 支持前端路由。
  • 支持前端状态管理。
  • 代码规范、自动格式化、Git 提交规范。
  • 基础的 UI 组件库。

针对上面的诉求,其实也是绝大部分项目都会需要,因此也有了常见的解决方案:

  • Webpack 5。
  • Babel。
  • React 17、React-dom。
  • TypeScript。
  • React-router-dom v6。
  • Redux、React-redux。
  • ESlint、Prettier、Lint-staged、Husky、@commitlint。
  • Arco Design。

二、项目打包构建

因为是 2022 年了,所以我们的项目所有依赖项全部用最新的工具库版本,搞起来!

首先是把项目的基本构建能力搭建好,让项目先跑起来!

1、 初始化 package.json

都 2022 年了,手动搭建 React 开发环境很难吗?

mkdir webpack-react
cd webpack-react
npm init --y git init

然后稍微改改 package.json 文件如下:

{ "name": "webpack-react", "private": true, "version": "0.1.0", "description": "一个基于 Webpack 构建的 React开发环境", "main": "index.js", "scripts": { "dev": "", "build": "", "preinstall": "npx only-allow yarn" }, "keywords": [], "author": "DYBOY", "license": "ISC" } 

由于没有安装一些三方库,所以该文件还比较“简陋”,所以接下来逐个安装模块,配置环境!

2、安装配置 React 和 Typescript

根据需求,我们先安装一些必要的模块。

首先是 React 的基本模块。

yarn add react react-dom
yarn add @types/react @types/react-dom

然后是 TypeScript 类型模块。

yarn add typescript -D

有了 TypeScript,就可以直接通过 TS 生成一个 tsconfig.json 的配置文件

yarn tsc --init

根据需要,稍微改改后如下:

// tsconfig.json { "compilerOptions": { "target": "ES2015", "lib": ["DOM", "ES2015"], "jsx": "react-jsx", "experimentalDecorators": true, "emitDecoratorMetadata": true, "module": "ESNext", "rootDir": "./src", "moduleResolution": "node", "baseUrl": ".", "paths": { "@/*": ["src/*"] }, "resolveJsonModule": true, "allowJs": true, "outDir": "./dist", "removeComments": true, "isolatedModules": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true }, "include": ["src/"] }

*关于 tsconfig.json 文件的配置解析可以参阅:《会写 TypeScript 但你真的会 TS 编译配置吗?[1]》。

此时可以创建文件和文件夹,有一个初步的项目结构。

都 2022 年了,手动搭建 React 开发环境很难吗?

都 2022 年了,手动搭建 React 开发环境很难吗?项目结构

其中:

  • dist/: 是用于存储打包的文件。
  • public/: 是用于存放打包的模板入口 HTML 文件。
  • src/: 是用于开发人员主要编码的文件夹。
  • .gitignore: 用于配置 Git 忽略哪些文件或文件夹。
  • tsconfig.json: TypeScript 的项目配置文件。
  • yarn.lock: 依赖模块的版本信息,用于保证开发环境一致性。

此时就可以简单的写支持 TS 和 React 的应用了。

3、 Webpack 相关

因为是一个项目,我们需要通过构建工具,帮助我们快速的实现打包,以及开发环境下的预览,因此第二步就是安装和配置 Webpack。

yarn add webpack webpack-cli webpack-dev-server webpack-merge -D

后两个模块分别是用于开启开发时的本地 HTTP 服务,和用于 Merge webpack 配置的工具函数。

(1) Webpack 配置文件结构

首先,先完善 package.json 中的 scripts(开发指令和构建指令):

+ "dev": "cross-env NODE_ENV=development webpack serve -c scripts/webpack.dev.js", + "build": "yarn ts:checker && cross-env NODE_ENV=production webpack -c scripts/webpack.prod.js", + "ts:checker": "tsc --noEmit",

同时安装一下 cross-env,该模块主要是用于支持在不同的操作系统下保证环境变量正确。

yarn add cross-env -D

通过指令,我们需要三个 Webpack 的配置文件:

都 2022 年了,手动搭建 React 开发环境很难吗?

(2) webpack.common.js 通用配置

这是公共的 Webpack 配置,主要配置了如下几个地方。

都 2022 年了,手动搭建 React 开发环境很难吗?

const path = require("path"); const chalk = require("chalk"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const ProgressBarPlugin = require("progress-bar-webpack-plugin"); const pkgJSON = require("../package.json"); console.log("process.env.NODE_ENV: ", process.env.NODE_ENV); module.exports = { entry: path.resolve(__dirname, "../src/index.tsx", output: { filename: "[name].[hash:8].js", path: path.resolve(__dirname, "../dist"), publicPath: "/", clean: true, }, resolve: { extensions: [".ts", ".tsx", ".js"], alias: { "@": path.resolve(__dirname, "../src"), }, }, module: { rules: [ { test: /\.tsx?$/, use: ["ts-loader"], exclude: /node_modules/, }, { test: /\.(jpe?g|png|svg|gif)$/i, type: "asset", parser: { dataUrlCondition: { maxSize: 25 * 1024, // 25kb }, }, generator: { filename: "assets/imgs/[name].[hash:8][ext]", }, }, ], }, plugins: [ new webpack.DefinePlugin({ // 定义在代码中可以替换的一些常量 
      __DEV__: process.env.NODE_ENV === "development", }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, "../public/index.html"), title: pkgJSON.name, meta: { description: { type: "description", content: pkgJSON.description, }, }, minify: "auto", }), new ProgressBarPlugin({ format: ` :msg [:bar] ${chalk.green.bold(":percent")} (:elapsed s)`, }), ], };

个人有一个观点,开发环境和构建环境应该在配置上相似性需要寻找平衡,开发环境寻求的是热更新快,构建环境寻求的是兼容性好,且尽可能和开发环境看到效果相同!

针对缺失的模块还需要安装到开发依赖中:

# 支持 ts 和 tsx 文件的处理
yarn add ts-loader -D
# 美化终端输出,安装特定版本是为了处理模块化包的问题
yarn add chalk@4.1.2 -D
# 将 /public/index.html 作为模板入口文件打包
yarn add html-webpack-plugin -D
# 美化 webpack 编译时候的进度条
yarn add progress-bar-webpack-plugin -D

(3) webpack.dev.js 开发配置

然后再配置下开发环境下的 Webpack 配置,主要是支持热更新、本地预览功能,以及一些和生产环境差异的配置。

const { merge } = require("webpack-merge"); const common = require("./webpack.common.js"); module.exports = merge(common, { mode: "development", // 开发模式
  devServer: { hot: true, // 热更新
    open: true, // 编译完自动打开浏览器
    compress: false, // 关闭gzip压缩
    port: 7878, // 开启端口号
    historyApiFallback: true, // 支持 history 路由重定向到 index.html 文件 }, module: { // 插件的执行顺序从右到左
    rules: [ { test: /\.(css|scss|sass)$/, use: [ "style-loader", "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [["autoprefixer"]], }, }, }, "sass-loader", ], // 排除 node_modules 目录
        exclude: /node_modules/, }, ], }, stats: "errors-only", // Webpack 在编译的时候只输出错误日志,终端更清爽 }); 

这里增加了对 scss/css 文件的处理,因此还需要安装相关的模块:

# style-loader 将 css 注入到 HTML 的内联样式
# css-loader 用于加载 CSS 文件,转化 CSS 为 CommonJS
yarn add style-loader css-loader -D
# postcss 用于处理 CSS 兼容性
# autoprefixer 用于自动根据兼容需求增加 CSS 属性的前缀
yarn add postcss postcss-loader autoprefixer -D
# sass 主要是用于支持 “CSS 编程”
# sass-loader 会将 .scss 后缀文件编译成 CSS
yarn add sass sass-loader -D

讲到了 CSS 自动前缀处理兼容性,因此可以将需要兼容浏览器版本的配置放到 package.json -> browserslist 属性下:

{ ... "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "defaults", "not ie < 11", "last 2 versions", "> 1%", "iOS 9", "last 3 iOS versions" ] } ... }

(4) webpack.prod.js 生产配置

针对 Webpack 的构建环境下(mode: "production")的配置,实际上在 Webpack 5 版本中默认就集成了很多优化,更多自定义诉求可以参考:Webpack Optimization[2] 配置。

const { merge } = require("webpack-merge"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const common = require("./webpack.common.js"); module.exports = merge(common, { mode: "production", optimization: { minimize: true, minimizer: [ "...", new TerserPlugin({ terserOptions: { format: { comments: false, }, }, extractComments: false, }), ], }, module: { rules: [ { test: /\.(css|scss|sass)$/, use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [["autoprefixer"]], }, }, }, "sass-loader", ], exclude: /node_modules/, }, ], }, plugins: [ new MiniCssExtractPlugin({ filename: "assets/css/[hash:8].css", }), ], });

需要安装依赖:

都 2022 年了,手动搭建 React 开发环境很难吗?

# 用于将 CSS 导出到单独文件
yarn add mini-css-extract-plugin -D
# 用于做源代码压缩
yarn add terser-webpack-plugin -D

(5) 开发&构建

弄好了上面的 Webpack 配置,就可以实际的开发了。

/src/index.tsx 文件如下:

都 2022 年了,手动搭建 React 开发环境很难吗?

执行:yarn dev,会自动打开浏览器页面:http://localhost:7878/。

执行:yarn build,会将项目编译打包输出到 ./dist/ 文件夹下。

4、 Bable 处理兼容性

我们的项目可能会在各种浏览器中运行,为了尽可能兼容大多数用户的设备,因此引入 Babel 来统一处理兼容性。

在 webpack.common.js 配置文件中增加:

...
rules: [ { test: /\.tsx?$/, use: [ + { + loader: "babel-loader", + options: { + presets: [ + [ + "@babel/preset-env", // 预制配置 + { + corejs: { + version: 3, + }, + useBuiltIns: "usage", // 按需引入 pollyfill + }, + ], + "@babel/preset-react", // React 环境 + ], + plugins: ["@babel/plugin-transform-runtime"], + }, + }, "ts-loader", ], exclude: /node_modules/, }, ... ], ...

放到 webpack.common.js 文件下也是为了考虑在开发环境下验证引入 pollyfill 的正确性。

同时还需要安装如下依赖:

# 安装 babel 核心和加载器
yarn add @babel/core babel-loader -D
# core-js 中有各种各样的 pollyfill,用于提升兼容性
# https://github.com/zloirock/core-js
yarn add core-js -D
# 预制环境
yarn add @babel/preset-env @babel/preset-react -D
# 统一的 pollyfill,打包时候加载到代码中,减少冗余代码
yarn add @babel/plugin-transform-runtime -D

三、路由 React-router-dom

都 2022 年了,手动搭建 React 开发环境很难吗?都 2022 年了,手动搭建 React 开发环境很难吗?

前端的页面一般是多页面的,因此我们需要一个统一的路由来方便管理,这里用到了 react-router-dom v6[3] 版本。

多路由的使用方式基本相似,因此官方提炼出了 useRoutes 的 Hooks,用于便捷生成路由,相较于 V5 版本,确实方便太多了。

安装作为应用依赖:

yarn add react-router-dom

1、 统一管理的路由配置首先是配置

路由 /src/config/router.tsx 文件:

import { RouteObject } from "react-router-dom"; import HomePage from "@/pages/home"; const ROUTER_CONFIG: RouteObject[] = [ { path: "/", element: <HomePage />, }, { path: "*", element: <>404 Not Found!, }, ]; export { ROUTER_CONFIG }; 

之后如果新增任意页面,都可以在 /src/pages/ 文件夹下新增任,并且都可以放到 /src/config/router.tsx 文件来统一管理,嵌套路由同样适用,只需要根据 RouteObject 类型声明规范即可:

/**  * A route object represents a logical route, with (optionally) its child  * routes organized in a tree-like structure.  */ export interface RouteObject { caseSensitive?: boolean; // 大小写敏感
    children?: RouteObject[]; // 子路由
    element?: React.ReactNode; // 组件
    index?: boolean; // 在子路由中,默认为父级路由的首页
    path?: string; // URL 路径 } 

2、 项目中引入

然后在 /src/app.tsx 文件中使用 useRoutes() 并嵌入到应用中:

import { useRoutes } from "react-router-dom"; import { ROUTER_CONFIG } from "./config/router"; const App = () => { const appRoutesElement = useRoutes(ROUTER_CONFIG); return appRoutesElement; }; export default App; 

最后在 /src/inde.tsx 使用 BrowserRouter 包裹 组件。

import { render } from "react-dom"; import { BrowserRouter } from "react-router-dom"; import App from "./app"; render( <BrowserRouter> <App /> BrowserRouter>, document.getElementById("root") ); 

此时的项目目录结构如下:

都 2022 年了,手动搭建 React 开发环境很难吗?

都 2022 年了,手动搭建 React 开发环境很难吗?目录结构

如此就可以愉快的编写任意页面啦!

3、 [优化]延迟按需加载页面

虽然路由集中管理了,但是首屏加载的 js 文件太大,会使得白屏时间较长,增加了用户等待时间。

因此考虑延迟按需加载页面方式,使用 import() 和 React.lazy() 来主动优化。

新建一个通用组件 LazyWrapper 在 /src/components/lazy-wrapper/index.tsx 文件。

import { FC, lazy, Suspense } from "react"; interface LazyWrapperProps { /** 组件路径: 在 src/pages 目录下的页面路径,eg: /home => src/pages/home/index.tsx */ path: string; } /**  * 懒加载组件包装器  */ const LazyWrapper: FC<LazyWrapperProps> = ({ path }) => { const LazyComponent = lazy(() => import(`/src/pages${path}`)); return ( <Suspense fallback={<div>loading...div>}> <LazyComponent /> Suspense> ); }; export default LazyWrapper; 

此时修改 /config/router.tsx 路由配置文件:

都 2022 年了,手动搭建 React 开发环境很难吗?

效果如下: 当加载 Home 页面时,按需加载对应的组件。

都 2022 年了,手动搭建 React 开发环境很难吗?

另外由于拆包之后可能组件容易因网络抖动原因加载失败,所以还需要做自动重试拉取组件的方案,这里也不赘述了,参考之前写的文章:《性能优化竟白屏,难道真是我的锅?》

通过二次封装 Errorboundary 组件,实现组件加载失败自动重试,并针对错误上报日志,便于后期针对性优化。

四、状态管理 Redux

都 2022 年了,手动搭建 React 开发环境很难吗?都 2022 年了,手动搭建 React 开发环境很难吗?

在一个应用中,自然是少不了全局状态管理,一般情况下如果状态比较简单,可以直接使用 React 的 useContext 和 useReducer Hooks 组合实现简单的全局状态管理。

但通常我们的项目应该是比较庞大复杂,为了提升后期可维护性,因此使用了 Redux 作为全局状态管理。

Redux 的另一大优势则是提供了 @reduxjs/toolkit[4] 辅助工具,使得状态管理更加简单。

安装:

# react-redux 是 redux 的 UI 桥接层
yarn add redux react-redux
yarn add @reduxjs/toolkit

这里就不在赘述了,对于 Redux 的状态管理方案,可以参考之前写的文章:《用 Redux 做状态管理,真的很简单

延伸 · 阅读

精彩推荐
  • ReactReact tsx生成随机验证码

    React tsx生成随机验证码

    这篇文章主要为大家详细介绍了React tsx生成随机验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    laipengfei196252022-03-09
  • Reactreact-native 实现购物车滑动删除效果的示例代码

    react-native 实现购物车滑动删除效果的示例代码

    这篇文章主要介绍了react-native 实现购物车滑动删除效果的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    程序猿tx3902021-12-31
  • Reactreact如何用懒加载减少首屏加载时间

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

    这篇文章主要介绍了react如何利用懒加载减少首屏加载时间,帮助大家更好的理解和学习使用react,感兴趣的朋友可以了解下...

    阿政想暴富7412022-03-03
  • ReactReact html中使用react的两种方式

    React html中使用react的两种方式

    这篇文章主要介绍了React html中使用react的两种方式,本文给大家提到了React pwa的配置代码,给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴...

    愚公搬代码6422022-02-23
  • React详解React Fiber的工作原理

    详解React Fiber的工作原理

    这篇文章主要介绍了React Fiber的工作原理的相关资料,帮助大家更好的理解和学习使用React框架,感兴趣的朋友可以了解下...

    陌上兮月7942022-03-08
  • Reactreact获取input输入框的值的方法示例

    react获取input输入框的值的方法示例

    这篇文章主要介绍了react获取input输入框的值的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    Pinkh8222022-02-24
  • Reactreact diff算法源码解析

    react diff算法源码解析

    这篇文章主要介绍了react diff算法源码解析的相关资料,帮助大家更好的理解和学习使用react,感兴趣的朋友可以了解下...

    zhangyu5632022-02-27
  • React深入理解React Native核心原理(React Native的桥接(Bridge)

    深入理解React Native核心原理(React Native的桥接(Bridge)

    这篇文章主要介绍了深入理解React Native核心原理(React Native的桥接(Bridge),本文重点给大家介绍React Native的基础知识及实现原理,需要的朋友可以参考下...

    Gavell9712022-02-23