问:为什么需要 webpak?

场景:在打包工具出现之前,假设一个HTML文件中需要引入很多外部文件,且由于js的单线程处理,相互依赖的文件还不能颠倒,否则会引起项目的崩溃,这么开发会有很大的负担,代码也很难扩展
image.png
然后,我们将这些文件按照一定的顺序合并到一个文件中去
image.png
虽然它解决了加载多个 js 文件的问题,但是可能会导致很多其他的问题:

1. 没有打包工具出现的问题

① 作用域问题

使用 jQuery,lodash,bootstrap 等库的时候,它们会给 windows 对象上绑定全局的变量,如:jQuery 会给 windows 上绑定 $(window.$xxx),lodash 会绑定下划线 _ (window._xxx)。互联网自己的文件可能也会在全局上面绑定自己的变量,这些变量会严重的污染 windows 对象,使 window 对象变得臃肿

② 文件太大的问题

如上:如果一个HTML中的多个 js 文件分散加载,那么页面的内容会随着文件的加载而逐渐显现出来。但如果将这多个文件合成一个 js 文件,那么这个脚本将会带来网络瓶颈,用户得等待一段时间才能看到内容,会有短暂的白屏,用户体验差

③ 可读性差、可维护性差

如果我们将所有代码都合并在一个超大的文件里,那么这对文件的可读性以及可维护性将会是灾难

2. 早期解决作用域问题的方法

Grunt、Gulp 解决作用域问题的方法:
早期我们用任务执行器—Grunt、Gulp来管理项目资源,这两个工具是将所有的项目文件拼接在一起,原理是利用了 JS 的立即调用函数表达式(IIFE),解决了大型项目作用域问题,当脚本封装在 IIFE 内部的时候,可以安全的拼接和组合所有的文件,而不必担心作用域冲突问题

什么是 IIFE ?

  1. // 写个 ; 表示在压缩代码的时候不会有问题
  2. ;(function() {
  3. var sub = '前端'
  4. })()
  5. // console.log(sub)
  6. // sub is not defined

上述代码显示:当函数变成一个立即调用的表达式以后,表达式的变量是不能在外部访问的,即不会污染 window 环境,也就解决了作用域问题
当然也可以在外部获取作用域内暴露的内容

  1. const res = (function() {
  2. var sub = '前端'
  3. return sub
  4. })()
  5. console.log(res)

如此:既解决了作用域问题,又可以在外部获取作用域内想暴露的内容

新的问题:若只修改一个文件(超大文件)中的部分代码,则整个文件都要重新编译。所以我们需要一些方法将这个超大文件拆成一个个方法的模块

3. 解决代码拆分的方法

解决方法:node.js 中引入了一个叫 require 的机制,它允许我们在当前的文件中去加载和使用某个模块,导入需要的每个模块,这个开箱即用的功能帮助我们解决了代码的拆分问题

例:
math.js

  1. const add = (a, b) =>{
  2. return a + b
  3. }
  4. const minus = (a, b) => {
  5. return a - b
  6. }
  7. module.exports = {
  8. add,
  9. minus
  10. }

serve.js

  1. // 将 math.js 当做模块引入
  2. const math = require('./math')
  3. console.log(math.add(1, 2), math.minus(9, 2))

但 CommonJS 没有浏览器支持。没有 live binding(实时绑定)。循环引用存在问题。同步执行的模块解析加载器速度很慢。虽然 CommonJS 是 Node.js 项目的绝佳解决方案,但浏览器不支持模块,似乎又遇到了新问题

  1. <script src="./commonJS/serve.js"></script>
  2. // Uncaught ReferenceError: require is not defined

4. 让浏览器支持模块的方法

在早期,能够使用 browserify 或者 requirejs 这样的打包工具来编写能够在浏览器中运行 commonjs 的模块的代码

例:使用 requirejs 来编写代码,它提供了 define 方法暴露模块,require 方法引入模块

  • define 方法有两个参数:
  • 第一个参数是一个数组,它定义了当前模块依赖的其他模块;
  • 第二个参数是一个回调函数,这个回调函数是这个模块对外暴露的接口,函数体的返回值为当前模块需要暴露出去的内容

  • require 方法有两个参数:
  • 第一个参数是一个数组,它定义了当前模块依赖的其他模块;
  • 第二个参数是一个回调函数,这个回调函数的参数为引入模块中暴露的内容

① 先定义两个模块:add.js minus.js

  1. // add.js
  2. const add = (a, b) => {
  3. return a + b
  4. }
  5. define([], function() {
  6. return add
  7. });
  8. // minus.js
  9. const minus = (a, b) => {
  10. return a - b
  11. }
  12. define([], function() {
  13. return minus
  14. });

② 在使用 requireJS 之前需要在html文件上引入 requirejs 的包,并设置 requirejs 的入口文件(即设置属性 data-main 的路径)

  1. <script
  2. src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"
  3. data-main="./requireJS/main.js"
  4. ></script>

③ main.js

  1. require(['./add', './minus'], function(add, minus) {
  2. let sum = add(2, 5)
  3. let diff = minus(8, 5)
  4. console.log(sum, diff)
  5. })

这样就可以在浏览器的控制台上打印结果了,这就是早期拆分模块的方法

但现在:模块已经成为 ECMAScript 官方的一个标准,它用 export default 导出模块,用 import xxx from ‘模块路径’ 导入模块
例:
① 先定义一个模块

  1. // math.js
  2. const add = (a, b) => {
  3. return a + b
  4. }
  5. const minus = (a, b) => {
  6. return a - b
  7. }
  8. export default {add, minus}

② 在 html 上引入并使用该模块

两个注意点:

  • 要在 script 标签上加入 type=’module’,否则报错:Cannot use import statement outside a module
  • 在终端上输入 npx http-server 命令,开启服务。(不开启服务会产生跨域),然后在浏览器上输入网址即可访问

    1. <script type="module">
    2. import math from './ES/math.js'
    3. console.log(math.add(1,2), math.minus(9, 4))
    4. </script>

    ECMAScript 的缺点:浏览器对它的支持还不是很完整,版本迭代也不够快

答: webpack 解决以上所有问题

webpack 不仅可以让我们编写模块,而且还支持任何模块格式(同时支持 es6 和 commonjs ),并且可以同时处理 resource 和 assets。它是一个工具,可以打包你的 JavaScript 应用程序(支持 ESM 和 CommonJS),可以扩展为支持许多不同的静态资源,例如:images, fonts 和 stylesheets。
webpack 关心性能和加载时间;它始终在改进或添加新功能,例如:异步地加载和预先加载代码文件,以便为你的项目和用户提供最佳体验。
image.png

webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具;

借助于webpack这个前端自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能

  1. 在没有使用webpack之前:
  2. 举个例子:index.html里面有一大堆的cssjs文件,如a.js b.js c.js d.js等等
  3. 1a.js要用到b.js里面的一个函数,则a.js要放在b.js后面
  4. 2c.js要用到a.js里面的一个函数,则c.js要放在a.js后面
  5. 3b.js又要用到d.js文件里面的函数,则b.js就要放在d.js后面
  6. 如果有N多个js文件,需要手动处理他们的关系,即容易出错。
  7. 使用webpack
  8. webpack的理念就是一切皆模块化,把一堆的css文件和js文件放在一个总的入口文件,通过require引入,
  9. 剩下的事情webpack会处理,包括所有模块的前后依赖关系,打包、压缩、合并成一个js文件,
  10. 公共代码抽离成一个js文件、某些自己指定的js单独打包,模块可以是css/js/images/font等等。