发布于 2026-01-06 1 阅读
0

从零开始搭建 React + TypeScript + webpack 应用,无需使用 create-react-app

从零开始搭建 React + TypeScript + webpack 应用,无需使用 create-react-app

艺术作品:https://code-art.pictures/
艺术作品中的代码:React JS

既然有 create-react-app,为什么还要费这个劲呢?

问得好!其实,如果你对它满意create-react-app,就尽管用吧🙂 不过,如果你想了解所有部件是如何协同工作的,那我们来自己把它们组合起来吧!

完整代码

本示例的完整代码可在此处获取。您可以直接复制粘贴使用,或者参考这篇博文进行操作。

关于保持其时效性的说明

亲爱的读者和开发者同行,我力求保持这篇文章的时效性。但是,如果您发现任何不准确或过时的信息,请随时留言。谢谢!

我们将要创建的项目的结构

/hello-react
  /dist
    index.html
    main.da363aed60cf1cf68088.css
    main.db8733ab04253d079b1c.js
    main.db8733ab04253d079b1c.js.LICENSE.txt
  /src
    index.css
    index.tsx
  index.html
  package.json
  tsconfig.json
  webpack.config.mjs
Enter fullscreen mode Exit fullscreen mode

1. 安装 Node.js 和 npm

Node.js 的安装步骤取决于您的操作系统。请访问下载页面并按照提供的说明进行操作。

npm不需要单独安装,因为它已与 Node.js 捆绑在一起。要验证所有内容是否已正确安装在您的系统上,请参考这些说明

附注: Node.js 和 npm 并非唯二的选择。还有Deno(Node.js 的替代方案)和Yarn(npm 的替代方案)。如果您不确定,我建议您暂时先使用 Node.js 和 npm。

2. 创建项目

创建项目根目录,hello-reactnpm init在该目录下运行向导:

mkdir hello-react
cd hello-react
npm init
Enter fullscreen mode Exit fullscreen mode

向导会通过逐一提问的方式引导您创建一个空项目。要自动接受所有默认答案,您可以将-y参数添加到npm init命令中。

向导完成后,会创建以下文件:

package.json

{
  "name": "hello-react",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

虽然不多,但这已经是一个有效的 Node.js 项目了!🎊

3. 安装 TypeScript

在项目根目录下,运行以下命令:

npm i --save-dev typescript
Enter fullscreen mode Exit fullscreen mode

4. 创建tsconfig.json

此文件包含项目的 TypeScript 配置。tsconfig.json在项目根目录下创建一个文件,并插入以下内容:

tsconfig.json

{
  "compilerOptions": {
    "esModuleInterop": true,
    "jsx": "react-jsx",
    "module": "esnext",
    "moduleResolution": "bundler",
    "lib": [
      "dom",
      "esnext"
    ],
    "strict": true,
    "sourceMap": true,
    "target": "esnext",
  },
  "exclude": [
    "node_modules"
  ]
}
Enter fullscreen mode Exit fullscreen mode

这些选项是什么意思?我们一起来看看!

compilerOptions

  • esModuleInterop修复了从 CommonJS 到 TypeScript 的默认导入和命名空间导入问题。这通常是为了兼容性而必须做的。
  • jsx:指定 TypeScript 应该如何转译 JSX 文件(例如,react-jsx用于 React)。
  • module:决定 TypeScript 如何转译 ES6 的导入和导出。如果设置为 false,esnext则不会更改它们,我建议这样做,以便 webpack 可以处理这些转换。
  • moduleResolution:指定 TypeScript 如何解析模块,这取决于目标运行时。由于我们的应用使用 webpack 打包,bundler因此是最佳选择。早期版本使用过另一个node选项,该选项也适用于我们的示例。
  • lib:指定目标环境中存在的库,以便 TypeScript 隐式包含它们的类型。注意:TypeScript 无法在运行时验证这些库是否实际可用——这是你的承诺。更多详情稍后介绍。
  • strict启用 TypeScript 中的所有严格类型检查选项,以实现最大的类型安全性。
  • sourceMap启用 TypeScript 生成源映射。我们将配置 webpack,使其在生产构建中排除这些源映射。
  • target配置目标 ECMAScript 版本,该版本取决于用户的环境。稍后将详细介绍。

exclude

  • exclude:将某些库排除在类型检查和转译之外。但是,您的代码仍然会根据这些库提供的类型定义进行检查。

完整tsconfig.json参考信息请查看官方文档

5. 安装 webpack、插件和加载器

请在项目根目录下运行以下命令。该命令很长,请确保您已滚动到足够远的位置并复制整行内容!

npm i --save-dev webpack webpack-cli webpack-dev-server css-loader html-webpack-plugin mini-css-extract-plugin esbuild-loader
Enter fullscreen mode Exit fullscreen mode

6. 创建webpack.config.mjs

webpack.config.mjs在项目根目录下创建一个名为 `.htm` 的文件,并插入以下内容:

webpack.config.mjs

import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'

const prod = process.env.NODE_ENV === 'production'

export default {
  mode: prod ? 'production' : 'development',
  devtool: prod ? undefined : 'source-map',
  entry: './src/index.tsx',
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'esbuild-loader',
        options: {
          target: 'esnext',
          jsx: 'automatic',
        },
        resolve: {
          extensions: ['.ts', '.tsx', '.js', '.json'],
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ]
  },
  output: {
    filename: '[name].[contenthash].js',
    path: import.meta.dirname + '/dist/',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'index.html',
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
  ],
}
Enter fullscreen mode Exit fullscreen mode

