Omi v5.0 - Friendly to MVVM

This commit is contained in:
dntzhang 2018-11-25 21:13:16 +08:00
parent d113070505
commit 59b75cf7be
43 changed files with 608 additions and 321 deletions

View File

@ -696,7 +696,8 @@ class MyApp extends WeElement {
| `installed` | after the component gets mounted to the DOM |
| `uninstall` | prior to removal from the DOM |
| `beforeUpdate` | before update |
| `afterUpdate` | after update |
| `afterUpdate` | after update (deprecated) |
| `updated` | after update |
| `beforeRender` | before `render()` |
| `receiveProps` | parent element re-render will trigger it |

View File

@ -670,7 +670,8 @@ class MyApp extends WeElement {
| `installed` | after the component gets mounted to the DOM |
| `uninstall` | prior to removal from the DOM |
| `beforeUpdate` | before update |
| `afterUpdate` | after update |
| `afterUpdate` | after update (deprecated) |
| `updated` | after update |
| `beforeRender` | before `render()` |
| `receiveProps` | parent element re-render will trigger it |

View File

@ -697,7 +697,8 @@ class MyApp extends WeElement {
| `installed` | after the component gets mounted to the DOM |
| `uninstall` | prior to removal from the DOM |
| `beforeUpdate` | before update |
| `afterUpdate` | after update |
| `afterUpdate` | after update (deprecated) |
| `updated` | after update |
| `beforeRender` | before `render()` |
| `receiveProps` | parent element re-render will trigger it |

View File

@ -206,7 +206,8 @@ define('el-button', class extends WeElement {
| `installed` | after the component gets mounted to the DOM |
| `uninstall` | prior to removal from the DOM |
| `beforeUpdate` | before update |
| `afterUpdate` | after update |
| `afterUpdate` | after update (deprecated) |
| `updated` | after update |
| `beforeRender` | before `render()` |
| `receiveProps` | parent element re-render will trigger it |

View File

@ -219,7 +219,8 @@ class ElButton extends WeElement {
| `installed` | after the component gets mounted to the DOM |
| `uninstall` | prior to removal from the DOM |
| `beforeUpdate` | before update |
| `afterUpdate` | after update |
| `afterUpdate` | after update (deprecated) |
| `updated` | after update |
| `beforeRender` | before `render()` |
| `receiveProps` | parent element re-render will trigger it |

View File

@ -222,7 +222,8 @@ define('el-button', class extends WeElement {
| `installed` | after the component gets mounted to the DOM |
| `uninstall` | prior to removal from the DOM |
| `beforeUpdate` | before update |
| `afterUpdate` | after update |
| `afterUpdate` | after update (deprecated) |
| `updated` | after update |
| `beforeRender` | before `render()` |
| `receiveProps` | parent element re-render will trigger it |

View File

@ -61,6 +61,18 @@ declare namespace Omi {
uninstall?(): void;
beforeUpdate?(): void;
afterUpdate?(): void;
updated?(): void;
beforeRender?(): void;
receiveProps?(): void;
}
interface ModelView<P, D> {
install?(): void;
installed?(): void;
uninstall?(): void;
beforeUpdate?(): void;
afterUpdate?(): void;
updated?(): void;
beforeRender?(): void;
receiveProps?(): void;
}
@ -71,6 +83,7 @@ declare namespace Omi {
uninstall?(): void;
beforeUpdate?(): void;
afterUpdate?(): void;
updated?(): void;
beforeRender?(): void;
receiveProps?(): void;
}
@ -96,6 +109,27 @@ declare namespace Omi {
abstract render(props: RenderableProps<P>, data: D): void;
}
abstract class ModelView<P = {}, D = {}> {
constructor();
// Allow static members to reference class type parameters
// https://github.com/Microsoft/TypeScript/issues/24018
static props: object;
static data: object;
props: RenderableProps<P>;
data: D;
host: HTMLElement;
css(): void;
update(): void;
fire(name: string, data?: object): void;
// Abstract methods don't infer argument types
// https://github.com/Microsoft/TypeScript/issues/14887
abstract render(props: RenderableProps<P>, data: D): void;
}
abstract class Component<P = {}, D = {}> {
constructor();

View File

@ -1,5 +1,5 @@
/**
* omi v4.1.7 http://omijs.org
* omi v5.0.0 http://omijs.org
* Omi === Preact + Scoped CSS + Store System + Native Support in 3kb javascript.
* By dntzhang https://github.com/dntzhang
* Github: https://github.com/Tencent/omi
@ -1071,8 +1071,9 @@
this.store.instances.push(this);
}
}
this.beforeInstall();
!this._isInstalled && this.install();
this.afterInstall();
var shadowRoot;
if (!this.shadowRoot) {
shadowRoot = this.attachShadow({
@ -1090,9 +1091,12 @@
!this._isInstalled && this.beforeRender();
options.afterInstall && options.afterInstall(this);
if (this.constructor.observe) {
this.beforeObserve();
proxyUpdate(this);
this.observed();
}
this.host = diff(null, this.render(this.props, this.data, this.store), {}, false, null, false);
this.rendered();
if (isArray(this.host)) {
this.host.forEach(function (item) {
shadowRoot.appendChild(item);
@ -1106,6 +1110,7 @@
WeElement.prototype.disconnectedCallback = function disconnectedCallback() {
this.uninstall();
this._isInstalled = false;
if (this.store) {
for (var i = 0, len = this.store.instances.length; i < len; i++) {
if (this.store.instances[i] === this) {
@ -1122,6 +1127,7 @@
this.beforeRender();
this.host = diff(this.host, this.render(this.props, this.data, this.store), null, null, this.shadowRoot);
this.afterUpdate();
this.updated();
this._willUpdate = false;
};
@ -1129,20 +1135,32 @@
this.dispatchEvent(new CustomEvent(name, { detail: data }));
};
WeElement.prototype.beforeInstall = function beforeInstall() {};
WeElement.prototype.install = function install() {};
WeElement.prototype.afterInstall = function afterInstall() {};
WeElement.prototype.installed = function installed() {};
WeElement.prototype.uninstall = function uninstall() {};
WeElement.prototype.beforeUpdate = function beforeUpdate() {};
WeElement.prototype.afterUpdate = function afterUpdate() {};
WeElement.prototype.afterUpdate = function afterUpdate() {}; //deprecated, please use updated
WeElement.prototype.updated = function updated() {};
WeElement.prototype.beforeRender = function beforeRender() {};
WeElement.prototype.rendered = function rendered() {};
WeElement.prototype.receiveProps = function receiveProps() {};
WeElement.prototype.beforeObserve = function beforeObserve() {};
WeElement.prototype.observed = function observed() {};
return WeElement;
}(HTMLElement);
@ -1442,6 +1460,34 @@
});
}
function _classCallCheck$2(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn$2(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits$2(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var ModelView = function (_WeElement) {
_inherits$2(ModelView, _WeElement);
function ModelView() {
_classCallCheck$2(this, ModelView);
return _possibleConstructorReturn$2(this, _WeElement.apply(this, arguments));
}
ModelView.prototype.beforeInstall = function beforeInstall() {
this.data = this.vm.data;
};
ModelView.prototype.observed = function observed() {
this.vm.data = this.data;
};
return ModelView;
}(WeElement);
ModelView.observe = true;
var Component = WeElement;
var omi = {
@ -1458,11 +1504,12 @@
getHost: getHost,
rpx: rpx,
tick: tick,
nextTick: nextTick
nextTick: nextTick,
ModelView: ModelView
};
options.root.Omi = omi;
options.root.Omi.version = '4.1.7';
options.root.Omi.version = '5.0.0';
if (typeof module != 'undefined') module.exports = omi;else self.Omi = omi;
}());

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/**
* omi v4.1.7 http://omijs.org
* omi v5.0.0 http://omijs.org
* Omi === Preact + Scoped CSS + Store System + Native Support in 3kb javascript.
* By dntzhang https://github.com/dntzhang
* Github: https://github.com/Tencent/omi
@ -1068,8 +1068,9 @@ var WeElement = function (_HTMLElement) {
this.store.instances.push(this);
}
}
this.beforeInstall();
!this._isInstalled && this.install();
this.afterInstall();
var shadowRoot;
if (!this.shadowRoot) {
shadowRoot = this.attachShadow({
@ -1087,9 +1088,12 @@ var WeElement = function (_HTMLElement) {
!this._isInstalled && this.beforeRender();
options.afterInstall && options.afterInstall(this);
if (this.constructor.observe) {
this.beforeObserve();
proxyUpdate(this);
this.observed();
}
this.host = diff(null, this.render(this.props, this.data, this.store), {}, false, null, false);
this.rendered();
if (isArray(this.host)) {
this.host.forEach(function (item) {
shadowRoot.appendChild(item);
@ -1103,6 +1107,7 @@ var WeElement = function (_HTMLElement) {
WeElement.prototype.disconnectedCallback = function disconnectedCallback() {
this.uninstall();
this._isInstalled = false;
if (this.store) {
for (var i = 0, len = this.store.instances.length; i < len; i++) {
if (this.store.instances[i] === this) {
@ -1119,6 +1124,7 @@ var WeElement = function (_HTMLElement) {
this.beforeRender();
this.host = diff(this.host, this.render(this.props, this.data, this.store), null, null, this.shadowRoot);
this.afterUpdate();
this.updated();
this._willUpdate = false;
};
@ -1126,20 +1132,32 @@ var WeElement = function (_HTMLElement) {
this.dispatchEvent(new CustomEvent(name, { detail: data }));
};
WeElement.prototype.beforeInstall = function beforeInstall() {};
WeElement.prototype.install = function install() {};
WeElement.prototype.afterInstall = function afterInstall() {};
WeElement.prototype.installed = function installed() {};
WeElement.prototype.uninstall = function uninstall() {};
WeElement.prototype.beforeUpdate = function beforeUpdate() {};
WeElement.prototype.afterUpdate = function afterUpdate() {};
WeElement.prototype.afterUpdate = function afterUpdate() {}; //deprecated, please use updated
WeElement.prototype.updated = function updated() {};
WeElement.prototype.beforeRender = function beforeRender() {};
WeElement.prototype.rendered = function rendered() {};
WeElement.prototype.receiveProps = function receiveProps() {};
WeElement.prototype.beforeObserve = function beforeObserve() {};
WeElement.prototype.observed = function observed() {};
return WeElement;
}(HTMLElement);
@ -1439,6 +1457,34 @@ function rpx(str) {
});
}
function _classCallCheck$2(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn$2(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits$2(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var ModelView = function (_WeElement) {
_inherits$2(ModelView, _WeElement);
function ModelView() {
_classCallCheck$2(this, ModelView);
return _possibleConstructorReturn$2(this, _WeElement.apply(this, arguments));
}
ModelView.prototype.beforeInstall = function beforeInstall() {
this.data = this.vm.data;
};
ModelView.prototype.observed = function observed() {
this.vm.data = this.data;
};
return ModelView;
}(WeElement);
ModelView.observe = true;
var Component = WeElement;
var omi = {
@ -1455,12 +1501,13 @@ var omi = {
getHost: getHost,
rpx: rpx,
tick: tick,
nextTick: nextTick
nextTick: nextTick,
ModelView: ModelView
};
options.root.Omi = omi;
options.root.Omi.version = '4.1.7';
options.root.Omi.version = '5.0.0';
export default omi;
export { tag, WeElement, Component, render, h, h as createElement, options, define, observe, cloneElement, getHost, rpx, tick, nextTick };
export { tag, WeElement, Component, render, h, h as createElement, options, define, observe, cloneElement, getHost, rpx, tick, nextTick, ModelView };
//# sourceMappingURL=omi.esm.js.map

File diff suppressed because one or more lines are too long

View File

@ -531,6 +531,25 @@
return window.innerWidth * Number(b) / 750 + 'px';
});
}
function _classCallCheck$2(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}
function _possibleConstructorReturn$2(self, call) {
if (!self) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return call && ("object" == typeof call || "function" == typeof call) ? call : self;
}
function _inherits$2(subClass, superClass) {
if ("function" != typeof superClass && null !== superClass) throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: !1,
writable: !0,
configurable: !0
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
var options = {
store: null,
root: function() {
@ -775,7 +794,9 @@
}
if (this.store) this.store.instances.push(this);
}
this.beforeInstall();
!this.B && this.install();
this.afterInstall();
var shadowRoot;
if (!this.shadowRoot) shadowRoot = this.attachShadow({
mode: 'open'
@ -787,8 +808,13 @@
this.css && shadowRoot.appendChild(cssToDom(this.css()));
!this.B && this.beforeRender();
options.afterInstall && options.afterInstall(this);
if (this.constructor.observe) proxyUpdate(this);
if (this.constructor.observe) {
this.beforeObserve();
proxyUpdate(this);
this.observed();
}
this.host = diff(null, this.render(this.props, this.data, this.store), {}, !1, null, !1);
this.rendered();
if (isArray(this.host)) this.host.forEach(function(item) {
shadowRoot.appendChild(item);
}); else shadowRoot.appendChild(this.host);
@ -797,6 +823,7 @@
};
WeElement.prototype.disconnectedCallback = function() {
this.uninstall();
this.B = !1;
if (this.store) for (var i = 0, len = this.store.instances.length; i < len; i++) if (this.store.instances[i] === this) {
this.store.instances.splice(i, 1);
break;
@ -808,6 +835,7 @@
this.beforeRender();
this.host = diff(this.host, this.render(this.props, this.data, this.store), null, null, this.shadowRoot);
this.afterUpdate();
this.updated();
this.J = !1;
};
WeElement.prototype.fire = function(name, data) {
@ -815,16 +843,37 @@
detail: data
}));
};
WeElement.prototype.beforeInstall = function() {};
WeElement.prototype.install = function() {};
WeElement.prototype.afterInstall = function() {};
WeElement.prototype.installed = function() {};
WeElement.prototype.uninstall = function() {};
WeElement.prototype.beforeUpdate = function() {};
WeElement.prototype.afterUpdate = function() {};
WeElement.prototype.updated = function() {};
WeElement.prototype.beforeRender = function() {};
WeElement.prototype.rendered = function() {};
WeElement.prototype.receiveProps = function() {};
WeElement.prototype.beforeObserve = function() {};
WeElement.prototype.observed = function() {};
return WeElement;
}(HTMLElement);
WeElement.is = 'WeElement';
var ModelView = function(_WeElement) {
function ModelView() {
_classCallCheck$2(this, ModelView);
return _possibleConstructorReturn$2(this, _WeElement.apply(this, arguments));
}
_inherits$2(ModelView, _WeElement);
ModelView.prototype.beforeInstall = function() {
this.data = this.vm.data;
};
ModelView.prototype.observed = function() {
this.vm.data = this.data;
};
return ModelView;
}(WeElement);
ModelView.observe = !0;
var Component = WeElement;
var omi = {
tag: tag,
@ -840,10 +889,11 @@
getHost: getHost,
rpx: rpx,
tick: tick,
nextTick: nextTick
nextTick: nextTick,
ModelView: ModelView
};
options.root.Omi = omi;
options.root.Omi.version = '4.1.7';
options.root.Omi.version = '5.0.0';
if ('undefined' != typeof module) module.exports = omi; else self.Omi = omi;
}();
//# sourceMappingURL=omi.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +0,0 @@
import ovd from '../view-data/other'
export function random() {
ovd.update()
}

View File

@ -1,25 +0,0 @@
import todo from '../model/todo'
import tvd from '../view-data/todo'
import ovd from '../view-data/other'
import shared from '../view-data/shared'
export function add(text) {
todo.add(text)
tvd.update(todo)
ovd.update()
tvd.update()
}
export function getAll() {
todo.getAll(function() {
tvd.update(todo)
ovd.update()
tvd.update()
})
}
export function changeSharedData() {
shared.projName = 'I love omi-mvc.'
ovd.update()
tvd.update()
}

View File

@ -1,25 +0,0 @@
import { WeElement, define } from '../../../src/omi'
import vd from '../view-data/other'
import { random } from '../controller/other'
define('other-view', class extends WeElement {
static observe = true
data = vd
onClick = () => {
random()
}
render() {
return (
<div>
<h3>Other View</h3>
<div>{vd.data.num} </div>
<button onClick={this.onClick}>random</button>
<div>Todo List Length: {vd.data.length}</div>
<div>{vd.data.projName}</div>
</div>
)
}
})

View File

@ -1,45 +0,0 @@
import { WeElement, define } from '../../../src/omi'
import { add, getAll, changeSharedData } from '../controller/todo'
import vd from '../view-data/todo'
import './todo-list'
import './other-view'
define('todo-app', class extends WeElement {
static observe = true
data = vd
onClick = () => {
changeSharedData()
}
install() {
getAll()
}
render() {
return (
<div>
<h3>TODO</h3>
<todo-list items={vd.data.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={vd.data.text} />
<button>Add #{vd.data.items.length + 1}</button>
</form>
<div>{vd.data.projName}</div>
<button onClick={this.onClick}>Change Shared Data</button>
<other-view />
</div>
)
}
handleChange = e => {
this.data.text = e.target.value
}
handleSubmit = e => {
e.preventDefault()
add(this.data.text)
this.data.text = ''
}
})

View File

@ -1069,8 +1069,9 @@
this.store.instances.push(this);
}
}
this.beforeInstall();
!this._isInstalled && this.install();
this.afterInstall();
var shadowRoot = void 0;
if (!this.shadowRoot) {
shadowRoot = this.attachShadow({
@ -1088,9 +1089,12 @@
!this._isInstalled && this.beforeRender();
options.afterInstall && options.afterInstall(this);
if (this.constructor.observe) {
this.beforeObserve();
proxyUpdate(this);
this.observed();
}
this.host = diff(null, this.render(this.props, this.data, this.store), {}, false, null, false);
this.rendered();
if (isArray(this.host)) {
this.host.forEach(function (item) {
shadowRoot.appendChild(item);
@ -1104,6 +1108,7 @@
WeElement.prototype.disconnectedCallback = function disconnectedCallback() {
this.uninstall();
this._isInstalled = false;
if (this.store) {
for (var i = 0, len = this.store.instances.length; i < len; i++) {
if (this.store.instances[i] === this) {
@ -1120,6 +1125,7 @@
this.beforeRender();
this.host = diff(this.host, this.render(this.props, this.data, this.store), null, null, this.shadowRoot);
this.afterUpdate();
this.updated();
this._willUpdate = false;
};
@ -1127,20 +1133,32 @@
this.dispatchEvent(new CustomEvent(name, { detail: data }));
};
WeElement.prototype.beforeInstall = function beforeInstall() {};
WeElement.prototype.install = function install() {};
WeElement.prototype.afterInstall = function afterInstall() {};
WeElement.prototype.installed = function installed() {};
WeElement.prototype.uninstall = function uninstall() {};
WeElement.prototype.beforeUpdate = function beforeUpdate() {};
WeElement.prototype.afterUpdate = function afterUpdate() {};
WeElement.prototype.afterUpdate = function afterUpdate() {}; //rendered, please use updated
WeElement.prototype.updated = function updated() {};
WeElement.prototype.beforeRender = function beforeRender() {};
WeElement.prototype.rendered = function rendered() {};
WeElement.prototype.receiveProps = function receiveProps() {};
WeElement.prototype.beforeObserve = function beforeObserve() {};
WeElement.prototype.observed = function observed() {};
return WeElement;
}(HTMLElement), _class.is = 'WeElement', _temp);
@ -1441,6 +1459,35 @@
});
}
var _class$1, _temp$1;
function _classCallCheck$2(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn$2(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits$2(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
//add mapper to this
var ModelView = (_temp$1 = _class$1 = function (_WeElement) {
_inherits$2(ModelView, _WeElement);
function ModelView() {
_classCallCheck$2(this, ModelView);
return _possibleConstructorReturn$2(this, _WeElement.apply(this, arguments));
}
ModelView.prototype.beforeInstall = function beforeInstall() {
this.data = this.vm.data;
};
ModelView.prototype.observed = function observed() {
this.vm.data = this.data;
};
return ModelView;
}(WeElement), _class$1.observe = true, _temp$1);
var Component = WeElement;
var omi = {
@ -1457,19 +1504,86 @@
getHost: getHost,
rpx: rpx,
tick: tick,
nextTick: nextTick
nextTick: nextTick,
ModelView: ModelView
};
options.root.Omi = omi;
options.root.Omi.version = '4.1.7';
options.root.Omi.version = '5.0.0';
function _classCallCheck$2(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Auto map object's props to object's props.
* @method mapper
* @param {Object} options {from: .., to: .., rule: .. }
* @return {Object} To Object
*/
var mapper = function mapper(options) {
var from = options.from;
var to = options.to;
var rules = options.rule;
var res = to || {};
Object.keys(from).forEach(function (key) {
res[key] = from[key];
});
rules && Object.keys(rules).forEach(function (key) {
var rule = rules[key];
var isPath = key.match(/\.|\[/);
if (typeof rule === 'function') {
if (isPath) {
setPathValue(res, key, rule.call(from));
} else {
res[key] = rule.call(from);
}
} else {
if (isPath) {
setPathValue(res, key, rule);
} else {
res[key] = rule;
}
}
});
return res;
};
function setPathValue(obj, path, value) {
var arr = path.replace(/]/g, '').replace(/\[/g, '.').split('.');
var current = obj;
for (var i = 0, len = arr.length; i < len; i++) {
var key = arr[i];
var temp = current[key];
if (i === len - 1) {
current[arr[len - 1]] = value;
} else {
if (temp === undefined) {
if (isNaN(Number(arr[i + 1]))) {
current[key] = {};
} else {
current[key] = [];
}
temp = current[key];
}
}
current = temp;
}
}
var shared = {
projName: 'omi-mvc'
};
function _classCallCheck$3(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var id$1 = 0;
var TodoItem = function () {
function TodoItem(text, completed) {
_classCallCheck$2(this, TodoItem);
_classCallCheck$3(this, TodoItem);
this.id = id$1++;
this.text = text;
@ -1505,11 +1619,11 @@
});
}
function _classCallCheck$3(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _classCallCheck$4(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Todo = function () {
function Todo() {
_classCallCheck$3(this, Todo);
_classCallCheck$4(this, Todo);
this.items = [];
}
@ -1575,87 +1689,48 @@
var todo = new Todo();
/**
* Auto map object's props to object's props.
* @method mapper
* @param {Object} options {from: .., to: .., rule: .. }
* @return {Object} To Object
*/
var mapper = function mapper(options) {
var from = options.from;
var to = options.to;
var rules = options.rule;
function _classCallCheck$5(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var res = to || {};
var OtherViewData = function () {
function OtherViewData() {
_classCallCheck$5(this, OtherViewData);
Object.keys(from).forEach(function (key) {
res[key] = from[key];
});
this.data = {
num: 0,
length: 0
};
}
rules && Object.keys(rules).forEach(function (key) {
var rule = rules[key];
var isPath = key.match(/\.|\[/);
if (typeof rule === 'function') {
if (isPath) {
setPathValue(res, key, rule.call(from));
} else {
res[key] = rule.call(from);
}
} else {
if (isPath) {
setPathValue(res, key, rule);
} else {
res[key] = rule;
}
}
});
return res;
};
OtherViewData.prototype.update = function update() {
this.data.num = Math.random();
this.data.projName = shared.projName;
this.data.length = vd$1.data.items.length;
};
function setPathValue(obj, path, value) {
var arr = path.replace(/]/g, '').replace(/\[/g, '.').split('.');
OtherViewData.prototype.random = function random() {
this.update();
};
var current = obj;
for (var i = 0, len = arr.length; i < len; i++) {
var key = arr[i];
var temp = current[key];
if (i === len - 1) {
current[arr[len - 1]] = value;
} else {
if (temp === undefined) {
if (isNaN(Number(arr[i + 1]))) {
current[key] = {};
} else {
current[key] = [];
}
return OtherViewData;
}();
temp = current[key];
}
}
var vd = new OtherViewData();
current = temp;
}
}
var shared = {
projName: 'omi-mvc'
};
function _classCallCheck$4(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _classCallCheck$6(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var TodoViewData = function () {
function TodoViewData() {
_classCallCheck$4(this, TodoViewData);
_classCallCheck$6(this, TodoViewData);
this.data = {
items: []
};
}
TodoViewData.prototype.update = function update(todo) {
TodoViewData.prototype.update = function update(todo$$1) {
var _this = this;
todo && todo.items.forEach(function (item, index) {
todo$$1 && todo$$1.items.forEach(function (item, index) {
_this.data.items[index] = mapper({
from: item,
to: _this.data.items[index],
@ -1670,54 +1745,33 @@
this.data.projName = shared.projName;
};
TodoViewData.prototype.add = function add(text) {
todo.add(text);
this.update(todo);
vd.update();
this.update();
};
TodoViewData.prototype.getAll = function getAll() {
var _this2 = this;
todo.getAll(function () {
_this2.update(todo);
vd.update();
_this2.update();
});
};
TodoViewData.prototype.changeSharedData = function changeSharedData() {
shared.projName = 'I love omi-mvc.';
vd.update();
this.update();
};
return TodoViewData;
}();
var vd = new TodoViewData();
function _classCallCheck$5(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var OtherViewData = function () {
function OtherViewData() {
_classCallCheck$5(this, OtherViewData);
this.data = {
num: 0,
length: 0
};
}
OtherViewData.prototype.update = function update() {
this.data.num = Math.random();
this.data.projName = shared.projName;
this.data.length = vd.data.items.length;
};
return OtherViewData;
}();
var vd$1 = new OtherViewData();
function add$1(text) {
todo.add(text);
vd.update(todo);
vd$1.update();
vd.update();
}
function getAll$1() {
todo.getAll(function () {
vd.update(todo);
vd$1.update();
vd.update();
});
}
function changeSharedData() {
shared.projName = 'I love omi-mvc.';
vd$1.update();
vd.update();
}
var vd$1 = new TodoViewData();
define('todo-list', function (props) {
this.useCss('\n\t span{\n\t\t\tcolor: #888;\n\t\t\tfont-size: 11px;\n\t\t}\n\t');
@ -1742,48 +1796,42 @@
);
});
function random() {
vd$1.update();
}
function _classCallCheck$7(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var _class$1, _temp2;
function _possibleConstructorReturn$3(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _classCallCheck$6(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits$3(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _possibleConstructorReturn$2(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
define('other-view', function (_ModelView) {
_inherits$3(_class2, _ModelView);
function _inherits$2(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
define('other-view', (_temp2 = _class$1 = function (_WeElement) {
_inherits$2(_class, _WeElement);
function _class() {
function _class2() {
var _temp, _this, _ret;
_classCallCheck$6(this, _class);
_classCallCheck$7(this, _class2);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn$2(this, _WeElement.call.apply(_WeElement, [this].concat(args))), _this), _this.data = vd$1, _this.onClick = function () {
random();
}, _temp), _possibleConstructorReturn$2(_this, _ret);
return _ret = (_temp = (_this = _possibleConstructorReturn$3(this, _ModelView.call.apply(_ModelView, [this].concat(args))), _this), _this.vm = vd, _this.onClick = function () {
vd.random();
}, _temp), _possibleConstructorReturn$3(_this, _ret);
}
_class.prototype.render = function render$$1() {
_class2.prototype.render = function render$$1(props, data) {
return Omi.h(
'div',
null,
Omi.h(
'h3',
null,
'Other View'
'Other View2'
),
Omi.h(
'div',
null,
vd$1.data.num,
data.num,
' '
),
Omi.h(
@ -1795,55 +1843,53 @@
'div',
null,
'Todo List Length: ',
vd$1.data.length
data.length
),
Omi.h(
'div',
null,
vd$1.data.projName
data.projName
)
);
};
return _class;
}(WeElement), _class$1.observe = true, _temp2));
return _class2;
}(ModelView));
var _class$2, _temp2$1;
function _classCallCheck$8(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _classCallCheck$7(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn$4(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _possibleConstructorReturn$3(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits$4(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _inherits$3(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
define('todo-app', function (_ModelView) {
_inherits$4(_class2, _ModelView);
define('todo-app', (_temp2$1 = _class$2 = function (_WeElement) {
_inherits$3(_class, _WeElement);
function _class() {
function _class2() {
var _temp, _this, _ret;
_classCallCheck$7(this, _class);
_classCallCheck$8(this, _class2);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn$3(this, _WeElement.call.apply(_WeElement, [this].concat(args))), _this), _this.data = vd, _this.onClick = function () {
changeSharedData();
return _ret = (_temp = (_this = _possibleConstructorReturn$4(this, _ModelView.call.apply(_ModelView, [this].concat(args))), _this), _this.vm = vd$1, _this.onClick = function () {
vd$1.changeSharedData();
}, _this.handleChange = function (e) {
_this.data.text = e.target.value;
}, _this.handleSubmit = function (e) {
e.preventDefault();
add$1(_this.data.text);
vd$1.add(_this.data.text);
_this.data.text = '';
}, _temp), _possibleConstructorReturn$3(_this, _ret);
}, _temp), _possibleConstructorReturn$4(_this, _ret);
}
_class.prototype.install = function install() {
getAll$1();
_class2.prototype.install = function install() {
vd$1.getAll();
};
_class.prototype.render = function render$$1() {
_class2.prototype.render = function render$$1(props, data) {
return Omi.h(
'div',
null,
@ -1852,22 +1898,22 @@
null,
'TODO'
),
Omi.h('todo-list', { items: vd.data.items }),
Omi.h('todo-list', { items: data.items }),
Omi.h(
'form',
{ onSubmit: this.handleSubmit },
Omi.h('input', { onChange: this.handleChange, value: vd.data.text }),
Omi.h('input', { onChange: this.handleChange, value: data.text }),
Omi.h(
'button',
null,
'Add #',
vd.data.items.length + 1
data.items.length + 1
)
),
Omi.h(
'div',
null,
vd.data.projName
data.projName
),
Omi.h(
'button',
@ -1878,8 +1924,8 @@
);
};
return _class;
}(WeElement), _class$2.observe = true, _temp2$1));
return _class2;
}(ModelView));
render(Omi.h('todo-app', null), 'body');

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,10 @@ class OtherViewData {
this.data.projName = shared.projName
this.data.length = tvd.data.items.length
}
random() {
this.update()
}
}
const vd = new OtherViewData()

View File

@ -1,5 +1,7 @@
import mapper from './mapper'
import shared from './shared'
import todo from '../model/todo'
import ovd from './other'
class TodoViewData {
constructor() {
@ -24,6 +26,27 @@ class TodoViewData {
this.data.projName = shared.projName
}
add(text) {
todo.add(text)
this.update(todo)
ovd.update()
this.update()
}
getAll() {
todo.getAll(() => {
this.update(todo)
ovd.update()
this.update()
})
}
changeSharedData() {
shared.projName = 'I love omi-mvc.'
ovd.update()
this.update()
}
}
const vd = new TodoViewData()

View File

@ -0,0 +1,22 @@
import { ModelView, define } from '../../../src/omi'
import vm from '../view-model/other'
define('other-view', class extends ModelView {
vm = vm
onClick = () => {
vm.random()
}
render(props, data) {
return (
<div>
<h3>Other View2</h3>
<div>{data.num} </div>
<button onClick={this.onClick}>random</button>
<div>Todo List Length: {data.length}</div>
<div>{data.projName}</div>
</div>
)
}
})

View File

@ -0,0 +1,42 @@
import { ModelView, define } from '../../../src/omi'
import vm from '../view-model/todo'
import './todo-list'
import './other-view'
define('todo-app', class extends ModelView {
vm = vm
onClick = () => {
vm.changeSharedData()
}
install() {
vm.getAll()
}
render(props, data) {
return (
<div>
<h3>TODO</h3>
<todo-list items={data.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={data.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.data.text = e.target.value
}
handleSubmit = e => {
e.preventDefault()
vm.add(this.data.text)
this.data.text = ''
}
})

View File

@ -1,6 +1,6 @@
{
"name": "omi",
"version": "4.1.7",
"version": "5.0.0",
"description": "Next generation web framework.",
"main": "dist/omi.js",
"jsnext:main": "dist/omi.esm.js",
@ -16,7 +16,7 @@
"use": "rollup -c config/rollup.example.js --watch",
"decorators": "rollup -c config/rollup.example.js --watch",
"mobx": "rollup -c config/rollup.example.js --watch",
"mvc": "rollup -c config/rollup.example.js --watch",
"mvvm": "rollup -c config/rollup.example.js --watch",
"receive-props": "rollup -c config/rollup.example.js --watch",
"rpx": "rollup -c config/rollup.example.js --watch",
"tree": "rollup -c config/rollup.example.js --watch",

View File

@ -0,0 +1,13 @@
import WeElement from './we-element'
export default class ModelView extends WeElement {
static observe = true
beforeInstall() {
this.data = this.vm.data
}
observed() {
this.vm.data = this.data
}
}

View File

@ -61,6 +61,18 @@ declare namespace Omi {
uninstall?(): void;
beforeUpdate?(): void;
afterUpdate?(): void;
updated?(): void;
beforeRender?(): void;
receiveProps?(): void;
}
interface ModelView<P, D> {
install?(): void;
installed?(): void;
uninstall?(): void;
beforeUpdate?(): void;
afterUpdate?(): void;
updated?(): void;
beforeRender?(): void;
receiveProps?(): void;
}
@ -71,6 +83,7 @@ declare namespace Omi {
uninstall?(): void;
beforeUpdate?(): void;
afterUpdate?(): void;
updated?(): void;
beforeRender?(): void;
receiveProps?(): void;
}
@ -96,6 +109,27 @@ declare namespace Omi {
abstract render(props: RenderableProps<P>, data: D): void;
}
abstract class ModelView<P = {}, D = {}> {
constructor();
// Allow static members to reference class type parameters
// https://github.com/Microsoft/TypeScript/issues/24018
static props: object;
static data: object;
props: RenderableProps<P>;
data: D;
host: HTMLElement;
css(): void;
update(): void;
fire(name: string, data?: object): void;
// Abstract methods don't infer argument types
// https://github.com/Microsoft/TypeScript/issues/14887
abstract render(props: RenderableProps<P>, data: D): void;
}
abstract class Component<P = {}, D = {}> {
constructor();

View File

@ -9,6 +9,7 @@ import { cloneElement } from './clone-element'
import { getHost } from './get-host'
import { rpx } from './rpx'
import { tick, nextTick } from './tick'
import ModelView from './model-view'
const Component = WeElement
@ -26,11 +27,12 @@ const omi = {
getHost,
rpx,
tick,
nextTick
nextTick,
ModelView
}
options.root.Omi = omi
options.root.Omi.version = '4.1.7'
options.root.Omi.version = '5.0.0'
export default omi
@ -48,5 +50,6 @@ export {
getHost,
rpx,
tick,
nextTick
nextTick,
ModelView
}

View File

@ -29,8 +29,9 @@ export default class WeElement extends HTMLElement {
this.store.instances.push(this)
}
}
this.beforeInstall()
!this._isInstalled && this.install()
this.afterInstall()
let shadowRoot
if (!this.shadowRoot) {
shadowRoot = this.attachShadow({
@ -48,7 +49,9 @@ export default class WeElement extends HTMLElement {
!this._isInstalled && this.beforeRender()
options.afterInstall && options.afterInstall(this)
if (this.constructor.observe) {
this.beforeObserve()
proxyUpdate(this)
this.observed()
}
this.host = diff(
null,
@ -58,6 +61,7 @@ export default class WeElement extends HTMLElement {
null,
false
)
this.rendered()
if (isArray(this.host)) {
this.host.forEach(function(item) {
shadowRoot.appendChild(item)
@ -71,6 +75,7 @@ export default class WeElement extends HTMLElement {
disconnectedCallback() {
this.uninstall()
this._isInstalled = false
if (this.store) {
for (let i = 0, len = this.store.instances.length; i < len; i++) {
if (this.store.instances[i] === this) {
@ -93,6 +98,7 @@ export default class WeElement extends HTMLElement {
this.shadowRoot
)
this.afterUpdate()
this.updated()
this._willUpdate = false
}
@ -100,17 +106,29 @@ export default class WeElement extends HTMLElement {
this.dispatchEvent(new CustomEvent(name, { detail: data }))
}
beforeInstall() {}
install() {}
afterInstall() {}
installed() {}
uninstall() {}
beforeUpdate() {}
afterUpdate() {}
afterUpdate() {} //deprecated, please use updated
updated() {}
beforeRender() {}
rendered() {}
receiveProps() {}
beforeObserve() {}
observed() {}
}

View File

@ -1,27 +0,0 @@
## 基于 Omi 的 MVC + ViewData 架构设计,分离 Web 视图与业务逻辑
从宏观的角度来看MVC + ViewData 架构也属性网状架构,网状架构目前来看有:
* Mobx + React
* Hooks + React
* MVC + ViewData + Omi
大势所趋!简直是前端工程化最佳实践!也可以理解成网状结构是描述和抽象世界的最佳途径。那么网在哪?
* ViewData 与 ViewData 之间相互依赖甚至循环依赖的网状结构
* ViewData 一对一、多对一、一对多、多对多依赖 Models 形成网状结构
* Model 与 Model 之间形成相互依赖甚至循环依赖的网状结构
* View 一对一依赖 ViewData 形成网状结构
* View 多对多依赖 Controller 形成网状结构
* Controller 多对多依赖 Model 和 ViewData 形成网状结构
总结如下:
|| Model| ViewData|View| Controller|
|---|----|----|
|Model|多对多|多对多|无关联|多对多|
|ViewData|多对多|多对多|一对一|多对多|
|View|无关联|一多一|多对多|多对多|
图形描述如下:

25
tutorial/omi-mvvm.cn.md Normal file
View File

@ -0,0 +1,25 @@
## Web 前端 MVVM 王者归来- 基于 Omi 分离视图与业务逻辑的架构设计
Omi 正式发布 5.0,依然专注于 View但是对 MVVM 架构更加友好的集成。从宏观的角度来看Omi 的 MVVM 架构也属性网状架构,网状架构目前来看有:
* Mobx + React
* Hooks + React
* MVVM (Omi)
大势所趋!简直是前端工程化最佳实践!也可以理解成网状结构是描述和抽象世界的最佳途径。那么网在哪?
* ViewModel 与 ViewModel 之间相互依赖甚至循环依赖的网状结构
* ViewModel 一对一、多对一、一对多、多对多依赖 Models 形成网状结构
* Model 与 Model 之间形成相互依赖甚至循环依赖的网状结构
* View 一对一依赖 ViewModel 形成网状结构
总结如下:
|| Model| ViewModel|View|
|---|----|----|
|Model|多对多|多对多|无关联|
|ViewModel|多对多|多对多|一对一|
|View|无关联|一多一|多对多|
图形描述如下: