前言据说setup是compositionapi带来的overview,compositionapi(组合api)和optionalapi(可选api)是两种组织代码的方法。vue3的各种相关介绍相信大家已经了解很多,它们可以同时存在,不强制只能使用其中之一,但是结合API的两个优点确实让开发者更倾向于使用它而不是可选的API。以功能为基本单元,将可复用逻辑封装并注入到任意组件中,使视图与服务的解耦更加优雅,将具有相同功能的服务更紧密地放在一起而不分离,提升开发和维护体验。points在react中被hooks优雅的解决了,那么组合的API相比于hooks还有哪些优势呢?相信有小伙伴已经知道,在你引入组合API的时候,组合API是静态定义的,解决了hook每次渲染都要重新生成临时闭包函数的性能问题,没有了钩子中闭包的旧值、依赖关系的手动检测和其他编码体验问题。但是react是一个allinjs的编码方式,所以只要敢想敢做,所有优秀的编程模型都可以吸收。接下来我们使用原生的hook和concentsetup,结合实例和讲解来彻底解决问题。说说hooks的痛点。^\_^reacthook这里设计一个传统的计数器,需要一个小数和一个大数,有两组加减按钮,分别对小数和大数进行操作,小数键加减1,大数键加减100。初次安装计数器时,拉出欢迎语。当小数点到达100时,按钮变为红色,否则变为绿色。当大数字达到1000时,按钮变为紫色。否则,它变成绿色。当大数达到10000时,卸载报大数的数字计算器时,报当前数。为了完成这个需求,我们需要使用下面5个钩子useState来完成需求。我们需要使用第一个hookuseState来初始化组件第一次渲染的状态functionCounter(){const[num,setNum]=useState(6);const[bigNum,setBigNum]=useState(120);}useCallback如果需要使用缓存功能,需要使用到第二个hookuseCallback,这里我们使用这个hook来定义加减函数constaddNum=useCallback(()=>setNum(num+1),[数量]);constaddNumBig=useCallback(()=>setBigNum(bigNum+100),[bigNum]);如果useMemo需要使用缓存的计算结果,则需要使用第三个hookuseMemo,这里我们使用这个hook来计算按钮颜色constnumBtnColor=useMemo(()=>{returnnum>100?'red':'green';},[数字]);constbigNumBtnColor=useMemo(()=>{returnbigNum>1000?'purple':'green';},[bigNum]);useEffect处理函数的sideeffect需要用到第四个hookuseEffect,这里我们用它来处理最大数量达到10时的两个需求000,当卸载报大数的数计算器时,报当前数useEffect(()=>{if(bigNum>10000)api.report('reach10000')},[bigNum]);useEffect(()=>{return()=>{api.reportStat(num,bigNum)}},[]);哎,写到这里,react的新手已经中招了,就是关闭旧值的陷阱,在卸载的那一刻提交了原值,这里清理函数的useEffect写法在IDE中也会被警告,因为内部使用了num和bigNum变量,所以需要声明依赖useRef但是如果为了避免IDE警告,改成下面的方法显然不是我们的本意,我们只是想在组件卸载的时候上报数量,而不是触发清理函数useEffect(()=>{return()=>{api.reportStat(num,bigNum)}},[num,bigNum])这个时候我们需要第五个hookuseRef来帮助我们修复依赖,所以正确的写法是constref=useRef();//ref是一个固定变量,每一轮渲染指向同一个值ref.current={num,bigNum};//帮助我们记住最新的值useEffect(()=>{return()=>{const{num,bigNum}=ref.current;reportStat(num,bigNum);};},[ref]);完整的计数器使用了5个钩子,我们完整的组件如下functionCounter(){const[num,setNum]=useState(88);const[bigNum,setBigNum]=useState(120);constaddNum=useCallback(()=>setNum(num+1),[num]);constaddNumBig=useCallback(()=>setBigNum(bigNum+100),[bigNum]);constnumBtnColor=useMemo(()=>{returnnum>100?"red":"green";},[num]);constbigNumBtnColor=useMemo(()=>{returnbigNum>1000?"purple":"green";},[bigNum]);useEffect(()=>{if(bigNum>10000)report("达到10000");},[bigNum]);constref=useRef();ref.current={num,bigNum};useEffect(()=>{return()=>{const{num,bigNum}=ref.current;reportStat(num,bigNum);};},[ref]);//renderui...}当然,我们可以根据钩子的可定制特性,将这段代码抽象成钩子。case,只需要Export数据和方法,让各种ui表达的Counter组件可以复用,同时把ui和业务隔离,方便维护functionuseMyCounter(){//....简写return{num,bigNum.addNum,addNumBig,numBtnColor,bigNumBtnColor}}concentsetuphook函数在每一轮渲染时都要重新执行,所以难免会在每一轮渲染时产生大量的临时闭包函数,如果能保存下来,确实可以帮助gc减轻一些回收压力,下面我们来看看setup改造后的Count,使用co很简单集中。你只需要使用runapi在根组件之前启动。因此,我们这里没有模块定义。直接调用即可。import{run}from'concent';run();//先开始,在renderReactDOM.render(,rootEl)然后我们稍微修改一下上面的逻辑,把它全部包在setup里面,setup里面的逻辑函数只会执行一次。需要用到的渲染上下文ctx提供的API有initState、computed、effect、setState。同时调用setState时需要读取的状态也是通过ctx获取的。functionsetup(ctx){//渲染上下文const{initState,computed,effect,state,setState}=ctx;//setup只在组件第一次渲染之前执行一次,我们可以在内部写相关的业务逻辑如何划分状态粒度。initState({num:6,bigNum:120});这里也支持函数式写法来初始化状态initState(()=>({num:6,bigNum:120}));computedComputed用于定义计算函数,从参数列表解构时确定计算的输入依赖,比useMemo更直接优雅。//这个计算函数只有在num变化的时候才会触发。computed('numBtnColor',({num})=>(num>100?'red':'green'));这里需要定义两个计算函数,可以使用计算对象描述体来配置计算函数,这样只需要调用一次computedcomputed({numBtnColor:({num})=>num>100?'red':'green',bigNumBtnColor:({bigNum})=>bigNum>1000?'purple':'green',});effect的用法和useEffect完全一样,不同的是依赖数组只能传入键名,effect内部会使用函数组件和类组件,生命周期统一封装,用户无需任何修改即可将业务迁移到类组件。effect(()=>{if(state.bigNum>10000)api.report('reach10000')},['bigNum'])effect(()=>{//这里可以写需要做什么当第一次渲染完成时return()=>{//卸载时触发清理函数api.reportStat(state.num,state.bigNum)}},[]);setState用于修改状态。在setup里面定义了基于setState的方法后,我们就可以返回它了。然后我们可以在任何使用这个setup的组件中通过ctx.settings获取这些方法句柄然后调用functionsetup(ctx){//renderingcontextconst{state,setState}=ctx;return{//导出方法addNum:()=>setState({num:state.num+1}),addNumBig:()=>setState({bigNum:state.bigNum+100}),}}完整的设置计数器基于以上api,我们最终的Counter逻辑代码如下functionsetup(ctx){//渲染上下文const{initState,computed,effect,state,setState}=ctx;//初始化数据initState({num:6,bigNum:120});//定义计算函数computed({//当解构参数列表时,计算输入依赖于numBtnColor:({num})=>num>100?'red':'green',bigNumBtnColor:({bigNum})=>bigNum>1000?'紫色':'绿色',});//定义副作用effect(()=>{if(state.bigNum>10000)api.report('reach10000')},['bigNum'])effect(()=>{return()=>{api.reportStat(state.num,state.bigNum)}},[]);return{//exportmethodaddNum:()=>setState({num:state.num+1}),addNumBig:()=>setState({bigNum:state.bigNum+100}),}}定义核心后业务逻辑,我们可以使用useConcent将任何函数组件setup里面定义的组装起来使用,useConcent会返回一个渲染上下文(和setup函数的参数列表中的对象引用一样,有时我们也称它为实例上下文),我们可以根据需要从ctx中获取目标数据和方法,对于本例,我们可以导出state(数据)、settings(setup包返回的方法)、refComputed(结果容器)这三个key实例的计算函数)供使用。import{useConcent}from'concent';functionNewCounter(){const{state,settings,refComputed}=useConcent(setup);//const{num,bigNum}=state;//const{addNum,addNumBig}=settings;//const{numBtnColor,bigNumBtnColor}=refComputed;}上面我们提到setup也可以组装成class组件,使用register即可。需要注意的是,组装好的类组件可以直接从this.ctx中获取,用于集中生成渲染上下文,同时this.state和this.ctx.state是等价的,this.setState和this.ctx。setState也是等价的,方便用户零代码改动访问concent。import{register}from'concent';@register(setup)classNewClsCounterextendsComponent{render(){const{state,settings,refComputed}=this.ctx;}}结语相比原生的hooks,setup将业务逻辑固定在只执行一次的函数里面,提供了更友好的api,完美兼容class组件和function组件,让用户摆脱了hook的使用规则(想想useEffect和useRef,是不是认识多了?知道成本了?),而不是把这些约束传递给用户,同时对gc也更加友好。相信大家已经默认了hook是react的一项重要发明,但其实它不是针对用户的,而是针对frameworks的,用户其实不需要去了解那些费脑筋的细节和规则,但是对于concent用户来说,他们只需要一个钩子打开一个入口,就可以在另一个空间实现所有的业务逻辑,而且这些逻辑也可以在类组件上复用。看一百遍,不如亲手过一遍。下面是两种写法的链接。如果您尝试,您必须有一些经验。原来的挂钩CountersetupCounter吸引了两个挂钩Counter。但是在Concent的开发模式下,setup不需要任何修改。它只需要预先声明一个模块,然后将模块注册为组件的一部分。这种丝滑的迁移过程可以让用户灵活应对各种复杂场景。import{run}from'concent';run({counter:{state:{num:88,bigNum:120},},//reducer:{...},//如果操作数据流比较复杂,你可以在此处添加业务推广})//对于功能组件useConcent({setup});//--->更改为useConcent({setup,module:'counter'})//对于功能组件@register({setup});//--->改为@register({setup,module:'counter'});sharedCounterOnemorething如果你有兴趣在concent中搭建一个管理站点,我们也提供了一个样例站点tntweb-admin供你参考。得益于wp2vite的支持,实现了可以通过vite或webpack在本地启动的双引擎驱动能力。只需要3步:gitclonegit@github.com:tnfe/tntweb-admin.gitnpminpmrunvite当然,如果你想使用webpack启动,可以使用npmrunstart来连接,不过还是推荐使用webpack构建上线,即npmrunbuild。除了双引擎驱动,tntweb-admin还内置了很多功能,比如实时主题换肤、页面标签、27种动态布局等,还在开发中,等我们的模板更多之后页面发布,微前端部署方式文档准备就绪,将尽快开放给开发者分享。TeamTNTWeb——腾讯新闻前端团队,TNTWeb致力于行业前沿技术的探索和团队成员个人能力的提升。针对前端开发者,我们整理了最新的小程序和web前端技术的优质内容,每周更新?,欢迎star,github地址:https://github.com/tnfe/TNT-每周