这里涉及的内容很多!Webpack 配置可以说是整个设置中最复杂的部分。让我们一步一步来:

  • 设置NODE_ENV变量:这是区分开发模式和生产模式的常用方法。稍后,您将看到如何在脚本中设置它。
  • HtmlWebpackPlugindist/index.html根据模板生成文件index.html,我们稍后将创建该模板。
  • MiniCssExtractPlugin:将样式提取到单独的文件中。如果没有这一步,样式将保留在代码中index.html
  • mode:指定构建是用于开发环境还是生产环境。在生产模式下,Webpack 会自动压缩包。
  • devtool配置源映射以便于调试。
  • entry:定义应用程序在客户端加载后首先执行的模块。它作为启动应用程序的引导程序。
  • module.rules:描述如何将不同类型的文件加载(导入)到捆绑包中。
    • test: /\.(ts|tsx)$/:使用 . 处理 TypeScript 文件。请在此处esbuild-loader查看示例
    • test: /\.css$/处理 CSS 文件。
  • output
    • filename设置文件名包含内容哈希值。这种技术被称为“缓存清除”
    • path:指定已编译文件的目标目录。
  • plugins列出所有插件及其设置。

6.1. 替代方案:将 webpack 配置作为 CJS 文件

我们在 ESM 文件中定义了 webpack 配置。这从4.5版本开始就可行,并且在新版本中也应该有效node。但是,如果这种方法对您无效,您可以将配置切换到 CJS。

webpack.config.js
首先,将文件扩展名从 . 更改为.mjs..js然后,按如下方式更新内容:
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const prod = process.env.NODE_ENV === 'production'

module.exports = {
  // (no changes)
  output: {
    // (no changes)
    // The only change:
    // replace import.meta.dirname with __dirname
    path: __dirname + '/dist/',
  },
  // (no changes)
}

Enter fullscreen mode Exit fullscreen mode

6.2. 替代方案:使用ts-loader代替esbuild-loader

我们使用esbuild-loader来加载 TypeScript 文件。截至 2025 年,它很可能是 webpack 中最快的 TypeScript 加载器。

然而,根据 webpack文档, `typescript`ts-loader是默认推荐的 TypeScript 加载器。与esbuild-loader相对较新的 `typescript` 不同,ts-loader`typescript` 更成熟、更可靠。如果您有更复杂的配置,`typescript`ts-loader更有可能提供可靠的支持。

切换到 ts-loader
npm uninstall esbuild-loader
npm i -D ts-loader
Enter fullscreen mode Exit fullscreen mode

webpack.config.mjs

// ...
export default {
  // ...
  module: {
    rules: [
//    {
//      test: /\.(ts|tsx)$/,
//      exclude: /node_modules/,
//      loader: 'esbuild-loader',
//      options: {
//        target: 'esnext',
//        jsx: 'automatic',
//      },
//      resolve: {
//        extensions: ['.ts', '.tsx', '.js', '.json']
//      },
//    },
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        resolve: {
          extensions: ['.ts', '.tsx', '.js', '.json'],
        },
        use: 'ts-loader',
      },
      // ...
    ],
  },
  // ...
}
Enter fullscreen mode Exit fullscreen mode

