nine-9 1.11.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  一个轻量、高性能、类型安全的 Vanilla DOM 响应式 UI 框架。
4
4
 
5
5
  融合了 Vue 模板指令和 React Hooks 的优点,取两者之长。
6
- 同时运行及其轻量,甚至打包后可以用于 **单模块 UserScript**。
6
+ 同时运行及其轻量,甚至打包后可以用于 **UserScript**。
7
7
 
8
8
  ## 特性
9
9
 
@@ -25,67 +25,119 @@ npm install nine-9
25
25
  ## 示例用法
26
26
 
27
27
  ```typescript
28
- // Counter.ts
29
- import { $, createComponent, tree, sync, styleSet, createArray, when } from "nine";
30
-
31
- export default createComponent({ //创建组件
28
+ //Selector.ts
29
+
30
+ import {
31
+ $,
32
+ createComponent,
33
+ defineEvent,
34
+ defineSlot,
35
+ defineTemplate,
36
+ typed,
37
+ styleSet,
38
+ sync,
39
+ tree,
40
+ when,
41
+ wrap
42
+ } from "@";
43
+
44
+ export default createComponent({
32
45
  props: {
33
- value: { //参数名
34
- validate: Number.isInteger, //验证器
35
- transform: Number, //转换器
36
- required: false, //是否必填
37
- shadow: 0, //默认值
38
- downloadable: true, //(上游→下游)
39
- uploadable: true, //(下游→上游),v-model双向绑定
46
+ items: {
47
+ validate: Array.isArray, //验证参数是否合法
48
+ transform: typed<string[]>(), //将输入的参数进行标准化,typed()函数不进行任何处理,只是类型投射
49
+ required: true, //参数是否必填
50
+ shadow: ["OptionA", "OptionB", "OptionC"], //默认值
51
+ downloadable: true, //是否可下载,即上游组件向下游传递值
52
+ uploadable: false //是否可上传,即下游组件向上游传递值
53
+ },
54
+ value: {
55
+ transform: Number,
56
+ uploadable: true, //组件的参数🉑上传,即v-model
57
+ required: true
40
58
  }
41
- }
42
- }, (props) => {
43
- const doubled = sync(() => props.value.get() * 2, [props.value]); //computed
59
+ },
60
+ events: [
61
+ defineEvent("select", {
62
+ template: defineTemplate<number>() //定义事件被触发时需要传递的数据类型
63
+ }),
64
+ defineEvent("toggleState", { template: defineTemplate<boolean>() })
65
+ ],
66
+ styles: [ //这些样式会被封装在组件所在的DOM域
67
+ styleSet(".item")
68
+ .backgroundColor("blue")
69
+ .color("white"),
70
+ styleSet(".flexdown")
71
+ .display("flex")
72
+ .flexDirection("column")
73
+ ],
74
+ slots: [
75
+ defineSlot("title", {
76
+ template: defineTemplate<string>(), //插槽作用域传值的数据类型
77
+ required: false, //插槽是否必填
78
+ })
79
+ ]
80
+ }, (props, slot, emit) => {
81
+ const showing = wrap(false); //ref包装一个数据,基于事件订阅的响应式系统
82
+ const text = sync(() => //computed同步一个数据,任何一个依赖更新时都会引起自身的重新渲染
83
+ props.items.get()[props.value.get()]
84
+ , [props.items, props.value]); //🉑灵活的配置依赖列表
85
+
86
+ const select = (index: number) => {
87
+ props.value.set(index);
88
+ showing.set(false);
89
+ emit("select", props.value.get());
90
+ };
91
+ showing.event.subcribe(e => { //订阅一个包装器的更新事件
92
+ emit("toggleState", e); //发布组件的自定义事件
93
+ });
94
+
44
95
  return tree("div")
45
- .use(styleSet().fontSize("20px").padding("10px"))
96
+ .class("flexdown")
97
+ .ariaAtomic("false")
46
98
  .append(
47
- "敲木鱼", tree("br"),
48
- tree("button")
49
- .on("click", () => props.value.set(props.value.get() + 1)) //参数uploadable,赋值会实时同步到上游
50
- .textContent("点击加一"),
51
- tree("button")
52
- .on("click", () => props.value.set(props.value.get() - 1))
53
- .textContent("点击减一"),
54
- tree("br"),
55
- "当前值:", $(props.value), //引用响应式的值,类似模板语法{{ count }}
56
- "双倍值:", $(doubled),
57
- $(sync( //只要是能渲染的东西,就能进行响应式引用
58
- () => createArray(
59
- doubled.get(),
60
- () => tree("div").textContent("你点了一下")
61
- ),
62
- [doubled]
63
- )), //列表渲染v-for
64
- when(
65
- () => props.value.get() > 10,
66
- () => tree("p").textContent("count > 10 时显示"),
67
- [props.value]
68
- ), //条件渲染v-if
99
+ tree("span")
100
+ .class("item")
101
+ .use(styleSet().backgroundColor("red")) //通过style赋值
102
+ .append(
103
+ tree("div").append($(text)), //引用响应式包装器的值
104
+ slot.title(text) //像正常元素一样,把插槽查到想要的位置(参数类型在定义时给出)
105
+ )
106
+ .on("click", () => showing.set(!showing.get())),
107
+ when(showing, () =>
108
+ tree("div")
109
+ .class("flexdown")
110
+ .append(
111
+ $(sync(() => //只要包装器返回的数据可以被渲染,就可以通过$函数进行引用
112
+ props.items.get().map((label, index) =>
113
+ tree("span")
114
+ .class("item")
115
+ .append(label)
116
+ .on("click", () => select(index))
117
+ ), [props.items]))
118
+ )
119
+ )
69
120
  );
70
121
  });
71
122
  ```
