UNPKG

10.2 kBMarkdownView Raw
1# vgg
2
3vgg是根据vue技术栈的最佳实践总结而来的技术架构,它能以插件的形式扩展框架本身。支持单页面应用
4的服务端和客户端渲染。
5
6架构如下:
7
8```
9|--------------------|
10| vgg + vgg-plugin-* |
11|--------------------|
12| egg-vgg |
13|--------------------|
14| eggjs |
15|--------------------|
16```
17
18**重要:**我们基于[egg-vgg](https://github.com/acthtml/egg-vgg)可快速开始开发。
19
20## 1. 文件目录结构
21
22```
23 - api api service层
24 - modules api对应的接口模块
25 - user.js
26 - index.js api实例扩展
27 - common 通用资源
28 - context vue上下文,这里的资源会绑定到vue原型,允许你以this.$的形式访问。
29 - filter vue filter
30 - directive vue directive
31 - plugins vue plugin
32 - utils vgg.utils工具库
33 - commponents 全局组件
34 - index.js
35 - config 配置
36 - config.default.js
37 - config.local.js
38 - config.stage.js
39 - config.prod.js
40 - plugin.json 插件配置
41 - plugins 插件约定存放文件夹
42 - plugin_a
43 - router 路由
44 - router.js router实例扩展
45 - routes.js 路由注册
46 - store vuex store
47 - modules store对应命名空间的模块
48 - user.js
49 - index.js vuex实例扩展
50 - views 单页面应用视图层
51 - home.vue
52 - app.template.html 服务端渲染基础html
53 - app.vue 根组件
54 - pages 多页面应用视图层
55 - page_a.vue
56 - app.js 根实例扩展
57```
58
59从目录结构我们可以看出,框架分为如下几个重要的部分:
60
61- 配置(config)
62- 通用vue资源(common和commponents)
63- store
64- api服务层
65- 路由系统
66- 插件系统
67
68
69## 2. 环境与配置
70
71根据当前的环境读取对于的配置,运行环境配置参考[eggjs运行环境](https://eggjs.org/zh-cn/basics/env.html)。
72
73配置上的约定也跟eggjs一致,即config.default.js为默认配置,环境配置覆盖默认配置,覆盖采用
74`_.defaultsDeep`方式。
75
76通过vgg.env获取当前环境,vgg.config获取当前配置。
77
78```js
79 // config/config.default.js
80 export default {
81 auth: {
82 enabled: false,
83 mod: 'sso'
84 }
85 }
86
87 // config/config.local.js
88 export default {
89 auth: {
90 enabled: true
91 }
92 }
93
94 // 当在本地开发环境(local)时
95 vgg.config.auth.enabled === true;
96 vgg.config.auth.mod === 'sso';
97```
98
99
100## 3. 通用vue资源
101
102框架支持几种vue资源全局性的注入,分别是:
103
104- 组件(componetns)
105- 上下文(common/context)
106- vue的过滤器、指令、插件
107- 工具类库(utils)
108
109只要按照约定,框架会自动注入你配置的资源。
110
111### 3.1 组件(components)
112
113`components/index.js`中声明组件,key为组件名称,value为需要引入的组件。
114
115```js
116 // components
117 import MyApp from './app.vue';
118 export default {
119 MyHeader: () => import('./header.vue'),
120 MyApp
121 }
122
123 // 也可以使用辅助函数来自动添加前缀。
124 let components = vgg.utils.prefix('My', {
125 Header: () => import('./header.vue'),
126 App: () => import('./app.vue')
127 });
128 export default components;
129```
130
131### 3.2 上下文(common/context)
132
133`common/context/index.js`中声明上下文,key为上下文名称,value为上下文创建函数。
134
135```js
136 // common/context/index.js
137 import axios from 'axios';
138 export default {
139 /**
140 * 创建一个axios实例,并注入上下文。
141 * @param {[type]} appContext koa app context,是服务端返回的上下文。
142 * @param {[type]} context 注入好的上下文。
143 * @return {[type]} 上下文对应的内容。
144 */
145 http: (appContext, context) => {
146 return axios.create();
147 }
148 }
149```
150
151注入好之后,你能在其他appContext中使用,例如appContext.http,或则在组件中以this.$http来
152访问。
153
154框架内置的上下文有:cookies, http, logger。
155
156### 3.3 vue的过滤器、指令、插件
157
158分别对应这些文件夹:`common/filter、common/directive、common/plugin`
159
160这3类资源跟context的声明方式类似,在各自文件夹下的`index.js`中声明,key为资源名称,value为
161创建方法,最终都会交给Vue来进行全局性的注入:
162
163```js
164 // 对于filter
165 Vue.filter(key, filters[key]);
166 // 对于directive
167 Vue.directive(key, directives[key]);
168 // 对于plugin
169 Vue.use(plugins[key]);
170```
171
172### 3.4 工具类库(common/utils)
173
174全局性的工具类库,注入到vgg.utils中。
175
176```js
177 // 在`common/utils/index.js`中进行注入对应的方法。
178 export default {
179 now(){return new Date()}
180 }
181
182 // 其他地方使用
183 import vgg from 'vgg';
184 vgg.utils.now();
185```
186
187## 4. store
188
189store是vuex的实现,[文章和架构参考](https://vuex.vuejs.org/zh-cn/structure.html)。
190
191### 4.1 创建store module
192
193在文件夹`store/modules`中创建模块,创建之后,即可使用`store.register`对模块进行注册,文件
194命名采用`snake_case`规则。
195
196```js
197 // 创建store模块
198 // store/modules/my_book.js
199 export default (namespace, appContext) => {
200 return {
201 namespaced: !!namespace,
202 state(){
203 return {}
204 },
205 // getters,
206 // mutations,
207 // actions
208 }
209 }
210```
211
212### 4.2 使用store module
213
214要使用模块,先要使用`store.register()`进行模块注册。注册的本质是获取对应的store模块,调用
215`store.registerModule()`注册到对应的命名空间中。
216
217```js
218 // 某某组件:my_component.vue
219 export default {
220 async asyncData({store}){
221 // 注册模块到指定命名空间上
222 // store.register(namespace, modulepath, ...args)
223 // 向命名空间example/some添加模块example/some
224 store.register('example/some', 'example/some');
225 // 该语句有个简写
226 store.register('example/some');
227 },
228 created(){
229 this.$store.register('example/some')
230 },
231 methods: {
232 ...mapState('example/some', ['some'])
233 }
234 }
235```
236
237其他一些api,参考[create_store.js](./src/core/create_store.js)。
238
239- store.register(namespace[, modulepath[, ...args]])
240- store.unregister(namespace)
241- store.isRegistered(namespace)
242- store.once(type, namespace, name[, ...args])
243- store.ensure(type, namespace, name)
244- store.has(type, namespace, name)
245- store.try(type, namespace, name, ...args)
246
247``注意点``
248
2491. 如果模块需要在组件beforeCreate生命周期(包含beforeCreate)前使用,那么这个模块需要在路由
250组件的asyncData中注入。[参考服务器端数据预取](https://ssr.vuejs.org/zh/data.html)
2512. 保留命名空间'route'。[参考vue-router-sync](https://github.com/vuejs/vuex-router-sync)
252
253## 5. api服务层
254
255类似于service层,用于跟后端进行数据交互。
256
257### 5.1 api的创建
258
259api在文件夹`api/modules`中创建,创建后可在组件、store中使用,文件名采用`snake_case`规范。
260
261```js
262 // 创建api book。
263 // 文件:api/modules/book.js
264 // 整个模块的默认格式为如下,其函数返回内容即为这个api的可使用接口。
265 export default (http, api, logger) => {
266 return {
267 async getList(){
268 return http( /* ...do some http request */)
269 },
270 async getDetail(){
271 return http( /* ...do some http request */)
272 }
273 }
274 }
275```
276
277### 5.2 api的使用
278
279`store`中,api被注入到上下文appContext中。
280
281```js
282 export default (namespace, appContext) => {
283 const {api} = appContext;
284 return {
285 // ....
286 // 某某store
287 actions: {
288 async init(){
289 let data = await api('book').getList();
290 }
291 }
292 }
293 }
294```
295
296在组件中可通过`$api`这个对象访问:
297
298```js
299 // 某某组件中
300 export default {
301 methods: {
302 async init(){
303 let data = await this.$api('book').getList();
304 }
305 },
306 // 但在asyncData方法中,因为组件还没有实例化,所以通过参数进行注入了。
307 async asyncData({api}){
308 let data = await api('book').getList();
309 }
310 }
311```
312
313
314## 6. 路由
315
316路由是基于[vue-router](https://router.vuejs.org/zh-cn/)实现的,在`router/routes.js`
317中添加路由,配置同`vue-router`
318
319在config的router属性中配置路由初始化属性,配置项[参考](https://router.vuejs.org/zh/api/#router-%E6%9E%84%E5%BB%BA%E9%80%89%E9%A1%B9)
320
321通过hook系统可扩展router实例本身:
322
323- hook router.alter
324- hook router.onError
325- hook router.beforeEach
326- hook router.afterEach
327
328
329## 7. 插件
330
331框架通过插件来扩展自身,可以扩展框架的所有内容。
332
333### 7.1 使用插件
334
335在文件夹`config/plugin.js`中来声明要使用的插件。
336
337```js
338 // config/plugin.js
339 export default {
340 // 命名空间。(插件的命名空间,使用的时候,因为技术限制,不能自定义命名空间。命名空间是由插件作者指定好的。)
341 pluginA: {
342 // 对应插件的包名称或相对地址。例如'./plugin/plugin_a'
343 package: 'plugin-a',
344 // 默认注入组件。关闭之后需要手动注入,这样有助于减少打包体积。
345 components: true,
346 // 默认注入路由。关闭之后需要手动注入,这样有助于减少打包体积。
347 routes: true
348 },
349 // 当components和routes都是需要注入时,配置可以简写:
350 // pluginA: 'plugin-a'
351 }
352```
353
354引入其他插件的资源:`import('$pluginA/foo/bar')`。这样就是找根目录是插件`pluginA`,文件
355相对地址是`./foo/bar`的模块。
356
357使用插件的api和store:
358
359```js
360 // api
361 this.$api('myapi', 'pluginA');
362 // store
363 this.$store.register('pluginA/user', '$pluginA/user');
364 // 等同于
365 this.$store.register('$pluginA/user');
366```
367
368### 7.2 创建插件
369
370@todo
371
372
373
374
375