Next.js酷在哪里?
之前使用 Next.js + strapi 做了一個(gè)簡(jiǎn)單博客站點(diǎn)也順道寫了一篇 Next.js 簡(jiǎn)明教程,之后 Next 本身一直在迅猛發(fā)展。利用代 js 能力來(lái)說(shuō)做到了:
- 極佳的開發(fā)體驗(yàn)
- 極佳的網(wǎng)站最佳的”動(dòng)“,“靜”平衡
從特性上來(lái)說(shuō),支持:
- SSR(Server Side Rendering)
提供 getServerSideProps 方法,在用戶訪問(wèn)時(shí)請(qǐng)求數(shù)據(jù),適用于實(shí)時(shí)數(shù)據(jù)頁(yè)面。 - SSG(Static Site Generation)
提供 getStaticProps,getStaticPaths 方法來(lái)預(yù)先生產(chǎn)靜態(tài)頁(yè)面;
而更酷的一點(diǎn)是:使用 fallback,revalidate 來(lái)支持一定的動(dòng)態(tài)性。
這種能“動(dòng)”的 SSG 自然是我所需要的,保持靜態(tài)訪問(wèn),而又能在我新增修改文章的時(shí)候,站點(diǎn)能夠自動(dòng)更新。絕佳!!
為什么還需要來(lái)Webify“折騰”一番?
既然上面已經(jīng)很酷了,為什么會(huì)有今天的文章,為什么還需要折騰一番?
原因也很簡(jiǎn)單:成本略高,為了不錯(cuò)的訪問(wèn)速度,你需要一臺(tái)性能不錯(cuò)的虛擬機(jī),一定的帶寬。對(duì)于一般個(gè)人博客,投入不劃算。
在這里就隆重地有請(qǐng)我們的解決方案:騰訊云開發(fā)Webify,簡(jiǎn)單來(lái)說(shuō)就是類似 vercel 的 Serverless 托管服務(wù),不過(guò)支持更多的框架,而且是國(guó)內(nèi)服務(wù)商,便宜且訪問(wèn)速度一流。
有圖為證:
而且現(xiàn)在托管,能免費(fèi)領(lǐng)300元無(wú)門檻代金券,香啊~感興趣的可以點(diǎn)擊下方鏈接了解一下:https://cloud.tencent.com/developer/article/1871549
CloudBase Webify實(shí)戰(zhàn)
對(duì)于一般文章使用類似 github 管理的就簡(jiǎn)單了,Webify支持版本 Github、Gitlab、Gitee 服務(wù)商,聽(tīng)說(shuō)即將支持 Coding:
- Vue.js (vue-cli)
- React.js (create-react-app)
- Hexo
- Gatsby.js
- Angular
- Next.js SSG
- Nuxt.js SSG
- 以及自動(dòng)適配框架
以本博客 next 為例,Webify實(shí)際上使用時(shí)了 next export 的能力,構(gòu)建后,直接部署靜態(tài)文件到 server。
如果你的博客文章,直接使用 md,git 管理,看到這里就OK了,git 提交,Webify自動(dòng)會(huì)重新部署你的站點(diǎn)。cool~~
問(wèn)題是如果你的站點(diǎn)數(shù)據(jù)來(lái)源于類似 strapi 這種 serverless cms 怎么辦?next export 不支持next SSG中“動(dòng)”的特性(fallback,revalidate)。
Webify高階——自動(dòng)化Webify
其實(shí)方法也很簡(jiǎn)單,加一個(gè)橋接服務(wù),讓你的 serverless cms 的更新變動(dòng)到 git 就好。
具體以 strapi 為例子:
- strapi 數(shù)據(jù)發(fā)布
- web hook到自定義的橋接服務(wù)。
- 橋接服務(wù)更新站點(diǎn)git。
- Weify觸發(fā)重新部署。
當(dāng)然如果后續(xù) webify 支持更多的重新部署方式,這里會(huì)更簡(jiǎn)單一點(diǎn)。
這樣乍看,似乎又回到了原點(diǎn),我們還是需要一臺(tái)服務(wù)器,這里又要引入本文的另一個(gè)嘉賓了,tcb 云函數(shù)。上述這種按需調(diào)用的服務(wù),使用云函數(shù)最合適了,你不需要一個(gè)一直開機(jī)的虛擬機(jī),你只需要在更新文章時(shí)候才需要喚起云函數(shù)就好,隨用隨停,成本低廉。
按照本博客的場(chǎng)景,我們讓橋接服務(wù)在運(yùn)行的時(shí)候,自動(dòng)生成站點(diǎn)的 sitemap 到github來(lái)一舉兩得。
- 用來(lái)sitemap生成站點(diǎn)地圖xml;
- 使用@octokit/rest,@octokit/plugin-create-or-update-text-file來(lái)更新github中文件。
下面是精簡(jiǎn)過(guò)的代碼:
生成站點(diǎn)地圖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 上添加密鑰來(lái)確保安全
????if?(CMS_TOKEN !== authorization) {
????????return?{
????????????doTrigger:?false
????????}
????}
????let?doTrigger =?false?//?TODO:?識(shí)別真正的發(fā)布
????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,騰訊高級(jí)前端開發(fā)工程師。
歡迎訪問(wèn)Webify官網(wǎng):https://webify.cloudbase.net/
個(gè)人站點(diǎn)扶持計(jì)劃,免費(fèi)領(lǐng)300元無(wú)門檻代金券:https://webify.cloudbase.net/blog/personal-site-plan
本文摘自 :https://www.cnblogs.com/