- 命令窗口运行初始化eslint
切换到./node_modules/.bin/文件夹下运行 eslint --init
用airbnb
fix: true可以自动修复问题
// eslint-disable-next-line 不转义
- 解决eslint不认识浏览器的全局变量如$,修改package.json
"eslintConfig": {
"extends": "airbnb-base",
"env": {
es6: true, // 启用 ES6 语法支持以及新的 ES6 全局变量或类型
node: true, // Node.js 全局变量和 Node.js 作用域
browser: true, // 浏览器全局变量
jquery: true // jQuery 全局变量
}
}
webpack4.0以上的版本都要安装webpack-cli,否则报错
- js兼容
babel @bable/core
- 基本js兼容性处理 --> @babel/preset-env
- 问题:只能转换基本语法,如promise高级语法不能兼容
- 全部js兼容性处理 --> @babel/polyfill
- 问题:只需要解决部门兼容性问题,但是将所有文件引入体积过大
- 需要做兼容性处理的就做,按需要加载-->core-js
- HMR
- css文件:style-loader内部实现了
- js文件:默认不能使用HMR功能-->修改js代码,添加支持HRM功能的代码
if(module.hot) {
module.hot.accpet('./print.js', function() {
console.log('Accepting the updated printMe module!');
printMe();
})
}
只有print.js会被重新加载,其他文件不会被重新加载
注意:HMR功能对js的处理,只能处理非入口js文件
- html文件:默认不能使用HMR功能,导致html文件不能热更新(不用HMR功能)
解决:修改entry入口,将html文件引入
4.source-map
提供了源代码到构造后代码映射关系(如果构建后代码出错了,通过映射可以追踪源代码错误)
- source-map 外部
- 错就代码的准备信息和源代码的准确位置
- inline-source-map 内联
只生成一个内联的source-map - hidden-source-map 外部
- 错误代码准确信息,没有错误位置,不能追踪代码错误,只能提示到构建后代码的错误位置
- eval-source-map 内联
- 每个文件都生成对就的source-map,都在eval
- nosource-source-map 外部
- 错误代码准确信息,没有源代码
- cheap-source-map 外部
- 错误代码准确信息和源代码的错误位置
- 只能精确到行,其他的可以精确到列
- cheap-module-source-map 外部
- 错误代码准确信息和源代码错误位置
- module会将loader的source map加入
- 内联和外部区别:
1.外部生成了文件,内联没有 2.内联构建速度快
内联会让代码体积很大,所以生产环境一般不会用内联 - 速度快:eval>inline>cheap>
eval-cheap-source-map > eval-source-map - 调试更友好
source-map
cheap-module-source-map
cheap-source-map - 开发环境:调试友好+速度快 -->eval-source-map(vue,react脚手架用) / eval-cheap-module-source-map
- 生产环境:
nosource-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
devtool: 'source-map'
- 运行npm run dev遇到的问题:Error: Cannot find module 'webpack-cli/bin/config-yargs'
- 原因:使用webpack到5.*, webpack-cli到4.*,需要使用webpack-cli 3.*
- 卸载当前的 webpack-cli npm uninstall webpack-cli
- 安装 webpack-cli 3.* 版本 npm install webpack-cli@3 -D
- oneof 只处理一个loader
- 以免每个loader都执行一遍,提高打包构造速度
- 如果需要执行多个,可以外部放一个,oneof中放只执行一次
- 缓存
- babel缓存
- cacheDirectory: true
- 让第二次打包构建速度更快
- 文件资源缓存
- hash: 每次webpack构建时会生成一个唯一的hash值
- 问题:因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效(可我却只改动一个文件)
- chunkhash: 根据chunk生成hash值,如果打包来源于同一个chunk,那么hash值就一样
- 问题:js和css的hash值还是一样,因为css是在js中被引入的,所以同属于一个chunk
- chunk的概念:一个入口文件,
- contenthash:根据文件的内容生成hash值,不同文件的hash值一定不一样
- hash: 每次webpack构建时会生成一个唯一的hash值
- tree shaking 去除无用代码
- 前提:1.必须使用ES6模块化 2.开启production环境
- 作用:减少代码体积
- 在package.json中配置:sideEffects: false 所有代码都是没有副作用(都可以进行tree shaking)
- 问题:可能会把css/@babel/polyfill(副作用)文件删掉
- "sideEffects": ["*.css", "*.less"]表示css文件不用进行tree shaking
- 代码分隔
- 多入口:一个入口,输出一个bundle
entry: {
index: './src/js/index.js',
test: './src/js/test.js'
}
- splitChunks 将node_modules中代码单独打包一个chunk最终输入出
- 自动分析多入口chunk中,有没有公共的文件,如果有打包成单独一个chunk,不会重复打包多次
optimization: {
splitChunks: {
chunks: 'all'
}
}
- 通过js代码,让某个文件单独打包成一个chunk
- import动态导入语法:能将某个文件单独打包
import (/* webpackChunkName: 'test'*/'./test').then(() => {
console.log('success');
}).catch(() => {
console.log('error');
})
- 懒加载
- 懒加载:进入页面不会加载print,当点击按钮才会加载print文件
- 第一次加载后,第二次会直接执行,不会重复加载
- 使用时才加载
- webpackPrefetch 预加载
- 使用之前加载js文件
- 等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
- 兼容性不好
- 正常加载可以认为是并行加载(同时加载多个文件),没有明确的顺序,在前的先加载,同时文件过多过大,影响顺序
button.onclick = () => import(/*webpackChunkName: 'print', webpackPrefetch: true*/'./print').then((module) => {
const print = module.default;
print();
});
- PWA 渐进式网络开发应用程序(离线可访问)
workbox --> workbox-webpack-plugin
new workboxWebpackPugin.GenerateSw({
/*
1.帮助serviceworker快速启动
2.删除旧的serviceworker
生成一个serviceworker配置文件~
*/
clientClaim: true,
skipWaiting: true
})
入口文件index.js
/*
1.eslint不认识window、navigator全局变量
解决:需要修改package.json中eslintConfig配置
"env": {
"browser": true // 支持浏览器端全局变量
}
2.serviceworker代码必须运行在服务器上
--> nodejs
--> npm i serve -g
serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
*/
// 注册serviceworker
// 处理兼容问题
if('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./service-worker.js')
.then(() => {
console.log('serviceworker注册成功');
})
.catch(() => {
console.log('注册失败');
})
})
}
- 多进程打包
{
test: /\.js$/,
exclude: /node_modules/,
use: [
/*
开启多进程打包
进程启动大概为600ms,进程通信也有开销
只有工作消耗时间比较长,才需要多进程打包
*/
{
loader: 'thread-loader',
options: {
worker: 2 // 进程2个
}
},
{
loader: 'babel-loader',
options: [
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
]
}
]
}
- externals
externals: {
// 拒绝打包jquery
jquery: 'jQuery'
}
- dll 优化重复打包第三方库,加快打包速度
使用dll技术,对某些库(第三方库:jquery、react、vue^)进行单独打包
当你运行webpack时,默认查找webpack.config.js配置文件
需求:需要运行webpack.dll.js文件
--> webpack --config webpack.dll.js //webpack配置文件指向webpack.dll.jswebpack.dll.js文件
const resolve = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
// 最终打包生成的[name] --> jquery
// ['jquery'] --> 要打包的库是jquery
jquery: ['jquery']
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
library: '[name]_[hash]', // 打包的库里面向外暴露出去的内空叫什么名字
},
plugins: [
// 打包生成一个manifest.json --> 提供和jquery映射
new webpack.DllPlugin({
name: '[name]_[hash]', // 映射库暴露的内容名称
path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
})
]
}
生成dll文件后,就不需要打包了
- webpack.config.js文件
const resolve = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'build.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
// 告诉webpack哪些库不参与打包,同时使用时的名称也得变
new webpack.DllRefrencePlugin({
manifest: resolve(_dirname,'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
]
}
- 性能优化总结
- webpack性能优化
- 开发环境性能优化
- 优化打包构建速度
- HMR
- 优化代码调试
- source-map
- 优化打包构建速度
- 生产环境性能优化
- 优化打包构建事项
- oneof
- babel缓存
- 多进程打包
- externals
- dll
- 优化代码运行的性能
- 缓存(hash-chunkhash-contenthash)
- hash:webpack每次打包都会重新生成一个hash
- chunkhash:同属于一个chunk,共用一个hash
- contenthash:根据文件内容生成的hash,文件内容变化才会重新生成hash
- tree shaking
- es6 module
- production环境,uglyfy
- sideEffect参数
- code split 代码分隔
- 单入口
- 多入口
- 懒加载/预加载
- pwa
- 缓存(hash-chunkhash-contenthash)
- 优化打包构建事项
- entry
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
/*
entry: 入口起点
1. string --> './src/index.js'
单入口
打包形成一个chunk,输出一个bundle文件
filename: '[name].js' 此时chunk的名称默认是main
2. array --> ['./src/index.js', './src/add.js']
多入口
所有入口文件最终只会形成一个chunk,输出出去只有一个bundle文件
--> 只有在HMR功能中让html热更新生效~
3. object --> {index: './src/index.js', add: './src/add.js'}
多入口
有几个入口文件就形成几个chunk,输出几个bundle文件
此时chunk的名称是key
--> 特殊用法
{
// 所有入口文件最终只会形成一个chunk,输出出去只有一个bundle文件
index: ['./src/index.js', './src/count.js'],
// 形成一个chunk,输出一个bundle文件
add: './src/add.js'
}
*/
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].js',
path: resolve(__dirname, 'build')
}
}
- output
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
// 文件名称(指定名称+目录)
filename: '[name].js',
// 输出文件目录(将来所有资源输出的公共目录)
path: resolve(__dirname, 'build'),
// 所有资源引入公共路径的前缀 --> 'imgs/a.jpg' --> './imgs/a.jpg'
publicPath: '/', // 一般用于生产环境
chunkFilename: '[name]_chunk.js', // 非入口chunk的名称
// 通常结合dll用
library: '[name]', // 整个库向外暴露的变量名
libraryTarget: 'window', // 变量名添加到哪个上 browser
libraryTarget: 'global', // 变量名添加到哪个上 node
}
}
index.js
import count from './count';
console.log('index.js文件加载~');
/*
chunkFilename: '[name]_chunk.js'
--> 使用:打包成0_chunk.js(0为id)
--> 不使用:使用filename: '[name].js'规则,打包成0.js
*/
import('./add').then(({default: add}) => {
console.log(add(1,2);
})
console.log(count(3,2));
- loader
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
{
// loader的配置
test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader'],
},
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/,
// 只检查src下的js文件
include: resolve(__dirname, 'src'),
// 优先执行
enforce: 'pre',
// 延后执行
enforce: 'post',
// 单个loader用loader
loader: 'eslint-loader',
options: {}
},
{
// 以下配置只会生效一个
oneOf: []
}
],
}
}
- resolve
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
{
// loader的配置
test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader'],
},
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/
}
]
},
mode: 'development',
// 解析模块的规则
resolve: {
// 配置解析模块路径别名:简写路径 缺点路径没有提示
alias: {
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路径的后缀名
extensions: ['.js', '.json', '.jsx'],
// 告诉webpack解析模块是去找哪个目录
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
}
index.js
import '../css/index.css'
--> 可以写成: import '$css/index.css'
- devServer
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
{
// loader的配置
test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader'],
},
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/
}
]
},
devServer: {
// 运行代码的目录
contentBase: resolve(__dirname, 'build'),
// 监视contentBase目录下的所有文件,一旦文件变化就会reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动gzip压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
host: true,
// 不显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本启动信息以外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示~
overlay: false,
// 服务器代理 --> 解决开发环境跨域问题
proxy: {
// 一旦devServe(5000)服务器受到/api/xx的请求,就会把请求文件转到另外一服务器(3000)
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将/api/xxx --> /xxx (去掉/api)
pathRewirte: {
'^/api': ''
}
}
}
}
}
浏览器和服务器是存在跨域问题,服务器和服务器不存在跨域问题,浏览器通过代理服务器向服务器发送请求,代理服务器把请求数据返回到代理服务器上
23.optimization
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
{
// loader的配置
test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader'],
},
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/
}
]
},
mode: 'production',
optimization: {
splitChunks: {
chunks: 'all',
// 以下是默认值,可以不写
/*
minSize: 30*1024, // 分割的chunk最小为30kb
maxSize: 0, // 最大没有限制
minChunks: 1, // 要提取的chunk最少被引用1次
maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
maxInitialRequests: 3, // 入中js文件最大并行请求数量
automaticNameDelimiter: '~', // 名称连接符 vendors~xxx.js 因为这个里是~
name: true, //可以使用命名规则
cacheGroups: { // 分割chunk的组
// node_modules文件会被打包到vendors --> vendors~xxx.js xxx是模块名称
// 满足上面的公共规则:如:大小超过30kb,至少被引用一次
vendors: { // vendors~xxx.js 因为这个里是vendors
test: /[\\/]node_modules[\\/]/,
// 打包的优先级
priority: -10
},
default: {
// 要提取的chunk最少被引用2次
minChunks: 2,
// 优先级
priority: -20,
// 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
reuseExistingChunk: true
}
}
*/
},
// 将当前模块的记录其他模块的的hash单独打包为一个文件runtime
// 解决:修改a文件导致b文件contenthash变化
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: {
// 配置生产环境的压缩方案:js和css
new TerserWebpackPlugin: {
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启动source-map
sourceMap: true
}
}
}
}