第十三章 webpack5项目搭建React-Cli(开发模式)

简介: 详细介绍搭建React-cli开发模式配置的过程

step1--新建项目目录

创建一个新的目录,用于搭建React项目。

mkdir react-cli
cd react-cli

step2--初始化项目

初始化项目生成package.json文件。

npm init -y

step3--新建webpack开发模式配置文件

新建config目录文件夹,在创建开发配置文件,并定义好初始配置结构。

文件名:webpack.dev.js

module.exports = {
  // 入口
  entry: '',

  // 输出
  output: '',

  // 存放loader的module
  module: {
    rules: []
  },

  // 需要加载的插件plugins
  plugins: [],

  // 关于压缩的配置项
  optimization: {},

  // 开发模式mode
  mode: 'development'
}

step4--详细配置开发模式

1、指定入口文件

entry: './src/main.js'

2、指定输出目录以及文件命名

output: {
    path: undefined, // 开发环境不需要打包,可以设置路径为undefined
    filename: 'static/js/[name].js', // 指定bundle资源的路径以及命名
    chunkFilename: 'static/js/[name].chunk.js', // 一般是动态导入的一些资源
    assetModuleFilename: 'static/media/[hash:10][ext][query]', // 一些静态资源如图片等
},

3、配置loader用于处理为webpack可识别的资源

需要处理的有css资源、图片资源、js资源。

  • 处理css资源

主要的loader有:style-loadercss-loaderless-loadersass-loaderstylus-loader

处理样式的兼容性:postcss-loader

配置兼容的程度:需要在package.json里面配置browserslist属性

1、封装通用样式处理loader函数

由于样式文件有多种(.css|.less|.sass|.scss|.styl),一个个配置会代码冗余,所以使用通用函数来复用代码,减少配置代码的冗余。

// 处理样式loader的通用函数
const getStyleLoader = (pre) => {
  return [
    'style-loader','css-loader',{
      // 处理css兼容性
      // 需要配合package.json的browserlist属性来决定兼容性
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: ['postcss-preset-env']
        }
      }
    },
    pre
  ].filter(Boolean)
}

2、结合通用函数配置样式loader

  module: {
    rules: [
      {
        test: /\.css$/i, // 以css结尾的文件
        use: getStyleLoader()
      },
      {
        test: /\.less$/i, // 以less结尾的文件
        use:getStyleLoader('less-loader')
      },
      {
        test: /\.s[ac]ss$/i, // 以sass/scss结尾的文件
        use:getStyleLoader('sass-loader')
      },
      {
        test: /\.styl$/i, // 以styl结尾的文件
        use:getStyleLoader('stylus-loader')
      }
    ]
  },

3、配置browserlist

 "browserslist": [
    "last 2 version",
    "> 1%",
    "not dead"
  ]  
  • 处理图片资源
 // 处理图片资源
{
  test: /\.(jpe?g|png|webp|svg|gif)$/,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 10 * 1024, // 当图片小于10kb时转为base64
    }
  }
},
  • 处理其他资源
// 处理其他资源
{
  test: /\.(woff2?|ttf|mp3|mp4)$/,
  type: 'asset/resource'
}
  • babel处理js|jsx资源
// webpack.dev.js
const path = require('path')

//...省略

{
  test: /\.(jsx|js)$/,
  include: path.resolve(__dirname, "../src"),
  loader: "babel-loader",
  options: {
    cacheDirectory: true,
    cacheCompression: false,
    plugins: [
      // "@babel/plugin-transform-runtime", // presets中包含了
      "react-refresh/babel", // 开启js的HMR功能
    ],
  },
},
  • 编写babel配置文件
// babel.config.js
module.exports = {
  // 使用react官方规则
  presets: ["react-app"],
};
  • ESlint监测语法插件
// webpack.dev.js
const path = require('path')
const ESLintWebpackPlugin = require("eslint-webpack-plugin");

//...省略

plugins: [
    new ESLintWebpackPlugin({
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules", // 排除的目录
      cache: true, // 开启缓存
      cacheLocation: path.resolve(
        __dirname,
        "../node_modules/.cache/.eslintcache"
      ),
    }),
]
  • 编写ESlint配置文件
// .eslintrc.js
module.exports = {
  extends: ["react-app"], // 继承 react 官方规则
  parserOptions: {
    babelOptions: {
      presets: [
        // 解决页面报错问题
        ["babel-preset-react-app", false],
        "babel-preset-react-app/prod",
      ],
    },
  },
};
  • 处理HTML资源
// webpack.dev.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
//...省略
 plugins: [
    new ESLintWebpackPlugin({
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules",
      cache: true,
      cacheLocation: path.resolve(
        __dirname,
        "../node_modules/.cache/.eslintcache"
      ),
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
    }),
]
  • 设定开发模式
// webpack.dev.js
// 开发模式mode
mode: 'development'
  • 配置sourceMap
