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() {
super.show()
this._logger.restoreScroll()
this._logger.renderViewport()
}
overrideConsole() {
const origConsole = (this._origConsole = {})

View File

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

View File

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