首页 > 其他分享 >【webpack系列】从基础配置到掌握进阶用法

【webpack系列】从基础配置到掌握进阶用法

时间:2023-06-25 10:44:17浏览次数:42  
标签:文件 hash 进阶 ... module js webpack 用法

前言

本篇文章将介绍一些webpack的进阶用法,演示内容继承自上一篇文章的内容,所以没看过上一篇文章的建议先学习上一篇内容再阅读此篇内容,会更有利于此篇的学习~

文件指纹

文件指纹指的是打包输出的文件名后缀,一般用来做版本管理、缓存等

w1.png

webpack的指纹策略有三种:hashchunkhashcontenthash,它们之间最主要的区别就是每种hash影响的范围不同。

占位符

webpack提供占位符用于将特定信息附加在打包输出的文件上

名称 含义
[ext] 资源后缀名
[id] 文件标识符
[name] 文件名称
[path] 文件的相对路径
[folder] 文件所在的文件夹
[hash] 模块标识符的 hash,默认是 md5 生成
[chunkhash] chunk 内容的 hash,默认是 md5 生成
[contenthash] 文件内容的 hash,默认是 md5 生成
[query] 文件的 query,例如,文件名 ? 后面的字符串
[emoji] 一个随机的指代文件内容的 emoji

我们可以使用特定的语法,对 hashchunkhashcontenthash 进行切片:[chunkhash:4],像 8c4cbfdb91ff93f3f3c5 这样的哈希会最后会变为 8c4c

hash

与整个项目的构建有关,只要项目内文件有修改,整个项目构建的hash值就会改变

我们使用多入口打包来体验一下:

// webpack.config.js
module.exports = {
  entry: { 
        main: './src/main.js',
        index: './src/index.js'
    },
    output: {
        filename: '[name].[hash:6].js',
        path: __dirname + '/dist',
        clean: true
    },
  // ...
}

此时我们使用了占位符来设置文件指纹[name].[hash:6].js代表的是文件名+6位hash

此时我们执行npm run build,看打包出来的内容如下:

w2.png

此时两个js文件的hash都是207495

我们修改一下index.js的内容,再打包一次

w3.png

我们会发现此时两个js文件的hash都变成了9f0e2d

chunkhash

chunkhash 是和 webpack 打包的模块相关,每一个 entry 作为一个模块,会产生不同的 Chunkhash 值,文件改变时只会影响当前chunk组的hash值

我们再来看看chunkhash

// webpack.config.js
module.exports = {
  entry: { 
        main: './src/main.js',
        index: './src/index.js'
    },
    output: {
        filename: '[name].[chunkhash:6].js',
        path: __dirname + '/dist',
        clean: true
    },
  // ...
}

还是延用上面的例子,这次我们只修改main.js文件内容

修改前两个文件的hash值如下:

w4.png

修改后:

w5.png

此时只有main.js的打包产物的hash发生了变化

contenthash

contenthash 是和根据文件内容相关,单个文件发生变化,只会引起此文件的hash值

这里我们使用miniCssExtractPlugin将CSS内容提取成文件,并为它设置contenthash

// webpack.config.js
const miniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  entry: { 
        main: './src/main.js',
        index: './src/index.js'
    },
    output: {
        filename: '[name].[contenthash:6].js',
        path: __dirname + '/dist',
        clean: true
    },
  mudole: {
    rules: [
      {
        test: /\.css$/,
        use: [miniCssExtractPlugin.loader, 'css-loader']
      },
      // ...
    ]
  },
  plugins: [
    // ...
    new miniCssExtractPlugin({
            filename: 'css/[name].[contenthash:6].css'
        }),
  ]
  // ...
}

然后打包看一下此时的hash:

w6.png

我们修改index.css内容再打包一次

w7.png

此时只有index.css的打包产物hash值发生了变化。

