webpack诞生的历史背景
前端技术从90年Web生态诞生到现在,其实一直有着沉重的历史包袱,这种包袱来源于浏览器最初的设计不够完善。不像移动设备生态的发展,大部分开发如IOS、Android开发真正发展是从2008年起步,技术向后兼容的难度很小,而web开发则由于浏览器厂商众多、浏览器版本众多、浏览器标准众多等等问题,虽然后续各浏览器统一了标准,但是web开发向后兼容的压力仍然鸭梨山大。
以一个中小公司网站为例,一次版本发布可能导致一半用户无法正常使用,极有可能造成大量用户的流失,这是无法忍受的。而又有很多用户需要新功能来满足需求,这就造成了新需求与旧标准之间尖锐的矛盾。其实这种矛盾一直都在,且愈演愈烈。这种问题就造成前端开发有大量补丁和类似补丁的工具的诞生,诞生这些补丁和工具对于问题的解决依然是治标不治本,整个前端开发生态依然无法实现健康发展。而 webpack 就是一个帮助我们解决这种历史包袱的工具,就当下前端生态而言,可以算得上是最好、最完善的工具。

学习使用webpack需要具备的前提是你对前端开发的历史包袱,也就是新需求和旧标准之间的矛盾有所认识。
webpack是什么
要理解webpack是什么、解决了什么问题,我们首先要了解前端最大的坑:即浏览器在解析 js 时有一个盲点,那就是没有向其他语言一样,实现 js 语言的模块化。简单来说,一个js文件A.js如果想要暴露给另一个js文件B.js一部分数据或变量,你只能将其定义在全局作用域下,没有其他办法。这里的全局作用域就是window对象,而随着工程的逐渐复杂化,保留的变量之间会有各种依赖,且极易被其他文件变量修改,这个问题直到现在也没有得到解决。
而随着node.js的出现和发展,事情出现了些许转机。node.js和浏览器都可以运行js,区别在于node.js可以直接运行在操作系统上,而且node.js提供了模块化引入和导出的功能。
在node.js中,所有文件都是一个module,任何一个模块都要一个入水口和出水口,即exports和require,都是一个对象,在对象中可以定义要共享的内容
1 2 3 4 5 6 7 8 9 10 |
# A.js var msg = 'hello'; module.exports = { msg: msg }; # B.js var msg = require('./b').msg; console.log('msg:', msg); # 运行测试,命令行跳转到当前目录 node A.js |
上述例子就展示了node.js是如何实现模块化的方法,即不使用全局变量,而是维护每个模块的入口和出口,使得每个模块实现局部自治,无需关注对整体的影响。这就解决了浏览器的历史包袱,设计之初没有考虑清楚文件间的代码共享和数据共享,这就是webpack要解决最重要的问题。但是我们使用后端语言的方式编写的前端代码,前端根本就无法识别,因为没有模块化的体系。而webpack就可以动态地将后端代码变成浏览器能读懂的代码,如B.js中的require所需要的模块会被提取出来写入到一个文件,即打包出的js文件,e.g. bundle.js,命名随意,虽然打包完成的js代码非常丑陋,但是我们根本不关注,只需要这个文件体积小、性能好,其他都是浮云。同时webpack也可以打包css、图片等,功能很强大。
这里使用webpack进行打包,将node.js的后端代码转为前端语言,方法如下:
1 |
webpack A.js bundle.js #提供入口文件和打包后的文件名即可 |
打包完成后,只需要在HTML中导入bundel.js即可。
webpack的安装和配置
由于webpack是运行在node.js环境下,所以需要首先配置node.js,这里不赘述了,参见上篇文章介绍NPM的。配置好node环境后,可以直接安装webpack:
1 |
npm i webpack -g #全局安装 |
有时为了避免两台机器之间webpack之间版本差异可能造成的问题,可以不使用全局安装,而是进入到项目路径下,使用”npm init -y”生成package.json文件后,再安装webpack。
1 2 3 4 5 6 7 8 9 10 |
npm i webpack -D # 等价于 npm i webpack --save-dev # 使用安装好的webpack打包文件 ./node_modules/.bin/webpack a.js bundle.js # 可将上面的打包命令配置在package.json文件中的scripts中,使用时"npm run pack"即可。 "scripts": { "pack": "./node_modules/.bin/webpack a.js bundle.js" } # 使用只需要在HTML中引入bundle.js即可 |
由于webpack的功能非常强大,配置项非常多,无论打包前、打包中还是打包后都有很多参数可以配置,如果将所有参数都写在scripts中,会造成难以维护且命令过长的情况,而且有些命令还无法添加到scripts中,所以我们一般将webpack的配置直接写到一个配置文件”webpack.config.js”中,示例如下。
1 2 3 4 5 6 7 |
module.exports = { entry: './a', //入口文件 output: { filename: 'pack.js', //打包输出文件名 path: __dirname //打包文件存储地址,__dirname是node.js中的变量,指代当前目录 } } |
有了这个webpack的配置文件,packages.conf中scripts的pack命令就可以去除后面的参数,仅保留”./node_modules/.bin/webpack”即可。
webpack的多入口和多出口:entry 和 output
由于前段工程日趋复杂,往往我们并不是像上面的示例那样只有一页,而是有多个页面,比如下例所示,每一页都有相同的依赖,也有不同的js控制不同的业务逻辑。
1 2 3 4 5 6 7 8 9 10 |
-webpack.config.js -package.json -package-lock.json -js --base.js --home.js --signup.js -page --index.html: base.js & home.js --signup.html: base.js & signup.js |
这种情况下,使用webpack打包的配置方法参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# webpack.config.js module.exports = { entry: { home: './js/home.js', signup: './js/signup.js' } output: { filename: '[name].bundle.js', //变成动态的文件名,对应entry中的名称,即home和signup path: __dirname + '/dist', //约定惯例,distribution即可分发的 } } # base.js var open = true; module.exports = { open: open, }; # home.js var base = require('./base'); var open = base.open; if (oepn) { document.body.innerHTML = `<a href="signup.html">注册</a>`; } # signup.js var base = require('./base'); var open = base.open; if (oepn) { document.body.innerHTML = `<h1>欢迎入坑</h1>`; } else { document.body.innerHTML = `<h1>暂不开放注册</h1>`; } |
打包完成后,在各自的HTML文件中对应修改打包后的js文件即可。
ES6的写法
有了ES6以后,这种在node.js中编写前端代码的技术就显得略有些多余了,ES6是下一代js,性能更好、更省资源。ES6中在模块间分享数据的方法可以参考下例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# base.js var open = true; exports = { open }; # home.js import {open} from './base'; if (oepn) { document.body.innerHTML = `<a href="signup.html">注册</a>`; } # signup.js import {open} from './base'; if (oepn) { document.body.innerHTML = `<h1>欢迎入坑</h1>`; } else { document.body.innerHTML = `<h1>暂不开放注册</h1>`; } |
不难看出,ES6这种写法非常简洁,而且webpack开箱就支持ES6。
转载请注明:北凉柿子 » 前端爬坑 webpack