Skip to content

Commit

Permalink
refactor(ui): optimize init and update lifecycle events
Browse files Browse the repository at this point in the history
  • Loading branch information
qwqcode committed Jan 31, 2024
1 parent 0e0b53f commit 2b44452
Show file tree
Hide file tree
Showing 22 changed files with 75 additions and 61 deletions.
15 changes: 7 additions & 8 deletions docs/docs/develop/event.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,30 @@

|事件名|描述|
|-|-|
| `inited` | 初始化后 |
| `destroy` | 实例销毁时 |
| `created` | 初始化后 |
| `mounted` | 数据加载后 |
| `updated` | 数据更新后 |
| `unmounted` | 销毁后 |
| `list-fetch` | 评论列表请求时 |
| `list-fetched` | 评论列表请求后 |
| `list-load` | 评论装载前 |
| `list-loaded` | 评论装载后 |
| `list-error` | 评论加载错误时 |
| `list-failed` | 评论加载错误时 |
| `list-goto-first` | 评论列表归位时 |
| `list-reach-bottom` | 评论列表滚动到底部时 |
| `list-reach-bottom` | 评论列表滚动到底部时 |
| `comment-inserted` | 评论插入后 |
| `comment-updated` | 评论更新后 |
| `comment-deleted` | 评论删除后 |
| `comment-rendered` | 评论节点渲染后 |
| `unreads-updated` | 未读消息变更时 |
| `notifies-updated` | 未读消息变更时 |
| `list-goto` | 评论跳转时 |
| `page-loaded` | 页面数据更新后 |
| `editor-submit` | 编辑器提交时 |
| `editor-submitted` | 编辑器提交后 |
| `user-changed` | 本地用户数据变更时 |
| `conf-loaded` | 配置变更时 |
| `dark-mode-changed` | 深色模式变更时 |
| `sidebar-show` | 侧边栏显示 |
| `sidebar-hide` | 侧边栏隐藏 |


