当前位置: 首页 > 科技观察

窃取Next.js:我学到的6个设计技巧

时间:2023-03-20 10:14:09 科技观察

转载本文请联系前端公众号向后。这篇文章记录了我发现的设计技巧,包括API设计、文档设计、框架设计等,也分享给大家定义基类。它可能不如定义模块。首先,类(Class)和模块(Module)都是可以组织代码方法的选择,放在API设计的场景中,可以用来约束编写方法和暴露框架的能力。在模块的概念成为正统之前,大多数前端框架都提供了基类来满足这种需求,因为没有典型的选择。React通过React.Component基类暴露了各种生命周期钩子,同时定义了组件编写方法://ComponentsclassClockextendsReact.Component{//Propsconstructor(props){super(props);//Statethis.state={date:newDate()};}//LifecyclecomponentDidMount(){}componentWillUnmount(){}render(){//Templatereturn(

Hello,world!

是{this.state.date.toLocaleTimeString()}.

);}将Props、State、Lifecycle、Template等框架能力集成到一个Class中,称为组件。并且,长期以来,React中唯一可以称为组件的就是Class。这段时间有多长?从React开始,到ReactHooks的推出,并演变成一个完整的形式。目前(2021/1/2)ReactHooks还不是很完整,componentDidCatch、getSnapshotBeforeUpdate、getDerivedStateFromError等功能还不完善,请看DoHookscoverallusecasesforclasses?也就是说,即使在今天,ReactComponents仍然等同于ClassComponents,早期的函数式组件只能称为StatelessComponents。Hooks加持后的函数式组件虽然摆脱了Stateless,但与完整形态的ClassComponents还是有一点差距。组件和类的概念紧密结合在一起。多么糟糕的选择,而期望很高的Hooks,就充分说明了这一点。但是,Props、State、Lifecycle、Template等框架能力总是需要一些东西来承载。那么,什么是更好的选择呢?可能是模块。之所以强调这种可能性,是因为Module仅在组织代码方面比Class更纯粹。Module只是组织代码并圈出变量和函数等语法元素,不像Class,它强加了实例状态和成员方法等额外的概念。比如Next.js的Page定义就是一个文件模块://pages/about。jsfunctionAbout(){return
About
}exportdefaultAbout是最简单的Page,只要默认暴露一个React组件即可。需要使用更多的功能,然后根据需要暴露更多已建立的API://pages/blog.jsfunctionBlog({posts}){//Renderposts...}//API1exportasyncfunctiongetStaticProps(){}//API2exportasyncfunctiongetStaticPaths(){}//API3exportasyncfunctiongetServerSideProps(){}//APInexportasyncfunctionxxx(){}exportdefaultBlog与Class风格的API设计相比,这种Module风格的API设计更加纯粹,没有强加额外的语法元素(尤其是Class,基础庞大,与有很多super(),bind(this),static),在某些场景下是更好的选择。文件约定路由Next.js()中没有Router.register,没有newRoute(),也没有app.use,没有你能想到的路由定义API,因为根本没有API,而路由使用文件路径约定://静态路由pages/index.js→/pages/blog/index.js→/blogpages/blog/first-post.js→/blog/first-postpages/dashboard/settings/username.js→/dashboard/settings/username//动态路由页面/blog/[slug].js→/blog/:slug(/blog/hello-world)pages/[username]/settings.js→/:username/settings(/foo/settings)pages/post/[...all].js→/post/*(/post/2020/id/title)也就是说路由是通过源代码所在的文件路径来标识的定位,甚至支持通配符。太神奇了。当然要看源码目录才能感受视觉冲击:pages├──_app.js├──_document.tsx├──api│├──collection││├──[id]。tsx││└──index.tsx│├──照片│├──[id].tsx│├──下载││└──[id].tsx│└──index.tsx│├──stats│└──index.tsx│└──user│└──index.tsx├──collection│└──[slug].tsx└──index.tsxAPI无缝联动在前两篇文章中,我们知道Next.js要解决的问题就是预渲染。围绕预渲染,我们探索了SSG和SSR两种渲染模式,并在此基础上支持包括CSR在内的不同渲染模式的混合:SSGDowngradeSSR:当预生成的静态HTML遗漏时,立即执行SSRSSRwithstaticcaching:SSR完成后缓存并下载结果命中静态缓存直接返回(相当于SSG)SSG结合CSR:编译时生成静态部分(页面框架),CSR填充动态部分(页面内容)SSR联动CSR:URL直接访问更快的SSR,SPA跳转来体验更好的CSR从API设计的角度来说,在乍一看,似乎需要给每个组合起一个唯一的名字,并暴露一个专用的API,像SSGwithFallback、SSRwithStaticCache、PartialSSG、SPAMode……然而,Next.js不仅支持所有这些混合功能,但也没有添加任何顶级API。它的做法是添加一些选项,例如://SSG基础模型exportasyncfunctiongetStaticProps(context){return{props:{},//willbepassedtothepagecomponentasprops}}//SSG变成一个ISR,在返回值exportasyncfunctiongetStaticProps中添加revalidate属性(context){return{props:{},//willbepassedtothepagecomponentasprops//Next.jswillattempttore-generatethepage://-Whenarequestcomesin//-Atmostonceeverysecondrevalidate:1,//Inseconds}}//SSG感知路由的高级版本,实现getStaticPathsexportasyncfunctiongetStaticPaths(){return{paths:[{params:{...}}//见下面的“路径”部分],fallback:false};}//SSG转为带静态缓存的SSR,fallback选项改为trueexportasyncfunctiongetStaticPaths(){return{paths:[{params:{...}}//见下文“paths”部分],fallback:true};}//SSG转换为SSG并降级SSR,fallback选项改为'blocking'exportasyncfunctiongetStaticPaths(){return{paths:[{params:{...}}//Seethe"paths"sectionbelow],fallback:'blocking'};}这种基于细分选项的API联动使用起来更轻便,始终为用户保持渐进的运动感。你不需要从顶层了解所有的API和相关的设计概念来区分我的场景和顶层。应该使用哪种类型,使用哪种API,但是随着场景的深入,最合适的API/选项就在那里。从文档中可以明显感受到这种差异。例如,当Next.js引入ISR时,用户Directs到与静态缓存模式关联的SSR:IncrementalStaticRegeneration使用getStaticProps你不必停止对动态内容的依赖,因为静态内容也可以是动态的。增量静态再生允许您通过在背景中重新呈现它们来更新现有页面ndastrafficcomesin.这与fallback:true完美配合。因为现在您可以拥有一个始终与最新帖子保持同步的帖子列表,并拥有一个可以按需生成博客帖子的博客帖子页面,无论您添加或更新了多少帖子。积分,互动新手教程算作文档设计技巧(文档,当然也要有设计)。看了很多官方文档/教程,印象深刻的只有3个:ReduxDocumentation:Story文档,一步步设计redux,看完就停不下来。ElectronDemoApp:交互式文档,准确的说是一个文档完整的demo。在体验demoapp的同时了解相关功能的使用,比React更好。这是一种更懒惰的边做边学的方法。Next.js教程:积分,互动新手教程,几十页教程一口气搞定。附言Redux文档指的是2017版本。(这么小的概念怎么能编出那么多文档)积分和互动新手教程到底有多厉害?它让我在犯困迷茫的时候坚持阅读教程的全部内容,并正确回答所有试题。满500分(当然不用幻想了,没事就没有奖励),回想起来简直不可思议。诀窍在于:教程和文档分离:导航栏一级菜单明确区分Docs和Learn,教程的部分概念链接到文档,即使不看也能跟上不要完全阅读它。积分:教程显示在顶部醒目位置即可获得积分,每点一分都会加分。互动:重点章节有试题,答对加分。总积分可以社交分享从平台(Twitter)的角度来看,将少量在线教育融入文档的成熟模式可能会非常有效。默认情况下,它提供最佳实践的阅读体验技术和好的产品。Yubo默认的易用性给我留下了深刻的印象。而Next.js是一个真实的案例,在框架设计中默认容易上手。例如:Link自动预加载Image并自动延迟加载。动作与前两者不同,强调从框架的角度响应用户的点播特性,由框架决定渲染模式(SSR或SSG),无需用户显式指定/切换.从生产活动的角度来看,应该默认已经提供了最佳实践,新的最佳实践会不断下沉到环境层,比如npmpackage、ESModule、Babel等,现在的前端开发者几乎不需要关心关于这些前者的最佳实践只是从框架设计的角度。默认使用方便,需要在提供最佳实践的基础上更进一步。最好的做法应该做,让用户可以偷懒,认为一切都应该是这样的因此,最佳实践只是暂时的状态,尚未形成最佳实践的部分才是开发者需要关心和体现差异化竞争力的部分。一旦形成了广泛认可的最佳实践,就应该沉淀下来,成为默认的基础设施,开发者不需要关心这些最佳实践带来的各种好处。从尚未形成最佳实践,到提供最佳实践,再到默认提供最佳实践,这三个阶段可以通过图片延迟加载的例子来说明理解://第一阶段:最佳实践尚未形成scrollIntersectionObserver//业务单独实现,没有使用示例//第二阶段:提供最佳实践ReactLazyLoadComponent//使用示例//第三阶段:默认提供最佳实践next/image//使用示例第三阶段和第二阶段的区别是开发者不需要关心哪个组件可以提供懒加载功能(选择最佳实践),直接使用最常用的Image组件组件库中,需要的功能自然是应有尽有le,而延迟加载只是Serverless的扩展之一。在Serverless的浪潮下,前端生态也在发生着一些变化,各种融合层出不穷。集成应用:以前端项目/后端项目为主体的集成应用:如MidwayServerless,支持React、Vue等前端项目的集成。以SSR为主体的集成应用:如Next.js,支持集成SSR,数据接口(API端点)部署为ServerlessFunctionsNext.js提供SSR支持,需要服务端环境。Serverless的兴起解决了SSR渲染服务的运维问题。因此其Vercel平台默认支持以ServerlessFunctions的形式部署SSR。服务和API:使用Serv的页面二端渲染和API路由将自动成为隔离的ServerlessFunctions。这允许页面呈现和API请求无限扩展。虽然这样的集成应用还没有形成最佳实践,但传统的前端框架正在发生变化。或许,在未来的某一天,它会被一个完全融合Serverless技术的集成应用框架所取代,至于Universal系统会不会流行起来还不得而知。原文链接:https://mp.weixin.qq.com/s/F_4yg-0hsX0PSQ1oEOopZg