7. 将脚本添加到 package.json 文件中

将以下脚本添加startbuild您的文件中package.json

package.json(Linux、OS X)

{
  ...
  "scripts": {
    "start": "webpack serve --port 3000",
    "build": "NODE_ENV=production webpack"
  }
  ...
}
Enter fullscreen mode Exit fullscreen mode

package.json(Windows PowerShell)
命令build必须不同

{
  ...
  "scripts": {
    "start": "webpack serve --port 3000",
    "build": "set NODE_ENV=production && webpack"
  }
  ...
}
Enter fullscreen mode Exit fullscreen mode
  • start:在端口 3000 上启动一个开发服务器。开发服务器会自动监视您的文件,并在需要时重新构建应用程序。
  • build:构建用于生产环境的应用程序。它NODE_ENV=production会设置NODE_ENV变量,该变量会在第一行进行检查webpack.config.mjs

8. 创建 index.html 模板

HtmlWebpackPlugin即使没有模板,也可以生成 HTML 文件。不过,您可能还是需要一个模板,所以我们在项目根目录下创建一个。这就是我们在webpack.config.mjs插件部分提到的文件。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="initial-scale=1, width=device-width"/>
  <title>Hello React</title>
</head>
<body>
  <div id="app-root">App is loading...</div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

笔记:

  • lang="en"如果应用程序的实际语言与应用程序的语言不同,请记得更新为应用程序的实际语言。
  • <meta name="viewport" .../>标签对于响应式设计至关重要,它可以让您的布局适应各种设备尺寸。
  • <meta http-equiv="x-ua-compatible" content="ie=edge"/>如果您的目标浏览器是 IE,请务必在“也”<head>部分添加。

9. 安装 React

在项目根目录下,运行以下命令:

npm i react react-dom
Enter fullscreen mode Exit fullscreen mode

进而:

npm i --save-dev @types/react @types/react-dom
Enter fullscreen mode Exit fullscreen mode

10. 创建 src/index.tsx

这是您的应用程序的入口点,我们在[此处应插入参考文献]中引用过webpack.config.mjs。您也可以更新[main此处应插入参考文献]中的字段package.json以指向同一个文件,但这并非绝对必要。

src/index.tsx

import React from 'react'
import { createRoot } from 'react-dom/client'

const container = document.getElementById('app-root')!
const root = createRoot(container)
root.render(<h1>Hello React!</h1>)
Enter fullscreen mode Exit fullscreen mode

注意:此createRoot()API 是 React 18 的新增功能。如果您使用的是旧版本的 React,可以参考这篇博客文章获取指导,并使用以下代码:

React 17 中的 src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(
    <h1>Hello React!</h1>,
    document.getElementById('app-root'),
)
Enter fullscreen mode Exit fullscreen mode

11. 创建src/index.css并导入到src/index.tsx

为了确保我们的 CSS 插件正常工作,让我们应用一些简单的样式。

src/index.css

body {
  color: blue;
}
Enter fullscreen mode Exit fullscreen mode

src/index.tsx

import './index.css'
// The rest app remains the same
// ...
Enter fullscreen mode Exit fullscreen mode

12. 运行开发服务器

一路走来实属不易,但我们即将抵达终点!让我们运行开发服务器吧:

npm start
Enter fullscreen mode Exit fullscreen mode

现在在浏览器中打开http://localhost:3000/ — 你应该会看到彩色的欢迎信息:

你好 React!

现在尝试修改src/index.tsx,例如更改消息。应用程序应该会重新加载并显示更新后的文本。您还可以尝试更改样式——这些更改应该无需重启服务器即可生效。

13. 构建用于生产环境的应用

在项目根目录下,运行以下命令:

npm run build
Enter fullscreen mode Exit fullscreen mode

这将生成一个dist包含打包文件的文件夹。为了使这些文件在生产环境中呈现,您需要一个名为 `<utility_name>` 的小工具serve和一个相应的脚本。添加依赖项:

