vue3实战技巧 - 组件的封装

时间:2025-03-26 07:26:02

目录

封装展示类组件

/src/examples/

/src/examples/

/src/examples/

封装容器类组件

封装输入组件


封装展示类组件

  • Coding:纯展示类组件封装
  • Coding:样式的处理

/src/examples/

.btn {
  background-color: rgb(0, 119, 255);
}
.btn:hover {
  background-color: rgb(0, 204, 255);
}

/src/examples/

.btn {
  padding: 10px;
  font-size: 18px;
  border: none;
  color: #fff;
}
.btn:hover {
  color: #333;
}

/src/examples/

import { defineComponent, computed, ref, reactive, toRefs } from "vue";
import type { PropType, VNode, Ref } from 'vue'
// npm install scss 安装一下scss
import './'
import classes from "./" // 样式hash值全局不冲突
export const Component01 = () => {
  return <Button text={"Hello button!!"} />
}
// 封装展示类组件
const Button = defineComponent({
  props: {
    text: {
      type: String
    }
  },
  setup({ text }) {
    return () => {
      // 样式的处理
      // CSS Module
      return <button class={[, "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 = !
    const Child = (!) as any as () => 
    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>:Title</span>
    }}>
    <span>Hello Content</span>
  </Panel >
}
const Panel = defineComponent({
  props: {
    header: Object as PropType<>
  },
  setup(props, { slots }) {
    return () => {
      return (
        <div>
          <header>{}</header>
          <header>{!()}</header>
          {!()}
        </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 = !()[0] // 追求的是语义上的完美
      if (!) {
         = {}
      }
       = {
        display: 'flex'
      }
      (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(() => {
       = "def"
    }, 1000)
    const { username } = toRefs(form)
    return () => {
      return (
        <Input value={username} />
      )
    }
  }
})
const Input = ({ value }: { value: Ref<string> }) => {
  ('重绘:re render') // 非真实重绘,只是计算重绘,并没有渲染重绘
  return <input value={} onInput={e => {
     = ( as HTMLInputElement).value
  }} />
}
export const Component06 = defineComponent({
  setup() {
    const { form } = useForm({ // 初始值,表单项
      username: "abc",
      password: '123456'
    })
    setTimeout(() => {
       = "def"
       = "jqkA123"
    }, 1000)
    return () => {
      return (
        <div>
          <button onClick={() => {
            (())
          }}>submit</button>
          <Input1
            {...('username')}
          />
          <Input1
            value={}
            onChange={v => {
              // 会冒泡,覆盖这里
               = v
            }}
          />
        </div>
      )
    }
  }
})
const Input1 = ({
  value,
  onChange }: {
    value: string,
    onChange?: (v: string) => void
  }) => { // value: Ref<string>过度包装
  return <input value={value}
    onChange={e => {
      // 阻止一下这里冒泡
      ()
    }}
    onInput={e => {
      onChange && onChange(( as HTMLInputElement).value)
    }} />
}
// 表单类
class Form<T extends Record<string, any>> {
  private data: {
    [key: string]: any
  }
  constructor(data: T) {
     = reactive(data)
  }
  public getValue(key: string) {// ver 当前版本号
    return [key]
  }
  public setValue(key: string, value: any) {
    [key] = value
  }
  public getValues = () => {
    // return unref()
    return (())
  }
  public getField = (key: string): { // === v-model
    value: any
    onChange: (v: any) => void
  } => {
    return {
      value: [key],
      onChange: (v: any) => {
        [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 
      } else if (key === 'getField') {
        return 
      }
      return (key as string)
    },
    set(target, key, value) {
      (key as string, value)
      return true
    }
  })
  return {
    form: proxy as any as (T & FormOperators<T>) // 类型转换
  }
}