Next.js 酷在哪里?
之前应用 Next.js + strapi 做了一个简略博客站点也顺道写了一篇 Next.js 扼要教程,之后 Next 自身始终在迅猛发展。利用代 js 能力来说做到了:
- 极佳的开发体验
- 极佳的网站最佳的”动“,“静”均衡
从个性上来说,反对:
- SSR(Server Side Rendering)
提供 getServerSideProps 办法,在用户拜访时申请数据,实用于实时数据页面。 - SSG(Static Site Generation)
提供 getStaticProps,getStaticPaths 办法来事后生产动态页面;
而更酷的一点是:应用 fallback,revalidate 来反对肯定的动态性。
这种能“动”的 SSG 天然是我所须要的,放弃动态拜访,而又能在我新增修改文章的时候,站点可能自动更新。绝佳!!
为什么还须要来 Webify“折腾”一番?
既然下面曾经很酷了,为什么会有明天的文章,为什么还须要折腾一番?
起因也很简略:老本略高,为了不错的访问速度,你须要一台性能不错的虚拟机,肯定的带宽。对于个别集体博客,投入不划算。
在这里就隆重地有请咱们的解决方案: 腾讯云开发 Webify,简略来说就是相似 vercel 的 Serverless 托管服务,不过反对更多的框架,而且是国内服务商,便宜且访问速度一流。
有图为证:
而且当初托管,能收费领 300 元无门槛代金券,香啊~ 感兴趣的能够点击下方链接理解一下:https://cloud.tencent.com/developer/article/1871549
CloudBase Webify 实战
对于个别文章应用相似 github 治理的就简略了,Webify 反对版本 Github、Gitlab、Gitee 服务商,据说行将反对 Coding:
- Vue.js (vue-cli)
- React.js (create-react-app)
- Hexo
- Gatsby.js
- Angular
- Next.js SSG
- Nuxt.js SSG
- 以及主动适配框架
以本博客 next 为例,Webify 实际上应用时了 next export 的能力,构建后,间接部署动态文件到 server。
如果你的博客文章,间接应用 md,git 治理,看到这里就 OK 了,git 提交,Webify 主动会重新部署你的站点。cool~~
问题是如果你的站点数据来源于相似 strapi 这种 serverless cms 怎么办?next export 不反对 next SSG 中“动”的个性(fallback,revalidate)。
Webify 高阶——自动化 Webify
其实办法也很简略,加一个桥接服务,让你的 serverless cms 的更新变动到 git 就好。
具体以 strapi 为例子:
- strapi 数据公布
- web hook 到自定义的桥接服务。
- 桥接服务更新站点 git。
- Weify 触发重新部署。
当然如果后续 webify 反对更多的重新部署形式,这里会更简略一点。
这样乍看,仿佛又回到了原点,咱们还是须要一台服务器,这里又要引入本文的另一个嘉宾了,tcb 云函数。上述这种按需调用的服务,应用云函数最合适了,你不须要一个始终开机的虚拟机,你只须要在更新文章时候才须要唤起云函数就好,随用随停,老本低廉。
依照本博客的场景,咱们让桥接服务在运行的时候,主动生成站点的 sitemap 到 github 来两全其美。
- 用来 sitemap 生成站点地图 xml;
- 应用 @octokit/rest,@octokit/plugin-create-or-update-text-file 来更新 github 中文件。
上面是精简过的代码:
生成站点地图 sitemap.xml
const {
SitemapStream,
streamToPromise
} = require('sitemap')
const {
Readable,
Transform,
pipeline
} = require('stream')
const {
apiRequest,
getPostsWithGraphql
} = require('./request')
const PaginationLimit = 30
module.exports = ({
hostname,
cmsUrl
}) => {async function getPostSitemap() {
const smStream = new SitemapStream({hostname,});
let page = 0;
const postStream = new Readable({
objectMode: true,
async read(size) {const result = await getPostsWithGraphql(`${cmsUrl}/graphql`, page++, PaginationLimit);
if (result.error || !Array.isArray(result.data.posts)) {this.push(null);
} else {result.data.posts.forEach((item) => {this.push(item);
});
if (result.data.posts.length < PaginationLimit) {this.push(null);
}
}
},
});
const trans = new Transform({
objectMode: true,
transform(data, encoding, callback) {
callback(null, {url: `/p/${data.book.slug || data.book.uuid}/${data.slug || data.uuid}`,
changefreq: 'daily',
priority: 1,
lastmod: new Date(data.updated_at),
});
},
});
const buffer = await streamToPromise(pipeline(postStream, trans, smStream, (e) => {// throw e;}))
return {
path: 'public/sitemap.xml',
content: buffer.toString()}
}
return Promise.all([// getHomeSitemap(),
// getBookSitemap(),
getPostSitemap()])
}
更新 Github 中文件
'use strict';
const {Octokit} = require("@octokit/rest");
const {createOrUpdateTextFile,} = require("@octokit/plugin-create-or-update-text-file");
const {throttling} = require("@octokit/plugin-throttling");
const getSitemaps = require('./sitemap')
const MyOctokit = Octokit.plugin(createOrUpdateTextFile, throttling);
exports.main = async (event, context) => {
const {
headers: {
authorization,
'x-strapi-event': strapiEvent
},
body
} = event;
const {
model,
entry
} = JSON.parse(body)
const {
CMS_TOKEN,
GITHUB_ACCESS_TOKEN,
BLOG_URL = 'https://hicc.pro',
CMS_URL = 'https://cms.hicc.pro'
} = process.env;
// strapi 上增加密钥来确保安全
if (CMS_TOKEN !== authorization) {
return {doTrigger: false}
}
let doTrigger = false // TODO: 辨认真正的公布
const siteMaps = await getSitemaps({
hostname: BLOG_URL,
cmsUrl: CMS_URL
})
const octokit = new MyOctokit({
auth: GITHUB_ACCESS_TOKEN,
throttle: {onRateLimit: (retryAfter, options) => {
console.warn(`Request quota exhausted for request ${options.method} ${options.url}`
);
// Retry twice after hitting a rate limit error, then give up
if (options.request.retryCount <= 2) {console.log(`Retrying after ${retryAfter} seconds!`);
return true;
}
},
onAbuseLimit: (retryAfter, options) => {
// does not retry, only logs a warning
console.warn(`Abuse detected for request ${options.method} ${options.url}`
);
},
},
});
await Promise.all(siteMaps.map(({
path,
content
}) => {
return octokit.createOrUpdateTextFile({
// replace the owner and email with your own details
owner: "xxx",
repo: "xxx",
path,
message: `feat: update ${path} programatically`,
content: content,
branch: 'master',
sha: '',
committer: {
name: "xxx",
email: "xxxx@outlook.com",
},
author: {
name: "xxx",
email: "xxxx@outlook.com",
},
})
}))
return {doTrigger}
};
作者:hicc,腾讯高级前端开发工程师。
欢送拜访 Webify 官网:https://webify.cloudbase.net/
集体站点搀扶打算,收费领 300 元无门槛代金券:https://webify.cloudbase.net/blog/personal-site-plan