Webpack知识体系

什么是Webpack?

前端项目由资源构成:PNG JPG GIF JS TS css Less Sass…
在这里插入图片描述
在这里插入图片描述
Webpack做的两件事:模块化+一致性:
在这里插入图片描述

Webpack打包核心流程

如何使用Webpack
1,安装依赖 2,编辑配置文件webpack.config.js:运行webpack指令时,会加载里面的配置。另外因为所有构建工具都是基于nodejs平台运行的 ,模块化默认采用commonjs。module.exports={} 3,执行编译命令
在这里插入图片描述
配置项:entry:定义当前项目的入口,构建内部依赖图 ;output:定义打包完项目bundle的文件名及放到哪里;loader(在module里定义)(非js资源转为js。webpack自身只理解js,webpack能处理js/json,不能直接处理img、css等别的资源);plugin(执行范围更广的任务:如用于打包优化和压缩);mode(‘development’,‘production’)。
核心流程
在这里插入图片描述
webpack详细工作过程(面试常问!)
在这里插入图片描述
所有资源都会输出到output对象下面path所规定的文件夹如path:resolve(__dirname,‘build’),入口文件index.js也是,filename:‘js/bundle.js’。css资源会和index.js输出到一起build/js/,别的图片啊之类的资源输出到build文件夹下。
关于Webpack使用方法,基本围绕配置展开,配置大致分为两类

  • 流程类:作用于流程中(参考核心流程图)某个or若干个环节,直接影响打包效果的配置项
  • 工具类:主流程之外,提供更多工程化能力的配置项
    在这里插入图片描述
    在这里插入图片描述

Webpack开发环境基本配置(mode:‘development’)

使用Webpack——处理样式资源
利用module属性里的rules添加loader(test表示匹配哪些文件,用use定义处理这种文件使用哪些loader,loader执行顺序自下到上)
在这里插入图片描述
在这里插入图片描述
使用Webpack——生成HTML
用的是插件Plugin,可以自动生成一个HTML文件,自动引入打包的资源
在这里插入图片描述
在这里插入图片描述
使用Webpack——处理图片资源

{
	test:/\html$/,
	loader:'html-loader',
}
{
    test:/\.(jpg|png|gif)/,
    loader:'url-loader',
    options:{
        limit:8*1024,
        name:'[hash:10].[ext]',
        outputPath:'imgs',
        esModule:false,
    }
}

使用Webpack——打包其他资源

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

Webpack生产环境基本配置(mode:‘production’)

像代码压缩、css与js分离、兼容性问题这些事情如果都放到开发环境去做,会影响打包的速度和开发的效率,因此要放到生产环境去做,使得项目性能更好,更快更平稳、兼容性更好。
处理样式文件
1,提取css为单独文件:

npm i mini-css-extract-plugin -D
new MiniCssExtractPlugin({filename:'css/built.css'})//对输出css文件重命名
再用MiniCssExtractPlugin.loader取代style-loader(link标签ref引入,不需要style标签),保留'css-loader'

2,css兼容性处理

npm i postcss-loader postcss-preset-env -D
可以看脚手架里package.json的browserslist

3,压缩css

npm i optimize-css-assets-webpack-plugin -D
new OptimizeCssAssetsWebpackPlugin();

处理js
1,js语法检查eslint:

npm i eslint eslint-loader -D
rules:[
    {
        test:/\.js$/,
        exclude:/node-modules/,//只检查自己写的源代码,不检查第三方库里的代码
        enforce:'pre',
        loader:'eslint-loader',
        options:{fix:true}
    }
]

并在package.json的eslintConfig中设置语法检查规则(脚手架默认设置好了)
2,js兼容性处理:
用babel-loader处理js(如ES6转换为ES5)
在这里插入图片描述
在这里插入图片描述
但是只用上面的Babel对于promise等这些高级语法还是不能转换,import @babel/pollyfill又会一下子把所有兼容性代码引入,体积太大。这时可以用core-js按需加载。npm i core-js -D
3,压缩js:

只需要将mode调为'production',因为生产环境下会自动压缩js代码

处理html
压缩html:

