什么是webpack
官方给的解释:
At its core, webpack is a static module bundler for modern JavaScript applications.
翻译成中文:
从本质上讲,webpack是一个现代的JavaScript应用的静态模块打包工具。
从两点来解释上面的这句话:模块和打包
前端模块化
在上一篇文章中,vue2.x笔记5-前端模块化,解释了什么需要模块化以及目前使用模块化的一些方案:AMD、CMD、CommonJS、ES6。
在ES6之前,我们想进行模块化开发,需要借助其他的工具,并且在通过模块化开发完成后,还需要处理模块间的各种依赖并将其整合打包。
而webpack其中的一个核心就是让我们可以进行模块化开发并帮助我们处理模块间的依赖关系。而且不仅仅是js文件,还有css、图片、json等等,在webpack中都可以当做模块来使用,这就是webpack中模块化的概念。
打包
打包就是讲webpack中的各种资源模块进行打包合并成一个或多个包。
并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法等等。
但是打包的操作似乎和grunt/gulp也可以帮助我们完成,那么它们有什么不同呢?
webpack和grunt/gulp对比
- grunt/gulp的核心是Task
- 可以配置一系列的task,并且定义task要处理的事物,比如ES6、ts转化,图片压缩,三scss转css
- 之后让grunt/gulp来依次执行这些task,让整个流程自动化
- 所以grunt/gulp也被称为前端自动化任务管理工具
- 来看一个gulp的task
- 代码中的task就是将src下面所有的js文件转成ES5的语法,并最终输出到dist文件夹中
const gulp = require('gulp')
const babel = require('gulp-babel')
gulp.task('js', () =>
gulp.src('src/*.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('dist'))
)
- 什么时候用grunt/gulp呢?
- 如果工程模块依赖非常简单,甚至没有用到模块化的概念
- 只需要进行简单的合并、压缩,使用grunt/gulp即可
- 但如果整个项目使用了模块化管理,而且相互依赖非常强,就可以使用webpack了
- 所以,grunt/gulp和webpack有什么不同呢?
- grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心
- webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是它附带的功能
webpack安装
安装webpack首先需要安装node.js,node.js自带了软件包管理工具npm
查看node版本
node -v
全局安装webpack(这里我先指令版本3.6.0,因为vue cli2依赖该版本)
npm install webpack@3.6.0 -g
局部安装webpack
--save-dev 是开发时依赖,项目打包后不需要继续使用的
cd 对应目录
npm install webpack@3.6.0 --save-dev
为什么全局安装后,还需要局部安装呢?
全局安装的版本和项目版本不匹配,又不想搞一个项目改一个全局的版本,就可以局部安装
在终端直接执行webpack命令,使用的全局安装的webpack
当在package.json中定义了scripts时,其中包含webpack命令使用的就是局部安装的webpack
webpack的起步
准备工作
创建文件和文件夹
文件和文件夹解析:
dist文件夹:用于存放打包的文件
src文件夹:用于存放我们写的源文件
main.js:项目的入口文件
mathUtils.js:定义了一些工具函数。可以在其他地方引用并且使用
info.js:定义了用户信息
index.html: 浏览器打开展示的首页
js文件打包
现在js文件中使用了模块化方式进行开发,但是他们不能直接使用
因为如果直接在index.html中引用js文件,浏览器不能识别其中的模块化代码
另外,在真实的项目中有许多js文件时,一个个引用非常麻烦,并且后期也不利于管理
那该怎么做呢?可以使用webpack工具,对多个js文件进行打包
webpack是一个模块化的打包工具,所以它支持我们的代码中写模块化,可以对其进行处理
另外,在处理完所有模块之间的关系后,多个js打包到一个js中,引用也非常方便
如何打包?使用webpack指令即可
webpack src/main.js dist/bundle.js
打包后会在dist文件夹下生成一个bundle.js文件
bundle.js文件是webpack处理了项目文件依赖后生成的一个js文件,只需要将js文件在index.html中引用即可
webpack的配置
入口和出口
考虑到如果每次使用webpack的命令都需要写入口和出口的话,就非常麻烦,那有没有一种方法将这两个参数写到配置中,在运行是,直接读取呢?
当然可以,那就是创建一个webpack.config.js文件
const path = require('path')
module.exports = {
// 入口:可以是字符串、数组、对象,这里入口只有一个,所以写一个字符串即可
entry: './src/main.js',
// 出口:通常是一个对象,里面至少包含两个重要属性, path 和 filename
output: {
path: path.resolve(__dirname, 'dist'), // 注意:path通常是一个绝对路径
filename: 'bundle.js'
}
}
局部安装webpack
目前,我们使用的webpack都是全局的,那怎么局部来打包呢?
因为一个项目往往依赖特定的webpack版本,全局版本可能和项目的webpack版本不一致,导致打包出现问题,所以通常一个项目,都有自己的局部webpack
第一步,项目中安装自己局部的webpack
这里安装版本暂定webpack3.6.0
Vue CLI3中已经升级到webpack4,但是它将配置文件隐藏了起来,所以查看起来不方便
npm install webpack@3.6.0 --save-dev
第二步,通过 node_modules\.bin\webpack启动webpack打包
node_modules\.bin\webpack
但是,每次执行都要敲这么一长串也是不方便的,可以在package.json的scripts中定义自己的执行脚本,在执行脚本时,会按照一定顺序寻找命令对应的位置
首先,在本地寻找node_modules\.bin路径中对应的命令
如果没找到,会去全局的环境变量中寻找
生成package.json文件
npm init
定义执行脚本
执行build指令
npm run build
webpack的loader
什么是loader?
loader是webpack中一个非常核心的概念
webpack用来做什么?
在之前的示例中,我们主要是用webpack来处理写的js代码,并且webpack会自动处理js之间的依赖
但在开发中,不仅仅有基本的js代码处理,还需要加载css、图片,也包括一些高级将ES6转成ES5,将TypeScript转成ES5,将scss、less转成css,将.jsx、.vue文件转成js文件等等
对于webpack本身能力来说,这些转化是不支持的
那就需要给webpack扩展对应的loader就可以啦
loader使用过程:
一:通过npm安装需要使用的loader
二:在webpack.config.js中的modules关键字下进行配置
大部分的loader都可以在webpack官网中找到,并且学习对应的用法
css文件处理
项目开发过程中,需要添加很多的样式,而这些样式往往是写到一个单独的文件中
在src目录中,创建一个css文件夹,其中创建一个normal.css文件
将零散的js文件放在js文件夹中
在normal.css中设置body样式,这个时候样式并没有生效
因为还没有引用它
webpack也不可能找到它,因为只有一个入口,webpack会从入口开始查找其他依赖的文件
在入口文件中引用:
重新打包,会出现错误,错误信息指出,加载normal.css文件必须有对应的loader
在webpack的官网中,可以找到关于样式的loader使用方法:
按照官方配置webpack.config.js文件
注意:配置中有一个style-loader,没有安装,所以暂时不进行配置
重新打包项目,运行index.html,会发现样式没有生效。
原因是css-loader只负责加载css文件,但是并不负责将css具体样式嵌入到文档中
这个时候需要一个style-loader帮助处理
安装style-loader
npm install --save-dev style-loader
配置webpack.config.js文件
const path = require("path");
module.exports = {
// 入口:可以是字符串、数组、对象,这里入口只有一个,所以写一个字符串即可
entry: "./src/main.js",
// 出口:通常是一个对象,里面至少包含两个重要属性, path 和 filename
output: {
path: path.resolve(__dirname, "dist"), // 注意:path通常是一个绝对路径
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
};
注意:style-loader需要放在css-loader的前面
但是按照逻辑,在处理css文件过程中,应该是css-loader先加载css文件,再由style-loader来进一步处理,为什么将style-loader放在前面呢?
这是因为webpack在读取使用的loader过程中,是按照从右向左的顺序读取的
重新打包项目,出现错误,原因是安装的css-loader、style-loader版本过高,与项目运行的webpack3.6.0版本不匹配,需要指定loader版本
修改package.json的版本,执行 npm install 安装
重新打包项目,样式生效
less文件处理
如果在项目中使用less、scss来写样式,webpack是否可以帮助我们处理呢?
这里先以less为例,其他也是一样的
先创建一个less文件(special.less),放在css文件夹中
@fontSize: 50px;
@fontColoe: orange;
body {
font-size: @fontSize;
color: @fontColoe;
}
在入口文件中引入(main.js)
// 4.依赖less文件
require('./css/special.less')
// W为查看less生效的效果,添加一句话
document.writeln("<span>我是主体内容</span>")
打包文件,报错,提示需要loader
在官网中查找,可以找到less-loader相关的使用说明
首先,安装对应的loader
注意:这里还安装了less,因为webpack会使用less对less文件进行编译
指定安装版本,防止安装版本过高,导致出错
npm install less@3.9.0 less-loader@4.1.0 --save-dev
其次,修改对应的配置文件
添加一个rules选项,用于处理.less文件
{
test: /\.less$/,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
},
{
loader: "less-loader",
},
],
}
重新打包,运行成功,less文件生效
图片文件处理
首先,在项目中加入两张图片:
一张较小的图片test1.png(小于8kb),一张较大的图片test2.png(大于8kb)
一会儿会针对这两张图片进行不同的处理
先在css样式中引用图片,更改normal.css中的样式
body {
/* background-color: antiquewhite; */
background: url(../img/test1.png);
}
直接打包,报错,提示需要loader
图片处理,使用url-loader来处理,先安装url-loader
npm install url-loader@1.1.2 --save-dev
修改webpack.config.js配置文件
{
test: /\.(png|jpg|gif|jpeg)$/i,
use: [
{
loader: "url-loader",
options: {
limit: 8192
},
},
],
},
再次打包运行,可以看到背景图片显示出来了
通过控制台,会发现背景图片通过base64显示出来了
这也是limit属性的作用,当图片小于8kb时,对图片进行base64编码
那么问题来了,图片大于8kb呢?将background的图片改为test2.png,看看结果
打包运行,提示需要file-loader,也就是当图片大于8kb时,需要通过file-loader进行处理
安装file-loader
npm install file-loader@3.0.1 --save-dev
再次打包,会发现dist文件夹下多了一个图片文件
会发现webpack自动帮助我们生成了一个非常长的名字
这是一个32位的hash值,目的是防止名字重复
但是,真实开发中,可能对打包的图片名称有一定要求
比如,将所有图片放在一个文件夹中,加上图片原来的名字,同时防止重复
这时候,就可以在options中添加一个属性
options: {
limit: 8192,
name: "img/[name].[hash:8].[ext]",
},
img:文件要打包到的文件夹
name:获取图片原来的名字,放在该位置
hash:8:为了防止图片名称冲突,依然使用hash值,但只保留8位
ext:使用图片原来的扩展名
重新打包,会发现图片已经按配置生成
但运行后图片还是没有显示出来,这是因为图片使用的路径不正确
默认情况下,webpack会将生成的路径直接返回给使用者
但是,我们的整个程序是打包在dist文件夹下的,所以这里需要在路径前添加一个dist/
ES6语法处理
仔细阅读webpack打包的js文件,会发现ES6语法并没有转成ES5,那就意味着一些对ES6还不支持的浏览器没法运行代码
在webpack中,希望将ES6转ES5,直接使用babel对应的loader就可以了
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
配置webpack.config.js文件
{
test: /\.m?js$/,
// exclude: 排除
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["es2015"],
},
},
},
重新打包,在查看bundle.js,会发现其中的内容变成ES5的语法了