前端面试系列之webpack优化

webpack模块打包机:分析项目结构,找到js模块以及其他 一些浏览器不能直接运行的拓展语言,将其打包成合适的格式以供浏览器使用
主要做以下的工作:

  • 代码转换:比如说TS编译成js、scss编译成css
  • 文件优化:压缩js、css、html代码,压缩合并图片
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
  • 模块合并:在采用模块化的项目里会有很多的模块和文件,需要构建功能把模块分类合并成一个文件。
  • 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器
  • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统

webpack中的术语

  • Module:module 的角色是资源的映射对象,储存了当前文件所有信息,包含资源的路径、上下文、依赖、内容等
  • Chunk:一些模块 (module) 的封装单元。且直到资源构建阶段都会持续发生变化的代码块,在此期间插件通过各种钩子事件侵入各个编译阶段对 chunk 进行优化处理。可以通过由 SplitChunksPlugin 插件根据规则与 ChunkGraph 对 Chunk 做一系列的变化、拆解、合并操作,重新组织成一批性能更高的 Chunks。后续再为它们做排序和生成hash等一系列优化处理,直到 Compiler.compile 执行完成作为资源输出(emitAssets)。
  • Bundle:是 webpack 进程执行完毕后输出的最终结果。是对 chunk 进行编译压缩打包等处理后的产出。通常与构建完成的 chunk 为一对一的关系。

总结:Chunk是过程中的代码块,Bundle是结果的代码块。

如何借助webpack优化前端性能

关于webpack的性能优化,主要体现在三个方面:
`构建性能`:是指在开发阶段的构建性能。当构建性能越高,开发效率越高。
`传输性能`:在这方面重点考虑网络中的总传输量、JS文件数量以及浏览器缓存。
`运行性能`:主要是指JS代码在浏览器端运行的速度。

1构建性能

是指在开发阶段的构建性能。当构建性能越高,开发效率高越高

1.1 减少模块解析(模板解析包括:AST抽象语法书分析、依赖分析、模板语法替换,对某个模块不进行解析,可以缩短构建时间)

 如果某个模板不做解析,该模板经过Loader处理后的代码就是最终代码。
 如果没有loader对该模块进行处理,该模块的源码就是最终打包结果的代码。
module.exports = {
    mode: "development",
    module: {
        noParse: /JQuery/
    }
}

1.2 限制loader的应用范围
针对一些第三方库,不使用loader进行处理。例如babel-loader,转换一些本身就是用ES5语法书写的第三方库,反而会浪费构建时间。
因此通过module.rules.exclude或module.rules.include,排除或仅包含需要应用loader的场景。

module.exports = {
     module: {
         rules: [
             {
                 test: /\.js$/,
                 exclude: /node_modules/,
                 //或
                 // include: /src/,
                 use: "babel-loader"
             }
         ]
     }
 }

1.3 开启多线程打包
通过thread-loader会开启一个线程池,它会把后续的loader放到线程池的线程中运行,以提高构建效率。
thread-loader可以通过测试决定放置的位置。

module.exports = {
    module: {
        rules: [{
            test: /\.js$/,
            use: [
                "thread-loader",
                "babel-loader"
            ]
        }]
    }
};

1.6 热替换

let webpack = require('webpack')
    module.exports = {
        devServer: {
            open: true,
            hot: true  //开启HMR
        },
        module: {
            rules: [{
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }]
        },
        plugins: [
              new webpack.HotModuleReplacementPlugin(),
        ]
    }

module.hot.accept()的作用是让webpack-dev-server通过socket管道,把服务器更新的内容发送到浏览器,然后,将结果交给插件HotModuleReplacementPlugin注入的代码执行插件HotModuleReplacementPlugin会根据覆盖原始代码,然后让代码重新执行。

2 传输性能

重点考虑网络中的总传输量、JS文件数量以及浏览器缓存。

2.1 分包

chunk的默认分包规则:
1. 同一个entry入口模块与他的同步依赖组织成一个chunk
2. 每一个异步模块与他的同步依赖单独组成一个chunk。其中只会包含入口chunk中不存在的同步依赖;若存在同步第三方包,也会被单独打包成一个chunk。

参考博客
buildChunkGraph(是 chunk 生成阶段)的三个子方法按顺序来详解:
1.visitModules

  • 遍历compilation.modules建立起基本的 Module Graph (模块依赖图),为遍历异步依赖(block)等所用。
  • 先处理入口 chunk 的所有同步依赖,遍历时优先将同步依赖嵌套的同步模块添加完再去处理平级的同步依赖。
  • 然后按每个异步依赖的父模块被处理的顺序依次生成异步 chunk 和 chunkGroup。
  • 然后遍历 module graph,为入口模块和它所有(直接/间接)同步依赖形成一个 EntryPoint(继承自 ChunkGroup),入口 chunk此时才会建立起与其依赖模块的联系。
  • 为所有异步模块和它的同步依赖生成一个 chunk 和 chunkGroup(会重复)。如 chunk 的同步模块已存在于入口 chunk,则不会再存入它的_modules中。此阶段初始生成了 chunk graph(chunk 依赖图)。

