基本使用
webpack 是一个静态资源打包工具
它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。输出的文件就是编译好的文件,就可以在浏览器段运行了我们将 Webpack 输出的文件叫做 bundle 。
功能介绍
Webpack 本身功能是有限的:
- 开发模式:仅能编译 JS 中的 ES Module 语法
- 生产模式: 能编译 JS 中的 ES Module 语法,还能压缩 JS 代码
目标
webpack 本身功能比较少,只能处理 js 资源,一旦遇到 css 等其他资源就会报错 。
学习 webpack 主要是学习如何处理其他资源
webpack 核心概念
- 入口(entry)
指示 Webpack 从哪个文件开始打包 - 输出(output)
指示 Webpack 打包完的文件输出到哪里去,如何命名等 - loader
webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析 - 插件(plugins)
扩展 Webpack 的功能 - 模式(mode)
- 开发模式:development
- 生产模式:production
安装
npm init -y
npm i webpack webpack-cli -D
配置
在文件根目录新建 webpack.config.js
const path = require('path'); // nodejs核心模块,专门处理路径问题
// 配置
module.exports = {
// 入口
entry: './src/main.js', // 相对路径
// 输出
output: {
// 文件的输出路径
// __dirname node.js的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, 'dist'), // 绝对路径
// 文件名称
filename: 'main.js'
},
// 加载器
module: {
rules: [
// loader的配置
]
},
// 插件
plugins: [
// plugin的配置
],
// 模式
mode: 'development'
}
开发模式介绍
开发模式顾名思义就是我们开发代码时使用的模式。
这个模式下我们主要做两件事:
- 编译代码,使浏览器能识别运行
开发时我们有样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理这些资源,所以我们要加载配置来编译这些资源。
- 编译代码,使浏览器能识别运行
- 代码质量检查,树立代码规范
提前检查代码的一些隐患,让代码运行时能更加健壮。
提前检查代码规范和格式,统一团队编码风格,让代码更优雅美观。
- 代码质量检查,树立代码规范
处理样式资源
处理 css
- 下载 style-loader 和 css-loader
npm i style-loader css-loader -D
- 配置
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
处理 Less
- 下载 less-loader 和 less
npm i less-loader less -D
- 配置
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
处理 Sass
- 下载 sass-loader 和 sass
npm i sass-loader sass -D
- 配置
module: {
rules: [
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
处理 stylus
- 下载 stylus-loader
npm i stylus-loader -D
- 配置
module: {
rules: [
{
test: /\.styl$/,
use: ['style-loader', 'css-loader', 'stylus-loader']
}
]
}
处理图片资源
- webpack4 时,处理图片资源需要通过 file-loader 和 url-loader 进行处理
- 下载 url-loader 和 file-loader
npm i url-loader file-loader -D
- 配置
module: {
rules: [
{
test: /\.(jpg|png|gif|bmp|jpeg|webp)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs'
}
}
]
}
]
}
webpack5 时,这两个 loader 已经内置到 webpack 中,只需要配置 type: 'asset'
配置
module: {
rules: [
{
test: /\.(jpg|png|gif|bmp|jpeg|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb会转base64,(优点:会减少请求数量,缺点:体积会更大)
}
}
}
]
}
修改打包输出文件目录
-
- JS 文件的去找 output 配置,并修改其中的 filename
filename: 'static/js/main.js',
-
- 图片的去找图片的 loader,并添加 generator 配置
generator: { filename: 'static/images/[hash:10][ext][query]' }
自动清空上次打包结果
- 在 output 配置中添加
clean: true
处理字体图标资源
- 在 loader 中配置(和对图片的处理配置一样)
{
test: /\.(ttf|woff2?|eot|otf)$/,
type: 'asset/resource',
generator: {
// 生成输出字体图标名称
filename: 'static/fonts/[hash:10][ext][query]'
}
}
处理其他资源(音视频等)
- 在 type 为
'asset/resource'
配置的 test 匹配规则后面继续增加
test: /\.(ttf|woff2?|eot|otf|mp3|mp4)$/,
处理 JS 资源
- 针对 js 兼容性处理,我们使用 Babel 来完成
- 针对代码格式,我们使用 Eslint 来完成
Eslint
检测代码中潜在的问题和错误的工具,可以配置各项功能
在 webpack4 中是一个 loader,在 webpack5 中是一个 plugin
安装 Eslint
npm i eslint-webpack-plugin eslint -D
- 在 webpack.config.js 中配置 Eslint 插件
const ESLintWebpackPlugin = require('eslint-webpack-plugin')
module.exports = {
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, 'src') // 指定检查的目录
})
]
}
- 在根目录创建
.eslintrc.js
文件
module.exports = {
extends: ["eslint:recommended"],
env: {
node: true, // 启用node中全局变量
browser: true // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6, // es6
sourceType: "module" // es module
},
rules: {
"no-var": 2, // 不能使用var定义变量
}
// ...更多规则看官网
}
Babel
用于将 ES6 代码转换成向后兼容的 JS 语法
安装 Babel
npm i @babel/core @babel/preset-env babel-loader -D
- 在 webpack.config.js 中配置 Babel
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 不处理node_modules中的js文件
use: {
loader: 'babel-loader'
}
}
]
}
}
- 在根目录创建
babel.config.js
文件
module.exports = {
// 智能预设,能够编译es6的语法
presets: ["@babel/preset-env",]
}
处理 Html 资源
- 安装 html-webpack-plugin
npm i html-webpack-plugin -D
- 在 webpack.config.js 中配置 html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'public/index.html') // 指定模板文件路径
}
)
]
}
开发服务器&自动化
- 安装 webpack-dev-server
npm i webpack-dev-server -D
- 在 webpack.config.js 中配置开发服务器
module.exports = {
devServer: {
host: 'localhost', // 设置启动时候的主机地址
port: 3000, // 设置启动时候的运行端口
open: true // 自动打开浏览器访问
}
}
- 启动服务器
npx webpack serve
生产模式介绍
生产模式是开发完成代码后,我们需要得到代码将来部署上线,这个模式下我们主要对代码进行优化,让其运行性能更好。
优化主要从两个角度出发:
- 优化代码运行性能
- 优化代码打包速度
生产模式准备
- 根目录创建 config 文件夹
- 在下面创建
webpack.dev.js
和webpack.prod.js
,分别用于开发模式和生产模式下的配置文件,
- 在下面创建
- 2.1 开发环境不需要输出,所以设置 path: undefined
- 2.2 开发模式下,不需要输出,所以注释 clean: true
- 2.3 生产模式下,需要输出,所有用到绝对路径的都要用
../
返回上一层 - 2.4 生产模式不需要 devServer
- 2.5 开发模式和生产模式中 mode 要统一
生产模式配置
CSS 处理 (提取 CSS 成单独文件)
CSS 文件目前被打包到 JS 中,当 JS 加载时创建 style 标签来生成样式,这会导致页面闪屏,影响用户体验,应该将 CSS 直接提取成单独的样式文件,通过 link 标签引入
安装 mini-css-extract-plugin
npm i mini-css-extract-plugin -D
- 在 webpack.prod.js 中配置
- 之前所有写 style-loader 的地方,现在都需要改成 MiniCssExtractPlugin.loader,因为 style-loader 会动态创建 style 标签
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 提取成单独文件
'css-loader',
]
}
]
},
plugins: [
new MiniCssExtractPlugin()
],
}
CSS 兼容性处理
- 安装 postcss-loader postcss postcss-preset-env
npm i postcss-loader postcss postcss-preset-env -D
- 在 webpack.prod.js 中配置
- 该配置需要写在 css-loader 的后面和 less-loader、sass-loader 的前面
{
loader: 'postcss-loader', // 配置postcss
options: {
postcssOptions: {
plugins: [
postcss-preset-env' // 能解决大多数样式兼容性问题
]
}
}
},
- 在 package.json 中配置
- 指定兼容性处理到哪个浏览器版本
- 实际开发中可以设置为 "last 2 version" , "> 1%" , "not dead"
"browserslist": [
"ie >= 8"
]
封装样式 loader 函数
- 由于上面配置的 CSS 兼容性样式处理代码冗余,将他封装成一个 loader 函数
function getStyleLoader(pre) {
return [ // 执行顺序,从右到左,从下到上
MiniCssExtractPlugin.loader, // 将css提取成单独文件
'css-loader', // 将css编译成commonjs的模块到js中
{
loader: 'postcss-loader', // 配置postcss
options: {
postcssOptions: {
plugins: [
'postcss-preset-env' // 能解决大多数样式兼容性问题
]
}
}
},
pre
].filter(Boolean)
}
- 使用
getStyleLoader()
getStyleLoader('less-loader')
getStyleLoader('sass-loader')
getStyleLoader('stylus-loader')
CSS 压缩
- 安装插件
npm i css-minimizer-webpack-plugin -D
- 在 webpack.prod.js 中配置
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
optimization: {
minimizer: [
new CssMinimizerPlugin()
]
}
HTML 压缩
- 默认生产模式已经开启了 HTML 和 JS 压缩
webpack 优化
- 提升开发体验
- 提升打包构建速度
- 减少代码体积
- 头还代码运行性能
开发体验优化
SourceMap
是一个源代码映射文件, 里面存储了代码转换后的位置信息
实际开发中只需要关注两种情况即可
-
在 webpack 中配置
- 开发模式:cheap-module-source-map
- 优点: 打包速度快
- 缺点: 没有列映射
module.exports: {
// ...
mode:'development',
devtool:'cheap-module-source-map'
}
- 在 webpack 中配置
- 生产模式:source-map
- 优点: 包含行/ 列映射
- 缺点: 打包速度慢
module.exports: {
// ...
mode:'production',
devtool:'source-map'
}
提升打包构建速度
Hot Module Replacement
热模块替换, 简称 HMR
作用: 一个模块发生变化, 只会重新打包这一个模块(而不是打包所有模块)
CSS 已经由 style-loader 开启 HMR
JS 需要手动开启(但实际开发中,会使用 vue-loader 或 react-loader 来解决)
在 webpack 中配置
module.exports: {
// ...
devServer: {
// ...
// 开启HMR功能(只能用于开发环境)
// hot默认值:开启
hot:true
}
}
OneOf
- 作用: 防止匹配多个 loader 处理文件
- 在 webpack 中配置(开发模式和生产模式皆可)
module: {
rules: [
{
// 每个文件只能被其中一个loader配置处理
oneOf: [
// ...loader
]
}
]
}
Include/Exclude
- Include: 包含,只处理匹配到的文件
- Exclude: 排除,不处理匹配到的文件
- 注意: 只能使用一种,同时使用会报错
rules: [
{
test: /\.js$/,
// exclude: /node_modules/,
include: path.resolve(__dirname, '../src'),
use: { loader: 'babel-loader' }
}
]
Cache
- 每次打包 JS 文件都要经过 Eslint 检查 和 Babel 编译,速度比较慢。缓存之前的编译结果,第二次打包时速度会变快。
- 在 webpack.prod.js 中配置
module.exports: {
module:{
rules:[
{
test: /\.js$/,
include: path.resolve(__dirname, '../src'),
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
}
}
}
]
},
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, '../src'), // 指定检查的目录
exclude: 'node_modules', // 排除检查
cache: true, // 开启缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache') // 指定缓存位置
}),
]
}
Thead
- 多进程打包:开启电脑的多个进程同时干一件事,速度更快。
- 注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右开销。
- 安装
npm i thread-loader -D
- 在 webpack 中配置
// 获取cpu核数
const os = require('os')
const threads = os.cpus().length
const TerserPlugin = require("terser-webpack-plugin");
module.exports: {
module:{
rules:[
{
test: /\.js$/,
include: path.resolve(__dirname, '../src'),
use: [
{
loader: 'thread-loader', // 开启多进程
options: {
workers: threads, // 进程数量
}
},
{
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
}
}
]
}
]
},
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, '../src'), // 指定检查的目录
exclude: 'node_modules', // 排除检查
cache: true, // 开启缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache') // 指定缓存位置
threads, // 开启多进程和进程数量
}),
new TerserPlugin({
parallel: threads // 开启多进程和进程数量
})
]
}
减少代码体积
Tree Shaking
Tree Shaking
是一个术语,通常用于描述移除 JavaScript 中的没有使用上的代码。(例如我们引入了一些函数库,但实际只使用了其中的几个功能,如果将这些都打包进来,那么项目的体积就太大了)注意:它依赖
ES Module
。webpack 已经默认开启了这个功能,无需其他配置。
Babel
@babel/plugin-transform-runtime
: 禁用了 Babel 自动对每个文件的 runtime 注入,而是引入@babel/plugin-transform-runtime
并且使所有辅助代码从这里引用。安装
npm i @babel/plugin-transform-runtime -D
- 在 webpack 中配置
module.exports: {
module:{
rules:[
{
test: /\.js$/,
include: path.resolve(__dirname, '../src'),
use: [
{
loader: 'thread-loader', // 开启多进程
options: {
workers: threads, // 进程数量
}
},
{
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
}
}
]
}
]
}
}
Image Minimizer
压缩项目本地图片体积
安装
npm i image-minimizer-webpack-plugin imagemin -D
- 安装无损压缩
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
- 安装有损压缩
npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
- 配置
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // 无损压缩
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin({
parallel: threads // 开启多进程和进程数量
}),
// 压缩图片
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
]
},
}
优化代码运行性能
Code Split
打包代码时会将所有 js 文件打包到一个文件中,体积太大了。我们如果只要渲染首页,就应该只加载首页的 js 文件,其他文件不应该加载。
所以我们需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样加载的资源就少,速度就更快。
-
代码分割(Code Split)主要做了两件事:
- 分割文件:将打包生成的文件进行分割,生成多个 js 文件。
- 按需加载:需要哪个文件就加载哪个文件。
需求: 多入口文件之间有公共代码需要处理(多次引用的公共方法打包成一个单独的 JS 文件)
- 步骤 1
module.exports = {
// 单入口
entry: './src/main.js',
// 多入口
// 写几个配置就会输出多少js文件
entry: {
app: './src/app.js',
main: './src/main.js',
},
// ...
}
- 步骤 2
- 通过
optimization.splitChunks
实现,它是一个对象,里面包含很多配置项。
optimization: {
// 代码分割配置
splitChunks: {
chunks: "all", // 对所有模块都进行分割
// 以下是默认值
// minSize: 20000, // 分割代码最小的大小
// minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0
// minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量
// maxInitialRequests: 30, // 入口js文件最大并行请求数量
// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)
// cacheGroups: { // 组,哪些模块要打包到一个组
// defaultVendors: { // 组名
// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
// priority: -10, // 权重(越大越高)
// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
// },
// default: { // 其他没有写的配置会使用上面的默认值
// minChunks: 2, // 这里的minChunks权重更大
// priority: -20,
// reuseExistingChunk: true,
// },
// },
// 修改配置
cacheGroups: {
// 组,哪些模块要打包到一个组
// defaultVendors: { // 组名
// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
// priority: -10, // 权重(越大越高)
// reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
// },
default: {
// 其他没有写的配置会使用上面的默认值
minSize: 0, // 我们定义的文件体积太小了,所以要改打包的最小文件体积
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
- 步骤 3
- 按需加载,动态导入(加载页面时没有被使用的 JS 不要引入)
- import 动态导入,会将动态导入的文件代码分割(拆分成单独模块),在需要使用的时候自动加载
// 导入实现累加的方法
import('./add.js')
.then((res) => {
console.log('加载成功')
console.log(res.default(2,3))
})
.catch((err) => {
console.log('加载失败')
})
需求: 单入口文件之间有公共代码需要处理(多次引用的公共方法打包成一个单独的 JS 文件)
- 步骤 1
- 配置 optimization.splitChunks
// 只需要配置这一个就可以(配置方法同上),其他都用默认值
chunks: "all",
- 步骤 2
- 动态导入语法(同上)
动态导入文件更名
- 步骤 1
// 导入实现累加的方法
import(/* webpackChunkName:"add" */ "./add.js")
.then((res) => {
console.log('加载成功')
console.log(res.default(2,3))
})
.catch((err) => {
console.log('加载失败')
})
- 步骤 2
- 在 webpack 的 output 中配置 filename
output: {
// ...
chunkFilename: 'static/js/[name].js'
}
统一命名配置
module.exports = {
output:{
// 步骤1
// 入口文件打包输出文件名
filename:'static/js/[name].js',
// 非入口文件打包输出文件名
chunkFilename:'static/js/[name].chunk.js',
// 图片、字体等资源文件打包输出文件名
assetModuleFilename:'static/media/[hash:10][ext][query]'
},
plugins:[
// ...
new MiniCssExtractPlugin({
filename:'static/css/[name].css',
chunkFilename:'static/css/[name].chunk.css'
})
]
}
Preload 和 Prefetch
-
初始 JS 加载完之后,在没有更多资源需要从服务器获取时,可以使用
Preload
或Prefetch
来加载更多的将要用到的资源-
Preload
:告诉浏览器立即加载资源。 -
Prefetch
:告诉浏览器在空闲时才开始加载资源。
-
-
共同点
- 它们都只会加载资源,不会执行。
- 都有缓存。
-
它们区别:
-
Preload
加载优先级高,Prefetch
加载优先级低。 -
Preload
只能加载当前页面需要使用的资源,Prefetch
可以加载当前页面资源,也可以加载下一个页面需要使用的资源。
-
-
总结:
- 当前页面优先级高的资源用
Preload
加载。 - 下一个页面需要使用的资源用
Prefetch
加载。
- 当前页面优先级高的资源用
安装
npm i @vue/preload-webpack-plugin -D
- 在 webpack 中配置
const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");
module.exports = {
plugins: [
new PreloadWebpackPlugin({
// 用preload
rel: "preload", // preload兼容性更好
as: "script",
// 用prefetch
// rel: 'prefetch' // prefetch兼容性更差
}),
],
}
需求
当我们修改 math.js 文件再重新打包的时候,因为 contenthash 原因,math.js 文件 hash 值发生了变化(这是正常的)。
但是 main.js 文件的 hash 值也发生了变化,这会导致 main.js 的缓存失效。明明我们只修改 math.js, 为什么 main.js 也会变身变化呢?
- 原因:
- 更新前:math.xxx.js, main.js 引用的 math.xxx.js
- 更新后:math.yyy.js, main.js 引用的 math.yyy.js, 文件名发生了变化,间接导致 main.js 也发生了变化
- 解决:
将 hash 值单独保管在一个 runtime 文件中。
我们最终输出三个文件:main、math、runtime。当 math 文件发送变化,变化的是 math 和 runtime 文件,main 不变。
runtime 文件只保存文件的 hash 值和它们与文件关系,整个文件体积就比较小,所以变化重新请求的代价也小。
- 配置
module.exports = {
optimization: {
minimizer: [
// ...
],
splitChunks: {
name: (entrypoint) => `runtime~${entrypoint.name}.js`, // runtime文件命名规则
}
},
}
Core.js
core-js
是专门用来做 ES6 以及以上 API 的polyfill
。polyfill
翻译过来叫做垫片/补丁。就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该新特性。安装
npm i core-js
- 入口文件引入
// 手动全部引入
import "core-js";
// 手动按需引入(需要什么引入什么)
import "core-js/es/promise";
-
配置
- 在根目录创建 babel.config.js
module.exports = { // 智能预设:能够编译ES6语法 presets: [ [ "@babel/preset-env", // 按需加载core-js的polyfill { useBuiltIns: "usage", corejs: 3 }, ], ], };
PWA
需求: 开发 Web App 项目,项目一旦处于网络离线情况,就没法访问了。我们希望给项目提供离线体验。
渐进式网络应用程序(progressive web application - PWA):是一种可以提供类似于 native app(原生应用程序) 体验的 Web App 的技术。
其中最重要的是,在 离线(offline) 时应用程序能够继续运行功能。
内部通过 Service Workers 技术实现的。
安装
npm i workbox-webpack-plugin -D
- 在 webpack 中配置
const WorkboxPlugin = require("workbox-webpack-plugin");
module.exports = {
// ...
plugins: [
// ...
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
]
}
- 在入口文件中(一般是 main.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);
});
});
}