webpack的简介
-D 表示项目上线不需要了 development 开发
webpack的好处
之前写的JQ版本的CRM项目:
- 项目中有大量的模块(JS文件,CSS文件,IMG文件…)
- 项目放到服务器,通过浏览器访问时,需要发送大量的请求,加载速度肯定慢
- 项目上线时,还需要手动合并压缩JS,CSS,HMTL
- 小图合大图,减少HTTP的请求次数,精灵图,base64…
- 一个文件依赖大量的外部资源 < script src=“jq.js”> < script src=“index.js” > < link href=“xx.css”>
href=“xx.css”> - JS高级语法通过bable转成JS低级语法,让浏览器识别,把LESS转成CSS
上面的问题可以通过自动化构建的工具解决:webpack
webpack是什么?
https://www.webpackjs.com/
- 基于node.js开发的一个工具。
- 是一个模块的打包工具。 webpack会把所有的文件都当作模块
模块规范: 如果定义模块,如何导出模块,如何导入模块。
- CmmonJS 模块规范 node
- ES6 模块规范 vue react
- CMD 模块规范
- AMD 模块规范
- UMD 模块规范
基于工程化项目开发,项目的入口只能有一个。 我们把依赖都放到这个入口文件中,如下:
import "./index.css"
import data from "./data.json"
发现浏览器根本不能识别,需要借助webpack这个工具,让浏览器识别。
在webpack4.0之后,支持零配置打包,你不需要进行任何配置。
在老的webpack版本,你需要写代码去配置你的webpack。
webpack使用
首先初始化项目:npm init -y
安装webpack:
本地安装:npm i webpack webpack-cli
全局安装:npm i webpack webpack-cli -g
npx:
你要使用webpack命令,你必须全局安装webpack
如果你不想全局安装,那么就可以使用npx
npx 默认会临时全局安装webpack
开发模式打包 和 生产模式打包: 默认是生产模式打包
开发模式打包:
npx webpack ./src/index.js -o ./dist/main.js --mode=development
生产模式打包:
npx webpack ./src/index.js -o ./dist/main.js --mode=production
生产模式打包比开发模式打包多了一个代码压缩。
npx webpack ./src/index.js -o ./dist/main.js --mode=development是零配置打包。
零配置打包还是比较弱,一般情况下我们自己配置webpack。
配置webpack
项目的根目录下,创建一个webpack.config.js,webpack配置都写在这里面
配置入口和出口
// 这里写node代码,代码导出给webpack工具,
// 打包的时候回检测是否存在,存在就使用这些规范
const path = require("path")
module.exports={
mode:"development", //开发模式打包,代码不压缩
// 默认使用生产模式production,代码压缩
entry:"./src/index.js", //入口
output:{ //出口
//filename:"main.js",
//name表示原文件的名,后面跟的hash值
filename:"[name].[chunkhash].js"
path:path.resolve(__dirname,"dist")
}
}
配置OK后,只需要通过webpack命令就可以打包了,webpack会自动找webpack.config.js文件
配置处理css
style-loader 和 css-loader
让webpack处理样式: webpack默认不能处理css 我们配置让它可以处理css。
需要借助于Loader loader就是一个翻译官 可以把webpack不识别的代码翻译成webpack可以识别的代码。
处理css,需要两个loader,一个是style-loader css-loader:
- css-loader:把css文件转成commonjs模块,加载到JS模块
- style-loader: 创建一个style标签,把样式资源添加页面的头部中 所谓的内部样式
安装这两个loader:
npm i style-loader css-loader -D
-D 表示项目上线不需要了
入口文件中引入index.css
import "./index.css"
module:{ //mdule 中配置各种loader
rules:[ //规则,规则都放这里面
{
test:/\.css$/,
use:[ //创建style标签用来放样式,style标签在head标签中
"style-loader",
"css-loader" //把css文件转成commonjs模块
]
}
]
}
loader的加载顺序:从下向上,从右向左,依次执行
配置处理less
less-loader
让webpack处理less文件:
肯定需要配置loader让webpack可以打包less文件。
这个loader叫:less-loader 这个less-loader还需要依赖less模块,所以如果没有安装less的话需要一起安装。
你需要安装两个模块:npm i less less-loader -D
less只需要安装,不需要配置
入口文件中引入index.less
import "./01.less"
{
test:/\.less$/, //匹配到.less结束的文件
use:[
"style-loader", // 同css
"css-loader", // 同css
"less-loader" // 把less代码转化为css代码
]
}
生成配置新的html模板
html-webpack-plugin
自动创建HTML模板:
- 之前打包完后,生成一个main.js文件,需要手动放到index.html中
- 我们的想法:打包完后的main.js自动塞到index.html中。
- 现在我们在public下面的有一个index.html,这个index.html叫最基本的模块
- 我们的想法是,根据上面的模板自动创建出一个新的html文件,并把上main.js放到新的html文件中
安装:
npm i html-webpack-plugin -D
const HtmlWebpackPlugin =require("html-webpack-plugin")
// 配置plugin插件 配置插件需要new一下
plugins:[ //配置根据模板生成新的html文件
new HtmlWebpackPlugin({ //并在这个html文件中引入打包好的js文件
template:"./public/index.html" //指定模板
filename:"index.html" //指定打包生成的HTML文件名
//不写filename默认使用原文件名index.html,最好用index
})
]
配置删除无用的文件
clean-webpack-plugin
当配置出口文件名时通常都不是写死的,常常使用hash值得形式,( filename:"[name].[chunkhash].js" )因此就会产生很多的hash值不同的出口文件,旧文件没有用但依旧在,这个插件就是为了删除旧的无用文件。
使用 clean-webpack-plugin 这个插件,可以清理dist目录下面没有用的文件,安装:
npm i clean-webpack-plugin -D
//这个是一个对象,需要解构
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
plugins:[
//打包之前会,删除整个目标dist文件下的所有旧的文件
new CleanWebpackPlugin(),
]
配置打包css图片
配置webpack打包图片:
需要两个loader: file-loader url-loader
安装:
npm i file-loader url-loader -D
url-loader:比file-loader更加强大 url-loader依赖于file-loader 配置时,只会配置url-loader
file-loader能处理的url-loader也能处理
{ //处理css背景图片,背景图标等
test:/\.(jpg|png|gif)$/
} //会将打包的图片放到js中
{
loader: "url-loader",
options:{
outputPath:"images", //打包到那个文件
// 图片小于10kb,处理成base64,大于就打包成文件
limit:10*1024,
//name:"[name].[ext]" //使用原文件名
name:"[hash:24].[ext]" //多使用hash值
// 重命名,ext文件后缀,使用原文件后缀
}
}
配置html中的图片
file-loader
img类型图片
如果我们的Html文件中,使用img标签形式的图片,webpack能处理吗?
答:不能的,因为打包生成的html文件中的img标签不变,需要借助一个loader将路径变成打包好的图片路径或者base64。
如果你想处理模板中的img类型的图片,还需要一个loader,叫html-loader。
安装:
npm i html-loader -D
//处理HTML中的img标签
{
test: /\.html$/,
loader:"html-loader"
}
配置字体图标
file-loader
配置webpack打包字体图标需要的loader也是 file-loader
入口文件中引入引入字体图标样式:
import "./fonts/iconfont.css"
//处理字体图标
{ //排除的意思
exclude:/\.(css|js|html|json|less|jpg|png|gif)$/,
loader:"file-loader"
//options:{ 配置存放目录
// name:'[hash:12].[ext]',
// outputPath:"source"
//}
}
因为字体图标引入的是css,所以需要配置上面的css-loader。
配置开发服务器
webpack-dev-server
之前的打包是在硬盘上打包。之前的打包是直接生成到硬盘上的,明天打开看还会有。 使用代码npx webapck
作用是将打包好的那一堆文件托管的这个开发服务器上,后面通过该开发服务器访问打包好的代码文件。该服务器基于express。
配置开发服务器,需要安装一个模块:webpack-dev-server
npm i webpack-dev-server -D
devServer:{
//配置开发服务器托管的资源文件夹路由,表示该服务器托管dist下面的资源
contentBase:path.resolve(__dirname,"dist"),
port:8080, //开发服务器的端口
open:true, //自动打开浏览器
compress:true, //启动gzip压缩
}
只需要安装,不需要引入,只需要配置,但是打包的命令就变了
以前在硬盘中打包:npx webapck
现在在内存中打包:npx webpack-dev-server
在内存中打包看不见。
之前在硬盘上打包直接结束,在内存中打包相当于开了一个服务器,所以会一直闪。
在package.json中配置
"scripts": {
"serve":"npx webpack-dev-server",
"build":"npx webpack",
}
当终止操作时就会关闭服务器,就不能访问了。需要再次打开服务器输入代码:npm run serve
npm run serve 在内存中打包
npm run build 在硬盘中打包
抽离css
mini-css-extract-plugin
最好将图片,字体图标打包都放到一个文件夹里
默认情况下:打包的CSS是HTML的内部样式,我们的想放到外部,
专业的叫法:抽离CSS。
模块:mini-css-extract-plugin
npm i mini-css-extract-plugin -d
let MiniCssExtractPlugin = require("mini-css-extract-plugin")
//需要把前面的style-loader替换为 MiniCssExtractPlugin.loader
new MiniCssExtractPlugin({
filename:"css/index.css" //打包到css文件夹下面的index.css中
})
压缩css、js、html
压缩css
optimize-css-assets-webpack-plugin
把抽离出来的CSS也进行压缩,有一个模块可以专门对CSS进行压缩,
安装:
npm install --save-dev optimize-css-assets-webpack-plugin
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
new OptimizeCssAssetsPlugin() //压缩css代码
压缩html
想在开发模式下,压缩HTML代码:
//配置根据模板生成新的html文件并在这个html文件中引入打包好的js文件
new HtmlWebpackPlugin({
template: "./public/index.html", //指定模板
// filename: "index.html" //指定打包生成的HTML文件名
//不写filename默认使用原文件名index.html,最好用index
minify:{ //压缩html
removeComments:true, //移除html中的注释
collapseWhitespace:true, //删除空白符和换行符
}
}),
压缩JS:
uglifyjs-webpack-plugin
如果模式是生产模式 mode是production,默认就会压缩JS代码。
安装插件:
npm i uglifyjs-webpack-plugin -D
let uglifyjs = require('uglifyjs-webpack-plugin');
new uglifyjs({ //压缩js
uglifyOptions:{
output: {
comments:false //去除注释
}
}
})
完整代码
// 这里写node代码,代码导出给webpack工具,
// 打包的时候回检测是否存在,存在就使用这些规范
const path = require("path")
// 根据模板生成新的html文件,并把打包好的js放到这个文件中
const HtmlWebpackPlugin = require("html-webpack-plugin")
//这个是一个对象,需要解构,打包前清空目标dist文件夹
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
//抽离css
let MiniCssExtractPlugin = require("mini-css-extract-plugin")
// 压缩抽离的css代码
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// 压缩JS代码
let uglifyjs = require('uglifyjs-webpack-plugin');
module.exports = {
mode: "development", //开发模式打包,代码不压缩
// 默认使用生产模式production,代码压缩
entry: "./src/index.js", //入口
output: { //出口
//filename:"main.js",
//name表示原文件的名,后面跟的hash值
filename: "[name].[chunkhash].js",
path: path.resolve(__dirname, "dist")
},
//module 中配置各种loader
module: {
rules: [ //规则,规则都放这里面
{
test: /\.css$/, //匹配到.css结束的文件
use: [ //创建style标签用来放样式,style标签在head标签中
//"style-loader",
//如果要抽离css,就不能使用了,因为这个是把css处理成内部样式
MiniCssExtractPlugin.loader,
"css-loader" //把css文件转成commonjs模块
]
},
{
test: /\.less$/, //匹配到.less结束的文件
use: [
"style-loader", // 同css
"css-loader", // 同css
"less-loader" // 把less代码转化为css代码
]
},
// 处理css中的图片
{
test: /\.(jpg|png|gif)$/, //处理css背景图片,背景图标等
use: [
{
loader: "url-loader",
options: {
outputPath: "images", //打包到那个文件
// 图片小于10kb,处理成base64,大于就打包成文件
limit: 10 * 1024,
//name:"[name].[ext]" //使用原文件名
name: "[hash:24].[ext]" //多使用hash值
// 重命名,ext文件后缀,使用原文件后缀
}
}
]
},
//处理HTML中的img标签
{
test: /\.html$/,
loader: "html-loader"
},
//处理字体图标
{
exclude: /\.(css|js|html|json|less|jpg|png|gif)$/,
loader: "file-loader",
options:{ //配置存放目录
name:'[hash:12].[ext]',
outputPath:"source"
}
}
]
},
// 配置plugin插件 配置插件需要new一下
plugins: [
//配置根据模板生成新的html文件并在这个html文件中引入打包好的js文件
new HtmlWebpackPlugin({
template: "./public/index.html", //指定模板
// filename: "index.html" //指定打包生成的HTML文件名
//不写filename默认使用原文件名index.html,最好用index
minify:{ //压缩html
removeComments:true, //移除html中的注释
collapseWhitespace:true, //删除空白符和换行符
}
}),
//打包之前会,删除整个目标dist文件下的所有旧的文件
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename:"css/index.css" //打包到css文件夹下面的index.css中
}),
new OptimizeCssAssetsPlugin(), //压缩css代码
new uglifyjs({ //压缩js
uglifyOptions:{
output: {
comments:false //去除注释
}
}
})
],
devServer: {
//配置开发服务器托管的资源文件夹路由,表示该服务器托管dist下面的资源
contentBase: path.resolve(__dirname, "dist"),
port: 8080, //开发服务器的端口
open: true, //自动打开浏览器
compress: true, //启动gzip压缩
}
}
复习
webpack:
1)基于Node开发的 webpack配置中的写法,是Node的写法
2)webpack打包非常多类型的模块 js
3)解决模块之是的依赖关系 减少模块之间的依赖
4)图
webpack安装:
安装Ndoe 构建过程需要借助node环境。
npm i webpack webpack-cli -g 全局安装
只要全局安装了,就可以使用webpack命令进行打包了
npm i webpack webpack-cli -d 局部安装
要打包,需要使用npx webapck 进行打包 推荐
测试是否安装成功:
webpack -v
npx webpack -v
webpack4.0:
支持零配置打包 功能比较弱 默认只能打包js和josn文件
配置webpack:
在项目的根目录下面,创建一个webpack.config.js
entry: 所有模块的入口,webapck打包时,先找入口。
output: 配置打好的资源放到什么地方,配置打包好文件的名字
module: 配置很多的loader 可以让webpack打包一些非JS类型的文件
plugins: 配置webpack的插件 可以让webpack执行更折任务,如打包优化,压缩等
devServer: 配置开发服务器 打包好后,直接放到开发服务器上, 配置跨域...
mode: 打包的模式 development 开发 production 生产
其他配置
处理css兼容
最新的CSS属性,有些浏览器是不能识别的,需要加上所谓的前缀,特别是CSS3中的一些属性
如何知道,一个CSS属性,是否有兼容性问题?
答:可以通过一个网站来查询 叫:can i ues 地址:https://caniuse.com/
配置webpack兼容CSS:
安装:
npm postcss-loader postcss-preset-env -D
postcss-loader 是loader 针对postcss-loader还有很多插件 postcss-loader 处理兼容问题
postcss-preset-env 是postcss-loader的插件 处理不同的浏览器的
{
test: /\.css$/, //匹配到.css结束的文件
use: [ //创建style标签用来放样式,style标签在head标签中
//"style-loader",
//如果要抽离css,就不能使用了,因为这个是把css处理成内部样式
MiniCssExtractPlugin.loader,
"css-loader", //把css文件转成commonjs模块
{// 配置postcss-loader
loader:"postcss-loader",
options:{
// ident:'postcss',
plugins:(loader)=>[
require('postcss-preset-env')()
]
}
}
]
},
还需要配置兼容哪些浏览器 在 package.json 中 配置
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production":[
"> 0.2%",
"not dead"
]
}
ESLint 校验js代码规则
ESLint: 有些公司是强制使用ESLint的 刚开始使用时,非常不舒服。
ESLint 是一个插件化并且可配置的 JavaScript 语法规则和代码风格的检查工具。
作用:校验我们写的JS代码是否符合规则(可以自己定制,也可以使用第三方)
如果不满足规则,就报错,打包也打包不成功。
安装:
npm i eslint eslint-loader eslint-plugin-import eslint-config-airbnb-base -D
eslint-loader是用来校验的 依赖于eslint
针对的模块是JS模块。 只能校验你写的JS代码,不要去校验别人写的JS代码。
步骤:
1)配置校验JS的loader,在 module:{ rules: [
{
test:/\.js$/,
exclude:/node_modules/,
loader:"eslint-loader",
// 配置自动修复
options:{
fix:true
}
}
2)告诉webpack校验规则在哪里 在package.json中进行配置
"eslintConfig": {
"extends": "airbnb-base"
}
3)写JS代码:
function sum(num1,num2) { return num1+num2 }
console.log(sum(10,20)); console.log("hello eslint")
代码是没有任何问题,只是写法,不符合arabnb规范,这个规范是第三方的规范。
规则中不允许有console.log(), 如果你的代码有一行,需要eslint忽略校验,需要使用一个注释:
// eslint-disable-next-line
console.log(111)
处理JS代码的兼容性
我们写的JS代码,在比较低版本的浏览器中并不能识别,需要做兼容性处理。
使用ES6、ES7,ES8新版本的标准的写法,在有的低版本浏览器中并不能识别。
目的:ES6/7/8/9 ----> 编译老的写法 JS的兼容性处理
这个转化的写法,叫polyfill 把新的写法,使用JS模拟出来。
如箭头函数的polyfill 就是把箭头函数转成传统的function写法
配置webpack处理JS代码的兼容性。
高级JS语法:
1)可以转成低级写法 如:箭头函数 转成 function 解决:babel-loader
2)根本没有办法转 polyfill去模拟高级写法 解决:@babel/polyfill 有人把
安装:
npm i babel-loader @babel/core @babel/preset-env -D
babel-loader依赖于 @babel/core @babel/preset-env
配置:
使用babel-loader,但是还需要使用预设:
rules:[
{ //处理js的兼容性
test:/\.js$/,
exclude:/node_modules/, //排除node_modules的文件
loader:"babel-loader",
options:{
presets:[ // 预设
"@babel/preset-env"
]
}
}
]
使用babel-loader 并不能把所有的ES新的写法。转成传统写法。此时就可以使用 polyfill。如promise,是一种新的写法,并不存在老的写法,所以没法转,只能模拟效果。
安装:
npm i @babel/polyfill -D
@babel-polyfill包中模拟非常非常多的JS高级写法
使用 @babel/polyfill 并不是说模拟出来的JS支持所有的浏览器。
使用:入口中引入,index.js 全局引入polyfill
import "@babel/polyfill"
非常大缺点:体积非常大。小项目不推荐全局引入
如果要按需引入,需要借助一个模块,叫core-js,配置这个模块可以达到按需引入的目的。
安装:
npm i core-js -D
{ //处理js的兼容性
test:/\.js$/,
exclude:/node_modules/,
loader:"babel-loader",
options:{
presets:[ // 预设
"@babel/preset-env",
// 配置按需引入
{
useBuiltIns:'usage',
corejs:{ //core-js的版本
version:3
},
// 指定兼容做到哪个版本的浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
}
},
loader的优先执行
对于webpack来说,一切都是模块。
在webpack.config.js中针对JS模块,我们使用eslint-loader,还使用了babel-loader,当然也使用了ployfill。现在面临这样的问题:
- eslint是用来做语法检查,如果有语法错误,就没有必要做JS兼容了。
- bable把ES6/7/8 ----> ES5 ES6/7/8的代码通过ESlint检验 转成ES5时,可能就没有通过校验。
目标:先经过eslint-loader进行处理,处理OK后,再给bable-loader来处理。
{
test:/\.js$/,
exclude:/node_modules/,
loader:"eslint-loader",
enforce:"pre", // 优先执行eslint-loader 通过后再执行其它的loader
options: {
fix:true
}
},
enforce:“pre”, 优先执行eslint-loader 通过后再执行其它的loader
较完整的生产环境的配置代码
// 一个比较完整版本的用于生产环境的webpack配置
let { resolve } = require("path")
let HtmlWebpackPlugin = require("html-webpack-plugin")
let { CleanWebpackPlugin } = require("clean-webpack-plugin")
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
let MiniCssExtractPlugin = require("mini-css-extract-plugin")
// 处理样式的公共代码
let commonCssLoader = [
// MiniCssExtractPlugin.loader,
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath:"../"
}
},
"css-loader",
{
loader:"postcss-loader",
options:{
ident:"postcss",
plugins: () => [
require("postcss-preset-env")()
]
}
}
]
module.exports = {
mode:"production",
entry:"./src/index.js",
output:{
filename:"js/main.js",
path:resolve(__dirname,"dist")
},
module:{
rules:[
// 1,样式相关的 css less scss stylus
{
test:/\.css$/,
use:[...commonCssLoader]
},
{
test:/\.less$/,
use:[...commonCssLoader,"less-loader"]
},
// 2,JS相关的
{
test:/\.js$/,
exclude:/node_modules/,
loader:"eslint-loader",
enforce:"pre",
options:{
fix:true
}
},
{
test:/\.js$/,
exclude: /node_modules/,
loader:"babel-loader",
options: {
presets:[
[
'@babel/preset-env',
// 配置polyfill的按需加载
{
// 配置按需引入
useBuiltIns:'usage',
corejs:{
version:3
},
// 指定兼容做到哪个版本的浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
]
}
},
// 3,图片相关的
{ // 配置是css中的背景图
test:/\.(jpg|png|gif)$/,
loader:"url-loader",
options: {
limit:5 * 1024, // 图片小于5kb 会打包成base64
name:'[hash:12].[ext]',
outputPath:"img"
}
},
{
// 配置html模块中的插入图
test:/\.html$/,
loader:"html-loader",
},
// 4,字体图标相关的
{
exclude: /\.(js|json|css|less|jpg|png|gif|html)$/,
loader: "file-loader",
options: {
name:'[hash:12].[ext]',
outputPath:"source"
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:"./public/index.html",
// 配置生成的html文件
minify:{
collapseWhitespace:true,
removeComments:true
// ....
}
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename:"css/index.css"
}),
new OptimizeCssAssetsPlugin()
]
}
HML模块热替换
现在使用webpack进行打包时,有这样一问题:
项目中有很多的模块,使用webpack-dev-serve进行打包时,由于它是在内存中进行打包的,当我们修改了一个模块的代码,它会实时打包,我们明明改了css模块,但是js模块也会重新打包。这样,如果项目非常大,我们改一个小模块,其它模块都会重新进行打包,这样,不太完美。
解决:使用webpack中提供的热模块替换 只针对开发模式 只能使用内存形式打包
在webpack中,下面的几类的模块支持热模块替换:
1)样式模块:支持HMR,必须使用style-loader 非常多
devServer:{
contentBase:resolve(__dirname,"dist"),
port:8080,
open:true,
compress:true,
hot:true, // 支持热模块替换
}
2)HTML模块 由于HTML模块基本上我们不会动 所以基本上不会对HTML文件进行HMR
如果确实要对HTML文件进行HMR,需要配置多入口:
entry:["./src/index.js","./public/index.html"],
devServer:{
contentBase:resolve(__dirname,"dist"),
port:8080,
open:true,
compress:true,
hot:true, // 支持热模块替换
}
3)JS模块 默认是不支持HMR功能
如果想让它支持HMR功能,需要在入口JS中配置,index.js
JS模块HMR,不能针对入口文件(针对是非入口)
if (module.hot) {
// 使用HMR功能
// 针对vuex.js模块进行HMR
module.hot.accept("./js/vuex.js",()=>{
logVuex(); //函数
})
}
Source-Map
在JS模块中,写的JS代码,可能会出错误。由于你的JS模块被打包了,那么你,很难定位到你的错误。
打包后的JS模块,如果你想调试,那么就可以使用sourcemap。
在webpack.config.js中配置
// 开启source-map调试
devtool:"source-map"
source-map配置选项非常多:
[inline- | hidden- | eval- | nosources- | cheap- | cheap-module ]source-map
source-map: 外部映射
- 生成源代码到构建后代码的映射代码 此映射文件—>帮你精确定位错误
- 精确 哪个文件 此文件的哪个位置
inline-source-map: 内部映射
- 并不会生成所谓的映射文件
- 帮我们定义到哪个文件的哪个位置出错了
hidden-source-map:外部映射
- 不能帮我们定位到哪个文件的哪个位置出错了
- 生成所谓的映射文件
eval-source-map: 内部映射
- 并不会生成所谓的映射文件
- 帮我们定位到哪个文件的哪个位置出错了
nosources-source-map:
- 生成所谓的映射文件
- 能找到哪个文件出错了,但是找不到是此文件的哪个位置出错了
cheap-source-map:外部映射
cheap-module-source-map:外部映射
- 生成所谓的映射文件
- 帮我们定位到哪个文件的哪个位置出错了
速度排名:eval > inline > cheap > …
开发环境:调试友好,构建速度快一点 通常开发时,都是在内存中打包
eval-source-map : 速度快 定位到错误
eval-cheap-source-map 速度快 定位到错误 生map文件
生产环境:隐藏源代码(不要定位错误) 代码体积小(不要有map文件)
hidden-source-map
nosources-source-map
// 开启source-map调试
// devtool:"source-map"
// devtool:"inline-source-map"
// devtool:"hidden-source-map"
// devtool:"eval-source-map"
// devtool:"nosources-source-map"
devtool:"cheap-module-source-map"
补充配置
OneOf
在webpack.config.js中配置很多的loader,这些loader是用来处理不同的模块的,一个模块,默认情况下,都要被所有的loader过滤一下。
默认情况下,所有的模块进入rules中会都走一遍,没有必要,可以使用OneOf,只会匹配到一个loader,提升打包速度。
rules:[
{
test:/\.js$/,
......
}
{
oneOf:[
{test:/\.css$/,......},
{test:/\.js$/,.....}
]
}
]
设置缓存
构建出来的项目,需要放到服务器上。服务上面放的是我们打包好的项目。
要设置缓存,需要后端支持server.js。后端设置如下:
app.use(express.static(path.resolve(__dirname,"../dist"),{maxAge:3600*1000}))
如果后端设置了缓存,即使你重新打包,后端服务器也托管dist目录,效果也不会发生改变。webpack也需要设置
开启缓存:cacheDirectory:true 针对babel来设置的。
{
test:/\.js$/,
exclude: /node_modules/,
loader:"babel-loader",
options: {
presets:[
],
// 开启babel缓存
cacheDirectory:true
}
}
使用缓存步骤:
- 后端设置 {maxAge:3600*1000}
不管你动没动你的代码 都的一直是缓存。因为文件名没有发生改变 - webpack 需要设置 cacheDirectory:true
- 设置打包后的文件名 只有文件名发生改变,使用hash值
output:{ filename:"js/main.[hash:10].js", path:resolve(__dirname,"dist") }, new MiniCssExtractPlugin({ filename:"css/index.[hash:10].css" }),
这样,没有缓存了。 不管代码动没动,每一次打包,都会生成一个新的文件名,文件名变了,重新请求服务器,不会再走缓存了。没有达到目的。
目标:如果代码没有动,走缓存,如果代码动了,不走缓存。
上面的设置就不OK了,也就是说,hash值,需要根据文件内容来计算出来hash,如果内容没有变,hash值是不变,只有内容变了,hash值就变了。这样就能达到目的。
如何根据内容计算出hash值?
答:contenthash
内容变了,hash值才会变,文件名会变,不走缓存
内容不变,hash值不会变,文件名不变,要走缓存
output:{
filename:"js/main.[contenthash:10].js",
path:resolve(__dirname,"dist")
},
new MiniCssExtractPlugin({
filename:"css/index.[contenthash:10].css"
}),
Tree Shaking 树摇
去除掉无用的代码,减少代码体积。 tree shaking 是一种代码优化技术。
如:项目中引入elementui 默认是引入了所有的组件,默认webpack支持树摇,项目中没有使用到的组件,webpack不会进行打包。
默认开启tree shaking的前提:
- 使用的模块化是ES6模块化
- 基于开发环境
- webpack版本大于等于4
Tree Shaking用于去掉无用的代码:
有些第三方模块,不想进行Tree Shaking,需要关闭Tree Shaking,如何关闭:
// package.json文件
{
"sideEffects": false
}
直接关闭Tree Shaking 不可取。
可以这样配置: “sideEffects” 后面跟一个数组,数据里面的选项表示关闭Tree Shaking
"sideEffects": [ //关闭下面文件的树摇
"dist/*",
"es/**/style/*",
"lib/**/style/*"
]
除了可以在package.json中配置之外,还可以在rules中进行配置
参考:https://zhuanlan.zhihu.com/p/41795312
代码分割
多入口,不常用
多入口:如果项目比较大,打包出来的main.js也是非常大,加载就非常多。就可以配置多入口。用的不多,因为在开发中通常是单入口
entry:{
index:"./src/index.js",
router:"./src/js/router.js",
vuex:"./src/js/vuex.js"
},
output:{ //生成对应文件名的文件
filename:"js/[name].[contenthash:10].js",
path:resolve(__dirname,"dist")
},
分割第三方模块
以jquery为例:安装jquery第三方:npm i jquery
默认也是把jq打包到了main.js中,我们想分割出jq。
//webpack.config.js
optimization:{
splitChunks:{
chunks:'all'
}
}
分割自定义模块
需要我们写代码进行分割,在入口JS中写,如下:
import(/* webpackChunkName:'router' */'./js/router').then(res=>{
console.log(res)
})
import(/* webpackChunkName:'vuex' */'./js/vuex').then(res=>{
console.log(res)
})
模块的懒加载
所谓的懒加载就是指使用时候再去加载。
//加注释生成的文件名 router
document.getElementById("box1").onclick = function () {
import(/* webpackChunkName:'router' */"./js/router").then(res=>{
console.log(res.default())
})
} //点击之后再加载需要的数据。
模块的预加载:
所谓的预加载就是提前加载好模块,等待使用。
预加载比懒加载多了一个注释 webpackPrefetch:true
// 预加载 webpackPrefetch:true
document.getElementById("box2").onclick = function () {
import(/* webpackChunkName:'vuex', webpackPrefetch:true */"./js/vuex").then(res=>{
console.log(res.default())
})
} //还没有点击就加载好的需要的的数据。
PWA
Progressive Web Application 渐进式web应用
通过pwa可以让webapp在没有网络的情况下,看能看到一些数据。
第一步:安装:
npm i workbox-webpack-plugin
第二步:配置
let WorkboxWebpackPlugin = require("workbox-webpack-plugin")
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true, // 快速启动serverWorker
skipWaiting: true
})
第三步:在入口文件index.js中注册
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
第三步:安装一个叫serve的模块 这个模块就可以快速的搭建一台服务器
npm i serve -g 如果全局安装了,就可以使用serve命令
第四步:运行 serve -s dist
输入一个地址:http://localhost:5000
配置排除打包
如果项目中有些模块,我们不想进行打包,可以使用排除打包。
//webpack.config.js
externals:{
jquery:"jQuery"
....
}
配置resolve:
//webpack.config.js
resolve:{
// 配置路由的别名 @ src $myJs src/js
// 优点:路径简写了 缺点:没有提示 鸡肋 @xx/a/b/c
alias:{
$myJs:resolve(__dirname,"../../src/js")
},
// 配置模块匹配的后缀名 Home
// 鸡肋
extensions:[".js",".json",".css",".less"],
// 配置找node_module的目录
// modules:[resolve(__dirname,"../../node_modules")]
}
配置代理服务器-跨域
devServer: {
// 1,最基础的配置
contentBase: resolve(__dirname, "dist"),
port: 8080,
open: true,
compress: true,
hot: true, // 支持热模块替换
// 2,常用配置
watchContentBase:true, // 监听contentBase下面所有的文件,就重新的reload
watchOptions:{
// 忽略监听的模块
ignored:/node_modules/
},
// 3,跨域开发环境的跨域
proxy:{ //axios: /api/news 访问当前开发服务器 就会转发到另一个服务器
// /news 获取新闻列表 baseURL:http://www.baidu.com
// http://www.baidu.com/api/news
// http://www.baidu.com/news
"/api":{
target:"http://www.baidu.com", // 后台接口地址
pathRewrite:{
"^/api":""
}
}
}
// 在vue脚本架中 新建一个vue.config.js
// 在vue脚手架中,隐藏了所有的webpack配置,如果你想,你需要在vue.config.js中配置
// 你在vue.config.js中配置完毕后,脚手架会帮你合并到webpack中
},
主要理解
proxy:{
"/api":{ //表示接口地址,固定api
target:"http://www.baidu.com",
pathRewrite:{
"^/api":"" //以api开头的接口地址 api 换为空
}
}
}
到axios请求 /api/news
时,会访问当前开发服务器8080,配置了代理,8080会帮我们转发到http://www.baidu.com
,访问地址就成了http://www.baidu.com/api/news
,但要访问的地址是 /new
,所以需要pathRewrite 将 /api
替换为空,最后就访问了http://www.baidu.com/news
代码
// 一个比较完整版本的用于生产环境的webpack配置
let { resolve } = require("path")
let HtmlWebpackPlugin = require("html-webpack-plugin")
let { CleanWebpackPlugin } = require("clean-webpack-plugin")
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
let MiniCssExtractPlugin = require("mini-css-extract-plugin")
let WorkboxWebpackPlugin = require("workbox-webpack-plugin")
// 处理样式的公共代码
let commonCssLoader = [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath:"../"
}
},
"css-loader",
{
loader:"postcss-loader",
options:{
ident:"postcss",
plugins: () => [
require("postcss-preset-env")()
]
}
}
]
module.exports = {
mode:"development",
entry:"./src/index.js",
output:{
// filename:"js/main.[hash:10].js",
filename:"js/[name].[contenthash:10].js",
path:resolve(__dirname,"dist")
},
module:{
rules:[
/*{
test:/\.js$/,
exclude:/node_modules/,
loader:"eslint-loader",
enforce:"pre",
options:{
fix:true
}
},*/
{
oneOf:[
// 1,样式相关的 css less scss stylus
{
test:/\.css$/,
use:[...commonCssLoader],
sideEffects: false
},
{
test:/\.less$/,
use:[...commonCssLoader,"less-loader"]
},
{
test:/\.js$/,
exclude: /node_modules/,
use:[
/* {
loader:"thread-loader",
options: {
workers:2 // 开启两个进程进行打包
}
},*/
{
loader:"babel-loader",
options: {
presets:[
[
'@babel/preset-env',
// 配置polyfill的按需加载
{
// 配置按需引入
useBuiltIns:'usage',
corejs:{
version:3
},
// 指定兼容做到哪个版本的浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
],
// 开启babel缓存
cacheDirectory:true
}
}
]
},
// 3,图片相关的
{ // 配置是css中的背景图
test:/\.(jpg|png|gif)$/,
loader:"url-loader",
options: {
limit:5 * 1024, // 图片小于5kb 会打包成base64
name:'[hash:12].[ext]',
outputPath:"img"
}
},
{
// 配置html模块中的插入图
test:/\.html$/,
loader:"html-loader",
},
// 4,字体图标相关的
{
exclude: /\.(js|json|css|less|jpg|png|gif|html)$/,
loader: "file-loader",
options: {
name:'[hash:12].[ext]',
outputPath:"source"
}
}
]
},
]
},
plugins:[
new HtmlWebpackPlugin({
template:"./public/index.html",
// 配置生成的html文件
minify:{
collapseWhitespace:true,
removeComments:true
// ....
}
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename:"css/index.[contenthash:10].css"
}),
new OptimizeCssAssetsPlugin(),
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true, // 快速启动serverWorker
skipWaiting: true
})
],
devServer: {
// 1,最基础的配置
contentBase: resolve(__dirname, "dist"),
port: 8080,
open: true,
compress: true,
hot: true, // 支持热模块替换
// 2,常用配置
watchContentBase:true, // 监听contentBase下面所有的文件,就重新的reload
watchOptions:{
// 忽略监听的模块
ignored:/node_modules/
},
// 3,跨域开发环境的跨域
proxy:{ // axios: /api/news 访问是开发访问 就会转发到另一个服务器
// /news 获取新闻列表 baseURL:http://www.baidu.com
// http://www.baidu.com/api/news
// http://www.baidu.com/news
"/api":{
target:"http://www.baidu.com", // 后台接口地址
pathRewrite:{
"^/api":""
}
}
}
// 在vue脚本架中 新建一个vue.config.js
// 在vue脚手架中,隐藏了所有的webpack配置,如果你想,你需要在vue.config.js中配置
// 你在vue.config.js中配置完毕后,脚手架会帮你合并到webpack中
},
// devtool:"source-map",
optimization:{
splitChunks:{
chunks:'all'
}
},
// 配置排除打包
externals:{
jquery:"jQuery"
},
resolve:{
// 配置路由的别名 @ src $myJs src/js
// 优点:路径简写了 缺点:没有提示 鸡肋 @xx/a/b/c
alias:{
$myJs:resolve(__dirname,"../../src/js")
},
// 配置模块匹配的后缀名 Home
// 鸡肋
extensions:[".js",".json",".css",".less"],
// 配置找node_module的目录
// modules:[resolve(__dirname,"../../node_modules")]
}
}