webpack从0开始初始化搭建一个React17 + TS项目

本文详细指导如何使用Webpack自定义配置React 17项目,涉及package.json管理、CDN引入、TS支持、CSS模块化与antd样式定制,以及全局变量管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

快速初始化搭建一个react项目,可以使用React官方的create-react-app,也可以使用一些封装好的脚手架比如阿里的飞冰ice。但是这些方法想要进行定制化配置需要费一些周折(比如create-react-app需要npm run eject来吐出配置文件),而自己用webpack搭建一个简单的应用可以帮助我们更好地理解一个react项目是如何构建的。

项目文件树如下所示:
项目文件树

1. package.json

依赖项包括了webpack以及用到的一系列打包插件和loader,react相关库以及antd相关库

{
  "name": "xxx",
  "version": "0.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config ./webpack.prod.config.js --mode production",
    "dev": "webpack serve --hot --config ./webpack.config.js --mode development --port 8080",
    "typeCheck": "tsc  --watch --noEmit"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@ant-design/icons": "^4.6.2",
    "@types/dom-mediacapture-record": "^1.0.7",
    "@types/offscreencanvas": "^2019.6.2",
    "antd": "^4.16.3",
    "duration-time-conversion": "^1.0.2",
    "loadsh": "0.0.4",
    "react": "^17.0.1",
    "react-color": "^2.19.3",
    "react-dom": "^17.0.1",
    "resize-observer-polyfill": "^1.5.1",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "@babel/cli": "^7.12.10",
    "@babel/core": "^7.12.10",
    "@babel/plugin-proposal-class-properties": "^7.12.1",
    "@babel/preset-env": "^7.12.10",
    "@babel/preset-react": "^7.12.10",
    "@babel/preset-typescript": "^7.12.7",
    "@types/lodash": "^4.14.171",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "@types/uuid": "^8.3.0",
    "babel-loader": "^8.2.2",
    "babel-plugin-import": "^1.13.3",
    "copy-webpack-plugin": "^7.0.0",
    "core-js": "^3.8.1",
    "css-loader": "^5.0.1",
    "fast-sass-loader": "^1.5.0",
    "html-webpack-plugin": "^5.0.0-alpha.15",
    "less": "^3.13.0",
    "less-loader": "^7.1.0",
    "mini-css-extract-plugin": "^1.3.5",
    "node-sass": "^5.0.0",
    "style-loader": "^2.0.0",
    "typescript": "^4.1.3",
    "webpack": "^5.10.1",
    "webpack-bundle-analyzer": "^4.2.0",
    "webpack-cli": "^4.2.0",
    "webpack-dev-server": "^3.11.0",
    "worker-loader": "^3.0.7"
  }
}

上面的package.json包含了基本的包和一些基本的命令

2. webpack.config.js

2.1 入口文件

入口文件设置为 src/index.tsx

// webpack.config.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

组件入口App.tsx

// App.tsx
import React from 'react';
import style from './app.less';

const App = () => {
  return (
    <div className={style.helloWorld}>HelloWorld</div>
  );
};

export default App;

2.2 CDN引入依赖

react, react-dom 不打包进项目里,可以采用CDN的方式在index.html里引入:

// webpack.config.js
  externals: {
    react: 'React',
    'react-dom': 'ReactDOM',
  },

用cdn的方式引入React和ReactDOM

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="shortcut icon" type="image/x-icon" href="https://img.alicdn.com/tfs/TB1Mo_xl0Tfau8jSZFwXXX1mVXa-68-76.png" />
    <title>xxx</title>
  </head>

  <body>
    <div id="root"></div>
    <script src="https://g.alicdn.com/code/lib/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://g.alicdn.com/code/lib/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
  </body>
</html>

当然,为了在开发和协作中能够有比较好的体验,还是需要在package.json的dependencies里加上react和ReactDOM,以便在本地的node_modules里安装这两个包

2.3 TS解决方案

对于ts文件,和js一样统一交给babel-loader来处理(babel7 之后增加了对ts的支持,且比ts-loader更快)

      {
        test: /\.(ts|js)x?$/,
        use: 'babel-loader',
        exclude: /(node_modules)/,
      }

2.4 样式解决方案

2.4.1 模块化解决方案

react的样式不像vue那样,直接写在<style scoped></style>里加个scoped就能自动加上哈希值来实现模块化,而是需要手动选择一个模块化方案。常见的有CSS in JSCSS Modules等方案。这里选择CSS Modules

本项目的样式文件都采样less编写,所以这里匹配less文件用css-loader来配置CSS Modules:
CSS Modules 教程

{
        test: /\.less$/,
        exclude: [/node_modules/, /antd.dark.less$/],  // 忽略antd
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { 
              modules: { localIdentName: '[local]_[hash:base64:5]' },
            },
          },
          {
            loader: 'less-loader',
          },
        ],
      },

