Webpack

182 阅读10分钟

Webpack相关知识

Webpack并不会对ES6语法进行转换

  • 需要通过插件babel-loader

Webpack模块加载方式

遵循ES Modules标准的import声明

ES_Module.png

遵循CommonJS标准的require函数

CommonJs_require.png

遵循AMD标准的define函数和require函数

Loader加载的非javascript也会触发资源加载

  • css-loader加载的css文件
    • 样式代码中的@import指令和部分属性的url函数
  • html代码中图片标签的src属性

WebPack的核心工作原理

  • Loader机制是Webpack的核心
  • Loader可以实现各种各样资源的加载

WebPack开发一个Loader

  • Loader负责资源文件从输入(Source)到输出(return)的转换
  • Loader是一个管道的概念,对于同一个资源可以依次使用多个Loader

WebPack插件机制(核心特性)

  • 两个核心:loader和插件机制
  • loader专注实现资源模块加载
  • plugin解决其他自动化工作

plugin

  • 拷贝静态文件至输出目录
  • 压缩输出代码

WebPack自动清除输出目录插件

  • clean-webpack-plugin
  • 使用步骤:
    • yarn add clean-webpack-plugin --dev

WebPack常用插件

clean-webpack-plugin

html-webpack-plugin

  • 自动生成使用bundle.js的HTML
  • 不同于clean-webpack-plugin,它自己导出的就是一个插件的类型,不再需要解构。
  • 示例
// --------webpack.config.js文件-------
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

出现js路径引用错误

  • 检查publicpath:"",将其注释掉

自定义输出html文件内容的方式

  • 实例对象中传参
  • 创建index.html模板,让插件根据模板来生成html文件

同时输出多个页面的文件

  • 通过添加多个实例,达到输出多个文件的目的
  • 每个对象来负责生成一个html文件
  • 演示
plugins:[
  // 生成index.html
  new HtmlWebpackPlugin({
    title:'Webpack Plugin Sample',
    meta:{
      viewport: 'width=device-width'
    },
    template:'./src/index.html'
  }),
  // 生成about.html
  new HtmlWebpackPlugin({
    filename:'about.html'
  })
]

copy-webpack-plugin

  • 将静态文件同步拷贝到打包目录下
  • 注意:开发阶段最好不要使用这个插件
  • 实例
new CopyWebpackPlgin([
  'public'
  // 可以写文件夹 或者 相对路径 public/**
])

Webpack开发一个插件

  • plugin通过钩子机制实现
  • 插件必须是:一个函数 或者 一个包含apply方法的对象
  • 去掉文件中的注解,可以使用正则表达式来解决

自定义插件.png

  • 通过在生命周期的钩子中挂载函数实现扩展

增强Webpack开发体验

  • 常规:编写源代码 -> 打包 -> 运行应用 -> 刷新浏览器

Webpack自动编译

  • watch工作模式:监听文件变化,自动重新打包
  • 操作:yarn webpack --watch

WebPack自动刷新浏览器

  • BrowserSync: 实现自动刷新浏览器
  • 步骤
    • 关闭http的server
    • 运行 browser-sync dist --files "**/*"
  • 原理:webpack自动打包源代码到dist目录,dist文件的变化被browser-sync监听,就会实现自动编译并且自动刷新浏览器。

该方法的弊端

  • 但是这样就会产生两次磁盘读写操作,效率不高
  • 并且需要同时使用两个工具

Webpack Dev Server

  • 集成自动编译和自动刷新浏览器等功能

优点

  • 为了提高效率,并没有将打包内容放在目录下,而是暂时放在了内存当中。(减少磁盘操作)
  • 浏览器从缓存中读取信息,进行加载。

代理API服务

  • 跨域请求问题,使用跨域资源共享(CORS)
    • 前提是 API要支持CORS

问题:开发阶段接口跨域问题

目标:将github的API代理到本地的开发服务器

代理API.png

  • 查阅:主机名是HTTP协议中的相关概念

问题(访问静态资源文件)

  • 除了打包的资源能够被访问以外,如果还想访问一些静态资源文件,该怎么办呢?
  • 通过配置contentBase,路径,就可以访问到静态资源了。(额外为开发服务器指定查找资源目录)

