2-3-7 Vue3 组件封装

Posted 沿着路走到底

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2-3-7 Vue3 组件封装相关的知识,希望对你有一定的参考价值。

展示类组件封装

当属性的绘制完全依赖属性时,封装变得非常容易:

function Button(style, text : style : any, text : string)
    return <button style=style>text</button>

容器类组件

如果一个组件是容器,`vue` 是通过slot来处理的。

const ButtonWithSlots = (_ : any, context : any) => 
  return <button>context.slots.default()</button>

在`@vue/babel-plugin-jsx`中,slots被封装到了渲染函数的第二个参数中。 `slots.default` 代表了默认的`slot` 。使用时:

export const ButtonExample02 = () => 
  return <ButtonWithSlots><span>你好!</span></ButtonWithSlots>

当然可以有多个`slot` ,不过建议不要这样,因为这样阅读起来不是非常方便(美观):

const A = (props,  slots ) => (
  <>
    <h1> slots.default ? slots.default() : 'foo' </h1>
    <h2> slots.bar?.() </h2>
  </>
);

const App = 
  setup() 
    const slots = 
      bar: () => <span>B</span>,
    ;
    return () => (
      <A v-slots=slots>
        <div>A</div>
      </A>
    );
  ,
;

// or

const App = 
  setup() 
    const slots = 
      default: () => <div>A</div>,
      bar: () => <span>B</span>,
    ;
    return () => <A v-slots=slots />;
  ,
;

// or you can use object slots when `enableObjectSlots` is not false.
const App = 
  setup() 
    return () => (
      <>
        <A>
          
            default: () => <div>A</div>,
            bar: () => <span>B</span>,
          
        </A>
        <B>() => "foo"</B>
      </>
    );
  ,
;

输入组件

vue Input表单的一个完整的例子

import  ref, defineComponent, PropType, watch  from "vue"

const Input = defineComponent(
  props: 
    onChange: 
      type: Function as PropType<(v: any) => void>,
      required: false,
    ,
    value: 
      type: String,
      required: false,
    ,
  ,
  setup(props) 
    const input = ref<htmlInputElement | null>(null)

    watch(
      () => props.value,
      () => 
        const ipt = input.value!
        if(ipt.value !== props.value) 
          ipt.value = props.value || ""
        
      
    )
    return () => 
      return (
        <input onInput=e => 
          props.onChange &&
            props.onChange(
              (e.target as HTMLInputElement).value
            )
         value=props.value ref=input />
      )
    
  ,
)

export const FormExample = defineComponent(
  setup()
    let formData = 
      username : '张三',
      info : "xxx"
    

    const ver = ref(0)

    return () => 
      return <div key=ver.value>
        <button onClick=() => 

          console.log(formData)
          formData = 
            username : '张三',
            info : "xxx"
          
          ver.value ++
        >重置/提交</button>
        <Input
          value=formData.username
          onChange=(v) => formData.username = v
        />
        <Input
          value=formData.info
          onChange=(v) => formData.info = v
        />
      </div>
    
  

)

对表单数据的封装

可以对表单数据进行一定的封装,使用起来更加方便:

import 
  ref,
  defineComponent,
  PropType,
  watch,
 from "vue"

import Input from '../components/Input'
import useForm from '../hooks/useForm'

export const FromExample02 = defineComponent(
  setup() 
    const form, ver = useForm(
      username: "张三",
      info: "xxx",
    )

    watch(form.getValues(), () => 
      console.log('form data changed', form.getValues().value)
    )

    return () => (
      <div>
        <button
          onClick=() => 
            const values = form.getValues().value
            console.log("submit", values)
            form.setValues(
              username: "张三",
              info: "xxx",
            )
            ver.value++
          
        >
          提交/重置
        </button>
        <Input
          ...form.username
        />
        <Input
          ...form.info
        />
      </div>
    )
  ,
)

封装公共行为

封装事件和计算

function useMouse() 
  const x = ref(0)
  const y = ref(0)

  function handler(e: MouseEvent) 
    x.value = e.x
    y.value = e.y
    console.log('move', e.x, e.y)
  

  window.addEventListener("mousemove", handler)

  onScopeDispose(() => 
    window.removeEventListener("mousemove", handler)
  )

  return  x, y 

公共Scroll事件的封装

封装一个滚动到底部的判定

import  defineComponent  from "vue"

class ScrollDescriptor 
  private left: number = 0
  private top: number = 0
  private scrollHeight: number = 0
  private offsetHeight: number = 0

  private scrollToBottomHandlers: Function[] = []

  public onScrollToBottom(handler: Function) 
    this.scrollToBottomHandlers.push(handler)
    return () => 
      this.scrollToBottomHandlers =
        this.scrollToBottomHandlers.filter(
          (x) => x !== handler
        )
    
  

  private triggerScrollToBottom() 
    this.scrollToBottomHandlers.forEach((h) => h())
  

  public update(
    left: number,
    top: number,
    offsetHeight: number,
    scrollHeight: number
  ) 
    this.left = left
    this.top = top
    this.scrollHeight = scrollHeight
    this.offsetHeight = offsetHeight
    if (this.bottomReached()) 
      this.triggerScrollToBottom()
    
  

  public bottomReached() 
    return this.top + this.offsetHeight >= this.scrollHeight
  


const useScroll = () => 
  const scrollInfo = new ScrollDescriptor()

  const scrollHandler = <T extends HTMLElement>(
    e: Event
  ) => 
    const scroller = e.currentTarget as T
    const left = scroller.scrollLeft
    const top = scroller.scrollTop
    scrollInfo.update(
      left,
      top,
      scroller.offsetHeight,
      scroller.scrollHeight
    )
  

  return 
    onScroll: scrollHandler,
    info: scrollInfo,
  


export const ScrollerExample = defineComponent(
  setup() 
    const  onScroll, info  = useScroll()

    info.onScrollToBottom(() => 
      console.log('here---')
    )
    return () => (
      <div
        onScroll=onScroll
        style=
          height: '600px',
          width: '400px',
          overflow: "scroll",
        
      >
        <div
          style=
            height: '800px',
            width: "100%",
            background: "red",
          
        ></div>
        <div
          style=
            height: '800px',
            width: "100%",
            background: "blue",
          
        ></div>
        <div
          style=
            height: '800px',
            width: "100%",
            background: "yellow",
          
        ></div>
      </div>
    )
  ,
)

封装请求和逻辑

import ref, defineComponent from 'vue'
import Mock from 'mockjs'


type Product = 
  name : string

function useProducts() 
  const list = ref<Product[] | null>(null)

  async function request() 
    list.value = Mock.mock(
      "array|1-10" : [
        name: /iphone|xiaomi|hongmi|huawei|sanxing|google|ms/,
      ],
    ).array
    console.log(list.value)
  
  request()

  return 
    list,
    reload: request,
  



export const ProductList = defineComponent(

  setup() 

    const list, reload = useProducts()

    return () => 
      return <div>

        <button onClick=reload>reload</button>
        <ul>
          list.value?.map( (x, i) => 
            return <li key=i>x.name</li>
          )
        </ul>

      </div>

    
  

)

1

以上是关于2-3-7 Vue3 组件封装的主要内容,如果未能解决你的问题,请参考以下文章

vue2能用vue3封装的组件

手动封装面包屑组件(Vue3)

从0到1封装表单组件(TypeScript + Vue3.0 版)

vue3 封装单选框组件

vue3 封装单选框组件

Vue3封装数字框组件