Webパックの仕上げ

記事ディレクトリ

従来のモジュール開発と問題点

プロジェクトには多数のモジュールがあり、各モジュールは js ファイルであり、これらの js がプロジェクトに読み込まれると、多数の http リクエストが送信され、パフォーマンスに影響します。

<!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>我的网页</title>
    <!-- <style>
        body {
            background-color: grey;
        }
    </style> -->
    <!--  rel="stylesheet"必须写 -->
    <link href="./test.css" rel="stylesheet">
</head>
<body>
    <h1>我的网页</h1>
    <div id="app">
        <!-- <div>header</div>
        <div>content</div>
        <div>footer</div> -->
    </div>

    <!-- <script src="./test.js"></script> -->

    <script src="./header.js"></script>
    <script src="./content.js"></script>
    <script src="./footer.js"></script>
    <script src="./test.js"></script>
    <!-- 传统模块化开发 -->
    <!-- 多加载了js文件,发送了多次http请求 -->
    <!-- 变量的来源不是很确定  -->
    <!-- 加载顺序问题 -->

    <!-- 现在的模块化开发使用ES6的import export 使用模块 -->
    <!-- 但浏览器不能识别import export -->
    <!-- => webpack 解决以上问题 -->
</body>
</html>
body {
    
    
    background-color: rgb(212, 207, 207);
}
// alert('hello')
const app = document.getElementById('app')

const header = document.createElement('div')
header.innerText='header';
app.appendChild(header)

const content = document.createElement('div')
content.innerText='content';
app.appendChild(content)

const footer = document.createElement('div')
footer.innerText='footer';
app.appendChild(footer)

/***
 * 以上就是面向过程编程,所有的代码都写在一个js文件里面,
 * 对以后项目的维护,功能的扩展都不是很友好
 * 
 * => 面向对象编程
 * 
 */

new Header();
new Content();
new Footer();

//  header.js
function Header(){
    
    
    const header = document.createElement('div')
    header.innerText='header';
    app.appendChild(header)
}
// content.js
function Content(){
    
    
    const content = document.createElement('div')
    content.innerText='content';
    app.appendChild(content)
}
// footer.js
function Footer(){
    
    
    const footer = document.createElement('div')
    footer.innerText='footer';
    app.appendChild(footer) 
}

Webpack の入出力

https://webpack.docschina.org/

  • 基本的に、webpack は最新の JavaScript アプリケーション用の静的モジュール バンドル ツールです。
  • Webpack は、最初はデフォルトで js ファイルの導入のみをサポートします。
  • さて、css については、vue 形式も導入できますが、ローダーの設定が必要です。

エントリ -> npx webpack

  1. npx webpack index.js
    Webpack モジュールにパッケージ化のエントリ ファイルがindex.jsであることを伝え、パッケージ化を開始します。

  2. 構成エントリーファイルwebpack.config.js(デフォルトの構成名)

module.exports = {
    
    
  entry: './index.js',
};

コンソール上で直接実行可能npx webpack

  1. カスタム構成ファイルはwebpack.dev.config.js
    コンソールで直接実行可能npx webpack --config ./webpack.dev.config.js

出力

Output 属性は、作成するバンドルをどこに出力するか、およびこれらのファイルに名前を付ける方法を webpack に指示します。メイン出力ファイルのデフォルトは ./dist/main.js で、その他の生成されたファイルはデフォルトで ./dist フォルダーに配置されます。

構成で出力フィールドを指定することで、これらのプロセスを構成できます。

const path = require('path');
module.exports = {
    
    
  entry: './index.js',
  output: {
    
    
    path: path.resolve(__dirname, 'dist'),
    //在任何模块文件内部,可以使用__dirname变量获取当前模块文件所在目录的完整绝对路径。
    filename: 'my-first-webpack.bundle.js',
    publicPath: 'http://localhost:8080/' //打入引入js文件的域名
  },
};
  • main.js がデフォルトなのはなぜですか?
const path = require('path');
module.exports = {
    
    
  entry: {
    
    
      main:'./index.js',
    // entry中key值为打包后的名字
    // 可以有多个入口
  },
};

複数の入口の構成

const path = require('path');
module.exports = {
    
    
    entry: {
    
    
        main: './index.js',
        // entry中key值为打包后的名字
        // 可以有多个入口
        sub:'./index.js'
    },
    output: {
    
    
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
        // 输入两个文件main sub
    },
},
;

ブラウザキャッシュ

  • パフォーマンス
    https://webpack.docschina.org/configuration/performance/#root

  • パフォーマンスの最適化をオフにするためのヒント

module.exports = {
    
    
 //...
 performance:false,
 //关闭性能优化的相关内容,
//   发现npx weback 打包的一些报警信息没有了
};
  • ブラウザキャッシュ:
    毎回同じbundle.jsファイルがパッケージ化されて生成されるため、ブラウジング中にキャッシュがオンになっているとキャッシュから読み込まれて最新のファイルが更新されません。

  • 出力ファイル名は、生成されたコンテンツによって生成されたハッシュを使用します:
    webpack.config.js

module.exports = {
    
    
  //...
  output: {
    
    
    filename: '[name][contenthash:8].bundle.js',
  },
};

モード

モードを直接使用する

開発、運用、またはなしのいずれかを選択してモード パラメーターを設定すると、対応する環境に対して Webpack の組み込みの最適化を有効にすることができます。デフォルト値は実稼働です。

