Webpack5

418 阅读11分钟

webpack能处理js/json资源,不能处理css/img等其他资源

webpack构建工具都是基于node.js平台运行的,模块化默认采用common.js

安装:

npm install webpack webpack-cli --save-dev

打包样式资源

webpack配置文件:

const {resolve} = require('path')
module.exports = {
  //入口文件配置
  entry:'./src/index.js',
  //输出配置
  output:{
    //打包生成build文件夹中的build.js
    filename:'built.js',
    //打包生成build文件夹中的js文件夹中的build.js
    //filename:'js/build.js'
    //__dirname表示文件所在的目录
    path:resolve(__dirname,'build')
  },
    //主要用来处理css,img等资源
  module:{
    rules:[
      {
        test:/\.css$/,
        //要使用多个loader处理用use
        use:[
          'style-loader',
          'css-loader'
        ]}
    ]
  },
   //插件
  plugins:[

  ],
    //模式development或者production
  mode:'development'
}

注意:要使用多个loader处理用use,使用一个直接loader[^注意]

如何引入的是.less文件,则需要下载

npm i less less-loader --save-dev

webpack配置文件如下:

~
 modele:{
     rules:[
         {test:/\.less$/,
          use:[
             'style-loader',
              'css-loader',
              'less-loader'
         ]}
     ]
 }   
~

打包html资源

下载插件,plugins: 1.下载 2.引入 3.使用

  1. 下载

    #具体用法可以查找 中文文档--》插件
    #功能:默认会创建一个空的html,自动引入打包输出的所有资源(js/css)
    npm install --save-dev html-webpack-plugin
    
  2. 引入插件

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
  3. 使用

     //插件
     //将html文件打包
      plugins:[
    	new HtmlWebpackPlugin(
        {
            template:'html文件路径'
        })
      ],
    

打包图片资源

webpack5中第1步似乎不需要,有待验证;第一步为webpack4中的loader

1.下载的包:url-loader file-loader

注意:要使用多个loader处理用use,使用一个直接loader

  //主要用来处理css,img等资源
rules:[
      	{ test:/\.css$/,
          //要使用多个loader处理用use
          use:[ 'style-loader', 'css-loader' ]
        },
    	{
            //处理图片资源
            //问题:处理不了html中的img图片
            test:/\.(jpg|png|gif)$/,
            //使用一个loader
            loader:'url-loader',
            //属性配置
            options:{
                //图片大小小于8kb,就会被base64处理
                //优点:减少请求数量(减轻服务器压力)
                //缺点:图片体积会变大(文件请求速度更慢)
                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'
            },
            //如果要在webpack5中使用本loader
            type:'javascript/auto'
        }
    ]

上面的步骤可以跳过[^注意]

2.问题:处理不了html中的img图片

解决:下载html-loader

{
    test:/\.html$/,
    //处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
    loader:'html-loader'
}

打包其他资源

需要下载:file-loader

module:{
    rules:[
        //打包其他资源(除了html/js/css资源以外的资源)
        {
            //排除css/js/html资源
            exclude:/\.(css|js|html)$/,
            loader:'file-loader',
            options:{
                name:'[hash:10].[ext]'
            }
        }
    ]
}

devServer

开发服务器devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)

特点:只会在内存中编译打包,不会有任何输出

启动devServer指令为:webpack-dev-server

devServer:{
    static:{
      directory:resolve(__dirname,'build')
    },
    //启动gzip压缩
    compress:true,
    //端口   
    port:3000//自动打开浏览器
    open:true
  }

提取css到单独文件

本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。

下载:mini-css-extract-plugin

配置:

//引入
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
~
{
  test:/\.css$/,
  use:[
  //替换style-loader
  //'style-loader',
  MiniCssExtractPlugin.loader,
  'css-loader'
]},
~
 plugins:[
    new MiniCssExtractPlugin({
      //打包的css文件重命名为built.css放在css文件夹中
      filename:'css/built.css'
    }
    )
  ],    

css兼容性处理

下载:postcss-loader postcss-preset-env