问题(自动刷新导致的页面状态丢失)

  • 最好的状态:页面不刷新的情况下,模块也可以及时更新
  • Webpack HMR

Webpack HMR(模块热替换/更新)

  • 可以在应用程序的运行过程中实时替换某个模块,而应用的运行状态不受影响.

热更新和自动刷新之间的差异

  • 自动刷新会导致页面状态丢失
  • 热替换/更新只将修改的模块实时替换至应用中,不会完全刷新应用

Webpack 开启 HMR

  • HMR已经集成在webpack-dev-server中,不需要单独安装模块

步骤

  • 在运行webpack-dev-server时,在后面添加--hot,开启热更新功能
webpack-dev-server --hot
  • 或者在配置文件中添加配置,开启热更新功能
  • 问题:如何实现所有模块的热更新替换(按上述步骤后,只有html可以实现,js等不能实现)

HMR并不可以开箱即用

  • 因为js脚本文件,不像html只是做一个简单的替换,其还具有自身的逻辑,因此需要我们自己去书写逻辑替换语句,才可以实现.
  • 在使用脚手架框架中,也不用自己去写,是因为框架的文件本身就有一定的规则,并且其也会自身集成HMR.

Source Map 源代码地图

  • 映射转换后的代码和源代码之间的关系

解决的问题

  • 在前端方向引入了构建编译之类的概念之后,导致源代码与运行的代码不一样,所产生的调试的问题。

WebPack配置Source Map

webpack配置Source_Map.png

  • Webpack支持12种不同的方式,每种方式的效果和效率各不相同。

Webpack生产环境优化

  • 生产环境注重运行效率
  • 开发环境中注重开发效率
  • 为不同的工作环境创建不同的配置

Webpack不同环境下的配置

  • 1.配置文件根据环境不同导出不同配置

webpack开发环境.png

  • 执行命令: yarn webpack --env production

  • 返回生产模式下的配置

  • 2.一个环境对应一个配置文件(适用于大型项目)

    • 开发模式配置 webpack.dev.js
    • 生产模式配置 webpack.prod.js
    • 公共配置(抽象两者之间公共的配置)webpack.common.js
    • 使用步骤:assign()方法会将配置文件同名的属性直接重写,因此不行。这里就可以使用merge
      • yarn webpack-merge --dev
      • const common = require('./webpack.common');
      • module.exports = merge(.....);
          module.export = merge(common,{
            mode: 'production',
            plugins: [
              new ClearWebpackPlugin(),
              new CopyWebpackPlugin(['public'])
            ]
          });
        
      • yarn webpack --config webpack.prod.js
      • 简化命令,下载scripts标签中
          // ------------package.json-----------
          "scripts": {
            "build": "webpack --config webpack.prod.js"
          }
        
  • 模式(mode)

Webpack优化打包结果

DefinePlugin

  • 为代码注入全局成员
  • 注意:这里的书写是一个符合js的逻辑代码

DefinePlugin.png

Tree Shaking

  • 摇掉代码中未被引用的部分 -> 未引用代码dead-code
  • 生产模式下,自动开启 mode:production
yarn webpack --mode production

Tree Shaking使用

  • none模式

除了生产模式,使用tree-shaking方法.png

  • usedExports负责标记[枯树叶]
    • 没有使用的就不导出
  • minimize负责[摇掉]它们
    • 去除掉没有导出的代码

Webpack合并模块函数 Scope Hoisting

  • 还可以追加concatenateModules继续优化输出
  • 普通的打包输出,是将我们的每一个模块,单独的放在一个函数中,这样的话,当我们的模块很多,也就意味着会有很多的模块函数。

使用concatenateModules

  • 可以将所有的模块,输出到一个函数中
  • 优点:
    • 尽可能的将所有模块合并输出到一个函数中
    • 既提升了运行效率,又减少了代码的体积。
    • 额外补充:minimize也可以减少代码的体积,两者可以配合使用。

concatenateModules.png