module.exports = {
    
    
  mode: 'development',
};
  • 開発モードでのパッケージ化によって生成された Bundle.js は圧縮されません

モードに応じた設定ファイルを使用する

さまざまなファイル構成

  • webpack.deb.js
  • webpack.prod.js
  • パックヘ.json
    {
          
          
        "scripts":{
          
          
            "build":"webpack --config ./build/webpack.prod.js",
            "dev":"webpack-dev-server --config ./build/webpack.dev.js",
            "dev:build":"webpack --config ./build/webpack.dev.js",
        }
    }
    

Webパックのマージ

  • Webpack マージをインストールする
  • webpack.base.js のパブリック設定部分はここに配置されます
const path = require('path');
module.exports = {
    
    
    entry: {
    
    
        main: './index.js'
    },
    output: {
    
    
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
    },
},
;
  • 開発モードファイルと運用モードファイルのそれぞれの構成をマージします
const {
    
    merge}   = require('webpack-merge')

// 公共配置
const baseConfig = require('./webpack.base')
// 开发配置
const devConfig = module.exports = {
    
    
    "mode":"development",
    "devtool":"",
    "devServer":{
    
    },
    "plugins":[]
}

module.exports = merge(baseConfig,devConfig)

ローダーの基本的な使い方

  • webpack は JavaScript と JSON ファイルのみを理解できます。これは、すぐに使用できる webpack の組み込み機能です。
  • ローダーを使用すると、Webpack が他のタイプのファイルを処理し、有効なモジュールに変換できるようになります。

属性

Webpack 設定では、ローダーには 2 つの属性があります。

  • test 属性は、どのファイルが変換されるかを識別します。
  • use 属性は、変換を実行するときにどのローダーを使用するかを定義します。
const path = require('path');
module.exports = {
    
    
  output: {
    
    
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    
    
    rules: [{
    
     test: /\.txt$/, use: 'raw-loader' }],
  },
};

上記の構成では、rules 属性は別個のモジュール オブジェクトに対して定義されており、このオブジェクトには test と use という 2 つの必須属性が含まれています。これにより、Webpack コンパイラーに次のことが伝えられます。

「Webpack コンパイラ、require()/import ステートメントで '.txt' に解決されるパスを見つけたら、バンドルする前に raw-loader を使用して変換してください。」

https://webpack.docschina.org/loaders/ここにローダーをインストールします

画像処理用ローダー: file-loader、url-loader

  1. ファイルローダー
    https://v4.webpack.docschina.org/loaders/file-loader/
  • 画像形式の設定を処理します
module.exports = {
    
    
    module: {
    
    
        rules: [
            {
    
    
                test: /\.(png|jpg|gif)$/,
                use: [
                    {
    
    
                        loader: 'file-loader',
                        options: {
    
    
                            name: '[hash].[ext]',
                            // 生成的dist中图片名是原图片名(默认文件名是一个md5生成的)
                            // name配置项是配置打包生成的文件的名字,使用的是placeholder语法
                            // [name]表示的是原文件的名字;
                            // [hash]  表示的是这次打包的hash值   
                            // [ext]表示的是原文件的后缀;
                            outputPath: 'images',
                            // 生成的图片在dist/images/路径下
                        },
                    }
                ],
            },
        ],
    },
};
  • 使用
import testJpg from './resource/test.jpg';
console.log('name',testJpg)
const img = new Image();
img.src = testJpg;
const app = document.getElementsByTagName('div')[0]
app.appendChild(img);

npx パッケージ化コマンドを実行した後、dist でホームページを開くと、イメージが表示されます。

  1. URLローダーはbase64を返します

https://v4.webpack.docschina.org/loaders/url-loader/

ファイルをbase64 URIに変換するWebpack用のローダー。

  • url-loader は file-loader と同様に機能しますが、ファイルがバイト制限より小さい場合は DataURL を返すことができます。
module.exports = {
    
    
    module: {
    
    
        rules: [
            {
    
    
                test: /\.(png|jpg|gif)$/i,
                use: [
                    {
    
    
                        loader: 'url-loader',
                        options: {
    
    
                            limit: 8192
                            // 防止图片过大,限制大小
                        }
                    }
                ]
            }
        ]
    }
}
  1. URLローダー制限の包括的な使用
    • 画像が非常に大きい->URLローダー->base64文字列が非常に大きい->bundle.jsが非常に大きくなる->index.html Bundle.jsのロードに時間がかかる

    • 画像は非常に小さい -> URL ローダー -> 画像の Base64 は img の src 属性に直接設定される -> 追加の http リクエスト画像を送信する必要はありません

    • 画像は非常に小さい -> ファイルローダー -> 画像ファイルを別途生成 -> インデックスの導入 -> Bundle.js は非常に小さい -> ページ読み込みブロック -> もう一度 http リクエストを送信

    • 画像が非常に大きい -> ファイルローダー -> Bundle.js が非常に小さい -> 画像は別途リクエストされる

    module.exports = {
          
          
        module: {
          
          
            rules: [
                {
          
          
                    test: /\.(png|jpg|gif)$/i,
                    use: [
                        {
          
          
                            loader: 'url-loader',
                            options: {
          
          
                                name: 'dirname/[hash].[ext]',
                                // 生成的dist中图片名是原图片名(默认文件名是一个md生成的)
                                outputPath: 'images',
                                // 生成的图片在dist/images/路径下
                                limit: 8192
                                // 防止图片过大,限制大小
                            }
                        }
                    ]
                }
            ]
        }
    }
    

