目录
封装展示类组件
/src/examples/Components.tsx
封装容器类组件
封装输入组件
封装展示类组件
- Coding:纯展示类组件封装
- Coding:样式的处理
/src/examples/button.module.scss
.btn {background-color: rgb(0, 119, 255);
}
.btn:hover {background-color: rgb(0, 204, 255);
}
/src/examples/button.scss
.btn {padding: 10px;font-size: 18px;border: none;color: #fff;
}
.btn:hover {color: #333;
}
/src/examples/Components.tsx
import { defineComponent, computed, ref, reactive, toRefs } from "vue";
import type { PropType, VNode, Ref } from 'vue'
// npm install scss 安装一下scss
import './button.scss'
import classes from "./button.module.scss" // 样式hash值全局不冲突classes.btn
export const Component01 = () => {return <Button text={"Hello button!!"} />
}
// 封装展示类组件
const Button = defineComponent({props: {text: {type: String}},setup({ text }) {return () => {// 样式的处理// CSS Modulereturn <button class={[classes.btn, "btn"]} style={{border: "1px solid red",}}> {text}</button >}}
})
export const Component02 = () => {// 样式的处理return <Button2><span style={{ color: 'red', "font-size": '30px' }}>Hello button</span></Button2>
}
const Button2 = defineComponent({setup(props, { slots }) {const child = slots.default!const Child = (slots.default!) as any as () => JSX.Elementreturn () => {return (<div><button>【child()】:{child()}</button><button>【< Child />】:<Child /></button></div>)}}
})
export const Component03 = () => {return <Panel header={<span>header:Title</span>}v-slots={{header: <span>slots.header:Title</span>}}><span>Hello Content</span></Panel >
}
const Panel = defineComponent({props: {header: Object as PropType<JSX.Element>},setup(props, { slots }) {return () => {return (<div><header>{props.header}</header><header>{slots.header!()}</header>{slots.default!()}</div>)}}
})
封装容器类组件
- Coding:容器类组件封装
- Coding:Copy Props
import { defineComponent, computed, ref, reactive, toRefs } from "vue";
import type { PropType, VNode, Ref } from 'vue'
// 封装容器类组件
export const Component04 = () => {return (<Flex><div><div>a</div><div>b</div><div>c</div><div>d</div></div></Flex>)
}
// 仅是举例,一般Flex不这样用,只有特别重要容器组件才会这样使用
const Flex = defineComponent({setup(props, { slots }) {return () => {const vNode: VNode = slots.default!()[0] // 追求的是语义上的完美if (!vNode.props) {vNode.props = {}}vNode.props.style = {display: 'flex'}console.log(vNode)// <></>展示出来不占层级return <>{vNode}</>}}
})
封装输入组件
- Coding:封装Input组件
追求的是语义上的完美
import { defineComponent, computed, ref, reactive, toRefs } from "vue";
import type { PropType, VNode, Ref } from 'vue'
// 封装输入类组件
export const Component05 = defineComponent({setup() {const form = reactive({username: "abc"})setTimeout(() => {form.username = "def"}, 1000)const { username } = toRefs(form)return () => {return (<Input value={username} />)}}
})
const Input = ({ value }: { value: Ref<string> }) => {console.log('重绘:re render') // 非真实重绘,只是计算重绘,并没有渲染重绘return <input value={value.value} onInput={e => {value.value = (e.target as HTMLInputElement).value}} />
}
export const Component06 = defineComponent({setup() {const { form } = useForm({ // 初始值,表单项username: "abc",password: '123456'})setTimeout(() => {form.username = "def"form.password = "jqkA123"}, 1000)return () => {return (<div><button onClick={() => {console.log(form.getValues())}}>submit</button><Input1{...form.getField('username')}/><Input1value={form.password}onChange={v => {// 会冒泡,覆盖这里form.password = v}}/></div>)}}
})
const Input1 = ({value,onChange }: {value: string,onChange?: (v: string) => void}) => { // value: Ref<string>过度包装return <input value={value}onChange={e => {// 阻止一下这里冒泡e.stopImmediatePropagation()}}onInput={e => {onChange && onChange((e.target as HTMLInputElement).value)}} />
}
// 表单类
class Form<T extends Record<string, any>> {private data: {[key: string]: any}constructor(data: T) {this.data = reactive(data)}public getValue(key: string) {// ver 当前版本号return this.data[key]}public setValue(key: string, value: any) {this.data[key] = value}public getValues = () => {// return unref(this.data)return JSON.parse(JSON.stringify(this.data))}public getField = (key: string): { // === v-modelvalue: anyonChange: (v: any) => void} => {return {value: this.data[key],onChange: (v: any) => {this.data[key] = v}}}
}
// 接口
interface FormOperators<T> {getValues(): T,getField(key: string): { value: any, onChange: (v: any) => void }
}
// 表单函数 代理
function useForm<T extends Record<string, any>>(data: T) {const form = new Form<T>(data)const proxy = new Proxy(form, {get(target, key) {if (key === 'getValues') {return form.getValues} else if (key === 'getField') {return form.getField}return form.getValue(key as string)},set(target, key, value) {form.setValue(key as string, value)return true}})return {form: proxy as any as (T & FormOperators<T>) // 类型转换}
}