👉用Webpack从零搭建一个 React 开发环境

372 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情

上一次我们讲了如何用Webpack从零搭建一个Vue3 + TypeScript开发环境,本期我们来讲一下如何用Webpack从零搭建一个React开发环境

还没看过上期的小伙伴可以去看看~

👉用Webpack从零搭建一个 Vue3 + TypeScript 开发环境

前言

传统的 Web 开发中,往往是强调结构、样式和逻辑三者分离,降低代码的复杂度。

但是在 React 框架当中,其认为渲染逻辑实际上与 UI 逻辑之间是有内在的耦合关系的,于是乎提出了使用JSX的方式,将结构、样式和逻辑组合在一起,形成“组件”,以“组件”为单位来维护代码,实现关注点分离

JSX写起来十分容易上手,它能够允许我们在js中编写html,并且又能够在html中插入js,十分灵活,用起来仿佛就像一切都本该如此

import React from 'react'

const Component = () => {
  return <div className="container">Hello React</div>
}

以上就是一段JSX代码,相信用过React的读者对它都不陌生,我们今天的重点不是介绍JSX的语法,而是要关注如何使用Webpack从零搭建一个React的开发环境

要知道,JSX这种语法浏览器是无法识别的,因此我们要想办法将其转成纯js代码,从而能够在浏览器中执行

那么这个该如何实现呢?这就要用到咱们的Babel

让 Webpack 支持处理 JSX

首先咱们把Webpack装上

pnpm i webpack webpack-cli -D

JSXjs交给Babel实现,因此我们再装个Babel

pnpm i @babel/core @babel/preset-react babel-loader -D

然后新建一个webpack.config.js去配置jsxbabel-loader处理

/**
 * @type {import('webpack').Configuration}
 */
const config = {
  mode: 'development',
  devtool: false,
  entry: './src/main.jsx',
  resolve: {
    // 导入 js 和 jsx 时可以不用写后缀
    extensions: ['.js', '.jsx'],
  },
  module: {
    rules: [
      {
        test: /\.jsx$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-react'],
            },
          },
        ],
      },
    ],
  },
}

module.exports = config

main.jsx中的内容如下:

import ReactDOM from 'react-dom/client'
import Component from './components/Component'

ReactDOM.createRoot(document.getElementById('app')).render(<Component />)

Component.jsx中是一个组件,代码如下:

const Component = () => {
  return <div className="container">Hello React</div>
}

export default Component

然后我们使用webpack进行打包

打包Component组件.png

可以看到打包后的JSX被转成了React.createElementjs函数,这样就能够在浏览器中运行了

自动注入 React 导入代码

在上面的示例中,我们编写JSX的时候总是需要写一个import React from 'react'这样的导入语句,十分麻烦,实际上babel-loader支持配置自动注入React运行时代码

我们只需要修改babel-loader@babel/preset-react的预设配置即可

babel中关于自动注入React运行时的文档.png

/**
 * @type {import('webpack').Configuration}
 */
const config = {
  module: {
    rules: [
      {
        test: /\.jsx$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-react',
                  {
                    runtime: 'automatic',
                  },
                ],
              ],
            },
          },
        ],
      },
    ],
  },
}

现在我们将index.jsx中的import React from 'react'去掉后再打包看看

配置runtime为automatic后再打包.png

依然可以打包成功,这样一来我们就可以随心所欲使用JSX了,不需要管有没有导入React

配置打包后自动生成对应 html

目前我们打包后的结果只有一个js文件,如果想要在浏览器中预览效果还得手动创建一个html,比较麻烦

这里我们可以使用html-webpack-plugin去帮我们自动生成html

pnpm i html-webpack-plugin -D

然后在webpack.config.js中加上插件配置

/**
 * @type {import('webpack').Configuration}
 */
const config = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
}

配置开发环境服务器

为了方便在开发环境下更改代码后立刻看到运行效果,也就是HMR热更新的能力,我们还需要用到webpack-dev-server

