Vinson

Vinson

webpack5(二)

发表于 2023-09-12
Vinson
阅读量 50

3. 加载和处理其他资源

asset module type的介绍

  • 我们当前使用的webpack版本是webpack5:
    • 在webpack5之前,加载这些资源我们需要使用一些loader,比如raw-loader 、url-loader、file-loader;
    • 在webpack5之后,我们可以直接使用资源模块类型(asset module type),来替代上面的这些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,并且配置资源 体积限制实现;
  • 比如加载图片,我们可以使用下面的方式:
output: {
  filename: 'js/bundle.js',
   path: path.resolve(__dirname, './dist'),
   assetModuleFilename: 'img/[name].[hash:6][ext]' //方式一:自定义文件的输出路径和文件名
}

{
  test: /\.(png|svg|jpe?g|gif)$/i,
   type: 'asset/resource',
   generator: {
     filename: 'img/[name].[hash:6][ext]' // 方式二:自定义文件的输出路径和文件名
   }
}
javascript
  • url-loader的limit效果
rules: [
  {
    test: /\.(png|jpe?g|svg|gif)$/i,
    type: 'asset',
    generator: {
      filename: 'img/[name].[hash:6][ext]'
    },
    parser: {
      dataUrlCondition: {
        maxSize: 100 * 1024
      }
    }
  }
]
javascript

加载字体文件

  • 如果我们需要使用某些特殊的字体或者字体图标,那么我们会引入很多字体相关的文件,这些文件的处理也是一样 的。
  • 首先,我从阿里图标库中下载了几个字体图标:

  • 在component中引入,并且添加一个i元素用于显示字体图标:

  • 字体打包
  • 这个时候打包会报错,因为无法正确的处理eot、ttf、woff等文件:
    • 我们可以选择使用file-loader来处理,也可以选择直接使用webpack5的资源模块类型来处理;
// webpack.config.js
{
  test: /\.(woff2?|eot|ttf)$/,
  type: 'asset/resource',
    generator: {
      filename: 'font/[name].[hash:6][ext]'
    }
}
javascript

认识plugin

  • Webpack的另一个核心是Plugin,官方有这样一段对Plugin的描述:
    • While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.
  • 上面表达的含义翻译过来就是:
    • Loader是用于特定的模块类型进行转换;
    • Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等;

CleanWebpackPlugin

每次修改了一些配置,重新打包时,自动删除dist文件夹

npm i clean-webpack-plugin -D
shell
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.export = {
  // 其他省略
  plugins: [
    new CleanWebpackPlugin()
  ]
}
javascript

HtmlWebpackPlugin

对HTML进行打包处理的插件

  • 安装

npm i html-webpack-plugin -D

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.export = {
  // 其他省略
  plugins: [
    new HtmlWebpackPlugin({
      title: 'webpack案例', // 在进行htmlWebpackPlugin.options.title读取时,就会读到该信息
      template: './public/index.html' // 指定我们要使用的模块所在的路径
    })
  ]
}
javascript
  • 根目录新建public目录
<!-- index.html 自定义模版 -->
<!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">
    <!-- 上面的代码中,会有一些类似这样的语法<% 变量 %>,这个是EJS模块填充数据的方式 -->
    <link rel="shortcut icon" href="<%= BASE_URL %>favicon.ico" type="image/x-icon">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
    <div id="app"></div>
</body>
</html>
html

DefinePlugin的介绍

  • 在编译template模块时,有一个BASE_URL:
  • ;
  • 但是我们并没有设置过这个常量值,所以会出现没有定义的错误;
  • 这个时候我们可以使用DefinePlugin插件;
  • DefinePlugin允许在编译时创建配置的全局常量,是一个webpack内置的插件(不需要单独安装)
const { DefinePlugin } = require('webpack')

module.export = {
  // 其他省略
  plugins: [
    new DefinePlugin({
      BASE_URL: '"./"'
    })
  ]
}
javascript

CopyWebpackPlugin

  • 在vue的打包过程中,如果我们将一些文件放到public的目录下,那么这个目录会被复制到dist文件夹中。
    • 这个复制的功能,我们可以使用CopyWebpackPlugin来完成;
  • 安装

npm i copy-webpack-plugin -D

  • 接下来配置CopyWebpackPlugin即可: p复制的规则在patterns中设置;
    • from:设置从哪一个源中开始复制;
    • to:复制到的位置,可以省略,会默认复制到打包的目录下;
    • globOptions:设置一些额外的选项,其中可以编写需要忽略的文件:
      • .DS_Store:mac目录下回自动生成的一个文件;
      • index.html:也不需要复制,因为我们已经通过HtmlWebpackPlugin完成了index.html的生成;
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.export = {
  // 其他省略
  plugins: [
    new CopyWebpackPlugin({
            patterns: [
                {
                    from: 'public',
                    globOptions: {
                        ignore: [
                            '**/index.html',
                            '**/.DS_Store',
                        ]
                    }
                }
            ]
        })
  ]
}
javascript

4.模块化原理和source-map

Mode配置

  • Mode配置选项,可以告知webpack使用响应模式的内置优化:
    • 默认值是production(什么都不设置的情况下);
    • 可选值有:‘none’ | ‘development’ | ‘production’;
  • 选项区别

Mode配置代表