查看文档postcss-loader | webpack 中文文档 (docschina.org)

帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式

webpack.config.js文件中

  rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader','css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  [
                    'postcss-preset-env',
                    {
                        // 其他选项
                    },
                  ],
                ],
              },
            },
          },
        ],

js兼容性处理(babel)

查看文档:babel-loader | webpack 中文文档 (docschina.org)

下载:babel-loader @babel/core @babel/preset-env

npm install babel-loader @babel/core @babel/preset-env -D

webpack.config.js


rules:[
   {
        test:/\.js$/,
        exclude:/node_modules/,
        loader:'babel-loader',
        options:{
          // 预设:指示babel做怎么样的兼容处理
          presets:['@babel/preset-env']
        }
      }, 
]
      

js兼容性处理:babel-loader @babel/core @babel/preset-env

  1. 基本js兼容性处理 --> @babel/preset-env 问题:只能转换基本语法,如promise高级语法不能转换,IE浏览器报错

  2. 全部js兼容性处理 --> @babel/polyfill,可以兼容ie浏览器

    下载:@babel/polyfill

    index.js

    import '@babel/polyfill'
    

    问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了

  3. 需要做兼容性处理的就做:按需加载 --> core-js

    使用第三种方案则不能使用第二种方案,需要注释import '@babel/polyfill'[^注意]

    下载:core-js

    rules:[
       {
            test:/\.js$/,
            exclude:/node_modules/,
            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'
                        }
              		}
                  ]
              ],
              
            }
          }, 
    ]
        
    

设置node.js环境变量

Tip:不是webpack的环境变量

在webpack.config.js文件中:

//注释后nodejs环境变量为production
process.env.NODE_ENV = 'development';

压缩html(webpack4)和 js

 //插件
 //将html文件打包
  plugins:[
	new HtmlWebpackPlugin(
    {
        template:'html文件路径',
        //压缩html代码
        minify:{
          //移除空格
            collapseWhitespace:true,
          //移除注释
            removeComments:true
        }
    })
  ],
  //生产环境下会自动压缩js
   mode:'production'

压缩css

webpack5中设置mode:'production'可以压缩其他资源(html...)

安装:css-minimizer-webpack-plugin

查看文档:CssMinimizerWebpackPlugin | webpack 中文文档 (docschina.org)

webpack.config.js

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
~~~
optimization: {
    minimizer: [
      // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
      // `...`,
      new CssMinimizerPlugin(),
    ],
    //在开发环境下启用css优化,不写只在生产环境中开启
    minimize:true
  },

HRM热模块替换

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有的模块),极大提升构建速度

HRM基于devserver,不适用于生产环境[^注意]

开启热模块替换webpack.config.js

devServer:{
    static:{
      directory:resolve(__dirname,'build')
    },
    compress:true,
    port:3000,
    open:true,
    // 开启HMR功能
    // 当修改了webpack配置,新配置想要生效,必须重新webpack服务
    hot:true
  }

出现的问题:

  • 样式文件:可以使用HMR功能,因为syle-loader内部实现了

  • js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR代码

    注意:HMR功能对js的处理,只能处理非入口js文件的其他文件

    解决:查看文档模块热替换 | webpack 中文文档 (docschina.org)

  • html文件:默认不能使用HMR功能,同时会导致问题,HTML文件不能热更新了(不需要做HMR功能)

​ 解决:修改entry入口,将html文件引入

Tip:该方法可以实现修改html文件刷新浏览器页面

//根据文件路径填写
//该方法可以实现修改html文件刷新浏览器页面
entry:['./src/js/index.js','./src/index.html']

source-map

source-map:一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射可以追踪源代码错误)

查看文档:Devtool | webpack 中文文档 (docschina.org)

webpack.config.js

~
devServer:{}
devtool:'source-map'

打包后生成source-map文件,例如,built.js.map