2.connectChunkGroups

  • 检查入口 chunk 和 有异步依赖的异步 chunk, 如果它们的子 chunk 有它们未包含的新模块,就建立它们各自所属 chunkGroup 的 父子关系。

3.cleanupUnconnectedGroups

  • 找到没有父 chunkgroup 的 chunkgroup,删除它里面的 chunk,并解除与相关 module、chunk、chunkGroup 的关系。

可以使用splitChunks来指定规则,自动分包,下面讲一下splitChunks的用法:

splitChunks主要作用是提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件。

  • chunks 选项,决定要提取哪些模块
    默认是 async :只提取异步加载的模块出来打包到一个文件中。
    异步加载的模块:通过 import(‘xxx’) 或 require([‘xxx’],() =>{}) 加载的模块。
  • initial:提取同步加载和异步加载模块;
    如果 xxx 在项目中异步加载了,也同步加载了,那么 xxx 这个模块会被提取两次,分别打包到不同的文件中。
    同步加载的模块:通过 import xxx 或 require(‘xxx’) 加载的模块。
  • all:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中;
  • minSize 选项:规定被提取的模块在压缩前的大小最小值,单位为字节;
    默认为30000,只有超过了30000字节才会被提取。
  • maxSize 选项:把提取出来的模块打包生成的文件大小不能超过maxSize值;
    如果超过了,要对其进行分割并打包生成新的文件。
    单位为字节,默认为0,表示不限制大小。
  • minChunks 选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。
  • maxAsyncRequests 选项:最大的按需(异步)加载次数,默认为 6;
  • maxInitialRequests 选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。
  • 优先级 :maxInitialRequests / maxAsyncRequests < maxSize < minSize;
  • automaticNameDelimiter 选项:打包生成的js文件名的分割符,默认为:~
  • name选项:打包生成 js 文件的名称;
  • cacheGroups 选项,核心重点,配置提取模块的方案,里面每一项代表一个提取模块的方案。下面是 cacheGroups 每项中特有的选项,其余选项和外面一致,若 cacheGroups 每项中有,就按配置的,没有就使用外面配置的;
    - test 选项:用来匹配要提取的模块的资源路径或名称,值是正则或函数;
    - priority 选项:方案的优先级,值越大表示提取模块时优先采用此方案,默认值为0;
    - reuseExistingChunk 选项:true / false。
    为true时,如果当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的 js 文件。
    - enforce选项:true / false。
    为true时,忽略minSize,minChunks,maxAsyncRequests和maxInitialRequests外面选项。下面这代码是vue-admin(github有公开代码)后台管理系统中的分块代码。
 config
    .optimization.splitChunks({
      // 针对哪些chunks进行优化,all(对所有chunk应用分包策略)
      // 比如说element-ui也属于基础类库,配置中如果不设置{chunk-ui}.priority的话,会被切分到chunk-libs中
      chunks: 'all',
      cacheGroups: {
      //会将node-modules文件夹下的模块打包进chunk-libs文件夹下。
        libs: {
          name: 'chunk-libs',
          test: /[\\/]node_modules[\\/]/,
          priority: 10,//进入分组的优先级
          chunks: 'initial' // only package third parties that are initially dependent
        },
        elementUI: {
          name: 'chunk-elementUI', // 将elementUI拆分为单个包
          priority: 20, // 权重需要大于libs和app,否则将打包到libs或app中
          //用正则表达式获取node_modules中的element-ui文件夹
          test: /[\\/]node_modules[\\/]_?element-ui(.*)/
        },
        //将src下的components组件打包进chunk-commons文件夹下,且引用超过三次的模块,需要单独拆分。
        commons: {
          name: 'chunk-commons',
          test: resolve('src/components'), 
          // 拆分前必须共享模板的最小 chunks 数。默认值为1
          // 这里主要是判断是否有多个 chunk 共享(引用)了这个 chunk,对于公共组件来说如果只有极少的 chunk 去引用,其实也是不需要进行单独拆分
          minChunks: 3,
          priority: 5,
          //遇到重复报直接引用,不用重新打包
          reuseExistingChunk: true
        }
      }
    })

2.3 代码压缩

