@k3000/ce 0.0.1 → 0.1.0
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 +125 -62
- package/app/comp/comp-test.mjs +24 -0
- package/{comp/new-form.mjs → app/comp/my-form.mjs} +2 -5
- package/app/comp/nav-bar.mjs +92 -0
- package/app/comp/page-container.mjs +55 -0
- package/app/comp/tab-bar.mjs +60 -0
- package/app/index.html +39 -0
- package/app/pages/not-found.mjs +7 -0
- package/app/pages/page-category.mjs +7 -0
- package/app/pages/page-home.mjs +14 -0
- package/app/pages/page-my.mjs +7 -0
- package/app/pages/page-test.mjs +22 -0
- package/comm/router-view.mjs +96 -0
- package/console/api/mock.mjs +211 -0
- package/console/components/common-action-button.mjs +63 -0
- package/console/components/common-header.mjs +26 -0
- package/console/components/common-input.mjs +44 -0
- package/console/components/common-modal.mjs +45 -0
- package/console/components/common-pagination.mjs +87 -0
- package/console/components/common-toolbar.mjs +39 -0
- package/console/components/console-header.mjs +26 -0
- package/console/components/layout-container.mjs +46 -0
- package/console/components/layout-header.mjs +179 -0
- package/console/components/layout-menu.mjs +221 -0
- package/console/index.html +50 -0
- package/console/pages/page-dashboard.mjs +15 -0
- package/console/pages/system-menu.mjs +179 -0
- package/console/pages/system-role.mjs +199 -0
- package/console/pages/system-user.mjs +199 -0
- package/index.mjs +854 -98
- package/package.json +1 -1
- package/comp/div-box.mjs +0 -20
- package/index.html +0 -17
package/index.mjs
CHANGED
|
@@ -1,192 +1,948 @@
|
|
|
1
|
-
|
|
1
|
+
let dir = './comp'
|
|
2
|
+
let ext = 'mjs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 读取模板字符串内变量集合
|
|
6
|
+
* @param {String} str
|
|
7
|
+
* @returns {Set<String>}
|
|
8
|
+
* @example
|
|
9
|
+
* getVariables('name: {{name}}, age: {{info.age}}')
|
|
10
|
+
* // {'name', 'info.age'}
|
|
11
|
+
*/
|
|
12
|
+
let getVariables = str => {
|
|
13
|
+
// 1. 匹配所有 {{ ... }} 结构
|
|
14
|
+
// result: ["{{age + test()}}"]
|
|
15
|
+
const templates = str.match(/\{\{.+?}}/g);
|
|
16
|
+
|
|
17
|
+
if (!templates) return new Set();
|
|
18
|
+
|
|
19
|
+
const variables = new Set();
|
|
20
|
+
// 简单的 JS 关键字黑名单,避免把 true/null 当作变量
|
|
21
|
+
const keywords = new Set(['true', 'false', 'null', 'undefined', 'this', 'new', 'typeof', 'in', 'instanceof']);
|
|
22
|
+
|
|
23
|
+
templates.forEach(tpl => {
|
|
24
|
+
// 去掉外层的 {{ }},只保留内容: "age + test()"
|
|
25
|
+
const content = tpl.slice(2, -2);
|
|
26
|
+
|
|
27
|
+
// 2. 正则核心逻辑
|
|
28
|
+
// \b -> 单词边界,防止匹配到单词的一部分
|
|
29
|
+
// [a-zA-Z_$] -> 变量必须以字母、下划线或 $ 开头
|
|
30
|
+
// [\w$]* -> 后续可以是字母、数字、下划线或 $
|
|
31
|
+
// (?!\s*\() -> 【关键】负向先行断言:排除后面紧跟(可能是空格+)左括号的情况
|
|
32
|
+
// (?!\s*:) -> (可选) 排除对象键值对中的键 (如 { key: val })
|
|
33
|
+
const regex = /\b[a-zA-Z_$][\w$]*(?:\s*\.\s*[a-zA-Z_$][\w$]*)*(?!\s*\()/g;
|
|
34
|
+
|
|
35
|
+
const matches = content.match(regex);
|
|
36
|
+
|
|
37
|
+
if (matches) {
|
|
38
|
+
matches.forEach(item => {
|
|
39
|
+
// 过滤关键字
|
|
40
|
+
if (!keywords.has(item)) {
|
|
41
|
+
variables.add(item);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
2
46
|
|
|
3
|
-
|
|
47
|
+
// console.log('variables', variables)
|
|
4
48
|
|
|
5
|
-
|
|
49
|
+
return variables;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 创建一个临时的 node
|
|
53
|
+
* @returns {Text}
|
|
54
|
+
*/
|
|
55
|
+
let createTmpNode = (data = '') => {
|
|
6
56
|
|
|
7
|
-
|
|
57
|
+
return document.createTextNode('')
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
*
|
|
61
|
+
* @param {String} code
|
|
62
|
+
* @returns {`with (scope) { return \`${*}\` \n}`}
|
|
63
|
+
* @example
|
|
64
|
+
* literalsCode('name: {{name}}')
|
|
65
|
+
* // 'with (scope) { return `name: ${name}` \n}'
|
|
66
|
+
*/
|
|
67
|
+
let literalsCode = code => {
|
|
68
|
+
|
|
69
|
+
return `with (scope) {\n\treturn \`${code.replace(/{{(.*?)}}/g, (_, key) => `\${${key}}`)}\` \n}`
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
*
|
|
73
|
+
* @param code
|
|
74
|
+
* @returns {`with (scope) { return ${*} \n}`}
|
|
75
|
+
* @example
|
|
76
|
+
* literalsCode('{{count() + 1}}')
|
|
77
|
+
* // 'with (scope) { return count() + 1 \n}'
|
|
78
|
+
*/
|
|
79
|
+
let functionCode = code => {
|
|
80
|
+
|
|
81
|
+
return `with (scope) {\n\treturn ${code} \n}`
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 处理实现<element each="{{[1,2,3]}}"></element>的循环
|
|
85
|
+
* 期望得到<element>1</element><element>2</element><element>3</element>
|
|
86
|
+
* @param {{
|
|
87
|
+
* ownerElement: Node
|
|
88
|
+
* nodeMap: Array
|
|
89
|
+
* mark: Node
|
|
90
|
+
* }} data
|
|
91
|
+
* @param {Array} array
|
|
92
|
+
* @returns {{
|
|
93
|
+
* ownerElement: Node
|
|
94
|
+
* nodeMap: Array
|
|
95
|
+
* mark: Node
|
|
96
|
+
* }}
|
|
97
|
+
*/
|
|
98
|
+
let handleArray = (data, array) => {
|
|
99
|
+
|
|
100
|
+
const target = data.ownerElement
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 第一次初始化好相关数据,后面用mark.parentNode去做替换,data.ownerElement变成克隆下来的快照,已经不是属性的归属element了。
|
|
104
|
+
*/
|
|
105
|
+
if (!data.nodeMap) {
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @type {[[*, Node]]}
|
|
109
|
+
*/
|
|
110
|
+
data.nodeMap = []
|
|
111
|
+
|
|
112
|
+
data.mark = createTmpNode('数组结束标记')
|
|
113
|
+
|
|
114
|
+
target.parentNode.replaceChild(data.mark, target)
|
|
115
|
+
|
|
116
|
+
data.ownerElement = target.cloneNode(true)
|
|
117
|
+
}
|
|
8
118
|
|
|
9
|
-
|
|
119
|
+
if (!Array.isArray(array)) return data
|
|
10
120
|
|
|
11
|
-
|
|
12
|
-
}
|
|
121
|
+
const parent = data.mark.parentNode
|
|
13
122
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
123
|
+
let i = 0
|
|
124
|
+
|
|
125
|
+
for (; i < array.length; i++) {
|
|
126
|
+
|
|
127
|
+
if (data.nodeMap[i] === undefined) {
|
|
128
|
+
|
|
129
|
+
const clone = target.cloneNode(true)
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
|
|
133
|
+
array[i]._ = data.scope
|
|
134
|
+
|
|
135
|
+
} catch (e) { }
|
|
136
|
+
|
|
137
|
+
bindNode(array[i], clone, true)
|
|
138
|
+
|
|
139
|
+
parent.insertBefore(clone, data.mark)
|
|
140
|
+
|
|
141
|
+
data.nodeMap[i] = [array[i], clone]
|
|
142
|
+
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (data.nodeMap[i][0] !== array[i]) {
|
|
147
|
+
|
|
148
|
+
const clone = target.cloneNode(true)
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
|
|
152
|
+
array[i]._ = data.scope
|
|
153
|
+
|
|
154
|
+
} catch (e) { }
|
|
155
|
+
|
|
156
|
+
bindNode(array[i], clone, true)
|
|
157
|
+
|
|
158
|
+
parent.replaceChild(clone, data.nodeMap[i][1])
|
|
159
|
+
|
|
160
|
+
data.nodeMap[i] = [array[i], clone]
|
|
161
|
+
|
|
162
|
+
// continue
|
|
19
163
|
}
|
|
20
164
|
}
|
|
165
|
+
|
|
166
|
+
const index = i
|
|
167
|
+
|
|
168
|
+
for (; i < data.nodeMap.length; i++) {
|
|
169
|
+
|
|
170
|
+
parent.removeChild(data.nodeMap[i][1])
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
data.nodeMap.splice(index, data.nodeMap.length - index)
|
|
174
|
+
|
|
175
|
+
return data
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 调用该方法完成可配置参数合方法的初始化
|
|
179
|
+
* @param options
|
|
180
|
+
* @example
|
|
181
|
+
* import { config } from '../index.mjs'
|
|
182
|
+
* config({
|
|
183
|
+
* dir: './app/comp',
|
|
184
|
+
* getVariables: () => {...}
|
|
185
|
+
* ...
|
|
186
|
+
* })
|
|
187
|
+
*/
|
|
188
|
+
export const config = (options) => {
|
|
189
|
+
|
|
190
|
+
if (options.dir) {
|
|
191
|
+
|
|
192
|
+
dir = options.dir
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (options.ext) {
|
|
196
|
+
|
|
197
|
+
ext = options.ext
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (typeof options.getVariables === 'function') {
|
|
201
|
+
|
|
202
|
+
getVariables = options.getVariables
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (typeof options.createTmpNode === 'function') {
|
|
206
|
+
|
|
207
|
+
createTmpNode = options.createTmpNode
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (typeof options.literalsCode === 'function') {
|
|
211
|
+
|
|
212
|
+
literalsCode = options.literalsCode
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (typeof options.functionCode === 'function') {
|
|
216
|
+
|
|
217
|
+
functionCode = options.functionCode
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (typeof options.handleArray === 'function') {
|
|
221
|
+
|
|
222
|
+
handleArray = options.handleArray
|
|
223
|
+
}
|
|
21
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* 获取自定义元素的名称,如果不只自定义元素返回undefined
|
|
227
|
+
* @param {Node} node
|
|
228
|
+
* @returns {string | undefined}
|
|
229
|
+
*/
|
|
230
|
+
const getCEName = node => {
|
|
22
231
|
|
|
23
|
-
|
|
24
|
-
const params = url.searchParams
|
|
25
|
-
const dir = params.get('dir') || '/comp'
|
|
26
|
-
const ext = params.get('ext') || 'mjs'
|
|
27
|
-
const prod = !params.get('dev')
|
|
232
|
+
if (node.tagName.includes('-')) {
|
|
28
233
|
|
|
29
|
-
|
|
234
|
+
return node.tagName.toLowerCase()
|
|
235
|
+
}
|
|
30
236
|
|
|
31
|
-
|
|
237
|
+
if (node.hasAttribute('is')) {
|
|
32
238
|
|
|
33
|
-
return
|
|
239
|
+
return node.getAttribute('is')
|
|
34
240
|
}
|
|
35
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* 函数节流
|
|
244
|
+
* @param {Function} fn
|
|
245
|
+
* @param {Number} delay
|
|
246
|
+
* @returns {(function(...[*]): void)|*}
|
|
247
|
+
*/
|
|
248
|
+
export const throttle = (fn, delay = 41) => {
|
|
36
249
|
|
|
37
|
-
|
|
250
|
+
let timer
|
|
38
251
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
252
|
+
return (...args) => {
|
|
253
|
+
|
|
254
|
+
clearTimeout(timer)
|
|
255
|
+
|
|
256
|
+
timer = setTimeout(() => fn(...args), delay)
|
|
257
|
+
}
|
|
42
258
|
}
|
|
43
|
-
`
|
|
44
259
|
|
|
45
|
-
|
|
260
|
+
/**
|
|
261
|
+
* 实现对象属性变化更新相应节点的函数池
|
|
262
|
+
* @type {WeakMap<WeakKey, Map<String, Set<Function>>>}
|
|
263
|
+
*/
|
|
264
|
+
const objectPool = new WeakMap
|
|
265
|
+
/**
|
|
266
|
+
* 避免重复处理自定义元素引入处理的节点池
|
|
267
|
+
* @type {WeakSet<Node>}
|
|
268
|
+
*/
|
|
269
|
+
const importPool = new WeakSet
|
|
270
|
+
/**
|
|
271
|
+
* 记录监听属性变化更新相应函数的方法
|
|
272
|
+
* @param {Object} scope
|
|
273
|
+
* @param {String} code
|
|
274
|
+
* @param {Function} update
|
|
275
|
+
* @example
|
|
276
|
+
* addListener({name: 'test'}, 'name', () => {})
|
|
277
|
+
*/
|
|
278
|
+
const addListener = (scope, code, update) => {
|
|
279
|
+
|
|
280
|
+
getVariables(code).forEach(variables => {
|
|
281
|
+
|
|
282
|
+
// 处理这种属性 {{user.info.name}} 达到监听info的name变化的效果
|
|
283
|
+
let key = '', obj = scope, index = 0, list = variables.split('.')
|
|
284
|
+
|
|
285
|
+
for ([index, key] of list.entries()) {
|
|
286
|
+
|
|
287
|
+
if (index === list.length - 1) {
|
|
288
|
+
|
|
289
|
+
break
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (obj === null || typeof obj !== 'object' || !Reflect.has(obj, key)) return
|
|
293
|
+
|
|
294
|
+
if (Array.isArray(obj[key]) && index === list.length - 2) break
|
|
295
|
+
|
|
296
|
+
obj = obj[key]
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (obj === null || typeof obj !== 'object') return
|
|
300
|
+
|
|
301
|
+
if (Object.getOwnPropertyDescriptor(obj, key) && !Object.getOwnPropertyDescriptor(obj, key).configurable) return
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 如果没有记录该对象就新增对象、属性、更新函数的对应关系
|
|
305
|
+
* 如果记录了对象,没有记录属性,就添加属性、更新函数的对应关系
|
|
306
|
+
* 如果属性也存在了,那么就添加更新函数,因为是 Set 所以不用担心函数重复添加
|
|
307
|
+
*/
|
|
308
|
+
if (objectPool.has(obj)) {
|
|
46
309
|
|
|
47
|
-
const
|
|
310
|
+
const map = objectPool.get(obj)
|
|
48
311
|
|
|
49
|
-
|
|
312
|
+
if (map.has(key)) {
|
|
313
|
+
|
|
314
|
+
map.get(key).add(update)
|
|
315
|
+
|
|
316
|
+
} else {
|
|
317
|
+
|
|
318
|
+
const set = new Set([update])
|
|
319
|
+
|
|
320
|
+
map.set(key, set)
|
|
321
|
+
|
|
322
|
+
let value = obj[key]
|
|
323
|
+
|
|
324
|
+
Object.defineProperty(obj, key, {
|
|
325
|
+
|
|
326
|
+
get: () => value,
|
|
327
|
+
|
|
328
|
+
set: (newValue) => {
|
|
329
|
+
|
|
330
|
+
if (value === newValue) return
|
|
331
|
+
|
|
332
|
+
value = newValue
|
|
333
|
+
// 数值变化就执行相应的更新函数
|
|
334
|
+
set.forEach(fn => fn())
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
} else {
|
|
340
|
+
|
|
341
|
+
const set = new Set([update])
|
|
342
|
+
|
|
343
|
+
objectPool.set(obj, new Map([[key, set]]))
|
|
344
|
+
|
|
345
|
+
let value = obj[key]
|
|
346
|
+
|
|
347
|
+
Object.defineProperty(obj, key, {
|
|
348
|
+
|
|
349
|
+
get: () => value,
|
|
350
|
+
|
|
351
|
+
set: (newValue) => {
|
|
352
|
+
|
|
353
|
+
if (value === newValue) return
|
|
354
|
+
|
|
355
|
+
value = newValue
|
|
356
|
+
// 数值变化就执行相应的更新函数
|
|
357
|
+
set.forEach(fn => fn())
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* 用于记录自定义元素引入后的相关数据
|
|
365
|
+
*/
|
|
366
|
+
const ceMap = Object.create(null)
|
|
367
|
+
/**
|
|
368
|
+
*
|
|
369
|
+
* @param {Node} node
|
|
370
|
+
* @param {String} name
|
|
371
|
+
*/
|
|
372
|
+
const disposeCE = (node, name) => {
|
|
373
|
+
|
|
374
|
+
node.querySelectorAll('*:not(:defined)').forEach(node => importCE(node))
|
|
375
|
+
// 提前创建的 shadowRoot nodes,clone绑定对象
|
|
376
|
+
if (ceMap[name].shadowRoot) {
|
|
377
|
+
|
|
378
|
+
node.attachShadow({mode: 'open'})
|
|
379
|
+
|
|
380
|
+
node.shadowRoot.appendChild(ceMap[name].shadowRoot.create(node))
|
|
381
|
+
}
|
|
382
|
+
// 提前创建的 innerHTML nodes,clone 绑定对象
|
|
383
|
+
if (ceMap[name].innerHTML || ceMap[name].outerHTML) {
|
|
384
|
+
const clone = (ceMap[name].innerHTML || ceMap[name].outerHTML).create(node)
|
|
385
|
+
// 如果没有 shadowRoot 然后子节点里面有 slot 就把标签内的内容插入到 slot 的位置,物理插入不同于 shadowRoot 的逻辑插入
|
|
386
|
+
if (!ceMap[name].shadowRoot) {
|
|
387
|
+
const slots = Array.from(clone.querySelectorAll('slot'))
|
|
388
|
+
const slotMap = slots.reduce((prev, curr) => {
|
|
389
|
+
prev[curr.getAttribute('name') || ''] = curr
|
|
390
|
+
return prev
|
|
391
|
+
}, {})
|
|
392
|
+
const children = Array.from(node.childNodes)
|
|
393
|
+
for (const child of children) {
|
|
394
|
+
const slot = typeof child.getAttribute === "function" ? child.getAttribute('slot') || '' : ''
|
|
395
|
+
if (slotMap[slot]) {
|
|
396
|
+
slotMap[slot].parentNode.insertBefore(child, slotMap[slot])
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
for (const slot of slots) {
|
|
400
|
+
slot.parentNode.removeChild(slot)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (ceMap[name].outerHTML) {
|
|
405
|
+
|
|
406
|
+
while (clone.firstChild) {
|
|
407
|
+
|
|
408
|
+
node.parentElement.insertBefore(clone.firstChild, node)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
node.parentElement.removeChild(node)
|
|
412
|
+
|
|
413
|
+
} else {
|
|
414
|
+
|
|
415
|
+
node.appendChild(clone)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// 执行ready通知已经处理好 nodes
|
|
419
|
+
if (typeof node.ready === "function") {
|
|
420
|
+
|
|
421
|
+
node.ready()
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
let tmp = null
|
|
425
|
+
/**
|
|
426
|
+
* 导入自定义元素
|
|
427
|
+
* @param {Node} node
|
|
428
|
+
* @param {String} name
|
|
429
|
+
*/
|
|
430
|
+
const importCE = (node, name = undefined) => {
|
|
431
|
+
|
|
432
|
+
name = name ?? getCEName(node)
|
|
50
433
|
|
|
51
434
|
if (importPool.has(node)) return
|
|
52
435
|
|
|
436
|
+
importPool.add(node)
|
|
437
|
+
// 处理导入完成的自定义元素
|
|
438
|
+
if (customElements.get(name) !== undefined) {
|
|
439
|
+
|
|
440
|
+
disposeCE(node, name)
|
|
441
|
+
|
|
442
|
+
return
|
|
443
|
+
}
|
|
444
|
+
|
|
53
445
|
// autonomous custom elements
|
|
54
446
|
const ace = node.nodeName.includes('-')
|
|
55
447
|
|
|
56
|
-
|
|
448
|
+
let _dir = dir
|
|
449
|
+
// 处理组件目录
|
|
450
|
+
if (node.attributes.dir && node.attributes.dir.nodeValue) {
|
|
57
451
|
|
|
58
|
-
|
|
452
|
+
const value = node.attributes.dir.nodeValue
|
|
59
453
|
|
|
60
|
-
|
|
454
|
+
node.removeAttribute('dir')
|
|
455
|
+
|
|
456
|
+
if (value.startsWith('/') || value.startsWith('./')) {
|
|
457
|
+
_dir = value
|
|
458
|
+
} else {
|
|
459
|
+
_dir = `${_dir}/${value}`
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const path = `${_dir}/${name}.${ext}`
|
|
464
|
+
|
|
465
|
+
// console.log(path)
|
|
466
|
+
|
|
467
|
+
import(path).then(result => {
|
|
468
|
+
// 处理第一次导入的情况
|
|
469
|
+
if (customElements.get(name) === undefined) {
|
|
470
|
+
|
|
471
|
+
ceMap[name] = Object.create(null)
|
|
472
|
+
// 保存好 innerHTML、shadowRoot node
|
|
473
|
+
if (typeof result.innerHTML === 'string') {
|
|
474
|
+
|
|
475
|
+
ceMap[name].innerHTML = bind(result.innerHTML)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (typeof result.outerHTML === 'string') {
|
|
479
|
+
|
|
480
|
+
ceMap[name].outerHTML = bind(result.outerHTML)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (typeof result.shadowRoot === 'string') {
|
|
484
|
+
|
|
485
|
+
ceMap[name].shadowRoot = bind(result.shadowRoot)
|
|
486
|
+
}
|
|
487
|
+
// 注册自定义元素
|
|
488
|
+
if (ace) {
|
|
489
|
+
|
|
490
|
+
customElements.define(name, result.default)
|
|
491
|
+
|
|
492
|
+
} else {
|
|
61
493
|
|
|
62
|
-
|
|
494
|
+
customElements.define(name, result.default, {
|
|
63
495
|
|
|
64
|
-
|
|
496
|
+
extends: node.nodeName.toLowerCase()
|
|
497
|
+
})
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
disposeCE(node, name)
|
|
502
|
+
})
|
|
503
|
+
.catch(error => {
|
|
504
|
+
console.error(error)
|
|
505
|
+
})
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* 处理节点内的模板字符串 <div>1 + 1 = {{1 + 1}}</div>
|
|
509
|
+
* @param {Object} scope
|
|
510
|
+
* @param {Node} node
|
|
511
|
+
*/
|
|
512
|
+
const disposeNode = (scope, node) => {
|
|
513
|
+
|
|
514
|
+
const value = node.nodeValue
|
|
515
|
+
|
|
516
|
+
if (value.match(/{{(.*?)}}/) === null) return
|
|
65
517
|
|
|
66
|
-
|
|
518
|
+
const f = new Function('scope', literalsCode(value))
|
|
67
519
|
|
|
68
|
-
|
|
520
|
+
addListener(scope, value, throttle(() => node.nodeValue = f.call(node, scope)))
|
|
521
|
+
|
|
522
|
+
node.nodeValue = f.call(node, scope)
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* 处理属性的模板字符串,多种情况
|
|
526
|
+
* @param {Object} scope
|
|
527
|
+
* @param {Node} element
|
|
528
|
+
* @param {Attr} attr
|
|
529
|
+
* @param {String} code
|
|
530
|
+
*/
|
|
531
|
+
const disposeAttrValue = (scope, element, attr, code) => {
|
|
532
|
+
// 普通情况:class="theme-{{mode}}"
|
|
533
|
+
if (!code.startsWith('{{') || !code.endsWith('}}') || code.slice(2).includes('{{')) {
|
|
534
|
+
|
|
535
|
+
const f = new Function('scope', literalsCode(code))
|
|
536
|
+
|
|
537
|
+
return () => attr.nodeValue = f.call(element, scope)
|
|
69
538
|
}
|
|
70
539
|
|
|
71
|
-
const
|
|
540
|
+
const f = new Function('scope', functionCode(code.slice(2, -2)))
|
|
72
541
|
|
|
73
|
-
|
|
542
|
+
return () => {
|
|
543
|
+
|
|
544
|
+
const result = f.call(element, scope)
|
|
545
|
+
// 三种情况,如:<div disable="{{disable}}"></div>
|
|
546
|
+
// true - <div disable></div>
|
|
547
|
+
// false - <div></div>
|
|
548
|
+
// "disable" - <div disable="disable"></div>
|
|
549
|
+
switch (result) {
|
|
550
|
+
|
|
551
|
+
case true:
|
|
552
|
+
|
|
553
|
+
attr.nodeValue = ''
|
|
554
|
+
|
|
555
|
+
element.attributes.setNamedItem(attr)
|
|
556
|
+
|
|
557
|
+
break
|
|
558
|
+
|
|
559
|
+
case false:
|
|
74
560
|
|
|
75
|
-
|
|
561
|
+
element.removeAttribute(attr.nodeName)
|
|
76
562
|
|
|
77
|
-
|
|
563
|
+
break
|
|
78
564
|
|
|
79
|
-
|
|
565
|
+
default:
|
|
80
566
|
|
|
81
|
-
|
|
567
|
+
attr.nodeValue = result
|
|
82
568
|
|
|
83
|
-
|
|
569
|
+
element.attributes.setNamedItem(attr)
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* 特殊情况处理属性的模板字符串,多种情况
|
|
575
|
+
* @param {Object} scope
|
|
576
|
+
* @param {Attr} attr
|
|
577
|
+
* @param {String} key
|
|
578
|
+
* @param {String} ceName
|
|
579
|
+
*/
|
|
580
|
+
const disposeSpecialAttr = (scope, attr, key, ceName) => {
|
|
84
581
|
|
|
85
|
-
|
|
582
|
+
const ownerElement = attr.ownerElement
|
|
583
|
+
let name = attr.nodeName
|
|
584
|
+
// 处理属性的事件,是直接覆盖事件,不是 addEventListener
|
|
585
|
+
if (name.startsWith('on')) {
|
|
86
586
|
|
|
87
|
-
|
|
587
|
+
ownerElement.removeAttribute(name)
|
|
588
|
+
// 如果绑定的是函数,直接把函数绑定在事件上
|
|
589
|
+
if (!key.includes('(')) {
|
|
88
590
|
|
|
89
|
-
|
|
591
|
+
const list = key.split('.')
|
|
90
592
|
|
|
91
|
-
|
|
593
|
+
let k = '', obj = scope, index = 0
|
|
92
594
|
|
|
93
|
-
|
|
595
|
+
for ([index, k] of list.entries()) {
|
|
94
596
|
|
|
95
|
-
|
|
597
|
+
if (index === list.length - 1) {
|
|
598
|
+
|
|
599
|
+
break
|
|
96
600
|
}
|
|
97
601
|
|
|
98
|
-
|
|
602
|
+
if (obj !== null && typeof obj === 'object' && Reflect.has(obj, k)) {
|
|
603
|
+
|
|
604
|
+
obj = obj[k]
|
|
99
605
|
|
|
100
|
-
|
|
606
|
+
} else {
|
|
101
607
|
|
|
102
|
-
|
|
608
|
+
obj = {}
|
|
609
|
+
|
|
610
|
+
break
|
|
103
611
|
}
|
|
104
612
|
}
|
|
105
613
|
|
|
106
|
-
if (
|
|
614
|
+
if (typeof obj[k] === "function") {
|
|
107
615
|
|
|
108
|
-
|
|
616
|
+
ownerElement[name] = event => obj[k](event)
|
|
109
617
|
|
|
110
|
-
|
|
618
|
+
return true
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
// 不是函数触发事件执行相应代码块
|
|
111
622
|
|
|
112
|
-
|
|
623
|
+
const f = new Function('scope', 'event', functionCode(key))
|
|
113
624
|
|
|
114
|
-
|
|
115
|
-
|
|
625
|
+
ownerElement[name] = event => f.call(ownerElement, scope, event)
|
|
626
|
+
|
|
627
|
+
return true
|
|
628
|
+
}
|
|
629
|
+
// 动态把节点加入或者移除 DOM 树
|
|
630
|
+
if (name === 'connect') {
|
|
631
|
+
|
|
632
|
+
ownerElement.removeAttribute(name)
|
|
633
|
+
|
|
634
|
+
const f = new Function('scope', functionCode(key))
|
|
635
|
+
|
|
636
|
+
const tmpNode = createTmpNode('节点断开重连的标记')
|
|
637
|
+
|
|
638
|
+
const update = () => {
|
|
639
|
+
|
|
640
|
+
if (f(scope)) {
|
|
641
|
+
|
|
642
|
+
tmpNode.parentNode?.replaceChild(ownerElement, tmpNode)
|
|
643
|
+
|
|
644
|
+
} else {
|
|
645
|
+
|
|
646
|
+
ownerElement.parentNode?.replaceChild(tmpNode, ownerElement)
|
|
116
647
|
}
|
|
117
648
|
}
|
|
118
649
|
|
|
119
|
-
|
|
650
|
+
// console.log(scope, attr.nodeValue, update)
|
|
120
651
|
|
|
121
|
-
|
|
652
|
+
addListener(scope, attr.nodeValue, throttle(() => update()))
|
|
122
653
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
654
|
+
update()
|
|
655
|
+
|
|
656
|
+
return true
|
|
657
|
+
}
|
|
658
|
+
// 在节点位置按照数组循环渲染节点
|
|
659
|
+
if (name === 'each') {
|
|
660
|
+
|
|
661
|
+
ownerElement.removeAttribute(name)
|
|
662
|
+
|
|
663
|
+
let data = {ownerElement, scope}
|
|
664
|
+
// 如果绑定的直接就是对象的值,则对象的值改变会重新根据变化的数组渲染
|
|
665
|
+
if (Reflect.has(scope, key)) {
|
|
666
|
+
|
|
667
|
+
const update = () => data = handleArray(data, scope[key])
|
|
668
|
+
|
|
669
|
+
addListener(scope, attr.nodeValue, throttle(() => update()))
|
|
670
|
+
|
|
671
|
+
update()
|
|
672
|
+
|
|
673
|
+
return true
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const f = new Function('scope', functionCode(key))
|
|
677
|
+
|
|
678
|
+
handleArray(data, f(scope))
|
|
679
|
+
|
|
680
|
+
return true
|
|
681
|
+
}
|
|
682
|
+
// 自定义元素的属性会以键值对的形式更新到自定义属性的对象里面
|
|
683
|
+
if (ceName) {
|
|
684
|
+
|
|
685
|
+
ownerElement.removeAttribute(name)
|
|
686
|
+
|
|
687
|
+
const f = new Function('scope', functionCode(key))
|
|
688
|
+
|
|
689
|
+
if (name.includes('-')) {
|
|
690
|
+
|
|
691
|
+
name = name.split('-').map((key, i) => key && i ? key[0].toUpperCase() + key.slice(1) : key).join('')
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const update = () => {
|
|
695
|
+
|
|
696
|
+
const result = f(scope)
|
|
697
|
+
|
|
698
|
+
ownerElement[name] = typeof result === 'function' ? result.bind(scope) : result
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// const update = () => ownerElement[name] = f(scope)
|
|
702
|
+
|
|
703
|
+
addListener(scope, attr.nodeValue, throttle(() => update()))
|
|
704
|
+
|
|
705
|
+
update()
|
|
706
|
+
|
|
707
|
+
return true
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return false
|
|
126
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* 处理节点属性
|
|
714
|
+
* @param {Object} scope
|
|
715
|
+
* @param {Attr} attr
|
|
716
|
+
* @param {String} name
|
|
717
|
+
*/
|
|
718
|
+
const disposeAttr = (scope, attr, name) => {
|
|
719
|
+
|
|
720
|
+
const value = attr.nodeValue
|
|
721
|
+
|
|
722
|
+
if (value.match(/{{(.*?)}}/) === null) return
|
|
127
723
|
|
|
128
|
-
|
|
724
|
+
// console.log('disposeAttr', attr, value)
|
|
725
|
+
// 如果属性绑定的是特殊值就按照特殊情况处理
|
|
726
|
+
if (value.startsWith('{{') && value.endsWith('}}') && disposeSpecialAttr(scope, attr, value.slice(2, -2), name)) return
|
|
129
727
|
|
|
130
|
-
|
|
728
|
+
const fn = disposeAttrValue(scope, attr.ownerElement, attr, value)
|
|
729
|
+
|
|
730
|
+
addListener(scope, value, throttle(() => fn()))
|
|
731
|
+
|
|
732
|
+
fn()
|
|
131
733
|
}
|
|
734
|
+
/**
|
|
735
|
+
* 处理自身或者子 node 的属性或者节点内容
|
|
736
|
+
* @param scope
|
|
737
|
+
* @param node
|
|
738
|
+
* @param self
|
|
739
|
+
* @returns {*}
|
|
740
|
+
*/
|
|
741
|
+
export const bindNode = (scope, node, self) => {
|
|
132
742
|
|
|
133
|
-
|
|
743
|
+
const list = self ? [node] : Array.from(node.childNodes)
|
|
134
744
|
|
|
135
|
-
for (const
|
|
745
|
+
for (const n of list) {
|
|
136
746
|
|
|
137
|
-
|
|
747
|
+
if (n.attributes) {
|
|
138
748
|
|
|
139
|
-
|
|
749
|
+
for (const attr of Array.from(n.attributes)) {
|
|
140
750
|
|
|
141
|
-
|
|
751
|
+
disposeAttr(scope, attr, getCEName(n))
|
|
142
752
|
|
|
143
|
-
|
|
753
|
+
if (attr.nodeName === 'each'
|
|
754
|
+
&& attr.nodeValue.startsWith('{{')
|
|
755
|
+
&& attr.nodeValue.endsWith('}}')) return node
|
|
144
756
|
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (n.childNodes) {
|
|
760
|
+
|
|
761
|
+
bindNode(scope, n)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (n.nodeType === 3 && n.nodeValue !== null) {
|
|
765
|
+
|
|
766
|
+
disposeNode(scope, n)
|
|
767
|
+
}
|
|
145
768
|
|
|
146
|
-
|
|
769
|
+
if (n.nodeType === 1) {
|
|
147
770
|
|
|
148
|
-
|
|
771
|
+
const name = getCEName(n)
|
|
772
|
+
|
|
773
|
+
if (name) {
|
|
774
|
+
|
|
775
|
+
importCE(n, name)
|
|
149
776
|
}
|
|
150
777
|
}
|
|
151
778
|
}
|
|
152
779
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
780
|
+
return node
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* 把节点字符串转化为节点对象
|
|
784
|
+
* @param html
|
|
785
|
+
* @returns {{create: function(*): *}}
|
|
786
|
+
*/
|
|
787
|
+
export const bind = html => {
|
|
788
|
+
|
|
789
|
+
const content = document.createElement('template')
|
|
790
|
+
|
|
791
|
+
content.innerHTML = html
|
|
792
|
+
|
|
793
|
+
return {create: scope => bindNode(scope, document.importNode(content.content, true))}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const style = document.createElement('style')
|
|
157
797
|
|
|
158
|
-
|
|
798
|
+
style.innerHTML = `
|
|
799
|
+
*:not(:defined) {
|
|
800
|
+
display: none
|
|
801
|
+
}
|
|
802
|
+
`
|
|
803
|
+
// 通过样式隐藏没有加载的自定义标签
|
|
804
|
+
document.head.appendChild(style)
|
|
159
805
|
|
|
160
|
-
|
|
806
|
+
// DOM 加载完成后集中处理没有加载的自定义元素
|
|
807
|
+
addEventListener('DOMContentLoaded', () => document.querySelectorAll('*:not(:defined)').forEach(node => importCE(node)))
|
|
161
808
|
|
|
162
|
-
|
|
809
|
+
class EventBus {
|
|
163
810
|
|
|
164
|
-
|
|
811
|
+
value = undefined
|
|
812
|
+
/**
|
|
813
|
+
* @type {Map<Function, {once: Boolean, preValue: Boolean}>}
|
|
814
|
+
*/
|
|
815
|
+
targets = new Map()
|
|
165
816
|
|
|
166
|
-
|
|
817
|
+
add(callback, options = Object.create(null)) {
|
|
167
818
|
|
|
168
|
-
|
|
819
|
+
this.targets.set(callback, options)
|
|
169
820
|
|
|
170
|
-
|
|
821
|
+
if (options.preValue && this.value !== undefined) {
|
|
171
822
|
|
|
172
|
-
|
|
823
|
+
callback(...this.value)
|
|
173
824
|
}
|
|
174
825
|
}
|
|
175
826
|
|
|
176
|
-
|
|
827
|
+
remove(callback) {
|
|
177
828
|
|
|
178
|
-
|
|
829
|
+
this.targets.delete(callback)
|
|
830
|
+
}
|
|
179
831
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
832
|
+
dispatch(...args) {
|
|
833
|
+
|
|
834
|
+
this.value = args
|
|
835
|
+
|
|
836
|
+
return Promise.allSettled(this.targets.entries().map(([callback, {once}]) => {
|
|
837
|
+
|
|
838
|
+
if (once) {
|
|
839
|
+
|
|
840
|
+
this.targets.delete(callback)
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return callback(...args)
|
|
844
|
+
}))
|
|
845
|
+
}
|
|
192
846
|
}
|
|
847
|
+
|
|
848
|
+
const eventBusObj = Object.create(null)
|
|
849
|
+
// eventBus 实现订阅和分发,订阅是能选择是否获取上一个分发内容,在事件产生后订阅的也能拿到分发的内容
|
|
850
|
+
const eventBus = Object.defineProperties(Object.create(null), {
|
|
851
|
+
add: {
|
|
852
|
+
value: (name, callback, options) => {
|
|
853
|
+
|
|
854
|
+
if (!Reflect.has(eventBusObj, name)) {
|
|
855
|
+
|
|
856
|
+
eventBusObj[name] = new EventBus()
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
eventBusObj[name].add(callback, options)
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
remove: {
|
|
863
|
+
value: (name, callback) => {
|
|
864
|
+
|
|
865
|
+
if (!Reflect.has(eventBusObj, name)) return
|
|
866
|
+
|
|
867
|
+
eventBusObj[name].remove(callback)
|
|
868
|
+
}
|
|
869
|
+
},
|
|
870
|
+
dispatch: {
|
|
871
|
+
value: (name, ...args) => {
|
|
872
|
+
|
|
873
|
+
if (!Reflect.has(eventBusObj, name)) {
|
|
874
|
+
|
|
875
|
+
eventBusObj[name] = new EventBus()
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return eventBusObj[name].dispatch(...args)
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
})
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* @returns {{
|
|
885
|
+
* add: (name: String, callback: Function, options: Object) => void,
|
|
886
|
+
* remove: (name: String, callback: Function) => void,
|
|
887
|
+
* dispatch: (name: String, ...args) => void
|
|
888
|
+
* }}
|
|
889
|
+
*/
|
|
890
|
+
export const useEvent = () => eventBus
|
|
891
|
+
/**
|
|
892
|
+
* 通过 ref 或者子节点
|
|
893
|
+
* @param {Node} node
|
|
894
|
+
* @returns {any}
|
|
895
|
+
*/
|
|
896
|
+
export const useRef = node => {
|
|
897
|
+
|
|
898
|
+
const result = Object.create(null)
|
|
899
|
+
|
|
900
|
+
for (const n of node.querySelectorAll('[ref]')) {
|
|
901
|
+
|
|
902
|
+
result[n.attributes.ref.nodeValue] = n
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
return result
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* 把属性转化为简单的键值对对象
|
|
909
|
+
* @param node
|
|
910
|
+
* @returns {any}
|
|
911
|
+
*/
|
|
912
|
+
export const useAttr = node => {
|
|
913
|
+
|
|
914
|
+
const result = Object.create(null)
|
|
915
|
+
|
|
916
|
+
for (const n of node.attributes) {
|
|
917
|
+
|
|
918
|
+
result[n.nodeName] = n.nodeValue
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
return result
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* @param {Object} target
|
|
925
|
+
* @returns {(function(*, boolean=): void)|*}
|
|
926
|
+
*/
|
|
927
|
+
export const useWatch = target => (props, call = false) => {
|
|
928
|
+
|
|
929
|
+
for (const [key, f] of Object.entries(props)) {
|
|
930
|
+
|
|
931
|
+
if (typeof f !== 'function') continue
|
|
932
|
+
|
|
933
|
+
let value = target[key]
|
|
934
|
+
|
|
935
|
+
Object.defineProperty(target, key, {
|
|
936
|
+
|
|
937
|
+
get: () => value,
|
|
938
|
+
set: val => {
|
|
939
|
+
|
|
940
|
+
const result = f(val, value)
|
|
941
|
+
|
|
942
|
+
if (result !== false) return value = val
|
|
943
|
+
}
|
|
944
|
+
})
|
|
945
|
+
|
|
946
|
+
call && f(value, value)
|
|
947
|
+
}
|
|
948
|
+
}
|