根据不同的文件类型一般选择不同的文件指纹策略,通常情况下:

  • JS文件采用[chunkhash]文件指纹策略
  • CSS文件采用[contenthash]文件指纹策略
  • 图片资源采用[hash]文件指纹策略

代码压缩

压缩JS

目前最成熟的JavaScript代码压缩工具是UglifyJS,它能够分析JavaScript语法树,理解代码含义,从而能做到诸如去掉无效代码、去掉日志输出代码、缩短变量名等优化。但很遗憾的是UglifyJS不再维护,并且它不支持 ES6 + 。

现在推荐使用的是Terser,它在 UglifyJS 基础上增加了 ES6 语法支持,并重构代码解析、压缩算法,使得执行效率与压缩率都有较大提升,并且Webpack5.0 后默认使用 Terser 作为 JavaScript 代码压缩器

简单实用:

// webpack.config.js
module.exports = {
  //...
  optimization: {
    minimize: true
  }
}

需要注意的是在生产模式中构建时,Terser压缩是默认开启的

当然它也允许你通过提供一个或多个定制过的TerserPlugin实例,覆盖默认的压缩工具,实现更精细的压缩功能

// webpack.config.js
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
  //...
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
        },
      })
    ]
  }
}

在Webpack4中 默认使用 uglifyjs-webpack-plugin压缩代码,也可以通过 minimizer 数组替换为 Terser 插件

压缩CSS

CSS代码同样也可以使用webpack来进行压缩,比较常见的CSS压缩工具有:cssnanocss-minimizer-webpack-plugin

对于 webpack5 或更高版本,官方推荐使用 CssMinimizerWebpackPlugin,该插件是使用 cssnano 优化和压缩 CSS,支持缓存和并发模式下运行。

安装:

npm i css-minimizer-webpack-plugin

配置:

// webpack.config.js

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 用压缩css
const MiniCssExtractPlugin = require("mini-css-extract-plugin");  // 用来提取css成单独的文件

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /.css$/,
        // 注意,MiniCssExtractPlugin.loader 与 style-loader不能同时使用
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  optimization: {
    minimize: true,
    minimizer: [
      // Webpack5 之后,约定使用 '...' 字面量保留默认 minimizer 配置
      "...",
      new CssMinimizerPlugin(),
    ],
  },
  plugins: [new MiniCssExtractPlugin()],
};

⚠️这里需要注意的是需要使用 mini-css-extract-plugin 将 CSS 代码抽取为单独的 CSS 产物文件,这样才能命中 css-minimizer-webpack-plugin 默认的 test 逻辑。

压缩HTML

我们之前使用的html-webpack-plugin,它除了可以生成html模版,也可以用来对html进行压缩。

htmlWebpackPlugin常见参数

  • template:模板的路径,默认会去寻找 src/index.ejs 是否存在。

  • filename:输出文件的名称,默认为 index.html

  • inject:是否将资源注入到模版中,默认为 true

  • minify:压缩参数。在生产模式下(production),默认为 true;否则,默认为false

// webpack.config.js

module.exports = {
  // ...
  plugins: [
    // ...
    new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'index.html',
            minify: true
        }),
  ]
}

生成的 HTML 将使用 html-minifier-terser 和以下选项进行压缩,所以它实际上的压缩功能其实是html-minifier-terser来实现的,更多配置可以查看这个工具文档

{
  collapseWhitespace: true,
  keepClosingSlash: true,
  removeComments: true,
  removeRedundantAttributes: true,
  removeScriptTypeAttributes: true,
  removeStyleLinkTypeAttributes: true,
  useShortDoctype: true
}

禁止生成LICENSE文件

经过上面这些配置后,我发现了一个奇怪的问题,那就是每个bundle产物都多了一个同名的LICENSE.txt文件,打开一看里面都是一些注释内容。

w-license.png

为什么会生成这些文件,带着疑惑我去翻了下官方文档,Webpack5 默认压缩代码工具为terser-webpack-plugin,那就先从它入手吧。

