Next.js 实战项目 | 青训营笔记

128 阅读5分钟

这是我参与「第五届青训营 」笔记创作活动的第8天

CSR,SSR,SSG

CSR

客户端渲染(Client-Side Rendering)。常见B端WEB应用开发模式,前后端分离,服务器压力相对更轻,渲染工作在客户端进行,服务器直接返回不加工的HTML用户在后续访问操作

SSR

服务器端渲染(Server-Side Rendering)。代码耦合度高,且模板语言中混杂编程语言对于一些复杂的功能,维护起来很痛苦。这种模式下JAVA,PHP负责渲染的逻辑,而前端只负责UI和交互。

SSG

静态站点生成(Static Site Generation),在构建的时候直接把结果页面输出html到磁盘,每次访问直接把html返回给客户端,相当于一个静态资源

image.png 相比SSR,因为不需要每次请求都由服务器端处理,所以可以大幅减轻服务器端的压力,缺陷在于只能用于偏静态的页面,无法生成用户相关的内容,也就是所有的用户访问的页面都是相同的。

SSR,SSG的优势

  • 利于SEO
    • 浏览器的推广程度,取决于搜索引擎对站点检索的排名,搜索引擎可以理解是一种爬虫,他会爬取指定页面的HTML,并根据用户输入的关键词对页面内容进行排序检索,最后形成我们看到的结果
  • 更短的首屏时间
    • SSR/SSG只需要请求一个HTML文件就能展现出页面,虽然在服务器上会调取接口,但服务器之间的通信要远比客户端快,甚至同一台服务器上的本地接口调取。因为不再需要请求大量js文件,这就使得SSR/SSG可以拥有更短的首屏时间

image.png

什么是Next.js

Next.js是一个构建与Node.js之上的开源Web开发框架,支持基于React的Web应用程序功能,例如服务端渲染和生成静态网站。 image.png

Next.js客户端开发

我们可以使用getServerSideProps、getStaticProps、getInitialProps、客户端注入进行数据注入

getInitialProps

在服务器端执行,只能在页面层面进行绑定、采用同构、首次渲染服务器端渲染、路由跳转使用客户端路由,意味着如果使用router跳转当前页,会在客户端执行这部分逻辑。

getServerSideProps

SSR,与getInitialProps不同的是即使使用router跳转当前页,也只会在服务端执行这部分逻辑。

getStaticProps

SSG,在服务器端构建时执行,如果涉及动态路由,需要使用getStaticPaths配置所有可能的参数情况

文件式路由

Next.js有一个基于页面概念的基于文件系统的路由器,当一个文件被添加到pages目录中时,它会自动作为一个路径可用

路由跳转

next/link跳转

import Link from "next/link";
<Link href = {item.link} key = {index}>
    <div className = {styles.card}></div>
    <p>{item.info}</p>
</Link>

useRouter跳转

import {useRouter} from 'next/router';
function ActiveLink({children,href}){
    const router = useRouter()
const style = {
    marginRight:10,
    color:router.asPath === href?'red':'black';
}
const handleClick = (e)=>{
    e.preventDeafult()
    router.push(href)
}
return(
    <a href = {href} onClick = {handleClick} style = {style}>{children}</a>
)
}

header的修改

可用于修改TDK(title,description,keywords)

多媒体适配

css适配

// 极小分辨率移动端设备
@mixin media-mini-mobile {
  @media screen and (max-width: 25.875rem) {
    @content;
  }
}

// 介于极小分辨率和正常分辨率之间的移动端设备
@mixin media-between-mini-and-normal-mobile {
  @media screen and (min-width: 25.876rem) and (max-width: 47.9375rem) {
    @content;
  }
}

// 移动端设备
@mixin media-mobile {
  @media screen and (max-width: 47.9375rem) {
    @content;
  }
}

// ipad
@mixin media-ipad {
  @media screen and (min-width: 47.9375rem) and (max-width: 75rem) {
    @content;
  }
}

JS适配

