继上一篇的服务端项目搭建,我们开始下一阶段的客户端搭建。这次文章中主要介绍的是使用vue-apollo整合
apollo-client
和Vue
代码仓库
1. 简介
什么是apollo-client
apollo-client是一个整合Vue、React和其他市面上比较流行的Graphql的客户端。
什么是vue-apollo
vue-apollo 通过声明式查询将apollo-client
整合到你的Vue
组件中。写这篇文章的时候,该插件兼容 Vue2
,Vue3
还在开发中。
2. 创建客户端工程
vue create vue-graphql
cd vue-graphql
npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
// ps: 文中所用工程使用到了element-ui、vue-router,这两者的安装过程就不多说了。
1.安装插件
import VueApollo from 'vue-apollo'
Vue.use(VueApollo)
2.创建apollo-link-http
import { createHttpLink } from 'apollo-link-http';
const httpLink = createHttpLink({
uri: 'http://localhost:3000/graphql', // 指定后端应用的路径,可以是函数或字符串。默认值为 /graphql
});
3.创建apollo-link
import { ApolloLink } from 'apollo-link';
const middlewareLink = new ApolloLink((operation, forward) => {
const token = localStorage.getItem('token'); // 在这里进行token的注入
operation.setContext({
headers: {
Authorization: token || null
}
});
return forward(operation);
});
4.创建apollo客户端
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloClient } from 'apollo-client';
const apolloClient = new ApolloClient({
link: middlewareLink.concat(httpLink),
cache: new InMemoryCache(),
connectToDevTools: true
});
5.创建Vue-apollo 实例
import VueApollo from 'vue-apollo'
const apolloProvider = new VueApollo({
defaultClient: apolloClient
})
new Vue({
render: h => h(App),
router,
apolloProvider // 注入到vue实例
}).$mount('#app')
并将apolloProvide注册到Vue实例中。
new Vue({
el: '#app',
// 像 vue-router 或 vuex 一样注入 apolloProvider,可以通过this.$apollo访问插件实例
apolloProvider,
render: h => h(App),
})
6.Vscode安装 Apollo GraphQL 扩展
安装vscode插件可以使开发者在开发Graphql应用时,更加的便捷。其主要功能如下:
- 在graphql文件和js文件中高亮graphql语法
- 代码的智能提示,语法优化提示
- 校验语法正确
- More.....
在项目的根目录下添加apollo graphql
的拓展文件
// apollo.config.js
module.exports = {
client: {
service: {
name: 'my-app',
// GraphQL API 的 URL
url: 'http://localhost:3000/graphql'
},
// 通过扩展名选择需要处理的文件
includes: [
'src/**/*.vue',
'src/**/*.js'
]
}
};
完成以上步骤后,能够完成我们基本的开发需求。
3. 使用
1. 查询
服务端代码
// src/cats/cats.graphql
type Query {
cats: [Cat] // 批量查询
cat(id: ID!): Cat // 单个查询
}
@Query('cats') // 声明query。与graphql文件中的query对应
@UseGuards(CatsGuard) // 这个注解是nestjs内置的注解。用于权限校验
async getCats() {
return this.catsService.findAll();
}
@Query('cat') // 声明query。与graphql文件中的query对应
async findOneById(
@Args('id', ParseIntPipe)
id: number,
): Promise<Cat> {
return this.catsService.findOneById(id);
}
客户端代码
<script>
import gql from 'graphql-tag';
export default {
data() {
return {
cats: [],
catList: []
}
},
apollo: {
// 当查询的名字和apollo的属性名一致
cats: gql`
query {
cats {
id,
name,
age
}
}
`,
// 当查询的属性名和apollo的属性名不一致时
catList: {
query: gql`
query {
cats {
id,
name,
age
}
}
`,
update: (data) => data.cats // 标识vue-apollo怎么处理数据
}
}
};
</script>
注意:当查询的属性名和query
的字段不一致时,vue-apollo
不会去把返回的数据直接塞入到对应的属性名中,而是需要我们使用update
属性去处理。正如上述的代码中所描述的。
2. 变更
methods: {
createDataButtonClick() {
this.addCat() // 调用mutation方法
},
async addCat() {
await this.$apollo.mutate({
mutation: gql`
mutation($createCatInput: CreateCatInput) {
createCat(createCatInput: $createCatInput) {
name,
age
}
}
`,
variables: {
createCatInput: {
name: "cat2",
age: 1
}
}
})
}
}
3. 订阅
1. 安装依赖
yarn add apollo-link-ws apollo-utilities subscriptions-transport-ws
2. vue-apollo初始化修改
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws'
import { InMemoryCache } from 'apollo-cache-inmemory';
// import { ApolloLink } from 'apollo-link';
import { split } from 'apollo-link'
import { getMainDefinition } from 'apollo-utilities'
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql',
// 对于token的校验可以改为headers,或者fetch
})
// 创建订阅的 websocket 连接
const wsLink = new WebSocketLink({
uri: 'ws://localhost:3000/subscriptions',
options: {
reconnect: true,
},
})
const link = split(
// 根据操作类型分割
({ query }) => {
const definition = getMainDefinition(query)
return definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
},
wsLink,
httpLink
)
// 创建 apollo 客户端
const apolloClient = new ApolloClient({
// link: middlewareLink.concat(httpLink).concat(),
link,
cache: new InMemoryCache(),
connectToDevTools: true
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
})
new Vue({
render: h => h(App),
router,
apolloProvider
}).$mount('#app')
3. 服务端声明订阅代码
订阅主要使用websocket进行传输。客户端注册回调函数,服务端注册和发布订阅。
// src/cats/cat.graphql
type Subscription {
catCreated: Cat
}
// src/cats/cat.resolver.ts
import { PubSub } from 'graphql-subscriptions';
.....
@Mutation('createCat')
async create(@Args('createCatInput') args: CreateCatDto): Promise<Cat> {
const createdCat = await this.catsService.create(args);
pubSub.publish('catCreated', { catCreated: createdCat }); // 添加订阅
return createdCat;
}
.....
@Subscription('catCreated')
catCreated() {
return pubSub.asyncIterator('catCreated'); // 发布订阅
}