Skip to content

Commit

Permalink
chore: 体积和部分性能优化,逻辑抽离
Browse files Browse the repository at this point in the history
  • Loading branch information
GrinZero committed Mar 2, 2024
1 parent 847ecd5 commit 8430297
Show file tree
Hide file tree
Showing 10 changed files with 295 additions and 178 deletions.
2 changes: 1 addition & 1 deletion packages/extreme/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sourcebug/extreme",
"version": "1.0.15",
"version": "1.0.16",
"description": "",
"main": "./dist/extreme.umd.js",
"module": "./dist/extreme.mjs",
Expand Down
27 changes: 20 additions & 7 deletions packages/extreme/src/core/dom-str.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,22 @@ export type AnalyzeUpdateKey =
}
| null;

const analyzeResultCache = new Map<string, AnalyzeUpdateKey>();

export const analyzeKey = (
template: string,
source: string,
start: number
): AnalyzeUpdateKey => {
const key = `${template}#${source}#${start}`;
if (analyzeResultCache.has(key)) {
return analyzeResultCache.get(key)!;
}
const back = (result: AnalyzeUpdateKey) => {
analyzeResultCache.set(key, result);
return result;
};

let type: string | undefined;
let contentStart: number | undefined;
for (let i = start; i >= 0; i--) {
Expand All @@ -100,7 +111,7 @@ export const analyzeKey = (
}
if (template[i] === " " && type === "attr") {
const key = template.slice(i + 1, start - 1);
return { type, key: key.replace(/=/g, "") };
return back({ type, key: key.replace(/=/g, "") });
}
if (template[i] === ">") {
type = "content";
Expand All @@ -113,8 +124,10 @@ export const analyzeKey = (
let tag = "";
for (let i = 1; i < template.length; i++) {
if (template[i] === " " || template[i] === ">") {
tag = template.slice(1, i + 1);
tag = tag.replace(/(<|>|\/)/g, "").trim();
tag = template
.slice(1, i + 1)
.replace(/(<|>|\/)/g, "")
.trim();
break;
}
}
Expand All @@ -123,7 +136,7 @@ export const analyzeKey = (
const endTagIndex = template.lastIndexOf(endTag);
const textContent = template.slice(contentStart as number, endTagIndex);
if (source === textContent) {
return { type: "textContent", key: "textContent" };
return back({ type: "textContent", key: "textContent" });
}
// analyze the index of the childNodes

Expand All @@ -141,14 +154,14 @@ export const analyzeKey = (
child.nodeType === Node.TEXT_NODE &&
child.textContent?.indexOf(source) !== -1
) {
return {
return back({
type: "textNode",
key: i,
content: child.textContent!,
};
});
}
}
}

return null;
return back(null);
};
Original file line number Diff line number Diff line change
@@ -1,43 +1,31 @@
import { useState, type Ref } from "../hooks";
import { markIdHandler } from "../worker/render";
import { useState, type Ref } from "../../hooks";
import {
collectCustomTaskHandler,
collectForTaskHandler,
collectIfTaskHandler,
collectMethodHanlder,
markIdHandler,
} from "./handler";
import {
findDomStr,
getDomID,
addDomID,
getRandomID,
getHash,
analyzeKey,
getDomAttr,
addDomAttr,
} from "./dom-str";
import { extreme } from "./extreme";
import { setCurrentListener } from "./listener";
import { addEventListener, haveParentDom, setParentDom } from "./render-utils";
} from "../dom-str";
import { extreme } from "../extreme";
import { setCurrentListener } from "../listener";
import { addEventListener, haveParentDom, setParentDom } from "../render-utils";
import { getValue } from "../../utils";

export interface TemplateProps {
state?: Record<string, any> | null;
ref?: Record<string, Ref> | null;
methods?: Record<string, Function> | null;
}

const getValue = (state: Record<string, any>, key: string) => {
const _key = key.trim();
const keys = _key.split(".");
if (keys.length > 1) {
let value = state;
for (let i = 0; i < keys.length; i++) {
if (!value) return;
if (typeof value === "function") {
// @ts-ignore
return (...rest) => value(...rest)[keys[i]];
}
value = value[keys[i]];
}
return value;
} else {
return state[_key];
}
};
const encodeValue = (value: any) => {
const str = String(value);
return str.replace(/[\u00A0-\u9999<>\&]/gim, (i) => `&#${i.charCodeAt(0)};`);
Expand Down Expand Up @@ -65,43 +53,43 @@ export async function render<T extends HTMLElement | HTMLTemplateElement>(
const customJobs: [string, Function, Record<string, unknown>][] = [];
const methodsMap = new Map<string, [string, Function][]>();

const addMethodTask = (methodName: string, funcName: string, dom: string) => {
const id = getDomID(dom);
if (methods && funcName in methods && id) {
const task = [id, methods[funcName]] as [string, Function];
// 第一阶段,为所有使用了{{}}的dom添加id,或者对id="{{ref}}"的dom进行替换
template = markIdHandler(template).replace(/id="{{(.*?)}}"/g, (_, key) => {
const _key = key.trim();
if (ref && _key in ref) {
return `id="${ref[_key]}"`;
}
if (state && _key in state && typeof state[_key] === "string") {
// TODO: 增加对state function的支持
return `id="${state[_key]}"`;
}
return _;
});

// 第二阶段,收集所有methods和对应的DOM节点
const { template: newTemplate, result: resultList } =
collectMethodHanlder(template);
template = newTemplate;

for (const { methodName, funcName, domId } of resultList) {
if (methods && funcName in methods && domId) {
const task = [domId, methods[funcName]] as [string, Function];
if (!methodsMap.has(methodName)) {
methodsMap.set(methodName, [task]);
} else {
methodsMap.get(methodName)!.push(task);
}
}
};

// 第一阶段,为所有使用了{{}}的dom添加id,或者对id="{{ref}}"的dom进行替换
template = markIdHandler(template, { ref, state });

// 第二阶段,收集所有methods和对应的DOM节点
{
template = template.replace(/@(.*?)}}"/g, (_, key, start) => {
const [methodName, funcName] = key.split(`=\"{{`);
addMethodTask(methodName, funcName, findDomStr(start, template));
return "";
});
}

// 第三阶段,识别:for和:if
{
if (state) {
// :if
const ifTasks: [string, string][] = [];
const testIfResult: [string, string, number][] = [];
template = template.replace(/:if="(.*?)"/g, (_, key, start) => {
testIfResult.push([_, key, start]);
return _;
});
const testIfResult = collectIfTaskHandler(template);

for (const [_, key, start] of testIfResult) {
const baseDom = findDomStr(start, template);
for (const [_, key, , baseDom] of testIfResult) {
const dom = baseDom.replace(_, "");
const value = getValue(state, key);
const id = getDomID(baseDom)!;
Expand Down Expand Up @@ -171,27 +159,17 @@ export async function render<T extends HTMLElement | HTMLTemplateElement>(
// :for
const forTasks: [string, string][] = [];

const testForResult: [string, string, number][] = [];
template = template.replace(/:for="(.*?)"/g, (_, key, start) => {
testForResult.push([_, key, start]);
return _;
});

for (const [_, key, start] of testForResult) {
const [itemName, listName] = key
.trim()
.split(" in ")
.map((_: string) => _.trim());

const baseDom = findDomStr(start, template);
const dom = baseDom.replace(_, "");
const parentDom = findDomStr(template.indexOf(baseDom) - 1, template);
const [newParentDOM, parentID] = addDomID(parentDom, getRandomID);
const keyIndex = (getDomAttr(baseDom, "key") || "key")
.replace("{{", "")
.replace("}}", "")
.replace(`${itemName}.`, "");

const forTaskList = collectForTaskHandler(template);
for (const {
baseDom,
itemName,
listName,
dom,
parentDom,
newParentDOM,
parentID,
keyIndex,
} of forTaskList) {
const list = getValue(state, listName);

const signalCache = new Map<string, Function>();
Expand Down Expand Up @@ -229,7 +207,6 @@ export async function render<T extends HTMLElement | HTMLTemplateElement>(
if (cloneProps.state && typeof cloneProps.state === "object") {
cloneProps.state = {
...cloneProps.state,
// TODO:通过设置state()的render,可以在item改变时触发render快速得到新的dom,不用重计算item
...{ [itemName]: sign },
key: item[keyIndex] ?? index,
};
Expand Down Expand Up @@ -385,6 +362,7 @@ export async function render<T extends HTMLElement | HTMLTemplateElement>(
newParentDOM.replace(baseDom, listDom.join("")),
]);
}

for (const [baseDom, newDom] of forTasks) {
template = template.replace(baseDom, newDom);
}
Expand All @@ -393,64 +371,42 @@ export async function render<T extends HTMLElement | HTMLTemplateElement>(

// 递归阶段,将所有大写开头的DOM做处理
{
const customTasks: [string, string][] = [];
template.replace(/<([A-Z].*?)[\/\>\s]/g, (source, name, start) => {
if (!extreme.store) return source;
const componentName = name.trim();

const dom = findDomStr(start, template);
const fn = extreme.store[componentName];
const jobs = collectCustomTaskHandler(template);
for (const { componentName, propsTask, dom } of jobs) {
const propsCurrent: Record<string, unknown> = {};
dom.replace(/\s(.*?)="(.*?)"/g, (_, _attrKey, _valueKey) => {
const attrName = _attrKey.trim();
const valueKey = _valueKey.trim();

if (/{{(.*?)}}/.test(valueKey)) {
const newValueKey = valueKey.split("{{")[1].split("}}")[0];

if (attrName === "id" && ref) {
propsCurrent[attrName] = getValue(ref, newValueKey);
return _;
}

if (attrName.startsWith(":")) {
propsCurrent[attrName] = `{{${newValueKey}}}`;
return _;
}

if (state && !attrName.startsWith("@")) {
propsCurrent[attrName] = getValue(state, newValueKey);
return _;
}
for (const { key, value, type } of propsTask) {
if (type === "ref" && ref) {
propsCurrent[key] = getValue(ref, value);
} else if (type === "state" && state) {
propsCurrent[key] = getValue(state, value);
} else if (type === "text") {
propsCurrent[key] = value;
}

propsCurrent[attrName] = valueKey;
return _;
});
}

const id = (propsCurrent.id as string) || getDomID(dom) || getRandomID();
let newDom = `<div id="${id}"`;
if (":if" in props) {
newDom += ` :if="${props[":if"]}"`;
delete props[":if"];
}
if (":for" in props) {
newDom += ` :for="${props[":for"]}"`;
delete props[":for"];
}
Object.keys(props).forEach((key) => {
if (key.startsWith("@")) {
newDom += ` ${key}="${propsCurrent[key]}"`;
delete propsCurrent[key];
}
});

let newDom = `<div id="${id}"`;
// if (":if" in props) {
// newDom += ` :if="${props[":if"]}"`;
// delete props[":if"];
// }
// if (":for" in props) {
// newDom += ` :for="${props[":for"]}"`;
// delete props[":for"];
// }
// Object.keys(props).forEach((key) => {
// if (key.startsWith("@")) {
// newDom += ` ${key}="${propsCurrent[key]}"`;
// delete propsCurrent[key];
// }
// });
newDom += `></div>`;
customTasks.push([dom, newDom]);
customJobs.push([id, fn, propsCurrent]);
return source;
});
for (const [dom, newDom] of customTasks) {

const fn = extreme.store[componentName];
if (fn) {
customJobs.push([id, fn, propsCurrent]);
}
template = template.replace(dom, newDom);
}
}
Expand Down Expand Up @@ -499,15 +455,13 @@ export async function render<T extends HTMLElement | HTMLTemplateElement>(
updateDom !== baseDomStr ? updateDom.indexOf(source) : start
);
if (analyzeUpdateKey === null) {
// debugger;
console.error(`[extreme] ${source} is not a valid UpdateKey`);
return source;
}
const rerenderDom = () => {
const dom =
document.getElementById(updateDomId ?? ele?.id ?? id) ||
document.getElementById(ele?.id ?? id);
// debugger
if (!dom) return;
const newValue = encodeValue(value());
switch (analyzeUpdateKey.type) {
Expand Down
Loading

0 comments on commit 8430297

Please sign in to comment.