// webpack.dev.js
devtool: "cheap-module-source-map"
  • 配置代码分割
 optimization: {
    splitChunks: {
      chunks: "all",
    },
    runtimeChunk: {
      name: (entrypoint) => `runtime~${entrypoint.name}`,
    },
  },
  • 开发模式自动化服务器配置
devServer: {
    open: true,
    host: "localhost",
    port: 3000,
    hot: true,
    compress: true,
    historyApiFallback: true, // 解决react-router刷新404问题
},

step5--编写react文件

  • src/main.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";


const root = ReactDOM.createRoot(document.getElementById("app"));
root.render(
    <App />
);
  • src/App.jsx
import React from "react";
function App() {
  return (
    <div>
      <h1>App</h1>
    </div>
  );
}

export default App;
  • public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>React</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>

step6--安装所需依赖

  • webpack相关依赖
npm i webpack webpack-cli webpack-dev-server -D
  • 资源处理相关
npm i eslint-webpack-plugin -D
npm i html-webpack-plugin -D
npm i style-loader css-loader less-loader sass-loader sass stylus-loader -D
npm i postcss-loader postcss-preset-env -D

npm i babel-loader @babel/core babel-preset-react-app eslint-config-react-app -D
  • react相关
npm i react react-dom 

step7--启动项目

  • 编写启动命令
  "scripts": {
    "start": "npm run dev",
    "dev": "webpack serve --config ./config/webpack.dev.js"
  },
  • 运行项目
npm start
  • 报错
Error: [BABEL] D:\MyWorkSpace\VUE3_WORKSPACE\react-cli\node_modules\webpack-dev-server\client\index.js: Using `babel-preset-react-app` requires that you specify `NODE_ENV` or `BABEL_ENV` environment variables. Valid values are "development", "test", and "production". Instead, received: undefined.
  • 解决方案

1、安装cross-env

npm i cross-env -D

2、修改启动命令

 "scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
  },

将开发环境变量传给配置

  • 再次启动
npm start
  • 遇到新的错误
ERROR in ./src/main.js 4:0-24
Module not found: Error: Can't resolve './App' in 'D:\MyWorkSpace\VUE3_WORKSPACE\react-cli\src'
resolve './App' in 'D:\MyWorkSpace\VUE3_WORKSPACE\react-cli\src'

原因是后缀为jsx的文件,webpack无法识别。

  • 解决方案-修改配置增加扩展名
 resolve: {
    extensions: [".jsx", ".js", ".json"], // 自动补全文件扩展名,让jsx可以使用
 },

再次启动就可以正常运行了。


step8--激活HMR功能

css默认在style-loader里面就已经激活了HMR功能,我们只需要激活js的HMR功能就好。

  • 安装依赖
npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh
  • 配置

1、配置babel-loader

{
        test: /\.(js|jsx)$/,
        loader: 'babel-loader',
        options: {
          cacheDirectory: true,
          cacheCompression: false,
          plugins: [
            // "@babel/plugin-transform-runtime", // presets中包含了
            require.resolve('react-refresh/babel'), // 开启js的HMR功能
          ],
        },
      }

2、开启插件

plugins: [
    // ...省略
    new ReactRefreshWebpackPlugin(), // 解决js的HMR功能运行时全局变量的问题
]

step9--解决react-router刷新404问题

  • 修改文件

1、src/main.js

import React from 'react'
import ReactDOM from 'react-dom/client'
import {BrowserRouter} from 'react-router-dom'
import App from './App'
 //..
const root = ReactDOM.createRoot(document.getElementById('app'))
root.render(<div>
  <BrowserRouter>
  <App />
  </BrowserRouter>
  
</div>)

2、src/App.jsx

import React from "react";
import {Link, Route, Routes} from "react-router-dom"

import Home from "./pages/home"
import About from "./pages/about"

function App() {
  return (
    <div>
      <h1>App1</h1>

      <ul>
        <li><Link to="/home">Home</Link></li>
        <li><Link to="/about">About</Link></li>
      </ul>

      <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/about" element={<About />} />
      </Routes>
    </div>
  );
}

export default App;

3、src/pages/home/index.jsx

import React from "react";

export default function Home() {
  return <h1>Home~~~</h1>;
}

4、src/pages/about/index.jsx

import React from "react";

export default function Home() {
  return <h1 >about~~~</h1>;
}
  • 安装依赖
npm i react-router-dom
  • 启动项目
npm run dev

在浏览器可以切换路由,但是在路由下刷新页面会出现404错误。

GET http://localhost:3000/home 404 (Not Found)
  • 解决方案
devServer: {
    //...
    historyApiFallback: true, // 解决react-router刷新404问题
}

重新启动项目即可。


step10--关于react的路由懒加载

  • 修改文件

1、src/App.jsx

import React, { Suspense, lazy } from 'react'
import { Link, Route, Routes } from 'react-router-dom'

// import Home from './pages/home'
// import About from './pages/about'

