官方定义:从本质上来说,webpack是一个现代的JavaScript的静态模块打包工具。
webpack是前端工程化的一个解决方案。
主要功能: 提供了前端模块化功能支持,模块混淆,代码压缩,处理浏览器JS兼容性,性能优化等强大功能。
学webpack的前提是要知道es6的模块化知识或者commonjs的知识(不用太深),两者都提供模块化功能。es6的模块化是官方标准,较新,将来一定是主流,commonjs比较早就有了,资历深。知道一个或者两个都知道比较好。没学过去看看,不然看不懂webpack例子。
其他前提:
javascript的es5基础 (不用太深)
html (不用太深)
css(不用太深)
node.js(不用太深)
webpack依赖node.js。没有的需要先安装node.js。就不说怎么安装了。
安装webpack3.6.0这个版本。
npm install [email protected] -g
能看到版本号,就表示安装成功了。
webpack -version
先把目录创建好,一个main.js文件,这是js的入口文件。dist文件夹是用来放打包后的文件,后面会用到。因为webpack是支持模块化的,所以加了一个mathUtil.js来简单模拟模块化。
main.js的代码如下:
调用了mathUtils.js的代码,实现简单输出。第一行代码是commonjs的语法,不了解的只需要知道是这是在引用模块就可以了。
const {add,mult}=require("./mathUtils.js")
console.log(add(1,2))
console.log(mult(1,2))
mathUtils.js定义了两个方法并且导出给别人使用。
function add(a, b) {
return a+b
}
function mult(a, b) {
return a*b
}
//使用commonJs导出
module.exports = {
add,
mult
}
index.html的内容为空,也就是默认的html文本,里面什么都没写。
分析:
现在我们有一个main.js,他引用了mathUtils.js里面的内容。
js有了,那么我们是不是要在js里面引用呢?答案是不要。如果不用webpack,我们可能就会引用多个js文件,比如main.js和mathUtils.js,当然你可能有很多个js文件,都引用也非常的麻烦。webpack可以将这些js文件打包成一个文件。然后你只需要引入打包好的这一个文件就行了,打包也非常的简单。
执行下面的代码,会在dist目录下面生成一个bundle.js文件,这就是打包好的文件,注意这里我们打包的时候只写了main.js,webpack会帮我们把相关联的js文件全部打包进来。这就是webpack帮我们简化步骤的地方,和我们一个个手动引用相比,这里相当于变成了自动引用,我们只需要提供入口js,也就是我们的main.js这一个js文件就可以了。
webpack ./src/main.js ./disk/bundle.js
我们简单看一下生成的这个bundle.js。前面的代码不用看,主要看后面的几行,后面那几行实际上就是把main.js和mathUtils.js的代码合并带一起了,还有一些commonjs的代码,这些代码浏览器还是不识别的,前面生成的一堆看不懂的代码就是给commonjs做支持用的。
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
const {add,mult}=__webpack_require__(1)
console.log(add(1,2))
console.log(mult(1,2))
/***/ }),
/* 1 */
/***/ (function(module, exports) {
function add(a, b) {
return a+b
}
function mult(a, b) {
return a*b
}
//使用commonJs导出
module.exports = {
add,
mult
}
/***/ })
/******/ ]);
最后,只需要在index.html里面引用我们生成的bundle.js就可以了。
<script src="./dist/bundle.js"></script>
前面我们使用webpack打包的时候,是手动指定路径的,这些东西可以写到配置文件里面,方便我们管理。
webpack ./src/main.js ./disk/bundle.js
需要在项目根目录下创建webpack.config.js文件。这是webpack的配置文件,默认就叫这个名字。
写入下面的内容,然后在终端执行webpack这个命令,不用加任何参数,这时候会报错,告诉你要用绝对路径。实际上,下面path的写法是不对的,需要绝对路径,我们直接写不合适,而是通过node.js提供的path包来获取。
module.exports = {
entry:"./src/main.js",
output:{
path:"./dist",
filename:"bundle.js"
}
}
默认情况下是没有path这个包的。我们需要将项目初始化为node工程。
提供下面的命令初始化node工程。要在index.html的父目录执行。
npm init
会叫你填一些值,不要写中文,不是中文的一直默认就可以,后面会说具体配置。
完成之后会多出一个package.json的文件,这是node的配置文件。
里面的内容大致如下。
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
现在就可以使用node提供的path库了。
通过require引入库,通过path.resolve方法获取绝对路径,__dirname是path库提供的项目路径
const path=require("path")
module.exports = {
entry:"./src/main.js",
output:{
path:path.resolve(__dirname,"dist"),
filename:"bundle.js"
}
}
现在我们可以直接使用webpack来打包,而不用指定路径了。
前面的方式运行webpack已经可以用了。但是还有一种更好的方式。那就是使用node的脚步运行webpack。
前面的问题是我们可能存在多个webpack配置文件,一个是生产环境的一个是开发环境的,那么如果环境变了,我们的webpack打包命令需要指定配置文件。
前面没有指定配置文件名称是因为默认配置文件名称就叫做webpack.config.js。
webpack production.config.js
我们可以使用一种更灵活的方式,就是把这些命令配置到脚本里面。下面的命令就是运行build这个脚本,这个脚本是我们自己定义的。
npm run build
在我们的package.json里面有下面的脚本。我们可以在这里面创建我们自己的build脚本,默认写了一个叫test的脚本。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
}
现在执行npm run build可以实现和原来一样的效果。
这里还存在一个问题,就是我们在终端执行命令的时候,调用的都是全局的webpack命令。这就会造成问题,因为实际项目中,本地和全局可能因为某些原因需要使用不同的版本。都用全局就会造成问题。
这时候我们需要安装一个本地版本就可以了。
npm install [email protected] --save-dev
执行完后会多出下面的目录。node_modules包含了node的所有模块,有几百个,webpack就在里面。
默认在终端执行webpack都是执行全局的node,除非写成下面的样子
./node_modules/webpack xxxxx
当然现在我们有更好的方式,执行下面的代码就可以了,这就是最终的效果。
npm run build
"devDependencies": {
"webpack": "^3.6.0"
}
我们先创建css,目录并且添加normal.css文件,内容如下。
body{
background-color: lightgreen;
}
调整一下js文件结构,把作为入口的main.js暴露再外面。
这时候我们不要在html中引入css代码了,而是交给webpack来做。
webpack本身并没有支持CSS的功能,这些功能通过loader来提供。
我们只需要加载对应的loader,就可以实现对应的功能。
webpack提供非常多的loader,CSS loader只是其中一个。
我们打开文档:
https://www.webpackjs.com/loaders/#%E6%A0%B7%E5%BC%8F
我们需要用到的css loader就两个,一个是css-loader,用于解析css代码,并返回css代码。如果只单独用这个loader是没有效果的,因为这个loader只负责加载,不负责导出显示到dom中。所以我们还需要一个style-loader模块,这个模块就是专门干这个事的。
安装模块,webpack 3.6.0对应的0.9.0是可以使用的,太高版本会给警告。
使用npm好像是会直接报错的,但是cnpm只有警告,并且给出提示说当前版本只能对应的版本范围。
npm install --save-dev [email protected]
npm install [email protected] --save-dev
然后需要在配置文件里面添加module节点,并添加rules的内容。
需要注意的是,use里面的两个loader是后面的先加载,顺序错了是会报错的,文档里面有这么说。不知道为什么这样设计。
const path=require("path")
module.exports = {
entry:"./src/main.js",
output:{
path:path.resolve(__dirname,"dist"),
filename:"bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
}
}
这时候执行下面的代码再运行index.html就可以看到css的样式了
npm run build
如果背景色变了就表示成功了。
less的配置和css的配置是类似的。
这里存在一个困难是版本问题,你根本不知道什么版本和你的webpack匹配。
而且版本号是没有统一的,一个loader是一个独立的版本号。
解决办法就是到github直接搜索你要安装的loader,比如这里的less-loader,随便点击一个tag版本。查看里面的package.json,里面是可以查看到webpack的版本范围的。
对于任意的loader都是类似的,这些loader都是webpack官方发布的。
使用下面的版本对应webpack3.6.0是可以的,只是会告诉你已经废弃,math方法有bug。实际项目还是不要用了,应该升级webpack版本。
npm install --save-dev [email protected] [email protected]
在webpack.config.js下面的rules节点下面添加下面的代码。
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}
创建css文件夹,在里面准备我们的special.less测试文件。
@fontSize:50px;
@fontColor:lightblue;
body{
font-size: @fontSize;
color: @fontColor;
}
在main.js中引用less
require("./css/special.less")
在index.html中添加如下内容:
<div id="box">
Hello Webpack
</div>
最后运行。
npm run build
效果正确就表示配置成功了。
图片也是需要loader的,在webpack里面图片分为两种情况,在配置文件里面可以配置一个limit属性来判断图片大小是否超过这个指,如果小于这个值,就会被当做base64来处理,如果大于这个值,就当正常的图片文件来处理。
版本一定要写1.1.2,1.0.0是不能显示图片的,对于webpack3.6.9。
npm install --save-dev [email protected]
这里的关键点是多了一个limit,也就是我们上面说的图片大小限制。
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
引入两张图片,gun.jpg的大小为4kb,baidu.png的大小为15kb.。
把body的背景指定为gnu.jpg,因为图片大小小于limit,所以图片会以base64的形式传递。
body{
/*background-color: lightgreen;*/
background: url("../img/gnu.jpg");
}
npm run build
如果把图片换成baidu.jpg,就会直接报错。告诉你找不到file-loader。前面讲过,webpack处理图片有两种方式,当图片大小小于limit的时候采用的是base64的字符串方式。这时候就需要用到url-loader,而当图片大于limit的时候,就不能用url-loader,而是要用file-loader.
Module build failed: Error: Cannot find module 'file-loader'
下载file-loader。
npm install [email protected] --save-dev
配置file-loader。
前面的url-loader是基于file-loader实现的,两者只能写其中一个,同时写是不能生成图片的。
前面的url-loader是基于file-loader实现的,两者只能写其中一个,同时写是不能生成图片的。
前面的url-loader是基于file-loader实现的,两者只能写其中一个,同时写是不能生成图片的。
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
}
编译运行后发现图片不显示。但是dist目录下是生成了图片的。
我们看下浏览器body的url地址,路径是不对的,应该在前面加一个dist/。这是可以提供配置实现的。
url(d9c8750bed0b3c7d089fa7d55720d6cf.png)
需要子啊webpack配置文件里面添加publicPath这个节点。
output:{
path:path.resolve(__dirname,"dist"),
filename:"bundle.js",
publicPath:"dist/"
},
现在baidu.jpg就可以被当做图片显示了。
我们不太可能将所以图片都放在dist根目录下,给dist创建一个img目录是比较合理的,可以提供下面的代码实现。[name]表示原来的文件名[hash:8]表示8位hash值,不写默认32位,[ext]表示扩展名。这样就非常的工程化了。
options: {
name:"img/[name]-[hash:8].[ext]"
}
webpack的一个最大的优点就是他帮我们做了一些兼容性问题的处理。可以通过babel-loader来实现。
npm install [email protected] @babel/core @babel/preset-env
在webpack配置文件里面添加下面内容:
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
观察输出的js文件,之前的const等这些es6语法就都转化成es5的语法了。