异步组件
defineAsyncComponent
函数是一个高阶组件,他的返回值是一个包装组件。此包装组件会根据状态来决定渲染的内容,加载成功后渲染组件,在未渲染成功时渲染一个占位符节点
基本实现
let asyncComponent = defineAsyncComponent(()=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
render:()=>h('div','hi jiang')
});
}, 1000);
})
})
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
export function defineAsyncComponent(loader){
let Comp = null;
return {
setup(){
const loaded = ref(false)
loader().then(c=>{
Comp = c;
loaded.value = true;
})
return ()=>{
return loaded.value ? h(Comp):h(Fragment,'');
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
异步组件超时处理
let asyncComponent = defineAsyncComponent({
loader:()=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
render(){
return h('div','hi jiang')
}
});
}, 1000);
})
},
timeout:2000,
errorComponent:{
render:()=>h('Text','超时错误')
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export function defineAsyncComponent(options){
if(typeof options === 'function'){
options = { loader:options }
}
let Comp = null;
return {
setup(){
const {loader} = options;
const loaded = ref(false);
const error = ref(false); // 是否超时
loader().then(c=>{
Comp = c;
loaded.value = true;
}).catch((err)=>error.value = err)
if(options.timeout){
setTimeout(()=>{
error.value = true; // 显示错误组件
},options.timeout)
}
const placeHolder = h(Fragment,'')
return ()=>{
if(loaded.value){
return h(Comp);
}else if(error.value && options.errorComponent){ // 超时显示错误组件
return h(options.errorComponent)
}
return placeHolder
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
组件卸载的时候需要稍作处理
const unmount = (vnode) =>{
const {shapeFlag} = vnode;
if(vnode.type === Fragment){
return unmountChildren(vnode.children)
}else if(shapeFlag & ShapeFlags.COMPONENT){
// 组件那么移除
return unmount(vnode.component.subTree); // 移除组件
}
hostRemove(vnode.el)
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
异步组件loading处理
let asyncComponent = defineAsyncComponent({
loader:()=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
render(){
return h('div','hi jiang')
}
});
}, 3000);
})
},
timeout:2000,
errorComponent:{
render:()=>h('Text','超时错误')
},
delay:1000,
loadingComponent:{
render: ()=> h('h2','loading....')
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// loading逻辑
const loading = ref(false);
let loadingTimer = null;
if(options.delay){
loadingTimer = setTimeout(()=>{
loading.value = true;
},options.delay)
}else{
loading.value = true;
}
const error = ref(false);
loader().then(c=>{
Comp = c;
loaded.value = true;
}).catch((err)=>error.value = err).finally(()=>{
loading.value = false;
clearTimeout(loadingTimer); // 加载完毕的时候清理定时器
})
// ...
return ()=>{
if(loaded.value){
return h(Comp);
}else if(error.value && options.errorComponent){
return h(options.errorComponent)
}else if(loading.value && options.loadingComponent){ // 显示loading组件
return h(options.loadingComponent)
}
return placeHolder
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
异步组件加载重试处理
let asyncComponent = defineAsyncComponent({
loader:()=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
reject({
render(){
return h('div','hi jiang')
}
});
}, 3000);
})
},
timeout:2000,
errorComponent:{
render:()=>h('Text','超时错误')
},
delay:1000,
loadingComponent:{
render: ()=> h('h2','loading....')
},
onError(retry){
console.log('错了')
retry()
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function load(){
return loader().catch(err=>{
if(options.onError){
return new Promise((resolve,reject)=>{
const retry = ()=>resolve(load());
const fail = ()=>reject(err);
options.onError(retry,fail)
});
}else{
throw err;
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13