dev 流程解析
主流程
function createApp(options) {
logger.wait("Extracting site metadata...");
return new App(options);
}
async function dev(options) {
const app = createApp(options);
await app.process();
return app.dev();
}
简单来说就是根据配置创建 app 实例,app.process() 解析项目,最后 dev 执行 webpack-dev-server
new App
constructor (options = {}) {
this.options = options
this.sourceDir = this.options.sourceDir || path.join(__dirname, 'docs.fallback')
// debug sourceDir /Users/**/Documents/work/workspace/vuepress-analysis/docs
logger.debug('sourceDir', this.sourceDir)
// debug Temp directory: repp.../node_modules/@vuepress/core/.temp
// writeTemp 是个写文件fn, 中间做了缓存判断
const { tempPath, writeTemp } = createTemp(options.temp)
this.tempPath = tempPath
this.writeTemp = writeTemp
this.vuepressDir = path.resolve(this.sourceDir, '.vuepress')
this.libDir = path.join(__dirname, '../')
}
这里主要做了基础文件路径的配置, 并定义了.temp
的位置
app.process()
async process () {
// 相关属性初始化
// this.siteConfig = {
// "title": "...",
// "description": "...",
// "base": "...",
// "themeConfig": {
// ...
// }
// }
this.resolveConfigAndInitialize()
// 注入head的url标签配置
this.normalizeHeadTagUrls()
// 读取theme 默认读取 repo/node_modules/@vuepress/theme-default
this.themeAPI = loadTheme(this)
// 验证一遍模板文件是否存在
// debug SSR Template File: repo/node_modules/@vuepress/core/lib/client/index.ssr.html
// debug DEV Template File: repo/vuepress-analysis/node_modules/@vuepress/core/lib/client/index.dev.html
this.resolveTemplates()
// 验证一遍全局的layout文件是否存在
// debug globalLayout: repo/node_modules/@vuepress/core/lib/client/components/GlobalLayout.vue
this.resolveGlobalLayout()
// 配置内容插件
this.applyInternalPlugins()
// 配置用户插件
this.applyUserPlugins()
// 插件初始化 整理好加入到pluginApi的options中
this.pluginAPI.initialize()
// 采用了markdown-it和markdown-it-chain 返回markdown实例,单独有createMarkdown章节
this.markdown = createMarkdown(this)
// 详见 resolvePages 章节
await this.resolvePages()
// 运行所有的默认plugin
await this.pluginAPI.applyAsyncOption('additionalPages', this)
await Promise.all(
this.pluginAPI
.getOption('additionalPages')
.appliedValues.map(async options => {
await this.addPage(options)
})
)
await this.pluginAPI.applyAsyncOption('ready')
await Promise.all([
this.pluginAPI.applyAsyncOption('clientDynamicModules', this),
this.pluginAPI.applyAsyncOption('enhanceAppFiles', this),
this.pluginAPI.applyAsyncOption('globalUIComponents', this)
])
}
插件的管理,pages 页面生成,创建 markdown 预设,最后逐渐开始执行插件
插件
app.dev()
async dev () {
this.isProd = false
// TODO: 详细分析DevProess
this.devProcess = new DevProcess(this)
// 执行dev过程
await this.devProcess.process()
// 如果存在文件改动,则运行createServer
// TODO: 这里的filechange机制有点搞不懂
const error = await new Promise(resolve => {
try {
this.devProcess
.on('fileChanged', ({ type, target }) => {
console.log(
`Reload due to ${chalk.red(type)} ${chalk.cyan(
path.relative(this.sourceDir, target)
)}`
)
this.process()
})
// 配置完成之后 createServer,启动项目
.createServer()
.listen(resolve)
} catch (err) {
resolve(err)
}
})
if (error) {
throw error
}
return this
}
这块逻辑主要是 webpack 的所有配置的生成,并执行 webpack-dev-server
总结
流程大致解析:
- 解析基础配置
- 加载模版、主题、布局
- 插件配置组合,初始化
- markdown 配置
- 解析并执行插件
- 启动 server
所有的 confin.js 的配置在 3 个部分被解析,1 解析基础配置,2 解析 themeconfig 和 components;5 解析 plugins
plugin 在第 5 步的原因是将所有执行的步骤都变成了插件;所以可以理解为 1~4 都是在解析配置,只有 5,6 是真正在执行 webpack 操作