Skip to content

Commit 9dba970

Browse files
committed
refactor(src): 重构插件系统并添加 vue 插件兼容
1 parent 5bfe0e1 commit 9dba970

File tree

11 files changed

+264
-92
lines changed

11 files changed

+264
-92
lines changed

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
"bugs": {
1515
"url": "https://github.com/vue-spark/vue-router-plugin-system/issues"
1616
},
17-
"keywords": ["vue", "vue-router", "plugin"],
17+
"keywords": [
18+
"vue",
19+
"vue-router",
20+
"plugin"
21+
],
1822
"exports": {
1923
".": "./dist/index.js",
2024
"./package.json": "./package.json"
@@ -38,7 +42,8 @@
3842
"lint": "eslint . --cache --cache-location ./node_modules/.cache/.eslint-cache",
3943
"lint:fix": "pnpm run lint --fix",
4044
"release": "bumpp && pnpm publish",
41-
"prepublishOnly": "pnpm run build"
45+
"prepublishOnly": "pnpm run build",
46+
"prepare": "simple-git-hooks"
4247
},
4348
"peerDependencies": {
4449
"vue": "^3.0.0",

src/create-router.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { RouterOptions as _RouterOptions, Router } from 'vue-router'
2+
import type { RouterPlugin } from './plugin'
3+
import { createRouter as _createRouter } from 'vue-router'
4+
import { RUN_WITH_APP_HANDLERS_KEY } from './meta-keys'
5+
import { prepareInstall } from './prepare-install'
6+
import { setupPlugin } from './setup-plugin'
7+
8+
export interface RouterOptions extends _RouterOptions {
9+
/**
10+
* Plugins to be installed on the router.
11+
*/
12+
plugins?: RouterPlugin[]
13+
}
14+
15+
/**
16+
* Equivalent to {@link createRouter} from `vue-router`, but with support for plugins.
17+
* @param options {@link RouterOptions}
18+
*
19+
* @example
20+
* ```ts
21+
* const SomePlugin: RouterPlugin = (ctx) => {
22+
* // plugin implementation
23+
* }
24+
*
25+
* createRouter({
26+
* history: createWebHistory(),
27+
* routes: [],
28+
* plugins: [SomePlugin()],
29+
* })
30+
* ```
31+
*/
32+
export function createRouter(options: RouterOptions): Router {
33+
const router = _createRouter(options)
34+
35+
// 重写 install 方法
36+
const { install } = router
37+
router.install = (...args) => {
38+
const [app] = args
39+
prepareInstall({ app, router })
40+
install.apply(router, args)
41+
42+
// 运行缓存的 runWithApp 函数
43+
const runWithAppHandlers = (router[RUN_WITH_APP_HANDLERS_KEY] ??= [])
44+
runWithAppHandlers.forEach(handler => handler(app))
45+
runWithAppHandlers.length = 0
46+
}
47+
48+
// 安装插件
49+
const { plugins = [] } = options
50+
plugins.forEach(plugin => setupPlugin({ router, plugin }))
51+
52+
return router
53+
}

src/create-vue-router-plugin.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { App } from 'vue'
2+
import type { RouterPlugin } from './plugin'
3+
import { prepareInstall } from './prepare-install'
4+
import { setupPlugin } from './setup-plugin'
5+
6+
export interface VueRouterPlugin extends RouterPlugin {
7+
/**
8+
* Install the plugin.
9+
* @param app vue app
10+
*/
11+
install: (app: App) => void
12+
}
13+
14+
/**
15+
* Create a {@link VueRouterPlugin} from a {@link RouterPlugin}.
16+
* @param plugin {@link RouterPlugin}
17+
* @returns Returns a {@link VueRouterPlugin}
18+
* @example
19+
* ```ts
20+
* const SomePlugin = createVueRouterPlugin((ctx) => {
21+
* // plugin implementation
22+
* })
23+
*
24+
* // must be installed after the router
25+
* app.use(router).use(SomePlugin)
26+
* ```
27+
*/
28+
export function createVueRouterPlugin(plugin: RouterPlugin): VueRouterPlugin {
29+
return Object.assign(plugin, {
30+
install(app: App) {
31+
const router = app.config.globalProperties.$router
32+
if (!router) {
33+
throw new Error(
34+
'[vue-router-plugin-system] Please install vue-router first.',
35+
)
36+
}
37+
38+
prepareInstall({ app, router })
39+
setupPlugin({ router, plugin })
40+
},
41+
})
42+
}

src/index.ts

Lines changed: 3 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,3 @@
1-
import type { App } from 'vue'
2-
import type { RouterOptions as _RouterOptions, Router } from 'vue-router'
3-
import type { RouterPlugin, RouterPluginContext } from './plugin'
4-
import { effectScope } from 'vue'
5-
import { createRouter as _createRouter } from 'vue-router'
6-
import { APP_KEY } from './meta-keys'
7-
8-
export interface RouterOptions extends _RouterOptions {
9-
/**
10-
* Plugins to be installed on the router.
11-
*/
12-
plugins?: RouterPlugin[]
13-
}
14-
15-
/**
16-
* Equivalent to {@link createRouter} from `vue-router`, but with support for plugins.
17-
* @param options {@link RouterOptions}
18-
*
19-
* @example
20-
* ```ts
21-
* createRouter({
22-
* history: createWebHistory(),
23-
* routes: [],
24-
* plugins: [SomePlugin()],
25-
* })
26-
* ```
27-
*/
28-
export function createRouter(options: RouterOptions): Router {
29-
const router = _createRouter(options)
30-
const { plugins = [] } = options
31-
32-
const effect = effectScope(true)
33-
const runWithAppFns: ((app: App) => void)[] = []
34-
const uninstallFns: (() => void)[] = []
35-
36-
const ctx: RouterPluginContext = {
37-
router,
38-
39-
runWithApp(fn) {
40-
// 将 fn 包装到 effect 中
41-
const rawFn = fn
42-
fn = function (this: any, ...args) {
43-
return effect.run(() => rawFn.apply(this, args))
44-
}
45-
46-
if (router[APP_KEY]) {
47-
fn(router[APP_KEY])
48-
} else {
49-
runWithAppFns.push(fn)
50-
}
51-
},
52-
53-
onUninstall(fn) {
54-
uninstallFns.push(fn)
55-
},
56-
}
57-
58-
// 在 effect 中执行插件
59-
effect.run(() => {
60-
plugins.forEach((plugin) => plugin(ctx))
61-
})
62-
63-
// 重写 install 方法
64-
const originalInstall = router.install
65-
router.install = (app: App) => {
66-
originalInstall(app)
67-
68-
router[APP_KEY] = app
69-
runWithAppFns.forEach((fn) => fn(app))
70-
runWithAppFns.length = 0
71-
72-
// TODO: use https://github.com/vuejs/core/pull/8801 if merged
73-
const { unmount } = app
74-
app.unmount = () => {
75-
effect.stop()
76-
delete router[APP_KEY]
77-
uninstallFns.forEach((fn) => fn())
78-
uninstallFns.length = 0
79-
unmount.call(app)
80-
}
81-
}
82-
83-
return router
84-
}
1+
export * from './create-router'
2+
export * from './create-vue-router-plugin'
3+
export * from './plugin'

src/meta-keys.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
1-
import type { App } from 'vue'
1+
import type { App, EffectScope } from 'vue'
2+
import type {
3+
RouterPluginRunWithAppHandler,
4+
RouterPluginUninstallHandler,
5+
} from './plugin'
26

37
export const APP_KEY = Symbol('vue app')
8+
export const EFFECT_SCOPE_KEY = Symbol('vue effect scope')
9+
export const UNINSTALL_HANDLERS_KEY = Symbol('router plugin uninstall handlers')
10+
export const RUN_WITH_APP_HANDLERS_KEY = Symbol(
11+
'router plugin run with app handlers',
12+
)
13+
export const PREPARED_FLAG_KEY = Symbol('router plugin system already prepared')
414

515
declare module 'vue-router' {
616
export interface Router {
717
/**
818
* The vue app instance.
919
*/
1020
[APP_KEY]?: App
21+
/**
22+
* The vue effect scope.
23+
*/
24+
[EFFECT_SCOPE_KEY]?: EffectScope
25+
/**
26+
* The router plugin uninstall functions.
27+
*/
28+
[UNINSTALL_HANDLERS_KEY]?: RouterPluginUninstallHandler[]
29+
/**
30+
* The router plugin run with app functions.
31+
*/
32+
[RUN_WITH_APP_HANDLERS_KEY]?: RouterPluginRunWithAppHandler[]
33+
/**
34+
* The flag to indicate whether the router is prepared.
35+
*/
36+
[PREPARED_FLAG_KEY]?: boolean
1137
}
1238
}

src/plugin.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type { App } from 'vue'
22
import type { Router } from 'vue-router'
33

4+
export type RouterPluginUninstallHandler = () => void
5+
export type RouterPluginRunWithAppHandler = (app: App) => void
6+
47
export interface RouterPluginContext {
58
/**
69
* The router instance.
@@ -9,11 +12,11 @@ export interface RouterPluginContext {
912
/**
1013
* Runs a function with the vue app.
1114
*/
12-
runWithApp: (fn: (app: App) => void) => void
15+
runWithApp: (handler: RouterPluginRunWithAppHandler) => void
1316
/**
1417
* Register a function to be called when the plugin is uninstalled.
1518
*/
16-
onUninstall: (fn: () => void) => void
19+
onUninstall: (handler: RouterPluginUninstallHandler) => void
1720
}
1821

1922
export interface RouterPlugin {

src/prepare-install.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { App } from 'vue'
2+
import type { Router } from 'vue-router'
3+
import {
4+
APP_KEY,
5+
EFFECT_SCOPE_KEY,
6+
PREPARED_FLAG_KEY,
7+
RUN_WITH_APP_HANDLERS_KEY,
8+
UNINSTALL_HANDLERS_KEY,
9+
} from './meta-keys'
10+
11+
/**
12+
* 在正式安装到 vue app 前运行此函数,以准备安装
13+
*/
14+
export function prepareInstall({
15+
app,
16+
router,
17+
}: {
18+
app: App
19+
router: Router
20+
}): void {
21+
if (router[PREPARED_FLAG_KEY]) {
22+
return
23+
}
24+
25+
// 避免重复执行后续操作
26+
router[PREPARED_FLAG_KEY] = true
27+
28+
// 用于 setupPlugin 内访问 vue app
29+
router[APP_KEY] = app
30+
31+
// 注册卸载函数
32+
// TODO: use https://github.com/vuejs/core/pull/8801 if merged
33+
const { unmount } = app
34+
app.unmount = () => {
35+
router[EFFECT_SCOPE_KEY]?.stop()
36+
router[UNINSTALL_HANDLERS_KEY]?.forEach(handler => handler())
37+
38+
delete router[PREPARED_FLAG_KEY]
39+
delete router[APP_KEY]
40+
delete router[EFFECT_SCOPE_KEY]
41+
delete router[UNINSTALL_HANDLERS_KEY]
42+
delete router[RUN_WITH_APP_HANDLERS_KEY]
43+
44+
return unmount.call(app)
45+
}
46+
}

src/setup-plugin.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { Router } from 'vue-router'
2+
import type { RouterPlugin } from './plugin'
3+
import { effectScope } from 'vue'
4+
import {
5+
APP_KEY,
6+
EFFECT_SCOPE_KEY,
7+
RUN_WITH_APP_HANDLERS_KEY,
8+
UNINSTALL_HANDLERS_KEY,
9+
} from './meta-keys'
10+
11+
/**
12+
* 安装单一插件
13+
*/
14+
export function setupPlugin({
15+
router,
16+
plugin,
17+
}: {
18+
router: Router
19+
plugin: RouterPlugin
20+
}): void {
21+
const scope = (router[EFFECT_SCOPE_KEY] ??= effectScope(true))
22+
// 在 scope 中运行插件
23+
scope.run(() => {
24+
plugin({
25+
router,
26+
onUninstall(handler) {
27+
const handlers = (router[UNINSTALL_HANDLERS_KEY] ??= [])
28+
handlers.push(handler)
29+
},
30+
runWithApp(handler) {
31+
// 在 scope 中运行 handler
32+
const rawHandler = handler
33+
handler = (...args) => {
34+
return scope.run(() => rawHandler(...args))
35+
}
36+
37+
if (router[APP_KEY]) {
38+
handler(router[APP_KEY])
39+
}
40+
else {
41+
const handlers = (router[RUN_WITH_APP_HANDLERS_KEY] ??= [])
42+
handlers.push(handler)
43+
}
44+
},
45+
})
46+
})
47+
}

src/types/global.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
declare global {
2+
const __DEV__: boolean
3+
}
4+
5+
export {}

0 commit comments

Comments
 (0)