devtool有多种配置如下 [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map外部(可以提示到错误代码准确信息和源代码的错误位置)

inline-source-map内联(只生成一个内联source-map,可以提示到错误代码准确信息和源代码的错误位置) hidden-source-map外部(错误代码错误原因,但是没有错误位置;不能追踪源代码错误,只能提示到构建后代码错误位置) eval-source-map内联(每一个文件都生成对应的source-map,都在eval;错误代码准确信息和源代码的错误位置)

nosources-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(调试最友好,react和vue默认) / eval-cheap-module-source-map(新能更好)

生产环境:源代码要不要隐藏?调试要不要更友好

  • 内联会让代码体积变大,所以在生产环境不用内联

  • nosources-source-map 全部隐藏

  • hidden-source-map 只隐藏源代码,会提示构建后代码错误信息

  • 推荐配置:

    -->source-map / cheap-module-source-map

oneOf

作用:提升构建速度

查看文档:文档->配置->Module->Rule.oneOf

module:{
    rules:[
        {
            ~
        },
        {
            //以下loader只会匹配一个
            //注意:不能有两个配置处理同一种类型文件
            oneOf:[
                {test:/\.css$/}
                ~
            ]
        }
    ]
}

缓存

由于HRM只能在开发环境中使用,所以生产环境中需要以下方法:

  1. 开启babel缓存

    rules:[
       {
            test:/\.js$/,
            exclude:/node_modules/,
            loader:'babel-loader',
            options:{
              // 预设:指示babel做怎么样的兼容处理
              presets:['@babel/preset-env'],
              //开启babel缓存
              //第二次构建时,会读取之前的缓存
              cacheDirectory:true  
            }
          }, 
    ]   
    

    --> 让第二次打包构建速度更快,实测速度对比明显

  2. 资源文件缓存

    hash:每次webpack构建时会生成一个唯一的hash值。

    问题:因为js和css同时使用一个hash值。如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)

    output:{
        //添加hash值
        filename:'js/index.[hash:10].js',
        path:resolve(__dirname,'build')
      },
    
  3. chunkhash:根据chunk生产的hash值。如果打包来源同一个chunk,那么hash值就一样

    问题:js和css的hash值还是一样的,因为css是在js中被引入的,所以同属于一个chunk

    output:{
        //添加chunkhash值
        filename:'js/index.[chunkhash:10].js',
        path:resolve(__dirname,'build')
      },
    
  4. contenthash:根据文件内容生产hash值。不同文件hash值一定不一样

    output:{
        //添加contenthash值
        filename:'js/index.[contenthash:10].js',
        path:resolve(__dirname,'build')
      },
    

    --> 让代码上线运行缓存更好使用

tree shaking

作用:去除无用代码,减少打包体积

前提:

  1. 必须使用ES6模块化

  2. 开启production环境

在package.json中设置(webpack4?):

"sideEffects":false//所有代码都没有副作用(都可以进行tree shaking)
问题:可能会把css/@babel/polyfill(副作用)文件干掉
"sideEffects":["*.css","*.less"]//不再使用tree shaking

webpack5配置如下(查看文档:Tree Shaking | webpack 中文文档 (docschina.org)):

webpack.config.js

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
 + mode: 'development',
 + optimization: {
 + usedExports: true,
 +},
};

code split(代码分割)

查看文档:代码分离 | webpack 中文文档 (docschina.org)

多入口文件

webpack.config.js

entry:{
    //单入口
    entry:'./src/js/index.js'
    //多入口
    //打包生成多个js文件,打包后的文件名和index,test同名
    entry:{
        index(自定义名称):'./src/js/index.js',
        test:'./src/js/test.js'
    }
    output:{
       	//多文件入口记得配置[name]
        //[name]:去文件名
        filename:'js/[name].[contenthash:10].js',
        path:resolve(__dirname,'build')
    }
}

多入口配置将打包成两个js文件(index.js and test.js)

SplitChunksPlugin[^Tip:not install]

将node_modules中代码单独打包一个chunk最终输出(引入的loader和入口文件单独打包)

Tip:自动分析多入口chunk中,有没有公共的文件,如果有就会打包成单独一个chunk;

单入口如果引入npm包则npm包也会打包成一个chunk[^用的多]

