我在https://www.shandillia.com/blog上有一个 NextJS 原型。显示的数据是从https://dev.schantillia.com/graphql上的 Strapi 安装中提取的。我还在 Github 上https://github.com/amitschandillia/proost/web(前端)有整个代码库。
我正在使用 Apollo 客户端与 graphql 源进行交互。还有一个服务工作者设置为启用 PWA。
一切正常,除了我无法在浏览器中缓存查询结果。Service Worker 能够缓存除 Apollo 查询结果之外的所有内容。有什么办法可以启用吗?目标是:
- 为了能够在服务器上使用某种查询结果的预取。
- 能够通过 service worker 将结果缓存在浏览器中。
与此问题相关的三个文件如下:
阿波罗设置
// web/apollo/index.js
import { HttpLink } from 'apollo-link-http';
import { withData } from 'next-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';
// Set up cache.
const cache = new InMemoryCache();
// Configure Apollo.
const config = {
link: new HttpLink({
uri: 'https://dev.schandillia.com/graphql', // Server URL (must be absolute)
}),
cache,
};
export default withData(config);
查询组件
// web/pages/PostsList.jsx
import ReactMarkdown from 'react-markdown';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import { Fragment } from 'react';
import Typography from '@material-ui/core/Typography';
import CircularProgress from '@material-ui/core/CircularProgress';
const renderers = {
paragraph: props => <Typography variant="body1" gutterBottom {...props} />
};
const PostsList = ({ data: { error, posts } }) => {
let res = '';
if (error) res = (
<Typography variant="subtitle2" gutterBottom>
Error retrieving posts!
</Typography>
);
if (posts && posts.length) {
if (posts.length !== 0) {
// Payload returned
res = (
<Fragment>
{posts.map(post => (
<div>
<Typography variant="display1" gutterBottom>{post.title}</Typography>
<Typography variant="subtitle1" gutterBottom>{post.secondaryTitle}</Typography>
<Typography variant="subtitle2" gutterBottom>Post #{post._id}</Typography>
<ReactMarkdown source={post.body} renderers={renderers} />
</div>
))}
</Fragment>
);
} else {
res = (
// No payload returned
<Typography variant="subtitle2" gutterBottom>
No posts Found
</Typography>
);
}
} else {
res = (
// Retrieving payload
<CircularProgress />
);
}
return res;
};
const query = gql`
{
posts {
_id
title
secondaryTitle
body
}
}
`;
// The 'graphql' wrapper executes a GraphQL query and makes the results
// available on the 'data' prop of the wrapped component (PostsList)
export default graphql(query, {
props: ({ data }) => ({
data,
}),
})(PostsList);
博客页面
// web/pages/blog.jsx
import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';
import Head from 'next/head';
import Link from 'next/link';
import withRoot from '../lib/withRoot';
import PostsList from '../components/PostsList';
const styles = theme => ({
root: {
textAlign: 'center',
paddingTop: theme.spacing.unit * 20,
},
paragraph: {
fontFamily: 'Raleway',
},
});
class Blog extends PureComponent {
constructor(props) {
super(props);
}
componentDidMount() {
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceWorker.js'); }
}
render() {
const { classes } = this.props;
const title = 'Blog | Project Proost';
const description = 'This is the blog page';
return (
<Fragment>
<Head>
<title>{ title }</title>
<meta name="description" content={description} key="description" />
</Head>
<div className={classes.root}>
<Typography variant="display1" gutterBottom>
Material-UI
</Typography>
<Typography gutterBottom>
<Link href="/about">
<a>Go to the about page</a>
</Link>
</Typography>
<Typography gutterBottom>
<Link href="/blog">
<a>View posts</a>
</Link>
</Typography>
<Button variant="raised" color="primary">
Super Secret Password
</Button>
<Button variant="raised" color="secondary">
Super Secret Password
</Button>
</div>
<PostsList />
</Fragment>
);
}
}
Blog.propTypes = {
classes: PropTypes.shape({
root: PropTypes.string,
}).isRequired,
};
// Posts.propTypes = {
// classes: PropTypes.object.isRequired,
// };
export default withRoot(withStyles(styles)(Blog));
有问题的服务人员如下(为简洁起见,已编辑):
// web/offline/serviceWorker.js
const CACHE_NAME = '1b23369032b1541e45cb8e3d94206923';
const URLS_TO_CACHE = [
'/',
'/about',
'/blog',
'/index',
'apple-touch-icon.png',
'browserconfig.xml',
'favicon-16x16.png',
'favicon-194x194.png',
'favicon-32x32.png',
'favicon.ico',
'manifest.json',
];
// Call install event
self.addEventListener('install', (e) => {
e.waitUntil(
caches
.open(CACHE_NAME)
.then(cache => cache.addAll(URLS_TO_CACHE))
.then(() => self.skipWaiting())
);
});
// Call activate event
self.addEventListener('activate', (e) => {
// remove unwanted caches
e.waitUntil(
caches.keys().then((cacheNames) => {
Promise.all(
cacheNames.map((cache) => {
if (cache !== CACHE_NAME) {
return caches.delete(cache);
}
})
);
})
);
});
// Call fetch event
self.addEventListener('fetch', (e) => {
e.respondWith(
fetch(e.request).catch(() => caches.match(e.request))
);
});
请指教!