目的
表单组件库
学习优点
核心功能
和其他表单组件库的区别
内容结构
TypeScript
TS最重要的核心就是type(类型),和js最大的区别就是把java换成了type,也就是说有类型的js。
所以使用ts:
创建项目
win+R
cmd
cd 
F:\work_mysself\company_workspace\vue-json-schema-form_workspace
F:
vue create vue-json-schema-form

选择自定义,然后通过空格
选中/取消选中,Babel必须的,用于编译JSX文件。
其中router和vuex其实不需要,但是我这里也选中了。

class-style是vue2喜欢用的一种编写方式,但是vue3已经不需要这种写法,所以这里选择n。

是否在ts的基础上使用babel,这是需要的 , 选择y。
这里用历史路由,并用node-sass

为了更好的学习,这里选择Perttier,个人其实更喜欢使用standard config。

这里选择Jest使用。

单独写在各自的文件里面。


完成后,打开项目,终端运行
npm run serve


Prettier
代码格式化工具,并在保存的时候会自动格式化,保持风格一致。
首先需要安装插件

创建配置文件
.prettierrc,它支持json语法:
{
  "desc-semi": "代码里面是否需要写分号:不写",
  "semi": false,
  "desc-singleQuote": "是否使用单引号:习惯单引",
  "singleQuote": true,
  "desc-arrowParens": "匿名函数单个参数时是否写括号:习惯写",
  "arrowParens": "always",
  "desc-trailingComma": " object属性一行行写下去之后,是否在最后加一个逗号",
  "trailingComma": "all"
}
还有很多规则,可以去官网学习并设置。
设置好之后打开src\main.ts,尝试保存,或者shift+alt+f格式化,发现没有变化,这是需要我们配置一下这个文件:


这里我们需要设置工作区,所以在
工作区打钩Format On Save。关闭这个设置页面,会发现项目下多出来一个.vscode文件夹
里面保存vscode针对这一个项目的各种配置。
然后去掉``,再打开
src/main.ts,保存就会发现被规则修改:
vscode还有一个推荐的setting配置:

当切换文件、或者关闭保存、或者vscode突然崩溃的时候,自动保存代码。
vue3中TS如何去定义组件——Component接口
vue3中提供了一个defineComponent函数。
函数的实现直接返回了组件的定义。
如何定义Props的类型
打开src\views\Home.vue
发现初始化模板中已经实现了defineComponent函数。
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
export default defineComponent({
  name: "Home",
  components: {
    HelloWorld,
  },
});
</script>
避免props出现 undefined值,如下代码是正常的:

但如果像下面这样写,还是允许出现
undefined值:
<script lang="ts">
import { defineComponent } from 'vue'
const PropsType = {
  msg: String,
  age: { type: Number, required: true },
}
export default defineComponent({
  name: 'HelloWorld',
  props: PropsType,
  mounted() {
    this.age
  },
})
</script>
这时候需要如下约束:

<script lang="ts">
import { defineComponent } from 'vue'
const PropsType = {
  msg: String,
  age: { type: Number, required: true } as const,
}
export default defineComponent({
  name: 'HelloWorld',
  props: PropsType,
  mounted() {
    this.age
  },
})
</script>

这是放在外面,因为在声明的时候,vue并不知道它要使用的用途,如果放在里面就不会出现这个问题:

h函数
h函数就是用来创建节点的。
比如修改src/main.js
import { createApp, defineComponent, h } from 'vue'
// import App from './App.vue'
import router from './router'
import store from './store'
import HelloWorld from './components/HelloWorld.vue'
const App = defineComponent({
  render() {
    return h('div', { id: 'app' }, [
      h('img', { alt: 'Vue logo', src: './assets/logo.png' }),
      h(HelloWorld, {
        msg: '欢迎来到Vue+TS 的应用现场',
        age: 12,
      })
    ])
  },
})
createApp(App).use(store).use(router).mount('#app')

这里图片不显示是因为如果是正常写的html代码会vue-loader会进行自动寻址。
如果想正常显示可以这么写:
import { createApp, defineComponent, h } from 'vue'
// import App from './App.vue'
import router from './router'
import store from './store'
import HelloWorld from './components/HelloWorld.vue'
const img = require('./assets/logo.png') //eslint-disable-line
const App = defineComponent({
  render() {
    return h('div', { id: 'app' }, [
      h('img', { alt: 'Vue logo', src: img }),
      h(HelloWorld, {
        msg: '欢迎来到Vue+TS 的应用现场',
        age: 12,
      }),
    ])
  },
})
createApp(App).use(store).use(router).mount('#app')

