浅谈MVVM ——Model-View-View-Model
前言:
笔者最近接到这样一个机器人项目,接入他们的机器人平台做二次开发,开发自己的opk移植到机器人上做医院展示使用。opk是必须使用机器人厂家提供的经过一定封装过的,该opk的架构是MVVM。笔者是首次写这样的结构,所以想浅谈自己的一些理解。
正文:
MVVM框架搭建的前端程序,把数据和页面进行了分离,分别为View层、ViewModel层和Model层。他们之间的交互如图所示:
View层(视图层)的主要职责是页面的渲染,也仅仅是用于页面的渲染。
Model层(模型层)的主要职责是封装核心数据。模型是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。
ViewModel(视图模型层)就像是一个桥梁用于连接View层和Model层,负责它们之间的数据通信。百度上是这样说的:视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信。
笔者才接触这样框架的时候有这样的疑惑,一个页面是由组件和数据渲染出来的,那么只要有View负责渲染组件,Model负责分发数据不就可以了?弄出一个ViewModel是为了什么,这不是使得页面和数据之间的通信多走了一层,何必呢?
我们可以结合代码进行一定的理解:
View.ts: 这是一段View里面的代码,其实你并不需要关心太多,它最大的最用只是render出组件。我们可以看到View里面拿到ViewModel处理过的数据放在了组件里面。
export class DemoScreen extends BaseComponent<BaseComponentProps, DemoViewModel, DemoVoice> {
public viewModel: DemoViewModel;
public constructor(props: BaseComponentProps) {
super(props);
this.viewModel = new DemoViewModel();
let voice = new DemoVoice(this.viewModel);
//关联ViewModel及Voice的生命周期到当前界面上
this.setViewModel(this.viewModel);
this.setVoice(voice);
}
public componentDidMount() {
//重写界面的didMount,必须调用super
super.componentDidMount();
}
public componentWillMount() {
}
public componentWillUnmount() {
//重写界面的Unmount,必须调用super
super.componentWillUnmount();
}
public render() {
speechApi.setRecognizeMode(true);
return (
<View>
<Text style={{ fontSize: 17, color: 'red' }}> {demoModel.getInfoText()}</Text>
</View>
);
}
}
Model.ts:相对应的,这是一段Model里面的代码。可以看出这里面只负责set和get数据,除此之外没有别的操作了。
export class DemoModel {
@observable private infoText = 'Hello Robot!';
@action
public setInfoText(infoText: string) {
console.log('DemoVoice Set info text : ' + infoText);
this.infoText = infoText;
}
public getInfoText(): string {
return this.infoText;
}
}
export const demoModel = new DemoModel();
ViewModel.ts: 真正的数据处理就在ViewMode里面了。这样做的好处是View和Model都是很纯粹的,View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。并且开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
export class DemoViewModel extends BaseViewModel {
public constructor() {
//super参数为ViewModel与Trigger相互通信的标识,必须保证与Trigger的一致
super('Demo');
}
public onStart() {
}
public onStop() {
}
public exit() {
//发送消息到Trigger中,eventId为消息id, data为携带的数据
this._apiTrigger(1001, '');
}
public showSpeechText(text: string) {
console.log('DemoVoice : set ' + text);
demoModel.setInfoText(text);
}
}