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 为例子:

  1. strapi 数据公布
  2. web hook到自定义的桥接服务。
  3. 桥接服务更新站点git。
  4. 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 = 30module.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