天天看点

webpack-React脚手架配置

React脚手架配置

新建config/webpack.dev.js         config/webpack.prod.js

安装依赖 

npm i  webpack-dev-server webpack webpack-cli -D

npm i eslint-webpack-plugin html-webpack-plugin style-loader css-loader  postcss-loader

postcss-preset-env less-loader sass-loader sass stylus-loader -D

安装babel

npm i babel-loader @babel/core babel-preset-react-app eslint-config-react-app -D 

安装 react 

 npm i  react react-dom 

"dependencies": {
    "css-loader": "^6.7.1",
    "eslint-webpack-plugin": "^3.1.1",
    "html-webpack-plugin": "^5.5.0",
    "style-loader": "^3.3.1"
  },
  "devDependencies": {
    "@babel/core": "^7.18.2",
    "babel-loader": "^8.2.5",
    "babel-preset-react-app": "^10.0.1",
    "eslint-config-react-app": "^7.0.1",
    "less-loader": "^11.0.0",
    "postcss-loader": "^7.0.0",
    "postcss-preset-env": "^7.7.1",
    "sass": "^1.52.3",
    "sass-loader": "^13.0.0",
    "stylus-loader": "^7.0.0",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.9.2",
    "webpack-dev-server": "^4.9.2"
  }
}
           
配置启动项 package.json
"scripts": {
    "dev":"webpack serve --config ./config/webpack.dev.js"
  },
           
webpack-React脚手架配置

 安装定义环境变量库 cross-env

npm install --save-dev cross-env
"scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
  },
           
const path = require("path");
const EslintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const getStyleloaders=(pre)=>{
    return [
        "style-loader",
        "css-loader",
        {//css做兼容处理  还需在package.json中browserslist来指定兼容性
           loader:'postcss-loader',  
           options:{
              postcssOptions:{
                  plugins:["postcss-preset-env"],
              },
           },
        },
        pre
    ].filter(Boolean);
}


module.exports={
    entry:'./src/main.js',
    output:{
        path:undefined,
        filename:"static/js/[name].js",
        chunkFilename:"static/js/[name].chunk.js",//动态import导入的js
        assetModuleFilename:"static/media/[hash:10][ext][query]",
    },
    module:{
        rules:[
            //处理css
            {
                test:/\.css$/,
                use:getStyleloaders(),
            },
            {
                test:/\.less$/,
                use:getStyleloaders('less-loader'),
            },
            {
                test:/\.s[ac]ss$/,
                use:getStyleloaders('sass-loader'),
            },
            {
                test:/\.styl$/,
                use:getStyleloaders('stylus-loader')
            },

            //处理图片
            {
                test:/\.(jpe?g|png|gif|webp|svg)/,
                type:"asset",
                parser:{
                    dataUrlCondition:{
                        maxSize:10*1024,
                    },
                },
            },
            //处理其他资源
            {
                test:/\.(woff2?|ttf)/,
                type:"asset/resource",
            },


            //处理js
            {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:'babel-loader',
                options:{
                    cacheDirectory:true,//开启缓存
                    cacheCompression: false,//不要压缩打包性能更快
                },
            },
        ],
    },
    plugins:[
        new EslintWebpackPlugin({
            context:path.resolve(__dirname,'../src'),
            exclude:"node_modules",//包含排除 指定处理范围
            cache:true,//缓存
            cacheLocation:path.resolve(__dirname,'../node_modules/.cache/.eslintcache'),//缓存目录
            //添加.eslintrc.js 配置
        }),
        //处理html
        new HtmlWebpackPlugin({
               template:path.resolve(__dirname,"../public/index.html"),
        }),
    ],
    mode: "development", //开发模式
    devtool: "cheap-module-source-map", //调试设置
    optimization: {
        splitChunks:{
            chunks:'all', //进行代码分割 主要分割 动态导入的语法
        },
        //代码分割会导致缓存失效  避免文件的频繁变更导致浏览器缓存失效,
        //所以其是更好的利用缓存。提升用户体验。
        runtimeChunk: { 
            name: (entrypoint) => `runtime~${entrypoint.name}.js`,
          },
    },
    //webpack解析模块加载选项
    resolve:{
          //自动补全文件扩展名
          extensions:[".jsx",".js",".json"],
    },
    //热更新
    devServer: {
        open: true,
        host: "localhost",
        port: 3000,
        hot: true, //开启HMR
      },
}
           

JSX 配置HMR(热更新) 功能  React Refresh Webpack Plugin

安装依赖

npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh

<!-- webpack.dev.js   只有开发环境使用-->

const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
...
      //处理js
         {
                test:/\.jsx?$/,
                include:path.resolve(__dirname,'../src'),
                loader:'babel-loader',
                options:{
                    cacheDirectory:true,//开启缓存
                    cacheCompression: false,//不要压缩打包性能更快
                    plugins:[
                       'react-refresh/babel' //激活JSX 的HMR 功能
                    ]
                },
            },
...

    plugins:[
...
        //react 热更新
        new  ReactRefreshWebpackPlugin()
    ],

...
   devServer: {
        open: true,
        host: "localhost",
        port: 3000,
        hot: true, //开启HMR
      },
           

前端路由修改  

安装配置: npm i react-router-dom 

//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(
<BrowserRouter>
<App />
</BrowserRouter>);
           
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>App</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;
           

前端路由刷新404问题  

配置:
devServer: {
        open: true,
        host: "localhost",
        port: 3000,
        hot: true, //开启HMR
        historyApiFallback:true,//前端路由刷新404问题
      },
}
           

