萧山知秋 發表於 2022-11-4 19:25:00

十分钟上手 Next.js

<h1 class="Post-Title">十分钟上手 Next.js</h1>
<div class="Post-Author">
<div class="AuthorInfo">
<div class="AuthorInfo">
<div class="css-1gomreu"><img src="https://pica.zhimg.com/v2-7fc3d5ed09d3768a98a519e659fa634f_l.jpg?source=172ae18b" srcset="https://pica.zhimg.com/v2-7fc3d5ed09d3768a98a519e659fa634f_l.jpg?source=172ae18b 2x" alt="写代码的海怪" class="Avatar AuthorInfo-avatar css-1y9jkzv"></div>
<div class="AuthorInfo-content">
<div class="AuthorInfo-head">
<div class="css-1gomreu">写代码的海怪</div>
<span class="UserLink AuthorInfo-name"><span class="css-18biwo">​</span></span></div>
<div class="AuthorInfo-detail">
<div class="AuthorInfo-badge">
<div class="ztext AuthorInfo-badgeText css-0">腾讯 前端工程师</div>
</div>
</div>
</div>
</div>
</div>
<button class="Button FollowButton Button--primary Button--blue" type="button">​关注他</button></div>
<div class="LabelContainer-wrapper">&nbsp;</div>
<div><span class="Voters"><button class="Button Button--plain" type="button">28 人赞同了该文章</button></span></div>
<div class="Post-RichTextContainer">
<div class="css-1yuhvjn">
<div class="RichText ztext Post-RichText css-4em6pe">
<h2 data-first-child="">前言</h2>
<p data-pid="EUfWdvJo">Next.js&nbsp;已经出来很久了,但是一直没机会看这个框架。</p>
<p data-pid="UIca7Ywg">以前一直在用&nbsp;create-react-app&nbsp;来创建 React 项目,奈何 CRA 实在太难用了,今天花了点时间扫了一下 Next.js 的官网,发现用起来还挺简单的。</p>
<h2>Next.js</h2>
<p data-pid="QiZ2z9qd">虽然 Next.js 总被人称为 SSR 框架,其实 Next.js 还提供了很多功能,比如官网列出来的这些:</p>
<img src="https://pic1.zhimg.com/80/v2-7df9fc5af7a0b1a7cefeede0f4a820c0_1440w.webp" width="1028" height="952" class="origin_image zh-lightbox-thumb lazy" data-caption="" data-size="normal" data-rawwidth="1028" data-rawheight="952" data-original="https://pic1.zhimg.com/v2-7df9fc5af7a0b1a7cefeede0f4a820c0_r.jpg" data-actualsrc="https://pic1.zhimg.com/v2-7df9fc5af7a0b1a7cefeede0f4a820c0_b.jpg" data-lazy-status="ok">
<p data-pid="PJzZydu0">所以说,Next.js 更像是一个框架,包含了路由、优化、SSR 等一系列功能。</p>
<h2>起步</h2>
<p data-pid="wfhey7jp">和&nbsp;create-react-app&nbsp;一样,Next.js 一样有个&nbsp;create-next-app&nbsp;的脚手架。</p>
<div class="highlight">
<pre><code class="language-text">create-next-app demo</code></pre>
</div>
<p data-pid="JaRiCVlP">使用上面命令后就可以创建一个 Next.js 框架的 React 项目。</p>
<h2>路由</h2>
<p data-pid="avTh1m6Z">Next.js 也提供了路由系统,采用名字约定路由</p>
<ul>
<li data-pid="5rlQN57H"><code>pages/index.js</code>&nbsp;对应&nbsp;<code>/</code></li>
<li data-pid="Kd1jgMrP"><code>pages/xxx/first.js</code>&nbsp;对应&nbsp;<code>/xxx/first</code></li>
</ul>
<p data-pid="MBsGKWTm">使用&nbsp;<code>Link</code>&nbsp;组件来做路由跳转</p>
<div class="highlight">
<pre><code class="language-js"><span class="kd">function <span class="nx">FirstPost<span class="p">() <span class="p">{
<span class="k">return <span class="p">(
    <span class="o">&lt;&gt;
      <span class="o">&lt;<span class="nx">h1<span class="o">&gt;<span class="nx">First <span class="nx">Post<span class="o">&lt;<span class="err">/h1&gt;
      <span class="o">&lt;<span class="nx">h2<span class="o">&gt;
      <span class="o">&lt;<span class="nx">Link <span class="nx">href<span class="o">=<span class="s2">"/"<span class="o">&gt;
          <span class="o">&lt;<span class="nx">a<span class="o">&gt;<span class="nx">Back <span class="nx">to <span class="nx">home<span class="o">&lt;<span class="err">/a&gt;
      <span class="o">&lt;<span class="err">/Link&gt;
      <span class="o">&lt;<span class="err">/h2&gt;
    <span class="o">&lt;<span class="err">/&gt;
<span class="p">)
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="yIBcN5pN">有的时候需要跳转到外链,可以使用&nbsp;<code>&lt;a&gt;</code>&nbsp;标签就可以。</p>
<h2>静态资源</h2>
<p data-pid="_bSfTYa4">静态资源用的最多的就是&nbsp;图片&nbsp;了,Next.js 提供了&nbsp;<code>Image</code>&nbsp;组件来替代&nbsp;<code>img</code>。</p>
<p data-pid="ecKO9GNd"><code>Image</code>&nbsp;组件的好处就是可以提高网页加载图片的性能,可以自动按需加载,当图片进入视图时再加载图片。</p>
<p data-pid="35zHRlZQ">除了相对路径引入,还可以将图片放在&nbsp;<code>public/images/</code>&nbsp;下,然后用 “绝对路径” 引入。</p>
<img src="https://pic2.zhimg.com/80/v2-e893e0930ac6aa07311aab109128a175_1440w.webp" width="142" height="89" class="content_image lazy" data-caption="" data-size="normal" data-rawwidth="142" data-rawheight="89" data-actualsrc="https://pic2.zhimg.com/v2-e893e0930ac6aa07311aab109128a175_b.png" data-lazy-status="ok">
<div class="highlight">
<pre><code class="language-js"><span class="kr">export <span class="k">default <span class="kd">function <span class="nx">Home<span class="p">() <span class="p">{
<span class="k">return <span class="p">(
    <span class="o">&lt;&gt;
      <span class="o">&lt;<span class="nx">h1<span class="o">&gt;<span class="nx">My <span class="nx">Homepage<span class="o">&lt;<span class="err">/h1&gt;
      <span class="o">&lt;<span class="nx">Image
      <span class="nx">src<span class="o">=<span class="s2">"/profile.jpg"
      <span class="nx">alt<span class="o">=<span class="s2">"Picture of the author"
      <span class="nx">width<span class="o">=<span class="p">{<span class="mi">500<span class="p">}
      <span class="nx">height<span class="o">=<span class="p">{<span class="mi">500<span class="p">}
      <span class="o">/&gt;
      <span class="o">&lt;<span class="nx">p<span class="o">&gt;<span class="nx">Welcome <span class="nx">to <span class="nx">my <span class="nx">homepage<span class="o">!&lt;<span class="err">/p&gt;
    <span class="o">&lt;<span class="err">/&gt;
<span class="p">)
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h2>MetaData</h2>
<p data-pid="b_wZ1rZW">网页的 Meta Data 主要是指&nbsp;<code>&lt;head&gt;</code>&nbsp;元素里的内容,Next.js 直接提供了一个&nbsp;<code>&lt;Head&gt;</code>&nbsp;组件来包裹这些 Meta Data。</p>
<div class="highlight">
<pre><code class="language-js"><span class="o">&lt;<span class="nx">Head<span class="o">&gt;
   <span class="o">&lt;<span class="nx">title<span class="o">&gt;<span class="nx">First <span class="nx">Post<span class="o">&lt;<span class="err">/title&gt;
<span class="o">&lt;<span class="err">/Head&gt;
</span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="AQCQrgeg">好处就是可以在不同的页面组件里写不同的 Meta Data。</p>
<h2>CSS</h2>
<p data-pid="DulBsyU-">样式这一块和&nbsp;<code>create-react-app</code>&nbsp;差不多,使用 CSS module,命名为&nbsp;<code>xxx.module.css</code>&nbsp;就可以了,否则别的 CSS 文件都需要&nbsp;<code>import 'xxx.css'</code>&nbsp;来引入 CSS 样式。</p>
<p data-pid="jhYw8ZyQ">需要注意的是全局样式引入只能在&nbsp;<code>pages/_app.js</code>&nbsp;的根文件里引入。</p>
<p data-pid="OJsBe2u-">上述操作 Sass 同理。</p>
<h2>预渲染</h2>
<p data-pid="oyW51KZB">终于来到 Next.js 最引以为豪的&nbsp;预渲染&nbsp;了。Next.js 提供了三种渲染方式:</p>
<ul>
<li data-pid="aXMbvWe8">Client-side Rendering (CSR)</li>
<li data-pid="DlYiyzbg">Static Generation (SSG)</li>
<li data-pid="JxFEErWI">Server-side Rendering (SSR)</li>
</ul>
<h3>Client-side Rendering</h3>
<p data-pid="4ELubEd7">客户端渲染其实就是我们经常看到的前后端分离的场景了:只提供一个 html,拿到&nbsp;<code>&lt;script&gt;</code>&nbsp;的 JS 再去渲染页面。</p>
<div class="highlight">
<pre><code class="language-js"><span class="kr">import <span class="nx">useSWR <span class="nx">from <span class="s1">'swr'

<span class="kd">function <span class="nx">Profile<span class="p">() <span class="p">{
<span class="kr">const <span class="p">{ <span class="nx">data<span class="p">, <span class="nx">error <span class="p">} <span class="o">= <span class="nx">useSWR<span class="p">(<span class="s1">'/api/user'<span class="p">, <span class="nx">fetch<span class="p">)

<span class="k">if <span class="p">(<span class="nx">error<span class="p">) <span class="k">return <span class="o">&lt;<span class="nx">div<span class="o">&gt;<span class="nx">failed <span class="nx">to <span class="nx">load<span class="o">&lt;<span class="err">/div&gt;
<span class="k">if <span class="p">(<span class="o">!<span class="nx">data<span class="p">) <span class="k">return <span class="o">&lt;<span class="nx">div<span class="o">&gt;<span class="nx">loading<span class="p">...<span class="o">&lt;<span class="err">/div&gt;
<span class="k">return <span class="o">&lt;<span class="nx">div<span class="o">&gt;<span class="nx">hello <span class="p">{<span class="nx">data<span class="p">.<span class="nx">name<span class="p">}<span class="o">!&lt;<span class="err">/div&gt;
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="nxNrjcGV">由于需要等加载到 JS 再渲染页面,所以这种渲染方式的有以下缺点:&nbsp;<em>SEO 不友好&nbsp;</em>白屏时间较长</p>
<p data-pid="JlYQrypb">聪明的前端程序员就想:当访问 URL 的时候,我直接把数据都放到 HTML 上,那爬虫就可以直接拿到页面的信息,解决 SEO 的问题了,同样的不需要等 JS 加载完再发 Ajax 获取数据了,基础数据优先展示,也就能解决白屏时间过长的问题了。</p>
<p data-pid="bYWh0nHe">所以,预渲染说的就是 SSG 和 SSR。</p>
<h3>Static Generation</h3>
<p data-pid="V4bb60Iw">Static Generation 会在&nbsp;build time 的 production&nbsp;时候直接将数据写在 HTML 上,所以一般来说这些数据都是以静态、固定为主。</p>
<div class="highlight">
<pre><code class="language-js"><span class="kr">export <span class="nx">async <span class="kd">function <span class="nx">getStaticProps<span class="p">() <span class="p">{
<span class="kr">const <span class="nx">allPostsData <span class="o">= <span class="nx">getSortedPostsData<span class="p">()
<span class="k">return <span class="p">{
    <span class="nx">props<span class="o">: <span class="p">{
      <span class="nx">allPostsData
    <span class="p">}
<span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="R3sOR98U">将&nbsp;<code>getStaticProps</code>&nbsp;这个函数&nbsp;<code>export</code>&nbsp;出来,里面则为 build time 时获取数据的逻辑。</p>
<h3>Server-side Rendering</h3>
<p data-pid="1zkpzNHo">通常情况下,我们很少使用静态的数据,一般以动态数据为主,不可能每次数据更新了又要打包一遍。所以就有了 Server-side Rendring。</p>
<p data-pid="xyrfvtCx">Server-side Rendering 则在每次&nbsp;请求这个 URL&nbsp;的时候,都会执行一次数据获取并生成 HTML 返回给前端。</p>
<blockquote data-pid="aIAhsa99">看到这里你可能会想 Next.js 和以前的 PHP、JSP 有什么区别么?都是吐 HTML 的呀。 Next.js 这里的 SSR 其实是同构渲染,即一套代码两端执行,具体区别请看这篇回答</blockquote>
<p data-pid="amUYmiBM">和 Static Generation 类似,Server-side Rendering 同样有一个对应的需要&nbsp;<code>export</code>&nbsp;出一个&nbsp;<code>getServerSideProps</code>&nbsp;函数。</p>
<div class="highlight">
<pre><code class="language-js"><span class="kr">export <span class="nx">async <span class="kd">function <span class="nx">getServerSideProps<span class="p">(<span class="nx">context<span class="p">) <span class="p">{
<span class="k">return <span class="p">{
    <span class="nx">props<span class="o">: <span class="p">{
      <span class="c1">// props for your component
<span class="c1">    <span class="p">}
<span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<h2>动态路由</h2>
<p data-pid="HYLO3xq4">所谓动态路由就是可以生成&nbsp;<code>posts/:id</code>&nbsp;这样的路由,<code>:id</code>&nbsp;可以为 post 的 id。文件命名只需要写成&nbsp;<code>.js</code>&nbsp;就可以了。</p>
<p data-pid="EPnO7p8p">在页面组件文件里,可以通过前面说到的&nbsp;<code>getStaticProps</code>&nbsp;和&nbsp;<code>getServerSideProps</code>&nbsp;获取 params:</p>
<div class="highlight">
<pre><code class="language-js"><span class="kr">export <span class="nx">async <span class="kd">function <span class="nx">getStaticProps<span class="p">({ <span class="nx">params <span class="p">}) <span class="p">{
<span class="kr">const <span class="nx">postData <span class="o">= <span class="nx">getPostData<span class="p">(<span class="nx">params<span class="p">.<span class="nx">id<span class="p">)
<span class="k">return <span class="p">{
    <span class="nx">props<span class="o">: <span class="p">{
      <span class="nx">postData
    <span class="p">}
<span class="p">}
<span class="p">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p data-pid="JfQ3lQ-_">其中,<code>pages/posts/[...id].js</code>&nbsp;会匹配&nbsp;<code>/posts/a/b</code>&nbsp;路由和&nbsp;<code>/posts/a</code>,<code>...</code>&nbsp;为全匹配。</p>
<h2>API</h2>
<p data-pid="RFsG5fhP">除了正常写页面外,有时候我们还需要提供 API 接口,可以在&nbsp;<code>pages/api</code>&nbsp;下添加文件,文件名则为 API 名。</p>
<p data-pid="JVg-naSU">注意:不能在&nbsp;<code>getStaticProps</code>&nbsp;和&nbsp;<code>getStaticPaths</code>&nbsp;里添加 fetch 数据,因为他们只在 server side 运行,不会在 client side 运行,应该使用 helper function 来获取数据。</p>
<p data-pid="g7SiYTwq">API 代码将不会在 client side 的 bundle 里。</p>
<h2>部署</h2>
<p data-pid="YedAv7Dw">部署这一块 Next.js 推荐使用&nbsp;Vercel&nbsp;来部署。</p>
<p data-pid="qYtUxSt1">因为 Vercel 本身就是为 Next.js 服务的,所以只需要连上 Github Repo 就可以一键部署了。</p>
<h2>总结</h2>
<p data-pid="LTPTbj4B">稍微总结一下,Next.js 提供的有如下功能:</p>
<ul>
<li data-pid="NmloY8Hn"><code>Link</code>&nbsp;组件,方便路由</li>
<li data-pid="wEf3W32W"><code>Image</code>&nbsp;组件,优化图片加载</li>
<li data-pid="_jpwiAyE">文件路径生成路由机制,动态路由使用&nbsp;<code>.js</code>&nbsp;这样的命令</li>
<li data-pid="DkUuMoWA">SSR、SSG 的同构开发模式(其实就是 export 一个对应名字的函数,在里面提前获取数据就好了)</li>
<li data-pid="WHb4N_9f">样式方面和&nbsp;<code>create-react-app</code>&nbsp;差不多</li>
</ul>
</div>
</div>
</div>

</div>
<div id="MySignature" role="contentinfo">
    漫思<br><br>
来源:https://www.cnblogs.com/sexintercourse/p/16858887.html
頁: [1]
查看完整版本: 十分钟上手 Next.js