npm i --save-dev serve
Enter fullscreen mode Exit fullscreen mode

接下来,请更新您的脚本package.json

package.json

{
  ...
  "scripts": {
    ...
    "preview": "serve dist -p 3000"
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

然后运行npm run preview。打开http://localhost:3000/ — 你应该会看到欢迎信息!

14. 针对较旧的环境

这部分内容稍微高级一些,所以等你熟悉了基本设置之后再回来学习吧。

14.1. 目标 ES 版本

目标 ES 版本在两个地方设置:

  • tsconfig.json: 在下面compilerOptions.target
  • webpack.config.mjsesbuild-loader配置中

这取决于你的应用的目标用户群体。那么,你的目标用户是谁呢?

  • 你和你的团队——如果你们使用的是现代工具,那么保留默认设置是安全的esnext。你们可能不需要任何过时的东西🙂
  • 普通互联网用户——我建议以 2021 年为目标es<currentYear-3>。例如,在撰写本文的年份(2021 年),您应该以 2021 年为目标es2018。为什么不呢esnext?有时,即使是看似最新的浏览器也会缺少对某些功能的支持。例如,小米 MIUI 浏览器 12.10.5-go(2021 年 5 月发布)就不支持空值合并运算符。这里有一个示例程序,可以在该浏览器中进行测试。您的测试结果如何?
  • IE 用户— 如果您要支持 Internet Explorer,则目标必须为 ES5 es5。请注意,某些 ES6+ 功能在转译为 ES5 时可能会变得臃肿。

14.2. 选择目标库

库设置在tsconfig.jsoncompilerOptions.lib,此选项也取决于您对目标用户的猜测。

您可能会用到的典型库:

  • dom— 包括浏览器提供的所有 API。
  • es...例如,es2018— 包括带有相应 ES 规范的 JavaScript 内置函数。

重要提示:与 Babel 不同,这些选项不会自动添加任何 polyfill。因此,如果您的目标环境较旧,则需要手动添加 polyfill,如下一节所述。

14.3. 添加 polyfill

根据应用程序所需的 API,可能需要使用 Polyfill。

  • React 需要以下 polyfill:
  • 如果您的客户端代码使用了相对较新的 API,例如flatMapfetch,同时又面向较旧的浏览器,请考虑对其进行 polyfill。

以下是一些常用的填充材料:

  • core-js — 用于缺失的Set、、MapArray.flatMap
  • raf — 缺失requestAnimationFrame
  • whatwg-fetch — 用于查找缺失的资源fetch。注意:这不包含Promisepolyfill,它已包含在core-js上述内容中。

鉴于我们决定全部使用它们,设置如下:

npm i core-js raf whatwg-fetch
Enter fullscreen mode Exit fullscreen mode

index.tsx

import 'core-js/features/array/flat-map'
import 'core-js/features/map'
import 'core-js/features/promise'
import 'core-js/features/set'
import 'raf/polyfill'
import 'whatwg-fetch'

// The rest app remains the same
// ...
Enter fullscreen mode Exit fullscreen mode

14.4. 调整webpack运行时

或许令人惊讶,即使编译成 ES5,webpack代码中仍然可能包含 ES6+ 的结构。因此,如果您的目标浏览器是 Internet Explorer 等旧版浏览器,则可能需要禁用这些功能。更多详情,请查看文档页面。

添加这么多填充元素是否合理?

不,这样做并非总是合理的,因为大多数用户都使用现代浏览器。添加不必要的 polyfill 会浪费运行时资源和带宽。最佳方案是创建两个独立的 bundle:一个用于现代环境,另一个用于旧环境,然后根据用户环境加载相应的 bundle。不过,这种方法超出了本教程的范围。

你完成了!

我知道这并不容易😅 但我相信这些概念对你来说已经不再是难题了。感谢你一路陪伴我!

接下来会发生什么?

完成设置和应用的基本逻辑后,您可能需要某种形式的服务器端渲染 (SSR)。本系列的下一篇文章将探讨一种便捷实用的方法,无需服务器端 Worker 即可预渲染您的应用。祝您使用愉快!

文章来源:https://dev.to/alekseiberezkin/setting-up-react-typescript-app-without-create-react-app-oph