vue3-json-schema-form中StringField.vue报错 `<script setup>` cannot contain ES module exports vue/no-e
'# vue3-json-schema-form中StringField.vue报错 <script setup> cannot contain ES module exports vue/no-e
一、背景与问题
在使用 vue3-json-schema-form 框架开发表单组件时,开发者常会遇到 StringField.vue 组件报错: <script setup> cannot contain ES module exports vue/no-e
该错误的根源在于 eslint-plugin-vue 的规则 vue/no-module-export,它禁止在 <script setup> 中使用 ES 模块的导出方式。例如:
export default {
name: 'StringField',
props: ['value'],
emits: ['update:Value']
}这种写法在 <script setup> 中是非法的,因为 <script setup> 是基于组合式 API 的封装,需要通过 defineProps 和 defineEmits 显式声明 props 和 emits。
二、基本原理
1. <script setup> 语法原理
Vue 3 的 <script setup> 是基于组合式 API 的封装,其核心机制是将代码逻辑绑定到组件实例上。它通过 defineProps 和 defineEmits 显式声明 props 和 emits,而不是直接使用 export default。
2. ESLint 规则冲突
vue/no-module-export 规则会检测 <script setup> 中的 ES 模块导出(如 export default),这与 <script setup> 的语法规范冲突。
3. JSON Schema 表单组件的特殊性
在 vue3-json-schema-form 中,StringField.vue 作为基础组件,需要通过 props 接收 schema 配置,并通过 emits 触发值更新。这种模式要求严格遵守 <script setup> 的语法规范。
三、环境准备
确保项目已安装以下依赖:
npm install -S vue@3.2.0 eslint-plugin-vue@8.0.0创建 StringField.vue 组件时,需在 .eslintrc.cjs 中配置规则:
module.exports = {
rules: {
'vue/no-module-export': 'warn'
}
}四、核心实现
1. 错误示例:违反 ESLint 规则的代码
<script setup>
export default {
name: 'StringField',
props: ['value'],
emits: ['update:value']
}
</script>错误原因:<script setup> 中直接使用 export default,违反了 ESLint 规则。
2. 正确示例:使用 defineProps 和 defineEmits
<script setup>
const props = defineProps({
value: {
type: String,
required: true
}
})
const emit = defineEmits(['update:value'])
const handleChange = (e) => {
emit('update:value', e.target.value)
}
</script>
<template>
<input type="text" :value="props.value" @input="handleChange" />
</template>关键点:
- 使用
defineProps替代props选项 - 使用
defineEmits替代emits选项 - 通过
props.value访问 props - 通过
emit('update:value', value)触发事件
3. 进阶示例:结合 JSON Schema 配置
<script setup>
const props = defineProps({
schema: {
type: Object,
required: true
},
value: {
type: [String, Number],
required: true
}
})
const emit = defineEmits(['update:value'])
const handleChange = (e) => {
emit('update:value', e.target.value)
}
</script>
<template>
<input
type="text"
:value="props.value"
@input="handleChange"
:placeholder="props.schema?.description || '请输入'"
/>
</template>关键点:
- 接收
schema配置 - 使用
props.schema访问 schema 信息 - 通过
placeholder展示 schema 描述
五、完整案例
1. 完整的 StringField.vue 组件
<template>
<input
type="text"
:value="props.value"
@input="handleChange"
:placeholder="props.schema?.description || '请输入'"
:class="{'is-invalid': props.schema?.errors?.length}"
/>
<div class="error" v-if="props.schema?.errors?.length">
{{ props.schema.errors.join(', ') }}
</div>
</template>
<script setup>
const props = defineProps({
schema: {
type: Object,
required: true
},
value: {
type: [String, Number],
required: true
}
})
const emit = defineEmits(['update:value'])
const handleChange = (e) => {
emit('update:value', e.target.value)
}
</script>
<style scoped>
.is-invalid {
border-color: red;
}
.error {
color: red;
font-size: 12px;
}
</style>2. 父组件使用示例
<template>
<JsonSchemaForm :schema="schema" v-model:value="formData" />
</template>
<script setup>
import { ref } from 'vue'
import JsonSchemaForm from './JsonSchemaForm.vue'
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
description: '姓名'
},
email: {
type: 'string',
description: '邮箱'
}
}
}
const formData = ref({
name: '',
email: ''
})
</script>关键点:
- 使用
v-model:value绑定表单数据 - 通过
schema配置表单字段 - 父组件无需关心子组件实现细节
六、源码解析
1. <script setup> 的执行顺序
// 代码执行顺序
setup() {
// 初始化 props 和 emits
const props = defineProps(...)
const emit = defineEmits(...)
// 业务逻辑
const handleChange = (e) => {
emit('update:value', e.target.value)
}
// 返回值
return {
handleChange
}
}2. defineProps 的类型校验机制
const props = defineProps({
value: {
type: [String, Number],
required: true
}
})type可以是单一类型或数组required表示是否必传default可设置默认值
3. defineEmits 的事件触发机制
const emit = defineEmits(['update:value'])
// 触发事件
emit('update:value', value)- 事件名必须与
v-model绑定的事件名一致 - 可以使用
defineEmits(['update:value'])或defineEmits(['update:Value'])
七、进阶使用
1. 动态绑定 schema 配置
<script setup>
const props = defineProps({
schema: {
type: Object,
required: true
},
value: {
type: [String, Number],
required: true
}
})
const emit = defineEmits(['update:value'])
const handleChange = (e) => {
emit('update:value', e.target.value)
}
</script>2. 增加表单验证逻辑
const props = defineProps({
schema: {
type: Object,
required: true
},
value: {
type: [String, Number],
required: true
}
})
const emit = defineEmits(['update:value'])
const validate = () => {
const errors = []
if (!props.value) {
errors.push('字段不能为空')
}
return errors
}3. 支持多种输入类型
<template>
<input
type="text"
:value="props.value"
@input="handleChange"
:placeholder="props.schema?.description || '请输入'"
:class="{'is-invalid': props.schema?.errors?.length}"
/>
<div class="error" v-if="props.schema?.errors?.length">
{{ props.schema.errors.join(', ') }}
</div>
</template>八、性能与工程实践
1. 表单组件的性能优化
- 避免不必要的重新渲染:使用
v-model保持数据同步 - 使用
v-on懒加载:@input事件改为@change可减少触发次数 - 避免在
setup中使用ref或reactive定义过多变量
2. 安全性考虑
- 输入过滤:使用
v-sanitize过滤用户输入 - 输入校验:在
validate方法中进行严格校验 - 防止 XSS 攻击:使用
v-html时要确保内容安全
3. 异常处理
const handleChange = (e) => {
try {
emit('update:value', e.target.value)
} catch (err) {
console.error('更新值时出错:', err)
}
}4. 组件复用
通过封装 StringField.vue,可以复用在多个表单场景中,如:
- 用户信息表单
- 表单配置页面
- 数据录入界面
九、常见问题与踩坑
1. 常见错误
| 错误类型 | 错误示例 | 解决方案 |
|---|---|---|
| 导出错误 | export default { ... } | 使用 defineProps 和 defineEmits |
| 事件命名错误 | emit('update:Value') | 确保事件名与 v-model 一致 |
| 类型校验错误 | type: String | 使用 type: [String, Number] 等 |
| 未定义 props | props.value | 使用 defineProps 声明 props |
2. 常见错误示例
<script setup>
export default {
props: ['value'],
emits: ['update:value']
}
</script>错误原因:<script setup> 中直接使用 export default
解决方法:使用 defineProps 和 defineEmits
3. 常见性能问题
- 频繁触发
@input事件导致性能问题 - 大量使用
v-model导致内存占用过高
优化建议:
- 使用
@change代替@input - 使用
v-model.lazy延迟更新 - 使用
v-model.number强制类型转换
十、最佳实践
1. 推荐的使用场景
- 需要严格遵循
<script setup>语法规范的项目 - 需要高度可维护的组件结构
- 需要与 JSON Schema 配置深度集成的场景
2. 不推荐的使用场景
- 需要使用
mixins的项目 - 需要兼容 Vue 2 的项目
- 需要使用
this的项目
3. 推荐的实现方式
- 使用
defineProps和defineEmits显式声明 props 和 emits - 使用
v-model进行双向绑定 - 使用
ref和reactive管理组件状态 - 使用
eslint-plugin-vue配置规则
十一、总结
vue3-json-schema-form 中 StringField.vue 组件报错 <script setup> cannot contain ES module exports vue/no-e 的根本原因在于违反了 ESLint 规则。通过正确使用 defineProps 和 defineEmits,可以避免该错误。同时,需要关注表单组件的性能、安全性和可维护性。在开发 JSON Schema 表单组件时,建议使用