目录
一. 什么是webpack
是一个前端资源构建工具,静态模块打包器
何为资源构建工具?
将一系列操作整合成大的工具处理。
何为静态模块打包器?
会对项目入口js文件中的各个模块,如less,vue等模块的依赖关系引入,形成一个chunk(代码块),再对这个代码块进行处理,比如将less编译成css等,这些操作就是打包,打包后将这些资源输出成bundle。
二. 核心概念
1. entry
即入口,指webpack以哪个文件为入口起点开始打包,分析构建内部依赖关系;
1.写成String格式:打包形成一个chunk,输出一个bundle文件,此时chunk默认名称为main
entry:'./src/js/index.js',
2.写成Array格式: 所有入口文件最终会形成一个chunk,输出一个bundle文件,用于HMR让html热更新生效。
entry:['./src/js/index.js','./src/js/test.js'],
3.写成Object格式: 用几个入口文件就形成几个chunk,输出一个bundle文件,chunk文件名称为对象的key。
entry:{
index:'./src/js/index.js',
test:'./src/js/test.js'
},
4.特殊用法
entry:{
// 所有入口文件最终会形成一个chunk,输出一个bundle文件
index:['./src/js/index.js','./src/js/index1.js'],
// 形成一个chunk,输出一个bundle文件
test:'./src/js/test.js'
},
2. output
即出口,webpack打包后的资源bundles输出到哪里去,以及如何命名;
publicPath指定引入公共路径的前缀,比如打包后的js会提供script标签src路径引入html文件,会在此路径前添加该前缀;
chunkFilename指定非入口chunk的名称,非入口chunk指的是非entry指定生成的chunk,一般为import语法分割的chunk或optimization对node_modules的包进行分割的chunk。
打包生成的js文件是以匿名函数定义模块,内部模块无法在外部引用,若要暴露函数内部的模块使用library配置项指定整个库向外暴露的变量名,libraryTarget指定此变量名添加到哪个browser,即使用什么什么模块化yu'f浏览器端指定的是‘window’,nodejs指定的是‘global’或'commonjs‘等
output:{
// 输出文件名
filename:'js/built.[name].[contenthash:10].js',
// 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
path:resolve(__dirname,'build'),
// 所有资源引入公共路径前缀
publicPath:'/',
// 非入口chunk的名称
chunkFilename:'[name]_chunk.js',
library:'[name]',
libraryTarget:'window',
},
3. loader
让webpack能够处理哪些非js的文件,因为webpack只能处理js文件;
4. plugins
即插件,可以执行范围更广的任务,包括从打包优化和压缩,一直到重新定义环境中的变量等;
5. mode
指示webpack使用相应模式的配置,包含两种选项development(开发模式),production(生产模式),不同的模式webpack会启用不同的插件。
三. 关于npm
npm,全称是 Node Package Manager,通俗的说就是使用一台服务器集中管理一些第三方的依赖库,比如jQuery、Bootstrap等,方便下载使用。
在www.npmjs.com可以搜索第三方包,开发者发布在这个网站上的,npm命令就是在这个网站上下载第三方包。
npm的第二层含义就是一个命令行工具,只要你安装了node就已经安装了npm,npm也有版本的概念,输入 npm –version查看版本,升级npm npm install - -global npm
安装node环境:
1.前往node官网下载node安装包 下载 | Node.js 中文网
2.选择对应操作系统的.msi安装包,下载到本地后一键点击下一步安装即可,安装完成后在cmd控制台中输入node -v查看当前安装的版本,若能够查看安装版本,则证明node环境安装成功。
3.随后在控制台中输入指令 npm install npm -g 全局安装npm,这样才能在全局环境下使用npm
4. 随后就可以在电脑的任何路径打开控制台使用npm命令了
1. npm项目初始化
建议每一个项目都要有一个package.json文件(包描述问价,相当于产品的说明书),这个文件可以通过 npm init的方式初始化出来,该文件中最有用的是dependencies选项,可以用来保存第三方包的依赖信息。
安装的依赖包会统一安装到项目的node_modules文件下。
建议执行npm install 包名的时候 都加上- - save这个选项,目的是在package.json的dependencies选项下保存生产依赖项信息(开发和部署时候都需要的)。如果node_modules删除了只需要 npm install就会自动把package.json中的dependencies中的所有依赖项下载回来,版本5以后就不用加了会自动添加;devDependencies开发依赖模块(只有开发的时候需要的模块);scripts配置本地可执行命令
示例1:在script中配置一条npm可执行命令用于将项目中的less文件转换为css
{
"scripts":{
"less":"lessc 1.less 1.min.css -x"
}
}
这样就能在当前项目的终端下使用npm run less 进行less文件的转换操作,相当于执行了lessc 1.less 1.min.css -x这条webpack命令。
示例2:在配置可执行脚本命令的时候,基于process的环境变量区分开发环境还是生产环境
"scripts":{
"serve":"set NODE_EVN=dev&&node index1.js",
"build":"set NODE_EVN=pro&&node index1.js"
}
上述配置中 set NODE_ENV=dev 代表设置全局环境变量,但在MAC中使用 export NODE_ENV=dev
在index1.js中获取script中设置的环境变量进行判断
let n = process.env.NODE_EVN;
if(n === 'dev') {
console.log('开发环境下执行');
} else {
console.log('生产环境下执行');
}
2. 关于package-lock.json
在npm5以前是不会有package-lock.json这个文件的,之后才加入。当安装包的时候npm会自动生成或者更新package-lock.json这个文件,它会保存node_modules中所有包的信息(版本,下载地址),这样重新执行npm install的速度会更快一些; 也可以锁定版本,防止执行npm install时下载新的版本(如果项目依赖1.1.1版本,但重新下载会下载最新版本)
3. 常用命令
- npm init npm init –y:快速生成package.json文件,跳过向导,所有选项默认
- npm install 包名:下载指定的模块
- npm install 包名 --save:把模块保存在清单生产依赖中 (简写npm i –S 包名)
- npm install 包名 --save-dev:把模块保存在清单开发依赖中
- npm install:一次性把dependencies中的所有依赖项下载回来(开发 + 生产环境)
- npm install --production:只安装生产依赖的模块
- npm install 包名 -g(--global):把模块安装在全局环境中
- npm uninstall 包名:只删除,如果有依赖项会依然保存
- npm uninstall --save:删除的同时也会把依赖项给删除 npm un –S 包名
- npm help:查看npm使用帮助
- npm install 包名@版本号 --save:下载指定版本的包
- npm 命令 --help:查看指定命令的使用帮助
- npm config list:查看npm配置信息
- npm view 包名 versions > 包名.versions.json:查看某个模块的版本信息,输出到指定的模块中
- npm root -g:查看全局安装模块的目录
4. 解决npm被墙
使用cnpm
npm存储包的服务器在国外,有时会被墙,速度很慢,淘宝的开发团队把npm在国内做了一个备份 ---> 中国 NPM 镜像
安装淘宝的cnpm:npm install –global cnpm
接下来安装包的时候把之前的npm替换成cnpm,npm就会通过淘宝的服务器下载(在任意目录下都可以执行,global是安装到全局的意思)
如果不想安装cnpm又想使用淘宝的服务器来下载安装:
npm install jquery –registry=https://registry.npm.taobao.org
可以将这个选现加入配置文件中
npm config set registry https://registry.npm.taobao.org
只要经过上面命令的配置,所有的npm install都会默认从淘宝的服务器下载。
yarn常用指令
可以使用yarn代替npm安装项目依赖,它的安装速度要比npm要快(npm 的两倍至三倍),使用 npm i yarn -g 安装yarn
- yarn init -y:生成package.json文件
- yarn install:一次性把dependencies中的所有依赖项下载回来(跑环境)
- yarn add 包名@x.xx.xx:下载指定的模块
- yarn remove 包名:移除指定的模块
基于nrm切源提高npm速度
首先安装nrm:npm install nrm -g
查看有哪些源:nrm ls
切源:nrm use xxx
一般切源到taobao,即使用指令 nrm use taobao
5. 什么情况下把模块安装在全局?
使用npm install 包名 -g安装在全局下的模块,对任何项目都有作用(可能会导致版本冲突),只能基于命令的方式管理,不能基于CommonJS中的require导入使用(不能导入到文件中基于代码来处理)。
可以使用指令npm root -g查看全局安装模块的目录,一般安装在本地电脑c盘AppData/Roaming/npm文件夹下,之所以可以对安装在全局下的xxx模块使用对应的命令进行操作,是因为在该文件下存在xxx.cmd的文件;
安装在本地的模块,可以在项目中导入基于代码操作,不能基于命令来操作。但是可以通过package.json中的scripts配置一些npm的可执行命令,配置后通过 npm run xxx 运行。
四. webpack开发环境配置
1. 使用webpack打包js文件
新建文件,使用npm init初始化项目,生成package.json文件,下载webpack执行如下指令:
npm i webpack@4 webpack-cli@3 -g
npm i webpack@4 webpack-cli@3 -D
创建文件加src,作为项目打包的入口文件,其下创建index.js作为入口js;创建文件build,作为项目的打包的出口文件。
在index.js输入简单js代码:
function add(a,b) {
return a + b;
}
console.log(add(100,200));
随后执行命令:含义是以./src/index.js为入口文件打包到./build/built.js
npx webpack ./src/index.js -o ./build/built.js --mode=development
此时发现build目录下出现了built.js文件,这就是打包完成后生成的文件,内容如下:
以生产打包运行一次:
npx webpack ./src/index.js -o ./build/built.js --mode=production
生成的built.js文件是经过压缩的js代码
将该文件引入html文件运行,控制台成功输出 300.
同样,webpack如果将json文件引入入口js,webpack也能进行打包;
2. webpack打包静态资源
webpack可以打包js和json类型的文件,但例如样式,图片,html格式的文件是无法打包的,若需要打包它们,需要使用配置文件的形式进行。
所有构建工具都是基于node.js平台运行的,模块化默认采用commonjs
webpack.config.js基础配置如下:
module.exports = {
// 入口起点
entry:'./src/index.js',
// 输出
output:{
// 输出文件名
filename:'built.js',
// 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
path:resolve(__dirname,'build')
},
// loader配置
module:{
rules:[
]
},
// 插件配置
plugins:[
],
// 模式'production'--生产模式,'development'--开发模式
mode:'development'
}
以上是webpack配置文件的基本要素,包括之前介绍的webpack的5个核心概念。
1.1. webpack打包css样式资源
打包样式资源需要在module中添加loader加载器,处理样式资源,需要style-loader,css-loader两个loader,其作用分别是:
style-loader:创建style标签,将js中的样式资源插入到head中生效
css-loader:将css文件变成commonjs模块加载到js中,里面内容是样式字符串
需要下载这两个loader:
cnpm i css-loader@3 style-loader@1 -D
添加loader需要书写一些配置项,书写在rules数组中,test属性使用正则表达式确定检测文件类型;use属性决定使用哪些loader进行处理。
注意use是一个数组,数组中的loader执行顺序是从左到右,从下到上依次执行
const {resolve} = require('path');
module.exports = {
entry:'./src/index.js',
output:{
filename:'built.js',
path:resolve(__dirname,'build')
},
// loader配置
module:{
rules:[
{
// 匹配哪些文件
test:/\.css$/,
// 使用哪些loader进行处理
use:[
// loader执行顺序,从右到左,从下到上
// 创建你style标签,将js中的样式资源插入到head中生效
'style-loader',
// 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader'
]
}
]
},
plugins:[
],
mode:'development'
}
最后运行webpack项目:
npx webpack
控制台显示如下:
1.2. 使用webpack打包less样式资源
打包处理less文件,其实是在打包css文件操作之前添加一个less-loader,其作用是将less文件编译成css文件,再做上述css文件的打包工作。
在rules数组中再添加一个配置less-loader配置对象,一个配置对象只能处理一种类型的文件
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
下载less-loader和less
npm i less-loader@5 -D
npm i less -D
运行webpack,终端输出如下:
1.3. 使用webpack打包html资源
plugins:[
// 需求:需要有结构的html文档
new HtmlWebpackPlugin({
// 复制./src/index.html,并自动引入打包输出的所有资源
template:'./src/index.html'
})
],
打包html需要使用Plugin处理,首先是下载:
npm i html-webpack-plugin@3 -D
在webpack配置文件中引入:
const HtmlWebpackPlugin = require('html-webpack-plugin');
该插件默认会创建一个html文件,它没有任何结构和样式,会自动引入输出打包的所有资源,如:js/css
随后添加Plugins配置:先在src目录下创建一个index.html文件并使用template属性将上述html复制到该插件默认创建的html文件中,除了引入html外插件还会自动引入打包输出的所有资源。
plugins:[
// 需求:需要有结构的html文档
new HtmlWebpackPlugin({
// 复制./src/index.html,并自动引入打包输出的所有资源
template:'./src/index.html'
})
],
运行webpack
会在built文件下生成一个index.html,该文件会复制src/index.html,并将打包后的js/css/less文件添加到这个html中。
1.4. 使用webpack打包图片资源
打包图片资源需要使用url-loader,而使用url-loader依赖于file-loader,所以使用前需要下载这两个loader.
npm i url-loader@3 file-loader@5 -D
1) 通过样式引入图片
在less文件中使用背景图片,这里引入两张图片,一张大小大于8kb,一张小于8kb
#wrapper {
color:#bbffcc;
font-size: 14px;
background: url(./img/logo.jpg) no-repeat 100% 100%;
}
#img-wrapper {
background: url(./img/naruto.jpg) no-repeat 100% 100%;
}
在index.html中书写对应的结构
<body>
<div id="wrapper"></div>
<div id="img-wrapper"></div>
</body>
随后在配置文件中做配置
loader属性指定使用的loader,当需要使用的loader只有一个时可以这样使用,使用的loader为多个需要写成use数组的形式;
使用options属性指定该loader的具体配置,这里限制文件大小小于8kb时将图片转换为base64的格式,base64会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析;base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢;所以要进行折中考虑,只有小图片才能进行base64处理。
{
// 处理图片资源(默认不能处理img标签的图片)
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
// 图片大小小于8kb,就会被base64处理,会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析。
// base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢
// 所以要进行折中考虑,只有小图片才能进行base64处理
limit:8 * 1024
},
},
最后执行打包指令,终端输出如下:
打包输出的目录如下
只有一张图片参与了打包,是大小超过8kb的,而大小小于8kb的图片转换为了base64字符串,在built.js中可见
2)打包img标签中的图片
以上打包的的配置无法处理img标签引入的图片,需要使用html-loader处理,它的作用是引入img,从而能被url-loader打包处理。
下载loader
npm i html-loader@0 -D
由于url-loader默认使用es6模块化进行解析,但html-loader引入图片使用的是commonjs,需要在opions对象中使用esModule:false关闭url-loader的es6模块化,使用commonjs解析。
打包生成的图片名默认会以唯一hash值命名,可以使用name属性name:'[hash:10].[ext]定义图片名,[hash:10]即取图片的hash值前十位,[ext]即取原片原来的扩展名。
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8 * 1024,
esModule:false,
name:'[hash:10].[ext]',
},
},
{
test:/\.html$/,
loader:'html-loader',
},
打包后输出如下:
打包后生成的html文件:
1.4. 使用webpack打包其它资源
使用file-loader打包其它资源;
exclude配置排除指定的资源,options配置项给打包生成的文件重命名
{
// 处理其它文件,排除css,js,html等资源
exclude:/\.(css|js|html|less|jpg|png|gif)$/,
loader:'file-loader',
options:{
name:'[hash:10].[ext]'
},
}
3. devServer
项目开发是一个持续的过程,目前当我们修改了戴拿想看最新的页面效果需要重新打包构建一次,可以使用devServer自动构建打包,其作用是构建一个开发服务器,用来自动编译,自动打开浏览器,自动刷新浏览器它只会在内存中进行打包,不会有任何输出(不会生成built.js)。
需要在webpack.conjig.js进行配置,在module.exports暴露的暴露的对象里添加配置项如下:
devServer:{
// 构建后的项目路径
contentBase:resolve(__dirname,'build'),
// 启动gzip压缩
compress:true,
// 端口号
port:3000,
// 域名
host:'localhost',
// 自动打开浏览器
open:true,
// 开启HMR功能
hot:true,
}
使用需要下载 webpack-dev-server:
npm i webpack-dev-server@3 -D
启动devServer指令:
npx webpack-dev-server
启动后终端如下显示,会自动打开本地默认浏览器,此时程序一直运行,修改代码会自动编译,自动刷新浏览器:
4. webpack分模块打包
将打包输出的全部在build目录下,包括了多种类型的文件,不好维护,需要创建多个文件夹存放不同后缀名的文件。
首先修改output配置,将输出的js文件放在build的js目录下:
output:{
// 输出文件名
filename:'js/built.js',
// 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
path:resolve(__dirname,'build')
},
其次将输出的图片和其他资源分别打包输出在build文件的imgs和media目录下,需要在对应的options配置中添加outputPath属性,指定输出的路径,路径不需要写完整路径,webpack会以output配置项的path配置为根路径,在此路径目录下创建outputPath指定的文件,存放打包后的内容。
{
exclude:/\.(css|js|html|less|jpg|png|gif)$/,
loader:'file-loader',
options:{
name:'[hash:10].[ext]',
outputPath:'media'
}
},
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8 * 1024,
esModule:false,
name:'[hash:10].[ext]',
outputPath:'imgs'
},
}
打包完成后的build文件如下
5.HMR热模块切换
默认情况下修改一个模块的内容,所有模块的内容会重新打包,这样性能较差.使用HMR(hot module replacement)热模块切换
1) 作用
一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度。
2) 使用
1. 样式文件
可以使用HMR功能,因为style-loader内部实现了,在开发服务器配置中添加,hot:true属性,这样devServer只会重新构建样式文件,其它类型的文件不会变。
devServer:{
// 构建后的项目路径
contentBase:resolve(__dirname,'build'),
// 启动gzip压缩
compress:true,
// 端口号
port:3000,
// 自动打开浏览器
open:true,
// 开启HMR功能,当修改了webpack配置,新配置想要生效,必须重新启动webpack服务
hot:true
}
2. js文件
hot:true添加后默认不能使用HMR功能。
需要修改js代码,添加支持HMR功能的代码,只能处理非入口文件的js;在入口js文件中添加如下代码:
// 全局寻找module,若module.hot为true,说明开启了HMR功能
if(module.hot) {
// 让HMR代码生效
module.hot.accept('./模块路径.js',function(){
//该方法会监听xxx.js文件的变化,一旦发生变化,其它模块不会重新打包构建,会执行后面的回调函数
xxx();
})
}
3. html文件
hot:true添加后默认不能使用HMR功能,同时html文件不能热更新了。
需要修改entry选项,将html文件引入(html文件不需要做HMR,因为一个项目只有一个index.html文件,修改其他的文件其必定会改变)。
module.exports = {
entry:['./src/js/index.js','./src/index.html']
}
完整配置代码:
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 入口起点
entry:'./src/js/index.js',
// 输出
output:{
// 输出文件名
filename:'js/built.js',
// 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
path:resolve(__dirname,'build')
},
// loader配置
module:{
rules:[
{
// 匹配哪些文件
test:/\.css$/,
// 使用哪些loader进行处理
use:[
// loader执行顺序,从右到左,从下到上
// 创建你style标签,将js中的样式资源插入到head中生效
'style-loader',
// 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader'
]
},
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
{
// 处理图片资源(默认不能处理img标签的图片)
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
// 图片大小小于8kb,就会被base64处理,会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析。
// base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢
// 所以要进行折中考虑,只有小图片才能进行base64处理
limit:8 * 1024,
esModule:false,
name:'[hash:10].[ext]',
outputPath:'imgs'
},
},
{
test:/\.html$/,
// 处理html文件的img图片,负责引入html从而能被页面的url-loader处理
loader:'html-loader',
},
{
// 处理其它文件,排除css,js,html资源
exclude:/\.(css|js|html|less|jpg|png|gif)$/,
loader:'file-loader',
options:{
name:'[hash:10].[ext]',
outputPath:'media'
}
}
]
},
// 插件配置
plugins:[
// 需求:需要有结构的html文档
new HtmlWebpackPlugin({
// 在src目录下创建一个index.html文件并复制到该插件默认创建的html文件中
// 除了引入html外插件还会自动引入打包输出的所有资源
template:'./src/html/index.html'
}),
],
// 模式'production'--生产模式,'development'--开发模式
mode:'development',
// 开发服务器,用来自动编译,自动打开浏览器,自动刷新浏览器
// 启动指令:npx webpack-dev-server,启动前需要下载
devServer:{
// 构建后的项目路径
contentBase:resolve(__dirname,'build'),
// 启动gzip压缩
compress:true,
// 端口号
port:3000,
// 自动打开浏览器
open:true
}
}
五. webpack生产环境配置
1. 提取css为单独文件
打包后的css文件会自动引入到html的style标签中,会使html文件体积过大,在生产上线阶段需要分离;
1)提取css文件需要下载插件 mini-css-extract-plugin
npm i mini-css-extract-plugin@0 -D
2)style-loader会将css代码插入到style标签中,生产环境不需要使用,替换为MiniCssExtractPlugin.loader;
{
test:/\.css$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader'
]
},
3)添加插件,并对输出的css文件重命名
new MiniCssExtractPlugin({
// 1.1对输出的文件进行重命名
filename:'css/built.css'
}),
重新打包,终端输出如下:
打包输出文件如下:
2. css兼容性处理
部分浏览器不能兼容如flex等css样式需要做兼容,使用postcss-loader处理兼容性问题,它依赖于插件postcss-preset-env
1)安装
npm i postcss-loader@3 postcss-preset-env@6 -D
2)添加postcss-loader配置,这里由于不适用该loader的默认配置,需要在use数组中将该loader写成一个配置对象的形式,在其中添加postcss-loader插件postcss-preset-env,其作用是帮助postcss找到package.json中browserlist里面的配置,通过配置加载指定的css兼容性样式
{
test:/\.css$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
// 修改loader配置
{
loader:'postcss-loader',
options:{
ident:'postcss',
plugins:() => [
// postcss插件
require('postcss-preset-env')()
]
}
}
]
}
3)修改package.json,添加browserlist配置
"browerslist":{
"development":[
// 距离最近的xxx版本
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境(默认找生产环境)
"production":[
// 大于99.8%浏览器
">0.2%",
// 不要兼容已死的浏览器如IE10
"not dead",
"not op_mini all",
]
}
// !!package.json不能添加注释
browerslist配置默认使用的是生产环境的配置,如果要使用其中开发环境配置需要在webpack配置文件中修改环境变量
process.env.NODE_ENV = 'development'
打包输出的css文件中对部分样式按照package.json配置要求做了兼容性处理;
3. 压缩css
使css文件大小减小,加快项目加载速度。使用插件optimize-css-assest-webpack-plugin;
1)安装
npm i optimize-css-assets-webpack-plugin@5 -D
2)配置
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
......
plugins:[
// 压缩css
new OptimizeCssAssetsWebpackPlugin()
],
打包后css文件经过了压缩
4. 配置js语法检查eslint
eslint规范了项目代码风格,检查语法错误,团队开发中要求书写代码格式的一致性,正确性。
使用eslint-loader,只检查自己写的代码,第三方的库是不用检查的,排除node modules。
推荐使用airbnb规则,这是一种代码风格库,依赖eslint-config-aribnb-base eslint-plugin-import eslint 这些插件。
1)安装
npm i eslint-config-airbnb-base@14 eslint-plugin-import@2 eslint@6 eslint-loader@3 -D
2)配置webpack
添加fix:true配置可以让不符合规范的代码自动修改,不出现警告
{
// 3.1语法检查loader
test:/\.js$/,
exclude:/node_modules/,
// 优先执行
enforce:'pre',
loader:'eslint-loader',
options:{
// 自动修复eslint错误,发现了代码规范错误,自动修复,不用手动修改
fix:true
}
},
3)配置package.json
配置eslintConfig,需要继承库airbnb-base,会自动加载其中的配置,进行语法检查。
"eslintConfig": {
"extends": "airbnb-base"
}
此外在不想被eslint检查的代码行上一行加上 // eslint-disable-next-line 后一行内容不进行eslint检查
5. js兼容问题
js兼容性处理,解决低版本ie不兼容es6语法的问题,使用babel-loader,以及@babel/preset-env处理预设环境,这两者结合可以解决基本的js兼容问题,而对于Promise等高级语法还需要使用@babel/polyfill进行兼容,这三者结合解决全部js兼容性问题。当使用@babel/polyfill是将全部兼容性代码引入项目,会使项目体积过大,可以使用core-js按需加载。
1) 下载
npm i babel-loader@8 @babel/preset-env@7 @babel/core@7 -D
npm i @babel/polyfill@7 -D
npm i core-js@3 -D
2)使用
{
// 4.1解决js兼容性loader
test:/\.js$/,
exclude:/node_modules/,
loader:'babel-loader',
options:{
presets:[
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns:'usage',
// 指定core-js版本
corejs:{
version:3
},
// 指定兼容性做到哪个版本浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
]
}
},
使用@babel/polyfill非按需加载时,由于@babel/polyfill不是插件,只要在js文件中引入即可。
import '@babel/polyfill';
6. js的压缩
webpack生产环境会自动加载UglifyJsPlugin,只需将mode选项设置为production即可压缩js代码.
7. html的压缩
使用HtmlWebpackPlugin压缩,配置如下:
new HtmlWebpackPlugin({
template:'./src/html/index.html',
// 压缩html代码
minify:{
// 移除空格
collapseWhitespace:true,
// 移除注释
removeComments:true
}
}),
8. 缓存
1)babel缓存
在生产模式中,无法使用HMR提高打包速度,所以使用babel缓存,让第二次打包速度更快,配置如下
{
test:/\.js$/,
exclude:/node_modules/,
loader:'babel-loader',
options:{
presets:[
........
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory:true
}
}
2) 文件资源缓存
作用:让代码上线运行缓存更好使用,上线性能优化
浏览器获取服务器文件时,会在本地做一个缓存,在指定时间内就不会再向浏览器请求数据,直接使用本地缓存加载页面。但如果此时服务器文件发生变化,会造成资源不同步的问题。
解决方法:提供维护hash是否变化,判断资源是否更新
1.hash:每次webpack构建时会生成一个唯一的hash值,用hash值去命名打包后的文件。但js和css文件同时使用一个hash值,如果只修改某个js文件,重新打包,会导致所有文件缓存失效。
2.chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk(css是在js中被引入的,所以同属于一个chunk.js,所有在同一个入口js中引入的文件都属于一个chunk),那么hash值就是一样的,如果同一个chunk的css文件发生变化而js文件没有变化,依旧会改变chunkhash。
3.contenthash:根据文件的内容生成hash值。不同内容的文件的hash值一定不同。
给输出的js和css文件使用contenthash前十位命名 :
output:{
filename:'js/built.[contenthash:10].js',
path:resolve(__dirname,'build')
},
plugins:[
new MiniCssExtractPlugin({
// 1.1对输出的文件进行重命名
filename:'css/built.[contenthash:10].css'
}),
],
打包后终端显示结果,只要这些文件内容不变,不管打包几次它们的hash值都不变
9. tree shaking
作用:去除项目中没有使用的js/css代码,
使用前提:
1.必须使用es6模块化
2.开启production环境
在package.json中配置:
默认“sideEffect”:false -- 所有代码都没有副作用,都可以进行tree shaking,这样会把css以及@babel以及ployfill文件干掉,要添加配置:
"sideEffect":["*.css","*.less"]
将css和less后缀结尾的文件不放在tree shaking范围内。
以上操作webpack.config.js的完整代码:
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 2.3设置node.js环境变量
process.env.NODE_ENV = 'production'
module.exports = {
// 入口起点
entry:'./src/js/index.js',
// 输出
output:{
// 输出文件名
filename:'js/built.js',
// 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
path:resolve(__dirname,'build')
},
// loader配置
module:{
rules:[
{
// 匹配哪些文件
test:/\.css$/,
// 使用哪些loader进行处理
use:[
// loader执行顺序,从右到左,从下到上
// 创建你style标签,将js中的样式资源插入到head中生效
MiniCssExtractPlugin.loader,
// 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader',
// 修改loader配置
{
loader:'postcss-loader',
options:{
ident:'postcss',
plugins:() => [
// postcss插件
require('postcss-preset-env')()
]
}
}
]
},
{
// 3.1语法检查loader
test:/\.js$/,
exclude:/node_modules/,
// 优先执行
enforce:'pre',
loader:'eslint-loader',
options:{
// 自动修复eslint错误,发现了代码规范错误,自动修复,不用手动修改
fix:true
}
},
{
// 4.1解决js兼容性loader
test:/\.js$/,
exclude:/node_modules/,
loader:'babel-loader',
options:{
presets:[
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns:'usage',
// 指定core-js版本
corejs:{
version:3
},
// 指定兼容性做到哪个版本浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
]
}
},
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
{
// 处理图片资源(默认不能处理img标签的图片)
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
// 图片大小小于8kb,就会被base64处理,会将图片转换成一种字符转,浏览器解析这种字符串就会将其当作图片解析。
// base64编码格式能够减少请求数量,但图片体积会更大,文件请求速度更慢
// 所以要进行折中考虑,只有小图片才能进行base64处理
limit:8 * 1024,
esModule:false,
name:'[hash:10].[ext]',
outputPath:'imgs'
},
},
{
test:/\.html$/,
// 处理html文件的img图片,负责引入html从而能被页面的url-loader处理
loader:'html-loader',
},
{
// 处理其它文件,排除css,js,html资源
exclude:/\.(css|js|html|less|jpg|png|gif)$/,
loader:'file-loader',
options:{
name:'[hash:10].[ext]',
outputPath:'media'
}
}
]
},
// 插件配置
plugins:[
// 需求:需要有结构的html文档
new HtmlWebpackPlugin({
template:'./src/html/index.html',
// 压缩html代码
minify:{
// 移除空格
collapseWhitespace:true,
// 移除注释
removeComments:true
}
}),
// 分离js和css
new MiniCssExtractPlugin({
// 1.1对输出的文件进行重命名
filename:'css/built.css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin()
],
// 模式'production'--生产模式,'development'--开发模式
mode:'development',
// 开发服务器,用来自动编译,自动打开浏览器,自动刷新浏览器
// 启动指令:npx webpack-dev-server,启动前需要下载
devServer:{
// 构建后的项目路径
contentBase:resolve(__dirname,'build'),
// 启动gzip压缩
compress:true,
// 端口号
port:3000,
// 自动打开浏览器
open:true
}
}
六. webpack性能优化
1. source-map
一种提供源代码到构建后代码映射的技术,如果构建后代码出错了,通过映射可以追踪源代码的错误.
1) 使用
在webpack.config.js中添加如下属性
devtool:'source-map'
打包后在js目录下出现了built.js.map文件,外文件提供了源代码和构建后代码的映射关系。
2) devtool参数
[inline-|hidden-|eval-] [nosources-] [cheap-[module-]] source-map
- source-map
代码映射文件位置:外部
能提示到错误代码的准确信息和源代码的错误位置
- inline-source-map
代码映射文件位置:内联
只生成一个source-map
能提示到错误代码的准确信息和源代码的错误位置
- hidden-source-map
代码映射文件位置:外部
xxx.js.map 能提示到错误代码的错误原因,只有构建后代码的错误位置(半隐藏)
- eval-source-map
代码映射文件位置:内联
每一个参与打包的文件都生成了对应的source-map,都在eval函数中
能提示到错误代码的准确信息和源代码的错误位置,错误提示处多了一个hash值
- nosources-source-map
代码映射文件位置:外部
错误代码准确信息,源代码错误位置,但是没有任何源代码和构建后代码信息(全隐藏)
- cheap-source-map
代码映射文件位置:外部
能提示到错误代码的准确信息和源代码的错误位置,cheap只精确到行不是列(一行多句代码)
- cheap-module-source-map
代码映射文件位置:外部
能提示到错误代码的准确信息和源代码的错误位置,module会将loader的source-map加入,这样对于代码调试是最友好的
内联和外部的区别
1. 外部生成了.map映射文件,内联没有,source-map文件直接生成在js内容后(base64格式)
2. 内联构建速度快
开发环境:速度快,调试友好 使用eval-source-map,是主流框架的脚手架配置
生产环境:代码隐藏,内联会让代码体积变大,不能使用,一般使用source-map
2. oneof
一个项目会有很多loader如果正常情况下一种类型的文件会依次匹配rules数组中的所有loader,即使匹配到了相应的loader依旧会向下匹配,使用oneof配置,只要文件匹配到执行loader后就不会继续向下匹配,但要注意这只是用一类文件匹配一个loader的场合,需要匹配多个loader则需要将其余的loaders放在oneof配置对象之外。
module:{
rules:[
// 不需要使用oneof的loader
{ ...... },
{
// 以下loader只会匹配一个,如css文件匹配到/\.css$/就不继续了,oneof中不能有两项配置处理同一个文件
oneof:[
{
test:/\.css$/,
use:['style-loader','css-loader']
},
......
]
}
]
},
3. code split
开发完成的应用可能非常的庞大,需要使用webpack的code split即代码分割技术进行文件拆分,从而实现按需加载等项目性能优化处理。
配置单入口:用于单页面应用,最终会输出一个bundle,所有文件都会集合在一起。
entry:'./src/js/index.js'
配置多入口:用于多页面应用,一个页面打包成一个chunk.js
entry:{
index:'./src/js/index.js',
test:'./src/js/test.js'
},
output:{
// 输出文件名
filename:'js/built.[name].[contenthash:10].js',
// 输出路径,__dirname代表当前文件目录绝对路径,build是输出的文件名
path:resolve(__dirname,'build')
},
打包后输出如下:打包输出两个chunk
1.optimization配置
1).可以将node_modules中的代码单独打包成一个chunk,即第三方代码单独打包。
2).自动分析多入口chunk中,有没有公共的文件,如果有打包成单独的chunk(比如多个页面依赖jquery.js时,会将其单独带包成chunk一次)
在module.exports的对象中添加属性:
optimization:{
splitChunks:{
chunks:'all'
}
}
为单入口文件index.js中引入vue
打包后终端显示如下:
2.单页面应用将多个模块分别打包
optimization配置只能将js中node_module中的代码单独打包成一个chunk,但不能单独打包非第三方的代码,使用es10的import动态导入语法。
在入口index.js中添加如下代码,无需额外引入test.js。
import(/* webpackChunkName:'test' */'./test.js')
.then((result) => {
// 文件加载成功
console.log(result.add(123,211));
})
.catch(() => {
// 文件加载失败
})
终端输出如下,test.js独立打包成一个chunk了 。
4.js懒加载和预加载
懒加载指在文件需要时候被加载 ,多次调用会走缓存,和代码分割一样,使用import动态导入语法,写在某个异步回调中。
document.getElementById('btn1').onclick = function() {
import(/* webpackChunkName:'test' */'./test.js')
.then((result) => {
// 文件加载成功
console.log(result.add(123,211));
});
};
预加载Perfetch:会在使用之前提前加载js文件,正常加载会认为是并行加载,同一时间加载多个文件,预加载是等其它资源加载完毕浏览器空闲了,再去加载指定为预加载的文件。需要预加载,需添加webpackPerfetch:true。
document.getElementById('btn1').onclick = function() {
import(/* webpackChunkName:'test',webpackPerfetch:true */'./test.js')
.then((result) => {
// 文件加载成功
console.log(result.add(123,211));
});
};
七.resolve配置
resolve配置用于解析模块规则,常用配置如下:
alias配置路径别名,用来简写路径;
extension用于省略文件后缀名规则,里面的指定的后缀名在项目中使用时可以省略。
module用于告诉webpack解析模块前往的目录,默认是node_modules,可以使用参数一指定node_modules相对于当前文件的路径,这样可以减少程序查找node_modules的过程
resolve:{
// 配置解析模块的路径别名
alias:{
$css:resolve(__dirname,'src/css')
},
// 配置省略文件后缀名规则
extension:['js','json','css'],
// 解析模块时方便查找node_modules
module:[resolve(__dirname,'./node_modules'),'node_modules']
},
总结:
其实在一周前的2月7日,vue官方将vue3.0设为了默认版本,在新版本中官方大力提倡使用Vite进行构建,Pinia取代vuex做状态管理......但是当下的绝大多数项目还是基于webpack构建的,掌握好webpack依旧重要。不过话虽如此,也要时刻关注了解新技术的发展。哎,真是学海无涯!!!