框架的中文名叫做鱼。英文名也叫做yu。
直觉上函数是很适合用于组件定义的。 函数接收参数返回值。组件接收属性返回视图。
function(params) {
return;
}
function Component(props) {
const state:any;
const el:HTMLElement;
return el;
}
虽然我们最终期望组件返回视图元素。但如果不在组件和元素之间建立一种数据结构,那更复杂的变换将无从下手。 所以yu定义了Node数据结构。组件的定义现在变成这样:
interface Node {
typeof: Symbol;
type: Function | "string";
props: Object;
children: Node | Node[];
}
function Component(props) {
const state:any;
const node:Node;
return node;
}
定义了Node数据结构,但我们不可能手动去定义整个应用程序的组件结构。为了书写方便,yu定义了一个高阶函数: h 。
function h(Component) {
return function WrapperComponent(_props, _children) {
const { props, children } = propsChildren(_props, _children);
return {
type: Component,
props,
children,
typeof: Symbol.for("yu.node"),
};
};
}
所有自定义组件在导出前必须手动调用h。
import { h } from "../yu/index.js";
function TodoAdder({ onAdd }) {
const useState = this.useState;
const [value, setValue] = useState("");
return div([
input({
value: value,
oninput: (e) => {
setValue(e.target.value);
},
}),
button({
textContent: "添加",
onclick: () => {
if (value) {
onAdd(value);
}
},
}),
]);
}
export default h(TodoAdder);
无论什么前端ui库,最终的最小视图都是html元素。那如何高效地创建元素呢? 社区通常有两种做法:
但是它们都不符合yu设定的目标。yu把html元素也看作组件。暴露在window上。
["div", "input", "label", "button", "fragment"].forEach((type) => {
window[type] = function TagComponent(_props, _children) {
const { props, children } = propsChildren(_props, _children);
return {
type,
props,
children,
typeof: "yu.node",
};
};
});
state是组件实例的可变数据。yu是通过bind函数向useState注入组件实例的信息。
import originalUseState from "./useState.js";
function bind(component, keyPath) {
const useState = originalUseState.bind({
keyPath,
hookIndex: 0,
});
return component.bind({ useState });
}
export default bind;
if (isFunction(type)) {
type = bind(type, keyPath);
children = type(props);
node.children = children;
}
import { update } from "../dom/render.js";
const states = {};
function useState(initialValue) {
const key = `${this.keyPath.join(".")}.${this.hookIndex}`;
this.hookIndex += 1;
if (states[key] === undefined) {
if (initialValue !== undefined) {
states[key] = initialValue;
}
}
const setState = (value) => {
states[key] = value;
update(this.keyPath);
};
return [states[key], setState];
}
export default useState;
问题:有没有发现目前做法的问题?提示词:children、key
无论多么复杂的组件结构,人总是知道哪些被更改过。而程序却很难知道,或者要做很多工作才能像人一样知道。
function diff(appNode, initKeyPath, callback) {
//旧的节点
const prevNode = initKeyPath.reduce((node, key) => node[key], appNode);
//新的节点
let nextNode = cloneNode(prevNode);
expandNode(nextNode, initKeyPath);
console.log(prevNode, nextNode, initKeyPath);
//比较节点差异
diffNode(prevNode, nextNode, prevNode.parent);
//用新的节点替换旧的
replace(appNode, initKeyPath, nextNode, callback);
}