# vue3-json-schema-form **Repository Path**: xiuxiuyifan/vue3-json-schema-form ## Basic Information - **Project Name**: vue3-json-schema-form - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2022-03-19 - **Last Updated**: 2024-10-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 项目配置 ### 创建项目 在使用 vite 时候选择 eslint + preitter,来进行代码规范的检查 ### 配置 preitter 新建 preitterrc 配置文件 ```javascript //此处的规则供参考,其中多半其实都是默认值,可以根据个人习惯改写 module.exports = { printWidth: 80, //单行长度 tabWidth: 2, //缩进长度 useTabs: false, //使用空格代替tab缩进 semi: true, //句末使用分号 singleQuote: true, //使用单引号 quoteProps: "as-needed", //仅在必需时为对象的key添加引号 jsxSingleQuote: true, // jsx中使用单引号 trailingComma: "all", //多行时尽可能打印尾随逗号 bracketSpacing: true, //在对象前后添加空格-eg: { foo: bar } jsxBracketSameLine: true, //多属性html标签的‘>’折行放置 arrowParens: "always", //单参数箭头函数参数周围使用圆括号-eg: (x) => x requirePragma: false, //无需顶部注释即可格式化 insertPragma: false, //在已被preitter格式化的文件顶部加上标注 proseWrap: "preserve", //不知道怎么翻译 htmlWhitespaceSensitivity: "ignore", //对HTML全局空白不敏感 vueIndentScriptAndStyle: false, //不对vue中的script及style标签缩进 endOfLine: "lf", //结束行形式 embeddedLanguageFormatting: "auto" //对引用代码进行格式化 } ``` ### 配置 vscode 首选项 => 设置 => 搜索 format 关键字 在工作区勾选 format on save 选项,这时候会在当前项目下面生成一个.vscode 的文件夹,里面有对应的配置 setting.json ```json { "editor.formatOnSave": true, "files.autoSave": "onWindowChange" } ``` ## Vue3 开始 ### Props 在使用`ts`写 vue 代码的时候,我们需要给 props 添加类型,并且类型要告诉给`ts`,于是我们定义一个类型就需要这样写。 [注解 props](https://v3.cn.vuejs.org/guide/typescript-support.html#%E6%B3%A8%E8%A7%A3-props) ```typescript import { defineComponent, PropType } from "vue" interface Book { title: string author: string year: number } const Component = defineComponent({ props: { name: String, id: [Number, String], success: { type: String }, callback: { type: Function as PropType<() => void> }, book: { type: Object as PropType, required: true }, metadata: { type: null // metadata 的类型是 any } } }) ``` ### 提取 props 定义 ```typescript const PropsType = { age: { type: Number as PropType, required: true }, book: Object as PropType, callback: { type: Function as PropType<() => void> } } as const // 一定要加,之后就变成一个 readonly 的类型了 export default defineComponent({ props: PropsType }) ``` 由于 required 和 ts 中的 readonly 有什么关系,所以这块要 as const . ![](https://s2.loli.net/2022/03/19/OhAW7gNtceqBu9U.png) ![image-20220114112524306](https://s2.loli.net/2022/03/19/quNdPmO7bEseoiR.png) ### 理解 h 函数,以及.vue 文件是怎么运作的 理解写的.vue 文件就是在写一个 h 函数。 ### setup 的使用 1. setup 函数只会调用一次的 2. setup 函数可以 return 一个函数来当做 render 函数使用的。 ### 使用 jsx 开发 vue3 ```shell npm install @vue/babel-plugin-jsx -D ``` ```json plugins: ["@vue/babel-plugin-jsx"] ``` ### vscode 没有对 props 进行校验提示 在组件开发中都使用 tsx 进行开发,就可以。因为.vue 文件的格式对 ts 的支持并不好。 ## JSON-Schema & ajv 使用 ### 自定义 format ```javascript const Ajv = require("ajv") const schema = { type: "object", properties: { name: { type: "string", format: "test" // 配合下面的 addFormat使用 }, age: { type: "number" }, pets: { type: "array", items: [ { type: "string" }, { type: "number" } ] }, isWorker: { type: "boolean" } }, required: ["name", "age"] } const ajv = new Ajv() ajv.addFormat("test", (data) => { return data === "haha" }) const validate = ajv.compile(schema) const valid = validate({ name: "dfad", age: 10, pets: ["str", 100] }) if (!valid) console.log(validate.errors) ``` ### 自定义关键字 ### 如何转换错误语言 ### 如何自定义错误信息 ## 项目开始 ### 定义接口 props - schema 通过 schema 生成表单 - value 当前表单的值,或者默认值 - locale 语言 - onChange 表单改变了 - uiSchema 定制生成表单的样式 - 其他... - contextRef 传入一个 vue3 的 ref 对象 ```typescript const yourRef = ref({}) onMounted(()=>{ yourRef.value.doValidate() }) ``` ### 渲染一个复杂的数据类型 渲染数组或者对象 - $ref ```json { type: "object", item : [ "age": 10 ] } $ref 表示的是引用了根路径中的某个json子项的路径 ``` json schema 规范总结: 1. type = object 的同级必须要有 properties 属性。 2. type = array 的同级必须要有 items 属性。 3. dependencies 表示表单中存在依赖,这一项依赖于其中的某一项。oneof allof 等的转换 4. string、number、integer、boolean,这四个简单类型的 default 属性直接是一个单值。 5. array、的默认值是一个数组[]。 我们在实现的组件的时候,需要对传进来的 `json schema`进行解析,例如 $refs 等等,所以就需要给 `schema-item`添加一个属性`root schema`,并且在`schema item`这个组件里面解析好 `schema`之后把其传递下去。 把转换过之后的 schema 通过 schema-item 传递下去。 注意事项:组件文件之间可能出现循环引用的问题 , 提示方案:webpack 插件 `circular-dependency-plugin` vite: 还没查 通过 proivde 在 schemaForm 顶层组件上面把 schemaItem 传递下去。同时给 provide 的 key 设置成为 symbol 的值,不会造成冲突。 ```typescript 抽离成 hooks export function useVJSFContext() { const context: { SchemaItem: CommonFieldType } | undefined = inject(SchemaFormContextKey) if (!context) { throw Error("context 是必须注入的!") } return context } ``` vue 中指定一个变量的类型是某个组件的类型 ```typescript const TypeHelperComponent = defineComponent({ props: SchemaFormPropsDefine }) type SchemaItemDefine = typeof TypeHelperComponent const context: { SchemaItem: SchemaItemDefine } | undefined = inject(SchemaFormContextKey) // 类型保护 if (!context) { throw Error("context 是必须要注入的数据") } const { SchemaItem } = context ``` 动手渲染出一个普通对象的 form ![](https://s2.loli.net/2022/03/19/FagPo4DcAtUGjp1.png) ### 数组节点的渲染 抽离 手动 injetc() context 的代码,抽离出去一个函数。(零散的,比较特定的功能,就可以提取成一个函数,拆出去) 固定长度数组的渲染 单类型数组的渲染 ![](https://s2.loli.net/2022/03/19/Hl95zn3AWeJVUGC.png) ![](https://s2.loli.net/2022/03/19/jmVdH952nqGA38N.png) ## 单元测试 Jest API ### 1、describe 将测试用例`分组` ### 2、exists 存在 ## @vue/test-utils 单元测试 API ### 1、mount 得到一个包装器,并提供一些方便的测试方法。 ### 2、find 找到一个元素并返回一个`DOMWrapper`如果找到的话。 ```javascript wrapper.find('span') //=> found; returns DOMWrapper wrapper.find('[data-test="span"]') //=> found; returns DOMWrapper wrapper.find({ ref: 'span' }) //=> found; returns DOMWrapper wrapper.find('p') //=> nothing found; returns ErrorWrapper ``` ### 3、findComponent 找到一个 Vue 组件实例并返回一个`VueWrapper`if found。`ErrorWrapper`否则返回。 | 句法 | 例子 | 细节 | | ---------- | ----------------------------- | -------------------------------------- | | 查询选择器 | `findComponent('.component')` | 匹配标准查询选择器。 | | 组件名称 | `findComponent({name: 'a'})` | 匹配 PascalCase、snake-case、camelCase | | 组件参考 | `findComponent({ref: 'ref'})` | 只能用于已安装组件的直接引用子级 | | 证监会 | `findComponent(Component)` | 直接传递一个导入的组件 | ## 用Vue Cli 构建库 实现 theme 和 core 分开打包的效果。 ```shell vue-cli-service build --target lib --name myLib [entry] ``` ```json { "build:core": "cross-env TYPE=lib vue-cli-service build --target lib --name index lib/index.ts --no-clean", "build:theme": "cross-env TYPE=lib vue-cli-service build --target lib --name theme-default/index lib/theme-default/index.tsx --no-clean", "build": "rimraf dist && yarn build:core && yarn build:theme", } ``` ## 参考文章 1. [Jest 零基础入门](https://juejin.cn/post/7066792153027969032#heading-0) 2.