偷师Next.js:我学到的6个设计技巧

本文转载自微信公众号「前端向后」,作者黯羽轻扬 。转载本文请联系前端向后公众号。    

本文记录了我从中发现的设计技巧,包括 API 设计、文档设计、框架设计等,也分享给你

定义基类,可能不如定义模块

首先,类(Class)和模块(Module)都是组织代码的可选方式,放到 API 设计的场景,都能用来约束写法,暴露框架能力。而在模块概念成为正统之前,前端框架大多提供基类来满足这种需要,因为没得选

典型的,React 通过React.Component基类暴露出各种生命周期 Hook,同时定义了组件写法:

 
 
 
  1. // Components 
  2. class Clock extends React.Component { 
  3.   // Props 
  4.   constructor(props) { 
  5.     super(props); 
  6.     // State 
  7.     this.state = {date: new Date()}; 
  8.   } 
  9.  
  10.   // Lifecycle 
  11.   componentDidMount() { } 
  12.   componentWillUnmount() { } 
  13.  
  14.   render() { 
  15.     // Template 
  16.     return ( 
  17.       
     
  18.         

    Hello, world!

     
  19.         

    It is {this.state.date.toLocaleTimeString()}.

     
  20.       
 
  •     ); 
  •   } 
  • 将 Props、State、Lifecycle、Template 等框架能力整合成一个 Class,称之为组件。并且,在很长的一段时间里,React 中能称为组件的只有 Class

    这段很长的时间有多长?

    从 React 诞生之初一直到React Hooks推出并进化成完全形态。目前(2021/1/2)React Hooks 仍然不是完全形态,componentDidCatch、getSnapshotBeforeUpdate、getDerivedStateFromError等特性还不健全,具体见Do Hooks cover all use cases for classes?

    也就是说,时至今日,React Components 仍等价于 Class Components,早期的函数式组件只能叫 Stateless Components,获得 Hooks 加持之后的函数式组件虽然摆脱了 Stateless,但与完全形态的 Class Components 还有一点点差距

    将 Components 概念与 Class 强绑定在一起真是个糟糕的选择,被寄予厚望的 Hooks 充分说明了这一点。但 Props、State、Lifecycle、Template 这些框架能力又总要有东西来承载,那么,更好的选择是什么呢?

    可能是 Module。强调可能,是因为仅在组织代码这一点上,Module 比 Class 更纯粹。Module 只组织代码,将变量、函数等语法元素圈在一起,而不像 Class 会强加实例状态、成员方法等额外概念

    例如,Next.js 的 Page 定义就只是个文件模块:

     
     
     
    1. // pages/about.js 
    2. function About() { 
    3.   return 
      About
       
    4.  
    5. export default About 

    最简单的 Page,只要默认暴露出一个 React 组件即可。需要用到更多功能,再按需暴露更多的既定 API:

     
     
     
    1. // pages/blog.js 
    2. function Blog({ posts }) { 
    3.   // Render posts... 
    4.  
    5. // API 1 
    6. export async function getStaticProps() { } 
    7. // API 2 
    8. export async function getStaticPaths() { } 
    9. // API 3 
    10. export async function getServerSideProps() { } 
    11. // API n 
    12. export async function xxx() { } 
    13.  
    14. export default Blog 

    对比 Class 形式的 API 设计,这种Module 式 API 设计更加纯粹,不强加额外语法元素(尤其是 Class 这种根基庞大的语法元素,带来一众super()、bind(this)、static),在某些场景下不失为一种更好的选择

    文件约定路由

    Next.js 里没有Router.register、没有new Route()、也没有app.use(),没有一切你能想到的路由定义 API

    因为根本没有 API,路由采用的是文件路径约定:

     
     
     
    1. // 静态路由 
    2. pages/index.js → / 
    3. pages/blog/index.js → /blog 
    4. pages/blog/first-post.js → /blog/first-post 
    5. pages/dashboard/settings/username.js → /dashboard/settings/username 
    6.  
    7. // 动态路由 
    8. pages/blog/[slug].js → /blog/:slug (/blog/hello-world) 
    9. pages/[username]/settings.js → /:username/settings (/foo/settings) 
    10. pages/post/[...all].js → /post/* (/post/2020/id/title) 

    也就是说,通过源码所在文件路径来标识路由,甚至还能支持通配符,这么神奇,当然要亲眼看看源码目录才能感受到视觉冲击:

     
     
     
    1. pages 
    2. ├── _app.js 
    3. ├── _document.tsx 
    4. ├── api 
    5. │   ├── collection 
    6. │   │   ├── [id].tsx 
    7. │   │   └── index.tsx 
    8. │   ├── photo 
    9. │   │   ├── [id].tsx 
    10. │   │   ├── download 
    11. │   │   │   └── [id].tsx 
    12. │   │   └── index.tsx 
    13. │   ├── stats 
    14. │   │   └── index.tsx 
    15. │   └── user 
    16. │       └── index.tsx 
    17. ├── collection 
    18. │   └── [slug].tsx 
    19. └── index.tsx 

    API 之间的无缝联动

    通过前两篇文章,我们知道 Next.js 要解决的问题是预渲染,围绕预渲染探索出了 SSG、SSR 两种渲染模式,并在此基础上支持了包括 CSR 在内的不同渲染模式混用:

    从 API 设计的角度乍一看,似乎需要给每种组合取个别致的名字,并暴露出专门的 API,就像 SSGwithFallback、SSRwithStaticCache、PartialSSG、SPAMode…

    然而,Next.js 不仅支持了所有这些混用特性,而且没有增加任何顶层 API,它的做法是增加一些选项,例如:

     
     
     
    1. // SSG 基础款 
    2. export async function getStaticProps(context) { 
    3.   return { 
    4.     props: {}, // will be passed to the page component as props 
    5.   } 
    6.  
    7. // SSG 变身 ISR,给返回值添上 revalidate 属性 
    8. export async function getStaticProps(context) { 
    9.   return { 
    10.     props: {}, // will be passed to the page component as props 
    11.  
    12.     // Next.js will attempt to re-generate the page: 
    13.     // - When a request comes in 
    14.     // - At most once every second 
    15.     revalidate: 1, // In seconds 
    16.   } 
    17.  
    18. // SSG 感知路由的高级款,实现了 getStaticPaths 
    19. export async function getStaticPaths() { 
    20.   return { 
    21.     paths: [ 
    22.       { params: { ... } } // See the "paths" section below 
    23.     ], 
    24.     fallback: false 
    25.   }; 
    26.  
    27. // SSG 变身 SSR带静态缓存,fallback选项改为true 
    28. export async function getStaticPaths() { 
    29.   return { 
    30.     paths: [ 
    31.       { params: { ... } } // See the "paths" section below 
    32.     ], 
    33.     fallback: true 
    34.   }; 
    35.  
    36. // SSG 变身 SSG降级SSR,fallback选项改为'blocking' 
    37. export async function getStaticPaths() { 
    38.   return { 
    39.     paths: [ 
    40.       { params: { ... } } // See the "paths" section below 
    41.     ], 
    42.     fallback: 'blocking' 
    43.   }; 

    这种基于细分选项的 API 联动用起来更轻量,始终保持带给用户的渐进式体感,不需要一上来就了解全部 API、相关设计概念,从顶层区分我的场景属于哪类,该用哪个 API,而是随着场景的深入,发现那个最合适的 API/选项就在那里

    能从文档够明显地感受到这种差异,例如,Next.js 介绍 ISR 的地方将用户指引到与之关联的 SSR 带静态缓存模式:

    Incremental Static Regeneration

    With getStaticProps you don’t have to stop relying on dynamic content, as static content can also be dynamic. Incremental Static Regeneration allows you to update existing pages by re-rendering them in the background as traffic comes in.

    This works perfectly with fallback: true. Because now you can have a list of posts that’s always up to date with the latest posts, and have a blog post page that generates blog posts on-demand, no matter how many posts you add or update.

    积分、互动式新手教程

    这一点算作文档设计技巧(文档,当然也要有设计),看过许多官方文档/教程,留下深刻印象的只有 3 个:

    P.S.Redux 文档指的是2017 年的版本,现在貌似改过许多版,读着很差劲了(这么点儿概念怎么能整出来那么多文档)

    积分、互动式新手教程威力大到什么程度?

    让我能在困到迷糊的状态下坚持看完教程的全部内容,答对所有测试题目,积满 500 分(当然,不用幻想,全对是没有任何奖励的),事后回想起来也觉得不可思议,其中的技巧在于:

    如此看来,在文档中融入少量在线教育的成熟模式,可能效果极佳

    默认提供最佳实践

    读过体验科技与好的产品,对其中玉伯提出的默认好用印象很深,而 Next.js 算是默认好用在框架设计上的一个真实案例

    例如:

    从生产活动的角度来看,最佳实践本就应该是默认提供的,将新出现的最佳实践不断地下沉到环境层,就像 npm package、ES Module、Babel 等,如今的前端开发者已经几乎不需要关心这些曾经的最佳实践

    仅从框架设计角度而言,默认好用要求在提供最佳实践的基础上更进一步,要把最佳实践做没,让使用者能够偷懒地以为一切本该如此。因此,最佳实践只是一个临时态,尚未形成最佳实践的部分才是开发者需要关心,并体现差异化竞争力的地方,一旦形成广泛认同的最佳实践,就应该沉淀成为默认的基础设施,开发者无需关心即可获得这些最佳实践带来的种种好处

    从尚未形成最佳实践,到提供最佳实践,到默认提供最佳实践,这 3 个阶段可以通过一个图片懒加载的示例来理解:

     
     
     
    1. // 第一阶段:尚未形成最佳实践 
    2. scroll 
    3. IntersectionObserver 
    4. // 业务各自实现,不存在用法示例 
    5.  
    6. // 第二阶段:提供最佳实践 
    7. React Lazy Load Component 
    8. // 用法示例 
    9.  
    10.    
    11.  
    12.  
    13. // 第三阶段:默认提供最佳实践 
    14. next/image 
    15. // 用法示例 
    16.   src="/me.png" 
    17.   alt="Picture of the author" 
    18.   layout="fill" 
    19. /> 

    第三阶段与第二阶段的区别在于,开发者不必关心哪个组件能够提供懒加载功能(选择最佳实践),直接使用组件库中最普通的 Image 组件,该有的功能自然就有,而懒加载只是其中一项

    向 Serverless 延伸

    Serverless 浪潮之下,前端生态也正在发生着一些变化,涌现出各式各样的一体化应用:

    Next.js 提供 SSR 支持,本就需要服务端环境,Serverless 的兴起很好地解决了 SSR 渲染服务的运维问题,因此,其 Vercel 平台默认支持以 Serverless Functions 的形式部署 SSR 服务与 API:

    Pages that use Server-Side Rendering and API routes will automatically become isolated Serverless Functions. This allows page rendering and API requests to scale infinitely.

    诸如此类的一体化应用虽未形成最佳实践,但传统的前端框架正在历经变革。也许,在未来的某一天,取而代之的是与 Serverless 技术充分融合的一体化应用框架,Universal 体系大行其道也未可知。

    原文链接:https://mp.weixin.qq.com/s/F_4yg-0hsX0PSQ1oEOopZg

    当前名称:偷师Next.js:我学到的6个设计技巧
    链接分享:http://www.shufengxianlan.com/qtweb/news40/430390.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

    猜你还喜欢下面的内容

    网站排名知识

    同城分类信息