事件声明代码:[@ArtalkJS/Artalk - src/types/event.ts](https://github.com/ArtalkJS/Artalk/blob/master/ui/artalk/src/types/event.ts)

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/develop/plugs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import Artalk from 'artalk'

Artalk.use((ctx) => {
ctx.on('inited', () => {
ctx.on('mounted', () => {
ctx.get('editor').setContent("Hello World")
})

Expand Down
2 changes: 1 addition & 1 deletion ui/artalk-sidebar/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ onBeforeMount(() => {
throw new Error('Artalk instance not initialized')
}
artalk.on('conf-loaded', () => {
artalk.on('mounted', () => {
if (artalkLoaded.value) return
artalkLoaded.value = true
Expand Down
6 changes: 3 additions & 3 deletions ui/artalk/src/artalk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export default class Artalk {
if (typeof plugin === 'function') plugin(this.ctx)
})

// Trigger inited event
this.ctx.trigger('inited')
// Trigger created event
this.ctx.trigger('created')
}

/** Get the config of Artalk */
Expand All @@ -69,7 +69,7 @@ export default class Artalk {

/** Destroy instance of Artalk */
public destroy() {
this.ctx.trigger('destroy')
this.ctx.trigger('unmounted')
this.ctx.$root.remove()
}

Expand Down
26 changes: 8 additions & 18 deletions ui/artalk/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as I18n from './i18n'

import EventManager from './lib/event-manager'
import { convertApiOptions, handelCustomConf } from './config'
import { watchConf } from './lib/watch-conf'

// Auto dependency injection
interface Context extends TInjectedServices { }
Expand All @@ -26,6 +27,7 @@ class Context implements ContextApi {

/* Event Manager */
private events = new EventManager<EventPayloadMap>()
private mounted = false

constructor(conf: ArtalkConfig) {
this.conf = conf
Expand All @@ -35,6 +37,10 @@ class Context implements ContextApi {
this.$root.innerHTML = ''

this.data = new DataManager(this.events)

this.on('mounted', () => {
this.mounted = true
})
}

inject(depName: string, obj: any) {
Expand Down Expand Up @@ -144,7 +150,7 @@ class Context implements ContextApi {

updateConf(nConf: Partial<ArtalkConfig>): void {
this.conf = mergeDeep(this.conf, handelCustomConf(nConf, false))
this.events.trigger('conf-loaded', this.conf)
this.mounted && this.events.trigger('updated', this.conf)
}

getConf(): ArtalkConfig {
Expand All @@ -160,23 +166,7 @@ class Context implements ContextApi {
}

watchConf<T extends (keyof ArtalkConfig)[]>(keys: T, effect: (conf: Pick<ArtalkConfig, T[number]>) => void): void {
const deepEqual = (a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)
const val = () => {
const conf = this.getConf()
const res: any = {}
keys.forEach((key) => { res[key] = conf[key] })
return res
}
let lastVal = {}
this.on('conf-loaded', () => {
const newVal = val()
const isDiff = !deepEqual(lastVal, newVal)
// only trigger when specific keys changed
if (isDiff) {
lastVal = newVal
effect(newVal)
}
})
watchConf(this, keys, effect)
}
}

Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/layer/layer-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class LayerManager {
this.wrap = new LayerWrap()
document.body.appendChild(this.wrap.getWrap())

ctx.on('destroy', () => {
ctx.on('unmounted', () => {
this.wrap.getWrap().remove()
})

Expand Down
23 changes: 23 additions & 0 deletions ui/artalk/src/lib/watch-conf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { ArtalkConfig, ContextApi } from '@/types'

export function watchConf<T extends (keyof ArtalkConfig)[]>(ctx: ContextApi, keys: T, effect: (conf: Pick<ArtalkConfig, T[number]>) => void): void {
const deepEqual = (a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)
const val = () => {
const conf = ctx.getConf()
const res: any = {}
keys.forEach((key) => { res[key] = conf[key] })
return res
}
let lastVal: any = null
const handler = () => {
const newVal = val()
const isDiff = lastVal == null || !deepEqual(lastVal, newVal)
// only trigger when specific keys changed
if (isDiff) {
lastVal = newVal
effect(newVal)
}
}
ctx.on('mounted', handler)
ctx.on('updated', handler)
}
2 changes: 1 addition & 1 deletion ui/artalk/src/list/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const initListPaginatorFunc = (ctx: ContextApi) => {
})

// When list error
ctx.on('list-error', () => {
ctx.on('list-failed', () => {
paginator?.showErr?.($t('loadFail'))
})

Expand Down
9 changes: 6 additions & 3 deletions ui/artalk/src/plugins/conf-remoter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { handleConfFormServer } from '@/config'
import { showErrorDialog } from '../components/error-dialog'

export const ConfRemoter: ArtalkPlugin = (ctx) => {
ctx.on('inited', () => {
if (ctx.conf.immediateFetch === false) return
ctx.trigger('conf-fetch')
ctx.on('created', () => {
if (ctx.conf.immediateFetch !== false)
ctx.trigger('conf-fetch')
})

ctx.on('conf-fetch', () => {
Expand Down Expand Up @@ -52,6 +52,9 @@ function loadConf(ctx: ContextApi) {

console.error(err)
throw err
}).then(() => {
// Trigger mounted event
ctx.trigger('mounted')
}).then(() => {
// 评论获取
if (ctx.conf.remoteConfModifier) return // only auto fetch when no remoteConfModifier
Expand Down
4 changes: 2 additions & 2 deletions ui/artalk/src/plugins/dark-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ export const DarkMode: ArtalkPlugin = (ctx) => {
}

ctx.watchConf(['darkMode'], (conf) => sync(conf.darkMode))
ctx.on('inited', () => sync(ctx.conf.darkMode))
ctx.on('destroy', () => {
ctx.on('created', () => sync(ctx.conf.darkMode))
ctx.on('unmounted', () => {
// if handler exists, don't forget to remove it, or it will cause memory leak
darkModeAutoHandler && darkModeMedia?.removeEventListener('change', darkModeAutoHandler)
darkModeAutoHandler = undefined
Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/copyright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ArtalkPlugin } from '@/types'
import { version as ARTALK_VERSION } from '~/package.json'

export const Copyright: ArtalkPlugin = (ctx) => {
ctx.on('inited', () => {
ctx.on('mounted', () => {
const list = ctx.get('list')

const $copyright = list.$el.querySelector<HTMLElement>('.atk-copyright')
Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/error-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const ErrorDialog: ArtalkPlugin = (ctx) => {
Ui.setError(list.$el, null)
})

ctx.on('list-error', (err) => {
ctx.on('list-failed', (err) => {
showErrorDialog({
$err: ctx.get('list').$el,
errMsg: err.msg,
Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const Fetch: ArtalkPlugin = (ctx) => {
params.onError && params.onError(error)

// trigger events when error
ctx.trigger('list-error', error)
ctx.trigger('list-failed', error)
ctx.trigger('list-fetched', { params, error })

throw e
Expand Down
4 changes: 2 additions & 2 deletions ui/artalk/src/plugins/list/goto-first.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export const GotoFirst: ArtalkPlugin = (ctx) => {
})
}

ctx.on('inited', () => {
ctx.on('mounted', () => {
ctx.on('list-goto-first', handler)
})

ctx.on('destroy', () => {
ctx.on('unmounted', () => {
ctx.off('list-goto-first', handler)
})
}
4 changes: 2 additions & 2 deletions ui/artalk/src/plugins/list/goto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ export const Goto: ArtalkPlugin = (ctx) => {
delayGoto = false
check()
}
ctx.on('inited', () => {
ctx.on('mounted', () => {
window.addEventListener('hashchange', hashChangeHandler)
ctx.on('list-loaded', check)
})
ctx.on('destroy', () => {
ctx.on('unmounted', () => {
window.removeEventListener('hashchange', hashChangeHandler)
ctx.off('list-loaded', check)
})
Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/reach-bottom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const ReachBottom: ArtalkPlugin = (ctx) => {
setupObserver($target)
})

ctx.on('destroy', () => {
ctx.on('unmounted', () => {
clearObserver()
})
}
4 changes: 2 additions & 2 deletions ui/artalk/src/plugins/list/time-ticking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as Utils from '@/lib/utils'
export const TimeTicking: ArtalkPlugin = (ctx) => {
let timer: number|null = null

ctx.on('inited', () => {
ctx.on('mounted', () => {
timer = window.setInterval(() => {
const list = ctx.get('list')

Expand All @@ -16,7 +16,7 @@ export const TimeTicking: ArtalkPlugin = (ctx) => {
}, 30 * 1000) // 30s 更新一次
})

ctx.on('destroy', () => {
ctx.on('unmounted', () => {
timer && window.clearInterval(timer)
})
}
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/list/unread-badge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const UnreadBadge: ArtalkPlugin = (ctx) => {
}
}

ctx.on('inited', () => {
ctx.on('mounted', () => {
const list = ctx.get('list')

$unreadBadge = list.$el.querySelector<HTMLElement>('.atk-unread-badge')
Expand Down
4 changes: 2 additions & 2 deletions ui/artalk/src/plugins/list/with-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import $t from '@/i18n'
export const WithEditor: ArtalkPlugin = (ctx) => {
let $closeCommentBtn: HTMLElement|undefined

// on Artalk inited
// on Artalk mounted
// (after all components had mounted)
ctx.on('inited', () => {
ctx.on('mounted', () => {
const list = ctx.get('list')

$closeCommentBtn = list.$el.querySelector<HTMLElement>('[data-action="admin-close-comment"]')!
Expand Down
2 changes: 1 addition & 1 deletion ui/artalk/src/plugins/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as marked from '@/lib/marked'
export const Markdown: ArtalkPlugin = (ctx) => {
marked.initMarked()

ctx.on('conf-loaded', (conf) => {
ctx.on('updated', (conf) => {
if (conf.markedReplacers) marked.setReplacers(conf.markedReplacers)
})
}
15 changes: 7 additions & 8 deletions ui/artalk/src/types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ interface ListFetchedArgs { params: Partial<ListFetchParams>, data?: ListData, e

/** EventName to EventPayload Type */
export interface EventPayloadMap {
'inited': undefined // Artalk 初始化后
'destroy': undefined // Artalk 销毁时
// Basic lifecycle
'created': undefined
'mounted': undefined
'updated': ArtalkConfig
'unmounted': undefined

'conf-fetch': undefined // 配置请求时
'list-fetch': Partial<ListFetchParams> // 评论列表请求时
'list-fetched': ListFetchedArgs // 评论列表请求后
'list-load': CommentData[] // 评论装载前

// TODO merge 'list-loaded' and 'list-error', for purpose of `once` 'list-loaded' and simplify
// example need bind 'list-loaded' and 'list-error' to same handler
// consider name it to 'list-fetched': { data: CommentData[], error?: { msg: string, data?: any } }
// and remove `list-load`
'list-loaded': CommentData[] // 评论装载后
'list-error': ErrorData // 评论加载错误时
'list-failed': ErrorData // 评论加载错误时

'list-goto-first': undefined // 评论列表归位时
'list-reach-bottom': undefined // 评论列表滚动到底部时
Expand All @@ -34,7 +34,6 @@ export interface EventPayloadMap {
'editor-submit': undefined // 编辑器提交时
'editor-submitted': undefined // 编辑器提交后
'user-changed': LocalUser // 本地用户数据变更时
'conf-loaded': ArtalkConfig // Artalk 配置变更时
'sidebar-show': undefined // 侧边栏显示
'sidebar-hide': undefined // 侧边栏隐藏
}
2 changes: 1 addition & 1 deletion ui/artalk/tests/ui-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('Artalk instance', () => {
const fn = vi.fn()

await new Promise(resolve => {
artalk.on('conf-loaded', (conf) => {
artalk.on('mounted', (conf) => {
resolve(null)
fn()
})
Expand Down

0 comments on commit 2b44452

Please sign in to comment.