欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用vue3+TS實現(xiàn)簡易組件庫的全過程

 更新時間:2022年03月31日 11:52:59   作者:只與明月聽  
當市面上主流的組件庫不能滿足我們業(yè)務需求的時候,那么我們就有必要開發(fā)一套屬于自己團隊的組件庫,下面這篇文章主要給大家介紹了如何使用vue3+TS實現(xiàn)簡易組件庫的相關(guān)資料,需要的朋友可以參考下

前置

首先下載vue-cli,搭建我們的環(huán)境,vue-create-luckyUi,選擇vue3和TypeScript 。在src目錄下創(chuàng)建package作為組件目錄。再安裝bootstrap,用bootstrap里面的樣式來完成我們的組件。

組件編寫

dropdown

首先查看boorstrap文檔,是這樣用的

<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-expanded="false">
    Dropdown button
  </button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <a class="dropdown-item" href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >Action</a>
    <a class="dropdown-item" href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >Another action</a>
    <a class="dropdown-item" href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >Something else here</a>
  </div>
</div>

首先那個button按鈕就是我們dropdown按鈕的內(nèi)容,將這部分作為屬性傳入,而dropdown-menu的內(nèi)容是作為dropdown-item的,明顯這里不能固定寫三個,這里就用插槽占位,再封裝一個dropdown-item組件。

首先dropdown組件內(nèi)容如下:

<template>
  <div class="dropdown" ref="dropdownRef">
    <a
      href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" 
      class="btn btn-outline-light my-2 dropdown-toggle"
      @click.prevent="toggleOpen"
    >
      {{ title }}
    </a>
    <ul class="dropdown-menu" :style="{ display: 'block' }" v-if="isOpen">
      <slot></slot>
    </ul>
  </div>
</template>

dropdown-item的內(nèi)容就是:

<template>
  <li
    class="dropdown-option"
    :class="{'is-disabled': disabled}"
  >
    <slot></slot>
  </li>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  name: "DropdownItem",
  props: {
    disabled: {
      type: Boolean,
      default: false
    }
  }
})
</script>

<style>
.dropdown-option.is-disabled * {
  color: #6c757d;
  pointer-events: none;
  background-color: transparent;
}
</style>

還要實現(xiàn)一個點擊dropdown,dropdown-item會隨之收起來的功能,這個比較簡單,在dropdown上綁定一個點擊事件來控制變量isOpen為true或者false,在加上v-if即可實現(xiàn)功能。接下來還要實現(xiàn)一個點擊頁面的其他地方也能實現(xiàn)dropdown-item收縮,這里有兩個思路:

  • 首先在document上添加一個click事件,一旦觸發(fā)就設置isOpen為false,給dropdown也添加一個點擊事件,加上一個事件修飾符stop來阻止事件冒泡,這樣除了點擊dropdown意外的任何地方,document都會觸發(fā)點擊事件。
  • 第二個思路就是讓事件冒泡到document,通過判斷事件對象包不包括我們的目標對象,如果不包括說明點擊的是頁面的其他地方,就設置isOpen為false。這里用了到了組合式api,新建文件package/hooks/useClickOutside.ts,
import { ref, onMounted, onUnmounted, Ref } from 'vue'
const useClickOutside = (elementRef: Ref<null | HTMLElement>) => {
  const isClickOutside = ref(false)
  const handler = (e: MouseEvent) => {
    if (elementRef.value) {
      if (elementRef.value.contains(e.target as HTMLElement)) {
        isClickOutside.value = false
      } else {
        isClickOutside.value = true
      }
    }
  }
  onMounted(() => {
    document.addEventListener('click', handler)
  })
  onUnmounted(() => {
    document.removeEventListener('click', handler)
  })
  return isClickOutside
}

export default useClickOutside

然后直接導入即可使用定義的useClickOutside函數(shù)。這里監(jiān)聽isClickOutside的狀態(tài)來更改isOpen的狀態(tài)。

import useClickOutside from "../hooks/useClickOutside";
...
const isClickOutside = useClickOutside(dropdownRef);

watch(isClickOutside, () => {
  if (isOpen.value && isClickOutside.value) {
    isOpen.value = false;
  }
});

form

首先看下文檔用法

