深浅模式
#tech/vue/bug
Reproduction link
https://github.com/qmhc/vue-test
开发服务下会抛出警告:[Vue warn]: Missing ref owner context. ref cannot be used on hoisted vnodes. A vnode with ref must be created inside the render function.
还有警告 resolveComponent can only be used in render() or setup().
这个错误是因为组件连 Vue 一起打包了进去导致两份 Vue 冲突
解决方法3点:
- external: ['vue']
- rollup.config 中增加 resolve:
- 把vue从dependencies 改为 devDependencies
这样打包成多份的时候,完美解决
https://github.com/vuejs/core/issues/3930
IntersectionObserver
#tech/html/perform #tech/vue/select
懒加载组件
Intersection Observer API 是一个现代浏览器提供的JavaScript接口,它允许开发者异步地观察DOM元素相对于其祖先元素或视口(viewport)的交叉状态变化。也就是说,它能够监控一个目标元素是否可见,以及在视窗中可见的程度(比如完全可见、部分可见或完全不可见)。当目标元素进入或离开视窗,或者其可见比例达到预先设定的阈值时,注册的回调函数就会被调用。
利用Intersection Observer API,开发者可以有效地执行诸如图片懒加载(只加载可视区域内的图片)、无限滚动(当内容接近视口底部时自动加载更多内容)、节省资源(不在可视范围内的资源推迟加载)以及其他与元素可见性相关的优化操作。由于Intersection Observer的工作方式是异步且高效的,它不会像传统的滚动事件监听那样影响页面性能,特别是当处理大量元素时,能够显著提升页面响应速度和用户体验。
el-select 懒加载特性:
- 组件可见时,如果 modelValue 有值,需要到后台取出 value 相对应的 label 和 desc
- 允许用户进行查询关键字的输入,query 到后台做查询,并能分页读取
- 查询 value 与 label/desc 映射关系应当批量化,比如前台有 5个 下拉框,要求能够一次性到后台取数,并缓存在 window 中
- el-select 可能会被初始化在 aggrid 表格,表格中如果有20行数据,应当同 el-select 一样,批量到后台换取 value 对应的 label 属性,后台反馈结果之后,异步刷新到 aggrid 中
html
<template>
<div ref="selectWrapper">
<el-select
v-model="selected"
placeholder="请选择..."
filterable
@search="fetchDataByQuery"
:loading="isLoading"
:popper-append-to-body="true"
:remote-method="fetchRemoteData"
:reserve-keyword="true"
@change="handleChange"
>
<el-option
v-for="(item, index) in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</template>
<script>
import { defineComponent, ref, watch, onMounted, onBeforeUnmount } from 'vue';
import axios from 'axios';
export default defineComponent({
// ...其他不变的属性和方法...
setup(props, context) {
// ...不变的部分...
// 引入IntersectionObserver变量
let observer;
// 创建IntersectionObserver实例
const initObserver = () => {
observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
// 当el-select容器进入视口时
if (props.modelValue && !cacheStorage[props.batchFetchKey]) {
fetchData(props.modelValue);
} else if (props.modelValue) {
options.value = getFromCache(props.modelValue);
}
}
}, {
threshold: 0.5, // 可见度阈值,可自定义
});
// 将观察器绑定到el-select容器上
observer.observe(this.$refs.selectWrapper);
};
// 在组件卸载时清除IntersectionObserver
onBeforeUnmount(() => {
observer?.disconnect();
});
// 初始化IntersectionObserver
onMounted(initObserver);
// ...其他不变的方法...
// 返回
return {
...toRefs({ selected, isLoading, options }),
fetchDataByQuery,
initObserver, // 新增,用于在测试或需要重新初始化IntersectionObserver时使用
};
},
});
</script>
SQL重写,实现数据权限
#tech/java/querydsl
java
public static String rewrite(String originalSql) {
if (SQLRewriteHolder.isDisable()) {
// log.info("[sql-rewrite] disable");
return originalSql;
}
if (StringUtils.lowerCase(originalSql).contains("use_raw_sql")) {
// log.info("[sql-rewrite] use_raw_sql");
return originalSql;
}
String newSql = originalSql;
try {
// log.info("[sql-rewrite] start");
Statement statement = CCJSqlParserUtil.parse(originalSql);
if (statement instanceof Select) {
Select select = (Select) statement;
if (select.getSelectBody() instanceof PlainSelect) {
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
FromItem fromItem = plainSelect.getFromItem();
if (fromItem instanceof Table) {
Table firstTable = (Table) fromItem;
String tableName = StringUtils.lowerCase(firstTable.getName());
String tableAlias = firstTable.getAlias() == null ? StringUtils.EMPTY : firstTable.getAlias().getName();
String className = "com.galaxis.wms.query.Q" + NamingUtils.underline2Camel(tableName, false);
Class<?> tableClass = null;
try {
HotReloadEngine reloadEngine = SpringContextHolder.getBean(HotReloadEngine.class);
tableClass = reloadEngine.loadClass(className);
} catch (Exception ignored) {
}
if (tableClass == null) {
// 从 主classLoader 中获取类
try {
tableClass = Class.forName(className);
} catch (Exception ignored) {
}
}
if (tableClass != null && RelationalPathBase.class.isAssignableFrom(tableClass)) {
RelationalPathBase<?> tableQ = null;
for (Field field : tableClass.getFields()) {
if (Modifier.isStatic(field.getModifiers()) && Objects.equals(field.getType(), tableClass)) {
tableQ = (RelationalPathBase<?>) field.get(null);
}
}
if (tableQ != null) {
SqlAuthConfig sqlAuthConfig = SpringContextHolder.getBean(SqlAuthConfig.class);
if (sqlAuthConfig.getTables() != null && sqlAuthConfig.getTables().contains(com.yvan.core.StringUtils.lowerCase(tableQ.getTableName()))) {
return originalSql;
}
// log.info("[sql-rewrite] ownerIds projectId whId");
String rawWhere = plainSelect.getWhere() == null ? StringUtils.EMPTY : plainSelect.getWhere().toString();
String newWhere = rawWhere;
String fieldPrefix = (StringUtils.isNotBlank(tableAlias) ? tableAlias : firstTable.getName());
NumberPath<Long> ownerId = getField(tableQ, OWNER_ID);
ColumnMetadata ownerIdColumn = ownerId == null ? null : tableQ.getMetadata(ownerId);
String ownerIdName = ownerIdColumn == null ? null : ownerIdColumn.getName();
Set<Long> ownerIds = SQLRewriteHolder.getOwnerIds();
if (StringUtils.isNotBlank(ownerIdName) && !ownerIds.isEmpty() && !SQLRewriteHolder.isDisabledOwnerIds()) {
// log.info("[sql-rewrite] ownerIds");
newWhere = newWhere + " AND " + fieldPrefix + "." + ownerIdName + " in (" + StringUtils.join(ownerIds, ", ") + ")";
}
NumberPath<Long> projectId = getField(tableQ, PROJECT_ID);
ColumnMetadata projectIdColumn = projectId == null ? null : tableQ.getMetadata(projectId);
String projectIdName = projectIdColumn == null ? null : projectIdColumn.getName();
Set<Long> projectIds = SQLRewriteHolder.getProjectIds();
if (StringUtils.isNotBlank(projectIdName) && !projectIds.isEmpty() && !SQLRewriteHolder.isDisabledProjectIds()) {
// log.info("[sql-rewrite] projectIds");
newWhere = newWhere + " AND " + fieldPrefix + "." + projectIdName + " in (" + StringUtils.join(projectIds, ", ") + ")";
}
NumberPath<Long> whId = getField(tableQ, WH_ID);
ColumnMetadata whIdColumn = whId == null ? null : tableQ.getMetadata(whId);
String whIdName = whIdColumn == null ? null : whIdColumn.getName();
Set<Long> whIds = SQLRewriteHolder.getWhIds();
if (StringUtils.isNotBlank(whIdName) && !whIds.isEmpty() && !SQLRewriteHolder.isDisabledWhIds()) {
// log.info("[sql-rewrite] whIds");
newWhere = newWhere + " AND " + fieldPrefix + "." + whIdName + " in (" + StringUtils.join(whIds, ", ") + ")";
}
if (!Objects.equals(rawWhere, newWhere)) {
newWhere = StringUtils.trim(newWhere);
if (StringUtils.lowerCase(newWhere).startsWith("and ")) {
newWhere = newWhere.substring(4);
}
plainSelect.setWhere(CCJSqlParserUtil.parseCondExpression(newWhere));
newSql = select.toString();
}
}
}
}
}
}
} catch (Exception e) {
log.warn("重写sql失败 | sql={}", originalSql, e);
}
// log.info("[sql-rewrite] end | newSql={}", SqlLoggerUtils.deleteWhitespace(newSql));
return newSql;
}
pinyin-pro xe-utils sortablejs