Webpack Tree Shaking 与 Babel

  • Tree Shaking前提是ES Modules组织代码,也就是Webpack打包的代码必须是使用ESM
  • Webpack打包之前,先是将模块根据配置交给不同的loader去处理,最后将所有loader处理的结果打包到一起。
  • 而为了转换代码中ECMAScript新特性,会使用babel-loader,这个loader在转换的过程中就有可能将ES Modules 转换成 CommonJS,总的来说:这取决于我们有没有使用转换ES Modules的插件
  • 在我们使用的preset-env这个集合里面,就有这个插件。

解决方法

Webpack_Tree_Shaking与Babel.png

Webpack4中新增了sideEffects(副作用)

  • 允许通过配置的方式去标识代码是否有副作用,从而为Tree Shaking提供更大的压缩空间

副作用:模块除了导出成员之外所作的事情。

  • sideEffects一般用于npm包标记是否有副作用
  • production模式下,同样也会自动开启
  • 没有副作用的模块,就不会被打包

步骤

  • 开启功能

开启副作用功能.png

  • 标识代码是否有副作用

标识代码是否有副作用.png

注意事项

  • 如果不表明有副作用,webpack打包的时候会自动忽略有副作用的代码.
  • 举例:模块的原型方法 \ css文件
  • 引用进来即可

标记副作用文件.png

Webpack代码分割 Code Splitting

  • 所有的代码最终都被打包到一起,bundle体积就会过大.但是并不是每个模块在启动时都是必要的.

问题

  • 会浪费掉很多的流量和带宽
  • 解决办法
    • 按照一定的规则,将其分离到多个bundle中,根据应用的需要去加载模块.
    • 提高应用的响应速度,运行效率.

实际的解决方法

多入口打包 Multi Entry

  • 适用于:传统的多页应用程序
  • 一个页面对应一个打包入口,不同页面之间公共部门单独提取.

多入口打包.png

  • 在输出的html中指定需要注入的bundle

打包成功后只引入自己对应的js文件.png

提取公共模块

  • 不同的入口中肯定会有公共模块
  • 将所有公共的模块提取到一个bundle中
    • 开启功能

提取所有公共模块.png

动态导入 Dynamic Imports

  • 按需加载: 需要用到某个模块时,再加载这个模块.
  • 动态导入的模块会被自动分包
  • 步骤:
    • 无需配置任何地方,前提需要根据ES Module动态导入成员的方式,去导入模块
    • webpack会自动分包

比较

  • 动态导入比多入口打包更灵活
    • 可以更灵活的去控制需不需要加载某个模块
    • 或者什么时候需要加载这个模块

webpack动态导入.png

Webpack魔法注释 Magic Comments

魔法注释.png

  • 如果名称相同,最后会被打包到一起

Webpack MiniCssExtractPlugin

  • 提取css到单个文件
  • 实现css的按需加载

使用步骤

  • yarn add mini-css-extract-plugin --dev
  • 导入插件模块 webpack.config.js
  • 修改使用的loader

css提取单个css文件.png

注意

  • 只有当css文件超过了150kb才可以考虑是否需要提取到单个的css文件
  • 因为当文件较小的时候,嵌入到文件中,就可以减少一次请求,效果会更好.

Webpack OptimizeCssAssetsWebpackPlugin

  • 压缩输出的css文件

使用步骤

  • yarn add optimize-css-assets-webpack-plugin --dev
  • 在minimizer的管理下,使用这个插件.(只有开启minimizer服务,才会运行这个插件,生产模式下默认开启minimizer)

开启minimizer.png

出现问题

  • js文件没有压缩成功
  • 解决方法: 添加js压缩插件 TerserWebpackPlugin

Webpack输出文件名Hash (substitutions)

  • hash

hash.png

  • chunkhash:是同一路的输出文件,hash相同

chunkhash同一个chunk.png

chunkhash.png

  • contenthash: 根据不同的文件就会生成不同的hash值

contentHash.png

总结

  • contentHash是解决缓存问题,最好的方式.
  • 因为其精确的定位到了文件级的hash,只有文件发生了变化才会更新文件名

指定hash的长度

  • 如果觉得20位hash比较长
  • 指定缓存问题最好的选择

指定长度.png

VSCode 中折叠代码的快捷键

  • Ctrl + K,Ctrl + 0 (macOS:Command + K,Command + 0)