<form>
  <div class="form-group">
    <label for="exampleInputEmail1">Email address</label>
    <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
    <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
  </div>
  <div class="form-group">
    <label for="exampleInputPassword1">Password</label>
    <input type="password" class="form-control" id="exampleInputPassword1">
  </div>
  <div class="form-group form-check">
    <input type="checkbox" class="form-check-input" id="exampleCheck1">
    <label class="form-check-label" for="exampleCheck1">Check me out</label>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

首先編寫ValidateForm組件:

<template>
  <form class="validate-form-container">
    <slot name="default"></slot>
    <div class="submit-area" @click.prevent="submitForm">
      <slot name="submit">
        <button type="submit" class="btn btn-primary">提交</button>
      </slot>
    </div>
  </form>
</template>
<script lang="ts">
import { defineComponent, onUnmounted } from 'vue'
import mitt from 'mitt'
type ValidateFunc = () => boolean
export const emitter = mitt()
export default defineComponent({
  emits: ['form-submit'],
  setup(props, context) {
    let funcArr: ValidateFunc[] = []
    const submitForm = () => {
      const result = funcArr.map(func => func()).every(result => result)
      context.emit('form-submit', result)
    }
    const callback = (func?: ValidateFunc) => {
      if (func) {
        funcArr.push(func)
      }
    }
    emitter.on('form-item-created', callback)
    onUnmounted(() => {
      emitter.off('form-item-created', callback)
      funcArr = []
    })
    return {
      submitForm
    }
  }
})
</script>

接著編寫ValidateInput.vue組件:

<template>
  <div class="validate-input-container pb-3">
    <input
      class="form-control"
      :class="{'is-invalid': inputRef.error}"
      @blur="validateInput"
      v-model="inputRef.val"
      v-bind="$attrs"
    >
    <span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, PropType, onMounted, computed } from 'vue'
import { emitter } from './ValidateForm.vue'
const emailReg = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
interface RuleProp {
  type: 'required' | 'email' | 'custom';
  message: string;
  validator?: () => boolean;
}
export type RulesProp = RuleProp[]
export type TagType = 'input'
export default defineComponent({
  props: {
    rules: Array as PropType<RulesProp>,
    modelValue: String,
    tag: {
      type: String as PropType<TagType>,
      default: 'input'
    }
  },
  inheritAttrs: false,
  setup(props, context) {
    const inputRef = reactive({
      val: computed({
        get: () => props.modelValue || '',
        set: val => {
          context.emit('update:modelValue', val)
        }
      }),
      error: false,
      message: ''
    })
    const validateInput = () => {
      if (props.rules) {
        const allPassed = props.rules.every(rule => {
          let passed = true
          inputRef.message = rule.message
          switch (rule.type) {
            case 'required':
              passed = (inputRef.val.trim() !== '')
              break
            case 'email':
              passed = emailReg.test(inputRef.val)
              break
            case 'custom':
              passed = rule.validator ? rule.validator() : true
              break
            default:
              break
          }
          return passed
        })
        inputRef.error = !allPassed
        return allPassed
      }
      return true
    }
    onMounted(() => {
      emitter.emit('form-item-created', validateInput)
    })
    return {
      inputRef,
      validateInput
    }
  }
})
</script>

這里核心的地方有兩點:

  • 自定義組件實現(xiàn)v-model,vue2中自定義組件實現(xiàn)v-mdel必須要綁定一個value屬性和input事件,在input事件中將輸入的值傳遞給value。在vue3中就需要綁定一個modelValue和update:modelValue事件
  • 還有就是父子組件之間的傳值問題,因為有插槽,沒辦法使用常規(guī)的屬性傳值,這里使用的事件傳值采用了一個第三方庫mitt。在父組件中通過emitter.on('form-item-created', callback)來注冊事件,在子組件中通過emitter.emit('form-item-created', validateInput)觸發(fā)事件。

驗證

新建文件package/index.ts

import 'bootstrap/dist/css/bootstrap.min.css'
//導入組件
import Dropdown from "./Dropdown/Dropdown.vue";
import DropdownItem from "./Dropdown/DropdownItem.vue";

const components = [
  Dropdown,
  DropdownItem
]

const install = (Vue: any) => {
  components.forEach((_: any) => {
    Vue.component(_.name, _);
  });
};

