最关键的点就是希望打包速度更快一些就够了
目录结构复用5.开发环境配置中08.开发环境配置
HMR:hot module replacement 热模块替换/模块热替换
当我们修改css样式文件的时候,js文件没有变化,但是也被重新加载了一次,所以看似只修改了css文件,但实际上吧js文件也重新打包了一次。这代表着假设有100个模块,100个样式模块,只要修改其中的某一个模块,整个200个模块都要重新打包,这会严重影响速度。所以我们想要实现只有一个模块发生变化,就只修改这一个模块就够了,其他模块理应不变。这需要webpack的HMR功能来实现。
webpack.config.js
const {
resolve
} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader的配置
{
// 处理less资源
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
// 处理css资源
test: /\.css$/,
use: [
'style-loader',
'css-loader',
]
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭ES6模块化
esModule: false,
// 将图片输出到imgs目录下
outputPath: 'imgs'
}
},
{
// 处理html中的img资源
test: /\.html$/,
loader: 'html-loader',
},
{
// 处理其他资源
exclude: /\.(css|js|html|less|jpg|png|gif)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
// plugins的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 开启HMR功能
// 当修改了webpack配置,新配置想要生效,必须重新启动webpack服务
hot: true
}
}
注:当修改了webpack配置,新配置想要生效,必须重新启动webpack服务
可以看到此时样式文件发生变化,就只会更新样式文件而不是全部重新打包
发现以下结果:
样式文件:可以使用HMR功能:因为style-loader内部实现了
js文件:默认没有HMR功能
src->js->print.js
console.log('print.js被加载了')
function print() {
const content = 'hello print'
console.log(content)
}
export default print
src->js->index.js
// 引入
import print from './print'
import '../css/iconfont.css'
import '../css/index.less'
console.log('index.js文件被加载了~')
print()
function add(x, y) {
return x + y
}
console.log(add(2, 3))
if (module.hot) {
// 一旦module.hot为true,说明开启了HMR功能-->让HMR功能代码生效
module.hot.accept('./print.js', function () {
// 方法被监听 print.js文件的变化,一旦发生变化,其他默认不会重新打包构建
// 会执行后面的回调函数
print()
})
}
注意:HMR功能对js的处理,只能处理非入口的js文件
html文件:默认不能使用HMR功能,同时会导致问题:html文件不能热更新了(不用做HMR功能)
修改配置稳健的entry部分
entry: ['./src/js/index.js','./src/index.html'],
source-map:一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射可以追踪到源代码错误)
作用:优化代码调试功能
参数形式:[inline-|hidden-|eval-][nosources-][cheap-[module]]source-map
开启方式:在配置文件下的devtool进行配置,例如devtool: 'source-map'
cheap-source-map
cheap-module-source-map
目的:速度快一点,调试更友好
速度快(eval>inline>cheap)
eval-cheap-source-map(因为cheap只精确到行)
eval-source-map
调试更友好
综合起来最终
考虑的问题:源代码要不要隐藏?调试要不要更友好?
内联会让代码体积更加大,所以在生产环境不用内联
源代码隐藏
综合考虑
source-map
对于生产环境优化需要考虑的点则非常的复杂,面面俱到,目的是让我们将来上线的代码性能达到最好,从而让用户体验好,产品就更好。
一般来说,每处理一种类型资源,所有配置文件中的loader都会被过一遍,这样就不太好,通过oneOf能够让我们处理的性能更好
注意:不能有两个配置处理同一种类型的文件,如果有这种情况,则将其中一个配置提取出去即可
配置文件
const { resolve } = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 设置node.js环境变量:决定使用browserlist的哪个环境
process.env.NODE_ENV = 'development'
const commonCssLoader = [
// 提取成单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// 对样式进行兼容性处理
// use数组中loader执行顺序:从右到左,从下到上依次执行,所以样式兼容性处理放在coo-loader后面
{
// 还需要在package.json中对browserlist进行设置,且如果需要使用开发环境配置,则要设置node.js环境变量,不然browserlist将默认使用production下的配置
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
// 文件名
filename: 'js/bulit.js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
module: {
rules: [
// 处理js文件
{
// 在package.json文件中eslintConfig配置使用airbnb规则
test: /\.js$/,
exclude: /node_modules/, // 排除检测node_modules的内容
// 优先执行该loader,确保两个loader不会产生冲突
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 自动修复检查到的问题
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型的文件
oneOf: [
// 处理css样式文件
{
test: /\.css$/,
use: [...commonCssLoader]
},
// 处理less文件
{
test: /\.less$/,
use: [
...commonCssLoader,
// 将less文件编译成css文件
// 需要下载less-loader和less
'less-loader'
]
},
// 对js做兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设
presets: [
'@babel/preset-env',
{
// 实现按需加载
useBuiltIns: 'usage',
// 指定core-js版本,可以到package.json查看
corejs: {
version: 3
},
// 指定兼容哪些版本的浏览器
targets: {
chrome: '60',
firefox: '50',
ie: '9'
}
}
]
}
},
// 处理图片文件
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 输出路径
outputPath: 'imgs',
esModule: false,
}
}, {
test: /\.html$/,
// 处理html文件中的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader'
},
// 处理其它文件
{
// 排除以下几种文件
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 对输出的文件进行重命名
filename: 'css/built.css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin(),
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
})
],
// 生产环境下js会自动压缩
mode: 'production'
}
在写代码时,js的代码永远是最多的
// 对js做兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设
presets: [
'@babel/preset-env',
{
// 实现按需加载
useBuiltIns: 'usage',
// 指定core-js版本,可以到package.json查看
corejs: {
version: 3
},
// 指定兼容哪些版本的浏览器
targets: {
chrome: '60',
firefox: '50',
ie: '9'
}
}
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectroy: true
}
}
通过多进程的方式优化打包速度
适用场景:针对消耗时间较长的任务(大于600ms)
需要的工具:thread-loader
npm i thread-loader -D
配置文件
const {
resolve
} = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
// 设置node.js环境变量:决定使用browserlist的哪个环境
process.env.NODE_ENV = 'production'
// 复用loader
const commonCssLoader = [
// 提取成单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// 对样式进行兼容性处理
// use数组中loader执行顺序:从右到左,从下到上依次执行,所以样式兼容性处理放在coo-loader后面
{
// 还需要在package.json中对browserlist进行设置,且如果需要使用开发环境配置,则要设置node.js环境变量,不然browserlist将默认使用production下的配置
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
// 文件名
filename: 'js/built.[contenthash:10].js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
module: {
rules: [
// 处理js文件
{
// 在package.json文件中eslintConfig配置使用airbnb规则
test: /\.js$/,
exclude: /node_modules/, // 排除检测node_modules的内容
// 优先执行该loader,确保两个loader不会产生冲突
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 自动修复检查到的问题
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型的文件
oneOf: [
// 处理css样式文件
{
test: /\.css$/,
use: [...commonCssLoader]
},
// 处理less文件
{
test: /\.less$/,
use: [
...commonCssLoader,
// 将less文件编译成css文件
// 需要下载less-loader和less
'less-loader'
]
},
// 对js做兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
use: [
/**
* 开启多进程打包
* 进程启动大概为600ms,进程通信也有开销
* 只有工作消耗时间比较长,才需要多进程打包
*/
{
loader: 'thread-loader',
options: {
workers: 2 // 进程数
}
},
{
loader: 'babel-loader',
options: {
// 预设
presets: [
[
'@babel/preset-env',
{
// 实现按需加载
useBuiltIns: 'usage',
// 指定core-js版本,可以到package.json查看
corejs: {
version: 3
},
// 指定兼容哪些版本的浏览器
targets: {
chrome: '60',
firefox: '50',
ie: '9'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
},
// 处理图片文件
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
// 给图片重命名
// [hash: 10]取图片的hash的前10位
// [ext]取文件原来扩展名
name: '[hash:10].[ext]',
// 输出路径
outputPath: 'imgs',
esModule: false,
}
},
{
test: /\.html$/,
// 处理html文件中的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader'
},
// 处理其它文件
{
// 排除以下几种文件
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 对输出的文件进行重命名
filename: 'css/built.[contenthash:10].css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin(),
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
}),
new WorkboxWebpackPlugin.GenerateSW({
/**
* 1.帮助serviceworker快速启动
* 2.删除就得serviceworker
*
* 会生成一个serviceworker 配置文件
*/
clientsClaim: true,
skipWaiting: true
})
],
// 生产环境下js会自动压缩
mode: 'production',
devtool: 'source-map'
}
作用:防止将某一些包打包到最终输出的bundle中。
用法:当开发时发现有些包是通过cdn链接引入的,则可以通过配置externals来拒绝打包,再在html文件中通过script标签的方式将这些包引入即可
修改配置文件(演示以jQuery为例)
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
// 复制'./src/index.html'文件,并自动引入打包输出的所有资源(JS/CSS)
template: './src/index.html'
})
],
mode:'production',
externals: {
// 拒绝jQuery被打包进来
// 忽略的库名 -- npm包名
jquery: 'jQuery'
}
}
import $ from 'jquery'
console.log($)
获取cdn链接网址:https://www.bootcdn.cn/
index.html源代码
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<h1 id="title">hello htmlh1>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js">script>
body>
html>
可以看到index.js中引入的jQuery是可以生效的
动态连接库,类似externals一样会指示webpack哪些库是不参与打包的,不同的是dll会单独对某些库进行打包,把库先打包好,后面就直接用,不需要在再打包
以下以jquery为例进行演示
npm i add-asset-html-webpack-plugin -D
/**
* 使用dll技术,对某些库(第三方库:jquery、react、vue……)进行单独打包
* 当运行webpack时,默认查找webpack.config.js配置文件
* 需求:需要运行webpack.dll.js文件
* webpack --config webpack.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') // 输出文件路径
})
],
mode: 'production'
}
生成一个jquery.js和manifest.json(提供映射关系)文件
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: 'built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
// 复制'./src/index.html'文件,并自动引入打包输出的所有资源(JS/CSS)
template: './src/index.html'
}),
// 告诉webpack哪些库不参与打包,同时使用时名称也得变
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
mode: 'production'
}
通过输出文件命名依据(hsah值)来实现,开启babel缓存之后,每次加载在发现文件没有变化的时候就直接使用缓存,而不会再重新构建一次,所以只要维护hsah值的变化就能够实现控制某个文件是否有更新。
作用:为了使当改变某一文件时,其余文件不会一起更新,让代码上线运行缓存更好使用(上线代码性能优化)
共有以下三种hash值:
hash:每次webpack构建时会生成一个唯一的hash值
chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk(同一个入口),那么hash值就一样。
使用第三种方法修改js文件源代码,重新构建,对比前后发现只有js文件的hash值发生了变化,其余不变,同理修改css文件源代码也是如此。
这样就可以实现没有修改的文件在加载时直接使用缓存,有修改(文件hash值发生变化)就更新该文件。
使用第三种hash值(有文件输出的地方使用)的配置文件:
const {
resolve
} = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 设置node.js环境变量:决定使用browserlist的哪个环境
process.env.NODE_ENV = 'production'
// 复用loader
const commonCssLoader = [
// 提取成单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// 对样式进行兼容性处理
// use数组中loader执行顺序:从右到左,从下到上依次执行,所以样式兼容性处理放在coo-loader后面
{
// 还需要在package.json中对browserlist进行设置,且如果需要使用开发环境配置,则要设置node.js环境变量,不然browserlist将默认使用production下的配置
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
// 文件名
filename: 'js/built.[contenthash:10].js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
module: {
rules: [
// 处理js文件
{
// 在package.json文件中eslintConfig配置使用airbnb规则
test: /\.js$/,
exclude: /node_modules/, // 排除检测node_modules的内容
// 优先执行该loader,确保两个loader不会产生冲突
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 自动修复检查到的问题
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型的文件
oneOf: [
// 处理css样式文件
{
test: /\.css$/,
use: [...commonCssLoader]
},
// 处理less文件
{
test: /\.less$/,
use: [
...commonCssLoader,
// 将less文件编译成css文件
// 需要下载less-loader和less
'less-loader'
]
},
/**
* 正常来说,一个文件只能被一个loader处理。
* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
* 先执行eslint 再执行 babel
* 1. 因为eslint是执行语法检查的,如果语法出现错误,执行babel也没有意义
* 2. babel会将ES6语法转换为ES5语法,一旦经过babel转换之后再进行eslint检查就会报语法错误
*/
// 对js做兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设
presets: [
[
'@babel/preset-env',
{
// 实现按需加载
useBuiltIns: 'usage',
// 指定core-js版本,可以到package.json查看
corejs: {
version: 3
},
// 指定兼容哪些版本的浏览器
targets: {
chrome: '60',
firefox: '50',
ie: '9'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
},
// 处理图片文件
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理(8~12kb)
// 优点:减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 给图片重命名
// [hash: 10]取图片的hash的前10位
// [ext]取文件原来扩展名
name: '[hash:10].[ext]',
// 输出路径
outputPath: 'imgs',
/**
* 问题:因为url-loader默认使用ES6模块化解析,而html-loader引入图片是commonjs
* 解析时会出问题:[object Module]
* 解决:关闭url-loader的ES6模块化,使用commonjs解析
*/
esModule: false,
}
},
{
test: /\.html$/,
// 处理html文件中的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader'
},
// 处理其它文件
{
// 排除以下几种文件
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 对输出的文件进行重命名
filename: 'css/built.[contenthash:10].css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin(),
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
})
],
// 生产环境下js会自动压缩
mode: 'production',
devtool: 'source-map'
}
目的:去除应用程序中没有使用的代码,让代码体积更小
使用前提:
作用:减少代码体积,从而请求速度更快,加载速度也快
在package.json中配置"sideEffects":
代码分割就是将我们打包的一个chunk(打包输出的一个文件)分割成多个文件,这样就可以去实现各项功能(并行加载提高速度、按需加载)
通过指定入口(entry)的方式实现拆分
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 单入口
// entry: './src/js/index.js',
entry: {
// 多入口:有一个入口输出,最终输出就会有一个bundle
main: './src/js/index.js',
test: './src/js/test.js'
},
output: {
// [name]:取文件名
filename: 'js/[name].[contenthash:10].js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
plugins: [
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
})
],
// 生产环境下js会自动压缩
mode: 'production'
}
为了解决当多个入口文件中都使用了一些公共的文件,打包时每个入口文件都将该公共文件一起打包,导致在加载时这些公共文件被重复加载的问题。
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 单入口
// entry: './src/js/index.js',
entry: {
// 多入口:有一个入口输出,最终输出就会有一个bundle
main: './src/js/index.js',
test: './src/js/test.js'
},
output: {
// [name]:取文件名
filename: 'js/[name].[contenthash:10].js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
plugins: [
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
})
],
/**
* 1.可以将node_modules中代码单独打包成一个chunk最终输出
* 2.自动分析多入口chunk中,有没有公共文件。如果有会打包成单独一个chunk
*/
optimization: {
splitChunks: {
chunks: 'all'
}
},
// 生产环境下js会自动压缩
mode: 'production'
}
实际开发中,还是单页面应用程序比较多,多入口使用还是较少,但是在单入口情况下,方法2只能实现第一个功能,但是单入口情况下我们仍希望能够实现文件拆分的功能,因此一般情况下使用该方法。
作用:通过js代码,将某个文件单独打包
使用:将原先入口文件中的引用语法修改为以下动态导入语法,以希望将test.js文件单独打包为例
export function mul(x, y) {
return x * y;
}
export function count(x, y) {
return x - y;
}
function sum(...args) {
return args.reduce((p, c) => p + c, 0);
}
/**
* 通过js代码,让某文件被单独打包成一个chunk
*/
import(/* webpackChunkName: 'test' */'./test')
.then((result) => {
// 文件加载成功
// eslint-disable-next-line
console.log(result)
})
.catch(() => {
// eslint-disable-next-line
console.log('文件加载失败')
})
// eslint-disable-next-line
console.log(sum(1, 2, 3))
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 单入口
entry: './src/js/index.js',
output: {
// [name]:取文件名
filename: 'js/[name].[contenthash:10].js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
plugins: [
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
})
],
optimization: {
splitChunks: {
chunks: 'all'
}
},
// 生产环境下js会自动压缩
mode: 'production'
}
两者针对的都是js文件
配置文件
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 单入口
entry: './src/js/index.js',
output: {
// [name]:取文件名
filename: 'js/[name].[contenthash:10].js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
plugins: [
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
})
],
optimization: {
splitChunks: {
chunks: 'all'
}
},
// 生产环境下js会自动压缩
mode: 'production'
}
懒加载相当于延迟加载,就是当触发某些条件的时候才会加载,而不是一上来就加载。利用代码分割,将import动态导入语法放在一个异步的回调函数中,当满足异步函数触发的条件,再去懒加载代码
注:第二次加载直接读取缓存,不会存在重复加载的问题
console.log('index.js文件被加载了')
// import { mul } from './test'
document.getElementById('btn').onclick = function() {
// 懒加载
import( /* webpackChunkName: 'test' */ './test')
.then(({ mul }) => {
// 文件加载成功
// eslint-disable-next-line
console.log(mul(4, 5))
})
.catch(() => {
// eslint-disable-next-line
console.log('文件加载失败')
})
}
会在使用之前,提前加载js文件
与正常加载相比:
与懒加载相比
问题:兼容性较差,只能在一些高版本的浏览使用,在移动端或IE浏览器会有相当大的兼容性问题,所以需要慎用
console.log('index.js文件被加载了')
// import { mul } from './test'
document.getElementById('btn').onclick = function() {
// 懒加载
// 预加载 Prefetch:会在使用之前,提前加载js文件
// 正常加载可以认为是并行加载( 同一时间加载多个文件)
// 预加载: 等其他资源加载完毕, 浏览器空闲了再偷偷加载资源, 更加灵活
import( /* webpackChunkName: 'test', webpackPrefetch: true */ './test')
.then(({ mul }) => {
// 文件加载成功
// eslint-disable-next-line
console.log(mul(4, 5))
})
.catch(() => {
// eslint-disable-next-line
console.log('文件加载失败')
})
}
作用:帮助我们让我们的网页像APP应用程序一样离线也可以访问,性能也更好,也称为渐进式网络开发应用程序。
存在问题:因为兼容性问题,普及还需要一定时间。
npm i workbox -D
修改配置文件
const {
resolve
} = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
/**
* PWA:渐进式网络开发应用程序(离线可访问)
* workbox--> workbox-webpack-plugin
*/
// 设置node.js环境变量:决定使用browserlist的哪个环境
process.env.NODE_ENV = 'production'
// 复用loader
const commonCssLoader = [
// 提取成单独文件
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
// 文件名
filename: 'js/built.[contenthash:10].js',
// 输出的文件目录
path: resolve(__dirname, 'build')
},
module: {
rules: [
// 处理js文件
{
// 在package.json文件中eslintConfig配置使用airbnb规则
test: /\.js$/,
exclude: /node_modules/, // 排除检测node_modules的内容
// 优先执行该loader,确保两个loader不会产生冲突
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 自动修复检查到的问题
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型的文件
oneOf: [
// 处理css样式文件
{
test: /\.css$/,
use: [...commonCssLoader]
},
// 处理less文件
{
test: /\.less$/,
use: [
...commonCssLoader,
// 将less文件编译成css文件
// 需要下载less-loader和less
'less-loader'
]
},
// 对js做兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设
presets: [
[
'@babel/preset-env',
{
// 实现按需加载
useBuiltIns: 'usage',
// 指定core-js版本,可以到package.json查看
corejs: {
version: 3
},
// 指定兼容哪些版本的浏览器
targets: {
chrome: '60',
firefox: '50',
ie: '9'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
},
// 处理图片文件
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理(8~12kb)
// 优点:减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 给图片重命名
// [hash: 10]取图片的hash的前10位
// [ext]取文件原来扩展名
name: '[hash:10].[ext]',
// 输出路径
outputPath: 'imgs',
/**
* 问题:因为url-loader默认使用ES6模块化解析,而html-loader引入图片是commonjs
* 解析时会出问题:[object Module]
* 解决:关闭url-loader的ES6模块化,使用commonjs解析
*/
esModule: false,
}
},
{
test: /\.html$/,
// 处理html文件中的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader'
},
// 处理其它文件
{
// 排除以下几种文件
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 对输出的文件进行重命名
filename: 'css/built.[contenthash:10].css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin(),
// 处理html文件
new HtmlWebpackPlugin({
// 以指定html文件为模板创建新的html文件
template: './src/index.html',
// 压缩html文件
minify: {
// 压缩空格
collapseWhitespace: true,
// 去除注释
removeComments: true
}
}),
new WorkboxWebpackPlugin.GenerateSW({
/**
* 1.帮助serviceworker快速启动
* 2.删除就得serviceworker
*
* 会生成一个serviceworker 配置文件
*/
clientsClaim: true,
skipWaiting: true
})
],
// 生产环境下js会自动压缩
mode: 'production',
devtool: 'source-map'
}
修改入口文件
import { mul } from './test';
import '../css/index.css';
function sum(...args) {
return args.reduce((p, c) => p + c, 0);
}
console.log(mul(2, 3));
// eslint-disable-next-line
console.log(sum(1, 2, 3))
// 注册serviceworker
// 处理兼容性问题
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(() => {
console.log('sw注册成功了');
})
.catch(() => {
console.log('sw注册失败');
});
});
}
问题:eslint不认识window、navigator全局变量
解决:需要修改package.json中eslintConfig配置
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
}
可以看到构建目录下生成了一个service-worker.js文件,该文件通过注册的方式加载service-worker资源,通过该文件做一些相应的pwa工作。
注:sw代码必须运行在服务器上,所以必须通过服务器的方式启动构建后的资源
启动服务器方式