前些天公司发下来给一个项目的aws-amplify服务升级到v6的任务,以下是我的升级经历和总结,希望对你有所帮助。
1. 关于打包 (Webpack4) + Babel 转译的问题
如果你的项目使用 Webpack4 打包,而在代码中直接导入 Amplify v6(或者间接导入带有新语法的模块),因为webpack4不能直接识别一些很新的js语法,而且babel默认不会转译node_modules下的依赖包的文件,导致webpack打包不了,在npm run dev时报错:
error in ./node_modules/@aws-amplify/core/node_modules/uuid/dist/esm-browser/v4.js
Module parse failed: Unexpected token (9:33)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| }
| options = options || {};
> const rnds = options.random ?? options.rng?.() ?? rng();
| if (rnds.length < 16) {
| throw new Error('Random bytes length must be >= 16');
............
如果出现了这种错误,那么你需要使用到babel等工具来进行转译。
我是vue2项目,可以在vue.config.js中给transpileDependencies项进行配置:
TypeScript
// 详情可见https://cli.vuejs.org/zh/config/#transpiledependencies
module.exports = {
transpileDependencies: [
/aws-amplify/
],
.......
}
2. Amplify 配置按照官方文档配置后,启动仍报not configured错误
在我的升级过程中,出现了一个比较诡异的问题 ------ 即使按照官方文档已经配置好了**Amplify.configure(...),** 项目启动后仍报「没有配置」 (not configured)的错误**。**
最终解决方案是:
1、在项目根目录下的 amplify/backend/ 中补充配置文件。由 Amplify CLI 生成,具体内容视项目而定。如果你没有公司在Amplify上的账号,这一步操作通知相关负责人去完成。
2、在前端代码中,使用如下方式初始化 Amplify:
(1)把初始化的代码写到一个单独的 JS (或 TS) 文件里,然后在你的入口文件最顶部导入它:
aws-amplify-init.js:
TypeScript
import { Amplify } from 'aws-amplify';
import amplifyconfig from './amplifyconfiguration.json';
Amplify.configure(amplifyconfig);
入口文件.js:
TypeScript
import './amplify-init' // ← 必须放最前面,确保在任何组件/模块之前执行
(2)若你在项目代码中导入 Auth, API, Storage等 Amplify 模块,使用 @aws-amplify/xxx 的路径形式,而不是 aws-amplify/xxx、aws-amplify
TypeScript
import { signOut, signIn, fetchAuthSession } from '@aws-amplify/auth';
我这里在使用'aws-amplify/xxx'引入时,也会报没有配置(像什么userPool not configured)的问题
TypeScript
import { signOut, signIn, fetchAuthSession } from 'aws-amplify/auth';
这个问题目前也不清楚具体原因,可能是由于aws-amplify双包机制(aws-amplify和@aws-amplify) + webpack按package.json解析依赖path导致的。总之不太会是aws-amplify本身的问题。
3. Storage.put → uploadData 的迁移,以及路径差异的问题
在 v6 中,Storage (S3 上传/下载等) 的 API 做了重构。根据官方迁移指南:旧版本使用的是:
TypeScript
import { Storage } from 'aws-amplify';
await Storage.put('test.txt', blob);
而在 v6 中,推荐使用新的模块化导入 + 功能 API:
TypeScript
import { uploadData, getUrl } from 'aws-amplify/storage';
await uploadData({
key: 'test.txt',
data: blob,
});
但是新版本中使用uploadData之后,发现实际请求中,文件上传的路径不知道为何实际传输的文件的路径不一样:
原本: xxxxx/public/fileName
新版本v6使用uploadData: xxxxx/fileName
最后是在传输文件这里对比,补上了缺失的目录后,传输文件的地址才变得正确。 个人猜测可能升级后uploadData的验证和put不一样了,或者和公司在Amplify上的设置有关系还是怎么。但是本人在本次升级中只拿到了前端项目的代码,这些更细节的东西后续就交给公司的测试兄弟和项目经理们去解决了。(o.0)
4. GraphQL / API 的迁移:client 的生成 + subscribe / websocket / header 的改动
v6 对 API / GraphQL 部分也做了重构/优化:模块化导入 + 新的客户端 SDK + 更灵活的授权方式 (authMode) + 更好的 tree-shaking/bundle size 优化
参考链接:
Amplify クライアントを使用したクライアントアプリケーションの構築 - AWS AppSync GraphQL
在graphql()里,订阅实时事件的请求类型使用的是websocket。
在旧版本aws-amplify中,graphql的验证消息会直接补充到url的参数中:
graphql?header=xxxx&payload=e30=
如果你想在v6中也在url上包含这些信息,那么你需要在创建client时添加上header参数:
const client = generateClient({ headers: {......}, })
比如我的项目旧版本中具备两个参数:header、payload
那么我的代码就是:
const client = generateClient({ headers: { header: 'xxxxxx', payload: 'e30=' }, })
补充一点:旧版本的url中存放的是base64转码后的对象 Buffer.from(JSON.stringify(header)).toString('base64')
并且需要补充验证消息。v6创建client时,还要写认证模式 如果你的authMode是'apiKey',那你需要提供apiKey; 如果是'userPool',那你需要提供authToken。并且这个authToken,需要传入的是本次登录的验证消息里面idToken(不用base64转码)
5、signIn()升级和fetchAuthSession()
登录之后(signIn),本次登录的验证内容不再包含在signIn的返回内容中,你需要调用fetchAuthSession来获取本次登录的相关验证信息:
旧版本:
TypeScript
await Auth.signIn(userName, passWord).then(res => {
console.log('login成功,响应体内容:', res)
})
setJwtToken(res.signInUserSession.accessToken.jwtToken)
新版本:
TypeScript
await signIn({
username, password
})
const tokenData = (await fetchAuthSession()).tokens
if (tokenData) {
setJwtToken(tokenData.accessToken.toString())
} else {
console.error('fetchAuthSession error: User not signed in')
}
创建client使用到的authToken就是base64转码后的idToken。
idToken的获取:
TypeScript
const idToken = (await fetchAuthSession()).tokens.idToken.toString()
创建client:
TypeScript
generateClient({ authMode: 'userPool', authToken: idToken })
希望对你有所帮助。