export default {
  install
};

將寫的組件依次導入,然后定義一個install函數(shù),該函數(shù)有一個Vue實例的參數(shù),在函數(shù)中依次遍歷我們的導入組件數(shù)組,然后將組件掛載到vue實例上,導出install函數(shù)。

在根目錄下的main.ts上使用我們的新組件:

import { createApp } from 'vue'
import App from './App.vue'
import luckyUi from './package/index';
const app = createApp(App)
app.use(luckyUi);
app.mount('#app')

在app.vue中進行測試:

<template>
  <div>
    <div class="dropdown">
      <!-- 測試dropdown -->
      <dropdown :title="`你好啊`">
        <dropdown-item><a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >王大</a> </dropdown-item>
        <dropdown-item>
          <a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >王二</a>
        </dropdown-item>
        <dropdown-item disabled
          ><a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  class="dropdown-item">王三</a></dropdown-item
        >
        <dropdown-item
          ><a href="#" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  class="dropdown-item">王四</a></dropdown-item
        >
      </dropdown>
    </div>
  </div>
</template>

最后使用vue自帶的腳手架進行打包,詳細可看文檔

在package中配置打包命令:

"lib": "vue-cli-service build --target lib --name lucky-ui ./src/package/index.ts"

運行npm run lib即可在dist目錄下查看。

代碼地址

總結(jié)

到此這篇關(guān)于使用vue3+TS實現(xiàn)簡易組件庫的文章就介紹到這了,更多相關(guān)vue3+TS簡易組件庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue3中reactive不能直接賦值的解決方案

    vue3中reactive不能直接賦值的解決方案

    這篇文章主要介紹了vue3中reactive不能直接賦值的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • vue項目中添加electron的詳細代碼

    vue項目中添加electron的詳細代碼

    這篇文章通過實例代碼給大家介紹了vue項目中添加electron的方法,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-11-11
  • vue點擊右鍵出現(xiàn)自定義操作菜單實現(xiàn)代碼

    vue點擊右鍵出現(xiàn)自定義操作菜單實現(xiàn)代碼

    這篇文章主要給大家介紹了關(guān)于vue點擊右鍵出現(xiàn)自定義操作菜單實現(xiàn)的相關(guān)資料,在網(wǎng)頁中我們也希望可以像桌面軟件一樣,點擊右鍵后出現(xiàn)操作菜單,對選中的數(shù)據(jù)項進行相應的操作,需要的朋友可以參考下
    2023-08-08
  • vue+elementUI顯示表格指定列合計數(shù)據(jù)方式

    vue+elementUI顯示表格指定列合計數(shù)據(jù)方式

    這篇文章主要介紹了vue+elementUI顯示表格指定列合計數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Vue開發(fā)環(huán)境跨域訪問問題

    Vue開發(fā)環(huán)境跨域訪問問題

    這篇文章主要介紹了Vue開發(fā)環(huán)境跨域訪問問題,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-01-01
  • vue結(jié)合leaflet實現(xiàn)地圖放大鏡

    vue結(jié)合leaflet實現(xiàn)地圖放大鏡

    放大鏡在很多地方都可以使用的到,本文主要介紹了vue結(jié)合leaflet實現(xiàn)地圖放大鏡,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • Vue.js學習示例分享

    Vue.js學習示例分享

    本篇和大家分享的是學習Vuejs的總結(jié)和調(diào)用webapi的一個小示例;具有一定的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • 詳解新手使用vue-router傳參時注意事項

    詳解新手使用vue-router傳參時注意事項

    這篇文章主要介紹了詳解新手使用vue-router傳參時注意事項,詳細的介紹了幾種常見錯誤,需要的朋友們下面隨著小編來一起學習學習吧
    2019-06-06
  • vue使用screenfull插件實現(xiàn)全屏功能

    vue使用screenfull插件實現(xiàn)全屏功能

    這篇文章主要為大家詳細介紹了vue使用screenfull插件實現(xiàn)全屏功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • 詳解Vue ElementUI手動上傳excel文件到服務器

    詳解Vue ElementUI手動上傳excel文件到服務器

    這篇文章主要介紹了詳解Vue ElementUI手動上傳excel文件到服務器,對ElementUI感興趣的同學,可以參考下
    2021-05-05

最新評論