72
123
 
73
124
  ## 与 Vue 对比
74
125
 
75
- | nine-9 | Vue | 说明 |
76
- |------------------------------|--------------|----------------|
77
- | `wrap()` | `ref()` | 创建响应式引用 |
78
- | `sync()` | `computed()` | 响应式计算值 |
79
- | `when(condition, tree)` | `v-if` | 条件渲染 |
80
- | `sync(() => items.map(...))` | `v-for` | 列表渲染 |
81
- | `Property.uploadable` | `v-model` | 双向绑定 |
126
+ | nine-9 | Vue | 说明 |
127
+ |--------------------------|--------------|----------------|
128
+ | `wrap()` | `ref()` | 创建响应式引用 |
129
+ | `sync()` | `computed()` | 响应式计算值 |
130
+ | `when(condition, tree)` | `v-if` | 条件渲染 |
131
+ | `sync(() => Array<...>)` | `v-for` | 列表渲染 |
132
+ | `Property.uploadable` | `v-model` | 双向绑定 |
82
133
 
83
134
  ## 运行时特性
84
135
 
85
136
  ### 性能
86
137
 
87
- 1. 由于框架不需要使用 **Runtime** 伴随运行,也无需通过虚拟节点重新生成整个节点树(对节点树的更改完全基于原生DOM操作命令),因此应用的运行性能相当高,甚至可以媲美Vanilla.js的速度了。
88
- 2. 框架处理动态的节点树时,本质上是通过对新旧节点的CRUD实现。但不同于 Vue 的是,**nine-9** 不需要分析diff树,用列表渲染(`sync` ← `v-for`)举例,框架使用 `TreeContext` 接口来描述一个XML节点,**HTML元素、字符串、数字、各类空值(null、undefined)、组件渲染结果**都可以被归一化为一个 `TreeContext` 接口,而这个接口必定会用于封装一个非空的XML节点,当使用 `append` 方法添加一个响应式数组时,`TreeContext` 会首先在当前封装的节点最后添加一个注释节点用于当做锚点,旧列表中渲染出的节点将会被删除,新列表中的节点插入到锚点的后面。
138
+ 1. 框架不需要使用 **Runtime** 伴随运行,也无需通过虚拟节点定义,编译结果非常轻量。
139
+ 2. 框架处理动态的节点树时,本质上是通过对新旧节点的CRUD实现。由于不需要分析diff树,刷新组件的节点树时完全采用原生DOM操作命令,所以替换树的效率极其高。
140
+ 3. 框架的一切状态都是事件驱动的,只要包装器事件触发就能引起App视图更新。编写自定义的响应式封装器也相当灵活。
89
141
 
90
142
  ## 贡献指南
91
143
 
package/dist/index.d.ts CHANGED
@@ -199,8 +199,12 @@ declare const _default: {
199
199
  }, (EventDescriptor<string, "up"> | EventDescriptor<number, "down">)[], SlotDescriptor[]>;
200
200
  Selector: Component<{
201
201
  items: {
202
+ validate: (arg: any) => arg is any[];
202
203
  transform: PropertyTransformer<unknown, string[]>;
204
+ required: true;
203
205
  shadow: string[];
206
+ downloadable: true;
207
+ uploadable: false;
204
208
  };
205
209
  value: {
206
210
  transform: NumberConstructor;