实际应用场景:
组件复用,如当select里面的选项options是走后端动态获取的,如果把网络请求放在组件内部,当单页面多次引用该组件,则获取选项的请求也将发送多次。例如下面这个select组件的CCOptions,需要变量全局化+响应式+懒加载+初始化+性能提升(避免重复请求)。文章末尾附上你需要明白的Vue3 reactive特性!希望你能看到最后。
<t-select popup-props={popProp}>
{CCOptions.map((item, index) => (
<t-option
key={index}
value={item.value}
label={item.label}
popup-props={{
overlayClassName: "tdesign-demo-select__overlay-option",
}}
>
{item.label}
{item.count && <a style="color: var(--td-brand-color)">({item.count})</a>}
</t-option>
))}
</t-select>
解决问题逻辑:
- 将Function放在全局脚本文件(global.js)内部,按需获取,并且作为导出常量(export const)。例如下面的代码:
// global.ts
// 商品类目
export const getCommodityCategoryOptions = async () => {
let next_page = CommodityCategoryData.currentPage + 1;
let next_pageSize = CommodityCategoryData.pageSize;
if (
CommodityCategoryData.Options.length > next_page * next_pageSize ||
CommodityCategoryData.totalRecords < next_pageSize * (next_page - 1)
) {
// 数据已加载过了或者已经加载完毕
return CommodityCategoryData.Options;
}
await getCommodityCategoryApi({
page: next_page,
pageSize: next_pageSize,
})
.then((result: any) => {
CommodityCategoryData.Options.push(
...map(result.PagedList, (item: any) => {
return {
label: item.CategoryName ? item.CategoryName : "",
value: item.CategoryID,
count: item.Count,
};
})
);
CommodityCategoryData.totalRecords = result.TotalRecords;
CommodityCategoryData.currentPage = result.PageNo;
})
.catch((err) => {
MessagePlugin.error(err.message);
});
return CommodityCategoryData.Options;
};
MerchantClassificationData.Options.length >= next_page * next_pageSize ||
MerchantClassificationData.totalRecords < next_pageSize * (next_page - 1)
) {
// 数据已加载过了或者已经加载完毕
return MerchantClassificationData.Options;
}
await getMerchantClassificationApi({
page: next_page,
pageSize: next_pageSize,
})
.then((result: MerchantClassificationModel) => {
MerchantClassificationData.Options.push(
...map(result.PagedList, (item: MerchantModel) => {
return {
label: item.CategoryName,
value: item.CategoryID,
count: item.Count,
};
})
);
MerchantClassificationData.totalRecords = result.TotalRecords;
MerchantClassificationData.currentPage = result.PageNo;
})
.catch((err) => {
MessagePlugin.error(err.message);
});
return MerchantClassificationData.Options;
};
- 将获取到的结果数据存在在全局脚本内部,作为常量就够了(const)。例如下面的代码:
// 商品类目
const CommodityCategoryData: T = {
Options: [],
totalRecords: 0,
currentPage: 0,
pageSize: 15,
};
- 优化的关键是请求数据的方法避免重复请求,例如分页获取的数据,可以通过判断该数据是否获取过,以此减少请求次数,达到优化性能的目的;
- 数据默认在单页面import的脚本文件里面初始化一次。例如下面的代码:
//另外的脚本文件
export const CCOptions = reactive<Array<{ label: string; value: string; count: number }>>([]);
export const getCCOptions = () => {
getCommodityCategoryOptions().then((res) => {
while (CCOptions.length > 0) CCOptions.pop();
CCOptions.push(...res);
});
};
if (Dom_IsExist.formItem_goodsCategory_used) getCCOptions();
- 最后只需要在单页面中引用CCOptions 和getCCOptions 方法就可以实现数据响应式。
import { CCOptions, getCCOptions } from "@/config/appManager";
我的组件业务逻辑:
const popProp = {
onScrollToBottom: handleScrollToBottom_MerchantClassification,
};
const handleScrollToBottom_MerchantClassification = () => {
getCCOptions();
};
<t-select popup-props={popProp}>
{CCOptions.map((item, index) => (
<t-option
key={index}
value={item.value}
label={item.label}
popup-props={{
overlayClassName: "tdesign-demo-select__overlay-option",
}}
>
{item.label}
{item.count && <a style="color: var(--td-brand-color)">({item.count})</a>}
</t-option>
))}
</t-select>
你需要明白:
1、const+reactive+Array的定义,不能将变量直接=[],因为reactive只响应刚开始那个引用地址;也不能因为报错把const改成let;这样确实不会报错了,但是同样使用=[]之后,变量就不会拥有响应式了。
2、export let CCOptions =
reactive([]);如果使用CCOptions=reactive([…res])则在引用CCOptions的地方也将失去响应式。因为reactive
是跟着括号里面的变量走的;
3、综上所述:while (CCOptions.length > 0) CCOptions.pop();
CCOptions.push(…res);是我实践之后解决该类问题方法