王一之 发表于 2023-6-26 10:45:57

脚本猫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("你好,脚本猫");
```

steven026 发表于 2023-6-26 10:54:37

本意是一个UI框架,快速开发一个窗口,但是变成了组件的封装,需要自己组合
目前是一个组件生成一个shadowDOM,因此每个组件都要单独封装,
能不能所有组件共用一个shadowDOM,这样就可以不用封装了?

王一之 发表于 2023-6-26 11:00:04

steven026 发表于 2023-6-26 10:54
目前是一个组件生成一个shadowDOM,因此每个组件都要单独封装,
能不能所有组件共用一个shadowDOM,这样就 ...

所有组件共用一个shadowDOM也还是要封装UI组件,哥哥封两个普通的组件就明白了

只是刚好Message和Notification要那样做

cxxjackie 发表于 2023-6-26 23:39:00

模板字符串的方案如何:
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,实现难度大一点。

王一之 发表于 2023-6-27 00:35:08

cxxjackie 发表于 2023-6-26 23:39
模板字符串的方案如何:

规定库节点以大写字母开头,原生节点全小写,允许混用。函数设计割裂的问题,也许 ...

这样实现有点复杂,想不到怎么实现

有点怪怪的vue的样子的

王一之 发表于 2023-6-27 00:37:37

cxxjackie 发表于 2023-6-26 23:39
模板字符串的方案如何:

规定库节点以大写字母开头,原生节点全小写,允许混用。函数设计割裂的问题,也许 ...
可能想要不就先按现在的来吧,实现出来再说,这个也是基础框架,最终提供给用户(开发者)来用的可能是封装程度更高的方法,用起来可能就还好些了

cxxjackie 发表于 2023-6-27 23:32:00

王一之 发表于 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);

王一之 发表于 2023-6-28 00:05:50

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

后面的话,感觉就加强封装之类的,尽量简单使用

cxxjackie 发表于 2023-6-29 23:02:56

王一之 发表于 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);

王一之 发表于 2023-6-29 23:32:26

cxxjackie 发表于 2023-6-29 23:02
这个我看过了,没有摆脱React依赖,我还是更想要一个原生的,因为创建元素和绑定数据这些都不难,搞一个R ...

woc nb,好像也是可以直接转换一下

直接使用react的主要原因是UI框架可以直接用,而不用自己写了,确实是有点大了,不过现在暂时不考虑这个,如果要缩小的话,要自己去写UI样式之类的,然后也有一个小型的类似react的框架可以选择:https://github.com/vanjs-org/van

页: [1] 2
查看完整版本: 脚本猫UI库初版