スタイルローダー: style-loader、css-loader

  • 簡単なスタイルはjsでローダーを操作することで処理可能
  • CSS ファイルをインポートする場合は、対応するローダーをインストールする必要があります
    1. スタイルローダー
    2. cssローダー
    • style-loader は css-loader と組み合わせて使用​​され、順序を変更することはできません。
    • css-loader の機能は css ファイルをトランスコードすることであり、style-loader の機能はトランスコードされた css ファイルを対応するファイルに挿入することです。

    • パッケージ化によって生成されたスタイルは、index.html webpack.config.jsの style タグに挿入されます。
{
    
    
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        // use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], 
        uer: ['style-loader','css-loader']
      },
    ];
  }
}

ローダーの実行順序

右から左へ、下から上へ

サスローダー

// webpack.config.js
module.exports = {
    
    
    ...
    module: {
    
    
        rules: [{
    
    
            test: /\.scss$/,
            use: [
                "style-loader", // 将 JS 字符串生成为 style 节点
                "css-loader", // 将 CSS 转化成 CommonJS 模块
                "sass-loader" // 将 Sass 编译成 CSS,默认使用 Node Sass
            ]
        }]
    }
};
  • importLoaders
    オプションを使用すると、css-loader の前に @imported リソースに適用されるローダーの数を構成できます。
// webpack.config.js
{
    
    
  test: /\.scss/,
  use: [
    {
    
     loader: 'style-loader'},
    {
    
     loader: 'css-loader',
      options: {
    
    
            importLoaders: 2 //如果css中import scss文件 应用2个层级的loader
        },
    },
    {
    
     loader: 'postcss-loader' },
    {
    
     loader: 'sass-loader'}
  ]
}

postcss-loader+autoprefix スタイルプレフィックスを設定する

https://v4.webpack.docschina.org/loaders/postcss-loader/#sourcemap

  • webpack.config.js はローダーを設定します
{
    
    
  test: /\.css/,
  use: [
    {
    
     loader: 'style-loader', options: {
    
     sourceMap: true } },
    {
    
     loader: 'css-loader', options: {
    
     sourceMap: true } },
    {
    
     loader: 'postcss-loader', options: {
    
     sourceMap: true } },
    {
    
     loader: 'sass-loader', options: {
    
     sourceMap: true } }
  ]
}
  • postcss-loader プラグインの自動プレフィックスは、CSS スタイルにベンダープレフィックスを追加できます (ターゲットブラウザ範囲も設定する必要があります)
    https://v4.webpack.docschina.org/loaders/postcss-loader/#autoprefixing
    https://stackoverflow .com /questions/49782806/webpack-4-postcss-loader-and-autoprefixer-plugin
  1. webpack.config.js は自動プレフィクサーを読み込みます
    {
          
          
    test: /\.css$/,
    use: [
            {
          
          
            loader: 'postcss-loader',
            options: {
          
          
                    plugins: () => [require('autoprefixer')({
          
          
                        'browsers': ['> 1%', 'last 2 versions']
                    })],
                }
            },
        ]
    }
    

または

  1. postcss.config.js は自動プレフィクサーを読み込みます
    module.exports = ({
           
            file, options, env }) => ({
          
          
    //   parser: file.extname === '.sss' ? 'sugarss' : false,
    plugins: {
          
          
        // require('autoprefixer')({...options}),
        // require('autoprefixer')({
          
          
        //     // 也可以在这设置目标浏览器
        //     browsers : ['last 100 versions'] 
        // })
        require('autoprefixer')({
          
          
            'browsers': ['> 1%', 'last 2 versions']
        })
    }
    })
    
  2. ターゲット ブラウザの範囲を設定する
    https://github.com/browserslist/browserslist
    以下を追加すると、すべてのツールがターゲット ブラウザを自動的に見つけます。
    • パッケージ.json:
      "browserslist":[
          "> %1",
          "last 2 versions"
      ]
      
    • または、.browserslistrc 設定で次のようにします。

CSS のモジュール性

CSS モジュール性を実装する方法について説明する前に、モジュール性を使用してどのような問題を解決する必要があるかについて説明しましょう。

  1. スタイルの私物化
  2. 他のスタイルファイルによる汚染を避ける
  3. 再利用可能

https://github.com/css-modules/css-modules

https://webpack.docschina.org/loaders/css-loader/#modules

{
    
    
    test: /\.css$/i,
    loader: "css-loader",
    options: {
    
    
        modules: true,
    },
}
  • モジュール性の原則: 現在の CSS セレクターにサフィックスを追加します。
/* components/Button.css */
.normal {
    
     /* normal 相关的所有样式 */ }
.disabled {
    
     /* disabled 相关的所有样式 */ }
/* components/Button.js */
import styles from './Button.css';
console.log(styles);
buttonElem.outerHTML = `<button class=${
      
      styles.normal}>Submit</button>`

生成されたHTMLは、

<button class="button--normal-abc53"> Processing... </button>

印刷結果

Object {
    
    
  normal: 'button--normal-abc546',
  disabled: 'button--disabled-def884',
}

CSS モジュールは CSS 内のすべてのクラス名を処理し、オブジェクトを使用して元のクラスと難読化されたクラスの間の対応関係を保存します。

プラグインプラグイン

https://v4.webpack.docschina.org/plugins/
https://webpack.js.org/awesome-webpack/

html-webpack-プラグイン

https://v4.webpack.docschina.org/plugins/html-webpack-plugin/