const Home = lazy(()=>import(/*webpackChunkName: 'Home'*/"./pages/home"))
const About = lazy(()=>import(/*webpackChunkName: 'About'*/"./pages/about"))

function App() {
  return (
    <div>
      <h1>App1</h1>

      <ul>
        <li>
          <Link to="/home">Home</Link>
        </li>
        <li>
          <Link to="/about">About</Link>
        </li>
      </ul>

      <Suspense fallback={<div>loading...</div>}>
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </div>
  )
}

export default App
  • 启动项目
npm run dev

开启懒加载后,其路由代码会被单独打包到一个chunk里面,实现按需加载。


step11--现阶段的详细开发模式配置

const path = require('path')
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
// 处理样式loader的通用函数
const getStyleLoader = (pre) => {
  return [
    'style-loader','css-loader',{
      // 处理css兼容性
      // 需要配合package.json的browserlist属性来决定兼容性
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: ['postcss-preset-env']
        }
      }
    },
    pre
  ].filter(Boolean)
}


module.exports = {
  // 入口
  entry: './src/main.js',

  // 输出
  output: {
    path: undefined, // 开发环境不需要打包,可以设置路径为undefined
    filename: 'static/js/[name].js', // 指定bundle资源的路径以及命名
    chunkFilename: 'static/js/[name].chunk.js', // 一般是动态导入的一些资源
    assetModuleFilename: 'static/media/[hash:10][ext][query]', // 一些静态资源如图片等
  },

  // 存放loader的module
  module: {
    rules: [
      {
        test: /\.css$/i, // 以css结尾的文件
        use: getStyleLoader()
      },
      {
        test: /\.less$/i, // 以less结尾的文件
        use:getStyleLoader('less-loader')
      },
      {
        test: /\.s[ac]ss$/i, // 以sass/scss结尾的文件
        use:getStyleLoader('sass-loader')
      },
      {
        test: /\.styl$/i, // 以styl结尾的文件
        use:getStyleLoader('stylus-loader')
      },
      // 处理图片资源
      {
        test: /\.(jpe?g|png|webp|svg|gif)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024, // 当图片小于10kb时转为base64
          }
        }
      },
      // 处理其他资源
      {
        test: /\.(woff2?|ttf|mp3|mp4)$/,
        type: 'asset/resource'
      },
      // babel处理js|jsx资源
      {
        test: /\.(js|jsx)$/,
        loader: 'babel-loader',
        options: {
          cacheDirectory: true,
          cacheCompression: false,
          plugins: [
            // "@babel/plugin-transform-runtime", // presets中包含了
           'react-refresh/babel', // 开启js的HMR功能
          ],
        },
      }
    ]
  },

  // 需要加载的插件plugins
  plugins: [
    new ESLintWebpackPlugin({
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules",
      cache: true,
      cacheLocation: path.resolve(
        __dirname,
        "../node_modules/.cache/.eslintcache"
      ),
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
    }),
    new ReactRefreshWebpackPlugin()
  ],

  // 关于压缩的配置项
  optimization: {
    splitChunks: {
      chunks: "all",
    },
    runtimeChunk: {
      name: (entrypoint) => `runtime~${entrypoint.name}`,
    },
  },
  resolve: {
    extensions: [".jsx", ".js", ".json"], // 自动补全文件扩展名,让jsx可以使用
  },
  devServer: {
    open: false,
    host: "localhost",
    port: 3000,
    hot: true,
    historyApiFallback: true, // 解决react-router刷新404问题
    // static: {
    //   directory: path.join(__dirname, "../"),
    // },
},

  // 开发模式mode
  mode: 'development',
  devtool: "cheap-module-source-map"
}
相关文章
|
缓存 JSON 前端开发
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新 7
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新
612 0
|
缓存 JavaScript 前端开发
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新 2
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新
405 0
|
24天前
|
前端开发 JavaScript
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
|
6月前
|
前端开发 JavaScript 容器
前端vw自适应解决方案,适用pc端以及移动端,适用webpack以及vite,适用vue以及react
前端vw自适应解决方案,适用pc端以及移动端,适用webpack以及vite,适用vue以及react
405 0
|
6月前
|
前端开发 JavaScript
《Webpack5 核心原理与应用实践》学习笔记-> React全栈环境
《Webpack5 核心原理与应用实践》学习笔记-> React全栈环境
75 0
|
11月前
|
存储 前端开发
React & webpack &ant 学习
React & webpack &ant 学习
72 0
|
前端开发 应用服务中间件 nginx
简单几步,将React项目脚手架Webpack换成Vite⚡⚡,附带性能比较和思考
简单几步,将React项目脚手架Webpack换成Vite⚡⚡,附带性能比较和思考
|
前端开发
React 安装使用 Less(详细流程,包含 webpack、craco 方式)
React 安装使用 Less(详细流程,包含 webpack、craco 方式)
499 0
|
缓存 前端开发 JavaScript
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新 5
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新
186 1
|
Web App开发 移动开发 缓存
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新 6
2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新
373 0