处理路由组件单独打包  使用路由懒加载

// import React from "react";
import   { Link,Route,Routes} from "react-router-dom";
import React,{Suspense,lazy} from "react"; //lazy函数负责定义路由懒加载的组件
// 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>App</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;
           

搭建生产模式配置

指定输出路径
output:{
        path:path.resolve(__dirname,"../dist"),
        filename:"static/js/[name].[contenthash:10].js",
        chunkFilename:"static/js/[name].[contenthash:10].chunk.js",//动态import导入的js
        assetModuleFilename:"static/media/[hash:10][ext][query]",
        clean:true,
    },
           

提取CSS成单独文件

const MiniCssExtractPlugin  =require('mini-css-extract-plugin');

 plugins:[

     new MiniCssExtractPlugin({
            filename:'static/css/[name].[contenthash:10].css',
            chunkFilename:'static/css/[name].[contenthash:10].chunk.css'
        })

]
           
处理JS 压缩   CSS 压缩
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); //css压缩
const TerserWebpackPlugin = require("terser-webpack-plugin"); //js压缩
           
...
    optimization: {
        splitChunks:{
            chunks:'all', //进行代码分割 主要分割 动态导入的语法
        },
        //代码分割会导致缓存失效  避免文件的频繁变更导致浏览器缓存失效,
        //所以其是更好的利用缓存。提升用户体验。
        runtimeChunk: { 
            name: (entrypoint) => `runtime~${entrypoint.name}.js`,
          },
          //js压缩
        minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin()]
    },
           
图片压缩
new ImageMinimizerPlugin({
                minimizer: {
                    implementation: ImageMinimizerPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
           

下载依赖  npm i mini-css-extract-plugin  css-minimizer-webpack-plugin -D

下载图片依赖包

npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo --save-dev  

修改构建启动变量 package.json

   "build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"

运行npm run build 打包  会有路由刷新404问题 需要专门配置

网站配置LOGO 图标   copy-webpack-plugin

安装: npm install copy-webpack-plugin --save-dev

把public下的资源拷贝到dist目录下

const CopyPlugin = require("copy-webpack-plugin");

new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, "../public"),
          to: path.resolve(__dirname, "../dist"),
          toType: "dir",
          noErrorOnMissing: true, // 不生成错误
          globOptions: {
            // 忽略文件
            ignore: ["**/index.html"],
          },
          info: {
            // 跳过terser压缩js
            minimized: true,
          },
        },
      ],
    }),
           
//webpack.prod.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");

const getStyleLoaders = (preProcessor) => {
  return [
    MiniCssExtractPlugin.loader,
    "css-loader",
    {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: [
            "postcss-preset-env", // 能解决大多数样式兼容性问题
          ],
        },
      },
    },
    preProcessor,
  ].filter(Boolean);
};

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "../dist"),
    filename: "static/js/[name].[contenthash:10].js",
    chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
    assetModuleFilename: "static/js/[hash:10][ext][query]",
    clean: true,
  },
  module: {
    rules: [
      {
        oneOf: [
          {
            // 用来匹配 .css 结尾的文件
            test: /\.css$/,
            // use 数组里面 Loader 执行顺序是从右到左
            use: getStyleLoaders(),
          },
          {
            test: /\.less$/,
            use: getStyleLoaders("less-loader"),
          },
          {
            test: /\.s[ac]ss$/,
            use: getStyleLoaders("sass-loader"),
          },
          {
            test: /\.styl$/,
            use: getStyleLoaders("stylus-loader"),
          },
          {
            test: /\.(png|jpe?g|gif|svg)$/,
            type: "asset",
            parser: {
              dataUrlCondition: {
                maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
              },
            },
          },
          {
            test: /\.(ttf|woff2?)$/,
            type: "asset/resource",
          },
          {
            test: /\.(jsx|js)$/,
            include: path.resolve(__dirname, "../src"),
            loader: "babel-loader",
            options: {
              cacheDirectory: true,
              cacheCompression: false,
              plugins: [
                // "@babel/plugin-transform-runtime" // presets中包含了
              ],
            },
          },
        ],
      },
    ],
  },
  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 MiniCssExtractPlugin({
      filename: "static/css/[name].[contenthash:10].css",
      chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
    }),
    // 将public下面的资源复制到dist目录去(除了index.html)
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, "../public"),
          to: path.resolve(__dirname, "../dist"),
          toType: "dir",
          noErrorOnMissing: true, // 不生成错误
          globOptions: {
            // 忽略文件
            ignore: ["**/index.html"],
          },
          info: {
            // 跳过terser压缩js
            minimized: true,
          },
        },
      ],
    }),
  ],
  optimization: {
    // 压缩的操作
    minimizer: [
      new CssMinimizerPlugin(),
      new TerserWebpackPlugin(),
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ["gifsicle", { interlaced: true }],
              ["jpegtran", { progressive: true }],
              ["optipng", { optimizationLevel: 5 }],
              [
                "svgo",
                {
                  plugins: [
                    "preset-default",
                    "prefixIds",
                    {
                      name: "sortAttrs",
                      params: {
                        xmlnsOrder: "alphabetical",
                      },
                    },
                  ],
                },
              ],
            ],
          },
        },
      }),
    ],
    splitChunks: {
      chunks: "all",
    },
    runtimeChunk: {
      name: (entrypoint) => `runtime~${entrypoint.name}`,
    },
  },
  resolve: {
    extensions: [".jsx", ".js", ".json"],
  },
  mode: "production",
  devtool: "source-map",
};