cbvirtua 1.0.89 → 1.0.91
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/package.json
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Directive, DirectiveBinding } from 'vue'
|
|
2
|
+
import { useTestStore } from '@/store/index'
|
|
3
|
+
|
|
4
|
+
// 存储高度
|
|
5
|
+
let originHeight = 0
|
|
6
|
+
|
|
7
|
+
// 视口变化 监听函数
|
|
8
|
+
const hanlderResize = (): void => {
|
|
9
|
+
const resizeHeight =
|
|
10
|
+
document.documentElement.clientHeight || document.body.clientHeight
|
|
11
|
+
if (originHeight < resizeHeight) {
|
|
12
|
+
// 安卓机下 用户收起键盘 同时input还有焦点
|
|
13
|
+
// 执行失去焦点逻辑
|
|
14
|
+
hanlderblur()
|
|
15
|
+
} else {
|
|
16
|
+
// 安卓机下 用户展开键盘 同时input还有焦点
|
|
17
|
+
// 执行获取焦点逻辑
|
|
18
|
+
handlerfocus()
|
|
19
|
+
}
|
|
20
|
+
originHeight = resizeHeight
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// pinia 存储
|
|
25
|
+
const testStore = useTestStore()
|
|
26
|
+
|
|
27
|
+
// 失去焦点 展示 底部按钮
|
|
28
|
+
const hanlderblur = (): void => {
|
|
29
|
+
testStore.setFooterMenuType(true)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 获取焦点 隐藏 底部按钮
|
|
33
|
+
const handlerfocus = (): void => {
|
|
34
|
+
testStore.setFooterMenuType(false)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// 自定义指令
|
|
39
|
+
const keyboardExpansion: Directive = {
|
|
40
|
+
/**
|
|
41
|
+
el:指令绑定到的元素。这可以用于直接操作 DOM。
|
|
42
|
+
binding:一个对象,包含以下属性。
|
|
43
|
+
value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。
|
|
44
|
+
oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
|
|
45
|
+
arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。
|
|
46
|
+
modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
|
|
47
|
+
instance:使用该指令的组件实例。
|
|
48
|
+
dir:指令的定义对象。
|
|
49
|
+
**/
|
|
50
|
+
mounted (el: Element, binding: DirectiveBinding<any>) {
|
|
51
|
+
// 查找 目标元素
|
|
52
|
+
// 这里 inputElement 指的是 input/textarea
|
|
53
|
+
const inputElement: Element | null = ['INPUT', 'TEXTAREA'].includes(el.tagName)
|
|
54
|
+
? el
|
|
55
|
+
: el.querySelector('input') || el.querySelector('textarea')
|
|
56
|
+
// 绑定事件
|
|
57
|
+
inputElement?.addEventListener('blur', hanlderblur)
|
|
58
|
+
|
|
59
|
+
inputElement?.addEventListener('focus', handlerfocus)
|
|
60
|
+
|
|
61
|
+
// 判断 安卓机情况下 添加视口监听
|
|
62
|
+
if ((/android/i).test(navigator.userAgent)) {
|
|
63
|
+
originHeight = document.documentElement.clientHeight || document.body.clientHeight
|
|
64
|
+
window.addEventListener('resize', hanlderResize, false)
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
unmounted() {
|
|
68
|
+
if ((/android/i).test(navigator.userAgent)) {
|
|
69
|
+
// 指令元素 销毁前remove上面绑定的事件
|
|
70
|
+
window.removeEventListener('resize', hanlderResize, false)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
|
|
74
|
+
|
|
75
|
+
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
|
|
76
|
+
|
|
77
|
+
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
|
|
78
|
+
|
|
79
|
+
我们会在稍后讨论渲染函数时介绍更多 VNodes 的细节。
|
|
80
|
+
|
|
81
|
+
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
|
|
82
|
+
|
|
83
|
+
unbind:只调用一次,指令与元素解绑时调用。
|
|
84
|
+
|
|
85
|
+
@media (min-aspect-ratio: 13/20) {
|
|
86
|
+
.button {
|
|
87
|
+
display: none;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
MutationObserver
|
|
2
|
+
2024-08-29
|
|
3
|
+
296
|
|
4
|
+
阅读4分钟
|
|
5
|
+
|
|
6
|
+
前言
|
|
7
|
+
MutationObserver 是一个dom观察器,当dom发生改变前(willchanged),会执行其回调函数,自己观察会发现不少三方UI组件用到了
|
|
8
|
+
|
|
9
|
+
其会将回调添加到当前执行栈的末尾,且在其他异步任务之前执行((如I/O、计时器等)),对于高性能低延迟的场景非常好用,也避免了延迟调用出现的卡顿问题
|
|
10
|
+
|
|
11
|
+
MutationObserver
|
|
12
|
+
MutationObserver 创建观察很简单,直接 new MutationObserver 即可
|
|
13
|
+
|
|
14
|
+
new MutationObserver 创建监听回调
|
|
15
|
+
会在观察内容发生改变操作前,回调该函数,因为打印时会发件 target 节点的 childNodes 节点没有变化
|
|
16
|
+
|
|
17
|
+
第一个参数为变化数组,会按照顺序记录先后的改变操作(增删等),observer为我们的观察对象,可以取消、观察
|
|
18
|
+
|
|
19
|
+
let obser = new MutationObserver(
|
|
20
|
+
(mutations: MutationRecord[], observer: MutationObserver) => {
|
|
21
|
+
console.log(mutations);
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
回调返回值大致结构如下所示,主要用到第一个参数,第二个参数后面介绍,可用可不用
|
|
25
|
+
|
|
26
|
+
QQ_1724749432698.png
|
|
27
|
+
|
|
28
|
+
observer 开启监听,设置监听规则
|
|
29
|
+
通过调用其 observer 函数来启用监听,可以设置 config 参数,一般用 childList、subtree、characterDataOldValue 就足够了,参数使用如下所示
|
|
30
|
+
|
|
31
|
+
const dom = document.getElementById("pppp");
|
|
32
|
+
|
|
33
|
+
obser.observe(dom!, {
|
|
34
|
+
childList: true
|
|
35
|
+
});
|
|
36
|
+
config 属性如下所示,根据自己需要取即可
|
|
37
|
+
|
|
38
|
+
/** 设置一个属性本地名(不包含命名空间)的列表,如果并非所有属性更改都需要被观察,并且 attributes 为 true 或被省略。 */
|
|
39
|
+
attributeFilter?: string[];
|
|
40
|
+
/** 设置为 true,如果 attributes 为 true 或被省略,并且需要记录目标属性更改前的值。 */
|
|
41
|
+
attributeOldValue?: boolean;
|
|
42
|
+
/** 设置为 true,如果要观察目标属性的更改。如果指定了 attributeOldValue 或 attributeFilter,则可以省略此属性。 */
|
|
43
|
+
attributes?: boolean;
|
|
44
|
+
/** 设置为 true,如果要观察目标数据的更改。如果指定了 characterDataOldValue,则可以省略此属性。 */
|
|
45
|
+
characterData?: boolean;
|
|
46
|
+
/** 设置为 true,如果 characterData 为 true 或被省略,并且需要记录目标更改前的数据。 */
|
|
47
|
+
characterDataOldValue?: boolean;
|
|
48
|
+
/** 设置为 true,如果要观察目标子节点的更改。 */
|
|
49
|
+
childList?: boolean;
|
|
50
|
+
/** 设置为 true,如果要观察不只是目标,还有目标后代的更改。 */
|
|
51
|
+
subtree?: boolean;
|
|
52
|
+
disconnect取消监听
|
|
53
|
+
通过 disconnect 取消监听,有时为了避免不必要的监听,可以在合适的时机取消监听,在重新开启监听
|
|
54
|
+
|
|
55
|
+
obser.disconnect()
|
|
56
|
+
案例(让被删除节点恢复案例,部分水印防伪功能)
|
|
57
|
+
使用 MutationObserver 监听一个 pppp 的节点,检测到其删除操作后,我们给某个节点主动添加回来,一些水印防伪功能会用到,我们就做一个简易的防删除案例
|
|
58
|
+
|
|
59
|
+
//查找dom,用于监听其子节点
|
|
60
|
+
const dom = document.getElementById("pppp");
|
|
61
|
+
|
|
62
|
+
//设置改变前的回调
|
|
63
|
+
const obser = new MutationObserver(
|
|
64
|
+
(mutations: MutationRecord[], observer: MutationObserver) => {
|
|
65
|
+
mutations.forEach((node) => {
|
|
66
|
+
node.removedNodes.forEach((node) => {
|
|
67
|
+
dom?.appendChild(node);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
//添加回调
|
|
73
|
+
obser.observe(dom!, {
|
|
74
|
+
childList: true
|
|
75
|
+
});
|
|
76
|
+
这样完成了一个删除后的恢复功能,也可以根据需要调用 disconnect、observe方法,如果不想由于回调内部操作再次触发回调,需要这样做
|
|
77
|
+
|
|
78
|
+
例如:上面的删除后的追加操作,如果里面是不是单纯追加,而是重置操作,可能还有删除和追加操作,则再次删除操作可能会触发回调地狱,需要额外添加判断条件,如果使用 disconnect + observe则可以有效避免
|
|
79
|
+
|
|
80
|
+
此外,可能还会有很多情况,有时需要在操作的末尾,可能还会有其他dom操作,要延迟恢复监听的话,可以通过setTimeout 重新启动一个宏队列任务避免提前开启监听
|
|
81
|
+
|
|
82
|
+
毕竟其回调会被追加到当前执行栈的末尾,可以通过 setTimeout 会重新启动一个宏队列任务(优先级在执行中的微队列任务之后),以此解决监听时机问题
|
|
83
|
+
|
|
84
|
+
const obser = new MutationObserver(
|
|
85
|
+
(mutations: MutationRecord[], observer: MutationObserver) => {
|
|
86
|
+
observer.disconnect();
|
|
87
|
+
|
|
88
|
+
...我们的操作
|
|
89
|
+
|
|
90
|
+
observer.observe(dom!, {
|
|
91
|
+
childList: true,
|
|
92
|
+
});
|
|
93
|
+
//开启一个新的
|
|
94
|
+
// setTimeout(() => {
|
|
95
|
+
// observer.observe(dom!, {
|
|
96
|
+
// childList: true,
|
|
97
|
+
// });
|
|
98
|
+
// }, 0);
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
整体代码
|
|
102
|
+
function App() {
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
const dom = document.getElementById("pppp");
|
|
105
|
+
//改变前的回调
|
|
106
|
+
const obser = new MutationObserver(
|
|
107
|
+
(mutations: MutationRecord[], observer: MutationObserver) => {
|
|
108
|
+
console.log(mutations);
|
|
109
|
+
observer.disconnect();
|
|
110
|
+
mutations.forEach((node) => {
|
|
111
|
+
node.removedNodes.forEach((node) => {
|
|
112
|
+
dom?.appendChild(node);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
observer.observe(dom!, {
|
|
116
|
+
childList: true,
|
|
117
|
+
});
|
|
118
|
+
// setTimeout(() => {
|
|
119
|
+
// observer.observe(dom!, {
|
|
120
|
+
// childList: true,
|
|
121
|
+
// });
|
|
122
|
+
// }, 0);
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
obser.observe(dom!, {
|
|
126
|
+
childList: true,
|
|
127
|
+
});
|
|
128
|
+
}, []);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div className="App">
|
|
132
|
+
<header className="App-header">
|
|
133
|
+
<img src={logo} className="App-logo" alt="logo" />
|
|
134
|
+
<p id="pppp">
|
|
135
|
+
Edit <code>src/App.tsx</code> and save to reload.
|
|
136
|
+
</p>
|
|
137
|
+
</header>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
最后
|
|
142
|
+
自己也可以尝试将添加的文本信息换个颜色🤣
|