feat: multi-store injection supported in omio

This commit is contained in:
dntzhang 2019-10-08 10:34:09 +08:00
parent 4b0f364a62
commit 902a75f2ce
14 changed files with 2599 additions and 184 deletions

View File

@ -1,7 +1,7 @@
import WeElement from './we-element'
import options from './options'
export function define(name, ctor) {
export function define(name, ctor, config) {
if (options.mapping[name]) {
return
}
@ -10,24 +10,11 @@ export function define(name, ctor) {
options.mapping[name] = ctor
} else {
let depPaths
let config = {}
const len = arguments.length
if (len === 3) {
if (typeof arguments[1] === 'function') {
ctor = arguments[1]
config = arguments[2]
} else {
depPaths = arguments[1]
ctor = arguments[2]
}
} else if (len === 4) {
depPaths = arguments[1]
ctor = arguments[2]
config = arguments[3]
}
if (typeof config === 'string') {
config = { css: config }
} else {
config = config || { }
}
class Ele extends WeElement {

View File

@ -162,7 +162,7 @@ declare namespace Omi {
function render(vnode: ComponentChild, parent: string | Element | Document | ShadowRoot | DocumentFragment, store?: object): any;
function define(name: string, ctorOrDepPaths: any[] | WeElementConstructor, ctorOrOptions?: WeElementConstructor | string | object, cssStringOrOptions?: string | object): void;
function define(name: string, ctor: WeElementConstructor, cssStringOrOptions?: string | object): void;
function defineElement(name: string, ctorOrDepPaths: any[] | WeElementConstructor, ctorOrOptions?: WeElementConstructor | string | object, cssStringOrOptions?: string | object): void;
function tag(name: string, pure?: boolean): (ctor: WeElementConstructor) => void;
function tick(callback: Callback, scope?: any): void;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<meta charset="UTF-8" />
<title>Counter</title>
</head>
<body>
<script src="b.js"></script>
<a href="https://github.com/Tencent/omi" target="_blank" style="position: fixed; right: 0; top: 0; z-index: 3;">
<img src="//alloyteam.github.io/github.png" alt="">
</a>
</body>
</html>

View File

@ -0,0 +1,52 @@
import { define, render } from '../../src/omi'
define('my-app', _ => {
const store = _.store.storeA
const { data, add, sub } = store
return (
<p>
Clicked: {data.count} times
<button onClick={add}>+</button>
<button onClick={sub}>-</button>
<div>
{_.store.storeB.data.msg}
<button onClick={_.store.storeB.changeMsg}>
change storeB's msg
</button>
</div>
</p>
)
}, {
useSelf: {
storeA: ['count', 'adding'],
storeB: ['msg']
}
})
const storeA = new class Store {
data = {
count: 0,
adding: false
}
sub = () => {
this.data.count--
}
add = () => {
this.data.count++
}
}
const storeB = new class Store {
data = {
msg: 'abc'
}
changeMsg = () => {
this.data.msg = 'bcd'
}
}
render( <my-app /> , 'body', {
storeA,
storeB
})

View File

@ -1,38 +1,19 @@
import options from './options'
import Component from './component'
const OBJECTTYPE = '[object Object]'
const ARRAYTYPE = '[object Array]'
export function define(name, ctor) {
export function define(name, ctor, config) {
if(ctor.is === 'WeElement'){
options.mapping[name] = ctor
if (ctor.use) {
ctor.updatePath = getPath(ctor.use)
}
} else {
let depPaths
let config = {}
const len = arguments.length
if(len === 3){
if(typeof arguments[1] === 'function'){
ctor = arguments[1]
config = arguments[2]
} else {
depPaths = arguments[1]
ctor = arguments[2]
}
} else if(len === 4){
depPaths = arguments[1]
ctor = arguments[2]
config = arguments[3]
}
if(typeof config === 'string'){
config = { css: config }
}
} else {
config = config || {}
}
class Comp extends Component {
static use = depPaths
static css = config.css
@ -104,79 +85,6 @@ export function define(name, ctor) {
}
}
if(Comp.use){
Comp.updatePath = getPath(Comp.use)
}
options.mapping[name] = Comp
}
}
export function getPath(obj) {
if (Object.prototype.toString.call(obj) === '[object Array]') {
const result = {}
obj.forEach(item => {
if (typeof item === 'string') {
result[item] = true
} else {
const tempPath = item[Object.keys(item)[0]]
if (typeof tempPath === 'string') {
result[tempPath] = true
} else {
if(typeof tempPath[0] === 'string'){
result[tempPath[0]] = true
}else{
tempPath[0].forEach(path => result[path] = true)
}
}
}
})
return result
} else {
return getUpdatePath(obj)
}
}
export function getUpdatePath(data) {
const result = {}
dataToPath(data, result)
return result
}
function dataToPath(data, result) {
Object.keys(data).forEach(key => {
result[key] = true
const type = Object.prototype.toString.call(data[key])
if (type === OBJECTTYPE) {
_objToPath(data[key], key, result)
} else if (type === ARRAYTYPE) {
_arrayToPath(data[key], key, result)
}
})
}
function _objToPath(data, path, result) {
Object.keys(data).forEach(key => {
result[path + '.' + key] = true
delete result[path]
const type = Object.prototype.toString.call(data[key])
if (type === OBJECTTYPE) {
_objToPath(data[key], path + '.' + key, result)
} else if (type === ARRAYTYPE) {
_arrayToPath(data[key], path + '.' + key, result)
}
})
}
function _arrayToPath(data, path, result) {
data.forEach((item, index) => {
result[path + '[' + index + ']'] = true
delete result[path]
const type = Object.prototype.toString.call(item)
if (type === OBJECTTYPE) {
_objToPath(item, path + '[' + index + ']', result)
} else if (type === ARRAYTYPE) {
_arrayToPath(item, path + '[' + index + ']', result)
}
})
}

View File

@ -150,7 +150,7 @@ declare namespace Omi {
function render(vnode: ComponentChild, parent: string | Element | Document | ShadowRoot | DocumentFragment, store?: object, empty?: boolean, merge?: string | Element | Document | ShadowRoot | DocumentFragment): void;
function define(name: string, ctor: WeElementConstructor): void;
function define(name: string, ctor: WeElementConstructor, cssStringOrOptions?: string | object): void;
function tag(name: string, pure?: boolean): (ctor: WeElementConstructor) => void;
function tick(callback: Callback, scope?: any): void;
function nextTick(callback: Callback, scope?: any): void;

View File

@ -31,7 +31,8 @@ export default {
doc: typeof document === 'object' ? document : null,
root: getGlobal(),
//styleCache :[{ctor:ctor,ctorName:ctorName,style:style}]
styleCache: []
styleCache: [],
isMultiStore: false
//componentChange(component, element) { },
/** If `true`, `prop` changes trigger synchronous component updates.
* @name syncComponentUpdates

View File

@ -1,6 +1,7 @@
import { diff } from './vdom/diff'
import obaa from './obaa'
import { getUse } from './util'
import options from './options'
/** Render JSX into a `parent` Element.
* @param {VNode} vnode A (JSX) VNode to render
@ -10,7 +11,18 @@ import { getUse } from './util'
*/
export function render(vnode, parent, store, empty, merge) {
parent = typeof parent === 'string' ? document.querySelector(parent) : parent
obsStore(store)
if(store && store.data){
obsStore(store)
} else {
options.isMultiStore = true
for(let key in store){
if(store[key].data){
obsStore(store[key], key)
}
}
}
if (empty) {
while (parent.firstChild) {
@ -28,19 +40,18 @@ export function render(vnode, parent, store, empty, merge) {
return diff(merge, vnode, store, false, parent, false)
}
function obsStore(store){
if (store && store.data) {
store.instances = []
store.updateSelfInstances = []
extendStoreUpate(store)
function obsStore(store, storeName){
store.instances = []
store.updateSelfInstances = []
extendStoreUpate(store, storeName)
obaa(store.data, (prop, val, old, path) => {
const patchs = {}
const key = fixPath(path + '-' + prop)
patchs[key] = true
store.update(patchs)
})
}
obaa(store.data, (prop, val, old, path) => {
const patchs = {}
const key = fixPath(path + '-' + prop)
patchs[key] = true
store.update(patchs)
})
}
export function merge(vnode, merge, store) {
@ -54,34 +65,44 @@ export function merge(vnode, merge, store) {
return diff(merge, vnode, store)
}
function extendStoreUpate(store) {
function extendStoreUpate(store, key) {
store.update = function(patch) {
const updateAll = matchGlobalData(this.globalData, patch)
if (Object.keys(patch).length > 0) {
this.instances.forEach(instance => {
if (
updateAll ||
this.updateAll ||
(instance.constructor.updatePath &&
needUpdate(patch, instance.constructor.updatePath)) ||
(instance._updatePath &&
needUpdate(patch, instance._updatePath))
) {
//update this.use
if(instance.constructor.use){
instance.using = getUse(store.data, instance.constructor.use)
} else if(instance.use){
instance.using = getUse(store.data, typeof instance.use === 'function' ? instance.use() : instance.use)
}
if (key) {
if ((
instance._updatePath && instance._updatePath[key] && needUpdate(patch, instance._updatePath[key]))) {
if (instance.use) {
getUse(store.data, (typeof instance.use === 'function' ? instance.use() : instance.use)[key], instance.using, key)
}
instance.update()
instance.update()
}
} else {
if (instance._updatePath && needUpdate(patch, instance._updatePath)) {
if(instance.use){
instance.using = getUse(store.data, typeof instance.use === 'function' ? instance.use() : instance.use)
}
instance.update()
}
}
})
this.updateSelfInstances.forEach(instance => {
if (instance._updateSelfPath && needUpdate(patch, instance._updateSelfPath)) {
instance.usingSelf = getUse(store.data, typeof instance.useSelf === 'function' ? instance.useSelf() : instance.useSelf)
instance.updateSelf()
if (key) {
if ((
instance._updateSelfPath && instance._updateSelfPath[key] && needUpdate(patch, instance._updateSelfPath[key]))) {
if (instance.useSelf) {
getUse(store.data, (typeof instance.useSelf === 'function' ? instance.useSelf() : instance.useSelf)[key], instance.usingSelf, key)
}
instance.updateSelf()
}
} else {
if (instance._updateSelfPath && needUpdate(patch, instance._updateSelfPath)) {
instance.usingSelf = getUse(store.data, typeof instance.useSelf === 'function' ? instance.useSelf() : instance.useSelf)
instance.updateSelf()
}
}
})
@ -90,20 +111,6 @@ function extendStoreUpate(store) {
}
}
export function matchGlobalData(globalData, diffResult) {
if (!globalData) return false
for (let keyA in diffResult) {
if (globalData.indexOf(keyA) > -1) {
return true
}
for (let i = 0, len = globalData.length; i < len; i++) {
if (includePath(keyA, globalData[i])) {
return true
}
}
}
return false
}
export function needUpdate(diffResult, updatePath) {
for (let keyA in diffResult) {

View File

@ -186,7 +186,7 @@ export function nProps(props) {
return result
}
export function getUse(data, paths) {
export function getUse(data, paths, out, name) {
const obj = []
paths.forEach((path, index) => {
const isPath = typeof path === 'string'
@ -213,6 +213,7 @@ export function getUse(data, paths) {
obj[key] = obj[index]
}
})
out && (out[name] = obj)
return obj
}
@ -224,3 +225,29 @@ export function getTargetByPath(origin, path) {
}
return current
}
export function getPath(obj, out, name) {
const result = {}
obj.forEach(item => {
if (typeof item === 'string') {
result[item] = true
} else {
const tempPath = item[Object.keys(item)[0]]
if (typeof tempPath === 'string') {
result[tempPath] = true
} else {
if(typeof tempPath[0] === 'string'){
result[tempPath[0]] = true
}else{
tempPath[0].forEach(path => result[path] = true)
}
}
}
})
out && (out[name] = result)
return result
}

View File

@ -1,6 +1,6 @@
import Component from '../component'
import { getUse } from '../util'
import { getPath } from '../define'
import { getUse, getPath } from '../util'
import options from '../options'
/** Retains a pool of Components for re-use, keyed on component name.
* Note: since component names are not unique or even necessarily available, these are primarily a form of sharding.
* @private
@ -28,22 +28,47 @@ export function createComponent(Ctor, props, context, vnode) {
}
vnode && (inst.scopedCssAttr = vnode.css)
if ( inst.store && inst.store.data) {
if(inst.constructor.use){
inst.using = getUse(inst.store.data, inst.constructor.use)
inst.store.instances.push(inst)
} else if(inst.use){
const use = typeof inst.use === 'function' ? inst.use() : inst.use
inst._updatePath = getPath(use)
inst.using = getUse(inst.store.data, use)
inst.store.instances.push(inst)
if (inst.store) {
if(inst.use){
const use = typeof inst.use === 'function' ? inst.use() : inst.use
if(options.isMultiStore){
let _updatePath = {}
let using = {}
for (let storeName in use) {
_updatePath[storeName] = {}
using[storeName] = {}
getPath(use[storeName], _updatePath, storeName)
getUse(inst.store[storeName].data, use[storeName], using, storeName)
inst.store[storeName].instances.push(inst)
}
inst.using = using
inst._updatePath = _updatePath
}else{
inst._updatePath = getPath(use)
inst.using = getUse(inst.store.data, use)
inst.store.instances.push(inst)
}
}
if(inst.useSelf){
const use = typeof inst.useSelf === 'function' ? inst.useSelf() : inst.useSelf
inst._updateSelfPath = getPath(use)
inst.usingSelf = getUse(inst.store.data, use)
inst.store.updateSelfInstances.push(inst)
const use = typeof inst.useSelf === 'function' ? inst.useSelf() : inst.useSelf
if (options.isMultiStore) {
let _updatePath = {}
let using = {}
for (let storeName in use) {
getPath(use[storeName], _updatePath, storeName)
getUse(inst.store[storeName].data, use[storeName], using, storeName)
inst.store[storeName].updateSelfInstances.push(inst)
}
inst.usingSelf = using
inst._updateSelfPath = _updatePath
} else {
inst._updateSelfPath = getPath(use)
inst.usingSelf = getUse(inst.store.data, use)
inst.store.updateSelfInstances.push(inst)
}
}

View File

@ -49,7 +49,7 @@ describe('install()', () => {
expect(scratch.innerHTML).to.equal('<div>Ele</div>')
})
it('should render components', () => {
it('should render components b', () => {
define('my-ele', () => {
return <div>Ele2</div>
})
@ -63,7 +63,7 @@ describe('install()', () => {
it('should render components', () => {
it('should render components c', () => {
define('my-ele', _ => {
return <div>{_.props.msg}</div>
})

View File

@ -52,7 +52,7 @@ h1{
}
}
Preact.render(<Comp />, document.querySelector('#root3'))
Preact.render(<Comp />, document.querySelector('#root'))
```
## How to use rpx unit like omi ?
@ -107,7 +107,7 @@ class Comp extends Preact.Component {
}
}
Preact.render(<Comp />, document.querySelector('#root3'))
Preact.render(<Comp />, document.querySelector('#root'))
```
## Related links