443 lines
8.5 KiB
Markdown
443 lines
8.5 KiB
Markdown
![]() |
# HBuilderX小程序编译错误解决方案
|
|||
|
|
|||
|
## 错误信息
|
|||
|
|
|||
|
```
|
|||
|
[vite]: Rollup failed to resolve import "@dcloudio/uni-ui/lib/uni-button/uni-button.vue" from "components/MsgTips/MsgTips.vue".
|
|||
|
|
|||
|
[@vue/compiler-sfc] `defineExpose` is a compiler macro and no longer needs to be imported.
|
|||
|
[@vue/compiler-sfc] `defineProps` is a compiler macro and no longer needs to be imported.
|
|||
|
[@vue/compiler-sfc] `defineEmits` is a compiler macro and no longer needs to be imported.
|
|||
|
```
|
|||
|
|
|||
|
## 问题分析
|
|||
|
|
|||
|
### 问题1:uni-button 组件未安装
|
|||
|
|
|||
|
**原因:**
|
|||
|
- `uni-button` 不是 uni-app 的内置组件
|
|||
|
- 项目中使用了 `<uni-button>` 但没有安装 `@dcloudio/uni-ui` 完整包
|
|||
|
- 只安装了部分组件(uni-popup、uni-icons等)
|
|||
|
|
|||
|
**解决方案:**
|
|||
|
使用原生 `<button>` 替代 `<uni-button>`
|
|||
|
|
|||
|
### 问题2:Vue编译器宏错误导入
|
|||
|
|
|||
|
**原因:**
|
|||
|
- `defineProps`、`defineEmits`、`defineExpose` 是 Vue 3 的编译器宏
|
|||
|
- 它们由编译器自动注入,不需要从 vue 中导入
|
|||
|
- 在 `<script setup>` 中可以直接使用
|
|||
|
|
|||
|
**错误写法:**
|
|||
|
```javascript
|
|||
|
import { defineProps, defineEmits, defineExpose } from 'vue'
|
|||
|
```
|
|||
|
|
|||
|
**正确写法:**
|
|||
|
```javascript
|
|||
|
// 无需导入,直接使用
|
|||
|
const props = defineProps({...})
|
|||
|
const emit = defineEmits([...])
|
|||
|
defineExpose({...})
|
|||
|
```
|
|||
|
|
|||
|
## ✅ 已修复的文件
|
|||
|
|
|||
|
### 1. components/MsgTips/MsgTips.vue
|
|||
|
|
|||
|
**修改内容:**
|
|||
|
- ✅ 将 `<uni-button>` 替换为 `<button>`
|
|||
|
- ✅ 添加了 button 样式重置
|
|||
|
|
|||
|
**修改前:**
|
|||
|
```vue
|
|||
|
<uni-button class="popup-button" @click="close">确定</uni-button>
|
|||
|
```
|
|||
|
|
|||
|
**修改后:**
|
|||
|
```vue
|
|||
|
<button class="popup-button" @click="close">确定</button>
|
|||
|
```
|
|||
|
|
|||
|
**样式重置:**
|
|||
|
```scss
|
|||
|
button {
|
|||
|
padding: 0;
|
|||
|
margin: 0;
|
|||
|
border: none;
|
|||
|
background: none;
|
|||
|
font-size: inherit;
|
|||
|
line-height: inherit;
|
|||
|
}
|
|||
|
|
|||
|
button::after {
|
|||
|
border: none;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 2. pages/chat/components/ai-paging.vue
|
|||
|
|
|||
|
**修改内容:**
|
|||
|
- ✅ 移除 `defineProps` 和 `defineEmits` 的导入
|
|||
|
|
|||
|
**修改前:**
|
|||
|
```javascript
|
|||
|
import {
|
|||
|
ref,
|
|||
|
defineProps,
|
|||
|
defineEmits,
|
|||
|
// ...
|
|||
|
} from 'vue';
|
|||
|
```
|
|||
|
|
|||
|
**修改后:**
|
|||
|
```javascript
|
|||
|
import {
|
|||
|
ref,
|
|||
|
// 移除了 defineProps 和 defineEmits
|
|||
|
// ...
|
|||
|
} from 'vue';
|
|||
|
```
|
|||
|
|
|||
|
### 3. components/tabbar/midell-box.vue
|
|||
|
|
|||
|
**修改内容:**
|
|||
|
- ✅ 移除 `defineProps` 的导入
|
|||
|
|
|||
|
### 4. pages/chat/components/popupbadFeeback.vue
|
|||
|
|
|||
|
**修改内容:**
|
|||
|
- ✅ 移除 `defineEmits` 的导入
|
|||
|
|
|||
|
### 5. components/selectJobs/selectJobs.vue
|
|||
|
|
|||
|
**修改内容:**
|
|||
|
- ✅ 移除 `defineExpose` 的导入
|
|||
|
|
|||
|
### 6. components/renderJobs/renderJobsCheckBox.vue
|
|||
|
|
|||
|
**修改内容:**
|
|||
|
- ✅ 移除 `defineExpose` 的导入
|
|||
|
|
|||
|
## 📋 Vue 3 Composition API 编译器宏
|
|||
|
|
|||
|
### 什么是编译器宏?
|
|||
|
|
|||
|
编译器宏是 Vue 3 在 `<script setup>` 中提供的特殊函数,由编译器在编译时处理,不是运行时的函数。
|
|||
|
|
|||
|
### 常用编译器宏
|
|||
|
|
|||
|
| 宏名称 | 作用 | 是否需要导入 |
|
|||
|
|--------|------|------------|
|
|||
|
| `defineProps` | 定义组件 props | ❌ 不需要 |
|
|||
|
| `defineEmits` | 定义组件事件 | ❌ 不需要 |
|
|||
|
| `defineExpose` | 暴露组件方法/属性 | ❌ 不需要 |
|
|||
|
| `withDefaults` | 为 props 设置默认值 | ❌ 不需要 |
|
|||
|
| `defineOptions` | 定义组件选项 | ❌ 不需要 |
|
|||
|
| `defineSlots` | 定义插槽类型 | ❌ 不需要 |
|
|||
|
| `defineModel` | 定义 v-model | ❌ 不需要 |
|
|||
|
|
|||
|
### 正确使用示例
|
|||
|
|
|||
|
```vue
|
|||
|
<script setup>
|
|||
|
// ✅ 正确:直接使用,无需导入
|
|||
|
const props = defineProps({
|
|||
|
name: String,
|
|||
|
age: Number
|
|||
|
})
|
|||
|
|
|||
|
const emit = defineEmits(['update', 'change'])
|
|||
|
|
|||
|
defineExpose({
|
|||
|
open,
|
|||
|
close
|
|||
|
})
|
|||
|
|
|||
|
// ❌ 错误:不要从 vue 导入
|
|||
|
// import { defineProps } from 'vue'
|
|||
|
</script>
|
|||
|
```
|
|||
|
|
|||
|
## 🔧 替代方案
|
|||
|
|
|||
|
### 如果确实需要 uni-ui
|
|||
|
|
|||
|
如果项目中大量使用了 uni-ui 组件,可以安装完整的 uni-ui:
|
|||
|
|
|||
|
```bash
|
|||
|
npm install @dcloudio/uni-ui
|
|||
|
```
|
|||
|
|
|||
|
然后在 `pages.json` 中配置:
|
|||
|
|
|||
|
```json
|
|||
|
{
|
|||
|
"easycom": {
|
|||
|
"autoscan": true,
|
|||
|
"custom": {
|
|||
|
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 使用原生组件的优势
|
|||
|
|
|||
|
1. ✅ **更小的包体积** - 不需要引入额外的库
|
|||
|
2. ✅ **更好的兼容性** - 原生组件在所有平台都可用
|
|||
|
3. ✅ **更快的编译速度** - 减少依赖解析
|
|||
|
4. ✅ **更灵活的样式** - 可以完全自定义
|
|||
|
|
|||
|
## 🎯 button vs uni-button 对比
|
|||
|
|
|||
|
### 原生 button
|
|||
|
|
|||
|
```vue
|
|||
|
<button class="custom-btn" @click="handleClick">
|
|||
|
点击我
|
|||
|
</button>
|
|||
|
|
|||
|
<style>
|
|||
|
.custom-btn {
|
|||
|
width: 100%;
|
|||
|
height: 80rpx;
|
|||
|
background: #256BFA;
|
|||
|
color: white;
|
|||
|
border-radius: 12rpx;
|
|||
|
border: none;
|
|||
|
}
|
|||
|
|
|||
|
button::after {
|
|||
|
border: none; /* 移除默认边框 */
|
|||
|
}
|
|||
|
</style>
|
|||
|
```
|
|||
|
|
|||
|
**优点:**
|
|||
|
- ✅ 无需安装依赖
|
|||
|
- ✅ 完全自定义样式
|
|||
|
- ✅ 支持所有平台
|
|||
|
- ✅ 性能更好
|
|||
|
|
|||
|
**缺点:**
|
|||
|
- ⚠️ 需要手动重置样式
|
|||
|
- ⚠️ 需要自己实现 loading、disabled 等状态
|
|||
|
|
|||
|
### uni-button
|
|||
|
|
|||
|
```vue
|
|||
|
<uni-button type="primary" @click="handleClick">
|
|||
|
点击我
|
|||
|
</uni-button>
|
|||
|
```
|
|||
|
|
|||
|
**优点:**
|
|||
|
- ✅ 内置多种样式
|
|||
|
- ✅ 自带 loading、disabled 状态
|
|||
|
- ✅ 统一的UI风格
|
|||
|
|
|||
|
**缺点:**
|
|||
|
- ❌ 需要安装 uni-ui
|
|||
|
- ❌ 增加包体积
|
|||
|
- ❌ 自定义样式受限
|
|||
|
|
|||
|
## 📝 button 样式重置模板
|
|||
|
|
|||
|
推荐在全局样式中添加 button 重置:
|
|||
|
|
|||
|
```css
|
|||
|
/* App.vue 或 common.css */
|
|||
|
|
|||
|
/* 重置 button 默认样式 */
|
|||
|
button {
|
|||
|
padding: 0;
|
|||
|
margin: 0;
|
|||
|
border: none;
|
|||
|
background: none;
|
|||
|
font-size: inherit;
|
|||
|
font-family: inherit;
|
|||
|
color: inherit;
|
|||
|
line-height: inherit;
|
|||
|
text-align: inherit;
|
|||
|
cursor: pointer;
|
|||
|
box-sizing: border-box;
|
|||
|
}
|
|||
|
|
|||
|
/* 移除微信小程序 button 的默认边框 */
|
|||
|
button::after {
|
|||
|
border: none;
|
|||
|
}
|
|||
|
|
|||
|
/* 移除 button 按下时的背景色 */
|
|||
|
button:active {
|
|||
|
background-color: inherit;
|
|||
|
}
|
|||
|
|
|||
|
/* 通用按钮样式 */
|
|||
|
.btn {
|
|||
|
display: inline-block;
|
|||
|
padding: 20rpx 40rpx;
|
|||
|
border-radius: 12rpx;
|
|||
|
text-align: center;
|
|||
|
transition: all 0.3s;
|
|||
|
}
|
|||
|
|
|||
|
.btn-primary {
|
|||
|
background: linear-gradient(135deg, #13C57C 0%, #0FA368 100%);
|
|||
|
color: #FFFFFF;
|
|||
|
box-shadow: 0 8rpx 20rpx rgba(19, 197, 124, 0.3);
|
|||
|
}
|
|||
|
|
|||
|
.btn-secondary {
|
|||
|
background: #F7F8FA;
|
|||
|
color: #666666;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## ⚠️ 注意事项
|
|||
|
|
|||
|
### 1. 编译器宏的特殊性
|
|||
|
|
|||
|
```javascript
|
|||
|
// ❌ 错误:不能解构或重命名
|
|||
|
import { defineProps as props } from 'vue'
|
|||
|
|
|||
|
// ❌ 错误:不能在条件语句中使用
|
|||
|
if (someCondition) {
|
|||
|
defineProps({...})
|
|||
|
}
|
|||
|
|
|||
|
// ✅ 正确:直接在顶层使用
|
|||
|
const props = defineProps({...})
|
|||
|
```
|
|||
|
|
|||
|
### 2. TypeScript 支持
|
|||
|
|
|||
|
如果使用 TypeScript,编译器宏会自动获得类型支持:
|
|||
|
|
|||
|
```typescript
|
|||
|
<script setup lang="ts">
|
|||
|
// 自动获得类型提示
|
|||
|
const props = defineProps<{
|
|||
|
name: string
|
|||
|
age?: number
|
|||
|
}>()
|
|||
|
|
|||
|
const emit = defineEmits<{
|
|||
|
update: [value: string]
|
|||
|
change: [id: number]
|
|||
|
}>()
|
|||
|
</script>
|
|||
|
```
|
|||
|
|
|||
|
### 3. 在非 setup 语法中
|
|||
|
|
|||
|
如果不使用 `<script setup>`,需要用传统方式:
|
|||
|
|
|||
|
```vue
|
|||
|
<script>
|
|||
|
export default {
|
|||
|
props: {
|
|||
|
name: String
|
|||
|
},
|
|||
|
emits: ['update'],
|
|||
|
setup(props, { emit, expose }) {
|
|||
|
// ...
|
|||
|
expose({ open, close })
|
|||
|
}
|
|||
|
}
|
|||
|
</script>
|
|||
|
```
|
|||
|
|
|||
|
## ✅ 验证修复
|
|||
|
|
|||
|
修复后,重新编译项目应该看到:
|
|||
|
|
|||
|
1. ✅ 没有 "Rollup failed to resolve import" 错误
|
|||
|
2. ✅ 没有 "defineXxx is a compiler macro" 警告
|
|||
|
3. ✅ 小程序正常编译和运行
|
|||
|
4. ✅ 按钮样式显示正常
|
|||
|
|
|||
|
## 🔍 排查步骤
|
|||
|
|
|||
|
如果修复后仍然有问题:
|
|||
|
|
|||
|
### 1. 清除缓存
|
|||
|
|
|||
|
```
|
|||
|
HBuilderX:
|
|||
|
运行 → 停止运行
|
|||
|
工具 → 清除缓存
|
|||
|
重新运行
|
|||
|
```
|
|||
|
|
|||
|
### 2. 检查所有文件
|
|||
|
|
|||
|
使用全局搜索检查是否还有遗漏:
|
|||
|
|
|||
|
```
|
|||
|
Ctrl + Shift + F
|
|||
|
搜索: import.*defineProps
|
|||
|
搜索: import.*defineEmits
|
|||
|
搜索: import.*defineExpose
|
|||
|
```
|
|||
|
|
|||
|
### 3. 检查 pages.json
|
|||
|
|
|||
|
确保 easycom 配置正确:
|
|||
|
|
|||
|
```json
|
|||
|
{
|
|||
|
"easycom": {
|
|||
|
"autoscan": true,
|
|||
|
"custom": {
|
|||
|
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 4. 重新安装依赖
|
|||
|
|
|||
|
```bash
|
|||
|
# 删除 node_modules 和 lock 文件
|
|||
|
rm -rf node_modules
|
|||
|
rm package-lock.json
|
|||
|
|
|||
|
# 重新安装
|
|||
|
npm install
|
|||
|
```
|
|||
|
|
|||
|
## 📚 相关文档
|
|||
|
|
|||
|
- [Vue 3 script setup 文档](https://cn.vuejs.org/api/sfc-script-setup.html)
|
|||
|
- [uni-app button 组件](https://uniapp.dcloud.net.cn/component/button.html)
|
|||
|
- [uni-ui 文档](https://uniapp.dcloud.net.cn/component/uniui/uni-ui.html)
|
|||
|
- [Vite 构建优化](https://cn.vitejs.dev/guide/dep-pre-bundling.html)
|
|||
|
|
|||
|
## 🎉 总结
|
|||
|
|
|||
|
### 问题
|
|||
|
1. 使用了未安装的 `uni-button` 组件
|
|||
|
2. 错误地从 vue 导入了编译器宏
|
|||
|
|
|||
|
### 解决
|
|||
|
1. ✅ 将 `<uni-button>` 替换为 `<button>`
|
|||
|
2. ✅ 移除所有编译器宏的 import 语句
|
|||
|
3. ✅ 添加 button 样式重置
|
|||
|
|
|||
|
### 最佳实践
|
|||
|
- 优先使用原生组件
|
|||
|
- 不要导入 Vue 编译器宏
|
|||
|
- 保持依赖简洁
|
|||
|
- 定期清理未使用的代码
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
**所有错误已修复!** 🎉
|
|||
|
|
|||
|
现在可以正常编译和运行小程序了。
|
|||
|
|