Skip to content

Commit 05d8039

Browse files
authored
Merge pull request #578 from Tencent/dev
v3.15.0
2 parents 5933fb4 + 3d53d44 commit 05d8039

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1810
-157
lines changed

CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
English | [简体中文](./CHANGELOG_CN.md)
22

3+
## 3.15.0 (2022-11-02)
4+
5+
- `Feat(Log)` Add recycle scrolling to imporove performance, and add scroll to top/bottom buttons. (PR #570)
6+
- `Feat(Log)` Add support for `console.group(), console.groupCollapsed(), console.groupEnd()`. (issue #545)
7+
- `Feat(Network)` Add recycle scrolling to imporove performance.
8+
- `Feat(Network)` Add "Start Time" of a request.
9+
- `Feat(Network)` Use `curl` instead of `url` as the copy value of a request. (issue #410)
10+
- `Fix(Storage)` Fix an event bug that overflow content cannot scroll. (issue #542)
11+
- `Fix(Core)` Fix click event on `<select>` elements. (PR #577)
12+
13+
314
## 3.14.7 (2022-09-23)
415

516
- `Perf(Log)` Optimize rendering performance when adding logs. (PR #567)

CHANGELOG_CN.md

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
[English](./CHANGELOG.md) | 简体中文
22

3+
## 3.15.0 (2022-11-02)
4+
5+
- `Feat(Log)` 新增虚拟滚动列表以提升性能,并支持快速滚动到顶部/底部。 (PR #570)
6+
- `Feat(Log)` 新增对 `console.group(), console.groupCollapsed(), console.groupEnd()` 方法的支持。 (issue #545)
7+
- `Feat(Network)` 新增虚拟滚动列表以提升性能。
8+
- `Feat(Network)` 新增 request 的 "Start Time"(发起时间)。
9+
- `Feat(Network)` 使用 `curl` 格式作为 request 的复制内容,而非 `url`。 (issue #410)
10+
- `Fix(Storage)` 修复内容溢出的元素无法滑动的问题。 (issue #542)
11+
- `Fix(Core)` 修复 `<select>` 的点击事件问题。 (PR #577)
12+
13+
314
## 3.14.7 (2022-09-23)
415

516
- `Perf(Log)` 优化打印日志时的性能。 (PR #567)

dev/log.html

+20
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<a onclick="logTypes()" href="javascript:;" class="weui_btn weui_btn_default">Log/Info/Debug/Warn/Error</a>
3838
<a onclick="differentPanelLog()" href="javascript:;" class="weui_btn weui_btn_default">Output to Different Panels</a>
3939
<a onclick="logTime()" href="javascript:;" class="weui_btn weui_btn_default">console.time</a>
40+
<a onclick="logGroup()" href="javascript:;" class="weui_btn weui_btn_default">console.group</a>
4041
</div>
4142

4243
<div class="section">
@@ -160,6 +161,25 @@
160161
console.log('Wait...', i);
161162
}
162163
console.timeEnd(label);
164+
console.timeEnd(label);
165+
}
166+
167+
function logGroup() {
168+
vConsole.show();
169+
console.log('This is the outer level');
170+
console.group();
171+
console.log('Level 2');
172+
console.group(aa);
173+
console.log('Level 3');
174+
console.groupCollapsed('LV4');
175+
console.warn('More of level 4');
176+
console.info(aa);
177+
console.groupEnd();
178+
console.log('Back to level 3')
179+
console.groupEnd();
180+
console.log('Back to level 2');
181+
console.groupEnd();
182+
console.log('Back to the outer level');
163183
}
164184

165185
function formattingLog() {

dev/network.html

+49
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<div class="hd">XMLHttpRequest</div>
1919

2020
<a onclick="getAjax()" href="javascript:;" class="weui_btn weui_btn_default">GET XHR</a>
21+
<a onclick="putAjax()" href="javascript:;" class="weui_btn weui_btn_default">PUT XHR</a>
2122
<a onclick="postAjax()" href="javascript:;" class="weui_btn weui_btn_default">POST XHR (Data: Object)</a>
2223
<a onclick="postAjax('json')" href="javascript:;" class="weui_btn weui_btn_default">POST XHR (Data: JSON String)</a>
2324
<a onclick="postAjax('', 'massive.json')" href="javascript:;" class="weui_btn weui_btn_default">POST XHR (Massive Resp)</a>
@@ -34,6 +35,7 @@
3435
<a onclick="getFetchText()" href="javascript:;" class="weui_btn weui_btn_default">GET Fetch Text</a>
3536
<a onclick="getFetchJsonText()" href="javascript:;" class="weui_btn weui_btn_default">GET Fetch JSON Text</a>
3637
<a onclick="getFetch500()" href="javascript:;" class="weui_btn weui_btn_default">GET Fetch 500</a>
38+
<a onclick="putFetch()" href="javascript:;" class="weui_btn weui_btn_default">PUT Fetch</a>
3739
<a onclick="postFetch()" href="javascript:;" class="weui_btn weui_btn_default">POST Fetch</a>
3840
<a onclick="postFetchByRequest()" href="javascript:;" class="weui_btn weui_btn_default">POST Fetch Using XHR</a>
3941
<a onclick="optionsFetch()" href="javascript:;" class="weui_btn weui_btn_default">OPTIONS Fetch</a>
@@ -138,6 +140,30 @@
138140
};
139141
}
140142

143+
function putAjax() {
144+
showPanel();
145+
const url = window.location.origin + '/dev/data/success.json?method=xhrGet&id=' + Math.random() + '&<xss0>';
146+
147+
let postData = {
148+
foo: 'bar',
149+
book: { id: 123, type: 'comic', uid: [4,5,6,7,8] },
150+
id: `${Math.random()}${Math.random()}${Math.random()}${Math.random()}${Math.random()}${Math.random()}`,
151+
type: 'xhr',
152+
'<xss0>': '<xss1> XSS Attack!'
153+
};
154+
155+
const xhr = new XMLHttpRequest();
156+
xhr.open('PUT', url);
157+
xhr.setRequestHeader('custom-header', 'foobar');
158+
xhr.send(postData);
159+
xhr.onload = () => {
160+
console.log('put XHR Response:', JSON.parse(xhr.response));
161+
};
162+
xhr.onerror = () => {
163+
console.log('put XHR Error');
164+
};
165+
}
166+
141167
function getFetch() {
142168
showPanel();
143169
window.fetch('./data/success.json?method=fetchGet&id=' + Math.random(), {
@@ -185,6 +211,29 @@
185211
});
186212
}
187213

214+
function putFetch() {
215+
showPanel();
216+
window.fetch('./data/success.json?method=fetchPut&id=' + Math.random(), {
217+
method: 'PUT',
218+
headers: {
219+
'custom-header': 'foobar',
220+
'content-type': 'application/json'
221+
},
222+
body: { foo: 'bar', id: Math.random(), type: 'fetch' },
223+
}).then((data) => {
224+
console.log('putFetch() response:', data);
225+
setTimeout(() => {
226+
data.json().then((res) => {
227+
console.log(res);
228+
});
229+
}, 3000);
230+
// return data;
231+
// return data.json();
232+
}).then((data) => {
233+
// console.log('putFetch() json:', data);
234+
});
235+
}
236+
188237
function postFetch() {
189238
window.fetch('./data/success.json?method=fetchPost&<xss2>x=<xss2>', {
190239
method: 'POST',

doc/plugin_event_list.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ Trigger while vConsole trying to create a new tab for a plugin. This event will
2626
After binding this event, vConsole will get HTML from your callback to render a tab. A new tab will definitely be added if you bind this event, no matter what tab's HTML you set. Do not bind this event if you don't want to add a new tab.
2727

2828
##### Callback Arguments:
29-
- (required) function(html): a callback function that receives the content HTML of the new tab. `html` can be a HTML `string` or an `HTMLElement` object (Or object which supports `appendTo()` method, like JQuery object).
29+
- (required) function(html, options): a callback function that receives the content HTML of the new tab. `html` can be a HTML `string` or an `HTMLElement` object (Or object which supports `appendTo()` method, like JQuery object), and an optional `object` for tab options.
30+
31+
A tab option is an object with properties:
32+
33+
Property | | | |
34+
------- | ------- | ------- | -------
35+
fixedHeight | boolean | optional | Whether the height of tab is fixed to 100%.
3036

3137
##### Example:
3238

doc/plugin_event_list_CN.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ myPlugin.on('init', function() {
3333
绑定此事件后,vConsole 会认为此插件需要创建新 tab,并会将 callback 中获取的 HTML 用于渲染 tab。因此,只要绑定了此事件,新 tab 肯定会被渲染到页面中,无论 callback 传入的 HTML 是否为空。如果不需要添加新 tab,请不要绑定此事件。
3434

3535
##### Callback 参数
36-
- (必填) function(html): 回调函数,接收一个 HTML 参数用于渲染 tab。`html` 可以为 HTML 字符串,或者 `HTMLElement` 对象(或支持 `appendTo()` 方法的对象,如 jQuery 对象)。
36+
- (必填) function(html, options): 回调函数,第一个参数接收一个 HTML 参数用于渲染 tab。`html` 可以为 HTML 字符串,或者 `HTMLElement` 对象(或支持 `appendTo()` 方法的对象,如 jQuery 对象)。第二个参数接收一个可选配置信息。
37+
38+
配置的参数为:
39+
40+
Property | | | |
41+
------- | ------- | ------- | -------
42+
fixedHeight | boolean | 选填 | tab 高度固定为 100%。
3743

3844
##### 例子:
3945

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vconsole",
3-
"version": "3.14.7",
3+
"version": "3.15.0",
44
"description": "A lightweight, extendable front-end developer tool for mobile web page.",
55
"homepage": "https://github.com/Tencent/vConsole",
66
"files": [
File renamed without changes.
File renamed without changes.

src/component/iconCopy.svelte src/component/icon/iconCopy.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import copy from 'copy-text-to-clipboard';
3-
import * as tool from '../lib/tool';
4-
import Icon from '../component/icon.svelte';
3+
import * as tool from '../../lib/tool';
4+
import Icon from './icon.svelte';
55
66
export let content: any = '';
77
export let handler: (content: any) => string = undefined;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script lang="ts">
2+
import { onMount, onDestroy } from 'svelte';
3+
4+
export let show: boolean;
5+
export let top: boolean;
6+
7+
export let onResize: (height: number) => void = () => {};
8+
9+
let item: HTMLDivElement | undefined;
10+
11+
let observer: ResizeObserver | null = null;
12+
13+
onMount(() => {
14+
if (show) {
15+
onResize(item.getBoundingClientRect().height);
16+
}
17+
observer = new ResizeObserver((entries) => {
18+
const entry = entries[0];
19+
if (show) onResize(entry.contentRect.height)
20+
});
21+
observer.observe(item);
22+
});
23+
24+
onDestroy(() => {
25+
observer.disconnect();
26+
});
27+
</script>
28+
29+
<div
30+
bind:this={item}
31+
class="vc-scroller-item"
32+
style:display={show ? "block" : "none"}
33+
style:top="{top}px"
34+
>
35+
<slot />
36+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
const createRecycleManager = () => {
2+
const recycles: { key: number; index: number; show: boolean }[] = [];
3+
4+
const poolKeys: number[] = [];
5+
let poolStartIndex = 0;
6+
let poolEndIndex = 0;
7+
8+
let lastItemCount = 0;
9+
let lastStart = 0;
10+
let lastEnd = 0;
11+
12+
const update = (itemCount: number, start: number, end: number) => {
13+
if (lastItemCount === itemCount && lastStart === start && lastEnd === end)
14+
return recycles;
15+
16+
const poolCount = poolKeys.length;
17+
18+
// 计算新的 visible 区域
19+
const newFirstPool =
20+
start <= poolEndIndex
21+
? // 1. 开头一定在 [0, start]
22+
Math.max(
23+
0,
24+
Math.min(
25+
start,
26+
// 2. 开头一定在 [poolStartIndex, poolEndIndex) 之间
27+
Math.max(
28+
poolStartIndex,
29+
Math.min(poolEndIndex - 1, end - poolCount)
30+
)
31+
)
32+
)
33+
: start; // poolEndIndex 如果比 start 小,则前部无法保留下来
34+
35+
const newLastPool =
36+
poolStartIndex <= end
37+
? // 1. 结尾一定在 [end, itemCount] 之间
38+
Math.max(
39+
end,
40+
Math.min(
41+
itemCount,
42+
// 2. 结尾一定在 (poolStartIndex, poolEndIndex] 之间
43+
Math.max(
44+
poolStartIndex + 1,
45+
Math.min(poolEndIndex, newFirstPool + poolCount)
46+
)
47+
)
48+
)
49+
: end; // end 如果比 poolStartIndex 小,则后部无法保留下来
50+
51+
if (poolCount === 0 || newLastPool - newFirstPool < poolCount) {
52+
// 无法复用,全都重新生成
53+
const count = (recycles.length = poolKeys.length = end - start);
54+
for (let i = 0; i < count; i += 1) {
55+
poolKeys[i] = i;
56+
recycles[i] = {
57+
key: i,
58+
index: i + start,
59+
show: true,
60+
};
61+
}
62+
poolStartIndex = start;
63+
poolEndIndex = end;
64+
lastItemCount = itemCount;
65+
lastStart = start;
66+
lastEnd = end;
67+
return recycles;
68+
}
69+
70+
let usedPoolIndex = 0;
71+
let usedPoolOffset = 0;
72+
73+
// 复用区域
74+
let reuseStart = 0;
75+
let reuseEnd = 0;
76+
77+
if (poolEndIndex < newFirstPool || newLastPool < poolStartIndex) {
78+
// 完全没有交集,随便复用
79+
reuseStart = newFirstPool;
80+
reuseEnd = newFirstPool + poolCount;
81+
} else if (poolStartIndex < newFirstPool) {
82+
// 开头复用
83+
usedPoolOffset = newFirstPool - poolStartIndex;
84+
reuseStart = newFirstPool;
85+
reuseEnd = newFirstPool + poolCount;
86+
} else if (newLastPool < poolEndIndex) {
87+
// 尾部复用
88+
usedPoolOffset = poolCount - (poolEndIndex - newLastPool);
89+
reuseStart = newLastPool - poolCount;
90+
reuseEnd = newLastPool;
91+
} else if (newFirstPool <= poolStartIndex && poolEndIndex <= newLastPool) {
92+
// 新的 visible 是完全子集,直接复用
93+
reuseStart = poolStartIndex;
94+
reuseEnd = poolEndIndex;
95+
}
96+
97+
// 开头不可见区域
98+
// 如果有不可见区域,则一定是来自上一次 visible 的复用 row
99+
for (let i = newFirstPool; i < start; i += 1, usedPoolIndex += 1) {
100+
const poolKey = poolKeys[(usedPoolOffset + usedPoolIndex) % poolCount];
101+
const recycle = recycles[i - newFirstPool];
102+
recycle.key = poolKey;
103+
recycle.index = i;
104+
recycle.show = false;
105+
}
106+
107+
// 可见区域
108+
for (let i = start, keyIndex = 0; i < end; i += 1) {
109+
let poolKey: number;
110+
if (reuseStart <= i && i < reuseEnd) {
111+
// 复用 row
112+
poolKey = poolKeys[(usedPoolOffset + usedPoolIndex) % poolCount];
113+
usedPoolIndex += 1;
114+
} else {
115+
// 新建 row
116+
poolKey = poolCount + keyIndex;
117+
keyIndex += 1;
118+
}
119+
const recycleIndex = i - newFirstPool;
120+
if (recycleIndex < recycles.length) {
121+
const recycle = recycles[recycleIndex];
122+
recycle.key = poolKey;
123+
recycle.index = i;
124+
recycle.show = true;
125+
} else {
126+
recycles.push({
127+
key: poolKey,
128+
index: i,
129+
show: true,
130+
});
131+
}
132+
}
133+
134+
// 末尾不可见区域
135+
// 如果有不可见区域,则一定是来自上一次 visible 的复用 row
136+
for (let i = end; i < newLastPool; i += 1, usedPoolIndex += 1) {
137+
const poolKey = poolKeys[(usedPoolOffset + usedPoolIndex) % poolCount];
138+
const recycle = recycles[i - newFirstPool];
139+
recycle.key = poolKey;
140+
recycle.index = i;
141+
recycle.show = false;
142+
}
143+
144+
// 更新 poolKeys
145+
for (let i = 0; i < recycles.length; i += 1) {
146+
poolKeys[i] = recycles[i].key;
147+
}
148+
recycles.sort((a, b) => a.key - b.key);
149+
poolStartIndex = newFirstPool;
150+
poolEndIndex = newLastPool;
151+
lastItemCount = itemCount;
152+
lastStart = start;
153+
lastEnd = end;
154+
155+
return recycles;
156+
};
157+
158+
return update;
159+
};
160+
161+
export default createRecycleManager;

0 commit comments

Comments
 (0)