perf: lazy-load computations (#1538)

* perf: lazy-load computations (experimental)

* fix lint

* add marks

* fixup

* lazy-load mixins too

* add missing files

* fix tests
This commit is contained in:
Nolan Lawson 2019-09-26 05:23:36 -07:00 committed by GitHub
parent 8fbf38e974
commit 038dc27163
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 97 additions and 57 deletions

View file

@ -144,7 +144,7 @@
(name === 'notifications' && $hasNotifications) || (name === 'community' && $hasFollowRequests)
),
badgeNumber: ({ name, $numberOfNotifications, $numberOfFollowRequests }) => (
(name === 'notifications' && $numberOfNotifications) || (name === 'community' && $numberOfFollowRequests)
(name === 'notifications' && $numberOfNotifications) || (name === 'community' && $numberOfFollowRequests) || 0
)
},
methods: {

View file

@ -2,11 +2,14 @@
<svelte:component this={composeBox} {realm} {hidden} />
{/if}
<script>
import { importComposeBox } from '../../_utils/asyncModules'
import { importComposeBox, importLoggedInStoreExtensions } from '../../_utils/asyncModules'
export default {
async oncreate () {
const composeBox = await importComposeBox()
const [composeBox] = await Promise.all([
importComposeBox(),
importLoggedInStoreExtensions()
])
this.set({ composeBox })
},
data: () => ({

View file

@ -10,18 +10,19 @@
</style>
<script>
import { store } from '../../_store/store'
import { importTimeline } from '../../_utils/asyncModules'
import { importLoggedInStoreExtensions, importTimeline } from '../../_utils/asyncModules'
export default {
async oncreate () {
console.log('LazyTimeline oncreate')
const { currentInstance } = this.store.get()
const { timeline } = this.get()
const [timelineComponent] = await Promise.all([
importTimeline(),
importLoggedInStoreExtensions()
])
this.store.set({ currentTimeline: timeline })
this.store.setForTimeline(currentInstance, timeline, { runningUpdate: false })
console.log('importing timeline')
const timelineComponent = await importTimeline()
console.log('imported timeline')
this.set({ timelineComponent })
},
store: () => store,

View file

@ -1,4 +1,5 @@
import { get } from '../../_utils/lodash-lite'
import { mark, stop } from '../../_utils/marks'
const MIN_PREFIX_LENGTH = 2
// Technically mastodon accounts allow dots, but it would be weird to do an autosuggest search if it ends with a dot.
@ -17,6 +18,7 @@ function computeForAutosuggest (store, key, defaultValue) {
}
export function autosuggestComputations (store) {
mark('autosuggestComputations')
computeForAutosuggest(store, 'composeFocused', false)
computeForAutosuggest(store, 'composeSelectionStart', 0)
computeForAutosuggest(store, 'autosuggestSelected', 0)
@ -59,4 +61,5 @@ export function autosuggestComputations (store) {
!!(composeFocused && autosuggestSearchText && autosuggestNumSearchResults)
)
)
stop('autosuggestComputations')
}

View file

@ -1,11 +1,7 @@
import { instanceComputations } from './instanceComputations'
import { timelineComputations } from './timelineComputations'
import { navComputations } from './navComputations'
import { autosuggestComputations } from './autosuggestComputations'
export function computations (store) {
instanceComputations(store)
timelineComputations(store)
navComputations(store)
autosuggestComputations(store)
}

View file

@ -1,4 +1,5 @@
import { DEFAULT_THEME } from '../../_utils/themeEngine'
import { mark, stop } from '../../_utils/marks'
function computeForInstance (store, computedKey, key, defaultValue) {
store.compute(computedKey,
@ -7,6 +8,7 @@ function computeForInstance (store, computedKey, key, defaultValue) {
}
export function instanceComputations (store) {
mark('instanceComputations')
computeForInstance(store, 'currentTheme', 'instanceThemes', DEFAULT_THEME)
computeForInstance(store, 'currentVerifyCredentials', 'verifyCredentials', null)
computeForInstance(store, 'currentInstanceInfo', 'instanceInfos', null)
@ -59,4 +61,6 @@ export function instanceComputations (store) {
(currentInstanceInfo && currentInstanceInfo.max_toot_chars) || 500
)
)
stop('instanceComputations')
}

View file

@ -0,0 +1,10 @@
// like loggedInObservers.js, these can be lazy-loaded once the user is actually logged in
import { timelineComputations } from './timelineComputations'
import { autosuggestComputations } from './autosuggestComputations'
import { store } from '../store'
export function loggedInComputations () {
timelineComputations(store)
autosuggestComputations(store)
}

View file

@ -1,4 +1,8 @@
import { mark, stop } from '../../_utils/marks'
export function navComputations (store) {
mark('navComputations')
store.compute(
'pinnedListTitle',
['lists', 'pinnedPage'],
@ -89,4 +93,6 @@ export function navComputations (store) {
]
}
)
stop('navComputations')
}

View file

@ -9,6 +9,7 @@ import {
NOTIFICATION_POLLS,
NOTIFICATION_MENTIONS
} from '../../_static/instanceSettings'
import { mark, stop } from '../../_utils/marks'
function computeForTimeline (store, key, defaultValue) {
store.compute(key,
@ -45,6 +46,7 @@ function computeNotificationFilter (store, computationName, key) {
}
export function timelineComputations (store) {
mark('timelineComputations')
computeForTimeline(store, 'timelineItemSummaries', null)
computeForTimeline(store, 'timelineItemSummariesToAdd', null)
computeForTimeline(store, 'runningUpdate', false)
@ -191,4 +193,5 @@ export function timelineComputations (store) {
['numberOfFollowRequests'],
(numberOfFollowRequests) => !!numberOfFollowRequests
)
stop('timelineComputations')
}

View file

@ -0,0 +1,8 @@
import { loggedInComputations } from './computations/loggedInComputations'
import { loggedInObservers } from './observers/loggedInObservers'
import { loggedInMixins } from './mixins/loggedInMixins'
console.log('imported logged in observers and computations')
loggedInMixins()
loggedInComputations()
loggedInObservers()

View file

@ -0,0 +1,27 @@
import { get } from '../../_utils/lodash-lite'
export function composeMixins (Store) {
Store.prototype.setComposeData = function (realm, obj) {
const { composeData, currentInstance } = this.get()
const instanceNameData = composeData[currentInstance] = composeData[currentInstance] || {}
instanceNameData[realm] = Object.assign(
instanceNameData[realm] || {},
{ ts: Date.now() },
obj
)
this.set({ composeData })
}
Store.prototype.getComposeData = function (realm, key) {
const { composeData, currentInstance } = this.get()
return get(composeData, [currentInstance, realm, key])
}
Store.prototype.clearComposeData = function (realm) {
const { composeData, currentInstance } = this.get()
if (composeData && composeData[currentInstance]) {
delete composeData[currentInstance][realm]
}
this.set({ composeData })
}
}

View file

@ -1,32 +1,6 @@
import { get } from '../../_utils/lodash-lite'
export function instanceMixins (Store) {
Store.prototype.setComposeData = function (realm, obj) {
const { composeData, currentInstance } = this.get()
const instanceNameData = composeData[currentInstance] = composeData[currentInstance] || {}
instanceNameData[realm] = Object.assign(
instanceNameData[realm] || {},
{ ts: Date.now() },
obj
)
this.set({ composeData })
}
Store.prototype.getComposeData = function (realm, key) {
const { composeData, currentInstance } = this.get()
return composeData[currentInstance] &&
composeData[currentInstance][realm] &&
composeData[currentInstance][realm][key]
}
Store.prototype.clearComposeData = function (realm) {
const { composeData, currentInstance } = this.get()
if (composeData && composeData[currentInstance]) {
delete composeData[currentInstance][realm]
}
this.set({ composeData })
}
Store.prototype.getInstanceSetting = function (instanceName, settingName, defaultValue) {
const { instanceSettings } = this.get()
return get(instanceSettings, [instanceName, settingName], defaultValue)

View file

@ -0,0 +1,12 @@
import { timelineMixins } from './timelineMixins'
import { statusMixins } from './statusMixins'
import { autosuggestMixins } from './autosuggestMixins'
import { composeMixins } from './composeMixins'
import { PinaforeStore as Store } from '../store'
export function loggedInMixins () {
composeMixins(Store)
timelineMixins(Store)
statusMixins(Store)
autosuggestMixins(Store)
}

View file

@ -1,11 +1,5 @@
import { timelineMixins } from './timelineMixins'
import { instanceMixins } from './instanceMixins'
import { statusMixins } from './statusMixins'
import { autosuggestMixins } from './autosuggestMixins'
export function mixins (Store) {
instanceMixins(Store)
timelineMixins(Store)
statusMixins(Store)
autosuggestMixins(Store)
}

View file

@ -8,7 +8,7 @@ import { cleanup } from './cleanup'
// These observers can be lazy-loaded when the user is actually logged in.
// Prevents circular dependencies and reduces the size of main.js
export default function loggedInObservers () {
export function loggedInObservers () {
instanceObservers()
timelineObservers()
notificationObservers()

View file

@ -1,15 +1,12 @@
import { importLoggedInObservers } from '../../_utils/asyncModules'
let observedOnce = false
import { importLoggedInStoreExtensions } from '../../_utils/asyncModules'
// An observer that calls an observer... this is a bit weird, but it eliminates
// circular dependencies and also allows us to lazy load observers that are
// only needed when you're logged in.
// circular dependencies and also allows us to lazy load observers/computations
// that are only needed when you're logged in.
export function setupLoggedInObservers (store) {
store.observe('isUserLoggedIn', isUserLoggedIn => {
if (isUserLoggedIn && !observedOnce) {
importLoggedInObservers().then(loggedInObservers => loggedInObservers())
observedOnce = true
if (isUserLoggedIn) {
importLoggedInStoreExtensions()
}
})
}

View file

@ -67,7 +67,7 @@ const nonPersistedState = {
const state = Object.assign({}, persistedState, nonPersistedState)
const keysToStoreInLocalStorage = new Set(Object.keys(persistedState))
class PinaforeStore extends LocalStorageStore {
export class PinaforeStore extends LocalStorageStore {
constructor (state) {
super(state, keysToStoreInLocalStorage)
}

View file

@ -24,9 +24,9 @@ export const importDatabase = () => import(
/* webpackChunkName: 'database.js' */ '../_database/databaseApis.js'
)
export const importLoggedInObservers = () => import(
/* webpackChunkName: 'loggedInObservers.js' */ '../_store/observers/loggedInObservers.js'
).then(getDefault)
export const importLoggedInStoreExtensions = () => import(
/* webpackChunkName: 'loggedInStoreExtensions.js' */ '../_store/loggedInStoreExtensions.js'
)
export const importNavShortcuts = () => import(
/* webpackChunkName: 'NavShortcuts' */ '../_components/NavShortcuts.html'

View file

@ -3,12 +3,13 @@
import { store } from './_store/store'
import { goto } from '../../__sapper__/client'
import { decodeURIComponentWithPluses } from './_utils/decodeURIComponentWithPluses'
import { importLoggedInStoreExtensions } from './_utils/asyncModules'
const SHARE_KEYS = ['title', 'text', 'url']
export default {
store: () => store,
oncreate () {
async oncreate () {
const params = new URLSearchParams(location.search)
const text = SHARE_KEYS
@ -16,6 +17,7 @@
.filter(Boolean)
.join(' ')
await importLoggedInStoreExtensions()
this.store.set({ openShareDialog: true })
this.store.clearComposeData('dialog')
this.store.setComposeData('dialog', { text })