Feathers v3(秃鹰)
Feathers v3带有一些很棒的改进和新功能,我们强烈建议您尽快升级.一开始看起来有点令人生畏,但几乎在所有情况下,新的CLI都会自动为您提供所有方式.此页面包含有关快速升级路径的信息以及有关从Feathers v2升级到v3的所有更改的更多信息.
阅读 飞入2018年 的发布帖子
快速升级
要快速升级任何Feathers插件或应用程序,您可以使用新CLI中的 upgrade 命令.首先,如果安装了它,请卸载旧的 feathers-cli:
npm uninstall feathers-cli -g
然后安装 @feathersjs/cli 并升级项目:
npm install @feathersjs/cli -g
cd path/to/project
feathers upgrade
CLI将使用 package.json 中的 directories.lib 来知道源文件的位置,如果没有提供,则默认为 src.如果您有一个已转换的应用/模块,例如使用babel,包括 lib 和 src 文件夹,最简单的是将 package.json 中的 directories.lib 更改为 src 而不是 lib,以便CLI将正确升级原始源文件而不是已转换的源文件.
简而言之(有关详细信息,请参见下文),这将:
将所有核心软件包升级到新的作用域软件包名称及其最新版本
删除所有 feathers-hooks 导入和单行 app.configure(hooks()); (链接 .configure(hooks()) 调用必须手动删除))
为任何使用 feathers-rest 的应用程序添加Express兼容性(其他没有 feathers-rest 的Feathers应用程序必须手动更新)
删除所有 .filter 导入并调用已被通道功能取代的 service.filter
添加频道
如果您使用实时(使用Socket.io或Primus),请将以下文件添加为 src/channels.js:
module.exports = function(app) {
if(typeof app.channel !== 'function') {
// If no real-time functionality has been configured just return
return;
}
app.on('connection', connection => {
// On a new real-time connection, add it to the anonymous channel
app.channel('anonymous').join(connection);
});
app.on('login', (authResult, { connection }) => {
// connection can be undefined if there is no
// real-time connection, e.g. when logging in via REST
if(connection) {
// Obtain the logged in user from the connection
// const user = connection.user;
// The connection is no longer anonymous, remove it
app.channel('anonymous').leave(connection);
// Add it to the authenticated user channel
app.channel('authenticated').join(connection);
// Channels can be named anything and joined on any condition
// E.g. to send real-time events only to admins use
// if(user.isAdmin) { app.channel('admins').join(connection); }
// If the user has joined e.g. chat rooms
// if(Array.isArray(user.rooms)) user.rooms.forEach(room => app.channel(`rooms/${room.id}`).join(channel));
// Easily organize users by email and userid for things like messaging
// app.channel(`emails/${user.email}`).join(channel);
// app.channel(`userIds/$(user.id}`).join(channel);
}
});
app.publish((data, hook) => { // eslint-disable-line no-unused-vars
// Here you can add event publishers to channels set up in `channels.js`
// To publish only for a specific event use `app.publish(eventname, () => {})`
// e.g. to publish all service events to all authenticated users use
return app.channel('authenticated');
});
// Here you can also add service specific event publishers
// e..g the publish the `users` service `created` event to the `admins` channel
// app.service('users').publish('created', () => app.channel('admins'));
// With the userid and email organization from above you can easily select involved users
// app.service('messages').publish(() => {
// return [
// app.channel(`userIds/${data.createdBy}`),
// app.channel(`emails/${data.recipientEmail}`)
// ];
// });
};
并且需要在 src/app.js 中配置它(注意它应该在所有服务之后配置,以便 channels.js 可以注册服务特定的发布者):
const channels = require('./channels');
// After `app.configure(services)`
app.configure(channels);
重要
The channels.js file shown above will publish
all real-time events to all authenticated users. This is already
safer than the previous default but you should carefully review the
事件频道 documentation and implement
appropriate channels so that only the right users are going to
receive real-time events.
将应用程序迁移到通道后,可以删除所有 <servicename> .filter.js 文件.
保护领域
Feathers v3有一种新机制可确保敏感信息永远不会发布到任何客户端.要保护始终保护用户密码,请在 src/services/users/users.hooks.js 中添加 api/authentication/local#protect,而不是 remove('password') 钩子:
const { hashPassword } = require('@feathersjs/authentication-local').hooks;
const { hashPassword, protect } = require('@feathersjs/authentication-local').hooks;
module.exports = {
before: {
all: [],
find: [ authenticate('jwt') ],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
after: {
all: [
// Make sure the password field is never sent to the client
// Always must be the last hook
protect('password')
],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};
更新客户端应用程序
Client side Feathers applications can also be updated using the CLI but
may need some manual intervention. Most importantly, since Feathers core
now natively ships as ES6 code, the module bundler, like Webpack, has to
be instructed to transpile it. More information can be found in the
Feathers 客户端. For Webpack and
create-react-app usage (which both will throw a minification error
without changes), see Webpack.
@feathersjs npm scope
所有Feathers核心模块都已移至 @feathersjs npm范围.这使得更清楚哪些模块被视为核心模块以及哪些模块受社区支持,并且还允许我们更轻松地管理发布权限.以下模块已重命名:
主要Feathers
旧名 |
范围名称 |
feathers |
@feathersjs/feathers |
feathers-cli |
@feathersjs/cli |
feathers-commons |
@feathersjs/commons |
feathers-rest |
@feathersjs/express/rest |
feathers-socketio |
@feathersjs/socketio |
feathers-primus |
@feathersjs/primus |
feathers-errors |
@feathersjs/errors |
feathers-configuration |
@feathersjs/configuration |
feathers-socket-commons |
@feathersjs/socket-commons |
认证
旧名 |
范围名称 |
feathers-authentication |
@feathersjs/authentication |
feathers-authentication-jwt |
@feathersjs/authentication-jwt |
feathers-authentication-local |
@feathersjs/authentication-local |
feathers-authentication-oauth1 |
@feathersjs/authentication-oauth1 |
feathers-authentication-oauth2 |
@feathersjs/authentication-oauth2 |
feathers-authentication-client |
@feathersjs/authentication-client |
客户端 Feathers
旧名 |
范围名称 |
feathers/client |
@feathersjs/feathers |
feathers-client |
@feathersjs/client |
feathers-rest/client |
@feathersjs/rest-client |
feathers-socketio/client |
@feathersjs/socketio-client |
feathers-primus/client |
@feathersjs/primus-client |
feathers-authentication/client |
@feathersjs/authentication-client |
文档更改
通过更好地关注Feathers核心,非核心模块的存储库,文档和指南已移至更合适的位置:
框架独立
@feathersjs/feathers v3与框架无关,可以在客户端和开箱即用的Node中运行.这意味着它不再默认扩展Express.
而 @feathersjs/express 在 require('@feathersjs/express').rest 或 @feathersjs/ 中提供框架绑定和REST提供程序(以前称为 feathers-rest).express/rest``. @feathersjs/express 还带有Express内置中间件,如 express.static 和最近包含的 express.json 和 express.urlencoded 身体解析器.一旦Feathers应用程序被 “表达”,它就可以像以前的版本一样使用:
Before
const feathers = require('feathers');
const bodyParser = require('body-parser');
const rest = require('feathers-rest');
const errorHandler = require('feathers-errors/handler');
const app = feathers();
app.configure(rest());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Register an Express middleware
app.get('/somewhere', function(req, res) {
res.json({ message: 'Data from somewhere middleware' });
});
// Statically host some files
app.use('/', feathers.static(__dirname));
// Use a Feathers friendly Express error handler
app.use(errorHandler());
Now
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
// Create an Express compatible Feathers application
const app = express(feathers());
// Add body parsing middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Initialize REST provider (previous in `feathers-rest`)
app.configure(express.rest());
// Register an Express middleware
app.get('/somewhere', function(req, res) {
res.json({ message: 'Data from somewhere middleware' });
});
// Statically host some files
app.use('/', express.static(__dirname));
// Use a Feathers friendly Express error handler
app.use(express.errorHandler());
钩子在核心
feathers-hooks 插件现在是核心的一部分,不再需要导入和配置.所有服务都将立即包含挂钩功能.此外,现在还可以定义应该在 hook.dispatch 中发送给客户端的不同数据,这样可以正确保护不应该显示给客户端的属性.
Before
const feathers = require('feathers');
const hooks = require('feathers-hooks');
const app = feathers();
app.configure(hooks());
app.use('/todos', {
get(id) {
return Promise.resolve({
message: `You have to do ${id}`
});
}
});
app.service('todos').hooks({
after: {
get(hook) {
hook.result.message = `${hook.result.message}!`;
}
}
});
Now
const feathers = require('feathers');
const app = feathers();
app.use('/todos', {
get(id) {
return Promise.resolve({
message: `You have to do ${id}`
});
}
});
app.service('todos').hooks({
after: {
get(hook) {
hook.result.message = `${hook.result.message}!`;
}
}
});
事件频道和出版
以前,过滤器用于为每个事件和每个连接运行,以确定是否应该发送事件.
事件通道是一种更安全,更高效的方式,用于定义将实时事件发送到的连接.而不是为每个事件和每个连接运行,而是在建立或验证连接时定义连接属于哪个通道.
// On login and if it is a real-time connectionn, add the connection to the `authenticated` channel
app.on('login', (authResult, { connection }) => {
if(connection) {
const { user } = connection;
app.channel('authenticated').join(connection);
}
});
// Publish only `created` events from the `messages` service
app.service('messages').publish('created', (data, context) => app.channel('authenticated'));
// Publish all real-time events from all services to the authenticated channel
app.publish((data, context) => app.channel('authenticated'));
要仅发布到用户所在的房间:
// On login and if it is a real-time connection, add the connection to the `authenticated` channel
app.on('login', (authResult, { connection }) => {
if(connection) {
const { user } = connection;
// Join `authenticated` channel
app.channel('authenticated').join(connection);
// Join rooms channels for that user
rooms.forEach(roomId => app.channel(`rooms/${roomId}`).join(connection));
}
});
更好地分离客户端和服务器端模块
自v2以来,Feathers核心一直在客户端和服务器上运行,但并不总是很清楚应该使用哪些相关模块.现在,所有客户端连接器都位于它们自己的存储库中,而主要的Feathers存储库在客户端和服务器上可能需要相同的方式.
Before
const io = require('socket.io-client');
const feathers = require('feathers/client');
const hooks = require('feathers-hooks');
const socketio = require('feathers-socketio/client');
const auth = require('feathers-authentication-client');
const socket = io();
const app = feathers()
.configure(hooks())
.configure(socketio(socket))
.configure(auth());
Now
const io = require('socket.io-client');
const feathers = require('@feathersjs/feathers');
const socketio = require('@feathersjs/socketio-client');
const auth = require('@feathersjs/authentication-client');
const socket = io();
const app = feathers()
.configure(socketio(socket))
.configure(auth());
Node 6+
上面提到的核心存储库也已经被迁移到可以直接使用(例如,当npm将存储库安装为Git/GitHub依赖关系时),而不需要Babel转换步骤.
由于所有存储库都广泛使用ES6,这也意味着不再支持 node 4.
另见 /feathers/issues/608.
弃用和其他API更改
Feathers服务方法不再支持回调.所有服务方法总是返回Promise.自定义服务必须返回Promise或使用 async/await.
service.before 和 service.after 已被替换为单个 app.hooks({before,after})
app.service(path) 只返回一个服务,不再用于注册新服务(通过 app.service(path,service)).请使用 app.use(path,service).
之前直接添加到“params”的路由参数现在位于 params.route 中
像 feathers.static 这样的快速中间件现在位于 const express = require('@feathersjs/express') 使用 express.static
已从所有核心存储库中删除实验性TypeScript定义.可以在 feathersjs-ecosystem/feathers-typescript 中开发此版本的TypeScript定义.欢迎帮助.
向后兼容性polyfill
除了上面列出的步骤,现有的钩子,数据库适配器,服务和其他插件应该与Feathers v3完全兼容,无需任何额外的修改.
本节包含一些快速向后兼容性polyfill,用于突破性更改,可用于简化迁移或继续使用使用弃用语法的插件.
基本服务过滤器
这是先前事件过滤器功能的基本模拟.它不使用promises或允许修改数据(现在应该通过设置 hook.dispatch 来处理).
app.mixins.push(service => {
service.mixin({
filter(eventName, callback) {
const args = callback ? [ eventName ] : [];
// todos.filter('created', function(data, connection, hook) {});
if(!callback) {
callback = eventName;
}
// A publisher function that sends to the `authenticated`
// channel that we defined in the quick upgrade section above
args.push((data, hook) => app.channel('authenticated')
.filter(connection =>
callback(data, connection, hook)
)
);
service.publish(... args);
}
});
});
路线参数
app.hooks({
before(hook) {
Object.assign(hook.params, hook.params.route);
return hook;
}
})
.before 和 .after 挂钩注册
app.mixins.push(service => {
service.mixin({
before(before) {
return this.hooks({ before });
},
after(after) {
return this.hooks({ after });
},
})
});