module.exports = {
	optimization:{
		minimize:true, //是否启动压缩,默认是只在生产环境自动开启。
		minimizer:[
			//压缩时使用的插件,当你自动改压缩配置时必须配置相关压缩插件
			new TerserPlugin(),//js压缩插件
			new OptimizeCSSAssetsPlugin()//css压缩插件
		]
	}
}

2.6 gzip

const CompressionWebpackPlugin = require('compression-webpack-plugin')

module.export = {
	plugin:[
		new CompressionWebpackPlugin({
			test:/\.js$/ //针对需要预压缩的文件
			minRatio:0.5 //压缩比率
		})
	]
}

  1. 运行性能:主要指JS代码在浏览器端运行时的速度。

如何查看webpack的打包性能

在用这两个插件之前都需要先用npm进行下载
1、采用webpack-bundle-analyzer插件
可以看到 bundle 的大小以及具体的 code splitting 之后的分包情况,以便于我们对 bundle 进行量化分析。

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
.......
chainWebpack(config) {
    config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin)
}

下面的是我的一个vue项目打包后的一个性能分析。后台运行:npm run build
在这里插入图片描述
2、speed-measure-webpack-plugin(可以查看文件打包速度)

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin({
  outputFormat: 'human'
})
configureWebpack: smp.wrap({
    // ...
})

后台运行:npm run build
在这里插入图片描述

vue-admin中webpack的优化介绍

vue-admin没有了webpack.config.js文件,是直接在vue.config.js里的chainWebpack方法直接配置,这样做法的好处是用户既可以保留webpack的默认配置,又可以通过chainWebpack设置更具针对性的参数,链式的写法正是避免了用户直接面对webpack多而复杂的配置项。
当用npm run build进行打包的时候,事实上是根据cacheGroups的默认配置进来分包并包的。

1 分包策略的优化
在默认策略的基础上,进行了优化。针对体积大小、公用率、更新频率进行重新划分包。

  • 基础类库chunk-libs:主要放一些vue全家桶
  • 组件库:放element-ui
  • chunk-commen:放置全局组件

2 Preload / Prefetch
Prefetch是一种浏览器机制,浏览在下载或者预取用户可能要访问的文档。具体来说是通过 来实现预提取,设置 prefetch 的影响就是会降低首屏打开的速度,因为浏览器去需要加载这个资源。在 webpack4 中是默认开启 preferch 的,在首屏 index.html中会把十几个页面路由文件,一口气下载下来,可以看到打包后的 index.html中加载了非常多没有意义的 js 和 css 文件。
为了提升首屏打开速度,一般会关闭 prefetch 这个功能,这样首屏只会记载当前页面路由的组件。当然如果真的有大概率即将被访问的资源也可以使用 prefetch 配置来提升性能和体验。

module.exports = { 
  chainWebpack(config) { 
    // ...
    
    // when there are many pages, it will cause too many meaningless requests
    config.plugins.delete('prefetch')
  }
}

Preload也是使用来实现预下载.

  • preload用来声明当前页面的关键资源,强制浏览器尽快加载;
  • prefetch用来声明将来可能用到的资源,在浏览器空闲时进行加载
 config.plugins.delete('prefetch')
 config.plugin('preload').tap(() => [
      {
        rel: 'preload',
        //限制比如 runtime 等文件可以不用预下载的文件,以提升首屏打开速度。
        fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
        include: 'initial'
      }
    ])

总结:

  • 在默认策略的基础上,进行了优化。将一些vue全家桶、全局组件、Element-UI单独分包。
  • 合理使用Proload以及Prefetch,关闭了Prefetch,提高了首屏打开的速度。针对Proload则,规定某些文件除外,不需要在首屏打开时预加载。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端面试中,关于webpack的问题通常包括以下几个方面: 1. webpack的基本配置:这个问题涉及到webpack的入口、输出、加载器、插件等基本配置项。可以参考引用中的相关内容进行回答。 2. 如何利用webpack优化前端性能:这个问题主要是针对如何通过webpack进行性能优化,包括代码分割、懒加载、缓存等方面。可以通过使用插件和配置优化策略来实现。具体的优化方法可以参考引用中的相关内容进行回答。 3. 提高webpack的构建速度:这个问题涉及到如何提高webpack的构建速度,包括优化配置、使用缓存、并行处理等方面。可以参考引用中的相关内容进行回答。 4. 如何在vue项目中实现按需加载:这个问题主要是针对在vue项目中使用webpack进行按需加载,可以使用webpack的code splitting功能和Vue的异步组件来实现。具体的实现方法可以参考引用中的相关内容进行回答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [前端面试webpack](https://blog.csdn.net/Lycoriy/article/details/105835946)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [前端面试--webpack](https://blog.csdn.net/weixin_47964837/article/details/124557803)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值