您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“如何使用vue3+TS實現簡易組件庫”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“如何使用vue3+TS實現簡易組件庫”這篇文章吧。
首先下載vue-cli,搭建我們的環境,vue-create-luckyUi,選擇vue3和TypeScript 。在src目錄下創建package作為組件目錄。再安裝bootstrap,用bootstrap里面的樣式來完成我們的組件。
首先查看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> <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實現簡易組件庫”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。