我的项目背景
试想一下,如果你当初要为你本人或者你所在的组织创立一个强内容的站点,同时要求好的 SEO(搜素引擎优化),比方博客,你会怎么做呢?
由 vite 或者 create-react-app 等脚手架构建的一般 SPA 利用是不行的,因为这样数据都是通过 AJAX 返回的。你临时不理解这些概念也没关系,你只须要晓得,这种形式下,搜索引擎是无奈很好地理解你的网站是干什么的,所以就算公众在搜索引擎搜寻你的站点的相干内容,搜索引擎也很难把你的站点排在搜寻后果前列。
那么为每个页面都编写一个动态的 html 页面呢?比方,为每篇文章都编写一个 html 文件,而后放在服务器上,这样只有客户端申请某篇文章,服务器就把对应的文章页面间接返回。这样也不好,太麻烦了,如果每次更改内容,都要用硬编码的形式去应答,那就把事件弄得太简单了。如果有一种后盾零碎,能让管理员通过后盾零碎的简略操作,就能批改网站出现的内容就好了。
本文就将率领你采纳一种新鲜的、便捷的开发方式——通过联合 CloudBase CMS 和 Next.js,去构建内容治理不便,利于 SEO 且响应疾速的站点。
CMS 是云开发 CloudBase 推出的一款无头(headless)内容管理系统,提供给开发者不便地治理内容资源的能力。所谓无头,意思就是内容管理系统只负责管理你的内容,比方文章内容和作者列表。这些内容能够在客户端或者服务端通过 SDK 或者 API 的形式去获得。而如何去展现这些内容,则由开发者自行全权掌控。
Next.js 是一款生产级的 React 框架,它提供了动态生成的性能。动态生成的意思是,在构建的过程中,Next.js 就会主动执行数据拉取的逻辑,并把数据和 UI 渲染为一个个的动态 HTML 页面,这意味着,咱们的站点将响应迅速,而且利于 SEO。
本文将通过率领你构建一个集体博客,以展现如何联合这两者,并最终在腾讯云云开发上部署站点。
Demo在线预览:
开启环境和我的项目
1. 开明 CloudBase CMS
首先,你要在腾讯云控制台开明你的第一个云开发环境。云开发环境是云开发中的一个概念,每个云环境都集成了利用开发须要的根底能力,比方云数据库、云函数,开发者能够不便地组合、应用它们,为利用开发赋能。TCB-CMS 也是建设在云环境之上的。
创立环境时,你能够间接抉择空模板并勾选免费资源选项即可,最初将环境命名为 my-blog。
能够看到,环境曾经在创立中了。
环境创立结束后,进入扩大利用模块,能够看到“CMS内容管理系统”,能够在这里装置它。设置都依照默认就能够了,惟一要留神的是,务必记住本人设置的管理员账号和明码。
等装置结束后,能够在已装置利用一栏中进入利用。点击拜访地址后,咱们能够间接拜访利用。进入利用并输出管理员账号和明码,而后能够看到以下页面:
当初,零碎中还没有任何我的项目,点击创立一个名为 MyBlog 的新我的项目,创立结束后进入我的项目,能够看到内容模型和内容汇合,拿数据库作类比,这两者就是数据库表和数据库表内容的关系,这两者就是咱们要设置和治理的内容。曾经有筹备好的数据能够间接导入,别离就在 我的项目源码仓库 中的 ./schema 和 ./data 文件夹中。点击导入按钮,而后抉择导入文件即可。
好了,当初 CMS 曾经胜利开明了,咱们也往其中退出了内容。接下来,就能够着手 Next.js 利用的编写了。
2. 启动 Next.js 我的项目
Next.js 是构建于 React 之上的生产级前端框架。相比于本来的 React,Next.js 提供了动态生成、服务端渲染等个性,同时自带前端路由,咱们这次就次要用到 Next.js 的动态生成性能。应用 Next.js 编写前端利用,和应用 create-react-app 脚手架编写 SPA 利用十分相似,而且更加便捷、开箱即用。
间接在命令行执行以下命令能够创立 Next.js 的样板我的项目并启动它:
npx create-next-appnpm run dev
拜访 localhost:3000 能够看到以下成果:
接下来,咱们就将基于这个样板我的项目开发网站。
首页
这里,咱们将在首页搁置文章列表。
首先,关上我的项目下的 ./pages/index.js,发现页面导出了一个函数式的 React 组件。在 Next.js 中,pages 目录下,除了 api 文件夹下的内容和 _app.js,其余每个 js 文件导出的 React 组件都对应着一个或者一种页面,并且由 Next.js 间接生成对应的路由,index.js 导出的函数式组件就间接对应着咱们进入网站后的第一个页面,而其余 js 文件于 ./pages 的绝对地址就是 Next.js 为其主动生成的路由。
Next.js 在利用构建期,就会对每个页面执行数据拉取的逻辑,并依据 React 组件构建的 UI,渲染出最初的 HTML 页面,接下来,咱们要做的就是,构建主页的 UI,以及为主页编写拉取数据的逻辑。
UI 编写
接下来对主页的 UI 进行批改:
import Head from 'next/head'import Link from 'next/link'import styles from '../styles/Home.module.css'export default function Home({ posts }) { return ( <div className={styles.container}> <Head> <title>云开发小站</title> <link rel="icon" href="https://main.qcloudimg.com/raw/3b942431a6ef465d3b8369969e861c0f/favicon.png" /> </Head> <main className={styles.main}> <h1 className={styles.title}> Welcome to <a href="https://www.cloudbase.net/">云开发 CloudBase!</a> </h1> <div className={styles.grid}> {posts.map(post => ( <Link href={`/post/${post._id}`}> <a href="https://nextjs.org/docs" className={styles.card}> <h3>{post.title} →</h3> <p>{post.author}</p> </a> </Link> ))} </div> </main> <footer className={styles.footer}> <a href="https://www.cloudbase.net/" target="_blank" rel="noopener noreferrer" > Powered by{' '} <img src="https://main.qcloudimg.com/raw/3b942431a6ef465d3b8369969e861c0f/favicon.png" alt="TCB Logo" className={styles.logo} /> </a> </footer> </div> )}
能够看到,批改后的 Home 组件,承受了一个 posts 为参数,这个 post 就是文章列表数据,咱们将它在组件中渲染进去。
那么 post 从哪里来呢?在同一个 js 文件下,须要再导出一个 getStaticProps 函数。
export async function getStaticProps() { return { props: undefined, }}
这个函数返回的对象的 props 属性,就是导出的函数式组件用到的参数。所以,只须要在 getStaticProps 函数中失去数据并返回即可。
拉取数据
先装置拉取数据要用到的 SDK:
npm install --save @cloudbase/node-sdk
而后,咱们再创立 env.js 文件,在其中填入云环境相干信息:
export const tcbConfig = { env: '', secretId: '', secretKey: ''};
其中环境ID(env)能够间接在环境主页中看到,API 密钥(secretId,secretKey)则能够在 拜访治理 中获取。
最初,咱们创立 ./lib/api.js,而后填入以下内容,将数据拉取的逻辑全副集中在这个文件中。
import tcb from '@cloudbase/node-sdk';import { tcbConfig } from '../env';const { Author, Article } = (() => { const db = tcb.init(tcbConfig).database(); return { Author: db.collection('author'), Article: db.collection('article'), };})();export const getHomePosts = async () => { const posts = (await Article .where({}) .orderBy('_updateTime', 'desc') .limit(10) .get()).data; for (const post of posts) { const { name } = (await Author .where({ _id: post.author }) .get()).data[0]; post.author = name; } return { posts }};
在 api.js 中,依据环境信息,对 SDK 进行了初始化,并创立了用于查问文章和作者的实例 Author 和 Ariticle。在 getHomePosts 函数中,咱们获取了展现用的文章。具体的逻辑如果不懂也临时不用深究,当初只须要晓得:通过执行 getHomePosts 咱们能从云环境的 CMS 零碎中拉取文章列表。
接着,再来批改 ./pages/index.js 文件:
import Head from 'next/head'import Link from 'next/link'import styles from '../styles/Home.module.css'import { getHomePosts } from '../lib/api'...export async function getStaticProps() { return { props: await getHomePosts() };}
而后,再拜访 localhost:3000,能够看到如下成果:
这标记着:咱们胜利从 CMS 中获取数据并可能渲染出动态页面来返回给客户端啦!
文章页面
接下来,就要着手编写文章页面了,根本流程差不多,但值得注意的是,文章页面和主页不同,只有一个主页,然而文章页面可能有无数个,而Next.js 提供了能力,能让咱们只编写一个 js 文件,并加以轻微的改变,就能渲染出有数的文章页面 。
拉取用以渲染页面的文章内容
先筹备好须要的款式。首先创立 ./styles/Post.module.css 文件,具体款式内容能够参考:https://github.com/LRCong/nextjs-tcbcms-app/blob/main/styles/Post.module.css
接着,创立 ./pages/post/[id].js 文件,这个文件对应的,就是路由形如 /post/{id} 的所有页面,而 id 的作用就是匹配文章的 _id。这样通过拜访 URL:/post/{id1}.js,就能拜访到文章id等于 id1 的文章对应页面。
咱们先往 [id].js 文件中填入以下的内容:
import Head from 'next/head';import Link from 'next/link';import styles from '../../styles/Post.module.css';export default function Post({ title, image, author, avator, contentHtml}) { return ( <div className={styles.container}> <Head> <title>{title}</title> <link rel="icon" href="/favicon.png" /> </Head> <main className={styles.main}> <div className={styles.info_container}> <img className={styles.image} src={image}></img> <div className={styles.info}> <h1 className={styles.title}>{title}</h1> <div className={styles.author_info}> <div className={styles.avator} style={{ backgroundImage: `url(${avator})` }}></div> <h2>{author}</h2> </div> </div> </div> <div className={styles.markdown} dangerouslySetInnerHTML={{ __html: contentHtml }} /> <Link href='/'><h3 className={styles.back}>返回</h3></Link> </main> <footer className={styles.footer}> <a href="https://www.cloudbase.net/" target="_blank" rel="noopener noreferrer" > Powered by{' '} <img src="https://main.qcloudimg.com/raw/3b942431a6ef465d3b8369969e861c0f/favicon.png" alt="TCB Logo" className={styles.logo} /> </a> </footer> </div> )}export async function getStaticPaths() { return { paths: undefined, fallback: false };}export async function getStaticProps({ params }) { return { props: undefined };}
能够看到,相比 index.js,[id].js 多出了一个 getStaticProps 函数,getStaticProps 也多了一个 parms 参数。getStaticProps 函数临时不必管,而 param.id 就是在路由中匹配到的 id,能够借助它,执行获取对应文章内容的逻辑。
在 api.js 中,增加以下内容:
// 对于 image 类型的字段,间接获得的 id 须要转换为可用的 URLconst dealWithUrl = url => 'https://' + url .replace(`cloud://${tcbConfig.env}.`, '') .replace('/cloudbase-cms', '.tcb.qcloud.la/cloudbase-cms');export const getPost = async (id) => { const post = (await Article.where({ _id: id }).get()).data[0]; const { name, avator } = (await Author .where({ _id: post.author }) .get()).data[0]; post.author = name; post.avator = dealWithUrl(avator); post.image = dealWithUrl(post.image); return post;};
而后装置 remark 以及 remark-html 两个库,咱们将用它们把 markdown 转化为 html,而后批改 [id].js 文件中的 getStaticProps 为
import Head from 'next/head';import Link from 'next/link';import styles from '../../styles/Post.module.css';import { getPost } from '../../lib/api';import remark from 'remark'import html from 'remark-html'...export async function getStaticProps({ params }) { const post = await getPost(params.id); const processedContent = await remark() .use(html) .process(post.content) post.contentHtml = processedContent.toString() return { props: post };}
拉取所有的文章id以渲染所有文章页面
只到这一步还不够,咱们须要晓得所有的路由可能匹配到的 id 值,Next.js 能力渲染出全副的文章页面。[id].js 多出的 getStaticPaths 函数正是用来返回 id 所有可能的匹配值的。
这就是咱们只须要编写一次拉取文章数据逻辑,编写一次文章页面 UI,就能让 Next.js 生成出有数文章的动态页面的神秘。因为,能够让 Next.js 晓得所有的文章 id,而后 Next.js 就能对每个文章页面执行一次生成了。
批改往 api.js 中增加获取所有文章 id 的函数:
export const getAllPostId = async () => { let posts = (await Article.where({}).get()).data; return posts.map(value => ({ params: { id: value._id } }))};
而后批改 getStaticPaths 函数:
export async function getStaticPaths() { return { paths: await getAllPostId(), fallback: false };}
而后,拜访首页,轻易进入一篇文章,就能够看到如下成果:
到这里,咱们就胜利实现 Next.js 我的项目的构建啦!
部署
应用腾讯云云开发,你能够轻易地将利用部署到公共网络上。
咱们先批改 package.json 中依赖库的配置,因为云开发环境对于依赖版本有肯定限度:
"dependencies": { "@cloudbase/node-sdk": "^2.5.1", "next": "9.5.4", "react": "16.13.1", "react-dom": "16.13.1", "remark": "^13.0.0", "remark-html": "^13.0.1"}
而后,创立部署的配置文件 cloudbaserc.json,并填入以下内容:
{ "envId": "{{env}}", "version": "2.0", "$schema": "https://framework-1258016615.tcloudbaseapp.com/schema/latest.json", "functionRoot": "./functions", "functions": [], "region": "ap-shanghai", "framework": { "name": "tcbcms-nextjs", "plugins": { "client": { "use": "@cloudbase/framework-plugin-next", "inputs": {} } } }}
其中的 env 换成你刚刚创立的云环境的 ID。
批改结束后,执行命令:
cloudbase
能够看到部署流程启动,期待到部署结束后,进入云环境的“我的利用”模块,会发现利用列表多了一个“tcbcms-nextjs”,点击拜访键,就能拜访刚刚创立的利用,而且是通过公网 IP,这阐明咱们的利用曾经部署胜利了。
总结
到此,咱们的博客曾经胜利创立并部署了。当前如果博客中要增加新文章,或者要删改原有的文章,都只须要在 CMS 上进行内容的改变,而后在本地执行 Next.js 的构建和云开发部署即可。
更多 Next.js 和云开发相干常识,能够查看官网文档:
Next.js 官网文档:https://nextjs.org/docs/getting-started
CloudBase CMS 官网文档:https://docs.cloudbase.net/cms/intro.html
@cloudbase/node-sdk 官网文档:https://docs.cloudbase.net/api-reference/server/node-sdk/introduction.html
彩蛋:云开发是如何撑持利用开发的?
你可能会好奇云环境的能力是如何撑持咱们的零碎的。
再次进入腾讯云控制台,进入刚刚创立的云环境主页,进入HTTP拜访服务、云数据库以及云函数模块,会发现多了许多货色。实际上,CMS 零碎就是由这些货色撑持着的。
咱们每次拜访 CMS 零碎并操作,都会经由 HTTP拜访服务,导向某个云函数,在云函数中执行后盾逻辑,而零碎中的数据,也都存储在云数据库中,这也是咱们能够通过 @cloudbase/node-sdk 拜访云数据库失去 CMS 中的数据的起因。
而咱们刚刚部署的 Next 利用,实际上也是运行在云函数上的。
如果有趣味在云开发中更进一步,欢送返回云开发社区官网 cloudbase.net 浏览文档,退出技术交换群,一起摸索云开发的更多可能性。
产品介绍
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为开发者提供高可用、主动弹性扩缩的后端云服务,蕴含计算、存储、托管等serverless化能力,可用于云端一体化开发多种端利用(小程序,公众号,Web 利用,Flutter 客户端等),帮忙开发者对立构建和治理后端服务和云资源,防止了利用开发过程中繁琐的服务器搭建及运维,开发者能够专一于业务逻辑的实现,开发门槛更低,效率更高。
开明云开发:https://console.cloud.tencent.com/tcb?tdl_anchor=techsite
产品文档:https://cloud.tencent.com/product/tcb?from=12763
技术文档:https://cloudbase.net?from=10004
技术交换群、最新资讯关注微信公众号【腾讯云云开发】