Webpack动态加载与代码分割策略详解

作者:工具箱

时间:2026-10-26

浏览:192

本文深入解析Webpack的动态加载机制与代码分割策略。详细对比sync、lazy、eager等六种Context Module类型,阐述require.context()与import()函数的用法差异,并结合环境变量实现条件打包,帮助开发者优化前端资源加载性能。

文章目录

Webpack代码拆分

Webpack提供两种组织模块依赖的方式:同步与异步。其中,异步依赖被用作分割点,从而形成新的代码块。在优化依赖树之后,每个异步区块都会作为一个独立的文件进行打包。

上下文模块(Context Module)

上下文模块包含六种类型,分别为:sync、lazy、lazy-once、eager、weak 以及 sync-weak。

  • sync:直接将当前文件打包,以同步方式加载并执行。
  • lazy:为每个通过 import() 导入的模块生成一个可延迟加载(lazy-loadable)的代码块(chunk)。
  • lazy-once:生成一个单一的、可满足所有 import() 调用的可延迟加载代码块。
  • 该代码块将在第一次 import() 调用时获取,后续的 import() 调用将复用相同的网络响应。需注意,此模式仅在动态语句中有效,例如 import(./locales/${language}.json),其中可能包含多个被请求的模块路径。
  • eager:不会分离出独立的 chunk 文件,但会返回一个 Promise。只有当调用该 Promise 时代码才会执行,这相当于先加载代码但允许控制其延迟执行。
  • 此模式会阻止 Webpack 生成额外的 chunk。所有导入的模块均包含在当前 chunk 中,因此无需发送额外的网络请求。它仍然返回一个 Promise,但该 Promise 会被自动解析(resolved)。使用 eager 模式的动态导入与静态导入的区别在于,整个模块的代码仅在 import() 调用之后才会执行。

示例:假设当前 Vue 文件为 Home.vue,其同级目录下有一个名为 pages 的文件夹,其中包含 a.vue、b.vue 和 c.vue 三个文件。

// 把Home, a, b, c分别打包到不同的chunk中
import('./pages/' + pageId)

// 把Home打成一个chunk,a/b/c统一打包到一个chunk
import(/* webpackMode: "lazy-once" */ './pages/' + pageId)

// 把Home, a, b, c打包到一个chunk中,且a/b/c中的代码只有在import之后执行(不在chunk加载时执行)
import(/* webpackMode: "eager" */ './pages/' + pageId)

注意:即使代码中并未引用 pages 目录下的某个文件,Webpack 仍会将其打包!

通过上下文组件请求的实际路径是相对于指定目录的相对路径;在此例中,相对于 ‘/pages’ 上下文的路径分别为 ‘./a.vue’、‘./b.vue’ 和 ‘./c.vue’。

生成 Context Module 的方式

require(表达式)

require(./locale/${language}.js) 会将匹配到的文件打包进入最终的 bundle 中。

require.context() 函数

require.context(
  (directory: String),
  (includeSubdirs: Boolean) /* 可选的,默认值是 true */,
  (filter: RegExp) /* 可选的,默认值是 /^.\/.*$/,所有文件 */,
  (mode: String)  /* 可选的, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默认值是 'sync' */

一个 context module 会导出一个(require)函数,该函数接收一个参数:request。

此导出函数具备三个属性:resolve、keys 和 id。

  • resolve 是一个函数,它返回 request 被解析后得到的模块 id。
  • keys 也是一个函数,它返回一个数组,由所有可能被此 context module 处理的请求组成。
  • id 是 context module 的模块 id,在使用 module.hot.accept 时可能会用到。

示例:

在与当前文件 index.js 同级的 modules 目录下,存在 aaa/index.js 和 bbb/index.js 两个文件。

const modulesFiles = require.context('./modules', true, /index.js$/)
console.log('modulesFiles:', modulesFiles)
// ƒ webpackContext(req) {
//     var id = webpackContextResolve(req);
//     return __webpack_require__(id);
// }
console.log('modulesFiles.keys:', modulesFiles.keys()) // ['./aaa/index.js', './bbb/index.js']

console.log('modulesFiles.resove:', modulesFiles.resolve('./bbb/index.js')) // ./src/store/modules/bbb/index.js

console.log('modulesFiles.id:', modulesFiles.id) // ./src/store/modules sync recursive index.js$

export const modules = modulesFiles.keys().reduce((res, modulePath) => {
  const pathName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const moduleName = pathName.replace('/index', '')
  const module = modulesFiles(modulePath)
  const dModuleName = module.default.name || ''
  console.log('modulePath:', modulePath)
  console.log('moduleName:', moduleName)
  console.log('module:', module)
  res[dModuleName || moduleName] = module.default
  console.log('res:', res)
  return res
}, {})

import() 函数

import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */ /* 'eager' | 'weak' | 'lazy' | 'lazy-once',默认值是 'lazy' */
/* webpackPrefetch: true */
/* webpackPreload: true */
/* webpackIgnore: false */ /* 将 webpackIgnore 设置为 true 则不进行代码分割 */
`./locale/${language}`
)

require.ensure() 函数

require.ensure() 已被 import() 取代。

含有环境变量的动态加载

代码运行时通过判断环境变量(例如 process.env.NODE_ENV === 'production'),只会打包满足判断条件逻辑分支的请求。

export function getBaseUrl () {
  if (process.env.NODE_ENV === 'production') {
    require('@/prod.host.js')
    return '/performance/'
  } else {
    const requireHostFiles = require.context('@/api', false, /devHost.js$/)
    return requireHostFiles.keys().includes('./devHost.js') ? requireHostFiles('./devHost.js').devHost : require('@/dev.host.js').devHost
  }
}

如果 process.env.NODE_ENV === 'production',则只会打包 ‘@/prod.host.js’。

否则,会打包 ‘@/api’ 下直属子文件中满足 /devHost.js$/ 正则表达式的文件,以及 ‘@/dev.host.js’。

到此这篇关于webpack动态加载与打包方式的文章就介绍到这了,更多相关webpack动态加载内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章