export const UserAgentProvider = ({ children }: IProps): JSX.Element => {
  const [userAgent, setUserAgent] = useState<Environment>(Environment.none); // 服务器渲染初始化渲染未必是预期效果,none缓冲切换视觉)

  // 监听本地缓存来同步不同页面间的主题(当前页面无法监听到,直接在顶部栏进行了类的切换)
  useEffect(() => {
    const checkUserAgent = (): void => {
      const width = document.body.offsetWidth;
      // 用宽度去判断,是为了适配不改机型,仅拉扯屏幕宽度的情况
      if (width < 768) {
        // 手机端
        setUserAgent(Environment.mobile);
      } else if (width >= 768 && width < 1200) {
        // ipad端
        setUserAgent(Environment.ipad);
      } else if (width >= 1200) {
        // pc端
        setUserAgent(Environment.pc);
      } else {
        setUserAgent(Environment.none); // 增加none类型来缓冲默认类型样式切换时的视觉突变
      }
    };
    checkUserAgent();
    window.addEventListener('resize', checkUserAgent); // 监听屏幕宽度变化,及时适配当前页面样式
    return (): void => {
      window.removeEventListener('resize', checkUserAgent);
    };
  }, [typeof document !== 'undefined' && document.body.offsetWidth]);

  return <UserAgentContext.Provider value={{ userAgent }}>{children}</UserAgentContext.Provider>;
};

大图优化 - webp

当网速较慢大图片加载慢的时候。我们可以使用webp格式来压缩图片体积。 我们可以使用png to webp网站来转换格式。

但是webp存在兼容性问题,以下这种情况才支持webp格式

image.png

因此我们可以使用一下代码判断其兼容性

export const getIsSupportWebp = (context: AppContext) => {
  const { headers = {} } = context.ctx.req || {};
  return headers.accept?.includes("image/webp");
};

Next.js服务端开发

CMS全称是Content Management System,即内容管理系统。我们可以利用CMS存储添加数据,帮助我们快速创建API

image.png

核心功能讲解

image.png

主题化功能实现

先往localStorage注入主题,其中checkTheme()实现多进程间的主题同步。

 // 监听本地缓存来同步不同页面间的主题
  useEffect(() => {
    const checkTheme = (): void => {
      const item = (localStorage.getItem('theme') as Themes) || Themes.light;
      setTheme(item);
      document.getElementsByTagName('html')[0].dataset.theme = item;
    };
    checkTheme();
    window.addEventListener('storage', checkTheme);
    return (): void => {
      window.removeEventListener('storage', checkTheme);
    };
  }, []);

利用sass在全局变量设置主题

html[data-theme="dark"] {
  --primary-color: #ffffff;
  --primary-background-color: rgba(14, 14, 14, 1);
  --footer-background-color: rgba(36, 36, 36, 1);
  --navbar-background-color: rgba(0, 0, 0, 0.5);
  --secondary-color: rgba(255, 255, 255, 0.5);
  --link-color: #34a8eb;
  --semi-page-active-color: rgb(84, 169, 255);
  --semi-page-active-background-color: rgba(84, 169, 255, 0.2);
  --semi-page-hover-background-color: rgb(23, 23, 23);
  --navbar-icon: url("../public/logo_dark.png");
  --theme-icon: url("../public/theme_dark.png");
  --popup-close-icon: url("../public/close.png");
  --popup-close-hover-background-color: #353535;
  --popup-content-background-color: #1f1f1f;
  --home-background-icon: url("../public/home_bg_dark.png");
  --home-background-icon-webp: url("../public/home_bg_dark.webp");
}

html[data-theme="light"] {
  --primary-color: #333333;
  --primary-background-color: rgba(255, 255, 255, 1);
  --footer-background-color: #f4f5f5;
  --navbar-background-color: rgba(255, 255, 255, 0.5);
  --secondary-color: #666666;
  --link-color: #0070f3;
  --semi-page-active-color: #333333;
  --semi-page-active-background-color: rgb(234, 245, 255);
  --semi-page-hover-background-color: rgb(244, 245, 245);
  --navbar-icon: url("../public/logo_light.png");
  --theme-icon: url("../public/theme_light.png");
  --popup-close-icon: url("../public/close_light.png");
  --popup-close-hover-background-color: #f5f5f5;
  --popup-content-background-color: #f4f5f5;
  --home-background-icon: url("../public/home_bg_light.png");
  --home-background-icon-webp: url("../public/home_bg_light.webp");
}