new HtmlWebpackPlugin({
    minify:{
        //移除空格
        collapseWhitespace:true,
        //移除注释
        removeComments:true,
    }
})

Webpack性能优化配置:

  • 开发环境性能优化(优化打包构建速度,优化代码调试);
  • 生产环境性能优化(优化打包构建速度,优化代码运行的性能)

一:开发环境性能优化
优化打包构建速度——HMR(模块热替换):
不需要浏览器刷新,一个模块更新只会重新打包这个模块而不是重新打包所有,提升了构建速度。用到了devServer配置。
这里先介绍一下开发服务器devServer:用来自动化(自动编译、自动打开浏览器、自动刷新浏览器)。特点:只会在本地内存中编译打包,不会有任何输出,即本地看不到打包后的build文件夹,在内存中,停止服务器后自动删除。webpack命令会输出文件,webpack-serve命令不会。

npm i -D webpack-dev-server//自动起一个webpack服务器并打开浏览器,且自动编译
module.exports={
    mode:'development',
    devServer:{
    	//项目构建后路径
        contentBase:resolve(__dirname,'build'),//本地看不到打包后的build文件夹,在内存中,停止服务器后自动删除。
        //启动gzip压缩
        compress:true,
        //端口号
        port:3000,
        //自动打开浏览器
        open:true,
        //开启HMR
        hot:true,
    }
}

启动devServer指令为:

npx webpack serve

CSS的style-loader中就已经自动处理了样式文件的热更新,js需要自己写。
当然,Vue、React脚手架搭建的项目,貌似是可以实现JS热替换的,框架内部本身就实现了替换操作。
优化代码调试——source-map
提供源代码到构建后代码映射的技术(如果构建后代码出错了就可以定位到源代码错误位置)

module.exports={
    devtool:'source-map'
}

在这里插入图片描述
补充:内联会让代码体积庞大,所以生产环境不用会内联的。
具体用哪个前缀,要考虑到开发环境和生产环境:开发环境需要速度快,调试友好;生产环境需要考虑源代码隐藏,调试要不要友好。
在这里插入图片描述
react脚手架中默认开发环境用的是cheap-module-source-map,生产环境用的是source-map。
二:生产环境性能优化
oneOf
每个文件都会被所有loader过一遍,耗费性能。给rules最外面加上oneof,这样每个文件只会被一个loader处理。注意:用的时候不能让一个文件同时匹配多个loader。用oneOf可以优化生产环境打包构建速度。
缓存
babel缓存:不可能改一点js再重新对所有js文件进行编译,且在生产环境下又用不了HMR,所以用缓存。在之前babel-loader的options里添加cacheDirectory:true就行,这样第二次构建时会读取缓存,提高第二次构建时速度。
文件资源缓存:服务器设置缓存规则为强缓存且没过期时,如果代码改了就算重新打包也还是用的之前的缓存,这时可以给output里filename设置文件哈希值,文件名字改了,就不会用缓存了。缺点:比如只改动了js文件没改样式文件,但重新打包会导致所有缓存都失效。
hash:每次webpack构建时都会生成一个唯一的hash值;
chunkhash:根据chunk生成hash,如果打包来源于同一个chunk(所有根据入口文件引入的文件都在一个chunk里),那么hash值就一样;
contenthash:根据文件的内容生成hash。所以用这个。
webpack5统一在cache:{}里配置缓存。缓存将输出到node_modules/.cache/webpack。
Tree-Shaking:
用于删除Dead Code(如代码没被用到、代码执行结果没被用到、定义的模块没被引用)。作用:减小代码体积。
使用前提:1,使用ES6 module(webpack5对于commonjs也能进行treeshaking了) 。 optimazition.usedExports:true。2,开启mode:‘production’(这时会自动启用一个插件)
如果有不想删除的代码,可以写到sideEffect:[]里。
代码分割code split
代码分割可以将代码打包输出为多个js文件,从而实现并行加载。
法一:可以将node_modules中的代码单独打包为一个chunk输出。且会自动分析多文件入口有没有公用某一个依赖,会将这个依赖打包为单独的一个chunk。

module.exports={
    optimization:{
        splitChunks:{
            chunks:'all'
        }
    },
    mode:'production'
}