这样就可以在tsx文件里引入less文件并使用

import React from 'react';
import style from './app.less'; //引入less文件

const App = () => {
  return (
    <div className={style.helloWorld}>Hello World</div> // 把style当做对象来使用
  );
};

export default App;

为了类型检查不报错,需要在src/types/global.d.ts里指定less

// src/types/global.d.ts
declare module '*.less';

2.4.2 自定义antd的样式变量

注意如果需要修改antd的一些全局变量的配置(如主题色等),需要手动引入antd.less/antd.dark.less等文件(而不是antd.css),所以这里注意要在exclude项里匹配这些文件,防止它们也被css-loader打上哈希标签;

然后单独开一条规则去处理antd的样式文件(我这里用的是antd的暗黑主题,所以是antd.dark.less,正常主题应该是antd.less)

      {
        test: /antd.dark.less$/,  // 对antd的less文件制定特殊规则,不要应用css modules
        use: [
          'style-loader',
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',
            options: {
              lessOptions:{
                modifyVars: { 
                  '@primary-color': '#dc7f31'
                },
                javascriptEnabled: true,
              }
            }
          },
        ],
      },

在less-loader里,我加上了modifyVars去修改一些antd的全局配置,antd官网给出了常用的样式变量

@primary-color: #1890ff; // 全局主色
@link-color: #1890ff; // 链接色
@success-color: #52c41a; // 成功色
@warning-color: #faad14; // 警告色
@error-color: #f5222d; // 错误色
@font-size-base: 14px; // 主字号
@heading-color: rgba(0, 0, 0, 0.85); // 标题色
@text-color: rgba(0, 0, 0, 0.65); // 主文本色
@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
@disabled-color: rgba(0, 0, 0, 0.25); // 失效色
@border-radius-base: 2px; // 组件/浮层圆角
@border-color-base: #d9d9d9; // 边框色
@box-shadow-base: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08),
  0 9px 28px 8px rgba(0, 0, 0, 0.05); // 浮层阴影

2.4.3 全局变量管理

全局变量管理采用css 的 var()函数。
在app.less里,可以定义一些全局的样式变量。

// app.less
:global { // :global表示不被css-loader打包时加上哈希值
  html,
  body,
  * {
    margin: 0;
    padding: 0;
    font-size: 14px;
    box-sizing: border-box;
    color: #d3d3d3;
  }
  :root {
    --font-color-1: #d3d3d3;
    --bc-main-1: #000;
    --bc-main-2: #1b1b1b;
    --bc-main-3: #333435;
    --bc-main-4: #333333;
    --theme-color: #dc7f31;
    --bc-box: #1f1f1f;
  }
}
  • :global 表示不被css-loader打包时加上哈希值的标记
  • :root是一个伪类,表示文档根元素,非IE及ie8及以上浏览器都支持,在:root中声明相当于全局属性,只要当前页面引用了:root segment所在文件,都可以使用var()来引用
  • 变量需要以--开头
    在其他样式文件里,就可以使用上述定义的变量
// example.less
div{
	background-color: var(--bc-main-1);
}

综上,webpack.config.js是这样:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const resolve = (...dir) => path.resolve(__dirname, ...dir);

module.exports = {
  entry: {
    app: path.resolve(__dirname, './src/index.tsx'),
  },
  output: {
    filename: '[name].js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
    alias: {
      '@': resolve('./src'),
    },
  },
  externals: {
    react: 'React',
    'react-dom': 'ReactDOM',
  },
  devServer: {
    disableHostCheck: true,
    https: true, // 本地调试开启https
  },
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,
        use: 'babel-loader',
        exclude: /(node_modules)/,
      },
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      {
        test: /antd.dark.less$/,  // 对antd的less文件制定特殊规则,不要应用css modules
        use: [
          'style-loader',
          {
            loader: 'css-loader',
          },
          {
            loader: 'less-loader',
            options: {
              lessOptions:{
                modifyVars: { 
                  '@primary-color': '#dc7f31',
                  '@error-color': '#f5222d'
                },
                javascriptEnabled: true,
              }
            }
          },
        ],
      },
      {
        test: /\.less$/,
        exclude: [/node_modules/, /antd.dark.less$/],  // 忽略antd
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { 
              modules: { localIdentName: '[local]_[hash:base64:5]' },
            },
          },
          {
            loader: 'less-loader',
          },
        ],
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'fast-sass-loader'],
      },
      {
        test: /\.worker\.js$/,
        use: { loader: 'worker-loader', options: { inline: 'fallback' } },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: resolve('./', 'index.html'),
      filename: 'index.html',
      favicon: false,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
      },
    }),
  ],
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值