审校 | 孙淑娟 梁策
使用工具
Next.js - 用于生产的 React 框架,可以轻松地启动全栈应用程序。
Cosmic - 无头部 CMS工具,它实现了数据(内容)层的独立性,并使我们能够快速管理网站内容。
Sass - 一种稳定、强大的专业级 CSS 扩展语言。
相关资源
相关工具介绍
Next.js是一个完整的套件,用于构建超快的 React 应用程序。它对开发人员友好,使用轻松。随着Next.js 12.1的发布,性能优化、中间件、React 18 支持、按需 ISR、对 SWC 的扩展支持等新功能只会变得更好。
Cosmic是一款出色的无头 CMS,它使我们能够全面管理和存储网站内容和媒体,并进行快速更新。
探索 Next.js 的 4 个新杀手级功能并将其用于模板
先安装一个新的包含工具和配置的 Next.js应用程序。本教程中使用Node.js 12.22.0 或更高版本。
打开终端,输入:
npx create-next-app@latest nextjs-restaurant-website-cms
# or
yarn create next-app nextjs-restaurant-website-cms
安装依赖
cd nextjs-restaurant-website-cms
npm i
# or
cd nextjs-restaurant-website-cms
yarn
开始运行
npm run dev
# or
yarn dev
在浏览器中打开 *http://localhost:3000/*以查看主页。
1. Rust 编译器
Next.js 12 的关键特性之一是性能优化。为了提高性能,Next.js 用可扩展的 Rust 编译器替换了 Babel 编译器,并默认使用 Next.js 12 启用它,该编译器构建在SWC(Speedy Web Compiler)之上,它支持SWC。它可以将TypeScript和JavaScript转化为可以在旧浏览器上运行的 JavaScript 代码。
SWC 在单线程上比 Babel 快 20 倍,在四核上快 70 倍。
2. 中间件
这是最令人兴奋的功能之一。中间件使我们能够使用代码而不是配置。这意味着你可以在请求完成之前运行代码,并根据请求,你可以通过重写、重定向、添加标头、设置 Cookie 等来修改响应。通过中间件,你可以实现身份验证、机器人保护、重定向、重写、服务器端分析、日志记录和处理不受支持的浏览器等。
中间件被创建在 /pages/_middleware.ts ,它将在/pages目录中的所有路由上运行。_middleware.js文件长什么样?让我们以我们的模板为例。
// pages/_middleware.js
import { NextResponse } from 'next/server';
export async function middleware( request ) {
// create an instance of the class to access the public methods.
//This uses next(),
let response = NextResponse.next();
const country = request.geo.country || 'US';
const city = request.geo.city || 'San Francisco';
const region = request.geo.region || 'CA';
// get the cookies from the request
let cookieFromRequest = request.cookies['location-cookie'];
if(!cookieFromRequest) {
// set the `cookie`
response.cookie('location-cookie', `${country|city|region}`);
}
return response;
}
3.按需增量静态再生ISR
Next.js 公开了一个函数unstable_revalidate(),允许你使用getStaticProps重新授权各个页面。在getStaticProps中,你不需要指定 revalidate 来按需重新验证,只需要在unstable_revalidate()调用时按需重新验证页面。
// pages/api/revalidate.js
export default async function handler(req, res) {
try {
await res.unstable_revalidate('/menu/' + req.body.data.slug)
return res.json({ revalidated: true })
} catch (err) {
// If there was an error, Next.js will continue
// to show the last successfully generated page
return res.status(500).send('Error revalidating')
}
}
4. 使用 AVIF 实现更快的图像优化和更小的图像
内置的图像优化API已更新以支持与ISR页面相同的模式,即在后台提供过时的图像并重新验证。此外,它还支持 AVIF 图像,使图像比 WebP 小 20%。
此功能是可选的,在编辑图片配置的时候可以选择启用。在文件next.config.js中配置下面参数即可:
// next.config.js
const nextConfig = {
reactStrictMode: true,
images: {
formats: ['image/avif', 'image/webp'],
domains: ['imgix.cosmicjs.com'],
},
}
module.exports = nextConfig
Cosmic 特征概述
- 可定制的 API:用户自己定义 API 的 schema,models和 controllers。为方便起见, Cosmic 同时提供了REST 和 GraphQL API的方式。
- 快速且安全的内容管理系统和 API 工具包。
- Webhooks在你需要的任何地方回调,以获得你想要的功能,使用 Cosmic API 开箱即用。
- 包含Imgix集成,可让你为针对跨平台体验优化的动态应用程序进行强大的图像处理。
Cosmic 操作
第一步创建免费的 Cosmic 帐户。让我们选择“从头开始”(Start from scratch)选项。
现在让我们将内容放进groups,用Object Type来共享组里的内容。例如,部分名称、、简介和图片等具有类似属性的部分,这些模块希望得到复用以为不同部分创建内容。
创建Object Type并添加部分属性用来在“Content Model”中定义“Metafields”。
现在,你可以为部分创建一个Object Type模型,并且可以像这样填充内容。
以类似的方式,你可以按照当前的数据模型、架构设计定义模块并创建Object Type:
- Singleton 为一个单独的模型
- Multiple 为可重复使用的模型
是时候获取 Next.js 应用程序的值了
将 Cosmic 模块安装到 Next.js 应用程序中。
npm i cosmicjs
# or
yarn add cosmicjs
然后,转到 Cosmic 面板 Your Bucket > Settings > API Access并找到你的 Bucket slug 和 API 读取密钥。
将此 Bucket slug 和 API 读取密钥添加到你的 Next.js 应用程序.env中。
//.env
COSMIC_BUCKET_SLUG=your_cosmic_slug
COSMIC_READ_KEY=your_cosmic_read_key
要使用模板 UI,你需要在GitHub中将它克隆。打开终端,粘贴或键入此代码以安装所有依赖项,然后运行它。
git clone https://github.com/cosmicjs/nextjs-restaurant-website-cms.git
cd nextjs-restaurant-website-cms
npm install
#or
yarn install
npm run dev
#or
yarn dev
向我们之前在 Cosmic 面板中创建的函数getDataFromBucket请求,并按类型从 Cosmic 中获取我们创建的内容params。
// src/lib/api.js
import Cosmic from 'cosmicjs';
const BUCKET_SLUG = process.env.COSMIC_BUCKET_SLUG
const READ_KEY = process.env.COSMIC_READ_KEY
const bucket = Cosmic().bucket({
slug: BUCKET_SLUG,
read_key: READ_KEY,
});
export async function getDataFromBucket(preview) {
const params = {
type: 'header',
props: 'title,slug,metadata,created_at',
sort: '-created_at',
...(preview && { status: 'all' }),
}
const data = await bucket.getObjects(params)
return data.objects
}
显示我们的内容,将其与我们的 UI 集成,并将一些元素呈现到主页。为此,你需要将此添加到index.js。
// pages/index.js
import Head from 'next/head';
import Home from 'components/Home';
import Layout from 'components/Layout';
import Footer from 'components/Footer';
import AboutUs from 'components/AboutUs';
import SpacialMenu from 'components/Menu';
import Introduction from 'components/Introduction';
import VideoIntro from 'components/VideoIntro';
import Gallery from 'components/Gallery';
import Contacts from 'components/Contact';
import { getDataFromBucket } from 'lib/api';
import chooseByType from 'utils/chooseValueByType';
function Template({ data }) {
return (
<>
<Head>
<title>Next.js Restaurant CMS</title>
<meta name="description" content="Create template using cosmic.js CMS" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Layout navbar={chooseByType(data, 'navigation')}>
<Home info={chooseByType(data, 'header')}/>
<AboutUs info={chooseByType(data, 'about')}/>
<SpacialMenu info={[chooseByType(data, 'drink'), chooseByType(data, 'food')]}/>
<Introduction info={chooseByType(data, 'history')}/>
<Gallery info={[chooseByType(data, 'gallery'), chooseByType(data, 'food')]}/>
</Layout>
<Footer>
<VideoIntro url={chooseByType(data, 'video')}/>
<Contacts info={chooseByType(data, 'contact')}/>
</Footer>
</>
)
}
export async function getStaticProps({ preview }) {
const data = (await getDataFromBucket(preview)) || [];
return {
props: { data },
}
}
export default Template;
下面函数chooseByType将过滤我们在 Cosmic 面板中创建的 Object Type。(Slug)
(Slug)
// src/utils/chooseValueByType.js
const chooseByType = (data, slugName) => {
if( data && slugName ) {
const chooseBySlug = data?.filter(content => Object.values(content).includes(slugName));
return chooseBySlug ? chooseBySlug[0] : [];
}
}
export default chooseByType;
制作菜单项页面
在 Next.js 中,你可以创建动态路由,可以考虑用下面pages/menu/[slug].js页面来创建单个菜单项页面和动态路由:
// pages/menu/[slug].js
import Head from 'next/head';
import { useRouter } from 'next/router';
import Layout from 'components/Layout';
import Footer from 'components/Footer';
import Contacts from 'components/Contact';
import MenuIntro from 'components/MenuIntro';
import VideoIntro from 'components/VideoIntro';
import Gallery from 'components/Gallery';
import { getAllDataWithSlug,getDataFromBucket } from 'lib/api';
import chooseByType from 'utils/chooseValueByType';
function Menu({ data }) {
const {
query: {slug},
} = useRouter();
return (
<>
<Head>
<title>Next.js Restaurant CMS</title>
<meta name="description" content="Create template using cosmic.js CMS" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Layout navbar={chooseByType(data, 'navigation')}>
<MenuIntro info={[chooseByType(data, 'food'), chooseByType(data, 'drink')]} slug={slug} />
<Gallery info={[chooseByType(data, 'gallery'), chooseByType(data, 'food')]}/>
</Layout>
<Footer>
<VideoIntro url={chooseByType(data, 'sushi')}/>
<Contacts info={chooseByType(data, 'contact')}/>
</Footer>
</>
)
}
export async function getStaticProps({ params, preview = null }) {
const data = (await getDataFromBucket(preview)) || [];
return {
props: { data },
}
}
export async function getStaticPaths() {
const dataWithSlug = (await getAllDataWithSlug()) || [];
return {
paths: dataWithSlug.map((menu) => `/menu/${menu.slug}`),
fallback: true,
}
}
export default Menu;
该函数getServerSideProps用于每次调用此路由时从 Cosmic 获取数据。在pages/api/revalidate.js中,我们在unstable_revalidate()被调用时使用unstable_revalidate函数来按需重新验证页面。如果出现错误,Next.js 将继续显示最后成功生成的页面。
在Vercel上部署代码库后,你可以通过转到 Cosmic 面板并导航到Bucket Settings > Webhooks来启用内容更新的重新验证。编辑内容时要触发的事件是object.edited.published。Webhook URL 端点将如下所示:${YOUR_VERCEL_DEPLOYMENT_URL}/api/revalidate。
这也使得在创建或更新来自无头部的CMS 的内容时,你的网站更容易更新。
现在来测试一下,在 Cosmic 面板中编辑内容,并查看静态内容立即更新。
结论
现在,你已拥有一个动态的、可定制的、完全集成的模板,其中包含新的 Next.js 和 Cosmic 功能。你可以为其他类型的企业定制,并按照自己的喜好来使用。
译者介绍
吴河东,51CTO社区编辑,具有5年工作经验,从事电商相关IT工作。擅长后台开发,大数据,算法等。
原文Build a Production Ready Restaurant Website with Next.js 12 and Cosmic,作者:Naira Gezhoyan