perf: 90ms improvement: Defer more code in renderer, load state earlier

By deferring more code in the renderer and loading state earlier, we
improve startup time by another 90ms!

Before: 507 unique requires (1270-1280ms)
After: 506 unique requires  (1180-1190ms)
This commit is contained in:
Feross Aboukhadijeh
2016-09-30 22:00:58 -07:00
parent 02f5dbb63f
commit 5ff2d893b9
4 changed files with 49 additions and 28 deletions

View File

@@ -45,18 +45,26 @@ function init (state, options) {
y: initialBounds.y
})
win.once('ready-to-show', function () {
win.loadURL(config.WINDOW_MAIN)
win.once('ready-to-show', () => {
if (!options.hidden) win.show()
})
win.loadURL(config.WINDOW_MAIN)
if (win.setSheetOffset) win.setSheetOffset(config.UI_HEADER_HEIGHT)
if (win.setSheetOffset) {
win.setSheetOffset(config.UI_HEADER_HEIGHT)
}
win.webContents.on('dom-ready', function () {
menu.onToggleFullScreen(main.win.isFullScreen())
})
win.webContents.on('will-navigate', (e, url) => {
// Prevent drag-and-drop from navigating the Electron window, which can happen
// before our drag-and-drop handlers have been initialized.
e.preventDefault()
})
win.on('blur', onWindowBlur)
win.on('focus', onWindowFocus)

View File

@@ -6,6 +6,8 @@ const config = require('../../config')
const SAVE_DEBOUNCE_INTERVAL = 1000
appConfig.filePath = path.join(config.CONFIG_PATH, 'config.json')
const State = module.exports = Object.assign(new EventEmitter(), {
getDefaultPlayState,
load,
@@ -16,13 +18,11 @@ const State = module.exports = Object.assign(new EventEmitter(), {
// After first State.save() invokation, future calls go straight to the
// debounced function
State.save = debounce(saveImmediate, SAVE_DEBOUNCE_INTERVAL)
State.save()
State.save(...arguments)
},
saveImmediate
})
appConfig.filePath = path.join(config.CONFIG_PATH, 'config.json')
function getDefaultState () {
const LocationHistory = require('location-history')

View File

@@ -2,6 +2,7 @@
// Reports back so that we can improve WebTorrent Desktop
module.exports = {
init,
send,
logUncaughtError,
logPlayAttempt
}
@@ -22,7 +23,9 @@ function init (state) {
telemetry = state.saved.telemetry = createSummary()
reset()
}
}
function send (state) {
const now = new Date()
telemetry.version = config.APP_VERSION
telemetry.timestamp = now.toISOString()
@@ -223,7 +226,7 @@ function getElemString (elem) {
let ret = elem.tagName
try {
ret += '.' + Array.from(elem.classList).join('.')
} catch (e) {}
} catch (err) {}
return ret
}

View File

@@ -3,6 +3,12 @@ console.time('init')
const crashReporter = require('../crash-reporter')
crashReporter.init()
// Perf optimization: Start asynchronously read on config file before all the
// blocking require() calls below.
const State = require('./lib/state')
State.load(onState)
const dragDrop = require('drag-drop')
const electron = require('electron')
const fs = require('fs')
@@ -12,7 +18,6 @@ const ReactDOM = require('react-dom')
const config = require('../config')
const telemetry = require('./lib/telemetry')
const sound = require('./lib/sound')
const State = require('./lib/state')
const TorrentPlayer = require('./lib/torrent-player')
// Required by Material UI -- adds `onTouchTap` event
@@ -50,22 +55,22 @@ let state
// Root React component
let app
State.load(onState)
// Called once when the application loads. (Not once per window.)
// Connects to the torrent networks, sets up the UI and OS integrations like
// the dock icon and drag+drop.
function onState (err, _state) {
if (err) return onError(err)
state = window.state = _state // Make available for easier debugging
// Make available for easier debugging
state = window.state = _state
window.dispatch = dispatch
telemetry.init(state)
// Log uncaught JS errors
window.addEventListener('error',
(e) => telemetry.logUncaughtError('window', e),
true /* capture */)
window.addEventListener(
'error', (e) => telemetry.logUncaughtError('window', e), true /* capture */
)
// Create controllers
controllers = {
@@ -90,23 +95,18 @@ function onState (err, _state) {
// Restart everything we were torrenting last time the app ran
resumeTorrents()
// Initialize ReactDOM
app = ReactDOM.render(<App state={state} />, document.querySelector('#body'))
// Calling update() updates the UI given the current state
// Do this at least once a second to give every file in every torrentSummary
// a progress bar and to keep the cursor in sync when playing a video
setInterval(update, 1000)
app = ReactDOM.render(<App state={state} />, document.querySelector('#body'))
// Lazy-load other stuff, like the AppleTV module, later to keep startup fast
window.setTimeout(delayedInit, config.DELAYED_INIT)
// Listen for messages from the main process
setupIpc()
// Warn if the download dir is gone, eg b/c an external drive is unplugged
checkDownloadPath()
// OS integrations:
// ...drag and drop files/text to start torrenting or seeding
// Drag and drop files/text to start torrenting or seeding
dragDrop('body', {
onDrop: onOpen,
onDropText: onOpen
@@ -119,18 +119,28 @@ function onState (err, _state) {
window.addEventListener('focus', onFocus)
window.addEventListener('blur', onBlur)
// ...window visibility state.
document.addEventListener('webkitvisibilitychange', onVisibilityChange)
// Done! Ideally we want to get here < 500ms after the user clicks the app
if (electron.remote.getCurrentWindow().isVisible()) {
sound.play('STARTUP')
}
// To keep app startup fast, some code is delayed.
window.setTimeout(delayedInit, config.DELAYED_INIT)
// Done! Ideally we want to get here < 500ms after the user clicks the app
console.timeEnd('init')
}
// Runs a few seconds after the app loads, to avoid slowing down startup time
function delayedInit () {
telemetry.send(state)
// Warn if the download dir is gone, eg b/c an external drive is unplugged
checkDownloadPath()
// ...window visibility state.
document.addEventListener('webkitvisibilitychange', onVisibilityChange)
onVisibilityChange()
lazyLoadCast()
}