从 9 月份开始,vuepress 源码进行了从新设计和拆分。先是开了个 next 分支,后来又合并到 master 分支,为即将发布的 1.x 版本作准备。html
最主要的变化是:大部分的全局功能都被拆分红了插件的形式,以可插拔的方式来支撑 vuepress 的运做,这一点很像 webpack。前端
具体架构以下: vue
从图中咱们能够看出,vuepress 被划分红了两个部分:前端部分和服务端(Node.js)部分。node
在这个架构中,主题即插件。也就是说使用(开发)一个主题和使用(开发)一个插件的方式几乎一致。webpack
根据这个架构,vuepress 的插件即可以作不少事情了。具体用法能够参考文档。git
让咱们先来了解一下 vuepress 的内部插件和官方插件都有些什么,借助插件机制作了哪些事情。github
全局加强:默认用来实现全局应用加强的逻辑。 它使用 enhanceAppFiles 指定加强全局应用和主题的文件路径。凭着这个,vuepress 就能准确地找到你全局加强或是主题的文件所在地。web
布局组件:默认提供的布局组件。 它使用 clientDynamicModules 来实现动态引入布局相关的组件。vue-router
页面组件:默认提供的页面组件(布局组件的子组件)。 它使用 clientDynamicModules 来实现动态引入页面相关的组件。npm
根组件混入:默认往根组件混入的逻辑。 它使用 clientDynamicModules 来实现动态混入元信息。包括根组件的标题、语言等。
路由:默认的生成路由逻辑。 它使用 clientDynamicModules 来实现动态注册路由。咱们的 markdown 文件在转换成 vue 组件后就是经过它自动注册到 vue-router 的。
站点数据:默认的生成站点数据逻辑。 它使用 clientDynamicModules 来实现生成全局站点数据。咱们在页面里拿到的全局计算属性 $site 就是这样来的。
模块化转化:将 cmd 代码转成 esm 代码的逻辑。 仍是用 clientDynamicModules 来实现将 cmd 代码转成 esm 代码。主要是由于 ClientComputedMixin 这个类先后端代码都要使用。
样式加强 全局样式加强。使用 enhanceAppFiles 和 ready 钩子来实现(主题样式+用户样式+父主题样式)。
样式覆盖 全局样式覆盖,使用 ready 钩子来实现,覆盖 config.styl 和父主题的 palette。
dataBlock数据注入 解析 blockType=data 的数据,使用 chainWebpack 和 enhanceAppFiles 来实现,对 blockType=data 类型的数据注入到 markdown 生成的 vue 组件里去,每一个组件能够访问本身的 $dataBlock 属性拿到。
活动的标题连接 它会在用户滚动页面时自动转变侧边栏的高亮标题。 它使用了 clientRootMixin 和 define 往根组件混入了滚动逻辑:监听 onScroll 事件,获取全部锚点元素并根据滚动距离计算出高亮的锚点。
回到顶部 使用了 enhanceAppFiles 和 globalUIComponents 注册了一个全局组件:点击后能够滚动到页面顶部。
ga 谷歌分析站点的库。使用了 define 和 enhanceAppFiles 初始化了 ga。
国际化(废弃) 可让你的站点拥有切换语言的能力。使用了 enhanceAppFiles 和 additionalPages 注册了个 I18n 布局组件。
文档的最近更新时间 可让每一个文档页下面显示最近的 git 提交时间。使用 extendPageData 拓展了 $page 的 lastUpdated 属性。
图片预览 集成了 medium-zoom。使用了 define、clientRootMixin 往根组件里混入了 zoom 的初始化和更新逻辑。
分页 让共享侧边菜单栏的文档拥有分页切换的能力。使用了 enhanceAppFiles 定义了全部页面的索引和顺序。ready 定义了分页的规则如排序规则等、clientDynamicModules 生成动态模块给前端代码使用。
pwa 集成 service-worker 功能 - 9.1. 使用 ready 开启 serviceWorker 选项 - 9.2. 使用 alias 实现用 vue 当事件通道 - 9.3. 使用 define、globalUIComponents 注册更新 PWA 应用按钮组件 - 9.4. 使用 enhanceAppFiles 注入 register-service-worker 的初始化和更新逻辑 - 9.5. 使用 generated 经过 workbox-build 完成 sw 功能
注册全局 Vue 组件 使用 enhanceAppFiles 把一个文件夹中的 vue 组件文件都注册好。
搜索框 使用 alias 和 define 让搜索框能够动态引入。
进度条 使用 clientRootMixin 和 enhanceAppFiles 集成 nprogress。
项目管理上,插件机制也使得原来的一个大项目拆成了 1 + N 的形式,package.json
也变得多了起来,为了管理这种项目,vuepress 引入了 lerna。
关于 lerna 的知识,有兴趣的读者能够参考:lerna管理前端packages的最佳实践。
总体流程大体以下:
这里我划分红了两个阶段,用虚线分隔,一个是调用前阶段,一个是调用后阶段。插件们被调用前,是会被载入以及注册的,以后化整为零,映射成若干个 Option 实例。
_initialized
标志为 false 才能调用,用于确认哪些插件是能够被注册的:
normalizePlugin
方法将之转成对象
_pluginResolver(ModuleResolver 实例)
来解析模块
path.resolve
方法解析获得绝对路径,而后交给解析绝对路径模块的方法处理。
_initialized
标志位置为 true,而后注册全部可用的插件。
Option 类 - 每一个实例初始化 key(选项标识) 和 items(这个选项所对应的函数们) 属性。
AsyncOption 类
syncApply
,调用函数的时候使用了 await。
我也写了一个小插件,它能够将你的 vuepress 站点下载成一个 pdf 文件:vuepress-plugin-export-site
咱们熟悉的 webpack、vue 也有插件系统,它们都有两个共同的特色:
其实插件机制也能够看作设计模式的一种体现:抽离出变化的部分,保留不变的部分。这些变化的部分,即可以称之为插件。
在咱们造轮子的时候,若是轮子的功能愈来愈多,代码愈来愈臃肿的话,引入插件机制会让后续的开发更加灵活。
最后,帮插件机制的开发者真山同窗宣传一下,届时会有更加精彩的 vuepress 分享: