1***@qq.com
1***@qq.com
  • 发布:2026-10-06 19:15
  • 更新:2026-10-06 19:16
  • 阅读:23

easycom 自定义组件真机运行全部失效

分类:uni-app

产品分类: uniapp/App

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: Windows 11 专业版 64 位操作系统

HBuilderX类型: 正式

HBuilderX版本号: 5.26

手机系统: Android

手机系统版本号: Android 15

手机厂商: OPPO

手机机型: 一加ACE2

页面类型: vue

vue版本: vue3

打包方式: 云端

项目创建方式: HBuilderX

App下载地址或H5⽹址: https://saas.100xsys.cn/h5/app/latest.apk

示例代码:

pages.json

"easycom": {  
            "autoscan": true,  
            "custom": {  
                "^u--(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",  
                "^up-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",  
            "^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue"  
            }  
        },  

main.js

import uviewPlus, { setConfig } from './uni_modules/uview-plus'  
export function createApp() {  
 const app = createSSRApp(App)  
  app.use(uviewPlus)  
 setConfig({  
    config: {  
      iconUrl: '/static/font/uicon-iconfont.ttf',  
      loadFontOnce: true  
    }  
  })  
 return {  
    app  
  }  

}

App.vue

@import '@/uni_modules/uni-scss/index.scss';

vite.config.js

import { defineConfig } from "vite";  
// 注意:UniUpRoot 必须在 uni() 之前注册  
import UniUpRoot from "./uni_modules/uview-plus/libs/root/index.js";  
import uni from "@dcloudio/vite-plugin-uni";  

export default defineConfig({  
    server: {  
        host: true,  
        // 开发服务器端口,改为你想要的端口即可,如 3000、8080、9527  
        port: 3000,  
        // 端口被占用时是否自动换端口(false 则直接报错)  
        strictPort: false,  
        // 自动打开浏览器  
        open: true,  
    },  
    plugins: [  
        UniUpRoot({  
            // 根文件名(内部会自动归一化为 App.up.vue)  
            rootFileName: "App.up",  
            // 若 App.up.vue 不存在则自动创建一个最小版本  
            autoCreateRootFile: true,  
        }),  
        uni(),  
    ],  
});  

uni.scss

@import '@/uni_modules/uni-scss/variables.scss';    
@import '@/uni_modules/uview-plus/theme.scss';  

App.up.vue


<template>    
    <UpRootView />    
</template>  

uView-plus tabs 组件

<view class="process-tabs-wrap">  
    <up-tabs :list="list1" :current="currentTabIndex" :scrollable="true" @click="activeTabs"></up-tabs>  
</view>  
<script setup>  
const list1 = ref([]);  
const currentTabIndex = ref(0);  
const activeTabs = (item, index) => {  
    const tabIndex = typeof index === 'number' ? index : item?.index ?? 0;  
};  
</script>  
<style  lang="scss" scoped>  
.process-tabs-wrap :deep(.u-tabs) {  
    width: 100%;  
}  
.process-tabs-wrap {  
    margin: 16rpx 24rpx 0;  
    background: #ffffff;  
    border-radius: 16rpx;  
    overflow: hidden;  
    box-shadow: 0 2rpx 12rpx rgba(47, 123, 255, 0.06);  
    /* App端 scroll-view 必须有明确高度才能渲染,否则会塌陷为0 */  
    height: 80rpx;  
    display: flex;  
    align-items: center;  
}  
</style>

uni-popup 组件components/work-instruction-preview/work-instruction-preview.vue

uni-popup 真机运行默认未隐藏,加了 v-if="popupVisible" ,解决默认未隐藏的问题;点击取消触发closePopup函数,无法关闭弹窗;

<template>  
    <uni-popup v-if="popupVisible" ref="popupRef" type="bottom">  
        <view class="instruction-popup">  
            <view class="instruction-popup-title">作业指导书列表</view>  
            <view  
                v-for="(item, idx) in instructions"  
                :key="idx"  
                class="instruction-item"  
                @click="onPickInstruction(item)"  
            >  
                <text class="instruction-icon">?</text>  
                <text class="instruction-name">{{ item.src_name }}</text>  
                <text class="instruction-arrow">›</text>  
            </view>  
            <view class="instruction-cancel" @click="closePopup">  
                <text>取消</text>  
            </view>  
        </view>  
    </uni-popup>  
</template>  

<script setup>  
import { ref, nextTick } from 'vue';  
import { getToken } from '@/utils/auth.js';  

const props = defineProps({  
    // 作业指导书列表,每项含 src_name(PDF名称)、fileurl(PDF路径)  
    instructions: { type: Array, default: () => [] },  
});  

// 控制弹窗组件是否挂载(避免 uni-popup 在 App 端初始化时序问题导致内容直接显示在页面流中)  
const popupVisible = ref(false);  

// uni-popup 通过 ref 调用 open/close 控制显隐  
const popupRef = ref(null);  
const openPopup = () => {  
    // 先挂载组件,再在下一帧调用 open,确保 ref 可用  
    popupVisible.value = true;  
    nextTick(() => {  
        popupRef.value?.open('bottom');  
    });  
};  
const closePopup = () => {    
    // 先挂载组件,再在下一帧调用 open,确保 ref 可用  
    popupRef.value?.close();  
    // 关闭动画结束后再卸载组件,避免动画被截断  
    setTimeout(() => {  
        popupVisible.value = false;  
    }, 300);  
};  

// 构建 PDF 文件真实下载地址(H5 交给 pdf.js 阅读器渲染)  
const buildFileUrl = (fileurl) => {  
    const ticket = getToken() || '';  
    return `https://api.100xsys.cn:443/file/download?filePath=${encodeURIComponent(fileurl)}&bx_auth_ticket=${ticket}`;  
};  

// 构建 pdf.js 在线预览页地址(APP 交给系统原生浏览器打开)  
const buildPdfUrl = (fileurl) => {  
    const ticket = getToken() || '';  
    const downloadUrl = buildFileUrl(fileurl);  
    return `https://saas.100xsys.cn/vpages/#/viewpdf?pdfsrc=${encodeURIComponent(downloadUrl)}&bx_ticket=${ticket}`;  
};  

// 打开 PDF  
const openPdf = (fileurl, name) => {  
    if (!fileurl) {  
        uni.showToast({ title: 'PDF文件路径无效', icon: 'none' });  
        return;  
    }  
    // #ifdef H5  
    // H5:跳转 uview-plus PDF阅读器页面(pdf.js 应用内渲染),透传 PDF 名称作为页面标题  
    uni.navigateTo({  
        url: `/pages/pdfview/index?url=${encodeURIComponent(buildFileUrl(fileurl))}&name=${encodeURIComponent(name || '')}`  
    });  
    // #endif  
    // #ifdef APP-PLUS  
    // APP:调用系统原生浏览器打开  
    plus.runtime.openURL(buildPdfUrl(fileurl));  
    // #endif  
};  

// 弹窗中点击某个作业指导书  
const onPickInstruction = (item) => {  
    closePopup();  
    openPdf(item.fileurl, item.src_name);  
};  

// 解析 work_instruction_json 字符串为数组  
const parseWorkInstructions = (jsonStr) => {  
    if (!jsonStr) return [];  
    try {  
        const parsed = JSON.parse(jsonStr);  
        return Array.isArray(parsed) ? parsed : [];  
    } catch (e) {  
        console.error('解析作业指导书JSON失败:', e);  
        return [];  
    }  
};  

// 预览入口:0个提示/1个直接打开/多个弹窗列表  
const preview = () => {  
    const list = props.instructions;  
    if (!list || !list.length) {  
        uni.showToast({ title: '暂无作业指导书', icon: 'none' });  
        return;  
    }  
    if (list.length === 1) {  
        openPdf(list[0].fileurl, list[0].src_name);  
        return;  
    }  
    openPopup();  
};  

