perf(console): rendering

This commit is contained in:
redhoodsu
2019-11-01 15:16:35 +08:00
parent 848158d407
commit 2356ed0083
3 changed files with 66 additions and 47 deletions

View File

@@ -41,7 +41,7 @@ export default class Console extends Tool {
} }
show() { show() {
super.show() super.show()
this._logger.restoreScroll() this._logger.renderViewport()
} }
overrideConsole() { overrideConsole() {
const origConsole = (this._origConsole = {}) const origConsole = (this._origConsole = {})

View File

@@ -31,10 +31,11 @@ import {
trim, trim,
lowerCase, lowerCase,
keys, keys,
$ $,
Emitter
} from '../lib/util' } from '../lib/util'
export default class Log { export default class Log extends Emitter {
constructor({ constructor({
type = 'log', type = 'log',
args = [], args = [],
@@ -44,6 +45,8 @@ export default class Log {
displayHeader = false, displayHeader = false,
ignoreFilter = false ignoreFilter = false
}) { }) {
super()
this.type = type this.type = type
this.group = group this.group = group
this.targetGroup = targetGroup this.targetGroup = targetGroup
@@ -137,8 +140,12 @@ export default class Log {
isAttached() { isAttached() {
return !!this.el.parentNode return !!this.el.parentNode
} }
updateHeight() { updateHeight(silent = true) {
this.height = this.el.offsetHeight const height = this.el.offsetHeight
if (this.height !== height) {
this.height = this.el.offsetHeight
if (!silent) this.emit('updateHeight')
}
} }
html() { html() {
return this.el.outerHTML return this.el.outerHTML
@@ -193,7 +200,7 @@ export default class Log {
if ($json.hasClass('eruda-hidden')) { if ($json.hasClass('eruda-hidden')) {
if ($json.data('init') !== 'true') { if ($json.data('init') !== 'true') {
const jsonViewer = new JsonViewer(src, $json) const jsonViewer = new JsonViewer(src, $json)
jsonViewer.on('change', () => this.updateHeight()) jsonViewer.on('change', () => this.updateHeight(false))
$json.data('init', 'true') $json.data('init', 'true')
} }
$json.rmClass('eruda-hidden') $json.rmClass('eruda-hidden')

View File

@@ -19,7 +19,7 @@ import {
toArr, toArr,
keys, keys,
last, last,
debounce throttle
} from '../lib/util' } from '../lib/util'
let id = 0 let id = 0
@@ -53,6 +53,8 @@ export default class Logger extends Emitter {
this._isAtBottom = true this._isAtBottom = true
this._groupStack = new Stack() this._groupStack = new Stack()
this.renderViewport = throttle(force => this._renderViewport(force), 16)
// https://developers.google.cn/web/tools/chrome-devtools/console/utilities // https://developers.google.cn/web/tools/chrome-devtools/console/utilities
this._global = { this._global = {
copy(value) { copy(value) {
@@ -79,9 +81,6 @@ export default class Logger extends Emitter {
this._bindEvent() this._bindEvent()
} }
restoreScroll() {
if (this._isAtBottom) this.scrollToBottom()
}
renderAsync(flag) { renderAsync(flag) {
this._asyncRender = flag this._asyncRender = flag
} }
@@ -281,8 +280,6 @@ export default class Logger extends Emitter {
this._attachLog(logs[i]) this._attachLog(logs[i])
} }
this.scrollToBottom()
return this return this
} }
insert(type, args) { insert(type, args) {
@@ -295,17 +292,6 @@ export default class Logger extends Emitter {
this._handleAsyncList() this._handleAsyncList()
} }
isAtBottom() {
const { scrollTop, scrollHeight, offsetHeight } = this._container
// invisible
if (offsetHeight !== 0) {
this._isAtBottom = scrollTop === scrollHeight - offsetHeight
return this._isAtBottom
} else {
return false
}
}
insertSync(type, args) { insertSync(type, args) {
const logs = this._logs const logs = this._logs
const groupStack = this._groupStack const groupStack = this._groupStack
@@ -318,8 +304,6 @@ export default class Logger extends Emitter {
return this return this
} }
const isAtBottom = this.isAtBottom()
const options = isStr(type) ? { type, args } : type const options = isStr(type) ? { type, args } : type
if (groupStack.size > 0) { if (groupStack.size > 0) {
options.group = groupStack.peek() options.group = groupStack.peek()
@@ -342,6 +326,7 @@ export default class Logger extends Emitter {
} }
let log = new Log(options) let log = new Log(options)
log.on('updateHeight', () => this.renderViewport())
const lastLog = this._lastLog const lastLog = this._lastLog
if ( if (
@@ -371,16 +356,8 @@ export default class Logger extends Emitter {
this.emit('insert', log) this.emit('insert', log)
if (isAtBottom) this.scrollToBottom()
return this return this
} }
scrollToBottom() {
const container = this._container
const { scrollHeight, offsetHeight } = container
container.scrollTop = scrollHeight - offsetHeight
}
toggleGroup(log) { toggleGroup(log) {
const { targetGroup } = log const { targetGroup } = log
targetGroup.collapsed ? this._openGroup(log) : this._collapseGroup(log) targetGroup.collapsed ? this._openGroup(log) : this._collapseGroup(log)
@@ -394,6 +371,7 @@ export default class Logger extends Emitter {
this._$bottomSpace.css({ height }) this._$bottomSpace.css({ height })
} }
_updateLogHeight(log) { _updateLogHeight(log) {
if (this._fakeEl.offsetParent === null) return
if (!log.isAttached()) { if (!log.isAttached()) {
this._fakeEl.appendChild(log.el) this._fakeEl.appendChild(log.el)
log.updateHeight() log.updateHeight()
@@ -408,7 +386,7 @@ export default class Logger extends Emitter {
const idx = displayLogs.indexOf(log) const idx = displayLogs.indexOf(log)
if (idx > -1) { if (idx > -1) {
displayLogs.splice(idx, 1) displayLogs.splice(idx, 1)
this._renderViewport() this.renderViewport()
} }
} }
// Binary search // Binary search
@@ -419,14 +397,14 @@ export default class Logger extends Emitter {
if (displayLogs.length === 0) { if (displayLogs.length === 0) {
displayLogs.push(log) displayLogs.push(log)
this._renderViewport() this.renderViewport()
return return
} }
const lastDisplayLog = last(displayLogs) const lastDisplayLog = last(displayLogs)
if (log.id > lastDisplayLog.id) { if (log.id > lastDisplayLog.id) {
displayLogs.push(log) displayLogs.push(log)
this._renderViewport() this.renderViewport()
return return
} }
@@ -457,7 +435,7 @@ export default class Logger extends Emitter {
displayLogs.splice(middleIdx, 0, log) displayLogs.splice(middleIdx, 0, log)
} }
this._renderViewport() this.renderViewport()
} }
_handleAsyncList() { _handleAsyncList() {
const asyncList = this._asyncList const asyncList = this._asyncList
@@ -467,7 +445,7 @@ export default class Logger extends Emitter {
this._asyncTimer = setTimeout(() => { this._asyncTimer = setTimeout(() => {
this._asyncTimer = null this._asyncTimer = null
let done = false let done = false
for (let i = 0; i < 25; i++) { for (let i = 0; i < 20; i++) {
const item = asyncList.shift() const item = asyncList.shift()
if (!item) { if (!item) {
done = true done = true
@@ -476,7 +454,7 @@ export default class Logger extends Emitter {
this.insertSync(item[0], item[1]) this.insertSync(item[0], item[1])
} }
if (!done) this._handleAsyncList() if (!done) this._handleAsyncList()
}, 25) }, 15)
} }
_injectGlobal() { _injectGlobal() {
each(this._global, (val, name) => { each(this._global, (val, name) => {
@@ -584,35 +562,69 @@ export default class Logger extends Emitter {
self._openGroup($el.get(0).log) self._openGroup($el.get(0).log)
}) })
const renderViewport = debounce(() => this._renderViewport(), 15) this._$container.on('scroll', () => this.renderViewport(false))
this._$container.on('scroll', renderViewport)
} }
_renderViewport() { _renderViewport(force = true) {
const { scrollTop, offsetHeight } = this._container const container = this._container
const top = scrollTop if (container.offsetParent === null) return
const bottom = scrollTop + offsetHeight const { scrollTop, offsetHeight } = container
let top = scrollTop
let bottom = scrollTop + offsetHeight
if (!force) {
if (
this._topSpaceHeight < top &&
this._topSpaceHeight + this._el.offsetHeight > bottom
) {
return
}
}
const displayLogs = this._displayLogs const displayLogs = this._displayLogs
const tolerance = 1000
top -= tolerance
bottom += tolerance
let topSpaceHeight = 0 let topSpaceHeight = 0
let bottomSpaceHeight = 0 let bottomSpaceHeight = 0
let currentHeight = 0 let currentHeight = 0
this._$el.html('') this._$el.html('')
const frag = document.createDocumentFragment()
for (let i = 0, len = displayLogs.length; i < len; i++) { for (let i = 0, len = displayLogs.length; i < len; i++) {
const { el, height } = displayLogs[i] const log = displayLogs[i]
const { el } = log
let { height } = log
if (height === 0) {
this._updateLogHeight(log)
height = log.height
}
if (currentHeight > bottom) { if (currentHeight > bottom) {
bottomSpaceHeight += height bottomSpaceHeight += height
} else if (currentHeight + height > top) { } else if (currentHeight + height > top) {
this._el.appendChild(el) frag.appendChild(el)
} else if (currentHeight < top) { } else if (currentHeight < top) {
topSpaceHeight += height topSpaceHeight += height
} }
currentHeight += height currentHeight += height
} }
this._el.appendChild(frag)
this._updateTopSpace(topSpaceHeight) this._updateTopSpace(topSpaceHeight)
this._updateBottomSpace(bottomSpaceHeight) this._updateBottomSpace(bottomSpaceHeight)
container.scrollTop = scrollTop
const scrollHeight = container.scrollHeight
if (this._isAtBottom) {
container.scrollTop = scrollHeight - offsetHeight
this._isAtBottom = true
} else if (scrollHeight === offsetHeight) {
this._isAtBottom = true
} else if (container.scrollTop === scrollHeight - offsetHeight) {
this._isAtBottom = true
}
} }
} }