最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>
这段代码依次加载多个js文件。
这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
WebPack可以看做是模块打包机或项目自动化构建工具:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用
webpack是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js 文件、css 文件等)都看成模块,通过 loader(加载器)和 plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。
js本身没有模块化的功能,后来出现commonjs支持模块化语法,webpack也支持commonjs的模块化语法,让我们可以使用模块化的方式编写js(比如require引入其他js文件),我们写的模块代码,经过webpack构建和包装之后,能够在浏览器以模块化的方式运行。
webpack不仅支持commonjs,也支持es6,amd等其他模块化语法
PS: webpack的模块不仅指js,包括css、图片等资源都可以以模块看待
自动分析项目中的各种文件,进行相应操作,比如压缩图片、js文件;预编译less,Scss等文件
方便构建各种运行环境,比如测试环境、生产环境等
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
1.安装nodejs和npm:
NPM是随同NodeJS一起安装的包管理工具(安装了nodejs,会自带npm),能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:
1.从NPM服务器下载第三方包到本地使用。
2.从NPM服务器下载并安装第三方命令行程序到本地使用。
3.将自己编写的包或命令行程序上传到NPM服务器供别人使用。
由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入 "npm -v" 来测试是否成功安装
使用以下全局安装最新版npm:
npm install npm -g
或安装淘宝镜像npm(cnpm)
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装完成后可以用cnpm命令替代npm命令
更新nodejs:
第一步,先查看本机node.js版本:
$ node -v
第二步,清除node.js的cache:
$ sudo npm cache clean -f
第三步,安装 n 工具,这个工具是专门用来管理node.js版本的,别怀疑这个工具的名字,是他是他就是他,他的名字就是 "n"
$ sudo npm install -g n
第四步,安装最新版本的node.js
$ sudo n stable
第五步,再次查看本机的node.js版本:
$ node -v
2.安装、创建并初始化项目:
首先,创建一个项目文件夹:
mkdir app
安装webpack
全局安装webpack
npm i webpack -g
对于大多数项目,我们建议本地安装。这可以使我们在引入破坏式变更(breaking change)的依赖时,更容易分别升级项目。
初始化项目:
npm init -y
执行完后,将生产package.json
package.json定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)
也可以执行npm init,加一个-y会默认生成package.json,比如版本号,名称,包名这些会默认生成,如果没有-y,会提示你设置这些参数
使用-y默认生成后,也可以再执行npm init,手动设置各种配置参数,将会覆盖之前设置的值
然后本地安装webpack:
npm install --save-dev webpack //安装webpack
npm install --save-dev webpack-cli //4.x需要安装cli
npm list webpack---查看当前目录本地安装的webpack版本
3.简单项目样例:
sublime打开项目文件夹
按以下结构新建项目文件:
├── dist ---打包后的目标文件存放目录 ├── node_modules ---webpack本地安装的目录 ├── package.json ---项目配置文件,是一个标准的npm说明文件
├── webpack.config.js ---webpack配置文件
└── src
└── app.js ---源码存放目录
webpack.config.js需要自己新建,内容类似于:
module.exports = {
entry:'./src/app.js',
output:{
filename:'app.bundle.js'
}
}
entry是源文件,需要指定目录,output是打包后的文件,打包后的文件会自动放到dist里,不需要指定目录
进入项目,执行命令:
webpack
完成打包
webpack --watch:源文件改变后不需要重新打包,目标文件会自动改变
webpack -d:测试版,不会压缩js,加入调试信息可以方便在浏览器调试(调试的时候可以自动关联到源码,可以在源码里断点调试) 默认情况下不带参数会生成生产版,生成版会压缩
我们可以把webpack命令集成到package.json里面,这样就可以用npm命令来执行打包(一般推荐这么做)
在package.json的scripts模块里加入webpack命令的配置:
"scripts": { "dev": "webpack -d --watch", "prod": "webpack" },
执行npm run dev---打包生产调试版
执行npm run prod---打包生产版
4.项目样例2:
在项目中创建两个文件夹,app文件夹和public文件夹(跟之前的src和dist一样),app文件夹用来存放原始数据和我们将写的JavaScript模块,public文件夹用来存放之后供浏览器读取的文件(包括使用webpack打包生成的js文件以及一个index.html
文件)。接下来我们再创建三个文件:
index.html
--放在public文件夹中;Greeter.js
-- 放在app文件夹中;main.js
-- 放在app文件夹中;
此时项目结构如下图所示
我们在index.html文件中写入最基础的html代码,它在这里目的在于引入打包后的js文件(这里我们先把之后打包后的js文件命名为bundle.js
,之后我们还会详细讲述)。
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
<script src="bundle.js"></script>
</body>
</html>
我们在Greeter.js
中定义一个返回包含问候信息的html
元素的函数,并依据CommonJS规范导出这个函数为一个模块:
// Greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};
main.js
文件中我们写入下述代码,用以把Greeter模块
返回的节点插入页面。
//main.js
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());
module.exports = {
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
}
}
注:“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录。{ "name": "app6", "version": "1.0.0", "description": "", "main": "index.js", "dependencies": { "webpack-cli": "^3.2.3", "webpack": "^4.29.5" }, "devDependencies": {}, "scripts": { "start": "webpack", "debug":"webpack -d" }, "keywords": [], "author": "", "license": "ISC" }
start命令可以之前用npm start(也可以npm run start),其他命令必须是npm run xxx,比如npm run debug
5.html-webpack-plugin插件
前面的例子中, index.html 文件太死了,连 js 文件都写死了,有时候引用的 js 文件是动态变化的呢?
打个比方,类似下面这种例子:
<script src="app.bundle1.js"></script> <script src="app.bundle2.js"></script> <script src="app.bundle3.js"></script>
而且还不确定有多少个。
还有一种情况,有时候为了更好的 cache 处理,文件名还带着 hash,例如下面这样:
main.9046fe2bf8166cbe16d7.js
这个 hash 是文件的 md5 值,随着文件的内容而变化,你总不能每变化一次,就改一下 index.html 文件吧?
效率太低!
下面我们要使用一个 webpack 的插件 html-webpack-plugin 来更好的处理这个问题。
webpack 吸引人的地方就是因为它有太多的插件,有时候一些需求,一个插件就搞定。
安装html-webpack-plugin插件:
npm install html-webpack-plugin --save-dev
安装成功后,package.json 这个文件会多出一行 "html-webpack-plugin": "^x.xx.x",,如下所示:
{ "name": "hello-wepback", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "webpack -d --watch", "prod": "webpack -p" }, "author": "", "license": "ISC", "devDependencies": { "html-webpack-plugin": "^2.30.1", "webpack": "^3.8.1" } }也可以反向操作,先配置好devDependencies,然后在工程目录执行:
现在我们把第一个例子中生成的 index.html 先删除掉,我们要用 html-webpack-plugin 这个插件来自动生成它。
把 webpack.config.js 文件改一下,如下所示:
var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/app.js', output: { path: __dirname + '/dist', filename: 'app.bundle.js' }, plugins: [new HtmlWebpackPlugin()] };
<html> <head> <meta charset="UTF-8"> <title>Webpack App</title> </head> <body> <script type="text/javascript" src="app.bundle.js"></script></body> </html>
跟手动写的一样
连标题 <title>Webpack App</title> 都自动生成了,如果这是固定的话,就不太灵活,但是 html-webpack-plugin 有选项来处理这个问题。
要改变 title 很简单,上文提到 HtmlWebpackPlugin 这个方法可以传入很多参数的,下面这样就可以解决这个问题。
var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/app.js', output: { path: __dirname + '/dist', filename: 'app.bundle.js' }, plugins: [new HtmlWebpackPlugin({ title: "hello world" })] };
只是改变了一点点东西,其实也没多大用处,有时候我们要让 index.html 根据我们的意愿来生成。就是说它的内容是我们自己定的。
这个就不得不提到 html-webpack-plugin 的 template 功能。
把 webpack.config.js 更改如下:
var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/app.js', output: { path: __dirname + '/dist', filename: 'app.bundle.js' }, plugins: [new HtmlWebpackPlugin({ template: './src/index.html', })] };
接着新建 src/index.html 文件,内容如下:
<html lang="en"> <head> <meta charset="UTF-8"> <title>Hello World</title> </head> <body> </body> </html>
我们再来看看新生成的 dist/index.html 文件,内容如下:
<html lang="en"> <head> <meta charset="UTF-8"> <title>Hello World</title> </head> <body> <script type="text/javascript" src="app.bundle.js"></script></body> </html>
下面我再来介绍几个参数,以及它的结果。
filename: 'index.html' 默认情况下生成的 html 文件叫 index.html,但有时候你不想叫这个名字,可以改。
minify: { collapseWhitespace: true, },
这个可以把生成的 index.html 文件的内容的没用空格去掉,减少空间。(默认是true,可以改成false,这样就会按正常的格式生成html)
hash: true 为了更好的 cache,可以在文件名后加个 hash。
效果如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Webpack App</title> <meta name="viewport" content="width=device-width,initial-scale=1"></head> <body> <script src="bundle.js?4abc2d79d055a2c3cf3f"></script></body> </html>
最终的配置文件可能如下:
var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/app.js', output: { path: __dirname + '/dist', filename: 'app.bundle.js' }, plugins: [new HtmlWebpackPlugin({ template: './src/index.html', filename: 'index.html', minify: { collapseWhitespace: true, }, hash: true, })] };