脚本猫UI库初版
书接上文脚本猫UI库API设计提案
https://bbs.tampermonkey.net.cn/thread-4711-1-1.html
(出处: 油猴中文网)
初版完成了,越写越觉得是React了,有点陷入迷茫,希望哥哥们评价一下:
### 相比直接react的优点
- 直接require即可使用
- 处理了shadowDom之类的问题,不会有样式冲突
- 不需要打包工具
### 迷茫的点
- 本意是一个UI框架,快速开发一个窗口,但是变成了组件的封装,需要自己组合,写起来感觉又变麻烦了
- 这个问题可能后面需要写一个模板,但是模板怎么去写,还不知道
- 越写越像React,本来希望简单,现在看起来反而复杂了(虽然也有写一个页面就是需要这么去写的原因,可能只能再做一些封装了)
示例代码如下:
https://github.com/scriptscat/lib/tree/main/example
!(data/attachment/forum/202306/26/104509h0x3ge2x940pt2ct.png)
```js
const data = { input: "默认值" };
CAT_UI.createPlan({
header: {
title: "脚本猫的UI框架",
},
footer: {
version: "0.1.0",
},
render() {
const = CAT_UI.useState(data.input);
return CAT_UI.Space([
CAT_UI.Text("脚本猫的UI框架: " + input),
CAT_UI.Button("我是按钮", {
type: "primary",
onClick() {
CAT_UI.Message.info("我被点击了,你输入了: " + input);
}
}),
CAT_UI.Input({
value: input,
onChange(val) {
setInput(val);
data.input = val;
}
}),
CAT_UI.Checkbox("我是复选框"),
CAT_UI.Select([
CAT_UI.Select.Option("选项1"),
CAT_UI.Select.Option("选项2"),
]),
CAT_UI.createElement("div", {
style: {
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}
}, CAT_UI.Text("请输入"), CAT_UI.Input({
value: input,
onChange(val) {
setInput(val);
data.input = val;
},
style: {
flex: 1
}
}))
], {
direction: "vertical"
});
},
onReady(plan) {
plan.onDraggableStop((e) => {
console.log(e)
});
}
});
CAT_UI.Message.success("你好,脚本猫");
```
本意是一个UI框架,快速开发一个窗口,但是变成了组件的封装,需要自己组合
目前是一个组件生成一个shadowDOM,因此每个组件都要单独封装,
能不能所有组件共用一个shadowDOM,这样就可以不用封装了? steven026 发表于 2023-6-26 10:54
目前是一个组件生成一个shadowDOM,因此每个组件都要单独封装,
能不能所有组件共用一个shadowDOM,这样就 ...
所有组件共用一个shadowDOM也还是要封装UI组件,哥哥封两个普通的组件就明白了
只是刚好Message和Notification要那样做 模板字符串的方案如何:
return CAT_UI.jsx({
input: data.input,
inputChange: setInput,
buttonClick() {
CAT_UI.Message.info("我被点击了,你输入了: " + data.input);
},
})`
<Space>
<Text>脚本猫的UI框架: ${'input'}</Text>
<Button type="primary" onClick=${'buttonClick'}>我是按钮</Button>
<Input value=${'input'} onChange=${'inputChange'}></Input>
<Checkbox>我是复选框</Checkbox>
<Select>
<Option>选项1</Option>
<Option>选项2</Option>
</Select>
<div style="display: flex; justify-content: space-between; align-items: center;">
<Text>请输入:</Text>
<Input value=${'input'} onChange=${'inputChange'} style="flex: 1;"></Input>
</div>
</Space>
`;
规定库节点以大写字母开头,原生节点全小写,允许混用。函数设计割裂的问题,也许可以允许简单的函数直接写在jsx里?CAT_UI.jsx构造一个标签函数,通过字符串类型的参数来传值(或者不传值直接${函数}然后在标签函数里处理?语义上有点怪),可能得用上AST,实现难度大一点。 cxxjackie 发表于 2023-6-26 23:39
模板字符串的方案如何:
规定库节点以大写字母开头,原生节点全小写,允许混用。函数设计割裂的问题,也许 ...
这样实现有点复杂,想不到怎么实现
有点怪怪的vue的样子的 cxxjackie 发表于 2023-6-26 23:39
模板字符串的方案如何:
规定库节点以大写字母开头,原生节点全小写,允许混用。函数设计割裂的问题,也许 ...
可能想要不就先按现在的来吧,实现出来再说,这个也是基础框架,最终提供给用户(开发者)来用的可能是封装程度更高的方法,用起来可能就还好些了
王一之 发表于 2023-6-27 00:35
这样实现有点复杂,想不到怎么实现
有点怪怪的vue的样子的
简单方案就是先字符串处理转换成React的jsx,再通过Babel翻译成React.createElement,这样打包出来应该很大。自己实现的话我想到的是DOMParser,把jsx当成xml解析,再通过DOM操作转换成真正的元素,这样应该可以去掉React依赖。传值问题我想了想还是用React那种格式比较合适,模板取值的话还得做一遍替换,有点多此一举了。
let str = `
<Space>
<Text>脚本猫的UI框架: {input}</Text>
<Button type="primary" onClick={buttonClick}>我是按钮</Button>
<Input value={input} onChange={inputChange}></Input>
<Checkbox>我是复选框</Checkbox>
<Select>
<Option>选项1</Option>
<Option>选项2</Option>
</Select>
<div style="display: flex; justify-content: space-between; align-items: center;">
<Text>请输入:</Text>
<Input value={input} onChange={inputChange} style="flex: 1;"></Input>
</div>
</Space>
`
str = str.replace(/(?<=<[^>]+=)\{[^>} ]+\}/g, '"$&"'); // 处理引号问题
const dom = new DOMParser().parseFromString(str, 'text/xml');
console.log(dom); cxxjackie 发表于 2023-6-27 23:32
简单方案就是先字符串处理转换成React的jsx,再通过Babel翻译成React.createElement,这样打包出来应该很 ...
确实,如果都可以jsx了的话,{}这样传值也没啥必要了
然后就是字符串模板的话,不好做自动提示,也不好给webpack之类的打包工具使用
我和@steven026 写了一个demo(初版了),c大要不要试试:
https://github.com/scriptscat/lib/blob/main/example/ui.user.js
后面的话,感觉就加强封装之类的,尽量简单使用 王一之 发表于 2023-6-28 00:05
确实,如果都可以jsx了的话,{}这样传值也没啥必要了
然后就是字符串模板的话,不好做自动提示,也不好 ...
这个我看过了,没有摆脱React依赖,我还是更想要一个原生的,因为创建元素和绑定数据这些都不难,搞一个React进来有点太大了,很多东西用不到。
撸了个解析模板字符串的简单实现出来,这个即使不采用,应该也可以做成模块,API对接一下就行了:
function parseData(data, value) {
const arr = value.split(/({[^}]+})/);
for (let i = 0, l = parseInt(arr.length / 2); i < l; i++) {
const key = arr.slice(1, -1);
if (key in data) {
arr = data;
}
}
let result;
arr.forEach(v => {
if (v === '') return;
result = result ? result + v : v;
});
return result;
}
function createNode(data, node) {
const attrs = {};
for (const attr of node.attributes) {
attrs = parseData(data, attr.value);
}
let text = '';
const children = [];
for (const child of node.childNodes) {
if (child.nodeType === 3) {
text += child.nodeValue.trim();
} else {
children.push(createNode(data, child));
}
}
text = parseData(data, text);
return {
tag: node.tagName,
text: text,
attributes: attrs,
children: children
};
}
function jsx(data, str) {
str = str.replace(/(?<=<[^>]+=)\{[^>} ]+\}/g, '"$&"');
const dom = new DOMParser().parseFromString(str, 'text/xml');
const nodes = [];
for (const node of dom.children) {
nodes.push(createNode(data, node));
}
return nodes.length > 1 ? nodes : nodes;
}
const data = { input: "默认值" };
const result = jsx({
input: data.input,
inputChange(val) {
data.input = val;
},
buttonClick() {
alert("我被点击了,你输入了: " + data.input);
}}, `
<Space>
<Text>脚本猫的UI框架: {input}</Text>
<Button type="primary" onClick={buttonClick}>我是按钮</Button>
<Input value={input} onChange={inputChange}></Input>
<Checkbox>我是复选框</Checkbox>
<Select>
<Option>选项1</Option>
<Option>选项2</Option>
</Select>
<div style="display: flex; justify-content: space-between; align-items: center;">
<Text>请输入:</Text>
<Input value={input} onChange={inputChange} style="flex: 1;"></Input>
</div>
</Space>
`);
console.log(result); cxxjackie 发表于 2023-6-29 23:02
这个我看过了,没有摆脱React依赖,我还是更想要一个原生的,因为创建元素和绑定数据这些都不难,搞一个R ...
woc nb,好像也是可以直接转换一下
直接使用react的主要原因是UI框架可以直接用,而不用自己写了,确实是有点大了,不过现在暂时不考虑这个,如果要缩小的话,要自己去写UI样式之类的,然后也有一个小型的类似react的框架可以选择:https://github.com/vanjs-org/van
页:
[1]
2