var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
module.exports = {
    
    
  entry: 'index.js',
  output: {
    
    
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
//   插件都是实例化使用
  plugins: [new HtmlWebpackPlugin()]
};

これにより、次の内容を含むファイル dist/index.html が生成されます。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>webpack App</title>
  </head>
  <body>
    <script src="index_bundle.js"></script>
  </body>
</html>
  • 以前は、dist/index.html を自分たちで手動で作成し、パッケージ化された dist/bundle.js を導入していました。
  • html-webpack-plugin プラグインを使用すると、webpack がパッケージ化された後にindex.html が作成され、bundle.js が自動的に導入されます。
  • ただし、webpack によって作成された htm ファイルには、bundle.js が導入されているだけで、他の内容は含まれていません。
  • したがって、プラグイン用に他のコンテンツを設定する必要があります
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');

module.exports = {
    
    
  entry: 'index.js',
  output: {
    
    
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  plugins: [new HtmlWebpackPlugin({
    
    
      template:'./src/index.html'
      //配置dist/html模板
  })]
};

Webpack でよく使用されるプラグイン

  • CleanWebpackPlugin は、
    パッケージ化する前に、前のプロジェクトによって生成されたバンドル ファイルをクリーンアップするために使用されます。デフォルトでは、このプラグインは webpack の Output.Path ディレクトリ内のすべてのファイルを削除します。

開発ツール->ソースマップ

https://v4.webpack.docschina.org/configuration/devtool/#devtool

  • devtool -> パラメータ:文字列 false
  • デバッグ プロセスを強化するには、ソース マップ形式を選択します。値が異なると、ビルドとリビルドの速度に大きな影響を与えます。
    webpack.config.js
const path = require('path');
module.exports = {
    
    
    mode:'development',
    devtool:'none',
    entry: {
    
    
        main: './index.js',
    },
    output: {
    
    
        path: path.resolve(__dirname, 'dist'),
    },
}

インデックス.js

console.logg('hello')
// 这行代码webpack打包的时候不会报错,打开网页后浏览器报错,问题定位到dist/main.js
  1. devtool:'none'、Web ページがエラーを報告すると、ブラウザーはパッケージング速度が最も速い dist/main.js ファイル内でエラーを見つけます。
  2. devtool:'eval-source-map'、ブラウザは元のソース コード ファイル内のエラーを見つけます。
  3. devtool:'source-map'、オリジナルのソース コード、ソース コードとパッケージ化されたコード間の関係のマップ ファイルを生成します
  • ベストプラクティス
    1. 開発環境cheap -module-eval-source-map eval-cheap-module-source-map
    2. 本番環境の安価なモジュールのソースマップ
  • 安価: 質問がどの行にあるかをプロンプトしますが、列をプロンプトしません。
  • module:cheap はサードパーティのモジュールとローダーを無視します。モジュールを追加する場合は、プロンプトが表示されます。
  • eval: ソースコードとパッケージ化されたコードのマッピングファイルは生成されませんが、main.js に配置されます

webpack-dev-server

  • ソース コードを変更するたびに、再実行npx webpackまたはnpm run buildビルドしてパッケージ化し、ページを更新して変更されたページを表示する必要があります。
  • webpack-dev-server はこのプロセスを最適化できます
  • webpack-dev-server サーバーは、node+express を使用して作成されます。

https://v4.webpack.js.org/configuration/dev-server/

  1. インストールnpm install webpack-dev-server --save-dev
  2. 設定webpack.config.js
    var path = require('path');
    
    module.exports = {
          
          
        //...
        devServer: {
          
          
            // contentBase: path.join(__dirname, 'dist'),
            // webpack5中contentBase已经失效了
            static: {
          
          
                directory: path.join(__dirname, 'dist'),
            },
            compress: true,//压缩
            port: 9000,
            open: true //告诉 dev-server 在 server 启动后打开浏览器。默认禁用。
            // proxy 可以处理跨域问题
        },
    };
    
  3. 構成パッケージ.json
    "scripts": {
          
          
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack",
        "dev" : "webpack-dev-server"
    },
    
  4. npm run dev

devServer.proxy

  • 別のバックエンド開発サーバー API があり、同じドメイン名で API リクエストを送信したい場合は、特定の URL をプロキシすると便利です。
  • クロスドメインの問題は通常、リクエストの転送によって解決されます。
  • 開発環境のみに適用されます
    。localhost:3000 にバックエンド サービスがある場合は、次のようにプロキシを有効にできます。

webpack.config.js

module.exports = {
    
    
    //...
    devServer: {
    
    
        // 1.
        // proxy: {
    
    
        //     '/api': 'http://localhost:3000'
        // },
        // 2.
        // proxy: {
    
    
        //     '/api': {
    
    
        //         target: 'http://localhost:3000'
        //     }
        // }
        proxy: {
    
    
            '/api': {
    
    
                // 一旦devServer(5000)服务器接收到 /api/xxx 的请求,就会把请求转发到另一个服务器(3000)
                // 浏览器和服务器之间有跨域,但是服务器和服务器之间没有跨域
                target: 'http://thirdserver.com:3000',
                changeOrigin: true,
                pathRewrite: {
    
     
                    '^/api': '' 
                    // 所有以/api开头的请求,把/api转换为空字符串
                    // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api
                },
                secure: false,
                //默认情况下,不接受运行在 HTTPS 上,且使用了无效证书的后端服务器。
                // 如果你想要接受,修改配置如下:
            }
        }
    }
};

/api/users へのリクエストは http://localhost:3000/api/users へのリクエストにプロキシされるようになります。

webpack VS webpack-dev-server ビルドの違い

"scripts":{
    
    
    "build":"webpack", // 打包后文件在dist/main.js可以找到
    "dev":"webpack-dev-server" //打包后文件在内存中
}

HotModuleReplacementPlugin ホット アップデート プラグイン

  1. HMR ホット アップデートをご紹介します
    • 入力入力ボックスにテキストを入力します。ソース コードを変更すると、Web ページが自動的に更新され、再度リクエストされ、ステータスが失われます。
    • ソース コードを変更して Web ページを自動的に更新することは、ホット デプロイメントであり、ホット アップデートではありません。
  2. 解決策: HotModuleReplacementPlugin プラグイン
    https://webpack.docschina.org/plugins/hot-module-replacement-plugin/
    https://webpack.js.org/guides/hot-module-replacement/#enabling-hmr
    :: :warning
    HMR は運用環境では決して使用しないでください。
    :::
const webpack = require('webpack');
// ........
module.exports = {
    
    
    plugins:[new webpack.HotModuleReplacementPlugin()]
    // webpack内置插件
}

https://stackoverflow.com/questions/69102254/webpack-options-has-an-unknown-property-hotonly-invalid-options-object-dev-s

最新バージョンでは、hot: true を設定すると HotModuleReplacementPlugin プラグインが自動的に適用されるため、hot: true/hot: “only” の場合は、プラグインに HotModuleReplacementPlugin が含まれていないことを確認してください。「[webpack-dev-server] “hot: true” は HMR プラグインを自動的に適用します。webpack 設定に手動で追加する必要はありません。」という警告が表示されます。上記の設定がある場合。

  • style-loader はホット アップデートを自動的に設定します。CSS が変更されると、スタイル自体も変更されます。react および vue フレームワーク自体も設定されます。カスタマイズする必要がある場合は、
if(module.hot){
    
        //在你要监听的文件里面写
    console.log('hot')
    // './print.js'
    // Ignored an update to unaccepted module ./src/print.js -> ./src/index.js
    // accept第一个参数必须是当前依赖的模块路径
    module.hot.accept('./print.js',()=>{
    
        // ./src值得是更新文件路径
        console.log('代码热更新,写自己的代码控制')   
        const oldSpan = document.getElementsByTagName('span')[0];
        oldSpan.innerText=`HRP-sp${
      
      print.a}`;  
    })
}

バベル→ES6

https://www.babeljs.cn/docs/index.html

  • Babel は JavaScript コンパイラーです

Babel は、主に ECMAScript 2015 以降の構文で記述されたコードを下位互換性のある JavaScript 構文に変換し、現在および古いバージョンのブラウザーまたはその他の環境で実行できるようにするために使用されるツール チェーンです。Babel ができることを以下に示します。

  1. 文法変換
  2. Polyfill を介してターゲット環境に不足している機能を追加します (core-js などのサードパーティの Polyfill モジュールを導入することによって)
  3. ソースコード変換 (codemod)
  • preset-env は
    https://www.babeljs.cn/setup#installation
    https://webpack.js.org/loaders/babel-loader/
    https://babel.docschina.org/docs/en/next/configurationを使用します/
  1. npm install -D babel-loader @babel/core @babel/preset-env

  2. 構成

    module: {
          
          
    rules: [
        {
          
          
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,//排除第三方代码
        use: {
          
          
            loader: 'babel-loader',
            options: {
          
          
                presets: ['@babel/preset-env']
            }
         }
        }
      ]
    }
    
  3. babel.config.json
    https://babel.docschina.org/docs/en/next/usage/

木の揺れ

  • Tree Shaking は ES Modle のみをサポートし、CommonJs はサポートしません
  • ES Modle→静的導入、コンパイル時にインポート・エクスポート
  • CommonJS->動的導入、実行中に module.exports が導入されることが必要
const flag = true;
if(flag){
    
    
    const {
    
    add} = require('./math')
}

if(flag){
    
    
    import {
    
    add} from ('./math')
    // 这种动态引入会报错
    // import and export may only appear at the top level 
}

Tree Shaking とは、モジュールを導入するときに、そのモジュールのすべてのコードを導入するのではなく、必要なコードのみを導入することを意味します。これを実現するには、webpack に付属する Tree Shaking 機能の助けが必要です。Tree Shaking は ES Module(import…) のみをサポートし、require… はサポートしません。

  • https://developer.mozilla.org/zh-CN/docs/Glossary/Tree_shaking

  • https://webpack.js.org/guides/tree-shaking/

  • 開発モードでツリーシェイキングを設定する場合:

    mode:'development',
    optimization: {
          
          
        usedExports: true,
    },
    
    • 開発モードでは、デバッグの便宜のために使用されていないコードは削除されず、参照されていないメソッドのみに注釈が付けられます。
  • 運用モードでは、webpack.config.js で設定する必要はありません。usedExports はデフォルトで自動的に有効になります。

  • Tree Shaking が有効にならない場合は、ソースマップを確認してください。

副作用フリー(副作用がない)

  • 戻り値に加えて、関数の外部(ディスプレイ、ネットワーク、ファイル、グローバル変数など)に影響を与える効果は副作用と呼ばれます。

このメソッドは、package.json の「sideEffects」属性を通じて実装されます。

{
    
    
  "name": "your-project",
  "sideEffects": false
}

すべてのコードに副作用が含まれていない場合は、この属性を false としてマークするだけで、未使用のエクスポートを安全に削除できることを webpack に伝えることができます。


Tree Shaking は未使用のエクスポート関数を削除するため、ポリフィルはウィンドウ上に定義されておりエクスポートがないため、削除されます。副作用のチェックを除外するように副作用を設定します。:::tip "副作用 (副作用
)
" 定義1 つまたは複数のエクスポートを公開するだけではなく、インポート時に特別な動作を実行するコードです。たとえば、ポリフィルを例に挙げます。これはグローバル スコープに影響し、通常はエクスポートを提供しません。
:::

{
    
    
  "name": "your-project",
  "sideEffects": ["@babeli/polyfill","*.css"]
}

コード分​​割

https://webpack.docschina.org/guides/code-splitting/

  • コードは通常、ビジネス ロジック コードとその他のツールキット コードに分かれており、すべて main.js にパッケージ化されており、ファイルは非常に大きくなります。
  • ビジネス ロジック コードが変更され、他のツール コードが再パッケージ化されると、ブラウザーは大きな main.js を再ダウンロードします。
    • src/index.js
    import _ form 'lodash'
    console.log('hello world!');//每次修改业务逻辑,webpack打包的时候也会将lodash一块重新打包到bundle.js
    const result = _.join(['test1','test2','test3'],'-');
    console.log('result',result)
    
  • 解決 -> コード分離 -> 複数のエントリの設定

手動ソリューション -> 複数の入口と出口を構成する

  • エントリの開始点: エントリ構成を使用してコードを手動で分割します。
  • ソース/インデックス、js
    console.log('hello world!');
    const result = _.join(['test1','test2','test3'],'-');
    console.log('result',result)
    
  • src/lodash.js
    import _ form 'lodash'
    window._= _
    
  • webpack.config.js
    module.exports = {
          
          
        entry:{
          
          
            main:'./src/index.js',
            lodash:'./src/lodash.js'
            //打包生成 main.js和lodash.js并引入到index.html中
        },
        output: {
          
          
            path: path.resolve(__dirname, 'dist'),
            filename: '[name].js',
        }
    },
    }
    

