vue3实战技巧 - 组件的封装

目录

封装展示类组件

/src/examples/button.module.scss

/src/examples/button.scss

/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 Module
      return <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.Element
    return () => {
      return (
        <div>
          <button>【child()】:{child()}</button>
          <button>【&lt; Child /&gt;】:<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')}
          />
          <Input1
            value={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-model
    value: any
    onChange: (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>) // 类型转换
  }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值