pnpm i webpack-dev-server -D

然后修改webpack.config.js修改devServer配置

/**
 * @type {import('webpack').Configuration}
 */
const config = {
  // ...
  devServer: {
    hot: true,
    open: true,
  },
}

运行npx webpack serve即可开启开发环境服务器了

配置 css

目前我们还没法使用css,因此也需要配置一下相关loader

pnpm i css-loader style-loader -D

还可以顺便把预处理器sasssass-loader装上

pnpm i sass sass-loader -D

并添加相关配置

/**
 * @type {import('webpack').Configuration}
 */
const config = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(scss|sass)$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
    ],
  },
}

现在我们就可以随意在项目中导入css/sass/scss资源了

CSS Module

为了做到像Vuescoped特性的效果,我们可以使用CSS Module的方式,它的原理就是对导入的css类名做特殊处理,默认是用哈希值

这样的话就可以避免类名重复导致的样式冲突问题了,开启该功能很简单,只需要在css-loader中进行一下简单的配置即可

/**
 * @type {import('webpack').Configuration}
 */
const config = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
        ],
      },
      {
        test: /\.(scss|sass)$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
          'sass-loader',
        ],
      },
    ],
  },
}

使用的时候就可以像这样使用

import style from './style.scss'

console.log(style)

const Component = () => {
  return <div className={style.container}>Hello React</div>
}

export default Component

这里导入的style.scss变成了一个js对象

image.png

这里的container就是样式文件中的类名,其值是一个哈希值,会作为对应的类名

image.png

这就是CSS Module避免样式冲突的原理

集成 TypeScript

项目中往往会使用到TypeScript,这样也就能够顺便支持tsx,那么如何集成TypeScript呢?

社区中目前有两种主流的方案:

  1. 使用Babel@babel/preset-typescript
  2. 使用ts-loader

我们两种方案都尝试一下

@babel/preset-typescript

首先安装依赖

pnpm i typescript @babel/preset-typescript -D

然后添加对应loader配置

**
 * @type {import('webpack').Configuration}
 */
const config = {
  resolve: {
    // 不要忘记加上对 .ts 和 .tsx 的后缀忽略支持
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
  module: {
    rules: [
      // 集成 TypeScript
      {
        test: /\.ts$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-typescript'],
          },
        },
      },
      {
        test: /\.tsx$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-react',
                {
                  runtime: 'automatic',
                },
              ],
              '@babel/preset-typescript',
            ],
          },
        },
      },
    ],
  },
}

这样就可以在项目中使用tstsx了,我们可以随便建立一个tsx组件试试

image.png

可以看到会报错,这是因为还需要配置一下TypeScript,让它知道我们使用的是React17之后的版本,不需要导入React

新建tsconfig.json

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

这样就不会报错啦

ts-loader

至于第二种方式就简单多了,直接使用即可

/**
 * @type {import('webpack').Configuration}
 */
const config = {
  module: {
    rules: [
      // 集成 TypeScript -- 使用 babel-loader
      // {
      //   test: /\.ts$/,
      //   use: {
      //     loader: 'babel-loader',
      //     options: {
      //       presets: ['@babel/preset-typescript'],
      //     },
      //   },
      // },
      // {
      //   test: /\.tsx$/,
      //   use: {
      //     loader: 'babel-loader',
      //     options: {
      //       presets: [
      //         [
      //           '@babel/preset-react',
      //           {
      //             runtime: 'automatic',
      //           },
      //         ],
      //         '@babel/preset-typescript',
      //       ],
      //     },
      //   },
      // },

      // 集成 TypeScript -- 使用 ts-loader
      {
        test: /\.ts$/,
        use: ['ts-loader'],
      },
      {
        test: /\.tsx$/,
        use: ['ts-loader'],
      },
    ],
  },
}

至此,整个React开发环境就搭建完毕啦