作用域与封装 #
一、作用域概念 #
1.1 什么是作用域 #
Fastify的作用域是指插件注册内容的可见范围。每个通过 register 注册的插件都会创建一个新的作用域。
1.2 作用域类型 #
text
┌─────────────────────────────────────────────┐
│ Root Scope (根作用域) │
│ ┌───────────────────────────────────────┐ │
│ │ Plugin A Scope (插件A作用域) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ Plugin B Scope (插件B作用域) │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └───────────────────────────────────────┘ │
│ ┌───────────────────────────────────────┐ │
│ │ Plugin C Scope (插件C作用域) │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
1.3 封装规则 #
| 规则 | 说明 |
|---|---|
| 内部可见 | 子作用域可以访问父作用域的内容 |
| 外部不可见 | 父作用域无法访问子作用域的内容 |
| 兄弟隔离 | 同级作用域之间相互隔离 |
二、默认封装行为 #
2.1 基本封装 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.decorate('internal', 'plugin value')
console.log('Inside plugin:', fastify.internal)
})
fastify.ready(() => {
console.log('Outside plugin:', fastify.internal)
})
输出:
text
Inside plugin: plugin value
Outside plugin: undefined
2.2 路由封装 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.get('/internal', async (request, reply) => {
return { message: 'Internal route' }
})
})
fastify.ready(() => {
console.log(fastify.printRoutes())
})
2.3 钩子封装 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.addHook('onRequest', async (request, reply) => {
console.log('Plugin hook')
})
fastify.get('/plugin-route', async (request, reply) => {
return { message: 'Plugin route' }
})
})
fastify.get('/root-route', async (request, reply) => {
return { message: 'Root route' }
})
/plugin-route 会触发钩子,/root-route 不会。
三、破坏封装 #
3.1 使用fastify-plugin #
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
fastify.decorate('shared', 'global value')
}
module.exports = fp(myPlugin)
现在 shared 装饰器在整个应用中都可用:
javascript
const fastify = require('fastify')()
fastify.register(require('./my-plugin'))
fastify.ready(() => {
console.log(fastify.shared)
})
3.2 封装级别控制 #
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
fastify.decorate('value', 'shared')
}
module.exports = fp(myPlugin, {
encapsulate: false
})
3.3 部分破坏封装 #
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
fastify.decorate('shared', 'global')
fastify.register(async function (fastify, opts) {
fastify.decorate('internal', 'local')
})
}
module.exports = fp(myPlugin)
shared 全局可用,internal 只在内部可用。
四、作用域继承 #
4.1 装饰器继承 #
javascript
const fastify = require('fastify')()
fastify.decorate('root', 'root value')
fastify.register(async function (fastify, opts) {
console.log('Can access root:', fastify.root)
fastify.decorate('level1', 'level 1 value')
fastify.register(async function (fastify, opts) {
console.log('Can access root:', fastify.root)
console.log('Can access level1:', fastify.level1)
})
})
4.2 钩子继承 #
javascript
const fastify = require('fastify')()
fastify.addHook('onRequest', async (request, reply) => {
console.log('Root hook')
})
fastify.register(async function (fastify, opts) {
fastify.addHook('onRequest', async (request, reply) => {
console.log('Plugin hook')
})
fastify.get('/test', async (request, reply) => {
return { message: 'Test' }
})
})
访问 /test 会依次执行两个钩子。
4.3 插件继承 #
javascript
const fastify = require('fastify')()
fastify.register(require('@fastify/jwt'), {
secret: 'secret'
})
fastify.register(async function (fastify, opts) {
fastify.get('/protected', {
preHandler: async (request, reply) => {
await request.jwtVerify()
}
}, async (request, reply) => {
return { user: request.user }
})
})
五、作用域隔离 #
5.1 兄弟作用域隔离 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.decorate('pluginA', 'value A')
})
fastify.register(async function (fastify, opts) {
console.log('Cannot access pluginA:', fastify.pluginA)
})
5.2 前缀隔离 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.get('/', async (request, reply) => {
return { message: 'API v1' }
})
}, { prefix: '/api/v1' })
fastify.register(async function (fastify, opts) {
fastify.get('/', async (request, reply) => {
return { message: 'API v2' }
})
}, { prefix: '/api/v2' })
5.3 配置隔离 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.decorate('config', { version: 'v1', features: ['a', 'b'] })
fastify.get('/config', async (request, reply) => {
return fastify.config
})
}, { prefix: '/v1' })
fastify.register(async function (fastify, opts) {
fastify.decorate('config', { version: 'v2', features: ['a', 'b', 'c'] })
fastify.get('/config', async (request, reply) => {
return fastify.config
})
}, { prefix: '/v2' })
六、作用域控制 #
6.1 控制装饰器可见性 #
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
const privateData = 'private'
fastify.decorate('public', 'public value')
fastify.decorateRequest('getPrivate', function () {
return privateData
})
}
module.exports = fp(myPlugin)
6.2 控制钩子作用域 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.addHook('onRequest', async (request, reply) => {
console.log('Scoped hook')
})
fastify.register(require('./routes'), { prefix: '/api' })
})
fastify.get('/public', async (request, reply) => {
return { message: 'Public route' }
})
6.3 控制路由作用域 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
fastify.register(async function (fastify, opts) {
fastify.get('/users', async (request, reply) => {
return { users: [] }
})
}, { prefix: '/api' })
}, { prefix: '/v1' })
七、实际应用场景 #
7.1 多租户应用 #
javascript
const fastify = require('fastify')()
const tenants = {
tenant1: {
db: 'mongodb://localhost/tenant1',
config: { name: 'Tenant 1' }
},
tenant2: {
db: 'mongodb://localhost/tenant2',
config: { name: 'Tenant 2' }
}
}
for (const [tenant, config] of Object.entries(tenants)) {
fastify.register(async function (fastify, opts) {
fastify.register(require('@fastify/mongodb'), {
url: config.db
})
fastify.decorate('tenantConfig', config.config)
fastify.get('/info', async (request, reply) => {
return {
tenant,
config: fastify.tenantConfig
}
})
}, { prefix: `/${tenant}` })
}
7.2 API版本控制 #
javascript
const fastify = require('fastify')()
fastify.register(require('./routes/v1'), { prefix: '/api/v1' })
fastify.register(require('./routes/v2'), { prefix: '/api/v2' })
fastify.get('/api', async (request, reply) => {
return {
versions: ['v1', 'v2'],
latest: 'v2'
}
})
routes/v1.js:
javascript
module.exports = async function (fastify, opts) {
fastify.get('/users', async (request, reply) => {
return { version: 'v1', users: [] }
})
}
routes/v2.js:
javascript
module.exports = async function (fastify, opts) {
fastify.get('/users', async (request, reply) => {
return { version: 'v2', users: [], meta: {} }
})
}
7.3 模块化功能 #
javascript
const fastify = require('fastify')()
fastify.register(require('./modules/users'), { prefix: '/users' })
fastify.register(require('./modules/products'), { prefix: '/products' })
fastify.register(require('./modules/orders'), { prefix: '/orders' })
modules/users/index.js:
javascript
module.exports = async function (fastify, opts) {
fastify.register(require('./routes'))
fastify.register(require('./hooks'))
fastify.register(require('./services'))
}
7.4 条件性功能 #
javascript
const fastify = require('fastify')()
fastify.register(async function (fastify, opts) {
if (process.env.ENABLE_ADMIN === 'true') {
fastify.register(require('./admin'), { prefix: '/admin' })
}
if (process.env.ENABLE_METRICS === 'true') {
fastify.register(require('./metrics'), { prefix: '/metrics' })
}
})
八、作用域调试 #
8.1 打印插件树 #
javascript
fastify.ready(() => {
console.log(fastify.printPlugins())
})
输出:
text
root
├── plugin-a
│ └── plugin-b
└── plugin-c
8.2 检查装饰器 #
javascript
fastify.ready(() => {
console.log('Has db:', fastify.hasDecorator('db'))
console.log('Has user:', fastify.hasRequestDecorator('user'))
console.log('Has success:', fastify.hasReplyDecorator('success'))
})
8.3 调试作用域 #
javascript
fastify.register(async function (fastify, opts) {
console.log('Plugin scope:', fastify.pluginName)
fastify.register(async function (fastify, opts) {
console.log('Nested scope:', fastify.pluginName)
})
})
九、最佳实践 #
9.1 合理使用封装 #
javascript
const fp = require('fastify-plugin')
async function sharedPlugin(fastify, opts) {
fastify.decorate('shared', 'global')
}
module.exports = fp(sharedPlugin)
async function isolatedPlugin(fastify, opts) {
fastify.decorate('internal', 'local')
}
module.exports = isolatedPlugin
9.2 避免过度封装 #
javascript
const fp = require('fastify-plugin')
async function databasePlugin(fastify, opts) {
const db = await connect(opts)
fastify.decorate('db', db)
}
module.exports = fp(databasePlugin, {
name: 'database'
})
9.3 清晰的作用域边界 #
javascript
fastify.register(async function (fastify, opts) {
fastify.decorate('internal', 'value')
fastify.get('/internal-route', async (request, reply) => {
return { internal: fastify.internal }
})
}, { prefix: '/module' })
十、常见问题 #
10.1 装饰器未定义 #
问题:
javascript
fastify.register(async function (fastify, opts) {
fastify.decorate('value', 'test')
})
console.log(fastify.value)
解决:
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
fastify.decorate('value', 'test')
}
module.exports = fp(myPlugin)
10.2 钩子未触发 #
问题:钩子只在特定路由触发。
解决:使用 fastify-plugin 或在根作用域注册钩子。
10.3 插件加载顺序 #
问题:依赖插件未加载。
解决:
javascript
module.exports = fp(myPlugin, {
name: 'my-plugin',
dependencies: ['database', 'redis']
})
十一、总结 #
本章我们学习了:
- 作用域概念:什么是作用域、作用域类型
- 默认封装:装饰器封装、路由封装、钩子封装
- 破坏封装:fastify-plugin、封装级别控制
- 作用域继承:装饰器继承、钩子继承、插件继承
- 作用域隔离:兄弟隔离、前缀隔离、配置隔离
- 作用域控制:装饰器可见性、钩子作用域、路由作用域
- 实际应用:多租户、版本控制、模块化功能
- 作用域调试:打印插件树、检查装饰器
- 最佳实践:合理使用封装、避免过度封装
- 常见问题:装饰器未定义、钩子未触发、加载顺序
接下来让我们学习中间件!
最后更新:2026-03-28