认识source-map

  • 我们的代码通常运行在浏览器上时,是通过打包压缩的:
    • 也就是真实跑在浏览器上的代码,和我们编写的代码其实是有差异的;
    • 比如ES6的代码可能被转换成ES5;
    • 比如对应的代码行号、列号在经过编译后肯定会不一致;
    • 比如代码进行丑化压缩时,会将编码名称等修改; p比如我们使用了TypeScript等方式编写的代码,最终转换成JavaScript;
  • 但是,当代码报错需要调试时(debug),调试转换后的代码是很困难的
  • 但是我们能保证代码不出错吗?不可能。
  • 那么如何可以调试这种转换后不一致的代码呢?答案就是source-map
    • source-map是从已转换的代码,映射到原始的源文件;
    • 使浏览器可以重构原始源并在调试器中显示重建的原始源;

如何使用source-map

  • 如何可以使用source-map呢?两个步骤:
    • 第一步:根据源文件,生成source-map文件,webpack在打包时,可以通过配置生成source-map;
    • 第二步:在转换后的代码,最后添加一个注释,它指向sourcemap;
    • // # sourceMappingURL=common.bundle.js.map
  • 浏览器会根据我们的注释,查找响应的source-map,并且根据source-map还原我们的代码,方便进行调试。
  • 在Chrome中,我们可以按照如下的方式打开source-map:

分析source-map

  • 最初source-map生成的文件带下是原始文件的10倍,第二版减少了约50%,第三版又减少了50%,所以目前一个 133kb的文件,最终的source-map的大小大概在300kb。
  • 目前的source-map长什么样子呢?
    • version:当前使用的版本,也就是最新的第三版;
    • sources:从哪些文件转换过来的source-map和打包的代码(最初始的文件);
    • names:转换前的变量和属性名称(因为我目前使用的是development模式,所以不需要保留转换前的名 称);
    • mappings:source-map用来和源文件映射的信息(比如位置信息等),一串base64 VLQ(veriable- length quantity可变长度值)编码;
    • file:打包后的文件(浏览器加载的文件); psourceContent:转换前的具体代码信息(和sources是对应的关系);
    • sourceRoot:所有的sources相对的根目录;
  • [参考文档(MDN):](https://developer.mozilla.org/en- US/docs/Mozilla/JavaScript_code_modules/SourceMap.jsm)

生成source-map

  • 如何在使用webpack打包的时候,生成对应的source-map呢?
    • webpack为我们提供了非常多的选项(目前是26个),来处理source-map;
    • https://webpack.docschina.org/configuration/devtool/
    • 选择不同的值,生成的source-map会稍微有差异,打包的过程也会有性能的差异,可以根据不同的情况进行 选择;
  • 下面几个值不会生成source-map
    • false:不使用source-map,也就是没有任何和source-map相关的内容。
    • none:production模式下的默认值,不生成source-map。
    • eval:development模式下的默认值,不生成source-map
      • 但是它会在eval执行的代码中,添加 //# sourceURL=;
      • 它会被浏览器在执行时解析,并且在调试面板中生成对应的一些文件目录,方便我们调试代码;

source-map值

// webpack.config.js
module.exports = {
  mode: 'development',
  devtool: 'source-map'
}
javascript
  • source-map:
    • 生成一个独立的source-map文件,并且在bundle文件中有一个注释,指向source-map文件;
  • bundle文件中有如下的注释:
    • 开发工具会根据这个注释找到source-map文件,并且解析;
    • //# sourceMappingURL=bundle.js.map

eval-source-map值

// webpack.config.js
module.exports = {
  mode: 'development',
  devtool: 'eval-source-map'
}
javascript
  • eval-source-map:会生成sourcemap,但是source-map是以DataUrl添加到eval函数的后面

inline-source-map值

  • inline-source-map:会生成sourcemap,但是source-map是以DataUrl添加到bundle文件的后面

cheap-source-map

  • cheap-source-map:
    • 会生成sourcemap,但是会更加高效一些(cheap低开销),因为它没有生成列映射(Column Mapping)
    • 因为在开发中,我们只需要行信息通常就可以定位到错误了

cheap-module-source-map值

  • cheap-module-source-map:
    • 会生成sourcemap,类似于cheap-source-map,但是对源自loader的sourcemap处理会更好。
  • 这里有一个很模糊的概念:对源自loader的sourcemap处理会更好,官方也没有给出很好的解释
    • 其实是如果loader对我们的源码进行了特殊的处理,比如babel;

cheap-source-map和cheap-module-source-map

  • cheap-source-map和cheap-module-source-map的区别:

hidden-source-map值

  • hidden-source-map:
    • 会生成sourcemap,但是不会对source-map文件进行引用;
    • 相当于删除了打包文件中对sourcemap的引用注释;

// 被删除掉的
//# sourceMappingURL=bundle.js.map

  • 如果我们手动添加进来,那么sourcemap就会生效了

nosources-source-map值

  • nosources-source-map:
    • 会生成sourcemap,但是生成的sourcemap只有错误信息的提示,不会生成源代码文件;
  • 正确的错误提示:
  • 点击错误提示,无法查看源码:

多个值的组合

  • 事实上,webpack提供给我们的26个值,是可以进行多组合的。
  • 组合的规则如下:
    • inline-|hidden-|eval:三个值时三选一;
    • nosources:可选值;
    • cheap可选值,并且可以跟随module的值;
    • [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
  • 那么在开发中,最佳的实践是什么呢?
    • 开发阶段:推荐使用 source-map或者cheap-module-source-map
      • 这分别是vue和react使用的值,可以获取调试信息,方便快速开发;
    • 测试阶段:推荐使用 source-map或者cheap-module-source-map
      • 测试阶段我们也希望在浏览器下看到正确的错误提示;
    • 发布阶段:false、缺省值(不写)
评论
来发一针见血的评论吧!
表情

快来发表评论吧~

推荐文章
  • 测试文章

    20点赞10评论

  • webpack5(一)

    20点赞10评论