下面主要来认识webpack,以及熟悉webpack的基本使用。
webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具;
image
image
image-20200302081833764
image-20200302081925931
https://webpack.js.org/guides/getting-started/
image-20200302225404414
官网中文文档:https://www.webpackjs.com/guides/
image-20200310135709859
npm i webpack -g
全局安装webpack,这样就能在全局使用webpack的命令npm i webpack --save-dev
安装到项目依赖中这两种方式一种是全局安装,一种是局部项目内安装,那么下面我示例采用全局安装的方式,如下:
image-20200302082320336
安装完毕webpack
,还需要安装一下webpack-cli
,如下:
npm i webpack-cli -g
image-20200302220535154
现在安装完毕了webpack,那么下面就可以开始建立一个项目,用来练习webpack相关操作。
image-20200302212347588
在这里项目中,创建了图中的相关文件目录,主要的用途如下:
bundle.js
等压缩文件。这里main.js
是用来作为单一入口 js 文件,所有需要引入的 js
以及 css
都可以在这里编写,统一提供html
文件进行引入。
建立好文档结构之后,在根目录下初始化package.json
文件,执行:
npm init -y
效果如下:
image-20200302232307631
为了更好演示压缩 js、css的示例,下面先来编写一个 ul
无序列表,用来间隔改变颜色。
index.html
代码如下:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--ul>(li{$})*10-->
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
</body>
</html>
浏览器显示如下:
image-20200302213441391
main.js
中导入 jquery
,并且编写样式main.js
代码:
// 这是 main.js 是我们项目的JS入口文件
// 1\. 导入 Jquery
// import *** from *** 是ES6中导入模块的方式
// 由于 ES6的代码,太高级了,浏览器解析不了,所以,这一行执行会报错
import $ from 'jquery'
$(function () {
// :odd 选择器选取每个带有奇数 index 值的元素(比如 1、3、5)。
$('li:odd').css('backgroundColor', 'yellow');
// :even 选择器选取每个带有偶数 index 值的元素(比如 2、4、6)。
$('li:even').css('backgroundColor', function () {
return '#' + 'D97634'
});
});
这代码要注意一下,因为采用的是ES6的高级语法,浏览器是无法直接解析的,如果引入到index.js
中直接执行,则会报错。
在此之前,还要安装一下jquery.
cnpm i jquery --save
安装jquery类库cnpm i jquery --save
或者
cnpm i jquery -S
image-20200302214611035
当安装完毕,就会自动在项目目录下创建一个node_modules
的文件夹。
image-20200302214209122
浏览器运行index.html,如下:
image-20200302214902610
那怎么办呢?可以使用webpack进行压缩转化为浏览器可以执行的js文件。
webpack 入口文件路径 输出文件路径
对main.js
进行处理:webpack src/main.js dist/bundle.js
但是,如果直接这样执行,在webpack4就会发现报错如下:
image-20200302224124150
ERROR in multi ./src/main.js dist/bundle.js
Module not found: Error: Can't resolve 'dist/bundle.js' in 'E:\webProject\webpack-pratice'
@ multi ./src/main.js dist/bundle.js main[1]
这是什么意思呢?
上面的有一个警告和一个错误,主要看错误信息的话就是无法介绍相关的路径信息。经过多种尝试,最后我发现webpack4的确不支持这种打包方式了,后续采用配置文件的方式打包则能够成功。
首先创建webpack的配置文件webpack.config.js
,如下:
image-20200302224502994
const path = require('path');
// 这个配置文件,起始就是一个 JS 文件,通过 Node 中的模块操作,向外暴露了一个 配置对象
module.exports = {
// 在配置文件中,需要手动指定 入口 和 出口
entry: path.join(__dirname, './src/main.js'),// 入口,表示,要使用 webpack 打包哪个文件
output: { // 输出文件相关的配置
path: path.join(__dirname, './dist'), // 指定 打包好的文件,输出到哪个目录中去
filename: 'bundle.js' // 这是指定 输出的文件的名称
},
};
下面再来执行webpack
打包看看,如下:
image-20200302224600522
好了,到这里就可以看到成功打包了了bundle.js
压缩文件了。
index.html
中引入bundle.js
image-20200302225811786
在浏览器打开效果如下:
image-20200302225848781
可以看到经过webpack
的处理,浏览器可以显示编写的样式了。
那么当我们执行webpack
命令之后,到底执行了什么步骤呢?
当我们在 控制台,直接输入 webpack 命令执行的时候,webpack 做了以下几步:
webpack.config.js
的配置文件webpack
打包,存在不方便的地方在日常的开发中,我们可能需要经常修改代码,然后频繁去编译、打包,那么如果每次都要输入webpack
去重新打包,然后再刷新浏览器查看,其实是很不方便的。
image-20200302230630209
然后再去浏览器刷新查看变化,真的很不方便。那么有没有偷懒的方式,让代码变化的时候,自动去打包编译呢?
当然有办法,可以使用webpack-dev-server
工具。
安装命令:
npm i webpack-dev-server -g
npm i webpack-dev-server -D
在这里采用在项目中本地安装,并不进行全局安装,执行如下:
image-20200302231200630
image-20200302231443837
由于是本地安装在项目中,所以无法作为全局命令执行执行。
对于本地项目到项目的情况,就需要在package.json
的scripts
编写执行命令,如下:
image-20200302232506393
"scripts": {
"dev": "webpack-dev-server"
},
下面执行npm run dev
命令,如下:
image-20200302232555569
可以看到在项目本地执行的话,提示无法找到webpack
,那么我在本地在安装一下webpack
以及webpack-cli
,如下:
cnpm i webpack webpack-cli -D
image-20200302233314075
再次运行webpack-dev-server
,如下:
npm run dev
image-20200302233421219
image-20200302234745294
首先打开服务页面,如下:
image-20200302235055726
打开页面之后,并没有发现输出文件bundle.js
,那么直接设置一下路径,看看能否访问:
访问:http://localhost:8080/bundle.js
image-20200302235154133
发现在 / 路径下的确是可以访问 bundle.js
的,也就是说 index.html
的引入路径可以修改一下。
index.html
引入 bundle.js
的路径,并且删除dist目录下的bundle.js
文件image-20200302235421036
image-20200302235520223
总结一下:
使用 webpack-dev-server
这个工具,来实现自动打包编译的功能
webpack
打包的命令,比较麻烦,所以使用webpack-dev-server
来实现代码实时打包编译,当修改代码之后,会自动进行打包构建。cnpm i webpack-dev-server -D
安装到项目本地开发依赖webpack-dev-server
来进行打包,发现报错。这是由于我们是在项目中本地安装的 webpack-dev-server
, 所以无法把它当作脚本命令,在终端中直接运行;(只有那些 安装到 全局 -g 的工具,才能在 终端中正常执行)。package.json
文件中的指令,来进行运行webpack-dev-server
命令,在scripts
节点下新增"dev": "webpack-dev-server"
指令,发现可以进行实时打包,但是dist目录下并没有生成bundle.js
文件,这是因为webpack-dev-server
将打包好的文件放在了内存中。
在package.json
文件中编写scripts,如下:
"scripts": { "dev": "webpack-dev-server" },
webpack-dev-server
这个工具,如果想要正常运行,要求在本地项目中,必须安装 webpack
以及 webpack-cli
# 安装 webpack webpack-cli npm i webpack wabpack-cli -D
bundle.js
放在内存中的好处是:由于需要实时打包编译,所以放在内存中速度会非常快webpack-dev-server
启动的http://localhost:8080/
网站,发现是一个文件夹的面板,需要点击到src
目录下,才能打开我们的index首页,此时引用不到bundle.js
文件,需要修改index.html
中script的src
属性为:<script src="/bundle.js"></script>
http://localhost:8080/
的时候直接访问到index首页,可以使用--contentBase src
指令来修改dev
指令,指定启动的根目录:"dev": "webpack-dev-server --contentBase src"
同时修改index页面中script的src
属性为<script src="bundle.js"></script>
在上面已经安装好了webpack-dev-server
,已经能够初步使用了。那么现在可以往下了解几个让程序员继续偷懒的参数。
--open
在启动运行webpack-dev-server
服务,如果希望自动打开浏览器,那么则可以设置参数--open
,如下:
"scripts": {
"dev": "webpack-dev-server --open"
},
那么此时启动服务,就会自动打开默认的浏览器。
--port
webpack-dev-server
启动服务的默认端口号是8080,如果端口被占用的情况下,则必须要修改启动端口号。那么修改端口号的参数则是--port 端口号
。
下面我在package.json
中设置启动端口号为 3001,如下:
"scripts": {
"dev": "webpack-dev-server --open --port 3001"
},
启动服务如下:
image-20200303082400970
index.html
源文件路径 --contentBase
目前启动服务后,打开地址的页面如下:
image-20200303082631562
能否直接一打开就显示src
目录下的内容呢?因为index.html
是编写在src
目录下的,此时就需要配置--contentBase
参数了,配置package.json
如下:
"scripts": {
"dev": "webpack-dev-server --open --port 3001 --contentBase src"
},
再次启动服务,打开浏览访问如下:
image-20200303082840795
--hot
目前每当我们修改一下main.js
中的代码,都是完整生成一个新的bundle.js
,如下:
image-20200303083412210
配置package.json
设置--hot
参数,如下:
"scripts": {
"dev": "webpack-dev-server --open --port 3001 --contentBase src --hot"
},
重启服务后,修改颜色,发现更新以打补丁的方式:
image-20200303083650327
这样更新文件的内容只有 46 bytes,效率更高。
上面配置常用参数的方式是最常用的,另外还有第二种配置方式,了解一下即可。
package.json
复制一行webpack-dev-server
的启动命令image-20200303225036297
"scripts": {
"dev2": "webpack-dev-server --open --port 3001 --contentBase src --hot", // 第一种配置方式
"dev": "webpack-dev-server" // 第二种配置方式,将常数写到 webpack.config.js 中
},
webpack.config.js
文件,新增devServer
节点如下:image-20200303225539362
devServer: { // 这是配置 dev-server 命令参数的第二种形式,相对来说,这种方式麻烦一些
// --open --port 3000 --contentBase src --hot
open: true, // 自动打开浏览器
port: 3000, // 设置启动时候的运行端口
contentBase: 'src', // 指定托管的根目录
hot: true // 启用热更新
},
好了,配置到这里,那么执行一下npm run dev
命令,看看有无生效,如下:
image-20200303225859861
成功编译执行,并且自动打开浏览器,如下:
image-20200303225950297
在webpack4的版本直接就可以成功了,而在低一些的版本webpack3.x在启动hot
参数的时候会报错,还要配置new webpack.HotModuleReplacementPlugin()
插件,在这里就不演示了。因为现在肯定是用最新的版本的拉。
测试一下,将上面页面的蓝色改为红色,浏览器无刷新更新如下:
image-20200303230509660
image-20200303231000293
// 启用热更新的 第2步
const webpack = require('webpack')
module.exports = {
....
devServer: { // 这是配置 dev-server 命令参数的第二种形式,相对来说,这种方式麻烦一些
// --open --port 3000 --contentBase src --hot
open: true, // 自动打开浏览器
port: 3000, // 设置启动时候的运行端口
contentBase: 'src', // 指定托管的根目录
hot: true // 启用热更新 的 第1步
},
plugins: [ // 配置插件的节点
new webpack.HotModuleReplacementPlugin(), // new 一个热更新的 模块对象, 这是 启用热更新的第 3 步
],
在webpack4
已经不需要写第2、3步了。
html-webpack-plugin
插件配置启动页面前面在配置生成bundle.js
的使用时候,已经将其存储在内存中编译生成。那么能不能将index.html
页面也加载存储到内存中呢?
这个当然可以,可以使用html-wabpack-plugin
插件来完成这部分工作。
html-wabpack-plugin
插件cnpm i html-webpack-plugin -D
执行如下:
image-20200303233418173
webpack.config.js
配置文件中配置image-20200303233713102
const path = require('path');
// 导入在内存中生成 HTML 页面的 插件
// 只要是插件,都一定要 放到 plugins 节点中去
// 这个插件的两个作用:
// 1\. 自动在内存中根据指定页面生成一个内存的页面
// 2\. 自动,把打包好的 bundle.js 追加到页面中去
const htmlWebpackPlugin = require('html-webpack-plugin');
// 这个配置文件,起始就是一个 JS 文件,通过 Node 中的模块操作,向外暴露了一个 配置对象
module.exports = {
// 在配置文件中,需要手动指定 入口 和 出口
entry: path.join(__dirname, './src/main.js'),// 入口,表示,要使用 webpack 打包哪个文件
output: { // 输出文件相关的配置
path: path.join(__dirname, './dist'), // 指定 打包好的文件,输出到哪个目录中去
filename: 'bundle.js' // 这是指定 输出的文件的名称
},
devServer: { // 这是配置 dev-server 命令参数的第二种形式,相对来说,这种方式麻烦一些
// --open --port 3000 --contentBase src --hot
open: true, // 自动打开浏览器
port: 3000, // 设置启动时候的运行端口
contentBase: 'src', // 指定托管的根目录
hot: true // 启用热更新
},
plugins: [ // 配置插件的节点
new htmlWebpackPlugin({ // 创建一个 在内存中 生成 HTML 页面的插件
template: path.join(__dirname, './src/index.html'), // 指定 模板页面,将来会根据指定的页面路径,去生成内存中的 页面
filename: 'index.html' // 指定生成的页面的名称
})
],
};
image-20200303233906249
这个插件的两个作用:
前面都是只在打包js
文件,那么如何打包压缩css
文件呢?
创建编写一个index.css
:
image-20200304073543478
ul li{
list-style: none;
}
index.css
样式直接引入index.html
,确认一下样式效果image-20200304073708150
打开浏览器确认一下样式,如下:
image-20200304073749179
从上面来看,已经实现了样式效果了。但是如果像这样直接在index.html
引入样式文件的话,那么则会造成二次请求,最好将css
的引入操作也写到main.js
中,一起打包为bundle.js
单一文件。
main.js
引入index.css
样式文件image-20200304074011123
image-20200304074125864
在index.html
中注释css
文件的引入之后,在main.js
中使用import
命令引入css
文件。那么是否这样就可以了呢?
当然不行,因为webpack
默认只能打包处理 JS
类型的文件,无法处理 其它的非 JS
类型的文件;如果要处理 非JS
类型的文件,我们需要手动安装一些 合适 第三方 loader 加载器;
image-20200304074828725
style-loader css-loader
工具,用于处理css
文件安装命令:
cnpm i style-loader css-loader -D
执行如下:
image-20200304075205279
webpack.config.js
这个配置文件设置匹配css
文件处理的插件刚刚在上面安装好了两个插件style-loader css-loader
,但是webpack
并不会自动去使用的。需要我们手写正则匹配出css
文件,然后再去定义使用什么插件来处理。
在webpack.config.js
文件编写规则如下:
image-20200304080052249
module: { // 这个节点,用于配置 所有 第三方模块 加载器
rules: [ // 所有第三方模块的 匹配规则
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, // 配置处理 .css 文件的第三方loader 规则
]
}
注意:use
表示使用哪些模块来处理test
所匹配到的文件;use
中相关loader模块的调用顺序是从后向前调用的;也就是说,首先调用css-loader
来处理css
后缀文件,然后再继续交给style-loader
来处理。
那么写到这里,是否已经能否正常将css
打包到bundle.js
中呢?启动npm run dev
来看看有无报错,如下:
image-20200304080409169
那么只要修改一下路径为import './css/index.css'
即可,如下:
image-20200304080522459
在浏览器确认一下样式效果,如下:
image-20200304080621995
webpack
处理css
文件的打包过程webpack, 默认只能打包处理 JS 类型的文件,无法处理 其它的非 JS 类型的文件; 如果要处理 非JS类型的文件,我们需要手动安装一些 合适 第三方 loader 加载器;
webpack 处理第三方文件类型的过程:
上面已经实现了css
文件的打包合并,那么less
文件呢?其实具体的方法就是如下:
cnpm i less-loader less -D
webpack.config.js
这个配置文件:{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
那么知道方法好,下面来演示一下。
image-20200304081429698
安装命令如下:
cnpm i less-loader less -D
执行如下:
image-20200304081633043
main.js
引入index.less
样式文件image-20200304081726981
webpack.config.js
配置文件设置匹配less文件的处理image-20200304081911197
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, //配置处理 .less 文件的第三方 loader 规则
可以看到启动服务后,已经编译成功。打开浏览器看看效果,如下:
image-20200304082023303
cnpm i sass-loader node-sass --save-dev
webpack.config.js
中添加处理sass文件的loader模块:{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }
image-20200304082315516
html, body{
margin: 0;
padding: 0;
li{
font-size: 12px;
line-height: 30px;
}
}
安装命令如下:
cnpm i sass-loader node-sass -D
执行安装如下:
image-20200304084253820
在安装的过程发现还需要安装这两个工具,再安装如下:
cnpm i sass fiber -D
image-20200304084403476
main.js
引入index.scss
文件image-20200304084454691
webpack.config.js
配置文件设置匹配scss文件的处理image-20200304084615428
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }, // 配置处理 .scss 文件的 第三方 loader 规则
从上面可以看到,已经编译成功了。在浏览器查看一下效果如下:
image-20200304084703005
在上面的章节中,已经可以使用webpack打包 js、css、less、scss 等文件了,但是还有一些问题。例如,如果在css文件中使用图片、字体等文件,将会有相关的路径问题。
这个时候就要使用url-loader
来处理这些问题了。
cnpm i url-loader file-loader --save-dev
webpack.config.js
中添加处理url路径的loader模块:{ test: /\.(png|jpg|gif)$/, use: 'url-loader' }
limit
指定进行base64编码的图片大小;只有小于指定字节(byte)的图片才会进行base64编码:{ test: /\.(png|jpg|gif)$/, use: 'url-loader?limit=43960' },
下面来具体示例。
image-20200307155709570
我首先在项目中放入了2.png
和3.png
两个图片,那么下面使用css
中background
样式引入一下看看。
在index.html编写div如下:
image-20200307155917513
在index.css文件引入图片,如下:
image-20200307162416295
运行npm run dev
启动服务,查看打包是否报错如下:
image-20200307160157795
执行安装命令如下:
cnpm i url-loader file-loader -D
image-20200307160323527
webpack.config.js
中添加处理url路径的loader模块:{ test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader' }
image-20200307161845383
好了,到这里就配置好了。那么在执行npm run dev
来启动服务看看,如下:
image-20200307162444028
可以看到,浏览器已经正常显示图片了,使用网页元素看看图片是怎么样的,如下:
image-20200307162718933
从上面看到,当我们将图片使用url-loader
来处理,将会自动将图片转为 base64图片,那么则会让图片跟着 css 文件一起请求过来,不会发起二次请求来下载图片。
但是,这里有些地方需要注意一下,如果图片的大小特别大,那么如果还要转为 base64 格式的话,则会得不偿失。这时候还是使用 url 地址进行二次请求比较好。
那么如何来区分哪些图片可以转为 base64,哪些图片不要转为 base64呢?这时候可以使用图片的文件大小来进行区分,而区分的方法就是给url-loader
传入limit
参数。
先来看看这张2.png
图片的大小,如下:
image-20200307163604008
可以看到这张图片的大小是 674.18 kb
,而limit
参数的设置单位为 bytes
,只要将limit
参数设置大于或者等于 674.18 * 1024 bytes
就可以设置图片不转为 base64
,设置参数如下:
{ test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=500' }, // 处理 图片路径的 loader, 把小于 500 bytes 的文件打成Base64的格式,写入JS
这里设置只有小于 500 bytes的图片才会转为 base64,所以这张图片则不会被转为 base64 ,如下:
image-20200307165019785
image-20200307165153270
从上面可以看到图片的文件名已经被转为 一些看不懂的 hash值了。那么如果想要显示图片的原名称该怎么办?
上面的情况希望可以显示源图片的文件名,那么应该怎么处理呢?这时候可以使用name
参数,设置如下:
{ test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=500&name=[name].[ext]' },
参数说明:
[name]
显示源文件的名称[ext]
显示源文件的文件后缀名,不设置则统一转为 jpg 格式。那么配置之后,再来运行服务,浏览器查看如下:
image-20200307173304728
上面已经正常显示图片的源文件名称了,那么有一种情况,我们可以看到图片的文件夹路径是/
根路径,那么如果在不同的images
文件夹,有同名文件,这是什么样的情况呢?
images2
文件夹,放入另一张图片3.png
,改为2.png
,如下:image-20200307173738026
image-20200307173818470
在 index.html 编写如下:
image-20200307174013668
在index.css 编写如下:
image-20200307174718782
image-20200307174835115
出现这种问题的原因是因为webpack的打包顺序,如下:
/2.png
图片中/2.png
图片中,从而覆盖了第一张图片的内容/2.png
图片,所以两个div显示的图片是一样的最后一个图片。那么怎么解决这个问题呢? 这个只能在源文件的名称前面加一个 hash 值来作为区分了。
image-20200307201907578
{ test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=500&name=[hash:8]-[name].[ext]' },
重新运行服务,看看图片是否正常呈现,如下:
image-20200307202017479
cnpm i url-loader file-loader -D
webpack.config.js
中添加处理url路径的loader模块:{ test: /\.(jpg|png|gif|bmp|jpeg)$/, use: 'url-loader?limit=500&name=[hash:8]-[name].[ext]' },
上面这种输入参数的方式还有另一种方式,以对象的键值对方式,如下:
{
test: /\.(jpg|png|gif|bmp|jpeg)$/,
use: [{
loader: 'url-loader',
options: {
limit: 500, //是把小于500B的文件打成Base64的格式,写入JS
name: '[hash:8]-[name].[ext]' // [hash:8] 在名称前面设置8位哈希值,[name] 设置文件的原名, [ext] 设置文件的原后缀
}
}]
},// 处理 图片路径的 loader