中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用vue3+TS實現簡易組件庫

發布時間:2022-03-31 14:46:46 來源:億速云 閱讀:250 作者:小新 欄目:開發技術

這篇文章主要為大家展示了“如何使用vue3+TS實現簡易組件庫”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“如何使用vue3+TS實現簡易組件庫”這篇文章吧。

    前置

    首先下載vue-cli,搭建我們的環境,vue-create-luckyUi,選擇vue3和TypeScript 。在src目錄下創建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按鈕的內容,將這部分作為屬性傳入,而dropdown-menu的內容是作為dropdown-item的,明顯這里不能固定寫三個,這里就用插槽占位,再封裝一個dropdown-item組件。

    首先dropdown組件內容如下:

    <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" : v-if="isOpen">
          <slot></slot>
        </ul>
      </div>
    </template>

    dropdown-item的內容就是:

    <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>

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

    • 首先在document上添加一個click事件,一旦觸發就設置isOpen為false,給dropdown也添加一個點擊事件,加上一個事件修飾符stop來阻止事件冒泡,這樣除了點擊dropdown意外的任何地方,document都會觸發點擊事件。

    • 第二個思路就是讓事件冒泡到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函數。這里監聽isClickOutside的狀態來更改isOpen的狀態。

    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>

    這里核心的地方有兩點:

    • 自定義組件實現v-model,vue2中自定義組件實現v-mdel必須要綁定一個value屬性和input事件,在input事件中將輸入的值傳遞給value。在vue3中就需要綁定一個modelValue和update:modelValue事件

    • 還有就是父子組件之間的傳值問題,因為有插槽,沒辦法使用常規的屬性傳值,這里使用的事件傳值采用了一個第三方庫mitt。在父組件中通過emitter.on('form-item-created', callback)來注冊事件,在子組件中通過emitter.emit('form-item-created', validateInput)觸發事件。

    驗證

    新建文件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函數,該函數有一個Vue實例的參數,在函數中依次遍歷我們的導入組件數組,然后將組件掛載到vue實例上,導出install函數。

    在根目錄下的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目錄下查看。

    以上是“如何使用vue3+TS實現簡易組件庫”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    盐源县| 烟台市| 聊城市| 北辰区| 蚌埠市| 滦平县| 科尔| 丽江市| 资源县| 古蔺县| 崇义县| 隆德县| 建宁县| 易门县| 临泉县| 德江县| 镶黄旗| 贞丰县| 镇巴县| 时尚| 虹口区| 曲阜市| 曲麻莱县| 大埔区| 南涧| 探索| 策勒县| 辛集市| 商南县| 大兴区| 宁强县| 湟中县| 于田县| 苍山县| 山西省| 长顺县| 溧水县| 佛坪县| 霍山县| 高陵县| 太湖县|