最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • webpack(二)

    正文概述 掘金(anna_jia)   2021-02-02   560

    如何将构建时产生的bundle自动添加到html中

    使用HtmlWebpackPlugin插件,会生成新的index.html文件,替换我们的原有文件。

    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
     
     module.exports = {
       entry: {
         index: './src/index.js',
         print: './src/print.js',
       },
      plugins: [
        new HtmlWebpackPlugin({
          title: 'Output Management',
        }),
      ],
       output: {
         filename: '[name].bundle.js',
         path: path.resolve(__dirname, 'dist'),
       },
     };
    

    如何清理(webpack生成的文件放置在)/dist文件夹

    webpack 将生成文件并放置在 /dist 文件夹中,但是它不会追踪哪些文件是实际在项目中用到的。

    使用clean-webpack-plugin插件,在每次构建前清理/dist文件夹,这样只会生成用到的文件。

    如何将编译后的代码映射回原始源代码

    当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。

    webpack(二)

    为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 功能,可以将编译后的代码映射回原始源代码。

    const path = require('path');
     const HtmlWebpackPlugin = require('html-webpack-plugin');
     const { CleanWebpackPlugin } = require('clean-webpack-plugin');
     
     module.exports = {
     // 注意:仅用于开发环境,source map 相当消耗资源
     // 避免在生产中使用 inline-*** 和 eval-***,因为它们会增加 bundle 体积大小,并降低整体性能
       mode: 'development',
       entry: {
         index: './src/index.js',
         print: './src/print.js',
       },
      devtool: 'inline-source-map',
       plugins: [
         new CleanWebpackPlugin(),
         new HtmlWebpackPlugin({
           title: 'Development',
         }),
       ],
       output: {
         filename: '[name].bundle.js',
         path: path.resolve(__dirname, 'dist'),
       },
     };
    

    webpack(二)

    如何在代码发生变化后自动编译代码

    webpack 提供以下方式:

    • webpack --watch命令行(观察模式watch mode

    缺点:需要手动刷新浏览器,才能看到修改后的实际效果

    • webpack serve --open命令行(webpack-dev-server

    webpack-dev-server提供了一个简单的 web server,并具有 live reloading(实时重新加载) 功能

    注意:webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 publicPath 选项进行修改。

     const path = require('path');
     const HtmlWebpackPlugin = require('html-webpack-plugin');
     const { CleanWebpackPlugin } = require('clean-webpack-plugin');
     
     module.exports = {
       mode: 'development',
       entry: {
         index: './src/index.js',
         print: './src/print.js',
       },
       devtool: 'inline-source-map',
      devServer: {
        contentBase: './dist',//从什么位置查找文件serve 
      },
       plugins: [
         new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
         new HtmlWebpackPlugin({
           title: 'Development',
         }),
       ],
       output: {
         filename: '[name].bundle.js',
         path: path.resolve(__dirname, 'dist'),
       },
     };
    
    • 使用 webpack-dev-middleware

    webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。

    webpack-dev-server 在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。

    可结合ExpressJS(自定义设置server)等使用

    需要手动刷新浏览器,才能看到修改后的实际效果,要结合使用webpack-hot-middleware依赖包,以在你的自定义服务器或应用程序上启用HMR

    server.js

    const express = require('express');
    const webpack = require('webpack');
    const webpackDevMiddleware = require('webpack-dev-middleware');
    
    const app = express();
    const config = require('./webpack.config.js');
    const compiler = webpack(config);
    
    // 告知 express 使用 webpack-dev-middleware,
    // 以及将 webpack.config.js 配置文件作为基础配置。
    app.use(
      webpackDevMiddleware(compiler, {
        publicPath: config.output.publicPath,
      })
    );
    
    // 将文件 serve 到 port 3000。
    app.listen(3000, function () {
      console.log('Example app listening on port 3000!\n');
    });
    

    代码分离的方法

    1. 入口起点

      缺点:

    • 手动配置较多
    • 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
    • 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来
    1. 防止重复
    • 入口依赖

    配置dependOnoption 选项,这样可以在多个 chunk 之间共享模块

    const path = require('path');
     
     module.exports = {
       mode: 'development',
       entry: {
         index: {
           import: './src/index.js',
           dependOn: 'shared',
         },
         another: {
           import: './src/another-module.js',
           dependOn: 'shared',
         },
         shared: 'lodash',
       },
       output: {
         filename: '[name].bundle.js',
         path: path.resolve(__dirname, 'dist'),
       },
       //注意:要在一个 HTML 页面上使用多个入口时需设置optimization.runtimeChunk: 'single',将 runtime 代码拆分为一个单独的 chunk
      optimization: {
        runtimeChunk: 'single',
      },
     };
    
    • SplitChunksPlugin

    SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。

    const path = require('path');
    
      module.exports = {
        mode: 'development',
        entry: {
          index: './src/index.js',
          print: './src/print.js',
        },
        output: {
          filename: '[name].bundle.js',
          path: path.resolve(__dirname, 'dist'),
        },
        
       optimization: {
         splitChunks: {
           chunks: 'all',
         },
       },
      };
    
    1. 动态导入
    • ECMAScript提案的import()语法 (推荐)
    • webpack特定的require.ensure

    注意

    import() 调用会在内部用到 promises。如果在旧版本浏览器中(例如,IE 11)使用 import(),记得使用一个 polyfill 库(例如 es6-promisepromise-polyfill),来实现 Promise。

    由于 import() 会返回一个 promise,因此它可以和 async 函数一起使用。

    预获取/预加载

    • prefetch(预获取):将来某些导航下可能需要的资源
    //...
    import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
    

    这会生成<link rel="prefetch" href="login-modal-chunk.js">并追加到页面头部,指示着浏览器在闲置时间预取 login-modal-chunk.js文件。

    • preload(预加载):当前导航下可能需要资源

    ChartComponent.js

    //...
    import(/* webpackPreload: true */ 'ChartingLibrary');
    

    在页面中使用 ChartComponent 时,在请求 ChartComponent.js 的同时,还会通过 <link rel="preload"> 请求 charting-library-chunk。假定 page-chunk 体积很大,加载慢,页面此时就会显示 LoadingIndicator(加载进度条) ,等到 charting-library-chunk 请求完成,LoadingIndicator 组件才消失。启动仅需要很少的加载时间,因为只进行单次往返,而不是两次往返。尤其是在高延迟环境下。

    注意:不正确的使用webpackPreload会有损性能

    prefetch指令相比,preload指令有许多不同之处:

    • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
    • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
    • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
    • 浏览器支持程度不同。

    bundle分析

    • analyse官方:您可以运行此命令行webpack --profile --json > stats.json生成所需的JSON文件
    • webpack-chart: webpack stats 可交互饼图。
    • webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
    • webpack-bundle-analyzer:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。
    • webpack bundle optimize helper:这个工具会分析你的 bundle,并提供可操作的改进措施,以减少 bundle 的大小。
    • bundle-stats:生成一个 bundle 报告(bundle 大小、资源、模块),并比较不同构建之间的结果。

    缓存

    确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件。

      const path = require('path');
      const { CleanWebpackPlugin } = require('clean-webpack-plugin');
      const HtmlWebpackPlugin = require('html-webpack-plugin');
    
      module.exports = {
        entry: './src/index.js',
        plugins: [
          // 对于 CleanWebpackPlugin 的 v2 versions 以下版本,使用 new CleanWebpackPlugin(['dist/*'])
          new CleanWebpackPlugin(),
          new HtmlWebpackPlugin({
            title: 'Caching',
          }),
        ],
        output: {
          filename: '[name].[contenthash].js',
          path: path.resolve(__dirname, 'dist'),
        },
        optimization: {
        //解决vendor bundle 会随着自身的 module.id 的变化,而发生变化
         moduleIds: 'deterministic',
          runtimeChunk: 'single',//提取引导模板,将 runtime 代码拆分为一个单独的 chunk
          splitChunks: {
        	//利用 client 的长效缓存机制,命中缓存来消除请求,并减少向 server 获取资源,同时还能保证 client 代码和 server 代码版本一致
            cacheGroups: {
              vendor: {
                test: /[\\/]node_modules[\\/]/,
                name: 'vendors',
                chunks: 'all',
              },
            },
          },
        },
      };
    

    构建性能提升

    • loader通过使用include字段,仅将loader应用在实际需要将其转换的模块

    • 提高解析速度

      • 减少resolve.modules, resolve.extensions, resolve.mainFiles, resolve.descriptionFiles中条目数量,因为他们会增加文件系统调用的次数。
      • 如果你不使用symlinks(例如 npm link 或者 yarn link),可以设置 resolve.symlinks: false
      • 如果你使用自定义resolve plugin规则,并且没有指定context上下文,可以设置 resolve.cacheWithContext: false
    • 使用DllPlugin为更改不频繁的代码生成单独的编译结果

    • 尽量保持chunk体积小

      • 使用数量更少/体积更小的library
      • 在多页面应用程序中使用SplitChunksPlugin
      • 在多页面应用程序中使用SplitChunksPlugin,并开启async模式。
      • 移除未引用代码。
      • 只编译你当前正在开发的那些代码。
    • 不要使用太多的worker

    thread-loader可以将非常消耗资源的loader分流给一个worker pool(worker池)

    注意:不要使用太多的worker,因为Node.jsruntimeloader都有启动开销。最小化workermain process(主进程)之间的模块传输。进程间通讯(IPC, inter process communication)是非常消耗资源的。

    • 持久化缓存

    • ProgressPlugin从 webpack 中删除,可以缩短构建时间。

    • 最小化项目中的 preset/plugin 数量.

    • 在单独的进程中使用fork-ts-checker-webpack-plugin进行类型检查。

    • 配置loader跳过类型检查。

    • 使用ts-loader时,设置happyPackMode: true / transpileOnly: true.

    • node-sass中有个来自 Node.js 线程池的阻塞线程的 bug。 当使用thread-loader时,需要设置workerParallelJobs: 2

    如何独立配置开发和生成环境并合并公共部分

    在开发环境中,我们需要:强大的source map和一个有着live reloading(实时重新加载)hot module replacement(热模块替换)能力的localhost server

    在生产环境中,目标则转移至其他方面,关注点在于压缩bundle、更轻量的source map、资源优化等,通过这些优化方式改善加载时间。

    使用webpack-merge工具,此工具会引用“common”配置,避免再在环境特定(environment-specific)的配置中编写重复代码

    资源模块

    资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。

    在 webpack 5 之前,通常使用:

    • raw-loader将文件导入为字符串
    • url-loader将文件作为 data URI 内联到 bundle 中
    • file-loader将文件发送到输出目录

    资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:

    • asset/resource发送一个单独的文件并导出 URL。之前通过使用 file-loader实现。
    • asset/inline导出一个资源的 data URI。之前通过使用url-loader实现。
    • asset/source导出资源的源代码。之前通过使用raw-loader实现。
    • asset在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader,并且配置资源体积限制实现。
    module.exports = {
        entry: './src/index.js',
        output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist'),
        // 自定义输出文件名 1.assetModuleFilename 来修改此模板字符串
       	assetModuleFilename: 'images/[hash][ext][query]'
      },
      module: {
       rules: [
          {
            test: /\.(png|jpg|gif)$/i,
            // 需从 asset loader 中排除来自新 URL 处理的 asset
            dependency: { not: ['url'] }, 
            use: [
              {
                loader: 'url-loader',
                options: {
                  limit: 8192,
                }
              },
            ],
           // 想停止当前 asset 模块的处理,并再次启动处理,避免asset 重复
           type: 'javascript/auto'
          },
          // Resource资源配置
          {
           test: /\.png/,
           type: 'asset/resource'
         },
         // 自定义输出文件名 2.将某些资源发送到指定目录,仅适用于 asset 和 asset/resource 模块类型
         {
           test: /\.html/,
           type: 'asset/resource',
           generator: {
             filename: 'static/[hash][ext][query]'
           }
         },
         {
           test: /\.svg/,
           type: 'asset/inline',
           // 自定义 data URI 生成器,所有 .svg 文件都将通过 mini-svg-data-uri 包进行编码
           generator: {
             dataUrl: content => {
               content = content.toString();
               return svgToMiniDataURI(content);
             }
         },
         {
           test: /\.txt/,
           type: 'asset/source',
         },
         {
            test: /\.doc/,
            // 通用资源类型,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型
            type: 'asset',
          	parser: {
             dataUrlCondition: {
               maxSize: 4 * 1024 // 4kb
             }
           }
          }
       ]
      },
    }
    
    • resource资源
    import mainImage from './images/main.png';
    
    img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'
    
    • inline资源
    import metroMap from './images/metro.svg';
    
    block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
    
    • source资源

    src/example.txt

    Hello world
    

    src/index.js

    import exampleText from './example.txt';
    
    block.textContent = exampleText; // 'Hello world'
    
    • url资源

    当使用new URL('./path/to/asset', import.meta.url),webpack也会创建资源模块。

    src/index.js

    const logo = new URL('./logo.svg', import.meta.url);
    

    根据你配置中 target 的不同,webpack 会将上述代码编译成不同结果:

    // target: web
    new URL(__webpack_public_path__ + 'logo.svg', document.baseURI || self.location.href);
    
    // target: webworker
    new URL(__webpack_public_path__ + 'logo.svg', self.location);
    
    // target: node, node-webkit, nwjs, electron-main, electron-renderer, electron-preload, async-node
    new URL(__webpack_public_path__ + 'logo.svg', require('url').pathToFileUrl(__filename));
    

    起源地下载网 » webpack(二)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元