diff --git a/apps/demo/src/components/counter/index.ts b/apps/demo/src/components/counter/index.ts index c68f12e..1c565e4 100644 --- a/apps/demo/src/components/counter/index.ts +++ b/apps/demo/src/components/counter/index.ts @@ -1,12 +1,26 @@ -import { render, useStyles, useRef, useState, useEffect } from "extreme"; +import { + useStyles, + useRef, + useState, + useEffect, + createComponent, + useMount, +} from "extreme"; import styles from "./index.css?raw"; import template from "./index.html?raw"; -export const Counter = (element: HTMLElement) => { - useStyles(styles); +export const Counter = createComponent("Counter", () => { const resetRef = useRef(); const [count, setCount] = useState(0); const [title, setTitle] = useState(``); + useStyles(styles); + + useMount(() => { + resetRef()?.addEventListener("click", () => { + setCount(0); + setTitle("the form is not submit"); + }); + }); const decrement = () => { setCount(count() - 1); @@ -18,10 +32,19 @@ export const Counter = (element: HTMLElement) => { setTitle("submit success"); }; - const base = render(element, template, { + count((newV) => { + console.log("newV", newV); + }); + + useEffect(() => { + console.log("count&title", count(), title()); + }, [count, title]); + + return { + template, state: { count, - title + title, }, ref: { resetRef, @@ -31,20 +54,5 @@ export const Counter = (element: HTMLElement) => { increment, handleSubmit: submit, }, - }); - - count((newV) => { - console.log("newV", newV); - }); - - useEffect(() => { - console.log("count&title", count(), title()); - }, [count, title]); - - resetRef()?.addEventListener("click", () => { - setCount(0); - setTitle("the form is not submit"); - }); - - return base; -}; + }; +}); diff --git a/apps/demo/src/components/custom-component/index.ts b/apps/demo/src/components/custom-component/index.ts index c0c9e3d..3c322ea 100644 --- a/apps/demo/src/components/custom-component/index.ts +++ b/apps/demo/src/components/custom-component/index.ts @@ -1,35 +1,38 @@ -import { GetState, useRef, render } from "extreme"; +import { GetState, useRef, createComponent } from "extreme"; import template from "./index.html?raw"; -export const CustomComponent = ( - element: HTMLElement, - props: { - open?: boolean | GetState; - } = {} -) => { - const defaultData = [ - { - src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", - content: "这是一段描述1,但是我是新的默认数据", - title: "这是一段标题1", - key: 1, - }, - { - src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", - content: "这是一段描述2", - title: "这是一段标题2", - key: 2, - }, - ]; - const divRef = useRef(); +export const CustomComponent = createComponent( + "CustomComponent", + ( + props: { + open?: boolean | GetState; + } = {} + ) => { + const defaultData = [ + { + src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", + content: "这是一段描述1,但是我是新的默认数据", + title: "这是一段标题1", + key: 1, + }, + { + src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", + content: "这是一段描述2", + title: "这是一段标题2", + key: 2, + }, + ]; + const divRef = useRef(); - return render(element, template, { - state: { - defaultData, - open: props.open, - }, - ref: { - divRef, - }, - }); -}; + return { + template, + state: { + defaultData, + open: props.open, + }, + ref: { + divRef, + }, + }; + } +); diff --git a/apps/demo/src/components/list/index.ts b/apps/demo/src/components/list/index.ts index d197ffe..287e697 100644 --- a/apps/demo/src/components/list/index.ts +++ b/apps/demo/src/components/list/index.ts @@ -1,4 +1,4 @@ -import { useStyles, useState, useRef, render } from "extreme"; +import { useStyles, useState, useRef, createComponent } from "extreme"; import styles from "./index.css?raw"; import template from "./index.html?raw"; @@ -16,92 +16,94 @@ const defaultData = [ key: 2, }, ]; -export const List = ( - element: HTMLElement, - props: { - defaultData?: typeof defaultData; - } = {} -) => { - useStyles(styles); - const [data, setData] = useState(props.defaultData || defaultData); - const listRef = useRef(); +export const List = createComponent( + "List", + ( + props: { + defaultData?: typeof defaultData; + } = {} + ) => { + useStyles(styles); - const handleClear = () => { - setData([]); - }; - const handleReset = () => { - setData(defaultData); - }; - const handleAdd = () => { - const source = data(); + const [data, setData] = useState(props.defaultData || defaultData); + const listRef = useRef(); - const newKey = - source.length > 0 ? Math.max(...source.map((item) => item.key)) + 1 : 1; - setData([ - ...data(), - { - src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", - content: "这是一段描述" + newKey, - title: "这是一段标题" + newKey, - key: newKey, - }, - ]); + const handleClear = () => { + setData([]); + }; + const handleReset = () => { + setData(defaultData); + }; + const handleAdd = () => { + const source = data(); - const listContainer = listRef(); - if (listContainer) { - listContainer.scrollTop = listContainer.scrollHeight; - } - }; - const handleMove = () => { - setData([ - { - src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", - content: "这是一段描述", - title: "这是一段标题", - key: 2, - }, - { - src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", - content: "这是一段描述Move+Update", - title: "这是一段标题Move+Update", - key: 1, + const newKey = + source.length > 0 ? Math.max(...source.map((item) => item.key)) + 1 : 1; + setData([ + ...data(), + { + src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", + content: "这是一段描述" + newKey, + title: "这是一段标题" + newKey, + key: newKey, + }, + ]); + + const listContainer = listRef(); + if (listContainer) { + listContainer.scrollTop = listContainer.scrollHeight; + } + }; + const handleMove = () => { + setData([ + { + src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", + content: "这是一段描述", + title: "这是一段标题", + key: 2, + }, + { + src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", + content: "这是一段描述Move+Update", + title: "这是一段标题Move+Update", + key: 1, + }, + ]); + }; + const handleUpdate = () => { + setData([ + { + src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", + content: "这是一段描述[key没有变化]", + title: "这是一段标题", + key: 1, + }, + { + src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", + content: "这是一段描述", + title: "这是一段标题", + key: 2, + }, + ]); + }; + + return { + template, + state: { + items: data, + showReset: () => data().length > 0, }, - ]); - }; - const handleUpdate = () => { - setData([ - { - src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", - content: "这是一段描述[key没有变化]", - title: "这是一段标题", - key: 1, + methods: { + handleClear, + handleReset, + handleAdd, + handleMove, + handleUpdate, }, - { - src: "https://img.alicdn.com/imgextra/i4/O1CN01QYQ1QI1CZQYQ1QYQI_!!6000000001382-2-tps-200-200.png", - content: "这是一段描述", - title: "这是一段标题", - key: 2, + ref: { + listRef, }, - ]); - }; - - const list = render(element, template, { - state: { - items: data, - showReset: () => data().length > 0, - }, - methods: { - handleClear, - handleReset, - handleAdd, - handleMove, - handleUpdate, - }, - ref: { - listRef, - }, - }); - - return list; -}; + }; + } +); diff --git a/apps/demo/src/main.ts b/apps/demo/src/main.ts index 53f95c0..4f4acb4 100644 --- a/apps/demo/src/main.ts +++ b/apps/demo/src/main.ts @@ -1,16 +1,7 @@ import "./style.css"; -import { Counter, List, CustomComponent } from "./components"; -import { App, Topbar, Content, Demo } from "./modules"; - -import { extreme } from "extreme"; - -extreme.use({ - Counter, - List, - CustomComponent, - Topbar, - Content, -}); +import { App, Demo } from "./modules"; +export * from "./components" +export * from "./modules" const handleHashChange = () => { if (window.location.hash.match(/#\/demo/)) { diff --git a/apps/demo/src/modules/content/index.ts b/apps/demo/src/modules/content/index.ts index 9be859f..3b3e4a5 100644 --- a/apps/demo/src/modules/content/index.ts +++ b/apps/demo/src/modules/content/index.ts @@ -1,6 +1,6 @@ -import { render } from "extreme"; +import { createComponent } from "extreme"; import template from "./index.html?raw"; -export const Content = (element: HTMLElement) => { - return render(element, template); -}; +export const Content = createComponent("Content", () => { + return { template }; +}); diff --git a/apps/demo/src/modules/top-bar/index.ts b/apps/demo/src/modules/top-bar/index.ts index 090ba57..6ef1287 100644 --- a/apps/demo/src/modules/top-bar/index.ts +++ b/apps/demo/src/modules/top-bar/index.ts @@ -1,10 +1,11 @@ -import { render } from "extreme"; +import { createComponent } from "extreme"; import template from "./index.html?raw"; -export const Topbar = (element: HTMLElement) => { - return render(element, template, { +export const Topbar = createComponent("Topbar", () => { + return { + template, state: { title: "topbar", }, - }); -}; + }; +}); diff --git a/packages/extreme/src/core/extreme.ts b/packages/extreme/src/core/extreme.ts index 7caebde..d02b517 100644 --- a/packages/extreme/src/core/extreme.ts +++ b/packages/extreme/src/core/extreme.ts @@ -31,9 +31,10 @@ export const createComponent = (name: string, component: ExtremeRenderFn) => { replace: boolean = true ) => { const result = component(props); - render(element, result.template, result, replace); + const ele = render(element, result.template, result, replace); currentCell.mount?.(); resetCurrentCell(); + return ele; }; fn.displayName = name; if (extreme.store[name]) { diff --git a/packages/extreme/src/core/render.ts b/packages/extreme/src/core/render.ts index afc4ef7..bcd1cf9 100644 --- a/packages/extreme/src/core/render.ts +++ b/packages/extreme/src/core/render.ts @@ -72,8 +72,7 @@ export function render( { const usageDomSet = new Set(); template.replace(/{{(.*?)}}/g, (_, _key, start) => { - const dom = findDomStr(start, template); - usageDomSet.add(dom); + usageDomSet.add(findDomStr(start, template)); return _; }); usageDomSet.forEach((dom) => { @@ -94,8 +93,7 @@ export function render( { template = template.replace(/@(.*?)}}"/g, (_, key, start) => { const [methodName, funcName] = key.split(`=\"{{`); - const dom = findDomStr(start, template); - addMethodTask(methodName, funcName, dom); + addMethodTask(methodName, funcName, findDomStr(start, template)); return ""; }); } @@ -112,11 +110,10 @@ export function render( const id = getDomID(baseDom)!; const addTask = (value?: boolean | null) => { - const task = [ + ifTasks.push([ baseDom, value ? dom : ``, - ] as [string, string]; - ifTasks.push(task); + ] as [string, string]); }; if (typeof value === "function") { @@ -168,7 +165,6 @@ export function render( return _; }); for (const [baseDom, newDom] of ifTasks) { - console.log("baseDom", baseDom, newDom); template = template.replace(baseDom, newDom); } @@ -361,8 +357,7 @@ export function render( const value = getValue(state, key); if (typeof value === "function") { - const dom = findDomStr(start, template); - stateSet.add(dom); + stateSet.add(findDomStr(start, template)); return source; } return encodeValue(value); @@ -378,10 +373,7 @@ export function render( return ref[key] as unknown as string; } const value = getValue(state, key); - if (typeof value === "function") { - return source; - } - return encodeValue(value); + return typeof value === "function" ? source : encodeValue(value); }); const [baseDomStr, id] = addDomID(sourceDomStr, getRandomID); const newDomStr = baseDomStr.replace( @@ -398,23 +390,22 @@ export function render( const dom = document.getElementById(id); if (!dom) return; const newValue = encodeValue(value()); - if (analyzeUpdateKey.type === "textContent") { - dom.textContent = newValue; - return; - } - if (analyzeUpdateKey.type === "attr") { - dom.setAttribute(analyzeUpdateKey.key, newValue); - return; - } - if (analyzeUpdateKey.type === "textNode") { - dom.childNodes[analyzeUpdateKey.key].textContent = - analyzeUpdateKey.content.replace(/{{(.*?)}}/g, (_, key) => { - const value = getValue(state, key); - return encodeValue( - typeof value === "function" ? value() : value - ); - }); - return; + switch (analyzeUpdateKey.type) { + case "textContent": + dom.textContent = newValue; + break; + case "attr": + dom.setAttribute(analyzeUpdateKey.key, newValue); + break; + case "textNode": + dom.childNodes[analyzeUpdateKey.key].textContent = + analyzeUpdateKey.content.replace(/{{(.*?)}}/g, (_, key) => { + const value = getValue(state, key); + return encodeValue( + typeof value === "function" ? value() : value + ); + }); + break; } }; setCurrentListener(rerenderDom); @@ -422,7 +413,6 @@ export function render( setCurrentListener(null); return newValue; } - return encodeValue(value); } ); diff --git a/packages/extreme/src/hooks/useEffect.ts b/packages/extreme/src/hooks/useEffect.ts index a1affed..800bcdf 100644 --- a/packages/extreme/src/hooks/useEffect.ts +++ b/packages/extreme/src/hooks/useEffect.ts @@ -3,7 +3,5 @@ import type { GetState } from "./useState"; import { idleCallback } from "./useState"; export const useEffect = (fn: Function, deps: GetState[]) => { - for (const dep of deps) { - dep((v) => idleCallback(() => fn(v))); - } + for (const dep of deps) dep((v) => idleCallback(() => fn(v))) };