运行原理剖析
Wox的启动原理非常复杂,它通过webpack
插件的@wox/loader
来生成自动注入的require.context
。我们尽量保障webpack的能力来驱动wox的开发。它的启动所经历的图示如下,尽量准确的告诉开发者它的启动顺序,使得开发者能够灵活利用生命周期来开发插件。
webpack聚合文件.wox.js
.wox.js
将会由webpack启动的时候自动创建。主要是将一些自动注入到架构中的资源对象暴露出来。它主要由以下部分组成:
component: Array
webpack会根据config/plugin.json
中启动的插件中固定位置的文件夹,使用require.context
函数来加载整个文件夹的内容,形成一个数组,写入到这个变量中去,便于整个树状结构的拓扑。它也将被用于wox架构的初始化自动注入到Vue.component
中,供全局使用。
注意:加载的文件夹位置为每个插件(包括主项目)的
app/vue/component
。
创建文件:
cli wox <path/file> -c
cli wox <path/file> --component
2
模板:
<template>
<div>Untitled Component</div>
</template>
<script>
export default {
name: "<%=className%>"
}
</script>
<style lang="less" scoped>
</style>
2
3
4
5
6
7
8
9
10
与Vue.js
的组件写法一致,不做介绍了。
directive: Array
webpack会根据config/plugin.json
中启动的插件中固定位置的文件夹,使用require.context
函数来加载整个文件夹的内容,形成一个数组,写入到这个变量中去,便于整个树状结构的拓扑。它也将被用于wox架构的初始化自动注入到Vue.directive
中,供全局使用。
注意:加载的文件夹位置为每个插件(包括主项目)的
app/vue/directive
。
创建文件:
cli wox <path/file> -d
cli wox <path/file> --directive
2
模板:
export default {
bind(el, binding, vnode, oldVnode) {},
inserted(el, binding, vnode, oldVnode) {},
update(el, binding, vnode, oldVnode) {},
componentUpdated(el, binding, vnode, oldVnode) {},
unbind(el, binding, vnode, oldVnode) {}
}
2
3
4
5
6
7
与Vue.js
的指令写法一致,不做介绍了。
filter: Array
webpack会根据config/plugin.json
中启动的插件中固定位置的文件夹,使用require.context
函数来加载整个文件夹的内容,形成一个数组,写入到这个变量中去,便于整个树状结构的拓扑。它也将被用于wox架构的初始化自动注入到Vue.filter
中,供全局使用。
注意:加载的文件夹位置为每个插件(包括主项目)的
app/vue/filter
。
创建文件:
cli wox <path/file> -f
cli wox <path/file> --filter
2
模板:
return value => {
return value;
}
2
3
与Vue.js
的Filter写法一致,不做介绍了。
mixin: Array
webpack会根据config/plugin.json
中启动的插件中固定位置的文件夹,使用require.context
函数来加载整个文件夹的内容,形成一个数组,写入到这个变量中去,便于整个树状结构的拓扑。它也将被用于wox架构的初始化自动注入到Vue.mixin
中,供全局使用。
注意:加载的文件夹位置为每个插件(包括主项目)的
app/vue/mixin
。
创建文件:
cli wox <path/file> -x
cli wox <path/file> --mixin
2
模板:
return {
created() {},
mounted() {}
}
2
3
4
与Vue.js
的Mixin写法一致,不做介绍了。
bootstrap: Array
webpack会根据config/plugin.json
中启动的插件中的模块的根目录下的app.js
,包括主项目下的app.js
,通过require
方法加载内容到一个数组,写入到这个变量中去,便于整个树状结构的拓扑。它将会在PluginDidInstalled
的生命周期前进行初始化。它可以进行一系列的基础功能和业务功能操作,完成我们的需求。
主项目模板:
export default async (app) => {
// ...
console.log(app.$config);
}
2
3
4
插件模板:
export default async (app, plugin) => {
// ...
console.log(app.$cofnig);
console.log(plugin.$config);
}
2
3
4
5
view: Vue
webpack会根据config/plugin.json
中启动的插件中的模块的根目录下的app.vue
,包括主项目下的app.vue
,通过require
方法不断覆盖这个变量。它是我们项目基本骨架文件,所有内部通过MVVM数据响应得到的变化的内容都会被更新到这个文件的<WoxViewPage></WoxViewPage>
组件中去。
注意:
<WoxViewPage></WoxViewPage>
组件也是个全局组件,可以使用到任何地方中去,但是只允许使用一次。
模板:
<template>
<WoxViewPage></WoxViewPage>
</template>
<script>
export default {
name: "ApplicationPage"
}
</script>
<style lang="less" scoped>
</style>
2
3
4
5
6
7
8
9
10
plugin_configs: object
webpack会读取主项目下的config/plugin.${env}.json
中的内容作为所有插件的配置树,然后通过内部的逻辑分发到各自插件中去。每个插件可以通过以下代码获取到对应的配置参数
export default (app, plugin) => {
console.log(plugin.$config);
}
2
3
安装插件的时候,我们需要在插件根目录下建立一个文件.wox.config.json
来告诉我们的cli工具,安装完毕这个插件需要将这个模板数据添加到主项目的config/plugin.${env}.json
中去。
custom_configs: Array
webpack会读取主项目下的config/config.${env}.json
中的内容作为主项目的配置树,具体参考 配置 一章。
controller: Array
webpack会根据config/plugin.json
中启动的插件中固定位置的文件夹,使用require.context
函数来加载整个文件夹的内容,形成一个数组,写入到这个变量中去,便于整个树状结构的拓扑。它也将被用于wox架构的初始化自动注入路由到系统架构中。
注意:加载的文件夹位置为每个插件(包括主项目)的
app/controller
。
创建文件:
cli wox <path/file> -c
cli wox <path/file> --controller
2
模板:
import { Http, Controller } from '@wox/wox';
@Controller
export default class IndexController {
@Http.Get
async Home() {
// await this.ctx.render(webview)
// this.ctx.body = { abc: 'test demo' }
}
}
2
3
4
5
6
7
8
9
decorate: Array
webpack会根据config/plugin.json
中启动的插件中固定位置的文件夹,使用require.context
函数来加载整个文件夹的内容,形成一个数组,写入到这个变量中去,便于整个树状结构的拓扑。它也将被用于wox架构的初始化自动生成Decorate到Interface
对象中去,在开发的时候可以通过@Interface.xxx
来使用这个decorate。
注意:加载的文件夹位置为每个插件(包括主项目)的
app/decorate
。
创建文件:
cli wox <path/file> -t
cli wox <path/file> --decorate
2
模板:
import { DecorateInterface } from '@wox/wox';
export default class UnknownDecorate extends DecorateInterface {
constructor() {
super('Unknown');
}
interfaceWillInject(...value) {
return (target, property, descriptor) => {
this.set(descriptor.value, value);
}
}
interfaceDidRendered(data, { options, ctx }) {
options.list = data.concat([456]);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
我们就可以在Controller中这样使用:
import { Http, Controller, Interface } from '@wox/wox';
@Controller('/test')
export default class IndexController {
@Http.Get('/value')
@Interface.Unknown(123)
async Home({ list }) {
this.ctx.body = { abc: list };
// => list: [123, 456]
}
}
2
3
4
5
6
7
8
9
10
当我们访问/test/value
路由的时候,内部返回的值为{abc: [123, 456]}
。
Bootstrap render
系统通过.wox.json
中的bootstrap
参数数组,自动循环执行各个函数,用于安装插件初始化或者对项目的初始化行为。一般的,我们可以通过定义整个系统的生命周期来扩展功能。
export default (app, plugin) => {
console.log(plugin.$config);
app.on('RouterDidInstalled', async () => {
console.log('router has been installed');
})
}
2
3
4
5
6
Service Decorate Inject
注入关于架构中Service
层的注解功能。
创建文件:
cli wox <path/file> -s
cli wox <path/file> --service
2
模板:
export default class IndexService {
async GetData(abc) {
return abc;
}
}
2
3
4
5
在controller中调用
import { Http, Controller, Interface } from '@wox/wox';
import IndexService from '../service/index';
@Controller('/test')
export default class IndexController {
@Http.Get('/value')
@Interface.Service('abc', IndexService)
async Home({ Service }) {
this.ctx.body = { abc: Service.GetData('cde') };
// => abc: 'cde'
}
}
2
3
4
5
6
7
8
9
10
11
当我们访问/test/value
路由的时候,内部返回的值为{abc: 'cde'}
。
Analysis All Decorates
系统将所有注解树实例化,绑定到内部对象中,供之后的调用。我们称这个树为注解实例树。
Inject Decorates Into Inteface
系统将注解实例树各个对象生成对应的注解到Interface
中,使得开发时候可以使用这些注解。
注解注入函数为
interfaceWillInject
,完全可以自定义内容。
Controller parse and render
系统通过Controller
树进行遍历,结合一定的规则逻辑生成不同的new Router()
对象,将内容全部绑定。
运行过程中,注解将通过
interfaceDidRendered
方法进行内容拾取,注入到信的处理函数中。
Compose Routers
系统通过use(router.routes())
方法绑定到顶层路由去,这样启动服务后就可以进行内容处理。
Load vue and mount it
将Vue实例化绑定到配置节点上,同时Mount挂在到页面上。
Create Server
服务启动。如果服务启动过程失败,系统将自动销毁服务。如果启动成功,且启动路由不是/
,那么系统将会replace一次页面地址。
生命周期
具体生命周期示意图请看最上面的图解。
PluginDidInstalled
此生命周期处理插件和项目的初始化完成阶段。主要可以做一些功能的预注入。此时controller和router并未链接。
DecorateDidInstalled
此生命周期主要可以注入一些中间件,此时controller还未解析,正在等待解析。
RouterWillInstall
此时Controller已经解析完成,准备对接路由。
RouterDidInstalled
此时路由已经链接到主架构,基本所有服务工序都完成,正在准备通过Vue创建页面。
setup
此生命周期比较特殊,用来扩展Vue创建页面的时候我们的自定义参数的配置。比如说创建store
。
app.on('setup', options => {
// options 就是根vue的配置参数
options.store = new Vuex.Store({ ... });
})
2
3
4
ServerWillCreate
此时页面准备完成,并且已经挂载到节点上,虚拟服务正在启动中。
ServerDidCreated
虚拟服务启动完成,app已可以使用。