在它的配置中找到了extractComments参数,默认值为true,表示将注释剥离到单独的文件中。

w-ext.png

如果我们不想要,直接关掉该配置就行了

module.exports = {
  // ...
  optimization: {
        minimize: true,
        minimizer: [
            new cssMinimizerPlugin(),
            new terserPlugin({
                extractComments: false,  // 关闭注释剥离功能
            }),
            '...'
        ]
    },
}

CSS增强(autoprefixer)

前端最头疼的问题莫过于处理兼容性,因为前端的运行环境并不固定,可以在各种浏览器以及各种webview中运行,并且每个浏览器厂商对CSS的写法也各不相同,这就势必会导致出现一些问题。

比如为了兼容各种浏览器内核,圆角属性应该这样写:

.container {
  -moz-border-radius: 16px;
  -webkit-border-radius: 16px;
  -o-border-radius: 16px;
  border-radius: 16px;
}

试想一下如果在开发中需要你这样写,那是不是太不合理了?

我们一般都会通过webpack配置插件来帮我们解决这个问题,处理CSS我们首先会想到postcss,没错webpack也有使用postcss处理CSS的loader --- postcss-loader,然后我们还需要使用postcss的插件autoprefixer来帮我们自动添加浏览器前缀。

安装:

npm i postcss postcss-loader autoprefixer

修改配置:

// webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [
      //...
      {
        test: /\.css$/,
        use: [miniCssExtractPlugin.loader, 
              'css-loader', 
              {
                loader: 'postcss-loader',
                options: {
                  postcssOptions: {
                    plugins: ['autoprefixer']
                  }

                }
              }]
      },
    ]
  }
  //...
}

⚠️这里需要注意的是,如果你想自定义转换的规则,最好是将 autoprefixer 的 browsers选项替换为 browserslist 配置。在 package.json 或。Browserslistrc 文件。使用 browsers选项可能导致错误,并且browserslist 配置可以用于 babel、 autoprefixer、 postcss-norize 等工具。

比如package.json中配置browserslist:

// package.json
{
  //...
  "browserslist": [
    "last 10 Chrome versions",
    "last 5 Firefox versions",
    "Safari >= 6", 
    "ie> 8"
  ] 
}

此时我们打包的CSS的产物就会自动添加浏览器前缀

w8.png

静态资源拷贝

假如我们需要在html中引用一些不需要打包处理的资源,比如下面这种情况

w9.png

index.html中引入了一些日志的工具函数,这时候我们直接跑起来会发现这个文件直接404了,这是怎么回事?

首先我们写的路径肯定是没问题的,问题在于我们打包后这个utils文件肯定是不在这个位置了,所以会报404

w10.png

所以这里我们需要使用copy-webpack-plugin将文件拷贝至dist目录下

// webpack.config.js
const copyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
  // ...
  plugins: [
    new copyWebpackPlugin({
      patterns: [
        {from: 'module', to: __dirname + '/dist/module/'}
      ]
    }),
  ]
}

此时再打包,我们会发现dist目录下已经有了module/utils.js,并且页面也不会再报404了

w11.png

sourcemap

SourceMap 就是一个信息文件,里面储存着代码的位置信息。这种文件主要用于开发调试,现在代码都会经过压缩混淆,这样报错提示会很难定位代码。通过 SourceMap 能快速定位到源代码,并进行调试。

比如我们没有开启sourcemap,然后开发过程中报错了,它的报错信息是这样的:

w12.gif

定位过去是打包后的内容,这样的话对我们排查报错非常不方便。

当我们开启sourcemap,再来看看这个同样的报错是怎样的:

// webpack.config.js
module.exports = {
  // ...
  devtool: 'eval-cheap-module-source-map',
}

w13.gif

此时的报错指向就非常清晰了~

关键字

devtool的值有20多种,并且都是由以下七种关键字的一个或多个组成

  • eval 关键字

devtool 值包含 eval 时,生成的模块代码会被包裹进一段 eval 函数中,且模块的 Sourcemap 信息通过 //# sourceURL 直接挂载在模块代码内

  • source-map 关键字

devtool 包含 source-map 时,Webpack 才会生成 Sourcemap 内容

  • cheap 关键字

devtool 包含 cheap 时,生成的 Sourcemap 内容会抛弃维度的信息,这就意味着浏览器只能映射到代码行维度

  • module 关键字

module 关键字只在 cheap 场景下生效,例如 cheap-module-source-mapeval-cheap-module-source-map。当 devtool 包含 cheap 时,Webpack 根据 module 关键字判断按 loader 联调处理结果作为 source,还是按处理之前的代码作为 source

  • nosources 关键字

devtool 包含 nosources 时,生成的 Sourcemap 内容中不包含源码内容 —— 即 sourcesContent 字段

  • inline 关键字

devtool 包含 inline 时,Webpack 会将 Sourcemap 内容编码为 Base64 DataURL,直接追加到产物文件中

  • hidden 关键字

通常,产物中必须携带 //# sourceMappingURL= 指令,浏览器才能正确找到 Sourcemap 文件,当 devtool 包含 hidden 时,编译产物中不包含 //# sourceMappingURL= 指令

devtool的值以及各自的功能可以在webpack文档上查看

如何选择

  • 对于开发环境,适合使用:
    • eval:速度极快,但只能看到原始文件结构,看不到打包前的代码内容;
    • cheap-eval-source-map:速度比较快,可以看到打包前的代码内容,但看不到 loader 处理之前的源码;
    • cheap-module-eval-source-map:速度比较快,可以看到 loader 处理之前的源码,不过定位不到列级别;
    • eval-source-map:初次编译较慢,但定位精度最高;
  • 对于生产环境,则适合使用:
    • source-map:信息最完整,但安全性最低,外部用户可轻易获取到压缩、混淆之前的源码,慎重使用;
    • hidden-source-map:信息较完整,安全性较低,外部用户获取到 .map 文件地址时依然可以拿到源码,慎重使用;
    • nosources-source-map:源码信息缺失,但安全性较高,需要配合 Sentry 等工具实现完整的 Sourcemap 映射。

解决跨域

在开发过程中,我们势必会遇到跨域问题,对于本地开发我们一般可以通过配置代理来解决

我们先来简单写一个接口:

const express = require('express')
const app = express()
app.get('/api/getInfo', (req, res) => {
    res.json({
        code: 200,
        data: {
            name: 'nanjiu',
            age: 18
        }
    })
})

app.listen(3000, () => {
    console.log('服务已启动~')
})

然后把服务跑起来,再到vue项目中去调用

const getInfo = async () => {
    try {
        const res = await axios.get('http://localhost:3000/api/getInfo')
        console.log(res)
    } catch(err) {
        console.log(err)
    }
}

这时候你会发现接口调用跨域了

w14.png

配置代理

接着我们再来通过webpack配置代理解决跨域问题,由于我们本地使用了webpack-dev-server,所以我们可以直接通过它来配置

// webpack.config.js
module.exports = {
  // ...
  devServer: {
    hot: true,
    open: true,
    proxy: {
      '/api': 'http://localhost:3000'
    }
  }
}

这个时候我们的接口请求就正常了

w15.png

由于篇幅问题,这篇文章就介绍到这里了,后面会接着更新webpack更多高级用法。

如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新文章~

标签:文件,hash,进阶,...,module,js,webpack,用法
From: https://www.cnblogs.com/songyao666/p/17502360.html

