當(dāng)前位置:首頁 > IT技術(shù) > Web編程 > 正文

Next.js + 云開發(fā)Webify 打造絕佳網(wǎng)站
2021-09-10 19:17:26

Next.js酷在哪里?

之前使用 Next.js + strapi 做了一個簡單博客站點也順道寫了一篇 Next.js 簡明教程,之后 Next 本身一直在迅猛發(fā)展。利用代 js 能力來說做到了:

  • 極佳的開發(fā)體驗
  • 極佳的網(wǎng)站最佳的”動“,“靜”平衡

從特性上來說,支持:

  • SSR(Server Side Rendering)
    提供 getServerSideProps 方法,在用戶訪問時請求數(shù)據(jù),適用于實時數(shù)據(jù)頁面。
  • SSG(Static Site Generation)
    提供 getStaticProps,getStaticPaths 方法來預(yù)先生產(chǎn)靜態(tài)頁面;

而更酷的一點是:使用 fallback,revalidate 來支持一定的動態(tài)性。

這種能“動”的 SSG 自然是我所需要的,保持靜態(tài)訪問,而又能在我新增修改文章的時候,站點能夠自動更新。絕佳??!

為什么還需要來Webify“折騰”一番?

既然上面已經(jīng)很酷了,為什么會有今天的文章,為什么還需要折騰一番?

原因也很簡單:成本略高,為了不錯的訪問速度,你需要一臺性能不錯的虛擬機,一定的帶寬。對于一般個人博客,投入不劃算。

在這里就隆重地有請我們的解決方案:騰訊云開發(fā)Webify,簡單來說就是類似 vercel 的 Serverless 托管服務(wù),不過支持更多的框架,而且是國內(nèi)服務(wù)商,便宜且訪問速度一流。

有圖為證:

圖片

而且現(xiàn)在托管,能免費領(lǐng)300元無門檻代金券,香啊~感興趣的可以點擊下方鏈接了解一下:https://cloud.tencent.com/developer/article/1871549

CloudBase Webify實戰(zhàn)

對于一般文章使用類似 github 管理的就簡單了,Webify支持版本 Github、Gitlab、Gitee 服務(wù)商,聽說即將支持 Coding:

  • Vue.js (vue-cli)
  • React.js (create-react-app)
  • Hexo
  • Gatsby.js
  • Angular
  • Next.js SSG
  • Nuxt.js SSG
  • 以及自動適配框架

以本博客 next 為例,Webify實際上使用時了 next export 的能力,構(gòu)建后,直接部署靜態(tài)文件到 server。

如果你的博客文章,直接使用 md,git 管理,看到這里就OK了,git 提交,Webify自動會重新部署你的站點。cool~~

問題是如果你的站點數(shù)據(jù)來源于類似 strapi 這種 serverless cms 怎么辦?next export 不支持next SSG中“動”的特性(fallback,revalidate)。

Webify高階——自動化Webify

其實方法也很簡單,加一個橋接服務(wù),讓你的 serverless cms 的更新變動到 git 就好。

具體以 strapi 為例子:

  1. strapi 數(shù)據(jù)發(fā)布
  2. web hook到自定義的橋接服務(wù)。
  3. 橋接服務(wù)更新站點git。
  4. Weify觸發(fā)重新部署。

當(dāng)然如果后續(xù) webify 支持更多的重新部署方式,這里會更簡單一點。

這樣乍看,似乎又回到了原點,我們還是需要一臺服務(wù)器,這里又要引入本文的另一個嘉賓了,tcb 云函數(shù)。上述這種按需調(diào)用的服務(wù),使用云函數(shù)最合適了,你不需要一個一直開機的虛擬機,你只需要在更新文章時候才需要喚起云函數(shù)就好,隨用隨停,成本低廉。

按照本博客的場景,我們讓橋接服務(wù)在運行的時候,自動生成站點的 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:?識別真正的發(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,騰訊高級前端開發(fā)工程師。

歡迎訪問Webify官網(wǎng):https://webify.cloudbase.net/

個人站點扶持計劃,免費領(lǐng)300元無門檻代金券:https://webify.cloudbase.net/blog/personal-site-plan

本文摘自 :https://www.cnblogs.com/

開通會員,享受整站包年服務(wù)立即開通 >