mirror of
https://github.com/MercuryWorkshop/dreamlandjs.git
synced 2025-05-16 23:50:00 -04:00
another round of optimizations
This commit is contained in:
parent
c09027d0c3
commit
00995d4819
1 changed files with 38 additions and 32 deletions
64
src/js.js
64
src/js.js
|
@ -1,9 +1,12 @@
|
||||||
import { assert } from "./asserts";
|
import { assert } from "./asserts";
|
||||||
|
|
||||||
const Fragment = Symbol();
|
// enables a small terser optimization
|
||||||
|
let document = self.document;
|
||||||
|
|
||||||
|
let Fragment = Symbol();
|
||||||
|
|
||||||
// We add some extra properties into various objects throughout, better to use symbols and not interfere. this is just a tiny optimization
|
// We add some extra properties into various objects throughout, better to use symbols and not interfere. this is just a tiny optimization
|
||||||
const [USE_MAPFN, TARGET, PROXY, STEPS, LISTENERS, IF] = [, , , , , ,].fill().map(Symbol);
|
let [USE_MAPFN, TARGET, PROXY, STEPS, LISTENERS, IF] = [, , , , , ,].fill().map(Symbol);
|
||||||
|
|
||||||
|
|
||||||
// whether to return the true value from a stateful object or a "trap" containing the pointer
|
// whether to return the true value from a stateful object or a "trap" containing the pointer
|
||||||
|
@ -39,7 +42,7 @@ Object.defineProperty(window, "use", {
|
||||||
});
|
});
|
||||||
Object.assign(window, { isDLPtr, h, stateful, handle, $if, Fragment });
|
Object.assign(window, { isDLPtr, h, stateful, handle, $if, Fragment });
|
||||||
|
|
||||||
const TRAPS = new Map;
|
let TRAPS = new Map;
|
||||||
// This wraps the target in a proxy, doing 2 things:
|
// This wraps the target in a proxy, doing 2 things:
|
||||||
// - whenever a property is accessed, return a "trap" that catches and records accessors
|
// - whenever a property is accessed, return a "trap" that catches and records accessors
|
||||||
// - whenever a property is set, notify the subscribed listeners
|
// - whenever a property is set, notify the subscribed listeners
|
||||||
|
@ -48,8 +51,9 @@ function stateful(target, hook) {
|
||||||
assert(target instanceof Object, "stateful() requires an object");
|
assert(target instanceof Object, "stateful() requires an object");
|
||||||
target[LISTENERS] = [];
|
target[LISTENERS] = [];
|
||||||
target[TARGET] = target;
|
target[TARGET] = target;
|
||||||
|
let TOPRIMITIVE = Symbol.toPrimitive;
|
||||||
|
|
||||||
const proxy = new Proxy(target, {
|
let proxy = new Proxy(target, {
|
||||||
get(target, property, proxy) {
|
get(target, property, proxy) {
|
||||||
if (__use_trap) {
|
if (__use_trap) {
|
||||||
let sym = Symbol();
|
let sym = Symbol();
|
||||||
|
@ -57,10 +61,10 @@ function stateful(target, hook) {
|
||||||
[TARGET]: target,
|
[TARGET]: target,
|
||||||
[PROXY]: proxy,
|
[PROXY]: proxy,
|
||||||
[STEPS]: [property],
|
[STEPS]: [property],
|
||||||
[Symbol.toPrimitive]: () => sym,
|
[TOPRIMITIVE]: _ => sym,
|
||||||
}, {
|
}, {
|
||||||
get(target, property) {
|
get(target, property) {
|
||||||
if ([TARGET, PROXY, STEPS, USE_MAPFN, Symbol.toPrimitive].includes(property)) return target[property];
|
if ([TARGET, PROXY, STEPS, USE_MAPFN, TOPRIMITIVE].includes(property)) return target[property];
|
||||||
property = TRAPS.get(property) || property;
|
property = TRAPS.get(property) || property;
|
||||||
target[STEPS].push(property);
|
target[STEPS].push(property);
|
||||||
return trap;
|
return trap;
|
||||||
|
@ -75,7 +79,7 @@ function stateful(target, hook) {
|
||||||
set(target, property, val) {
|
set(target, property, val) {
|
||||||
if (hook) hook(target, property, val);
|
if (hook) hook(target, property, val);
|
||||||
let trap = Reflect.set(target, property, val);
|
let trap = Reflect.set(target, property, val);
|
||||||
for (const listener of target[LISTENERS]) {
|
for (let listener of target[LISTENERS]) {
|
||||||
listener(target, property, val);
|
listener(target, property, val);
|
||||||
}
|
}
|
||||||
return trap;
|
return trap;
|
||||||
|
@ -86,6 +90,7 @@ function stateful(target, hook) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let isobj = (o) => o instanceof Object;
|
let isobj = (o) => o instanceof Object;
|
||||||
|
let isfn = (o) => typeof o === "function";
|
||||||
function isDLPtr(arr) {
|
function isDLPtr(arr) {
|
||||||
return isobj(arr) && TARGET in arr
|
return isobj(arr) && TARGET in arr
|
||||||
}
|
}
|
||||||
|
@ -100,7 +105,7 @@ function $if(condition, then, otherwise) {
|
||||||
// This lets you subscribe to a stateful object
|
// This lets you subscribe to a stateful object
|
||||||
function handle(ptr, callback) {
|
function handle(ptr, callback) {
|
||||||
assert(isDLPtr(ptr), "handle() requires a stateful object");
|
assert(isDLPtr(ptr), "handle() requires a stateful object");
|
||||||
assert(typeof callback === "function", "handle() requires a callback function");
|
assert(isfn(callback), "handle() requires a callback function");
|
||||||
let step, resolvedSteps = [];
|
let step, resolvedSteps = [];
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
|
@ -116,7 +121,7 @@ function handle(ptr, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject ourselves into nested objects
|
// inject ourselves into nested objects
|
||||||
const curry = (target, i) => function subscription(tgt, prop, val) {
|
let curry = (target, i) => function subscription(tgt, prop, val) {
|
||||||
if (prop === resolvedSteps[i] && target === tgt) {
|
if (prop === resolvedSteps[i] && target === tgt) {
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -177,7 +182,7 @@ function JSXAddFixedWrapper(ptr, cb, $if) {
|
||||||
|
|
||||||
// returns a function that sets a reference
|
// returns a function that sets a reference
|
||||||
// the currying is a small optimization
|
// the currying is a small optimization
|
||||||
const curryset = ptr => val =>{
|
let curryset = ptr => val => {
|
||||||
let next = ptr[PROXY];
|
let next = ptr[PROXY];
|
||||||
let steps = ptr[STEPS];
|
let steps = ptr[STEPS];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
@ -195,13 +200,13 @@ function h(type, props, ...children) {
|
||||||
// functional components. create the stateful object
|
// functional components. create the stateful object
|
||||||
let newthis = stateful(Object.create(type.prototype));
|
let newthis = stateful(Object.create(type.prototype));
|
||||||
|
|
||||||
for (const name in props) {
|
for (let name in props) {
|
||||||
const ptr = props[name];
|
let ptr = props[name];
|
||||||
if (name.startsWith("bind:")) {
|
if (name.startsWith("bind:")) {
|
||||||
assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
|
assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
|
||||||
|
|
||||||
const set = curryset(ptr);
|
let set = curryset(ptr);
|
||||||
const propname = name.substring(5);
|
let propname = name.substring(5);
|
||||||
if (propname == "this") {
|
if (propname == "this") {
|
||||||
set(newthis);
|
set(newthis);
|
||||||
} else {
|
} else {
|
||||||
|
@ -231,7 +236,7 @@ function h(type, props, ...children) {
|
||||||
Object.assign(newthis, props);
|
Object.assign(newthis, props);
|
||||||
|
|
||||||
newthis.children = [];
|
newthis.children = [];
|
||||||
for (const child of children) {
|
for (let child of children) {
|
||||||
JSXAddChild(child, newthis.children.push.bind(newthis.children));
|
JSXAddChild(child, newthis.children.push.bind(newthis.children));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,8 +244,9 @@ function h(type, props, ...children) {
|
||||||
elm.$ = newthis;
|
elm.$ = newthis;
|
||||||
newthis.root = elm;
|
newthis.root = elm;
|
||||||
if (newthis.css) {
|
if (newthis.css) {
|
||||||
elm.classList.add(newthis.css);
|
let cl = elm.classList
|
||||||
elm.classList.add("self");
|
cl.add(newthis.css);
|
||||||
|
cl.add("self");
|
||||||
}
|
}
|
||||||
elm.setAttribute("data-component", type.name);
|
elm.setAttribute("data-component", type.name);
|
||||||
if (typeof newthis.mount === "function")
|
if (typeof newthis.mount === "function")
|
||||||
|
@ -250,10 +256,10 @@ function h(type, props, ...children) {
|
||||||
|
|
||||||
|
|
||||||
let xmlns = props?.xmlns;
|
let xmlns = props?.xmlns;
|
||||||
const elm = xmlns ? document.createElementNS(xmlns, type) : document.createElement(type);
|
let elm = xmlns ? document.createElementNS(xmlns, type) : document.createElement(type);
|
||||||
|
|
||||||
|
|
||||||
for (const child of children) {
|
for (let child of children) {
|
||||||
let cond = child && !isDLPtr(child) && child[IF];
|
let cond = child && !isDLPtr(child) && child[IF];
|
||||||
let bappend = elm.append.bind(elm);
|
let bappend = elm.append.bind(elm);
|
||||||
if (cond) {
|
if (cond) {
|
||||||
|
@ -264,18 +270,18 @@ function h(type, props, ...children) {
|
||||||
|
|
||||||
if (!props) return elm;
|
if (!props) return elm;
|
||||||
|
|
||||||
function useProp(name, callback) {
|
let useProp = (name, callback) => {
|
||||||
if (!(name in props)) return;
|
if (!(name in props)) return;
|
||||||
let prop = props[name];
|
let prop = props[name];
|
||||||
callback(prop);
|
callback(prop);
|
||||||
delete props[name];
|
delete props[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const name in props) {
|
for (let name in props) {
|
||||||
const ptr = props[name];
|
let ptr = props[name];
|
||||||
if (name.startsWith("bind:")) {
|
if (name.startsWith("bind:")) {
|
||||||
assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
|
assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
|
||||||
const propname = name.substring(5);
|
let propname = name.substring(5);
|
||||||
|
|
||||||
// create the function to set the value of the pointer
|
// create the function to set the value of the pointer
|
||||||
let set = curryset(ptr);
|
let set = curryset(ptr);
|
||||||
|
@ -304,7 +310,7 @@ function h(type, props, ...children) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const name of classlist) {
|
for (let name of classlist) {
|
||||||
if (isDLPtr(name)) {
|
if (isDLPtr(name)) {
|
||||||
let oldvalue = null;
|
let oldvalue = null;
|
||||||
handle(name, value => {
|
handle(name, value => {
|
||||||
|
@ -321,8 +327,8 @@ function h(type, props, ...children) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// apply the non-reactive properties
|
// apply the non-reactive properties
|
||||||
for (const name in props) {
|
for (let name in props) {
|
||||||
const prop = props[name];
|
let prop = props[name];
|
||||||
if (isDLPtr(prop)) {
|
if (isDLPtr(prop)) {
|
||||||
handle(prop, (val) => {
|
handle(prop, (val) => {
|
||||||
JSXAddAttributes(elm, name, val);
|
JSXAddAttributes(elm, name, val);
|
||||||
|
@ -365,10 +371,10 @@ function JSXAddChild(child, cb) {
|
||||||
function JSXAddAttributes(elm, name, prop) {
|
function JSXAddAttributes(elm, name, prop) {
|
||||||
if (name.startsWith("on:")) {
|
if (name.startsWith("on:")) {
|
||||||
assert(typeof prop === "function", "on: requires a function");
|
assert(typeof prop === "function", "on: requires a function");
|
||||||
const names = name.substring(3);
|
let names = name.substring(3);
|
||||||
for (const name of names.split("$")) {
|
for (let name of names.split("$")) {
|
||||||
elm.addEventListener(name, (...args) => {
|
elm.addEventListener(name, (...args) => {
|
||||||
window.$el = elm;
|
self.$el = elm;
|
||||||
prop(...args);
|
prop(...args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue