浅析Webpack

297 阅读6分钟

官方概念

本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。

详细内容可参见webpack官方手册

loader和plugin的区别

简易回答(翻译+解释+举例)

loader,加载器,用来load文件

如:

  • babel loader是用来加载高级的js把它变成IE支持的js
  • css loader和style loader是用来加载css然后把它变成页面中的style标签

plugin,插件,用来扩展webpack的功能

  • HtmlWebpackPlugin(html插件)用来生成html文件
  • MiniCssExtractPlugin(css插件)用来把多个css合并成一个css文件

复杂回答

从功能作用的角度区分:

  1. loader:

     loader从字面的意思理解,是加载的意思。

      由于webpack 本身只能打包commonjs规范的js文件,所以,针对css,图片等格式的文件没法打包,就需要引入第三方的模块进行打包。

      loader虽然是扩展了 webpack ,但是它只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译。

      loader是运行在NodeJS中。

      仅仅只是为了打包,仅仅只是为了打包,仅仅只是为了打包,重要的话说三遍!!!

       

如:css-loader和style-loader模块是为了打包css的

      babel-loader和babel-core模块时为了把ES6的代码转成ES5

      url-loader和file-loader是把图片进行打包的。

       

2、plugin是做什么的?

     plugin完成的是loader不能完成的功能,中文为插件

    plugin也是为了扩展webpack的功能,但是 plugin 是作用于webpack本身上的。而且plugin不仅只局限在打包,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共模块,避免首屏加载的bundle文件,或者按需加载的bundle文件体积过大,导致加载时间过长,是一把优化的利器。而在多页面应用中,更是能够为每个页面间的应用程序共享代码创建bundle。

      插件可以携带参数,所以在plugins属性传入new实例。

如:

1)、针对html文件打包和拷贝(还有很多设置)的插件:html-webpack-plugin。

       不但完成了html文件的拷贝,打包,还给html中自动增加了引入打包后的js文件的代码(),还能指明把js文件引入到html文件的底部等等。

具体使用,可以查看:webpack打包(主要是处理html文件),并启动服务器

 

从运行时机的角度区分

 

  1. loader运行在打包文件之前(loader为在模块加载时的预处理文件)
  2. plugins在整个编译周期都起作用。

为什么选择 webpack

想要理解为什么要使用 webpack,我们先回顾下历史,在打包工具出现之前,我们是如何在 web 中使用 JavaScript 的。

在浏览器中运行 JavaScript 有两种方法。第一种方式,引用一些脚本来存放每个功能;此解决方案很难扩展,因为加载太多脚本会导致网络瓶颈。第二种方式,使用一个包含所有项目代码的大型 .js 文件,但是这会导致作用域、文件大小、可读性和可维护性方面的问题。

  • 立即调用函数表达式(IIFE) - Immediately invoked function expressions IIFE 解决大型项目的作用域问题;当脚本文件被封装在 IIFE 内部时,你可以安全地拼接或安全地组合所有文件,而不必担心作用域冲突。

IIFE 使用方式产生出 Make, Gulp, Grunt, Broccoli 或 Brunch 等工具。这些工具称为任务执行器,它们将所有项目文件拼接在一起。

但是,修改一个文件意味着必须重新构建整个文件。拼接可以做到很容易地跨文件重用脚本,但是却使构建结果的优化变得更加困难。如何判断代码是否实际被使用?

即使你只用到 lodash 中的某个函数,也必须在构建结果中加入整个库,然后将它们压缩在一起。如何 treeshake 代码依赖?难以大规模地实现延迟加载代码块,这需要开发人员手动地进行大量工作。

  • 感谢 Node.js,JavaScript 模块诞生了 Node.js 是一个 JavaScript 运行时,可以在浏览器环境之外的计算机和服务器中使用。webpack 运行在 Node.js 中。

当 Node.js 发布时,一个新的时代开始了,它带来了新的挑战。既然不是在浏览器中运行 JavaScript,现在已经没有了可以添加到浏览器中的 html 文件和 script 标签。那么 Node.js 应用程序要如何加载新的代码 chunk 呢?

CommonJS 问世并引入了 require 机制,它允许你在当前文件中加载和使用某个模块。导入需要的每个模块,这一开箱即用的功能,帮助我们解决了作用域问题。

  • npm + Node.js + modules - 大规模分发模块 JavaScript 已经成为一种语言、一个平台和一种快速开发和创建快速应用程序的方式,接管了整个 JavaScript 世界。

但 CommonJS 没有浏览器支持。没有 live binding(实时绑定)。循环引用存在问题。同步执行的模块解析加载器速度很慢。虽然 CommonJS 是 Node.js 项目的绝佳解决方案,但浏览器不支持模块,因而产生了 Browserify, RequireJS 和 SystemJS 等打包工具,允许我们编写能够在浏览器中运行的 CommonJS 模块。

  • ESM - ECMAScript 模块 来自 Web 项目的好消息是,模块正在成为 ECMAScript 标准的官方功能。然而,浏览器支持不完整,版本迭代速度也不够快,目前还是推荐上面那些早期模块实现。

依赖自动收集 传统的任务构建工具基于 Google 的 Closure 编译器都要求你手动在顶部声明所有的依赖。然而像 webpack 一类的打包工具自动构建并基于你所引用或导出的内容推断出依赖的图谱。这个特性与其它的如插件 and 加载器一道让开发者的体验更好。

  • 这些方式看起来都不是很好…… 是否可以有一种方式,不仅可以让我们编写模块,而且还支持任何模块格式(至少在我们到达 ESM 之前),并且可以同时处理资源和资产?

这就是 webpack 存在的原因。它是一个工具,可以打包你的 JavaScript 应用程序(支持 ESM 和 CommonJS),可以扩展为支持许多不同的资产,例如:images, fonts 和 stylesheets。

webpack 关心性能和加载时间;它始终在改进或添加新功能,例如:异步地加载 chunk 和预取,以便为你的项目和用户提供最佳体验。