天天看點

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",
};
           

繼續閱讀