Observable模式的MVVM讓Vue在中小型Web應用中有自然的優點,但隨着Vue的流行度日益增加,Vue在大型項目中的運用略顯捉襟見肘。顯然在高複雜度項目中,類型檢查已成必須要素,而Vue2在TypeScript的類型檢查支持又不夠完全,更重要的是Vuex的狀態邏輯的模塊化設計欠缺。css
因此這裏提出如下解決方案:vue
lerna初始化後,進行領域驅動設計,獲得大的領域模塊。在必要狀況下,將能夠進行分包,同時啓用動態import懶加載或者是RequireJS等模塊加載器,以提升構建時性能和運行性能。git
在覈心應用子項目的初始化使用vue-cli3建構,選擇TypeScript做爲主要語言,它將自動引入Webpack的ts-loder。github
這是核心目錄結構:vuex
|-- App.vue
|-- main.ts
+-- modules/
|-- Todos/
|-- Navigation/
|-- Portal/
|-- Counter/
...
+-- lib/
|-- loader.ts
|-- moduleConnect.ts
...
+-- components/
...
複製代碼
main.ts
是默認的entry。vue-cli
// ...
// omit some modules
import { load } from './lib/loader';
const {
portal,
app,
} = load({
bootstrap: 'Portal',
modules: {
Counter,
Todos,
Portal,
Navigation
},
main: App,
components: {
home: {
screen: TodosView,
path: '/',
module: 'todos',
},
counter: {
screen: CounterView,
path: '/counter',
module: 'counter',
},
}
});
Vue.prototype.portal = portal;
new Vue(app).$mount("#app");
複製代碼
App.vue
是主視圖文件。bootstrap
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/counter">Counter</router-link>
</div>
<router-view />
</div>
</template>
複製代碼
modules
包含所有的業務邏輯,也包括視圖層狀態和導航模塊等,它將由Vuex來啓動。 例如如下是Counter模塊:bash
import { injectable } from "inversify";
import Module, { state, action, computed } from "../../lib/baseModule";
@injectable()
export default class Counter extends Module {
@state count: number = 0;
@action
calculate(num: number, state?: any) {
state.count += num;
}
getViewProps() {
return {
count: this.count,
calculate: (num: number) => this.calculate(num)
}
}
}
複製代碼
lib/loader.ts
是應用配置加載器。架構
import { Container } from 'inversify';
export function load(parmas: any = {}) {
const { bootstrap, modules, ...option } = parmas;
const container = new Container({ skipBaseClassChecks: true });
Object.keys(modules).forEach(key => {
container.bind(key).to(modules[key]);
});
container.bind("AppOptions").toConstantValue(option);
const portal: any = container.get(bootstrap);
portal.bootstrap();
const app = portal.createApp();
return {
portal,
app,
};
}
複製代碼
lib/moduleConnect.ts
是ViewModule的View鏈接器。 這是一個高階組件的鏈接器。app
import { Component, Vue } from "vue-property-decorator";
export default (ViewContainer: any, module: string) => {
@Component({
components: {
ViewContainer
}
})
class Container extends Vue {
get module() {
return this.portal[module];
}
render(createElement: any) {
const props = this.module.getViewProps();
return createElement(ViewContainer, {
props
})
}
}
return Container;
}
複製代碼
components/Counter/index.tsx
是Counter的組件。
import { Component, Vue, Prop } from "vue-property-decorator";
import './style.scss';
type Calculate = (sum: number) => void;
@Component
export default class CounterView extends Vue {
@Prop() count!: number;
@Prop(Function) calculate!: Calculate;
render(){
return (
<div class="body">
<button onClick={()=> this.calculate(1)}>+</button>
<span>{this.count}</span>
<button onClick={()=> this.calculate(-1)}>-</button>
</div>
)
}
}
複製代碼
配合TSX的View組件模塊,同時基於此架構等總體設計將很大程度上契合TypeScript的類型檢查和推導。
在該Vue架構中最核心的設計部分應該是usm-vuex
,它讓Vuex進行業務模塊化變得簡單明瞭,配合View層的ViewModule,它可以讓當前的架構設計變得高內聚低耦合,在複用性與維護性上大大提升,同時配合依賴注入(dependency injection),讓模塊間的依賴變得清晰易懂。對於現實中的大型應用還有不少其餘細節部分待完善,這裏就不敷述了。
usm-vuex
Repo: github.com/unadlib/usm
本文的架構完整Demo: github.com/unadlib/usm…