{"id":26684222,"url":"https://github.com/johnapache/webpack-usage-doc","last_synced_at":"2026-05-04T06:33:05.592Z","repository":{"id":39354238,"uuid":"214601774","full_name":"JohnApache/webpack-usage-doc","owner":"JohnApache","description":"webpack完整使用教程","archived":false,"fork":false,"pushed_at":"2022-12-11T09:08:55.000Z","size":675,"stargazers_count":0,"open_issues_count":19,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-18T06:02:59.530Z","etag":null,"topics":["webpack","webpack-doc","webpack-usage-doc"],"latest_commit_sha":null,"homepage":null,"language":"CSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JohnApache.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-12T07:38:31.000Z","updated_at":"2019-10-16T09:46:56.000Z","dependencies_parsed_at":"2022-08-26T02:01:46.959Z","dependency_job_id":null,"html_url":"https://github.com/JohnApache/webpack-usage-doc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fwebpack-usage-doc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fwebpack-usage-doc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fwebpack-usage-doc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JohnApache%2Fwebpack-usage-doc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JohnApache","download_url":"https://codeload.github.com/JohnApache/webpack-usage-doc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245623077,"owners_count":20645681,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["webpack","webpack-doc","webpack-usage-doc"],"created_at":"2025-03-26T09:19:21.434Z","updated_at":"2026-05-04T06:33:05.550Z","avatar_url":"https://github.com/JohnApache.png","language":"CSS","funding_links":[],"categories":[],"sub_categories":[],"readme":"# webpack 完整使用教程\n\n\u003e 全文基于 webpack **v4.41.0**版本\n\n## 使用指南\n\n#### 安装\n安装webpack\n```shell\nnpm install webpack --save-dev \nyarn add webpack -D \n```\n如果你使用 webpack 4+ 版本，你还需要安装 CLI。\n```shell\nnpm install webpack-cli --save-dev \nyarn add webpack-cli -D \n```\n\n#### 基本使用\n入口文件 src/index.js\n```js\nimport _ from 'lodash';\nconst createHelloWorld = () =\u003e {\n    const element = document.createElement('div');\n\n    element.innerHTML = _.join(['Hello', 'webpack'], ' ');\n\n    return element;\n}\n\ndocument.getElementById('root').appendChild(createHelloWorld());\n```\n创建webpack配置文件 webpack.config.js\n```js\nconst path = require('path');\nmodule.exports = {\n  entry: './src/index.js',\n  output: {\n    filename: 'main.js',\n    path: path.resolve(__dirname, 'dist')\n  }\n};\n```\n执行打包命令\n```shell\nnpx webpack\n```\n可以看到 index.js 和 lodash.js 一起被打包到 dist 目录下的main.js 文件中了\n\n\n#### 管理资源\n正常 webpack 只可以加载普通的js 文件 如果需要加载 不同的文件，webpack 需要添加不同loader 来支持加载不同格式文件\n+ 加载css资源文件\n    为了能从js模块中加载 css 文件需要在webpack配置文件 的 moudule 配置中添加 style-loader 和 css-loader\n    ```shell\n    npm install style-loader css-loader --save-dev\n    yarn add style-loader css-loader -D\n    ```\n    webpack配置文件添加module 配置 和 rules 规则\n    ```js\n    const path = require('path');\n    module.exports = {\n        input: './src/index.js',\n        output: {\n            filename: 'main.js',\n            path: path.resolve(__dirname, './dist')\n        },\n        module: {\n            rules: [\n                {\n                    test: /\\.css$/, // 匹配规则\n                    use: [\n                        'style-loader',\n                        'css-loader' // style-loader 一定要放在css-loader 之前\n                    ]\n                }\n            ]\n        }\n    }\n    ```\n    这样就可以在js中使用 import './styles/index.css'加载css 了, css 会被注入到 index.html 顶部 style 标签样式内联\n    ```html\n    \u003chtml\u003e\n        \u003chead\u003e\n        ...\n        \u003cstyle\u003e\n        #root {\n            background-color: #000;\n            color: #fff\n        }\n        \u003c/style\u003e\n        \u003c/head\u003e\n    \u003c/html\u003e\n    ```\n    \u003e 请注意，在多数情况下，你也可以进行 CSS 分离，以便在生产环境中节省加载时间。最重要的是，现有的 loader 可以支持任何你可以想到的 CSS 处理器风格 - postcss, sass 和 less 等。\n    \n+ 加载图片和字体文件    \n    通过file-loader 可以 让 webpack 在js中加载图片 和字体文件，如果安装了 css-loader 和 html-loader 还可以在html 和 css 文件中加载 字体图标 和 图片\n    安装依赖\n    ```shell\n    yarn add file-loader -D\n    ```\n    修改配置\n    ```js\n    const path = require('path');\n    module.exports = {\n        input: './src/index.js',\n        output: {\n           filename: 'main.js',\n           path: path.resolve(__dirname, './dist')\n        }，\n        module: {\n            rules: [\n                {\n                   test: /\\.(png|jpg|gif|svg)$/,\n                   use: [\n                       'file-loader'\n                   ]\n                },\n                {\n                    test: /\\.(woff|otf|woff2|eot|ttf)/,\n                    use: [\n                        'file-loader'\n                    ]\n                }\n            ]\n        }\n    }\n    ```\n   \u003e url-loader 功能 和 file-loader 差不多，不过url-loader 可以将小图片 替换成base64格式的 url \n+ 加载数据资源\n    nodejs 内置支持加载 json 文件, 但是 xml, csv, tsv 等文件 需要添加 额外的loader配置 xml-loader csv-loader\n    安装依赖\n    ```shell\n    yarn add csv-loader xml-loader -D\n    ```\n    修改配置\n    ```js\n     const path = require('path');\n    module.exports = {\n        input: './src/index.js',\n        output: {\n           filename: 'main.js',\n           path: path.resolve(__dirname, './dist')\n        }，\n        module: {\n            rules: [\n                {\n                   test: /\\.xml$/,\n                   use: [\n                       'xml-loader'\n                   ]\n                },\n                {\n                    test: /\\.(csv|tsv)/,\n                    use: [\n                        'csv-loader'\n                    ]\n                }\n            ]\n        }\n    }\n    ```\n    \n#### 管理输出\n\n+ 多入口文件管理\n    通过修改 webpack.config.js 的 input 配置 可以添加webpack 多入口文件\n    ```js\n    const path = require('path');\n    module.exports = {\n        input: {\n            main: './src/index.js',\n            some: './src/some.js'\n        },\n        output: {\n            filename: '[name]-[hash].js',\n            path: path.resolve(__dirname, 'dist')\n        }\n    }\n    ```\n    \n+ HtmlWebpackPlguin 的使用\n    当有多入口文件管理的存在时，我们不得不，一直修改 index.html文件来引入 webpack 打包后的多个文件, 这可以使用 html-webpack-plguin 插件解决这个问题\n    安装依赖\n    ```shell\n    yarn add html-webpack-plugin -D\n    ```\n    修改配置\n    ```js\n    const path = require('path');\n    const HtmlWebpackPlugin = require('html-webpack-plugin');\n    module.exports = {\n        input: {\n            main: './src/index.js',\n            some: './src/some.js'\n        },\n        output: {\n            filename: '[name]-[hash].js',\n            path: path.resolve(__dirname, 'dist')\n        },\n        plugins: [\n            new HtmlWebpackPlugin({\n                title: 'Hello World' + new Date(),\n                template: './index.html',\n                hash: true\n            })\n        ]\n    }\n    ```\n    具体的 html-webpack-plugin 配置可以参考 [这里](https://www.npmjs.com/package/html-webpack-plugin#)\n    html-webpack-plugin 可以帮助我们生成一个 index.html，即使之前dist包含html 也会覆盖上去,动态生成的 html 会自动引入所需要的依赖\n\n+ CleanWebpackPlugin 的使用\n    webpack 每次构建时不会自动清理目标路径的 文件，现在可以使用 clean-webpack-plugin 来帮助我们自动清理文件\n    新版的 clean-webpack-plugin 不需要手动传入目标路径，它会自动根据上下文环境配置 读取到 输出目录 output.path, 并清空该目录\n    安装依赖\n    ```shell\n    yarn add clean-webpack-plugin -D\n    ```\n    修改配置,该插件 放在其他插件之前执行\n    ```js\n    const path = require('path');\n    const HtmlWebpackPlugin = require('html-webpack-plugin');\n    const {CleanWebpackPlugin} = reuqire('clean-webpack-plugin')\n    module.exports = {\n        input: {\n            main: './src/index.js',\n            some: './src/some.js'\n        },\n        output: {\n            filename: '[name]-[hash].js',\n            path: path.resolve(__dirname, 'dist')\n        },\n        plugins: [\n            new CleanWebpackPlugin(),\n            new HtmlWebpackPlugin({\n                title: 'Hello World' + new Date(),\n                template: './index.html',\n                hash: true\n            })\n        ]\n    }\n    ```\n    具体 CleanWebpackPlugin 的相关配置 可以 [在这里查看](https://www.npmjs.com/package/clean-webpack-plugin)\n    \n+ WebpackManifestPlugin 使用\n    有时候你可能会感兴趣，webpack及其插件似乎“知道”应该哪些文件生成。答案是，通过 manifest，webpack 能够对「你的模块映射到输出 bundle 的过程」保持追踪。\n    安装依赖\n     ```shell\n    yarn add webpack-manifest-plugin -D\n    ```\n    修改配置\n    ```js\n    const path = require('path');\n    const WebpackMainfestPlugin = require('webpack-manifest-plugin');\n    module.exports = {\n        input: {\n            main: './src/index.js',\n            some: './src/some.js'\n        },\n        output: {\n            filename: '[name]-[hash].js',\n            path: path.resolve(__dirname, 'dist')\n        },\n        plugins: [\n            new WebpackMainfestPlugin()\n        ]\n    }\n    ```\n    \n#### 构建开发环境\n\n+ 使用 sourcemap\n    配置devtool属性即可以 生成对应文件的sourcemap，具体 devtool的属性可选配置 可以[查看这里](https://webpack.js.org/configuration/devtool/)\n    ```js\n    module.exports = {\n        entry: './index.js',\n        output: {\n            filename: '[name]-[contenthash].js',\n            path: './dist'\n        },\n        devtool: 'source-map'\n    }\n    ```\n+ 使用观察模式\n    ```shell\n    webpack --watch \n    ```\n    webpack 将会监视所有依赖关系图中的文件，当文件发生改变 会自动触发重新构建，唯一的缺点是您必须刷新浏览器才能查看更改，这里使用 webpack-dev-server 可以帮助我们实现这一点\n\n+ 使用 webpack-dev-server\n    webpack-dev-server 提供了一个简单的Web服务器以及使用实时重新加载的功能\n    安装依赖\n    ```js\n    yarn add webpack-dev-server -D\n    ```\n    修改webpack.config.js配置\n    ```js\n    module.exports = {\n        entry: './src/index.js',\n        output: {\n            filename: '[name]-[contenthash].js',\n            path: './dist'\n        },\n        devtool: 'source-map',\n        devServer: {\n            contentBase: './dist',\n            port: 9000,\n            publicPath: \n        }\n    }\n    ```\n    \u003e webpack-dev-server编译后不写入任何输出文件。相反，它将捆绑文件保存在内存中，并像在服务器根路径上挂载的真实文件一样提供它们。如果您的页面希望在其他路径上找到捆绑文件，则可以使用**publicPath**开发服务器的配置中的选项进行更改。\n    \n    启动weback-dev-server, 会自动打开浏览器 并实时刷新\n    ```shell\n    webpack-dev-server --open\n    ```\n    \n+ 使用 webpack-dev-middleware\n    webpack-dev-middleware是一个包装程序，它将由webpack处理的文件发送到服务器。它在webpack-dev-server内部使用，但是如果需要，它可以作为单独的软件包使用，以允许进行更多自定义设置\n    koa 使用实例\n    ```js\n    import Koa from 'koa';\n    import webpack from 'webpack';\n    import webpackDevMiddleware from 'koa-webpack-dev-middleware';\n    import webpackConfig from './webpack.config.js';\n    \n    const compiler = webpack(webpackConfig);\n    \n    app.use(webpackDevMiddleware(compiler, {\n       host: 'localhost',\n       contentBase: './dist'\n    }))\n    \n    app.listen(9999, () =\u003e {\n        console.log('server run in port 9999!');\n    })\n    \n    ```\n\n#### 代码分割\n代码拆分是webpack最引人注目的功能之一。此功能使您可以将代码分成多个捆绑包，然后可以按需或并行加载。它可用于实现较小的捆绑包并控制资源负载优先级，如果正确使用，则会对负载时间产生重大影响。\n+ webpack提供三种方式实现代码分割\n    - 配置webpack.config.js 的 文件入口点 entry 配置, 配置多个入口手动拆分代码\n    - 防止重复，webpack 内部使用了 splitChunksPlugin 来删除重复引用的代码并将代码单独拆分\n    - 动态导入，通过模块内的内联函数调用拆分代码。\n\n+ entry 多入口文件拆分代码\n    这是拆分代码的最简单，最直观的方法\n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: 'development',\n      entry: {\n        index: './src/index.js',\n        another: './src/another-module.js',\n      },\n      output: {\n        filename: '[name].bundle.js',\n        path: path.resolve(__dirname, 'dist'),\n      },\n    };\n    ```\n    该方法存在一点问题，多个入口文件的 重复模块代码，会同时包含在每个入口文件里，而且这种方法不能用于通过核心应用程序逻辑动态拆分代码。\n\n+ 使用splitChunksPlugin 防止重复\n    splitChunksPlugin 允许我们将重复的依赖提取到一个现有的模块或一个全新的模块中，通过配置 optimization.splitChunks 使用该功能\n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: 'development',\n      entry: {\n        index: './src/index.js',\n        another: './src/another-module.js',\n      },\n      output: {\n        filename: '[name].bundle.js',\n        path: path.resolve(__dirname, 'dist'),\n      },\n      optimization: {\n        runtimeChunk: 'single',\n\n        splitChunks: {\n          chunks: 'all',\n          cacheGroups: {\n            vendor: {\n              test: /\\/node_modules\\//,\n              name: 'vendors',\n              chunks: 'all',\n            },\n          },\n        }\n      }\n    };\n    ```\n    optimization.splitChunks 的具体配置可以[查看这里](https://webpack.js.org/plugins/split-chunks-plugin/#optimizationsplitchunks)\n    \n+ 动态导入分割代码\n    有两种方法可让webpack动态分割代码，\n    1. import() 语法 (推荐)\n    2. require.ensure 传统方法\n    \n    import()呼叫在内部使用Promise。如果您使用import()较旧的浏览器，请记住Promise使用诸如es6-promise或promise- polyfill之类的polyfill进行填充。\n    注意还需要在output 配置上添加一个配置 **chunkFilename** ，他可以确定非输入模块文件的名称, 可以作用于动态导入单独分割的模块，不配置该属性，默认动态分割的模块名称是 0,1,2\n    ```js\n    const createElement = async () =\u003e {\n        const {default: _} = await import(/* webpackPreload: true */ 'lodash');\n        const element = document.createElement('div');\n        element.innerHTML = _.join(['Hello', 'webpack'], ' ');\n        return element;\n    }\n    \n    (async () =\u003e {\n        document.getElementById('root').appendChild(await createElement());\n    })()\n\n    ```\n    我们需要default的原因是，从webpack 4开始，当导入CommonJS模块时，导入将不再解析为的值module.exports\n    \n+ prefetch 和 preload 模块\n    webpack 4.6.0+增加了对预取和预加载的支持。\n    - prefetch: 浏览器通常会在空闲状态取得这些资源，在取得资源之后搁在HTTP缓存以便于实现将来的请求。如果有多个‘预请求提示’则会在浏览器空闲时排队执行。当浏览器离开空闲状态时正好在‘预请求’资源，那么浏览器会取消任何正在进行中的请求（同时会将部分响应数据放置在缓存中，而在Header中继续使用Content-Range字段 ）并停止处理‘预请求’队列。\n    在闲置时获取资源\n    - preload: 这种“资源提示” 告诉浏览器这是一种在这次导航中必须的资源，只是会在之后才会被使用， chrome甚至会在资源加载后3秒没有被使用时打印一个警告， 浏览器通常以中等优先级（非布局阻塞）获取此资源。\n    正常获取，及早发现\n    \n    Preload用于更早地发现资源并避免发起类似瀑布一样的请求。 它可以将页面加载降低到2次往返（1. HTML，2。所有其他资源）。 使用它不会花费额外的带宽。\n    prefetch用于使用浏览器的空闲时间来加速将来的导航。 当用户未执行预期的未来导航时，使用它可能会花费额外的带宽。\n    具体文档可以[查看这里](https://www.zcfy.cc/article/link-rel-prefetch-preload-in-webpack)\n    \n    prefetch实例\n    ```js\n    import(/* webpackPrefetch: true */ 'LoginModal');\n    ``` \n    这将导致将\u003clink rel=\"prefetch\" href=\"login-modal-chunk.js\"\u003e其附加在页面的开头，这将指示浏览器在空闲时间预取login-modal-chunk.js文件。\n    \n    preload实例\n    ```js\n    import(/* webpackPreload: true */ 'ChartingLibrary');\n    ```\n    请求charting-library-chunk 通过 \u003clink rel=\"preload\"\u003e\n    \n#### 缓存\n    \n+ 输出文件名\n    可以使用output.filename 替换设置来定义输出文件的名称，webpack提供了一种使用方括号括起来的字符串来替代文件名的方法.\n    \n    [contenthash] 表示的是资源文件内容唯一的哈希，当文件发生改变时 contenthash 就会发生变化,\n    [name] 表示资源文件名称\n    [hash] 随意哈希值\n    [chunkhash]\n    使用实例如下\n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: 'development',\n      entry: './src/index.js',\n      output: {\n        filename: '[name].[contenthash].js',\n        path: path.resolve(__dirname, 'dist'),\n      },\n    };\n    ```\n\n+ 提取样版\n    webpack提供了一种优化功能，可以使用该optimization.runtimeChunk选项将运行时代码分成单独的块。对其进行设置single以为所有块创建单个运行时捆绑包, 'single' 是 {name: 'runtime'} 的别名\n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: 'development',\n      entry: './src/index.js',\n      output: {\n        filename: '[name].[contenthash].js',\n        path: path.resolve(__dirname, 'dist'),\n      },\n      optimization: {\n        //runtimeChunk: {\n        //    name: 'runtime'\n        //}\n        runtimeChunk: 'single',\n      }\n    };\n    ```\n    optimization.runtimeChunk该配置的 详细使用说明 [查看这里](https://webpack.js.org/configuration/optimization/#optimizationruntimechunk)\n    \n    webpack还可以将第三方库（例如lodash或）提取react到单独的vendor块中，通过配置 optimization.splitChunks.cacheGroups 选项\n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: 'development',\n      entry: './src/index.js',\n      output: {\n        filename: '[name].[contenthash].js',\n        path: path.resolve(__dirname, 'dist'),\n      },\n      optimization: {\n        splitChunks: {\n            cacheGroups: {\n                vendor: {\n                    name: 'vendor',\n                    test: /\\/node_modules\\//,\n                    chunks: 'all'\n                }\n            }\n        }\n      }\n    };\n    ```\n    \n    模块标识符， webpack打包的时候，每个模块的module.id默认情况下，每个值都会根据解析顺序递增。意思是当解决顺序改变时，ID也将改变。因此，让我们使用optimization.moduleIds = 'hashed'选项，可以解决这个问题\n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: 'development',\n      entry: './src/index.js',\n      output: {\n        filename: '[name].[contenthash].js',\n        path: path.resolve(__dirname, 'dist'),\n      },\n      optimization: {\n        moduleIds: 'hashed',\n        splitChunks: {\n            cacheGroups: {\n                vendor: {\n                    name: 'vendor',\n                    test: /\\/node_modules\\//,\n                    chunks: 'all'\n                }\n            }\n        }\n      }\n    };\n    ```\n    \n#### 使用webpack 构建库\n+ 使用实例\n    ```js\n    const path = require('path');\n    const {CleanWebpackPlugin} = require('clean-webpack-plugin')\n    module.exports = {\n        mode: 'development',\n        entry: './lib/index.js',\n        output: {\n            filename: 'util.js',\n            path: path.resolve(__dirname, './lib-dist'),\n            library: 'util',\n            libraryTarget: 'umd'\n        },\n        devtool: 'source-map',\n        externals: {\n            lodash: {\n                root: '_',\n                commonjs: 'lodash',\n                commonjs2: 'lodash',\n                amd: 'lodash'\n            }\n        },\n        plugins: [\n            new CleanWebpackPlugin()\n        ]\n    }\n    ```\n\n#### 使用webpack环境变量\n使用webpack命令行环境选项 --env，可以往 webpack.config.js 中传入 env 对象. 此时配置文件导出的应该是一个函数 接收env 对象\n+ 修改配置文件\n    ```js\n    const path = require('path');\n\n    module.exports = env =\u003e {\n      console.log('CCC: ', env.CCC); // 'local'\n      console.log('AAA: ', env.AAA); // true\n\n      return {\n        entry: './src/index.js',\n        output: {\n          filename: 'bundle.js',\n          path: path.resolve(__dirname, 'dist'),\n        },\n      };\n    };\n    ```\n+ 运行构建命令\n    ```shell\n    webpack --env.AAA=111 --env.CCC=2\n    ```\n\n#### 模块热更新替换\n热模块更换（或HMR）是webpack提供的最有用的功能之一。它允许在运行时更新各种模块，而无需完全刷新\n+ 启用webpack-de-server的 hmr 热更新\n    启用webpack-dev-server热更新模式很简单不需要安装 额外的插件，只需更新webpack.config.js的配置即可,添加 devServer.hot = ture;\n    ```js\n    const path = require('path');\n\n    module.exports = {\n        entry: './src/index.js',\n        devServer: {\n            contentBase: './dist',\n            port: 9000,\n            hot: true\n        },\n        output: {\n          filename: 'bundle.js',\n          path: path.resolve(__dirname, 'dist'),\n        },\n    };\n    ```\n    现在，让我们更新index.js文件，以便在检测到内部print.js更改时告诉Webpack接受更新的模块。\n    \n    ```js\n    import _ from 'lodash';\n    import printMe from './print.js';\n\n    function component() {\n        const element = document.createElement('div');\n        const btn = document.createElement('button');\n\n        element.innerHTML = _.join(['Hello', 'webpack'], ' ');\n\n        btn.innerHTML = 'Click me and check the console!';\n        btn.onclick = printMe;\n\n        element.appendChild(btn);\n\n        return element;\n      }\n\n      document.body.appendChild(component());\n\n     if (module.hot) {\n       module.hot.accept('./print.js', function() {\n        console.log('Accepting the updated printMe module!');\n         printMe();\n      })\n     }\n    ```\n+ 通过自定义服务器 koa 为例 做 热hmr\n    有的时候 我们需要 更灵活的配置 webpack-dev-server 的所有配置都封装在webpack.config.js 中，但是我们可以自己搭建服务器，通过 webpack-dev-middleware 和 webpack-hot-middleware 中间件做热更新，这两个插件是居于\n    ```js\n    const Koa = require('koa');\n    const webpack = require('webpack');\n    const webpackConfig = require('./webpack.config.js');\n    const webpackDevMiddle = require('webpack-dev-middleware');\n    const webpackHotMiddle = require('webpack-hot-middleware');\n    const compiler = webpack(webpackConfig);\n    // express 中间件 转 koa中间件\n    const applyExpressMiddleware = (expressMiddleware, req, res) =\u003e {\n        const _send = res.send;\n        return new Promise((resolve, reject) =\u003e {\n            try {\n                res.send = (...params) =\u003e {\n                    _send \u0026\u0026 _send.apply(res, params) \u0026\u0026 resolve(false)\n                }\n                expressMiddleware(req, res, () =\u003e resolve(true))\n            } catch (error) {\n                reject(error);\n            }\n        })\n    }\n    // 封装koa-webpack-dev-middleware\n    const KoaWebpackDevMiddleWare = (compiler, options) =\u003e {\n        const middleware = webpackDevMiddleware(compiler, options);\n        return async (ctx, next) =\u003e {\n            const hasNext = await applyExpressMiddleware(middleware, ctx.req, {\n                ...ctx.res,\n                send(content) { return ctx.body = content },\n                setHeader(...params) {\n                    ctx.set.apply(ctx, params);\n                }\n            })\n            hasNext \u0026\u0026 next();\n        }\n    }\n    \n    // 封装koa-webpack-hot-middleware\n    const KoaWebpackHotMiddleware = (compiler, options) =\u003e {\n        const middleware = webpackHotMiddleware(compiler, options);\n        return async (ctx, next) =\u003e {\n            const hasNext = await applyExpressMiddleware(middleware, ctx.req, ctx.res);\n            hasNext \u0026\u0026 await next();\n        }\n    }\n\n    const app = new Koa();\n    \n    app.use(KoaWebpackDevMiddleware(compiler, {\n        host: 'localhost',\n        contentBase: './dist',\n        log: false,\n        stats: {\n            colors: true, // webpack编译输出日志带上颜色，相当于命令行 webpack –colors\n            process: true\n        }\n    }))\n    \n    app.use(KoaWebpackHotMiddleware(compiler, {\n        log: false,\n        path: \"/__webpack_hmr\",\n        heartbeat: 2000,\n    }))\n    \n    app.listen(9999, () =\u003e {\n        console.log('server runnint on 9999 port!')\n    })\n    ```\n    \n+ hmr 修改style\n    借助于 style-loader 的帮助，CSS 的模块热替换实际上是相当简单的。当更新 CSS 依赖模块时，此 loader 在后台使用 module.hot.accept 来修补\u003cstyle\u003e 标签。不需要做额外的操作\n    \n#### tree shaking\n+ 将文件标记为无副作用(side-effect-free)\n    在一个纯粹的 ESM 模块世界中，识别出哪些文件有副作用很简单。然而，我们的项目无法达到这种纯度，所以，此时有必要向 webpack 的 compiler 提供提示哪些代码是“纯粹部分”。\n    这种方式是通过 package.json 的 \"sideEffects\" 属性来实现的。\n    ```json\n    {\n      \"name\": \"your-project\",\n      \"sideEffects\": false\n    }\n    ```\n    如同上面提到的，如果所有代码都不包含副作用，我们就可以简单地将该属性标记为 false，来告知 webpack，它可以安全地删除未用到的 export 导出。\n    \u003e 「副作用」的定义是，在导入时会执行特殊行为的代码，而不是仅仅暴露一个 export 或多个 export。举例说明，例如 polyfill，它影响全局作用域，并且通常不提供 export\n    \n    如果你的代码确实有一些副作用，那么可以改为提供一个数组：\n    ```json\n    {\n      \"name\": \"your-project\",\n      \"sideEffects\": [\n        \"./src/some-side-effectful-file.js\",\n        \"*.css\"\n      ]\n    }\n    ```\n+ 压缩输出\n    通过如上方式，我们已经可以通过 import 和 export 语法，找出那些需要删除的“未使用代码(dead code)”，然而，我们不只是要找出，还需要在 bundle 中删除它们。为此，我们将使用 -p(production) 这个 webpack 编译标记，来启用 uglifyjs 压缩插件\n    从 webpack 4 开始，也可以通过 \"mode\" 配置选项轻松切换到压缩输出，只需设置为 \"production\"。\n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: \"production\",\n      entry: './src/index.js',\n      output: {\n        filename: 'bundle.js',\n        path: path.resolve(__dirname, 'dist')\n      },\n    };\n    ```\n    \n#### 构建生产环境    \n\n#### shimming\n+ shimming 全局变量\n    webpack 可以在全局环境填充全局变量, 使用providePlugin 可以实现向全局环境提供变量的功能，我们还可以使用，ProvidePlugin通过使用“数组路径”（例如[module, child, ...children?]）配置模块来公开模块的单个导出。\n    ```js\n    const path = require('path');\n    const webpack = require('webpack');\n    module.exports = {\n      mode: \"production\",\n      entry: './src/index.js',\n      output: {\n        filename: 'bundle.js',\n        path: path.resolve(__dirname, 'dist')\n      },\n      plugins: [\n        new webpack.ProvidePlugin({\n            $: 'jquery',\n            _: 'lodash',\n            _join: ['lodash', 'join'] // 单个模块的导出\n        })\n      ]\n    };\n    ```\n+ 细粒度 shimming\n    一些传统的模块依赖的 this 指向的是 window 对象。在接下来的用例中，调整我们的 index.js：\n    ```js\n    const path = require('path');\n    const webpack = require('webpack');\n    module.exports = {\n      mode: \"production\",\n      entry: './src/index.js',\n      output: {\n        filename: 'bundle.js',\n        path: path.resolve(__dirname, 'dist')\n      },\n      module: {\n        rules: [\n            {\n                test: require.resolve('./src/alert.js'),\n                use: [\n                    'imports-loader?this=\u003ewindow'\n                ]\n            }\n        ]\n      }\n    };\n    ```\n    需要注意的是 alert.js 必须是 module.exports 导出变量 否则 会报错\n    \n+ 全局 exports\n    你可能从来没有在自己的源码中做过这些事情，但是你也许遇到过一个老旧的库(library)，和上面所展示的代码类似。在这个用例中，我们可以使用 exports-loader，将一个全局变量作为一个普通的模块来导出\n    创建一个gloabl.js\n    ```js\n    const username = 'JohnApache';\n    const fn = () =\u003e {\n        console.log('fn')\n    }\n    const ddd = {\n        test: () =\u003e console.log('test'),\n        exec: () =\u003e console.log('exec')\n    }\n    ```\n    修改webpack.config.js配置\n    ```js\n    const path = require('path');\n    const webpack = require('webpack');\n    module.exports = {\n      mode: \"production\",\n      entry: './src/index.js',\n      output: {\n        filename: 'bundle.js',\n        path: path.resolve(__dirname, 'dist')\n      },\n      module: {\n        rules: [\n            {\n                test: require.resolve('./src/global.js'),\n                use: [\n                    'exports-loader?username,fn,exec=ddd.exec'\n                ]\n            }\n        ]\n      }\n    };\n    ```\n    现在，从我们的输入脚本（即src/index.js）中，我们可以import { username, fn, exec } from './global.js';并且所有模块都应该顺利运行。\n\n+ polyfill \n    具体优化方式 参考[babel-usage-doc](https://github.com/JohnApache/babel-usage-doc)\n    \n    \n#### TypeScript    \n+ 基础安装\n    首先，执行以下命令，安装 TypeScript 编译器(compiler)和 loader：\n    ```shell\n    yarn add typescript ts-loader -D\n    ```\n    创建tsconfig.json tsc --init\n    ```json\n    {\n      \"compilerOptions\": {\n        \"outDir\": \"./dist/\",\n        \"noImplicitAny\": true,\n        \"module\": \"es6\",\n        \"target\": \"es5\",\n        \"allowJs\": true,\n        \"source-map\": true\n      },\n      \"include\": [\n        \"./src/**\"\n      ]\n    }\n    ```\n    \n    修改webpack.config.js \n    ```js\n    const path = require('path');\n    module.exports = {\n      mode: \"production\",\n      entry: './src/index.js',\n      output: {\n        filename: 'bundle.js',\n        path: path.resolve(__dirname, 'dist')\n      },\n      module: {\n        rules: [\n            {\n                test: /\\.(ts|tsx)$/,\n                use: [\n                    'ts-loader'\n                ]\n            }\n        ]\n      }\n    };\n    ```\n\n#### 渐进式网络应用 PWA\n渐进式网络应用程序(Progressive Web Application - PWA)，是一种可以提供类似于原生应用程序(native app)体验的网络应用程序(web app)。PWA 可以用来做很多事。其中最重要的是，在离线(offline)时应用程序能够继续运行功能。这是通过使用名为 Service Workers 的网络技术来实现的。\n\n+ 添加workbox\n    添加 workbox-webpack-plugin 插件，\n    ```shell\n    yarn add workbox-webpack-plugin -D\n    ```\n    调整 webpack.config.js 文件：\n    ```js\n    const path = require('path');\n    const WorkboxPlugin = require('workbox-webpack-plugin');\n    module.exports = {\n      mode: \"production\",\n      entry: './src/index.js',\n      output: {\n        filename: 'bundle.js',\n        path: path.resolve(__dirname, 'dist')\n      },\n      plugins: [\n        new WorkboxPlugin.GenerateSW({\n          cacheId: 'seed-cache',\n          importWorkboxFrom: 'cdn', // 可填`cdn`,`local`,`disabled`,\n\n          skipWaiting: true, //跳过waiting状态\n          clientsClaim: true, //通知让新的sw立即在页面上取得控制权\n          cleanupOutdatedCaches: true,//删除过时、老版本的缓存\n          \n          //最终生成的service worker地址，这个地址和webpack的output地址有关\n          // swDest: '../workboxServiceWorker.js', \n          // include: [\n              \n          // ], \n          //缓存规则，可用正则匹配请求，进行缓存\n          //这里将js、css、还有图片资源分开缓存，可以区分缓存时间(虽然这里没做区分。。)\n          //由于种子农场此站点较长时间不更新，所以缓存时间可以稍微长一些\n          runtimeCaching: [\n              {\n                  urlPattern: /.*\\.js.*/i,\n                  handler: 'CacheFirst',\n                  options: {\n                      cacheName: 'seed-js',\n                      expiration: {\n                          maxEntries: 20,  //最多缓存20个，超过的按照LRU原则删除\n                          maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days\n                      },\n                  },\n              },\n              {\n                  urlPattern: /.*css.*/,\n                  handler: 'CacheFirst',\n                  options: {\n                      cacheName: 'seed-css',\n                      expiration: {\n                          maxEntries: 30,  //最多缓存30个，超过的按照LRU原则删除\n                          maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days\n                      },\n                  },\n              },\n              {\n                  urlPattern: /.*(png|svga).*/,\n                  handler: 'CacheFirst',\n                  options: {\n                      cacheName: 'seed-image',\n                      expiration: {\n                          maxEntries: 30,  //最多缓存30个，超过的按照LRU原则删除\n                          maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days\n                      },\n                  },\n              }\n          ]\n        })\n      ]\n    };\n    ```\n    \n    构建后 生成了 2 个额外的文件：sw.js 和体积很大的 precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js。sw.js 是 Service Worker 文件，precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js 是 sw.js 引用的文件，所以它也可以运行。可能在你本地生成的文件会有所不同；但是你那里应该会有一个 sw.js 文件。\n    \n+ 注册 service-worker.js\n    ```js\n    import _ from 'lodash';\n    import printMe from './print.js';\n\n    if ('serviceWorker' in navigator) {\n      window.addEventListener('load', () =\u003e {\n        navigator.serviceWorker.register('/service-worker.js').then(registration =\u003e {\n          console.log('SW registered: ', registration);\n        }).catch(registrationError =\u003e {\n          console.log('SW registration failed: ', registrationError);\n        });\n      });\n    }\n    ```\n    如果浏览器能够支持 Service Worker，你应该可以看到你的应用程序还在正常运行。然而，服务器已经停止了服务，此刻是 Service Worker 在提供服务。","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnapache%2Fwebpack-usage-doc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnapache%2Fwebpack-usage-doc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnapache%2Fwebpack-usage-doc/lists"}