Skip to content

#tech/vue/bug

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