webpack 是什么
webpack 是一个用于现代JavaScript 应用程序的静态模块打包工具。
看来像是1个js的打包工具, 但是实际上并没有这么简单
传统html + js 写法1
对于前端新手, 或者被逼写前端的后端开发, 他们写的代码很可能是这样的
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Original</title>
</head>
<body>
<div id="app1"></div>
<script src="./js/app1/app1.js"></script>
</html>
js:
const app = document.getElementById("app1");
const header = document.createElement("div");
header.innerHTML = "It's a header";
app.appendChild(header);
const body = document.createElement("div");
body.innerHTML = "It's a body";
app.appendChild(body);
const footer = document.createElement("div");
footer.innerHTML = "It's a footer";
app.appendChild(footer);
就如上面把所欲header body footer 3个module 都写在1个js
也就是讲1个html 对应1个js
这样写法弊端很明显
- 没有对js里的模块拆分, 当业务复杂时,js又长又丑
- 不方便模块的共享
传统html + js 写法2
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Original</title>
</head>
<body>
<div id="app2"></div>
<script src="./js/app2/header.js"></script>
<script src="./js/app2/body.js"></script>
<script src="./js/app2/footer.js"></script>
</html>
header.js
const app = document.getElementById("app2");
const header = document.createElement("div");
header.innerHTML = "It's a header";
app.appendChild(header);
body.js
//const app = document.getElementById("app2");
const body = document.createElement("div");
body.innerHTML = "It's a body";
app.appendChild(body);
footer.js
//const app = document.getElementById("app2");
const footer = document.createElement("div");
footer.innerHTML = "It's a footer";
app.appendChild(footer);
这种写法,只是简单把原来的js文件拆分成各个module.js
但是弊端也很多
- body.js footer.js 都隐含依赖 header.js 的app 对象, app对象只能在header里创建一次. 这个坑导致以后的维护更加困难, 业务复杂的时候, 无用的模块不能顺便删除.
- html 引用的js顺序不能随便变动。 否则html dom顺序就乱了.
- 还是用面向过程的编程思想
面向对象html + js 写法3
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Original</title>
</head>
<body>
<div id="app3"></div>
<script src="./js/app3/baseObj.js"></script>
<script src="./js/app3/body.js"></script>
<script src="./js/app3/footer.js"></script>
<script src="./js/app3/header.js"></script>
<script src="./js/app3/app3.js"></script>
</html>
baseObj.js
class BaseObj{
constructor(app) { // app is a div obj
this.app = app;
}
createContent(content){
const div = document.createElement("div");
div.innerHTML = content;
this.app.appendChild(div);
}
}
export default BaseObj;
app3.js
const app3 = document.getElementById("app3");
new Header(app3).createContent("It's a Header!");
new Body(app3).createContent("It's a new Body!");
new Footer(app3).createContent("It's a new Footer!");
header.js
class Header extends BaseObj{
/**
* @override
*/
createContent(content){
const div = document.createElement("div");
div.innerHTML = "header:";
this.app.appendChild(div);
super.createContent(content);
}
}
body.js
class Body extends BaseObj{
/**
* @override
*/
createContent(content){
const div = document.createElement("div");
div.innerHTML = "body:";
this.app.appendChild(div);
super.createContent(content);
}
}
footer.js
class Footer extends BaseObj{
/**
* @override
*/
createContent(content){
const div = document.createElement("div");
div.innerHTML = "footer:";
this.app.appendChild(div);
super.createContent(content);
}
}
有点像写java了, 这种写法有了对象和继承的编程思想, 维护和可扩展性更好。
但是还是有一些缺点
- 引用都在html上, js上某些东西不知道怎么来的.
- html要引用多个js文件, 而且顺序也是不能随便调, 虽然header body footer 这3个引用的顺序不重要, 但是app3.js 还是必须在最后.
当运行时, 浏览器要下载多个js, 也就是必须与服务器进行多次沟通, 增加了服务器压力和影响了性能。
使用import + http服务器的 写法4
这次我会使用去解决上面写法3的第一个问题, 尝试在js文件之间生命引用
首先, 我们尝试去在js里引用别的js
header.js
import BaseObj from "./baseObj.js"
class Header extends BaseObj{
/**
* @override
*/
createContent(content){
const div = document.createElement("div");
div.innerHTML = "header:";
this.app.appendChild(div);
super.createContent(content);
}
}
export default Header;
body.js
import BaseObj from "./baseObj.js"
class Body extends BaseObj{
/**
* @override
*/
createContent(content){
const div = document.createElement("div");
div.innerHTML = "body:";
this.app.appendChild(div);
super.createContent(content);
}
}
export default Body;
footer.js
import BaseObj from "./baseObj.js"
class Footer extends BaseObj{
/**
* @override
*/
createContent(content){
const div = document.createElement("div");
div.innerHTML = "footer:";
this.app.appendChild(div);
super.createContent(content);
}
}
export default Footer;
app4.js
import Header from "./header.js"
import Body from "./body.js"
import Footer from "./footer.js"
const app4 = document.getElementById("app4");
new Header(app4).createContent("It's a Header!");
new Body(app4).createContent("It's a new Body!");
new Footer(app4).createContent("It's a new Footer!");
这样js之间的引用关系全部在js里体现了
然后我们在html里只引用app4.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Original</title>
</head>
<body>
<div id="app4"></div>
<script src="./js/app4/app4.js"></script>
</html>
但是执行时我们会遇到一个错误
这是因为import 这关键字其实是ES6 的规范, 浏览器是默认下不能解析这关键字的, 我们需要 把引用script的类型改成module, 而不是默认的text/javascript
<!-- <script src="./js/app4/app4.js"></script> -->
<script type="module" src="./js/app4/app4.js"></script>
再次执行时我们会遇到CORS error
这是因为 我们使用了file协议, 在浏览器运行必须启用1个http 服务器
我们把代码文件放入tomcat运行
这次引用成功了, 但是虽然我们在html文件只声明了app4.js 的引用, 但是实际上浏览器还是需要去下载app4.js依赖的其他引用。
所以写法3的第二个问题未解决。
而且调试时需要http 服务器, 并不是很方便。
使用webpack 的写法5
这次我们要解决多次下载js文件的问题, 根本办法就是把多个js文件整合成1个js文件。
问题时这不又回去写法1了吗?
而webpack 提供的js文件打包功能就立功了, 开发时我们会把js代码拆分多个文件, 而部署调试时,可以用webpack去把多个js文件打包成1个main.js
这时我们先在项目文件夹安装webpack
npm init -y
npm install webpack --save-dev
npm list
❯ npm list
webpack1@1.0.0 /home/gateman/Projects/jsproject/webpack1
├── webpack-cli@5.0.1
└── webpack@5.75.0
接下来我们用webpack 去打包 app4.js, 因为这时入口js文件
❯ npx webpack ./js/app4/app4.js --mode=production
asset main.js 739 bytes [emitted] [minimized] (name: main)
orphan modules 1.16 KiB [orphan] 4 modules
./js/app4/app4.js + 4 modules 1.45 KiB [built] [code generated]
webpack 5.75.0 compiled successfully in 282 ms
这时会在项目文件夹下生成 ./dist/main.js文件
在html中我们只需要引用 main.js
<body>
<div id="app4"></div>
<!-- <script type="module"src="./js/app4/app4.js"></script> if use type=module, we need a http server to aovid the CORS error-->
<script src="./dist/main.js"></script>
</html>
注意不需要 使用type=“module” 因为webpack 会把js 的ES6 语法翻译成浏览器默认能处理的ES5 语法。 否则调试时还是需要http server.
然后我们就可以在浏览器测试了
可以见到, 浏览器只需要下载1次 main.js 而且把所有的 js文件内容 打包了main.js文件中!
问题解决!
所以为什么要使用webpack?
- 鼓励我们使用面像对象思想去编写javasript, 令代码分层次,所谓的模块化编程。
- 部署时整合多个js文件, 减少客户端/浏览器对服务器的调用次数。