1。上面写的异步组件其实和异步请求数据是一样的,只不过是通过异步加载来加载和渲染的。异步组件的作用是什么?可用于代码拆分、服务端组件交付等场景。函数式组件其实就是让普通的函数来定义组件,将函数的返回值作为组件渲染的内容。功能组件最大的特点就是无状态。2异步组件要解决的问题同步渲染:importAppfrom"App.vue";createApp(App).mount("#app");异步渲染:constloader=()=>import("App.vue");loader().then(App=>{createApp(App).mount("#app");})在上面的代码中,通过动态导入加载组件,并返回一个Promise实例。组件加载成功后,会调用createApp函数完成挂载,从而实现异步渲染。问题多多的小明会问:上面的代码并没有实现整个页面的异步渲染,只是需要渲染页面的一部分,那么应该怎么处理呢?答案是:只需要实现部分组件的异步加载即可。上面代码中,CompA是一个同步加载的组件,CompB是一个异步加载的组件,通过动态组件绑定变量渲染。对于异步组件,一些异步问题应该和异步请求数据一样处理:组件加载失败或加载超时,显示错误组件组件加载时,显示加载组件或占位符组件加载超时显示加载组件组件可以发出请求后组件加载失败Retry三、异步组件的实现原理封装defineAsyncComponent接口在上一节的代码中,使用异步加载的组件并不容易。为了降低复杂度,封装了defineAsyncComponent接口。defineAsyncComponent函数是一个高阶函数,输入输出都是组件,输出的返回值就是封装好的组件。函数defineAsyncComponent(loader){letInnerComp=null;return{name:"AsyncComponentWrapper",setup(){//异步加载成功的标识符constloaded=ref(false);loader().then(comp=>{InnerComp=comp;//加载成功后loader.value=true;});返回()=>{返回loaded.value?{type:InnerComp}:{type:Text,children:""}}}}}上面的代码,defineAsyncComponent函数会根据loader加载器的状态来决定渲染内容。如果组件加载成功,则渲染加载的组件,否则显示占位符内容。超时处理和error组件异步加载组件异步请求数据是一样的。可能会出现弱网加载时间过长的情况。为此,需要在组件加载时间超过指定时间后触发超时错误。constAsyncComp=defineAsyncComponent({loader:()=>import("CompA"),timeout:2000,//ms//出错时渲染的组件errorComponent:MyErrorComponent});functiondefineAsyncComponent(options){//格式化配置项if(typeofoptions==="function"){options={loader:options}}const{loader}=optionsletInnerComp=null;return{name:"AsyncComponentWrapper",setup(){//异步加载成功Identifierconstloaded=ref(false);//存储错误对象consterror=shallowRef(null);loader().then(comp=>{InnerComp=comp;//加载成功后loader.value=true;})//在loading.catch(err=>error.value=err);中捕获错误;让定时器=空;if(options.timeout){timer=setTimeout(()=>{consterr=newError("异步组件将在${options.timeout}ms后加载超时");error.value=err;},options.timeout);}//占位符内容constplaceholder={type:Text,children:""}返回()=>{if(loaded.value){return{type:InnerComp}}elseif(error.value&&options.errorComponent){return{type:options.errorComponent,props:{error:error.value}}}else{返回占位符;}}}}}在上面的代码中,loader添加了一个catch来捕获加载错误,在加载超时后创建一个新的error对象,只要有error的值就赋值给error.value变量.value存在且配置了errorComponent,将直接渲染errorComponent组件并将error.value的值作为组件的props传递。这样就可以通过在自定义的Error组件上定义名为error的propr来接收error对象。延迟与Loading组件相同。众所周知,异步组件和异步请求会受到网络的影响。为此进行超时和错误处理。在加载过程中,可以设置Loading组件来提供更好的用户体验。Loading的显示时机是什么时候,如何控制它的显示和隐藏,可以给这个加上一个延迟时间,直到loading超过指定的时间才会显示Loading组件。constAsyncComp=defineAsyncComponent({loader:()=>newPromise(res=>/*...*/),delay:200,//ms//加载要渲染的组件loadingComponent:{setup(){return()=>{return{type:"h2",children:"Loading..."}}}}});上面代码中,delay用于指定显示Loading组件的延迟时间,loadingComponent用于配置Loading组件。functiondefineAsyncComponent(options){//格式化配置项if(typeofoptions==="function"){options={loader:options}}const{loader}=optionsletInnerComp=null;return{name:"AsyncComponentWrapper",setup(){//异步加载成功的标识符constloaded=ref(false);//存储错误对象consterror=shallowRef(null);常量加载=参考(假);让loadingTimer=null;if(options.delay){loadingTimer=setTimeout(()=>{loading.value=true;},options.delay)}else{loading.value=true}loader().then(comp=>{InnerComp=comp;//加载成功后loader.value=true;})//捕获加载错误.catch(err=>error.value=err).finally(()=>{loading.value=false;clearTimeout(loadingTimer);})让计时器=null;if(options.timeout){timer=setTimeout(()=>{consterr=newError("异步组件将在${options.timeout}ms后超时");error.value=错误;},options.timeout);}//Constplaceholder={type:Text,children:""}return()=>{if(loaded.value){return{type:InnerComp}}elseif(error.value&&options.errorComponent){return{type:options.errorComponent,props:{错误:错误。value}}}elseif(loading.value&&options.loadingComponent){return{type:options.loadingComponent}}else{返回占位符;}}}}}在上面的代码中,其实就是通过设置一个loading.value变量来标识是否正在加载。如果设置了延迟时间,时间到后设置loading.value=true。否则直接设置。为了避免内存泄漏,无论组件是否加载成功都需要清零定时器,避免Loading组件的加载。后面也有展示。当然,异步组件加载成功后,不仅需要清空定时器,还需要卸载Loading组件。为此,需要修改卸载函数:functionunmount(vnode){if(vnode.type==="string"){vnode.children.forEach(comp=>unmount(comp));返回;}elseif(typeofvnode.type==="object"){unmount(vnode.component.subtree);返回}constparent=vnode.el.parentVNode;如果(父母){parent.removeChild(vnode.el);}}重试机制重试是指当发生加载错误时,可以重新发起加载组件的请求,提供开箱即用的重试机制对于使用者来说是非常必要的。在组件加载错误的情况下,可以向用户提供重试或直接抛出异常的请求。functiondefineAsyncComponent(options){//省略代码//记录重试次数letretires=0;functionload(){returnloader().catch(err=>{if(options.onError){returnnewPromise((resolve,reject)=>{//重试constretry=()=>{resolve(load());retires++;}//failconstfail=()=>{reject(err)}options.onError(retry,fail,retries);})}else{throwerr}})}//省略部分code}4.函数式组件函数式组件本质上返回的是普通的函数,其值为虚拟DOM,因为函数式组件的简单性,所以Vue.js3使用了函数式组件。功能组件本身没有状态,而是从外部传递的道具。为此,需要将静态道具属性添加到函数中。functionMyFunctionComp(props){return{type:"h1",children:prop.name}}//定义propsMyFunctionComp.props={title:String}在有状态组件的基础上,只有挂载组件的逻辑可以被复用mountComponent函数,并支持patch函数内部函数类型的vnode.type。函数补丁(n1,n2,容器,锚点){如果(n1&&n1.type!==n2.type){卸载(n1);n1=空;}const{type}=n2;if(typeoftype==="string"){//...普通元素}elseif(typeoftype===Text){//...textnode}elseif(typeoftype===Fragement){//...fragment}elseif(typeoftype==="object"||//Statefulcomponenttypeoftype==="function"//Statelesscomponent){//vnode.type的值是一个选项对象,作为组件处理if(!n1){//挂载组件mountComponent(n2,container,anchor);}else{//更新组件patchComponent(n1,n2,anchor);}}}mountComponent函数代码:functionmountComponent(vnode,container,anchor){constisFunctional=typeofvnode.type==="function";让componentOptions=vnode.type;if(isFunctional){componentOptions={render:vnode.type,props:vnode.type.props}}}inmountComponentfunction检查组件类型是函数还是对象。对于函数,直接作为组件选项对象的渲染选项,组件函数props作为组件的props。5.写在最后这篇文章主要介绍了异步组件要解决的几个问题,这些问题如何解决,如何设计实现?Vue.js3中提供了异步组件,同时介绍了异步组件的加载超时问题、异常处理和加载、请求重试等,还讨论了功能组件的实现逻辑。