法二:import()动态导入语法能将某个文件单独打包。
ES6标准引入了import以方便我们静态加载模块: import xxx from xxx.js尽管import对于我们加载模块很有帮助,但是静态加载模块的方式一定程度上限制了我们来实现异步模块加载,动态import()提供了基于Promise的API,因此,import()的返回值是一个完成状态或拒绝状态的Promise对象,形式如:

import(/* webpackChunkName: 'module'*/ "module") 
.then(() => {        //todo    })   
.catch(_ => console.log('It is an error'))

webpack在编译时,识别到动态加载的import语法,则webpack会为当前动态加载的模块创建一个单独的bundle。如果你使用的是官方的Create-react-app脚手架,那么可以直接使用动态import语法。如果你的脚手架是你自己配置的webpack,那么你需要按照官方指南来设置。
懒加载和预加载
不是图片懒加载,而是js文件的懒加载。运用代码分割的思路,用import()动态引入,按需加载模块。
正常加载:并行加载多个资源
懒加载:按需加载,用到再加载
预加载:等浏览器空闲了再偷偷加载
PWA渐进式网络应用程序
ServiceWorker。让pc端网页能像app那样,即使没有网,也能显示。workbox-webpack-plugin
此外,还有多进程打包、externals(不打包某个库,即不需要打包,通过CDN引进来)、dll(某些库需要打包,可以对其单独打包,打包一次下次引进来) etc。。。此外一些更详细的配置信息用到的时候参考官方文档

Loader组件

Loader有什么作用?用来在Webpack中处理不同的资源的组件,是一个资源翻译模块,做一个内容的转化(因为Webpack只认识js)用于将非标准js资源翻译为标准js资源。
为什么处理CSS需要用到less-loader、css-loader、style-loader? less-loader实现less到css的转换;css-loader是为了让webpack认识.css文件,变成commonjs模块,将css包装成类似module.exports="${css}"样式字符串的内容,包装后的内容符合js语法(将css整合到js中);style-loader是把css模块包进require语句,在运行时调用injectStyle等函数将内容注入页面style标签中,挂到dom上。
链式调用:前面loader的输出会变成下一个loader的输入。
在这里插入图片描述
在这里插入图片描述
Webpack常用Loader:
在这里插入图片描述
在这里插入图片描述
自定义loader:
loader本身是一个函数,loader的执行顺序是自下向上,Loader执行有同步和异步执行两种方式(推荐异步loader,将处理后的结果传给callback)。
在这里插入图片描述

Plugin组件

插件是什么?在Webpack整个生命周期都生效的组件。插件就是在某些钩子函数中调用,调用的时候去修改/添加相关的资源,从而使webpack打包时输出资源变化。插件架构精髓:对扩展开放,对修改封闭。Webpack很多功能配置都是基于插件实现的。
react中崇尚的是异步钩子。
使用:装插件(npm) 引入插件(require) new插件实例
在这里插入图片描述
Webpack常用Plugin
在这里插入图片描述
自定义Plugin:
首先,每个插件都是一个类/构造函数(因为是通过new调用)。
apply 方法:这个方法会在 Webpack 启动时被调用,它接收一个 compiler 对象参数,这个对象是 Webpack 工作过程中最核心的对象,里面包含了我们此次构建的所有配置信息,我们就是通过这个对象去注册钩子函数。
compiler是webpack的主要引擎,compiler里面包含了一些钩子(生命周期函数),即webpack在执行的时候会依次触发的各种函数。在触发的时候,compiler里面会创建compilation对象。compilation对象里面包含了很多属性和方法,可以对打包资源做各种操作。同时它也像compiler一样有很多钩子函数。
自定义一个插件,实现往打包的输出文件中再添加一个文件输出的功能:

class MyPlugin {
  apply(complier) {
  //初始化compilation钩子
    complier.hooks.thisCompilation.tap("MyPlugin", (compilation) => {//注册钩子thisCompilation并通过thisCompilation去绑定
      console.log(compilation);
      //实现添加资源功能
      compilation.hooks.addtionalAssets.tapAsync('MyPlugin',(cb)=>{
      		console.log(compilation);//
      		const content='hello plugin';
      		//往要输出的资源中添加a.txt输出
      		compilation.assets['a.txt']={
      			size(){return content.length;};//代表文件大小
      			source(){return content};//文件具体内容
      		}
      	cb();
      })
      })
  }
}
module.exports = MyPlugin;
class RemoveCommentsPlugin {
  apply(complier) {
    console.log("RemoveCommentsPlugin Launch.");
    complier.hooks.emit.tap("RemoveCommentsPlugin", (compilation) => {
      // compilation => 可以理解为此次打包的上下文
      for (const name in compilation.assets) {
        console.log(name);
        if (name.endsWith(".js")) {
          const contents = compilation.assets[name].source()
          const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
          compilation.assets[name] = {
            source: () => withoutComments,
            size: () => withoutComments.length
          }
        }
      }
    });
  }
}

module.exports = RemoveCommentsPlugin;

loader和plugin的区别(面试常问)
loader就是对资源内容做一个转译,把非js的资源转化为js资源,只在模块加载环节工作;plugin没有一个明确的输入输出,它作用于webpack整个生命周期,一个在任意时间点修改webpack内部状态来修改webpack运行流程。

面试够用知识体系

在这里插入图片描述
require和import导入在Webpack使用中有区别吗
require是CMD规范(动态灵活),import是ES6 Module的规范(静态)
Webpack与vite的区别,相比于vite、esbuild,Webpack的优势在哪里
vite最大的优势是速度很快,因为它不需要把东西全部打包在一起(即跳过了bundle的流程),vite对开发环境友好,但是对于生产环境会有一个兼容性的问题。
ESBuild只完成了Webpack一部分的工作,即打包。tree-shaking和HMR他都不支持。
Webpack生态更完善,更成熟,发展了很多年,对兼容性要求高的项目一般都会选择webpack来做。编译较慢。
Webpack5做了哪些更新
通过持久化缓存提高性能,采用更好的持久化缓存算法
通过优化 Tree Shaking 和代码生成来减小Bundle体积
提高 Web 平台的兼容性
清除之前为了实现 Webpack4 没有不兼容性变更导致的不合理 state

打包体积优化

首先npm run eject查看webpack.config.js.

npm i -D webpack webpack-cli
npm install --save-dev webpack-bundle-analyzer

在module.exports中的plugins中添加一个新的plugin:
BundleAnalyzerPlugin:分析包大小,包分析以后从体积大的包入手。

const BundleAnalyzerPlugin=require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports={
plugins:[
 new BundleAnalyzerPlugin({analyzerPort:3000}),
]
}
npm run buid

自动在浏览器打开一个分析的文件。
具体优化方案
按需引入,
tree-shaking(尽量引入包含 esmodule 模块的包,让 webpack 能够对没用到的代码进行 tree-shaking),
css压缩(mini-css-extract-plugin react已经配置好了),
移除注释(htmlWebpackPlugin react也已经做好了)
以及react 引入cdn:

module.exports={
    externals:{
      react: 'React',
      'react-dom': 'ReactDOM',
      'react-router-dom': 'ReactRouterDOM',
      'antd': 'antd',
      'redux': 'Redux',
      'axios': 'axios'
    },
 }

在index.html中:

    <div id="root"></div>
    <link rel="stylesheet" href="https://unpkg.com/antd@4.16.13/dist/antd.min.css"><link rel="text/less" href="https://unpkg.com/antd@4.16.13/dist/antd.less">

    <!-- react相关 -->
    <script crossorigin src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-router-dom/5.3.0/react-router-dom.min.js"></script>
    <script src="https://unpkg.com/redux@4.0.1/dist/redux.js"></script>
    <!-- antd -->
    <script crossorigin src="https://unpkg.com/antd@4.16.13/dist/antd.min.js"></script>
    <!-- G2Plot -->
    <script type="text/javascript" src="https://unpkg.com/@antv/g2plot@2.3.33/dist/g2plot.min.js"></script>
    <!-- axios -->
    <script type="text/javascript" src="https://unpkg.com/axios/dist/axios.min.js"></script>
  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值