SplitChunksPlugin プラグイン ソリューション

  • SplitChunksPlugin は重複を削除し、チャンクを分離します。
    https://webpack.docschina.org/plugins/split-chunks-plugin/

  • src/index.js

import _ form 'lodash'
console.log('hello world!');//每次修改业务逻辑,webpack打包的时候也会将lodash一块重新打包到bundle.js
const result = _.join(['test1','test2','test3'],'-');
console.log('result',result)
  • webpack が SplitChunk を設定する
module.exports = {
    
    
  //...
  optimization: {
    
    
    splitChunks: {
    
    
        chunks:'all'
  },
};

遅延読み込みをインポート -> コード分離

https://webpack.docschina.org/guides/lazy-loading/#root

// Note that because a network request is involved, some indication
// of loading would need to be shown in a production-level site/app.
button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
    
    
    const print = module.default;
// 注意当调用 ES6 模块的 import() 方法(引入模块)时,必须指向模块的 .default 值,因为它才是 promise 被处理后返回的实际的 module 对象。
    print();
});
  • インポートの遅延読み込みコード分離も SplitChunksPlugin を通じて構成されます。

splitChunks.chunks:これは、最適化のためにどのチャンクが選択されるかを示します。文字列を指定する場合、有効な値は all、async、initial です。すべてを提供すると、非同期チャンクと非同期チャンク間でもチャンクを共有できるため、特に強力になります。

