20. 实现异步组件
defineAsyncComponent
函数是一个高阶组件,他的返回值是一个包装组件。此包装组件会根据状态来决定渲染的内容,加载成功后渲染组件,在未渲染成功时渲染一个占位符节点
1.基本实现
let asyncComponent = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
render: () => h('div', 'hi erxiao')
});
}, 1000);
});
});
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, '');
};
}
};
}
2.异步组件超时处理
let asyncComponent = defineAsyncComponent({
loader: () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
render() {
return h('div', 'hi erxiao');
}
});
}, 1000);
});
},
timeout: 2000,
errorComponent: {
render: () => h('Text', '超时错误')
}
});
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;
};
}
};
}
3.异步组件 loading 处理
let asyncComponent = defineAsyncComponent({
loader: () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
render() {
return h('div', 'hi erxiao');
}
});
}, 3000);
});
},
timeout: 2000,
errorComponent: {
render: () => h('Text', '超时错误')
},
delay: 1000,
loadingComponent: {
render: () => h('h2', '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) {
return h(options.loadingComponent);
}
return placeHolder;
};
4.异步组件加载重试处理
let asyncComponent = defineAsyncComponent({
loader: () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject({
render() {
return h('div', 'hi erxiao');
}
});
}, 3000);
});
},
timeout: 2000,
errorComponent: {
render: () => h('Text', '超时错误')
},
delay: 1000,
loadingComponent: {
render: () => h('h2', 'loading....')
},
onError(retry) {
console.log('错了');
retry();
}
});
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;
}
});
}