defineExpose({ preview, parseWorkInstructions });  
</script>  

<style lang="scss" scoped>  
.instruction-popup {  
    padding: 32rpx 28rpx;  
    padding-bottom: calc(32rpx + constant(safe-area-inset-bottom));  
    padding-bottom: calc(32rpx + env(safe-area-inset-bottom));  
    background: #ffffff;  
    border-radius: 32rpx 32rpx 0 0;  
    overflow: hidden;  
}  
.instruction-popup-title {  
    font-size: 32rpx;  
    font-weight: 700;  
    color: #1a1a1a;  
    text-align: center;  
    margin-bottom: 24rpx;  
}  
.instruction-item {  
    display: flex;  
    flex-direction: row;  
    align-items: center;  
    padding: 28rpx 0;  
    border-bottom: 2rpx solid #f0f2f5;  
}  
.instruction-icon {  
    font-size: 36rpx;  
    margin-right: 16rpx;  
}  
.instruction-name {  
    flex: 1;  
    font-size: 28rpx;  
    color: #1a1a1a;  
    overflow: hidden;  
    white-space: nowrap;  
    text-overflow: ellipsis;  
}  
.instruction-arrow {  
    font-size: 36rpx;  
    color: #c0c4cc;  
    margin-left: 8rpx;  
}  
.instruction-cancel {  
    margin-top: 24rpx;  
    padding: 24rpx 0;  
    text-align: center;  
    font-size: 28rpx;  
    color: #999;  
}  
</style>

引入 work-instruction-preview 组件

<template>  
<view class="guide-right" @click="onPreview">  
  <text class="preview-text">预览</text>  
</view>  

<work-instruction-preview ref="instructionRef" :instructions="workInstructions"></work-instruction-preview>  
</template>  
<script setup>  

const instructionRef = ref();  
const workInstructions = ref([]);  
const onPreview = () => {  
    instructionRef.value?.preview();  
};  
</script>

操作步骤:

HbuilderX 5.26 创建 uni-app 项目,uni-app 插件市场引入uView-plus 3.8.62

预期结果:

所有uView-plus正常显示;
uni-popup默认隐藏,点击打开按钮,uni-popup弹窗,遮罩层正常显示;点击取消按钮,uni-popup弹窗隐藏;

实际结果:

uView-plus 的tabs、popup不显示;

uni-popup 真机运行默认未隐藏,加了 v-if="popupVisible" ,解决默认未隐藏的问题;点击取消触发closePopup函数,无法关闭弹窗;
uni-app的弹窗,遮罩层未显示;
uni-popup 在App上遮罩层不显示,取消按钮无法关闭弹窗;弹窗默认情况未隐藏,直接显示了出来,点击取消按钮无法关闭弹窗;

bug描述:

  1. 从插件市场引入uView-plus,真机运行App后,所有组件不显示;
  2. uni-popup 在App上遮罩层不显示,取消按钮无法关闭弹窗;弹窗默认情况未隐藏,直接显示了出来,点击取消按钮无法关闭弹窗;
2026-10-06 19:15 负责人:无 分享
已邀请:
Ask小助手

Ask小助手 - Ask社区AI助手

欢迎到专业群( uni-ui官方技术交流群 ) 咨询,群中有相关专业的管理员和群友。

感谢提交这份 bug 反馈。从官方评审角度看,这份反馈的分类信息比较完整(HBuilderX 5.26 正式版、Windows 11、Android 15、一加 ACE2、Vue3 均已提供),但代码示例与复现信息存在几处关键疑点,目前尚不能将其确认为 uni-app 框架或 HBuilderX 的 bug,具体分析如下:

一、反馈完整性评审

  1. main.js 代码不完整。贴出的 createApp 函数缺少 return app:
export function createApp() {  
  const app = createSSRApp(App)  
  app.use(uviewPlus)  
  // 此处缺少 return app  
}

本助手使用国产模型,生成代码可能不准确。专业的代码生成需要全球顶尖的AI Coding模型。推荐使用uni-agent

