webpack
1.webpack的理解
1.为什么要用到webpack
| 传统方式开发项目的不足: |
|---|
| 1)项目的文件非常多,文件起一个名字叫模块。 |
| 2)有些模块需要手动的编译 less css |
| 3)模块特别多,放到服务器上,肯定会有非常多的二次请求 |
| 4)模块和模块之是有依赖关系 |
2.打包工具
| grunt |
|---|
| glup |
| Rollup 是一个 JavaScript 模块打包器 |
| webpack 大 笨重 可以打包很多不同类型的模块 |
| vite |
3.webpack的优势
| 1)基于Node开发的 webpack配置中的写法,是Node的写法 |
|---|
| 2)webpack打包非常多类型的模块 js |
| 3)解决模块之是的依赖关系 减少模块之间的依赖 |
2.webpack打包
1.安装webpack
本地安装:npm i webpack webpack-cli@3.3.12 //处理css兼容的loader与@4版本不兼容全局安装:npm i webpack webpack-cli -g
注:npx
你要使用webpack命令,你必须全局安装webpack如果你不想全局安装,那么就可以使用npx
npx 默认会临时全局安装webpack
2.打包
npx webpack ./src/index.js -o ./dist/main.js如果全局安装了,就不需要加npx 如果没有全局安装,需要加上npx。
默认情况下,webpack的功能也是比较弱的,只能处理低级的JS语法和json文件。
其它模块,webpack默认是不能处理的。
3.打包模式
开发模式打包 和 生产模式打包: 默认是生产模式打包
开发模式打包: npx webpack ./src/index.js -o ./dist/main.js --mode=development
生产模式打包: npx webpack ./src/index.js -o ./dist/main.js --mode=production
注: 生产模式打包比开发模式打包多了一个代码压缩。
3.配置webpack
上面的打包方式是零配置打包,零配置打包还是比较弱,一般情况下我们自己配置webpack。
创建webpack-.config.js文件,用于写配置代码
1.配置入口和出口
const path = require("path");module.exports = {mode:"development", //开发者模式// 入口entry:"./src/index.js", //用于打包的入口文件// 出口output:{filename:"main.js", //出口文件的名字path:path.resolve(__dirname,"dist") //出口目录}}
2.配置css的load
css-loader:把css文件转成commonjs模块,加载到JS模块<br />
style-loader: 创建一个style标签,把样式资源添加页面的头部中
npm i style-loader css-loader -D
js
// 配置loader
module:{
// 规则
rules:[
{
test:/\.css$/, // 匹配到点css结束的文件
use:[
"style-loader",
"css-loader"
]
}
]
}
3.配置less的load
这个Loader叫:less-loader 这个less-loader还需要依赖less模块
npm i less less-loader -D
less需要安装,但不需要配置
js
{
test:/\.less$/, // 匹配到.less结束的文件
use:[
"style-loader", // 会创建一个style标签,把样式放到style标签中,style标签会放到head标签中
"css-loader", // 把css文件转成commonjs模块
"less-loader", // 把less代码转成css代码
]
}
4.自动创建html
打包完后的main.js自动塞到index.html中。
webpack@5与html-webpack-plugin模块存在兼容问题(若全局安装了,也需降级)
npm i html-webpack-plugin -D
npm i clean-webpack-plugin -D
js
const HtmlWebpackPlugin = require("html-webpack-plugin")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
// 配置plugin
plugins:[
new HtmlWebpackPlugin({ // 配置自动根据模板创建新的HTML文件,并把打包好的main.js放到的新的Html文件中
template:"./public/index.html", // 指定模板
}),
new CleanWebpackPlugin() // 在打包之前,可以清理dist目录下面没有用的文件
]
为了区分每次打包给出口设置标记
filename:"[name].[chunkhash].js",
5.打包图片
需要两个loader: file-loader url-loader
url-loader:比file-loader更加强大 url-loader依赖于file-loader 配置时,只会配置url-loader
file-loader能处理的url-loader也能处理
npm i file-loader url-loader -D
若想处理html中的图片,需要用到html-load 打包后为base64格式
npm i html-loader -D
js
// 处理css文件中的背景图片
{
test:/\.(jpg|png|gif)$/,
use:[
{
loader:"url-loader", // 处理图片,处理字体图标...
options:{
outputPath: 'images', // 将文件打包到哪里
// 如果说图片小于10kb 处理成base64 如果大于10kb 就是一个单独的文件
limit:10*1024, // 10*1024 10kb
name:'[hash:24].[ext]' // 给图上重命名
}
}
]
},
// 处理html文件中的img标签
{
test:/\.html$/,
// html-loader 负责处理img 标签引入的图片
loader:"html-loader"
}
6.打包字体图标
loader叫:file-loader
npm i file-loader -D //之前是安装过的
js
{
// 处理字体图标
exclude:/\.(css|js|html|json|less|png|jpg|gif)/,
loader: "file-loader",
options: {
name:'[hash:12].[ext]',
// 设置输出的文件夹
outputPath:"source"
}
}
7.配置开发服务器
需要安装一个模块:webpack-dev-server
npm i webpack-dev-server -D
在pages中配置
"scripts": {
"serve": "npx webpack-dev-server", // 在内存中打包的命令
"build": "npx webpack", // 在硬盘上打包的命令
"test": "echo \"Error: no test specified\" && exit 1"
},
js
devServer:{
contentBase:path.resolve(__dirname,"dist"), // 配置开发服务器托管的资源的路由
port:8080, // 开发服务器的端口
open:true, // 自动打开浏览器
compress:true // 启动gzip压缩
}
8.抽离css
需要把打包后不同文件放到的不同的文件夹中.mini-css-extract-plugin
npm i mini-css-extract-plugin -D
js
let MiniCssExtractPlugin = require("mini-css-extract-plugin")
//修改css配置
{
test:/\.css$/,
use:[
// "style-loader", // 你要抽离css 不能使用style-loader style-loader是把样式处理成内部样式
MiniCssExtractPlugin.loader,
"css-loader"
]
},
//plugins配置
new MiniCssExtractPlugin({
filename: "css/index.css"
})
9.压缩html,css及js代码
压缩HTML:
npm i html-webpack-plugin //之前安装过的
js
new HtmlWebpackPlugin({
template:"./public/index.html",
filename:"index.html",
// 压缩HTML
minify:{
removeComments:true, //移除HTML中的注释
collapseWhitespace:true //删除空白符与换行符
}
}),
压缩css:奇怪:总报错,定义模块时用let就好啦???
安装:npm install --save-dev optimize-css-assets-webpack-plugin
js
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
new OptimizeCssAssetsPlugin(),
压缩js:
1)如果模式是生产模式 mode是production,默认就会压缩JS代码。
2)使用插件:uglifyjs-webpack-plugin (多打包几次吧!!!)
npm i uglifyjs-webpack-plugin -D
js
new uglifyjs({
uglifyOptions:{
output: {
comments:false
}
}
}),
10.处理css兼容性问题
查看css兼容性网站:https://caniuse.com/
postcss-loader 是loader 针对postcss-loader还有很多插件 postcss-loader 处理兼容问题
postcss-preset-env 是postcss-loader的插件 处理不同的浏览器的
npm i postcss-loader postcss-preset-env -D
js
{
test:/\.css$/,
use:[
// "style-loader", // 你要抽离css 不能使用style-loader style-loader是把样式处理成内部样式
MiniCssExtractPlugin.loader,
"css-loader",
// 配置postcss-loader
{
loader:"postcss-loader",
options:{
// ident:'postcss',
plugins:(loader)=>[
require('postcss-preset-env')()
]
}
}
]
},
webpack-cli@4版本不兼容,这里使用的@3.3.12版本的
还需要在package.json中配置
"browserslist": {
"development": [
"last 10 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
"> 0.2%",
"not dead"
]
}
11.配置ESLint校验js代码
ESLint 是一个插件化并且可配置的 JavaScript 语法规则和代码风格的检查工具。
作用:校验我们写的JS代码是否符合规则(可以自己定制,也可以使用第三方)
如果不满足规则,就报错,打包也打包不成功。
1)安装
npm i eslint eslint-loader eslint-plugin-import eslint-config-airbnb-base -D
2)配置校验js的loader
{
test:/\.js$/,
exclude:/node_modules/,
loader:"eslint-loader",
// 配置自动修复
options:{
fix:true
}
}
3)page.json中配置
"eslintConfig": {
"extends": "airbnb-base"
}
12.处理js的兼容性
我们写的JS代码,在比较低版本的浏览器中并不能识别,需要做兼容性处理。
目的:ES6/7/8/9 ——> 编译老的写法 JS的兼容性处理
npm i babel-loader @babel/core @babel/preset-env -D
babel-loader依赖于 @babel/core @babel/preset-env
按需引入polyfill
npm i @babel/polyfill -D
使用:入口中引入
import "@babel/polyfill" //非常大缺点:体积非常大 如果说项目非常大,那么你全局引入也OK
如果项目不是那么,全局引入所有的ployfill,就不那么合理了。此时我们就需要按需引入。
如果你要按需引入,需要借助一个模块,叫core-js
安装:npm i core-js 配置这个模块可以达到按需引入的目的。
js
{
test:/\.js$/,
exclude:/node_modules/,
loader:"babel-loader",
options:{
presets:[
[
"@babel/preset-env",
{
// 配置按需引入
useBuiltIns:'usage',
corejs:{
version:3
},
// 指定兼容做到哪个版本的浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
]
}
}
13.配置loader的优先执行
若添加loader比较多,如js用到的loader,该优先执行哪一个,这是个问题
{
test:/\.js$/,
exclude:/node_modules/,
loader:"eslint-loader",
enforce:"pre", // 优先执行eslint-loader 通过后再执行其它的loader
// 配置自动修复
options:{
fix:true
}
},
14.HML热模块替换
js配置
devServer:{
contentBase:resolve(__dirname,"dist"),
port:8080,
open:true,
compress:true,
hot:true, // 支持热模块替换
}
1)样式模块:支持HMR,必须使用style-loader
HTML模块 由于HTML模块基本上我们不会动 所以基本上不会对HTML文件进行HMR
如果确实要对HTML文件进行HMR,需要配置多入口:
配置:
entry:["./src/index.js","./public/index.html"],
// 出口
output:{
filename:"[name].[chunkhash].js",
path:path.resolve(__dirname,"dist")
},
3)JS模块 默认是不支持HMR功能
如果想让它支持HMR功能,需要在**入口JS中配置**, JS模块HMR,不能针对入口文件(针对是非入口)
if (module.hot) {
// 使用HMR功能
// 针对vuex.js模块进行HMR
module.hot.accept('./js/vuex.js', () => {
logVuex();
});
}
15.SourceMap调试
在JS模块中,写的JS代码,可能会出错误。由于你的JS模块被打包了,那么你,很难定位到你的错误。
配置js (注:生产模式下打包,和uglifyjs-webpack-plugin有冲突 )
//mode:"production",
devtool:"cheap-module-source-map"
source-map配置选项非常多:
[inline- | hidden- | eval- | nosources- | cheap- | cheap-module ]source-map
可选项目也可以组合搭配。
速度排名:eval > inline > cheap > ....
source-map: 外部映射
1)生成源代码到构建后代码的映射代码 此映射文件--->帮你精确定位错误
2)精确 哪个文件 此文件的哪个位置
inline-source-map: 内部映射
1)并不会生成所谓的映射文件
2)帮我们定义到哪个文件的哪个位置出错了
hidden-source-map:外部映射
1)不能帮我们定位到哪个文件的哪个位置出错了
2)生成所谓的映射文件
eval-source-map: 内部映射
1)并不会生成所谓的映射文件
2)帮我们定位到哪个文件的哪个位置出错了
nosources-source-map:
1)生成所谓的映射文件
2)能找到哪个文件出错了,但是找不到是此文件的哪个位置出错了
cheap-source-map:外部映射
cheap-module-source-map:外部映射
1)生成所谓的映射文件
2)帮我们定位到哪个文件的哪个位置出错了
开发环境:调试友好,构建速度快一点 通常开发时,都是在内存中打包
eval-source-map : 速度快 定位到错误
eval-cheap-source-map 速度快 定位到错误 生map文件
生产环境:隐藏源代码(不要定位错误) 代码体积小(不要有map文件)
hidden-source-map
nosources-source-map
16.oneof匹配loader
默认情况下,所有的模块进入rules中会都走一遍,没有必要,
可以使用OneOf,只会匹配到一个loader,提升打包速度。
{
oneof:[
// 1,样式相关的 css less scss stylus
{
test:/\.css$/,
},
{
test:/\.less$/,
},
{
test:/\.js$/,
},
{
test:/\.(jpg|png|gif)$/,
},
{
test:/\.html$/,
},
{
exclude: /\.(js|json|css|less|jpg|png|gif|html)$/,
}
]
},
17.设置缓存
1.使用缓存(不足)
1)后端设置 {maxAge:3600*1000}
2)webpack 需要设置 cacheDirectory:true
3)设置打包后的文件名 只有文件名发生改变
output:{
filename:"js/main.[hash:10].js",
path:resolve(__dirname,"dist")
},
没有缓存了。 不管代码动没动,每一次打包,都会生成一个新的文件名,文件名变了,重新请求服务器,不会再走缓存
目标:如果代码没有动,走缓存,如果代码动了,不走缓存。
2.重新配置代码获取缓存(修改)
使用contenthash计算代码是否发生改变
output:{
filename:"js/main.[contenthash:10].js",
path:resolve(__dirname,"dist")
},
new MiniCssExtractPlugin({
filename:"css/index.[contenthash:10].css"
}),
18.树摇
参考:https://zhuanlan.zhihu.com/p/41795312
Tree Shaking:
树摇 作用:去除掉无用的代码,减少代码体积。
如:项目中引入elementui 默认是引入了所有的组件,默认webpack支持树摇,项目中没有使用到的组件,webpack不会进行打包。
tree shaking 是一种代码优化技术。
默认开启tree shaking的前提:
1)使用的模块化是ES6模块化
2)基于开发环境
3)webpack版本大于等于4
Tree Shaking用于去掉无用的代码:
有些第三方模块,不想进行Tree Shaking,需要关闭Tree Shaking,如何关闭:
// package.json
"sideEffects": false
//或
"sideEffects": [
"dist/*",
"*.css",
"*.less",
"xx.js"
]
19.代码分割
配置JS多入口:
如果项目比较大,打包出来的main.js也是非常大,加载就非常多。 引时就可以配置多入口。
1.单入口改为多入口
entry:{
index:"./src/index.js",
router:"./src/js/router.js",
vuex:"./src/js/vuex.js"
},
2.分割模块
//webpack配置
optimization:{
splitChunks:{
chunks:'all'
}
}
20.懒加载和预加载
入口配置
懒加载js,用到时加载
// 懒加载
document.getElementById("box1").onclick = function () {
import(/* webpackChunkName:'router' */"./js/router").then(res=>{
console.log(res.default())
})
}
预加载js,提前加载好
// 预加载 webpackPrefetch:true
document.getElementById("box2").onclick = function () {
import(/* webpackChunkName:'vuex', webpackPrefetch:true */"./js/vuex").then(res=>{
console.log(res.default())
})
}
21.PWA
Progressive Web Application 渐进式web应用
通过pwa可以让webapp在没有网络的情况下,看能看到一些数据。
第一步:
安装:npm i workbox-webpack-plugin
第二步:配置
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true, // 快速启动serverWorker
skipWaiting: true
})
第三步:在入口文件中注册
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
21.多进程打包
npm i serve -g 如果全局安装了,就可以使用serve命令
js
{
loader:"thread-loader",
options: {
workers:3 // 开启两个进行进行打包
}
},
配置排除打包
externals:{
jquery:"jQuery"
....
}
22.配置代理服务器
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")]
}