相关文章

  • 强化学习从基础到进阶-常见问题和面试必知必答[4]::深度Q网络-DQN、double DQN、经验回
    强化学习从基础到进阶-常见问题和面试必知必答[4]::深度Q网络-DQN、doubleDQN、经验回放、rainbow、分布式DQN1.核心词汇深度Q网络(deepQ-network,DQN):基于深度学习的Q学习算法,其结合了价值函数近似(valuefunctionapproximation)与神经网络技术,并采用目标网络和经验回放等方法进......
  • 强化学习从基础到进阶-案例与实践[4.1]:深度Q网络-DQN项目实战CartPole-v0
    强化学习从基础到进阶-案例与实践[4.1]:深度Q网络-DQN项目实战CartPole-v01、定义算法相比于Qlearning,DQN本质上是为了适应更为复杂的环境,并且经过不断的改良迭代,到了NatureDQN(即VolodymyrMnih发表的Nature论文)这里才算是基本完善。DQN主要改动的点有三个:使用深度神经网络替......
  • MySQL 进阶语法
    selectinto语法在MySQL中,SELECTINTO语法用于将查询结果插入到一个新表或已存在的表中。下面是SELECTINTO的语法示例:创建一个新表并将查询结果插入其中:CREATETABLEnew_table_nameSELECTcolumn1,column2,...FROMoriginal_tableWHEREcondition;这将从ori......
  • numpy中的np.random用法
    转载自:https://blog.csdn.net/Candyerer/article/details/111300215一、np.random.rand():生成指定维度的[0,1)间的随机数np.random.rand(4,3);///生成4行3列的数组,数组中内一个元素都是[0,1)间的随机数二、np.random.random():生成指定维度的[0,1)间的随机数np.random.rando......
  • python中dict.copy()用法
    浅拷贝和深拷贝浅拷贝:只会对最表层的进行拷贝,也就是相当于二者共有深拷贝:会申请一块新的内存进行存储,相当于自己独占对于dict的copy来说,是对最表层的键值对进行了深拷贝,举例来说:a={'one':1,'two':2,'three':[1,2,3]}b=a.copy()b从a拷贝过来的是{'one':1,'two':......
  • 仙境传说脚本RO:NPC对话| mes/next/close函数用法详解
    仙境传说脚本RO:NPC对话|mes/next/close函数用法详解大家好,我是艾西,今天跟大家讲解下仙境传说mes/next/close函数,在游戏中所有的NPC对话都是用mes函数来创建的。我们先打开官方文档的script_commands.txt文件,搜索*messearch*mesmes"Hello,world!";注意:默认是没有关团或next按......
  • k8s进阶4-应用无损发布之健康检查
    一、配置探针kubernetes提供了三种探针(支持exec、tcp和http方式)来探测容器的状态:LivenessProbe:容器存活性检查,用于判断容器是否健康,告诉kubelet一个容器什么时候处于不健康的状态。如果LivenessProbe探针探测到容器不健康,则kubelet将删除该容器,并根据容器的重启策略做相应......
  • # yyds干货盘点 # 盘点Pandas中数据删除drop函数的一个细节用法
    大家好,我是皮皮。一、前言前几天在Python最强王者群有个叫【Chloe】的粉丝问了一个关于Pandas中的drop函数的问题,这里拿出来给大家分享下,一起学习。二、解决过程下图是粉丝写的代码。index是索引的意思,我感觉这块写在一起了,看上去不太好理解,在里边还多了一层筛选。这里给出【月神】......
  • 盘点Pandas中数据删除drop函数的一个细节用法
    大家好,我是皮皮。一、前言前几天在Python最强王者群有个叫【Chloe】的粉丝问了一个关于Pandas中的drop函数的问题,这里拿出来给大家分享下,一起学习。二、解决过程下图是粉丝写的代码。index是索引的意思,我感觉这块写在一起了,看上去不太好理解,在里边还多了一层筛选。这里给出......
  • 【linux命令】“最强大的编辑器”vim用法简介(基础篇)
    vim编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器。它主要分为命令令行模式、插入模式和底行模式这三种,下面主要介绍一下这三种模式最简单常用的用法。一.命令行模式1.移动光标左移:h光标右移:l光标上移:k光标下移:j光标向右移动一个单词:w(联......