Vue3 项目中 createApp 钩子必须返回 app 实例,否则应用无法正常初始化。请确认实际工程代码是否也缺少这一行。如果实际代码确实缺少,那么“所有组件不显示”很可能是应用初始化异常导致,而非 easycom 或组件库问题。

  1. vite.config.js 中引入了自定义插件 UniUpRoot(路径 ./uni_modules/uview-plus/libs/root/index.js),并配置了 rootFileName: "App.up"、autoCreateRootFile: true。这不是 uview-plus 官方文档的标准接入方式,该插件会把应用根文件接管为 App.up.vue。请在反馈中说明该插件的来源,并验证移除该插件、恢复默认 App.vue 后问题是否仍存在。这个对根组件的改写是当前配置中最可能影响组件渲染的变量,不排除该问题即由此引起。

  2. 缺少页面代码。未提供使用 u-tabs、uni-popup 的页面模板与脚本,无法确认组件使用方式(如 uni-popup 是否通过 ref 调用 open/close、是否在组件渲染完毕后调用)。

  3. 缺少真机运行时的控制台日志。请补充 HBuilderX 控制台是否出现 [Vue warn]: Failed to resolve component 之类的警告或其他报错。组件不显示通常伴随组件解析失败的警告,这对区分“easycom 解析失败”还是“渲染异常”非常关键。

  4. 复现步骤过于简略。“创建项目 + 插件市场引入 uView-plus 3.8.62”不足以让他人直接复现,建议补充:新建项目所用模板、插件引入方式(是否导入到 uni_modules)、页面中使用的组件标签及操作顺序。

  5. 建议将两个问题分离验证:uView-plus 组件不显示(第三方组件库)与 uni-popup 异常(官方 uni-ui 组件)属于两个独立组件库,请分别提供最小复现代码,避免相互干扰。

二、bug 是否成立的分析

  1. easycom 配置写法本身符合规范。pages.json 的 easycom 节点支持 autoscan 与 custom 正则自定义匹配,你配置的 ^u--(.*)、^up-(.*)、^u-([^-].*) 与 uview-plus 以 uni_modules 方式安装时的推荐写法一致,格式上没有问题。参考:easycom 配置文档

  2. 需要注意,文档明确说明:考虑到编译速度,直接在 pages.json 内修改 easycom 不会触发重新编译,需要改动页面内容触发。如果你是在编译运行过程中调整过 easycom 配置,请改动一次页面内容或重新编译后再验证,否则可能出现“配置了但未生效”的假象。

  3. uni-popup 的现象(默认未隐藏、遮罩不显示、取消按钮无法关闭)与组件未正确注册/初始化时的表现相符。官方注意事项中明确:组件需要依赖 sass 插件,请自行手动安装;若确认引用正确但提示未注册组件或显示不正常,请尝试重新编译项目。参考:uni-popup 注意事项

  4. 社区曾有相似案例:使用 uview-plus 时报 Failed to resolve component: u-popup、组件显示不出来,与编译缓存及启动状态相关(该案例中调整后热更新正常、重启后复现)。你的现象与之有相似之处,但平台(App 真机)与版本(5.26)不同,不能直接套用其结论。参考:ask 188958

三、排查建议

  1. 先补全 main.js 的 return app,重新编译验证。
  2. 移除 vite.config.js 中的 UniUpRoot 插件,恢复默认 App.vue,按 uview-plus 官方文档的标准方式接入后,验证组件是否恢复显示。
  3. 每次调整 easycom 配置后,改动页面内容触发重新编译,或直接重新编译项目。
  4. 确认 sass 插件已安装(uni-popup 依赖)。

若完成上述排查后问题依旧存在,请补充:完整可运行的最小复现代码、页面中使用组件的代码、真机运行时 HBuilderX 控制台的完整日志(含警告与报错),以及移除 UniUpRoot 插件前后的对比结果。补充这些信息后我们再进一步跟进确认是否为框架侧问题。

内容为 AI 生成,仅供参考

要回复问题请先登录或注册