Next.js 实战项目 | 青训营笔记
<style>.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rgba(37, 41, 51, 1) }.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { line-height: 1.5; margin-top: 35px; margin-bottom: 10px; padding-bottom: 5px }
.markdown-body h1 { font-size: 24px; line-height: 38px; margin-bottom: 5px }
.markdown-body h2 { font-size: 22px; line-height: 34px; padding-bottom: 12px; border-bottom: 1px solid rgba(236, 236, 236, 1) }
.markdown-body h3 { font-size: 20px; line-height: 28px }
.markdown-body h4 { font-size: 18px; line-height: 26px }
.markdown-body h5 { font-size: 17px; line-height: 24px }
.markdown-body h6 { font-size: 16px; line-height: 24px }
.markdown-body p { line-height: inherit; margin-top: 22px; margin-bottom: 22px }
.markdown-body img { max-width: 100% }
.markdown-body hr { border-top: 1px solid rgba(221, 221, 221, 1); border-right: none; border-bottom: none; border-left: none; margin-top: 32px; margin-bottom: 32px }
.markdown-body code { border-radius: 2px; overflow-x: auto; background-color: rgba(255, 245, 245, 1); color: rgba(255, 80, 44, 1); font-size: 0.87em; padding: 0.065em 0.4em }
.markdown-body code, .markdown-body pre { font-family: Menlo, Monaco, Consolas, Courier New, monospace }
.markdown-body pre { overflow: auto; position: relative; line-height: 1.75 }
.markdown-body pre>code { font-size: 12px; padding: 15px 12px; margin: 0; word-break: normal; display: block; overflow-x: auto; color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.markdown-body a { text-decoration: none; color: rgba(2, 105, 200, 1); border-bottom: 1px solid rgba(209, 233, 255, 1) }
.markdown-body a:active, .markdown-body a:hover { color: rgba(39, 91, 140, 1) }
.markdown-body table { display: inline-block !important; font-size: 12px; width: auto; max-width: 100%; overflow: auto; border: 1px solid rgba(246, 246, 246, 1) }
.markdown-body thead { background: rgba(246, 246, 246, 1); color: rgba(0, 0, 0, 1); text-align: left }
.markdown-body tr:nth-child(2n) { background-color: rgba(252, 252, 252, 1) }
.markdown-body td, .markdown-body th { padding: 12px 7px; line-height: 24px }
.markdown-body td { min-width: 120px }
.markdown-body blockquote { color: rgba(102, 102, 102, 1); padding: 1px 23px; margin: 22px 0; border-left: 4px solid rgba(203, 203, 203, 1); background-color: rgba(248, 248, 248, 1) }
.markdown-body blockquote:after { display: block; content: "" }
.markdown-body blockquote>p { margin: 10px 0 }
.markdown-body ol, .markdown-body ul { padding-left: 28px }
.markdown-body ol li, .markdown-body ul li { margin-bottom: 0; list-style: inherit }
.markdown-body ol li .task-list-item, .markdown-body ul li .task-list-item { list-style: none }
.markdown-body ol li .task-list-item ol, .markdown-body ol li .task-list-item ul, .markdown-body ul li .task-list-item ol, .markdown-body ul li .task-list-item ul { margin-top: 0 }
.markdown-body ol ol, .markdown-body ol ul, .markdown-body ul ol, .markdown-body ul ul { margin-top: 3px }
.markdown-body ol li { padding-left: 6px }
.markdown-body .contains-task-list { padding-left: 0 }
.markdown-body .task-list-item { list-style: none }
@media (max-width: 720px) { .markdown-body h1 { font-size: 24px } .markdown-body h2 { font-size: 20px } .markdown-body h3 { font-size: 18px } }</style><style data-highlight="" data-highlight-key="juejin">.markdown-body pre, .markdown-body pre>code.hljs { color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.hljs-comment, .hljs-quote { color: rgba(153, 153, 136, 1); font-style: italic }
.hljs-keyword, .hljs-selector-tag, .hljs-subst { color: rgba(51, 51, 51, 1); font-weight: 700 }
.hljs-literal, .hljs-number, .hljs-tag .hljs-attr, .hljs-template-variable, .hljs-variable { color: rgba(0, 128, 128, 1) }
.hljs-doctag, .hljs-string { color: rgba(221, 17, 68, 1) }
.hljs-section, .hljs-selector-id, .hljs-title { color: rgba(153, 0, 0, 1); font-weight: 700 }
.hljs-subst { font-weight: 400 }
.hljs-class .hljs-title, .hljs-type { color: rgba(68, 85, 136, 1); font-weight: 700 }
.hljs-attribute, .hljs-name, .hljs-tag { color: rgba(0, 0, 128, 1); font-weight: 400 }
.hljs-link, .hljs-regexp { color: rgba(0, 153, 38, 1) }
.hljs-bullet, .hljs-symbol { color: rgba(153, 0, 115, 1) }
.hljs-built_in, .hljs-builtin-name { color: rgba(0, 134, 179, 1) }
.hljs-meta { color: rgba(153, 153, 153, 1); font-weight: 700 }
.hljs-deletion { background: rgba(255, 221, 221, 1) }
.hljs-addition { background: rgba(221, 255, 221, 1) }
.hljs-emphasis { font-style: italic }
.hljs-strong { font-weight: 700 }</style><p>这是我参与「第五届青训营」伴学笔记创作活动的第 8 天</p>
<h3 data-id="heading-0">0x1 CSR,SSR,SSG</h3>
<ol>
<li>
<p>CSR</p>
<p>客户端渲染(Client-Side Rendering)。常见 B 端 Web 应用开发模式,前后端分离,服务器压力相对更轻,渲染工作在客户端进行,服务器直接返回不加工的 HTML 用户在后续访问操作</p>
<p><strong>缺点</strong>:首屏时间长</p>
</li>
<li>
<p>SSR</p>
<p>服务端渲染(Server-Side Rendering)。JSP/PHP 已经体现了服务器端渲染,其代码耦合度高,且模板语言中混杂编程语言对于一些复杂的功能,<strong>维护起来很麻烦</strong>。在这种模式下,Java 和 PHP 负责渲染的逻辑,前端只负责 UI 和交互</p>
<ul>
<li>
<p><span id="user-content-ssrtg">同构 SSR</span></p>
<blockquote>
<p>BFF:Backend For Frontend,服务于前端应用的后端</p>
</blockquote>
<p>前后端一体化,一套 React 代码在服务器上运行一遍,到达浏览器又运行一遍。前后端都要参与渲染,而且首次渲染出的 HTML 要相同</p>
</li>
</ul>
</li>
<li>
<p>SSG</p>
<p>静态站点生成(Server-Side Generation),在构建时直接把结果页面输出 HTML 到磁盘,每次访问直接把 HTML 返回给客户端,相当于一个静态资源。相比 SSR,因为不需要每次请求都由服务器端处理,所以可以大幅减轻服务器端的压力</p>
<p><strong>缺点</strong>:只能用于偏静态的页面,无法生成与用户相关的内容(如,产品展示页面,与用户操作无关)</p>
<blockquote>
<p>CDN:建立并覆盖在 Internet 之上,由分布在不同区域边缘节点服务器群组成的<strong>分布式网络</strong></p>
</blockquote>
</li>
<li>
<p>SSR / SSG 的共同优势</p>
<ol>
<li>
<p>利于 SEO</p>
<p>浏览器的推广程度,取决于搜索引擎对站点检索的排名。搜索引擎可以理解是一种爬虫,他会爬取指定页面的 HTML,并根据用户输入的关键词对页面内容进行排序检索,最后形成结果并展现出来</p>
</li>
<li>
<p>更短的首屏时间</p>
<p>SSR / SSG 只需要请求一个 HTML 文件就能展现出一个页面,虽然在服务器上会调取端口,但服务器之间的通信比客户端快得多。同时不再请求大量 JS 文件</p>
</li>
</ol>
</li>
</ol>
<h3 data-id="heading-1">0x2 Next.js</h3>
<ol>
<li>
<p>Next.js 是一个构建于 Node.js 之上的开源 Web 开发框架,支持基于 React 的 Web 应用程序功能。具有上手速度快、能力集中全面以及覆盖足够多的性能优化和生态的特点,对前后端一体化的开发模式十分友好</p>
</li>
<li>
<p>SSR 的实现</p>
<p>SSR 核心重要概念之一:<strong>同构</strong></p>
<blockquote>
<p>Demo 仓库</p>
<p><code>./client</code>:客户端</p>
<p><code>./pages</code>:页面</p>
<p><code>./server</code>:服务端</p>
</blockquote>
<ul>
<li>
<p>在服务器端返回 HTML 模板页面时,会将一些初始化数据脱出来(<strong>脱水</strong>),如</p>
<pre><code class="hljs language-html code-block-extension-codeShowNum" lang="html"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-comment"><!-- ./server/index.tsx --></span></span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript"></span></span>
<span class="code-block-extension-codeLine" data-line-num="3"> <span class="hljs-variable language_">window</span>.<span class="hljs-property">context</span> = {</span>
<span class="code-block-extension-codeLine" data-line-num="4"> <span class="hljs-attr">state</span>: ${<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(serverStore.<span class="hljs-title function_">getState</span>())}</span>
<span class="code-block-extension-codeLine" data-line-num="5"> }</span>
<span class="code-block-extension-codeLine" data-line-num="6"><span class="hljs-tag"></<span class="hljs-name">script</span>></span></span>
</code></pre>
<p>当页面进入客户端进行渲染时,会将脱出的初始化数据重新注入(<strong>注水</strong>),如</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-comment">// ./pages/Demo/store/demoReducer.ts</span></span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-keyword">typeof</span> <span class="hljs-variable language_">window</span> !== <span class="hljs-string">'undefined'</span> ?</span>
<span class="code-block-extension-codeLine" data-line-num="3"> (<span class="hljs-variable language_">window</span> <span class="hljs-keyword">as</span> <span class="hljs-built_in">any</span>)?.<span class="hljs-property">context</span>?.<span class="hljs-property">state</span>?.<span class="hljs-property">demo</span> : {</span>
<span class="code-block-extension-codeLine" data-line-num="4"> <span class="hljs-attr">content</span>: <span class="hljs-string">'初始默认数据'</span></span>
<span class="code-block-extension-codeLine" data-line-num="5">}</span>
</code></pre>
<p>使浏览器端与服务器端达到<strong>同步</strong>的效果</p>
</li>
</ul>
</li>
</ol>
<h3 data-id="heading-2">0x3 Next.js 客户端开发</h3>
<ol>
<li>
<p>Demo 仓库</p>
<p>CMS 仓库地址</p>
<p>Demo 仓库地址</p>
</li>
<li>
<p>Next.js 项目初始化:<code>npx create-next-app@latest --typescript</code></p>
<blockquote>
<p>next-env.d.ts:确保 TypeScript 编译器选择 Next.js 类型,可以放到 <code>./.gitignore</code>中,无需变更</p>
<p>next.config.js:Next.js 的配置,可以补充 webpack 的一些配置</p>
</blockquote>
</li>
<li>
<p>数据注入</p>
<ol>
<li>
<p><code>getInitialProps</code></p>
<p>在服务器端执行,只在页面层进行绑定,采用同构,首次渲染服务器端渲染,路由跳转使用客户端路由</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-variable constant_">XXX</span>.<span class="hljs-property">getInitialProps</span> = <span class="hljs-keyword">async</span> (context) => {</span>
<span class="code-block-extension-codeLine" data-line-num="2"> <span class="hljs-keyword">const</span> { <span class="hljs-title class_">XXXId</span> } = context.<span class="hljs-property">query</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="3"> <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">get</span>(<span class="hljs-string">`<span class="hljs-subst">${LOCALDOMAIN}</span>/api/xxxInfo`</span>, {</span>
<span class="code-block-extension-codeLine" data-line-num="4"> <span class="hljs-attr">params</span>: {</span>
<span class="code-block-extension-codeLine" data-line-num="5"> <span class="hljs-title class_">XXXId</span>,</span>
<span class="code-block-extension-codeLine" data-line-num="6"> }</span>
<span class="code-block-extension-codeLine" data-line-num="7"> });</span>
<span class="code-block-extension-codeLine" data-line-num="8"> <span class="hljs-keyword">return</span> data;</span>
<span class="code-block-extension-codeLine" data-line-num="9">};</span>
</code></pre>
</li>
<li>
<p><code>getServerSideProps</code></p>
<p>SSR,与<code>getInitialProps</code>不同是即使使用 router 跳转当前页,也只会在服务端执行这部分逻辑</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title function_">getServerSideProps</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params">context</span>) => {</span>
<span class="code-block-extension-codeLine" data-line-num="2"> <span class="hljs-keyword">const</span> { <span class="hljs-title class_">XXXId</span> } = context.<span class="hljs-property">query</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="3"> <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">get</span>(<span class="hljs-string">`<span class="hljs-subst">${LOCALDOMAIN}</span>/api/xxxInfo`</span>, {</span>
<span class="code-block-extension-codeLine" data-line-num="4"> <span class="hljs-attr">params</span>: {</span>
<span class="code-block-extension-codeLine" data-line-num="5"> <span class="hljs-title class_">XXXId</span>,</span>
<span class="code-block-extension-codeLine" data-line-num="6"> }</span>
<span class="code-block-extension-codeLine" data-line-num="7"> });</span>
<span class="code-block-extension-codeLine" data-line-num="8"> <span class="hljs-keyword">return</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="9"> <span class="hljs-attr">props</span>: data, <span class="hljs-comment">// 需要使用 props 包裹</span></span>
<span class="code-block-extension-codeLine" data-line-num="10"> }</span>
<span class="code-block-extension-codeLine" data-line-num="11">}</span>
</code></pre>
</li>
<li>
<p><code>getStaticProps</code></p>
<p>SSG,在服务器端构建执行,若涉及动态带参路由,则需使用<code>getStaticPaths</code>配置所有可能的参数情况</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-attr">getStaticPaths</span>: <span class="hljs-title class_">GetStaticPaths</span> = <span class="hljs-keyword">async</span> () => {</span>
<span class="code-block-extension-codeLine" data-line-num="2"> <span class="hljs-keyword">return</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="3"> <span class="hljs-attr">paths</span>: [{ <span class="hljs-attr">params</span>: { <span class="hljs-title class_">XXXId</span>: <span class="hljs-string">"001"</span> } }],</span>
<span class="code-block-extension-codeLine" data-line-num="4"> <span class="hljs-attr">fallback</span>: <span class="hljs-literal">false</span>,</span>
<span class="code-block-extension-codeLine" data-line-num="5"> };</span>
<span class="code-block-extension-codeLine" data-line-num="6">};</span>
<span class="code-block-extension-codeLine" data-line-num="7"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-attr">getStaticProps</span>: <span class="hljs-title class_">GetStaticProps</span> = <span class="hljs-keyword">async</span> (context) => {</span>
<span class="code-block-extension-codeLine" data-line-num="8"> <span class="hljs-keyword">const</span> { <span class="hljs-title class_">XXXId</span> } = context.<span class="hljs-property">query</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="9"> <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">get</span>(<span class="hljs-string">`<span class="hljs-subst">${LOCALDOMAIN}</span>/api/xxxInfo`</span>, {</span>
<span class="code-block-extension-codeLine" data-line-num="10"> <span class="hljs-attr">params</span>: {</span>
<span class="code-block-extension-codeLine" data-line-num="11"> <span class="hljs-title class_">XXXId</span>,</span>
<span class="code-block-extension-codeLine" data-line-num="12"> }</span>
<span class="code-block-extension-codeLine" data-line-num="13"> });</span>
<span class="code-block-extension-codeLine" data-line-num="14"> <span class="hljs-keyword">return</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="15"> <span class="hljs-attr">props</span>: data,</span>
<span class="code-block-extension-codeLine" data-line-num="16"> }</span>
<span class="code-block-extension-codeLine" data-line-num="17">}</span>
</code></pre>
</li>
</ol>
</li>
<li>
<p>CSS Modules</p>
<p>Next.js 支持使用文件名约定的 CSS 模块,<code>.module.css</code>,具体操作如下:</p>
<ol>
<li>创建相关模块文件,如:<code>style.moudle.scss</code></li>
<li>在<code>.tsx</code>文件中引入,如:<code>import styles from './style.module.scss'</code></li>
<li>在需要的地方引用,如:<code><div className={styles.divOne}><h1 className={styles.title}></h1></div></code></li>
</ol>
</li>
<li>
<p>Layout</p>
<p>通过在入口文件导入 Layout,可以实现每个页面公共的页眉页尾</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">return</span> (</span>
<span class="code-block-extension-codeLine" data-line-num="2"> <span class="hljs-comment">// ...</span></span>
<span class="code-block-extension-codeLine" data-line-num="3"> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Layout</span> <span class="hljs-attr">navbarData</span> = <span class="hljs-string">{</span> <span class="hljs-attr">navbarData</span> } <span class="hljs-attr">footerData</span> = <span class="hljs-string">{</span> <span class="hljs-attr">footerData</span> }></span></span></span>
<span class="code-block-extension-codeLine" data-line-num="4"> // ...</span>
<span class="code-block-extension-codeLine" data-line-num="5"> <span class="hljs-tag"></<span class="hljs-name">Layout</span>></span></span>
<span class="code-block-extension-codeLine" data-line-num="6">)</span>
</code></pre>
</li>
<li>
<p>文件式路由</p>
<p>Next.js 有一个基于页面概念的基于文件系统的路由器。当一个文件被添加到 pages 目录中时,他会自动作为一个路径可用</p>
<ul>
<li>
<p>BFF 层的文件式路由</p>
<p>BFF 作为服务器构建包,不影响客户端构建 bundle 体积。相同的 router 生成方式,不过是作为 API 层访问,而不是 page</p>
</li>
</ul>
</li>
<li>
<p>路由跳转</p>
<ol>
<li>
<p>next / link 跳转</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">import</span> <span class="hljs-title class_">Link</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-comment">// ...</span></span>
<span class="code-block-extension-codeLine" data-line-num="3"><span class="hljs-keyword">return</span> (</span>
<span class="code-block-extension-codeLine" data-line-num="4"> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Link</span> <span class="hljs-attr">href</span> = <span class="hljs-string">{</span> <span class="hljs-attr">item.link</span> } <span class="hljs-attr">key</span> = <span class="hljs-string">{</span> <span class="hljs-attr">index</span> }></span></span></span>
<span class="code-block-extension-codeLine" data-line-num="5"> <span class="hljs-tag"></<span class="hljs-name">Link</span>></span></span>
<span class="code-block-extension-codeLine" data-line-num="6">)</span>
</code></pre>
</li>
<li>
<p>useRouter 跳转</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/router"</span></span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-comment">// ...</span></span>
<span class="code-block-extension-codeLine" data-line-num="3"><span class="hljs-keyword">const</span> router = <span class="hljs-title function_">userRouter</span>()</span>
</code></pre>
</li>
<li>
<p>原生方法跳转</p>
<p><strong>缺点</strong>:不会进行 diff 比对渲染,性能上 Next.js 提供的路由跳转更好</p>
</li>
</ol>
</li>
<li>
<p>header 的修改</p>
<p>可用于修改 TDK(Title,Description,Keywords)</p>
<pre><code class="hljs language-typescript code-block-extension-codeShowNum" lang="typescript"><span class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">return</span>(</span>
<span class="code-block-extension-codeLine" data-line-num="2"> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Head</span>></span></span></span>
<span class="code-block-extension-codeLine" data-line-num="3"> <span class="hljs-tag"><<span class="hljs-name">title</span>></span><span class="hljs-tag"></<span class="hljs-name">title</span>></span></span>
<span class="code-block-extension-codeLine" data-line-num="4"> <span class="hljs-tag"><<span class="hljs-name">meta</span> /></span></span>
<span class="code-block-extension-codeLine" data-line-num="5"> <span class="hljs-tag"></<span class="hljs-name">Head</span>></span></span>
<span class="code-block-extension-codeLine" data-line-num="6">)</span>
</code></pre>
</li>
<li>
<p>多媒体适配</p>
<blockquote>
<p>对于不同分辨率的屏幕进行适配</p>
</blockquote>
<ol>
<li>CSS 适配</li>
<li>JS 适配</li>
</ol>
</li>
<li>
<p>大图优化——webp</p>
</li>
</ol>
<h3 data-id="heading-3">0x4 Next.js 服务端开发</h3>
<ol>
<li>
<p>BFF 层开发</p>
<p>和 Express 等开发类似,区别在于并<strong>没有参数可以直接区别请求类型</strong></p>
</li>
<li>
<p>调试方式</p>
<ul>
<li>
<p>VSCode</p>
<p>在需要调试的位置添加<code>debugger;</code>,启动 JavaScript Debug Terminal,执行命令<code>npm run dev</code>即可</p>
</li>
<li>
<p>浏览器</p>
<p><code>npm run debugger</code></p>
</li>
</ul>
</li>
<li>
<p>Strapi —— headless CMS</p>
<blockquote>
<p>仓库:github.com/strapi/stra…</p>
<p>初始化:<code>npx create-strapi-app my-project --quickstart</code></p>
</blockquote>
<p>一个接口的生成过程如下:</p>
<ol>
<li><code>content-type builder</code>编辑结构体</li>
<li><code>content manager</code>配置数据源并发布</li>
<li><code>setting roles</code>里选择对应角色并勾选要发布的接口类型</li>
<li>若涉及嵌套,则在接口后添加<code>populate=deep</code>参数(<code>npm i strapi-plugin-populate-deep --save</code>),没安装加参数<code>populate=*</code>,但只能嵌套一层</li>
</ol>
</li>
</ol>
<h3 data-id="heading-4">0x5 核心功能讲解</h3>
<ol>
<li>
<p>首页功能实现</p>
<ul>
<li>页面、动画、多媒体适配、BFF、Strapi</li>
</ul>
</li>
<li>
<p>文章页实现</p>
<ul>
<li>
<p>页面、动画、多媒体适配、BFF</p>
</li>
<li>
<p>Strapi分页(/api/articles?pagination=1&pagination=10 // 1/10页)</p>
</li>
<li>
<p>多媒体格式转换</p>
<ul>
<li>markdown 转 html:<code>npm i showdown -s</code></li>
<li>html 转 dom:<code>dangerouslySetInnerHTML</code></li>
<li>公共样式的定义</li>
</ul>
</li>
</ul>
</li>
<li>
<p>主题化功能实现</p>
<ul>
<li>基础样式和背景的抽离</li>
<li>主题化 context 全局注入</li>
<li>从注入数据中取出 theme 和 setTheme</li>
<li>多进程间的主题同步</li>
</ul>
</li>
</ol><br><br>
来源:https://www.cnblogs.com/SRIGT/p/18265043
頁:
[1]