例如,两个js文件同时引入了第三方文件(npm包),如果没有配置code split则只打包成两个文件,且都把第三方文件一同打包了;

配置后则会打包成三个文件,第三方文件单独打包了,避免了资源重复打包,第三方文件如果太小则该功能不生效

webpack.config.js

entry:{
    index:'xxxx.js',
    test:'xxx.js'
},
optimization:{
    splitChunks:{
        chunks:'all'
    }
}

通过js代码实现多入口文件功能

如果不想配置webpack.config.js,可以通过js代码,让某个文件被单独打包成一个chunk

import动态导入语法:能将某个文件单独打包

//懒加载
import(/*webpackChunkName:'test'*/ './test')
	.then(()=>{
    	//文件加载成功
	})
	.catch(()=>{
    	//文件加载失败
	})

/*webpackChunkName:'test'*/给test.js打包后的文件固定文件名

懒加载和预加载

  1. 懒加载和上面一样 --> 通过js代码实现多入口文件功能

  2. 预加载prefetch:会在使用之前,提前加载js文件

//懒加载
import(/*webpackChunkName:'test'*/ './test')
	.then(()=>{
    	//文件加载成功
	})
	.catch(()=>{
    	//文件加载失败
	})

//预加载
import(/*webpackChunkName:'test',webpackPrefetch:true*/ './test')
	.then(()=>{
    	//文件加载成功
	})
	.catch(()=>{
    	//文件加载失败
	})

pwa渐进式网络应用程序

安装:workbox-webpack-plugin

查看文档:渐进式网络应用程序 | webpack 中文文档 (docschina.org)

配置webapck.config.js

const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
~
plugins:[
    new WorkboxWebpackPlugin.GenerateSW({
        /*
        1.帮助serviceworker快速启动
        2.删除旧的serviceworker
        生成一个serviceworker配置文件
        */
        clientsClaim:true,
        skipWaiting:true
    })
]

index.js

//注册serviceworker
//sw代码必须运行在服务器上
//处理兼容性问题
if('serviceWorker' in navigator){
    navigator.serviceworker
    .register('./service-worker.js')
    .then(()=>{
        console.log('注册成功')
    })
    .catch(()=>{
        console.log('注册失败')
    })
}

如果配置了eslint会报错,eslint不能识别navigator window

解决:需要修改package.json中eslintConfig配置

package.json

"eslintConfig":{
    "env":{
    "browser":true//支持浏览器全局变量
	}
}

多进程打包

安装:thread-loader

开启多进程打包,进程启动大概为600ms,进程通信也有开销所以不要滥用。

只用工作消耗时间比较长,才需要多进程打包。

webpack.config.js

use:[
    {
        loader:'thread-loader',
        options:{
            workers:2//进程2个
        }
    }
]

查看文档:thread-loader | webpack 中文文档 (docschina.org)

Externals外部扩展

查看文档:外部扩展(Externals) | webpack 中文文档 (docschina.org)

在webpack打包的项目中使用cdn,而不是去把它打包,例如,

从cdn引入jQuery,不打包它

index.html

<script src="https://code.jquery.com/jquery.js"></script>

webpack.config.js

entry:{},
externals:{
	jquery:'jQuery',
}

index.js

import $ from 'jquery'
$('.xxx').animate()

dll

对某些库(第三方库:jquery,react,vue...)进行单独打包

查看文档:DllPlugin | webpack 中文文档 (docschina.org)

新建webpack.dll.js(自定义)

/*
使用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
    jqueryVue:['jquery','vue'],
  },
  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'
}

运行webpack.dll.js

npx webpack --config webpack.dll.js

然后打包生成了一个包含打包的jq和vue库文件还有一个manifest.json文件的dll文件夹。

配置webpack.config.js

const webpack = require('webpack')
~
plugins:[
     //告诉webpack那些库不参与打包,同时使用时的名称也要变
    new webpack.DllReferencePlugin({
      manifest:resolve(__dirname,'dll/manifest.json')
    })
]

最后打包,发现打包后的文件小了很多,打包速度也明显变快


参考:

b站尚硅谷 BV1e7411j7T5