前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Next.js + 腾讯云开发Webify 打造绝佳网站

Next.js + 腾讯云开发Webify 打造绝佳网站

作者头像
小刀c
发布2022-09-21 16:22:25
1.3K0
发布2022-09-21 16:22:25
举报
文章被收录于专栏:cc logcc log

toc

代码语言:txt
复制
- [生成站点地图sitemap.xml](https://hicc.pro/#%E7%94%9F%E6%88%90%E7%AB%99%E7%82%B9%E5%9C%B0%E5%9B%BEsitemapxml)
- [更新Github中文件](https://hicc.pro/#%E6%9B%B4%E6%96%B0github%E4%B8%AD%E6%96%87%E4%BB%B6)

Next.js酷在哪里?

之前使用Next.js + strapi做了一个简单博客站点也顺道写了一篇Next.js 简明教程,之后Next本身一直在迅猛发展。利用现代js能力来说做到了:

  • 极佳的开发体验
  • 极佳的网站最佳的”动“,“静”平衡

从特性上来说,支持:

  • SSR(Server Side Rendering)
    • 提供getServerSideProps方法,在用户访问时请求数据,适用于实时数据页面。
  • SSG(Static Site Generation)
    • 提供getStaticPropsgetStaticPaths方法来预先生产静态页面
    • 而更酷的一点是:使用fallback,revalidate来支持一定的动态性

这种能“动”的SSG自然是我所需要的,保持静态访问,而又能在我新增修改文章的时候,站点能够自动更新。绝佳!!

为什么还需要Webify来折腾一番?

既然上面已经很酷了,为什么会有今天的文章,为什么还需要折腾一番?

原因也很简单:成本略高,为了不错的访问速度,你需要一台性能不错的虚拟机,一定的带宽。对于一般个人博客,投入不划算。

在这里就隆重地有请我们的解决方案:腾讯云开发Webify,简单来说就是类似vercel的serverless 服务托管服务,不过支持更多的框架,而且是国内服务商,便宜访问速度一流

有图为证:

tcb Webify使用

对于一般文章使用类似github管理的就简单了,tcb Webify支持版本github,gitlab,gitee服务商,适配了:

  • 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中“动”的特性(fallbackrevalidate)。

Webify高阶——自动化Webify

其实方法也很简单,加一个桥接服务,让你的serverless cms的更新变动到git就好。

具体以strapi为例子

  1. strapi 数据发布
  2. web hook到自定义的桥接服务。
  3. 桥接服务更新站点git。
  4. Weify触发重新部署。

当然如果后续webify支持更多的重新部署方式,这里会更简单一点。

这样乍看,似乎又回到了原点,我们还是需要一台服务器,这里又要引入本文的另一个嘉宾了,tcb 云函数。上述这种按需调用的服务,使用云函数最合适了,你不需要一个一直开机的虚拟机,你只需要在更新文章时候才需要唤起云函数就好,随用随停,成本低廉。

按照本博客的场景,我们让桥接服务在运行的时候,自动生成站点的sitemap到github来一举两得。

下面是精简过的代码:

生成站点地图sitemap.xml

代码语言:javascript
复制
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中文件

代码语言:javascript
复制
'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
    }
};
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • toc
  • Next.js酷在哪里?
  • 为什么还需要Webify来折腾一番?
  • tcb Webify使用
  • Webify高阶——自动化Webify
    • 生成站点地图sitemap.xml
      • 更新Github中文件
      相关产品与服务
      云函数
      云函数(Serverless Cloud Function,SCF)是腾讯云为企业和开发者们提供的无服务器执行环境,帮助您在无需购买和管理服务器的情况下运行代码。您只需使用平台支持的语言编写核心代码并设置代码运行的条件,即可在腾讯云基础设施上弹性、安全地运行代码。云函数是实时文件处理和数据处理等场景下理想的计算平台。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档