setup
setup自带参数(props,{slots,attrs,emit})
attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用。
使用JSX开发vue3组件
JSX目前有2个解决方案,推荐vueComponent/jsx。
声明使用,需要修改babel.config.js:
module.exports = {
  presets: ['@vue/cli-plugin-babel/preset'],
  plugins: ['@vue/babel-plugin-jsx'], //这样配置jsx就可以用了
}
修改src/main.js:
import { createApp, defineComponent } from 'vue'
// import App from './App.vue'
import router from './router'
import store from './store'
import HelloWorld from './components/HelloWorld.vue'
import App from './App'
createApp(App).use(store).use(router).mount('#app')
新增src\App.tsx:
import { ref, reactive, defineComponent } from 'vue'
const img = require('./assets/logo.png') //eslint-disable-line
export default defineComponent({
  setup() {
    const state = reactive({ name: 'jokcy' })
    const numberRef = ref(1)
    setInterval(() => {
      state.name + -1
      numberRef.value += 1
    }, 1000)
    return () => {
      const number = numberRef.value
      return (
        <div id="app">
          <img src={img} alt="Vue Logo" />
          <p>{state.name + number}</p>
        </div>
      )
    }
  },
})
运行:

JSX还有一个好处就是在编译阶段就防止一些参数问题。比如如下代码:
修改
src\components\HelloWorld.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h1>{{ age }}</h1>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'HelloWorld',
  props: {
    msg: String,
    age: { type: Number, required: true },
  },
})
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
修改src\App.tsx
import { ref, reactive, defineComponent } from 'vue'
import HelloWorld from '@/components/HelloWorld.vue'
const img = require('./assets/logo.png') //eslint-disable-line
export default defineComponent({
  setup() {
    const state = reactive({ name: 'jokcy' })
    const numberRef = ref(1)
    setInterval(() => {
      state.name + -1
      numberRef.value += 1
    }, 1000)
    return () => {
      const number = numberRef.value
      return (
        <div id="app">
          <img src={img} alt="Vue Logo" />
          <p>{state.name + number}</p>
          <HelloWorld age={24} />
        </div>
      )
    }
  },
})
如果age缺失,编译就会提示缺少必备参数。
包括传字符串也会报错:

所以用TSX文件比vue文件要好的地方。
还可以复用很复杂的业务逻辑:
import { ref, reactive, defineComponent } from 'vue'
import HelloWorld from '@/components/HelloWorld.vue'
const img = require('./assets/logo.png') //eslint-disable-line
// function renderHelloWorld(num: number) {
//   return <HelloWorld age={num} />
// }
const renderHelloWorld = (num: number) => {
  return <HelloWorld age={num} />
}
export default defineComponent({
  setup() {
    const state = reactive({ name: 'jokcy' })
    const numberRef = ref(1)
    setInterval(() => {
      state.name + -1
      numberRef.value += 1
    }, 1000)
    return () => {
      const number = numberRef.value
      return (
        <div id="app">
          <img src={img} alt="Vue Logo" />
          <p>{state.name + number}</p>
          {renderHelloWorld(12)}
        </div>
      )
    }
  },
})
2种写法都可以。
而且还可以在html代码中使用v-model等:
import { ref, reactive, defineComponent } from 'vue'
import HelloWorld from '@/components/HelloWorld.vue'
const img = require('./assets/logo.png') //eslint-disable-line
// function renderHelloWorld(num: number) {
//   return <HelloWorld age={num} />
// }
const renderHelloWorld = (num: number) => {
  return <HelloWorld age={num} />
}
export default defineComponent({
  setup() {
    const state = reactive({ name: 'jokcy' })
    const numberRef = ref(1)
    setInterval(() => {
      state.name += 1
      numberRef.value += 1
    }, 1000)
    return () => {
      const number = numberRef.value
      return (
        <div id="app">
          <img src={img} alt="Vue Logo" />
          <p>{state.name + number}</p>
          <input type="text" v-model={state.name} />
          {renderHelloWorld(12)}
        </div>
      )
    }
  },
})
其实我们相当于把每个例如div标签看成一个个h函数即可。
当然可以通过input修改上面显示的值:
export default defineComponent({
  setup() {
    const state = reactive({ name: 'jokcy' })
    const numberRef = ref(1)
    // setInterval(() => {
    //   state.name += 1
    //   numberRef.value += 1
    // }, 1000)
    return () => {
      const number = numberRef.value
      return (
        <div id="app">
          <img src={img} alt="Vue Logo" />
          <p>{state.name + number}</p>
          <input type="text" v-model={state.name} />
          {renderHelloWorld(12)}
        </div>
      )
    }
  },
})










