# optimizeDeps 方法

const { root, logger, cacheDir } = config
const log = asCommand ? logger.info : debug

config 解析的配置中获取参数。

  1. root 项目的配置根目录,默认 process.cwd()
  2. logger 提示工具
  3. cacheDir 缓存目录

如果是通过命令行调用,提示使用 logger 工具中的 info 方法进行输出。

# 判断是否有 cacheDir 缓存目录

if (!cacheDir) {
  log(`No cache directory. Skipping.`)
  return null
}

如果没有缓存目录, 则直接跳过。

# 缓存相关依赖元信息文件地址

const dataPath = path.join(cacheDir, '_metadata.json')

dataPathesbuild 构建完生成依赖元信息和 hashbrowserHash存储的文件地址,存在缓存目录下的 _metadata.json 中。

# 生成比对是否变更构建 hash

const mainHash = getDepHash(root, config)

const lockfileFormats = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml']

let cachedHash: string | undefined

function getDepHash(root: string, config: ResolvedConfig): string {
  if (cachedHash) {
    return cachedHash
  }
  let content = lookupFile(root, lockfileFormats) || ''
  // also take config into account
  // only a subset of config options that can affect dep optimization
  content += JSON.stringify(
    {
      mode: config.mode,
      root: config.root,
      resolve: config.resolve,
      assetsInclude: config.assetsInclude,
      plugins: config.plugins.map((p) => p.name),
      optimizeDeps: {
        include: config.optimizeDeps?.include,
        exclude: config.optimizeDeps?.exclude
      }
    },
    (_, value) => {
      if (typeof value === 'function' || value instanceof RegExp) {
        return value.toString()
      }
      return value
    }
  )
  return createHash('sha256').update(content).digest('hex').substr(0, 8)
}

变量 mainHash 是由 getDepHash 方法生成,方法传入参数 root根目录config配置文件信息

# getDepHash 生成 mainHash 函数

声明 cachedHash 的变量, 如果有 cachedHash 则返回 cachedHash, 否则进行生成。 通过寻找锁文件来确定 node_modules 信息内容, 赋值到content变量上。是通过 utils 文件里 lookupFile 方法,以 root 配置的目录开始向上寻找。

# 理解注释

// also take config into account
// only a subset of config options that can affect dep optimization

不但 node_modules 的内容变更会影响构建结果是否符合预期, 同时 moderootresolveassetsIncludepluginsoptimizeDepsviteConfig 中的配置变动也会存在影响。

生成 hash 前再对配置进行 JSON.stringify 的时候将 函数 或者 正则 进行 string 化处理。

import { createHash } from 'crypto'

最后调用 Node中crypto 模块中的 createHash 方法生成 8hash,来确保上次构建的结果否与现在保持同样的预期结果, 减少不必要的再次构建。

# 确定构建内容元数据和 hash 的存储格式

const data: DepOptimizationMetadata = {
  hash: mainHash,
  browserHash: mainHash,
  optimized: {}
}

构建元信息讲解请关联这里~

# 是否需要预构建

 if (!force) {
    let prevData
    try {
      prevData = JSON.parse(fs.readFileSync(dataPath, 'utf-8'))
    } catch (e) {}
    // hash is consistent, no need to re-bundle
    if (prevData && prevData.hash === data.hash) {
      log('Hash is consistent. Skipping. Use --force to override.')
      return prevData
    }
  }

# server.force API

设置为 true 强制使依赖预构建。在重新启动服务时, 无论任何情况下, 都将对预构建内容和元数据进行重新生成。这往往在调试当中可以解决相应的问题。 当对构建的内容进行改动时, 重启服务后,config 配置和 .lock 文件并没有改动。同时hash 也不会变更, 将会跳过重新构建。此时就达到不到预期的结果。可以通过 force 每次启动重新构建。

# 理解注释

// hash is consistent, no need to re-bundle

声明 preData 变量, 存储读取缓存目录中的元数据内容。如果有元数据内容并且与上次生成的 hash 和此次启动生成的 hash 一致的情况下, 直接返回元数据内容。optimizeDeps 函数最后返回的就是构建后元数据内容。

# 清空, 生成缓存目录

  if (fs.existsSync(cacheDir)) {
    emptyDir(cacheDir)
  } else {
    fs.mkdirSync(cacheDir, { recursive: true })
  }

运行到这里, 说明两个情况。

  1. server.force 设置了 true
  2. 两个前后两个 hash 保持不一致, 说明 .lock 文件或者 viteConfig 配置文件内容有变更。

如果存在缓存目录,说明之前已经存在构建的情况,不是首次启动服务器,对缓存目录进行清空。否则递归创建缓存目录文件夹。用来后续建构结果元信息和 hash 进行存储。