webpack
webpack核心概念
webpack 静态模块打包器,是一种前端资源构建工具,前端所有资源文件(js/json/img/less…)都会作为模块处理,它将根据依赖关系进行静态分析,打包生成对应的静态资源(bundle)。
一、Entry
入口(Entry)指示webpack以哪个文件为入口起点开始打包分析构建内部依赖图。
二、Output
输出(Output)指示webpack打包后的资源bundles输出到哪里去,以及如何命名。
三、Loader
Loader让webpack能够处理那些非JavaScript文件(webpack自身只理解JavaScript)
Loader去翻译这些css,img等
四、Plugins
插件(Plugins)可以用于执行范围更广的任务。插件范围包括,从打包优化和压缩一直到重新定义环境中的变量等。
五、Mode
模式指示webpack使用相应的模式配置
development:会将process.env.NODE_ENV的值设置为development,并启用一些插件。能让代码在本地调试及运行。
production:会将process.env.NODE_ENV的值设置为production,并启用一些插件。能让代码优化上线的运行的环境。
开发环境:webpack ./src/index.js -o 输出到 ./build/built.js(可自己配置) --mode=development
webpack会以 ./src/index.js为入口文件开始打包,打包后输出到 ./build/built.js
整体打包环境,是开发环境
生产环境:webpack ./src/index.js -o 输出到 ./build/built.js(可自己配置) --mode=production
webpack:会以 ./src/index.js为入口文件开始打包,打包后输出到 ./build/built.js
整体打包环境,是生产环境
结论:
1,webpack能处理js/json资源,不能处理css/img 等其他资源
2,生产环境比开发环境对一个压缩js代码
3。生产环境和开发环境将Es6模块化编译成浏览器能够识别的模块化
可以吧打包后的./build/built.js 引入到index.html 进行验证
webpack 详细配置
/*
webpack.config.js 是webpack的配置文件
作用就是指示webpack干哪些活(运行webpack时,会运行其中的配置)
HMR的作用:hot module replacement 模块热替换. 作用:一个模块发生变化,只会重新打包发生过变化的模块。
js,没有HMR ,需要修改js代码,添加支持HMR的代码
在index.js中
if(module.hot) {
// 一旦module.hot 为true 说明开启了HMR功能 --> 让HMR代码生效
// 如果有其他模块也需要开启HMR ,就再写一个module.hot.accept()
module.hot.accept('xxx.js',function(){
// 方法会监听print.js文件的变化,一旦发生变化,其他模块会重新打包构建
// 会执行后面的回调函数
})
}
HTML文件没有HMR,并且会导致重新编写html不会重新更新
解决: 修改entry 入口,将html 引入
所有的构建工具都是基于node平台运行的,模块化默认采用common.js
loader 先下载 再使用(及配置loader)
plugins: 先下载 再引入 再使用
运行指令:webpack 会将打包的文件输出
npx wenpack-dev-server // 不会输出打包文件
source-map: 一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射可以追踪到源代码错误)
只需要加一句: devtool: 'source-map' 能够准确定位到源代码错误的位置
devtool: inline-source-map 内联(只生成一个内联的source-map) 内联会让体积变大,一般不使用
devtool: hidden-source-map 外部生成 定位到构建后代码的错误位置,是为了隐藏源代码,一般在生产环境下使用
devtool: eval-source-map 内联(每一个js文件生成一个source-map)
devtool: nosources-source-map 外部生成 找不到源代码错误,找不到错误位置,是为了隐藏源代码,一般在生产环境下使用
devtool: cheap-source-map 外部生成 只精确到行的错误位置,不推荐使用
devtool: cheap-module-source-map 外部生成 能够准确定位到源代码错误的位置
使用时考虑环境: 开发环境:速度快,调试更友好
速度: eval>inline>cheap..
eval-cheap-source-map 组合最快
调试更友好 : cheap-module-source-map
--> 综上选择 eval-source-map /
生成环境:源代码要不要隐藏?调试要不要友好
一般使用--> source-map /cheap-moudle-source-map
缓存:
babel 缓存 -- 直接配置即可 --> 第二次打包更快
文件资源缓存-- 给文件名添加hash值,每次webpack构建时会生成一个唯一的hash值,只要文件没变hash值就不会变
为什么要这样配置:因为后端如果做了强制缓存,那么代码出现问题,在缓存期内无法修改问题。
问题:当js和css同时使用一个hash值没如果重新打包会导致会有缓存失效,而我只改动了一个文件
chunkhash: 根据chunk生成hash,如果打包来源于同一个chunk(在一个文件中引入),那么hash值就一样
问题:js和css hash还是一样,因为css是在js中引入的(相互引用就是同一个hash值),所以同属于一个chunk
contenthash: 会根据文件的内容生成hash值,不同文件的hash值一定不一样 --> 代码上线运行缓存更好使用
tree shaking: 去除无用代码
前提: 1. 必须使用ES6 模块化
2.开启production环境
作用:减少代码体积
在package.json中配置
"sideEffects":false 所有代码都没有副作用(都可以进行tree shaking)
问题:可能会吧css @babel/polyfill 等文件干掉
所以:"sideEffects":["*.css","*.less"] 这样写去排除一些文件
PWA:渐进式网络开发应用程序(离线可访问)
workbox ---> workbox-webpack-plugin
多进程打包:thread-loader 那个东西启动多进程,就把它放进去,一般给babel-loader用
dll 技术:对某些(第三方库jQuery,react,vue ... )进行单独打包
新建dll 文件夹
新建webpack.dll.js
当运行 webpack 时,默认查找webpack.config.js文件
需求运行webpack --config webpack.dll 运行webpack.dll这个配置文件
const {resolve} = require('path')
const webpack = require('webpack')
module.exports = {
entry:{
// 最终打包生成的[name] -->jquery,对jQuery进行单独打包,需要单独打包什么库,就在这里配置
jquery: ['jquery]
},
output:{
filename:'[name].js',
path:resolve(__dirname,'dll'),
library:'[name]_[hash:10]', // 打包的库里面向外暴露的内容叫什么名字
},
plugins:[
// 打包生成manifest.json 一个映射关系
new webpack.DllPlugin({
name:[name]_[hash:10], // 映射库的暴露内容名称
path: resolve(__dirname,'dll/manifest.json') // 输出文件来路径
})
],
mode:'production'
}
// 这里做完过后主要配置也要变动
*/
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')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
//webpack 配置
// 入口起点
entry: ['./src/index.js', './src/index.html'],
// 输出路径
output: {
// filename: '[name].[contenthash:10].js',文件名,来输出,多入口时使用
filename: 'built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// 详细loader配置,从下到上依次执行
{
/*
语法检查 eslint-loader 依赖于 eslint
设置检查规则: package.json中eslintConfig中设置
airbnb -->eslint-config-airbnb-base eslint-plugin-import 需要下载
// airbnb 检查风格 --> 一个库
"eslintConfig":{
"extends": "airbnb-base"
}
// eslint-disable-next-line 下一行代码不进行eslint检查
*/
test: /\.js$/, // 只针对js文件检查,只检查自己写的源代码
exclude: /node_modules/, // 必须排除第三方依赖,不然会报错
enforce: 'pre', // 优先执行
loader: 'eslint-loader',
options: {
// 自动修复eslint错误,就不需要手动去修复了
fix: true
}
},
{
/*oneOf 以下loader 只会匹配一个,主要提高构建速度,不会有几个loader就过几个loader,
所以不能有两项loader处理同一项文件,所以部分需要提取出去,
*/
oneOf: [
{
test: /\.css$/, // 匹配.css结尾的文件
use: [ // use 表示使用什么loader
//'style-loader', // 创建style标签,将js中的样式资源进行插入,添加到head中生效
MiniCssExtractPlugin.loader, // 取代style-loader,用是将多个css文件合并成一个css文件,及将css提成一个单独的文件
'css-loader', // 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
// css兼容性处理postcss--》postcss-loader protcss-preset-env
/*
第二种找到package.json中添加browserslist配置
"browserslist":{
// 开发环境下兼容主流浏览器
"development":[
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 默认看生产环境
"productin":[
">0.2%", //兼容市场份额大于0.2的浏览器
"not dead", //已经死去的浏览器不兼容
"not op_mini all" //不要op_mini浏览器
]
}
*/
// 'postcss-loader', //使用loader默认配置这样写就行了
{
// 自己配置loader
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
require('protcss-preset-env')()
]
}
}
]
},
{
test: /\.less$/,
use: [
'style-loader', // 创建style标签,将js中的样式资源进行插入,添加到head中生效
'css-loader', // 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'less-loader' // 将less文件编译成css文件
]
},
{
// 图片资源处理,处理不了html中的img标签中的图片
test: /\.(jpg|png|gif)$/,
// 使用一个loader,需要下载url-loader 和 file-loader --》url-loader依赖于file-loader
loader: 'url-loader',
options: {
// 图片小于8kb时,会被处理成base64 (这里写着8,项目根据实际来设置这个值)
// 优点:减少请求数量(减轻服务器压力),缺点:图片体积会变大
limit: 8 * 1024,
// 问题:url-loader模式使用es6模块化解析,而html-loader引入图片commonjs
// 解析时会出问题:[object Module]
// 解决关闭url-loader的es6模块化,使用commonjs解析
esModule: false,
// 给图片重命名[hash:10]取图片的hash的前10位
// [ext] 取文件原来的扩展名
name: '[hash:10].[ext]',
outputPath: 'img' // 指定图片输出路径,其他资源也是一样的方法
}
},
{
// 处理html中img中的图片,负责引入img,从而能被url-loader进行处理
test: /\.html$/,
loader: 'html-loader'
},
{
// 打包其他资源除了html,css,js/img等其他资源
//排除css/js/html资源,想要排除的就在这里写清楚
exclude: /\.(css|js|html|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
},
{
/*
js兼容性处理 babel-loader @babel/prest-env @babel/core 下载第三方
1.基本的js兼容性处理 --> @babel/prest-env
问题:只能转换基本语法,不能处理promise等
2.全部js兼容性处理 --> @babel/polyfill(下载) -->
使用 通过 import '@babel/polyfill' 在源代码js文件中引入
问题:一次性将所有兼容性代码引入,体积变大了
3.按需加载兼容性 --> core-js(下载) 使用第三种方案,就需要注释第二种方案
*/
test: /\.js$/,
exclude: /node_modules/, //排除第三方node_modules 里的所有文件
use: [
/*
开启多进程打包
进程启动大概600ms,进程通信也需要时间
只有工作消耗时间比较长,才需要开启多进程打包
一般js比较消耗时间
也可以通过具体配置:指定开启多少个进程
workers: 2 ...
*/
'thread-loader',
{
loader: 'babel-loader',
options: {
// 预设:指示babel做怎么样的兼容性处理
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
// 指定core-js版本
corejs: {
version: 3
},
// 指定兼容性做到那个浏览器版本
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
],
// 开启bebel缓存,第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
}
]
}
]
},
plugins: [
//详细plugins配置
// html-webpack-plugin(打包html资源) 默认会创建一个空的HTML文件,自动引入打包输出的所有资源(JS/CSS)
// 需求:需要有结构的HTML,所以需要加配置
new HtmlWebpackPlugin({
// 功能:复制'./src/index.html' 文件,并自动引入打包输出所以的资源(JS/CSS)
template: './src/index.html',
// 压缩html
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
// 处理css
new MiniCssExtractPlugin({
filename: 'css/main.[contenthash:10].css' // 多个css文件合并成一个主css文件,并对输出的文件进行重命名
}),
// 压缩css文件
new OptimizeCssAssetsWebpackPlugin(),
new WorkboxWebpackPlugin.GenerateSW({
/*
帮助serviceworker快速启动
删除旧的serviceworker
插件就会serviceworker的配置文件,通过配置文件serviceworker,在入口文件中注册serviceworker
还要处理兼容性问题
在入口文件index.js 中添加
if('serviceWorker' in navigator) {
window.addEventListener('load',()=>{
navigator.serviceWorker
.register('./service-worker.js')
.then(()=>{
console.log('sw注册成功了')
})
.catch(()=>{
console.log('sw注册失败了')
})
})
}
注意这里如果这里配置了eslint 可能会不识别navigator window 等浏览器全局变量,所以要在package中添加配置
// "browser": true 表示支持浏览器全局变量
"eslintConfig":{
"extends": "airbnb-base",
"env":{
"browser": true
}
}
再注意: sw代码必须运行在服务器上
-- npm i serve -g
serve -s build 启动一个服务器,将build目录下的资源作为静态资源暴露出去
*/
clientsClaim: true,
skipWaiting: true
}),
// 告诉webpack哪些第三方库不参与打包,但是不参与打包,就会没有这些引用包,所以就需要另一个配置
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将不参与打包的文件输出出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
//模式
mode: 'development',
// 生产环境下会自动压缩js代码,不需要做其他操作
// mode: 'production'
// 开发服务器devServer :用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)
// 只会在内存中编译打包,不会任何输出
// 启动DevServer指令: npx webpack-dev-server,也可以在package.json 中配置
// 需要下载webpack-dev-server
devServer: {
contentBse: resolve(__dirname, 'build'),
// 启动gzip压缩
compress: true,
// 端口
port: 8080,
//自动打开浏览器
open: true,
// 开启HMR功能,开启模块热更新
hot: true,
// 不显示服务器日志信心
clientLogLevel: 'none',
// 出错了,不要全屏提示
overlay: false,
// 服务器代理 -->开发环境下解决跨域问题
// 一旦devServer(8080)服务器接收到/api/xxx的请求,就会把这个请求转发到服务器(3000 )
proxy: {
'/api': {
target: 'http://localhost:3000',
// 发送请求时路径重写;将/api/xxx --> /xxx (及去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
},
devtool: 'source-map',
// 代码按需加载,可以将node_modules 中代码单独打包成一个chunk最终输出,自己的东西单独放
// 自动分析多入口chunk中,有没有公共文件,如果有会单独打包成一个chuank
optimization: {
splitChunks: {
chunks: 'all'
},
minimizer: [
//配置生产环境的压缩方案:js和css
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启动source-map
sourceMap: true
})
]
}
}