написать впереди
Тенсент Оми ФреймворкОфициально выпущена версия 5.0, по-прежнему ориентированная на View, но более дружественная интеграция с архитектурой MVVM, полностью разделяющая архитектуру View и бизнес-логику.
Вы можете быстро испытать MVVM с помощью omi-cli:
$ npm i omi-cli -g
$ omi init-mvvm my-app
$ cd my-app
$ npm start
$ npm run build
npx omi-cli init-mvvm my-app
Также поддерживается (требуется npm v5.2.0+)
Эволюция МВВМ
На самом деле суть MVVM эволюционировала от MVC и MVP.
Цель состоит в том, чтобы разделить представление и модель, но в MVC представление зависит от модели, и связь слишком высока, что сильно снижает переносимость представления.В режиме MVP представление напрямую не зависит от модели. модель, а P (Presenter) отвечает за завершение Model.Interaction with View. MVVM и MVP похожи по шаблону. ViewModel играет роль Presenter и предоставляет источник данных, требуемый представлением пользовательского интерфейса, вместо того, чтобы напрямую разрешать представлению использовать источник данных модели, что значительно улучшает переносимость представления и модели.Например, одна и та же модель переключается с использованием Flash, HTML, рендеринг WPF, например, одно и то же представление с использованием разных моделей, если модель и модель представления хорошо сопоставлены, представление может быть изменено незначительно или нет.
Mappingjs
Конечно, с MVVM будут проблемы.Данные в модели сопоставляются с ViewModel, чтобы обеспечить привязку представления.Как это сопоставить? Ручное сопоставление? автоматическое отображение? существуетASP.NETВ MVC есть мощныеAutoMapperиспользуется для карт. Для среды JS я специально инкапсулировалmappingjsИспользуется для сопоставления модели с ViewModel.
const testObj = {
same: 10,
bleh: 4,
firstName: 'dnt',
lastName: 'zhang',
a: {
c: 10
}
}
const vmData = mapping({
from: testObj,
to: { aa: 1 },
rule: {
dumb: 12,
func: function () {
return 8
},
b: function () {
//可递归映射
return mapping({ from: this.a })
},
bar: function () {
return this.bleh
},
//可以重组属性
fullName: function () {
return this.firstName + this.lastName
},
//可以映射到 path
'd[2].b[0]': function () {
return this.a.c
}
}
})
Вы можете установить через npm после использования:
npm i mappingjs
Другой пример:
var a = { a: 1 }
var b = { b: 2 }
assert.deepEqual(mapping({
from: a,
to: b
}), { a: 1, b: 2 })
Deep mapping:
QUnit.test("", function (assert) {
var A = { a: [{ name: 'abc', age: 18 }, { name: 'efg', age: 20 }], e: 'aaa' }
var B = mapping({
from: A,
to: { d: 'test' },
rule: {
a: null,
c: 13,
list: function () {
return this.a.map(function (item) {
return mapping({ from: item })
})
}
}
})
assert.deepEqual(B.a, null)
assert.deepEqual(B.list[0], A.a[0])
assert.deepEqual(B.c, 13)
assert.deepEqual(B.d, 'test')
assert.deepEqual(B.e, 'aaa')
assert.deepEqual(B.list[0] === A.a[0], false)
})
Deep deep mapping:
QUnit.test("", function (assert) {
var A = { a: [{ name: 'abc', age: 18, obj: { f: 'a', l: 'b' } }, { name: 'efg', age: 20, obj: { f: 'a', l: 'b' } }], e: 'aaa' }
var B = mapping({
from: A,
rule: {
list: function () {
return this.a.map(function (item) {
return mapping({
from: item, rule: {
obj: function () {
return mapping({ from: this.obj })
}
}
})
})
}
}
})
assert.deepEqual(A.a, B.list)
assert.deepEqual(A.a[0].obj, B.list[0].obj)
assert.deepEqual(A.a[0].obj === B.list[0].obj, false)
})
Omi MVVM Todo в действии
Определить модель:
let id = 0
export default class TodoItem {
constructor(text, completed) {
this.id = id++
this.text = text
this.completed = completed || false
this.author = {
firstName: 'dnt',
lastName: 'zhang'
}
}
clone() {
return new TodoItem(this.text, this.completed)
}
}
Todo будет пропущено и не опубликовано, оно слишком длинное, вы можете напрямуюСмотри сюда. Так или иначе, унифицированная абстракция и инкапсуляция в соответствии с объектно-ориентированным программированием.
Определить ViewModel:
import mapping from 'mappingjs'
import shared from './shared'
import todoModel from '../model/todo'
import ovm from './other'
class TodoViewModel {
constructor() {
this.data = {
items: []
}
}
update(todo) {
//这里进行映射
todo &&
todo.items.forEach((item, index) => {
this.data.items[index] = mapping({
from: item,
to: this.data.items[index],
rule: {
fullName: function() {
return this.author.firstName + this.author.lastName
}
}
})
})
this.data.projName = shared.projName
}
add(text) {
todoModel.add(text)
this.update(todoModel)
ovm.update()
}
getAll() {
todoModel.getAll(() => {
this.update(todoModel)
ovm.update())
})
}
changeSharedData() {
shared.projName = 'I love omi-mvvm.'
ovm.update()
this.update()
}
}
const vd = new TodoViewModel()
export default vd
- vm фокусируется только на данных обновления, представление обновляется автоматически
- Публичные данные или vm могут зависеть от импорта
Определение вида, обратите внимание на следующее унаследованное от ModelView, а не заселение.
import { ModelView, define } from 'omi'
import vm from '../view-model/todo'
import './todo-list'
import './other-view'
define('todo-app', class extends ModelView {
vm = vm
onClick = () => {
//view model 发送指令
vm.changeSharedData()
}
install() {
//view model 发送指令
vm.getAll()
}
render(props, data) {
return (
<div>
<h3>TODO</h3>
<todo-list items={data.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.text} />
<button>Add #{data.items.length + 1}</button>
</form>
<div>{data.projName}</div>
<button onClick={this.onClick}>Change Shared Data</button>
<other-view />
</div>
)
}
handleChange = e => {
this.text = e.target.value
}
handleSubmit = e => {
e.preventDefault()
if(this.text !== ''){
//view model 发送指令
vm.add(this.text)
this.text = ''
}
}
})
- Все данные вводятся через vm
- Значит инструкция выдается через vm
define('todo-list', function(props) {
return (
<ul>
{props.items.map(item => (
<li key={item.id}>
{item.text} <span>by {item.fullName}</span>
</li>
))}
</ul>
)
})
Вы можете видеть, что todo-list можно использовать напрямуюfullName
.
→ Нажмите здесь, чтобы увидеть полный код
mapping.auto
Считаете ли вы, что написание отображения немного громоздко? ? Простота — это хорошо, но сложные объекты с глубокой вложенностью могут быть трудоемкими. Неважноmapping.auto
спасти тебя!
- map.auto(from, [to]), где to — необязательный параметр
Например:
class TodoItem {
constructor(text, completed) {
this.text = text
this.completed = completed || false
this.author = {
firstName: 'dnt',
lastName: 'zhang'
}
}
}
const res = mapping.auto(new TodoItem('task'))
deepEqual(res, {
author: {
firstName: "dnt",
lastName: "zhang"
},
completed: false,
text: "task"
})
Вы можете сопоставить любой класс с простым объектом json! Затем начните преобразовывать ViewModel:
class TodoViewModel {
constructor() {
this.data = {
items: []
}
}
update(todo) {
todo && mapping.auto(todo, this.data)
this.data.projName = shared.projName
}
...
...
...
То, что раньше было набором логики сопоставления, превратилось в одну строку кода:mapping.auto(todo, this.data)
. Конечно, поскольку атрибута fullName нет, нам нужно использовать отображенного автора непосредственно в представлении:
define('todo-list', function(props) {
return (
<ul>
{props.items.map(item => (
<li key={item.id}>
{item.text} <span>by {item.author.firstName + item.author.lastName}</span>
</li>
))}
</ul>
)
})
резюме
С макроэкономической точки зрения архитектура Omi MVVM также представляет собой ячеистую архитектуру, которая в настоящее время включает в себя:
- Mobx + React
- Hooks + React
- MVVM (Omi)
Тренд! Просто лучшая практика фронтенд-инжиниринга! Также можно понять, что сетевая структура — лучший способ описать и абстрагировать мир. Так где сетка?
- Сетевая структура взаимозависимости или даже круговой зависимости между ViewModel и ViewModel
- ViewModel «один к одному», «многие к одному», «один ко многим» и «многие ко многим» полагаются на модели для формирования структуры сетки.
- Между Моделью и Моделью формируется сеть взаимозависимых или даже циклических зависимостей.
- View опирается на ViewModel один на один для формирования сетчатой структуры.
Обобщенно следующим образом:
Model | ViewModel | View | |
---|---|---|---|
Model | многие ко многим | многие ко многим | нет связи |
ViewModel | многие ко многим | многие ко многим | один на один |
View | нет связи | еще один | многие ко многим |
Другие новые функции
Поддержка единицы rpx
import { render, WeElement, define, rpx } from 'omi'
define('my-ele', class extends WeElement {
css() {
return rpx(`div { font-size: 375rpx }`)
}
render() {
return (
<div>abc</div>
)
}
})
render(<my-ele />, 'body')
Например, div шириной в половину экрана определен выше.
поддержка HTML
htmЭто недавняя работа инженера Google и автора preact. Будь то будущее или нет, я сначала поддержу его:
import { define, render, WeElement } from 'omi'
import 'omi-html'
define('my-counter', class extends WeElement {
static observe = true
data = {
count: 1
}
sub = () => {
this.data.count--
}
add = () => {
this.data.count++
}
render() {
return html`
<div>
<button onClick=${this.sub}>-</button>
<span>${this.data.count}</span>
<button onClick=${this.add}>+</button>
</div>`
}
})
render(html`<my-counter />`, 'body')
Вы даже можете запустить следующий код прямо в современных браузерах без каких-либо инструментов сборки:
API, похожие на хуки
Вы также можете определить его как чистую функцию:
import { define, render } from 'omi'
define('my-counter', function() {
const [count, setCount] = this.use({
data: 0,
effect: function() {
document.title = `The num is ${this.data}.`
}
})
this.useCss(`button{ color: red; }`)
return (
<div>
<button onClick={() => setCount(count - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
})
render(<my-counter />, 'body')
Если вам не нужен метод эффекта, вы можете использовать его напрямуюuseData
:
const [count, setCount] = this.useData(0)
Дополнительные параметры шаблона
Template Type | Command | Describe |
---|---|---|
Base Template | omi init my-app |
базовый шаблон |
TypeScript Template(omi-cli v3.0.5+) | omi init-ts my-app |
Шаблоны с использованием TypeScript |
SPA Template(omi-cli v3.0.10+) | omi init-spa my-app |
Шаблон для одностраничного приложения с использованием omi-router |
omi-mp Template(omi-cli v3.0.13+) | omi init-mp my-app |
Веб-шаблон для разработки небольших программ |
MVVM Template(omi-cli v3.0.22+) | omi init-mvvm my-app |
Шаблон MVVM |