# optimizeDeps 方法
const { root, logger, cacheDir } = config
const log = asCommand ? logger.info : debug
从 config 解析的配置中获取参数。
root项目的配置根目录,默认process.cwd()logger提示工具cacheDir缓存目录
如果是通过命令行调用,提示使用 logger 工具中的 info 方法进行输出。
# 判断是否有 cacheDir 缓存目录
if (!cacheDir) {
log(`No cache directory. Skipping.`)
return null
}
如果没有缓存目录, 则直接跳过。
# 缓存相关依赖元信息文件地址
const dataPath = path.join(cacheDir, '_metadata.json')
dataPath 是 esbuild 构建完生成依赖元信息和 hash、browserHash存储的文件地址,存在缓存目录下的 _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 的内容变更会影响构建结果是否符合预期, 同时 mode、root、resolve、assetsInclude、plugins、optimizeDeps的 viteConfig 中的配置变动也会存在影响。
生成 hash 前再对配置进行 JSON.stringify 的时候将 函数 或者 正则 进行 string 化处理。
import { createHash } from 'crypto'
最后调用 Node中crypto 模块中的 createHash 方法生成 8 位 hash,来确保上次构建的结果否与现在保持同样的预期结果, 减少不必要的再次构建。
# 确定构建内容元数据和 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 })
}
运行到这里, 说明两个情况。
- 对
server.force设置了true。 - 两个前后两个
hash保持不一致, 说明.lock文件或者viteConfig配置文件内容有变更。
如果存在缓存目录,说明之前已经存在构建的情况,不是首次启动服务器,对缓存目录进行清空。否则递归创建缓存目录文件夹。用来后续建构结果元信息和 hash 进行存储。