モジュールチャンクバンドルについて

[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムが備わっている可能性があります。画像を保存して直接アップロードすることをお勧めします (img-jtOQyPHA-1654528404640)(https://gitee.com/jingyu7/pic/) raw/master/202201061256177.png )]

  • モジュール、チャンク、およびバンドルは、実際には、異なる変換シナリオで 3 つの名前を持つ同じ論理コードです。
  • 私たちが直接記述するのはモジュールであり、これは webpack によってチャンクとして処理され、最終的にブラウザーが直接実行できるバンドルを生成します。

SplitChunksPlugin の一般的な構成

  • splitChunks.chunks
    この構成オブジェクトは、SplitChunksPlugin のデフォルトの動作を表します。
module.exports = {
    
    
  //...
  optimization: {
    
    
    splitChunks: {
    
    
      chunks: 'async',//默认只有异步代码进行代码分隔 all, async, and initial
      minSize: 20000, //如果引入的包>20000KB就进行代码分隔
      minRemainingSize: 0,//通过确保拆分后剩余的最小 chunk 体积
      minChunks: 1, //拆分前必须共享模块的最小 chunks 数。
    // 这个配置表示split前单个非按需导入的module的并行数的最低下限
    // 注:只对import或require直接引入的module有效。
    // 1. minChunks设置为n
    // 2. 假设有m个入口点,这m个入口点都直接引入了某个模块module(通过import或require直接或间接地引入了模块),也就是共享次数为m
    // 3. 当m至少等于n时,module才会被单独拆分成一个bundle

      maxAsyncRequests: 30,//按需加载时的最大并行请求数。
      maxInitialRequests: 30,//入口点的最大并行请求数
      enforceSizeThreshold: 50000,//强制执行拆分的体积阈值和其他限制
      cacheGroups: {
    
     //缓存组
    //   对于缓存组是一个对象,处了可以有上面的chunks、minSize、minChunks、maxAsyncRequests、maxInitialRequests、name外,还有其他的一些参数:
    // 如果不在缓存组中重新赋值,缓存组默认继承上面的选项,但是还有一些参数是必须在缓存组进行配置的
    // 缓存组的概念理解
    // 源代码中引入了lodash jquery 都分别分隔到了对应的js
    // 假如希望它两合并后分隔
    // 当分析到引入lodash时缓存到defaultVendors,分析到jquery满足规则也缓存起来
    // 当所有模块到分析结束后,再把defaultVendors组一起分隔
        defaultVendors: {
    
    
          test: /[\\/]node_modules[\\/]/, //捕获模块规则
          priority: -10, //优先级
          reuseExistingChunk: true,
          //如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
          //假如源代码中不同文件中都引入lodash模块,
          //在第一次引入分析后被当前缓存组缓存,后面的此模块的引入分析复用之前缓存结果
        },
        default: {
    
    
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

CSSコード分割MiniCssExtractPlugin

このプラグインは、CSS を個別のファイルに抽出し、CSS を含む JS ファイルごとに CSS ファイルを作成し、CSS と SourceMaps のオンデマンド読み込みをサポートします

  • 以前は、CSS スタイル ファイルは styleLoader によって処理およびパッケージ化され、main.js に配置され、js の css に配置され、最後に head のスタイルに配置されました。
  • 通常、実稼働環境で使用されます
  • mini-css-extract - plugin
    https://webpack.docschina.org/plugins/mini-css-extract-plugin/webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
    
    
  plugins: [new MiniCssExtractPlugin()],
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};

Shimming プリセットは ProvidePlugin に依存します

どこでもモジュールをインポートしたり要求したりする代わりに、モジュールを自動的にロードします。

  • グローバル構成プラグインに使用でき、古いコードの jquery 互換性にも使用できます。
  • 文法
new webpack.ProvidePlugin({
    
    
  identifier: 'module1',
  // ...
});
  • 使用法: jQuery jquery を
    自動的にロードするには、jquery が公開する両方の変数を対応するノード モジュールにポイントします。
const path = require('path');
const webpack = require('webpack');
 module.exports = {
    
    
   entry: './src/index.js',
   output: {
    
    
     filename: 'main.js',
     path: path.resolve(__dirname, 'dist'),
   },
  plugins: [
    new webpack.ProvidePlugin({
    
    
        $: 'jquery',
        jQuery: 'jquery',
        // webpack为我们提供了ProvidePlugin的插件,上面配置的意思是,当我们在模块中遇到‘$’就会去自动的引入jquery
    });
  ],
 };

次に、ソース コードのいずれかで次のようにします。

// in a module
$('#item'); // <= works
jQuery('#item'); // <= also works
// $ is automatically set to the exports of module "jquery"

バージョン4とバージョン5の違い

パフォーマンスの最適化

最適化には 2 つの方向性があります

  1. パッケージのビルド速度
  2. 出力コードサイズ

babel-loader-exclude、キャッシュディレクトリ

  • 主な目的は、js のパッケージ化とビルドの速度を向上させることであり、1 つ目はキャッシュ、2 つ目は除外です。
  • CSSローダーはキャッシュを構築しません
  1. 除外する、含める

    exclude:path.resolve(__dirname,'node_modules')
    
    • 質問

      • Webpack で babel-loader を設定するときに、node_modules フォルダーを除外する必要があるのはなぜですか?
      • このフォルダー内の js モジュールもアロー関数と同様の ES6 構文を持っている場合はどうなるでしょうか?
    • このフォルダーを除外しない場合、独自のコード内で参照している他のツール ライブラリのコードも Babel によって読み取られて再コンパイルされるため、JavaScript コードのコンパイルは遅くなります。多くの場合、独自のビジネス コードのみが考慮されます。 10%ですが、コンパイル後に参照される他のツール ライブラリのコードが 80 ~ 90% を占める可能性があり、独自のビジネス コードのコンパイルに 10 秒かかる場合、プロジェクト全体のコードには 100 秒かかることがわかります。速度が非常に遅いため、開発効率の向上にはつながりません。一方、現在npm上で公開されているツールライブラリにはソースコードとコンパイル済みコードが含まれているのが一般的ですが、同時にコンパイル済みコードがデフォルトで参照されるため、コンパイル済みコードを再度コンパイルすることに相当し、実際には何の効果もありません。時間の無駄。したがって、構成するときは、通常、node_modules 内のツール ライブラリ コードを babel のコンパイル スコープから除外します。

    • es5/es3 構文にコンパイルする必要がある npm のパッケージに関する明確な仕様はありませんが、現状では、ほとんどの公開パッケージがこの仕様にコンパイルされているため、すでに事実上の標準となっています。

    • node_modules 内の一部のパッケージに es6 構文がある場合はどうなりますか?

    回答: 正規表現を使用して除外されないように、node_modules 内の特定のパッケージを指定します。

  2. Babel キャッシュ: babel-loader のオプションで、cacheDirectory: true を開き、
    コンパイルされた互換性コードをキャッシュに置きます。次回コンパイルするときにキャッシュにある場合は、コンパイルされたコードが直接使用されます。これは js に対してのみ有効
    です。

    module:{
          
          
        rules:[
            test: /\,js$/,
            options:{
          
          
                cacheDirectiry:true
            },
            inclued:srcPath,
            exclude:path.resolve(__dirname,'node_modules')
        ]
    }
    

webpack.IgnorePlugin によるモーメントの処理

Webpack の IgnorePlugin は、パッケージ化時にローカライズされたコンテンツを無視します。たとえば、プラグインが導入され、中国語のパッケージのみが使用されている場合、パッケージ化時に中国語以外の言語のパッケージは除外されます。

プロジェクトでよく使われる moment ライブラリの処理時間ですが、moment 導入後、プロジェクトのビルドファイルのサイズが明らかに大きくなっていることに気づきましたか?その原因と最適化方法を分析してみましょう。

//
import moment from 'moment'
// 引入中文
import 'moment/locale/zh-cn'
// 设置中文
moment.locale('zh-cn');
let momentStr = moment().subtract(10, 'days').calendar();
console.log('现在时间:', momentStr);

インターネットでいろいろ検索した結果、パッケージ化中にすべてのロケールがパッケージ化されることがわかりました。最初の解決策は、webpack.IgnorePlugin を使用してこれを処理することです。
IgnorePlugin は何をしましたか?

  • IgnorePlugin の原則分析
    IgnorePlugin の原則は、開発中にまったく使用しないことが多いため、その瞬間のすべてのローカル ファイルを削除することです。このプラグインの使用方法は次のとおりです。
...
plugins: [
    new webpack.IgnorePlugin(/^./locale$/, /moment$/) // 配置忽略规则
    // 忽略moment库中的locale文件夹啊
]

すべてのローカル ファイルが削除されている場合はどうすればよいかという疑問が生じるかもしれませんが、そのうちの 1 つを使用したいと考えています。心配しないでください。次のようにコード内で使用できます。

const moment = require('moment');
require('moment/locale/ja');//如果需要请单独引用
moment.locale('ja');

https://github.com/zhanzizhen/how-to-optimize-momentjs-with-webpack/blob/master/README.md

noParse 構成

一般に、.min.js で終わるファイルはモジュール化されているため、現時点では loder または webpack 分析を実行する必要はなく、noParer の文字通りの意味は解析されなくなります。

module: {
    
    
    noParse: /jquery/, // 不解析模块中的依赖关系 提高打包效率
    rules: []
}
  • ignorePlugin と noParse の比較

    1. IgnorePlugin は、一致条件を満たすモジュールを直接インポートし、コードには含めません。
    2. noParse は引き続き導入されますが、ローダーまたは Webpack の解析とパッケージ化には関与しません。
  • noParse はパッケージングのボリュームを変更せず、ビルド速度を高速化するだけです。

happPack マルチプロセスパッケージング

JavaScript はシングルスレッド モデルであるため、マルチコア CPU の機能を活用したい場合は、マルチ プロセスではなくマルチ スレッドでのみ実現できます。
Node.js 上で実行される Webpack はシングルスレッド モデルであるため、Webpack が処理する必要があるタスクは 1 つずつ実行する必要があります。Happypack の役割は、ファイル解析タスクを複数のサブプロセスに分解して同時実行することです。子プロセスがタスクを完了すると、結果がメインプロセスに送信されます。したがって、Webpack のプロジェクト構築速度が大幅に向上します。

watch 和 watchOptions

Webpack は、ファイルの変更を監視し、変更されたときに再コンパイルできます。
ウォッチモードを有効にします。これは、最初のビルド後、webpack は解析されたファイルへの変更をリッスンし続けることを意味します。

module.exports = {
    
    
  //...
  watch: true,
};

webpack-dev-server および webpack-dev-middleware では、監視モードがデフォルトで有効になっています。

  • watchOptions は、監視モードをカスタマイズするために使用されるオプションのセットです。
module.exports = {
    
    
  //...
  watchOptions: {
    
    
    ignored: /node_modules/,
    //监听大量文件会导致大量的 CPU 或内存占用。可以使用正则排除像 node_modules 如此庞大的文件夹:
    aggregateTimeout: 200,//批量更新
    // 当第一个文件更改,会在重新构建前增加延迟。这个选项允许 webpack 将这段时间内进行的任何其他更改都聚合到一次重新构建里。以毫秒为单位:
  },
};

DLLプラグイン

DllPlugin、DllReferencePluginは、ビルドに時間がかかるよく使うコード(react、react-domなど)を事前にパッケージ化し、dllという名前を付けるものです。後でパッケージ化する場合は、パッケージ化されていない元のコードをスキップして、dll を直接使用します。このようにして、ビルド時間が短縮され、Webpack のパッケージ化速度が向上します。

  • ただのキャッシュなのでしょうか?空間を時間と引き換えにしましょう。

  • autodll-webpack-plugin もクラス プラグインであり、以前は vue-cli で使用されていました。

  • webpack 4 は dll よりも優れたパッケージング パフォーマンスを備えています。そのため、autodll-webpack-plugin は使用されなくなりました

後で autodll-webpack-plugin を見つけて、その dll が放棄されていたことを知ったとき、私は正直少しがっかりしました。それ。しかし、dll の原理をよく考えてみると、空間と時間を交換するだけで、構成はもう少し複雑であることがわかりました。
したがって、これは、新しい知識を学ぶときに、プロセスの構成やパラメーターの調整に焦点を当ててはいけないことも思い出させます。なぜなら、プロセスは最終的に簡素化され、パラメータ(API)は最終的にアップグレードされるからです。大きなことに集中して小さなことは手放し、核となるコンテンツに集中する必要があります。核となるアイデアが最も時代遅れになる可能性が低いからです。

おすすめ

転載: blog.csdn.net/uotail/article/details/125156446