<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    使用 Vite 和 TypeScript 從零打造一個屬于自己的 Vue3 組件庫

    VSole2022-08-10 09:37:13

    前言

    隨著前端技術的發展,業界涌現出了許多的UI組件庫。例如我們熟知的ElementUI,Vant,AntDesign等等。但是作為一個前端開發者,你知道一個UI組件庫是如何被打造出來的嗎?

    讀完這篇文章你將學會:

    • 如何使用pnpm搭建出一個Monorepo環境
    • 如何使用vite搭建一個基本的Vue3腳手架項目
    • 如何開發調試一個自己的UI組件庫
    • 如何使用vite打包并發布自己的UI組件庫

    作為一個前端擁有一個屬于自己的UI組件庫是一件非常酷的事情。它不僅能讓我們對組件的原理有更深的理解,還能在找工作的時候為自己增色不少。試問有哪個前端不想擁有一套屬于自己的UI組件庫呢?

    本文將使用Vue3和TypeScript來編寫一個組件庫,使用Vite+Vue3來對這個組件庫中的組件進行調試,最后使用vite來對組件庫進行打包并且發布到npm上。最終的產物是一個名為kitty-ui的組件庫。

    話不多說~ 接下來讓我們開始搭建屬于我們自己的UI組件庫吧

    Monorepo環境

    首先我們要了解什么是menorepo及它是如何搭建的吧

    就是指在一個大的項目倉庫中,管理多個模塊/包(package),這種類型的項目大都在項目根目錄下有一個packages文件夾,分多個項目管理。大概結構如下:

    -- packages
      -- pkg1
        --package.json
      -- pkg2
        --package.json
    --package.json
      
    復制代碼
    

    簡單來說就是單倉庫 多項目

    目前很多我們熟知的項目都是采用這種模式,如Vant,ElementUI,Vue3等。打造一個menorepo環境的工具有很多,如:lerna、pnpm、yarn等,這里我們將使用pnpm來開發我們的UI組件庫。

    為什么要使用pnpm?

    因為它簡單高效,它沒有太多雜亂的配置,它相比于lerna操作起來方便太多

    好了,下面我們就開始用pnpm來進行我們的組件庫搭建吧

    使用pnpm

    安裝

    npm install pnpm -g
    復制代碼
    

    初始化package.json

    pnpm init
    復制代碼
    

    新建配置文件 .npmrc

    shamefully-hoist = true
    復制代碼
    

    這里簡單說下為什么要配置shamefully-hoist

    如果某些工具僅在根目錄的node_modules時才有效,可以將其設置為true來提升那些不在根目錄的node_modules,就是將你安裝的依賴包的依賴包的依賴包的...都放到同一級別(扁平化)。說白了就是不設置為true有些包就有可能會出問題。

    monorepo的實現

    接下就是pnpm如何實現monorepo的了。

    為了我們各個項目之間能夠互相引用我們要新建一個pnpm-workspace.yaml文件將我們的包關聯起來

    packages:
        - 'packages/**'
        - 'examples'
    復制代碼
    

    這樣就能將我們項目下的packages目錄和examples目錄關聯起來了,當然如果你想關聯更多目錄你只需要往里面添加即可。根據上面的目錄結構很顯然你在根目錄下新packages和examples文件夾,packages文件夾存放我們開發的包,examples用來調試我們的組件

    examples文件夾就是接下來我們要使用vite搭建一個基本的Vue3腳手架項目的地方

    安裝對應依賴

    我們開發環境中的依賴一般全部安裝在整個項目根目錄下,方便下面我們每個包都可以引用,所以在安裝的時候需要加個 -w

    pnpm i vue@next typescript less -D -w
    復制代碼
    

    因為我們開發的是vue3組件, 所以需要安裝vue3,當然ts肯定是必不可少的(當然如果你想要js開發也是可以的,甚至可以省略到很多配置和寫法。但是ts可以為我們組件加上類型,并且使我們的組件有代碼提示功能,未來ts也將成為主流);less為了我們寫樣式方便,以及使用它的命名空間(這個暫時這里沒用到,后面有時間再補

    • 配置tsconfit.json

    這里的配置就不細說了,可以自行搜索都是代表什么意思。或者你可以先直接復制

    npx tsc --init
    復制代碼
    

    tsconfig.json:

    {
      "compilerOptions": {
        "baseUrl": ".",
        "jsx": "preserve",
        "strict": true,
        "target": "ES2015",
        "module": "ESNext",
        "skipLibCheck": true,
        "esModuleInterop": true,
        "moduleResolution": "Node",
        "lib": ["esnext", "dom"]
      }
    }
    復制代碼
    

    手動搭建一個基于vite的vue3項目

    其實搭建一個vite+vue3項目是非常容易的,因為vite已經幫我們做了大部分事情

    初始化倉庫

    進入examples文件夾,執行

    pnpm init
    復制代碼
    

    安裝vite和@vitejs/plugin-vue

    @vitejs/plugin-vue用來支持.vue文件的轉譯

    pnpm install vite @vitejs/plugin-vue -D -w
    復制代碼
    

    這里安裝的插件都放在根目錄下

    配置vite.config.ts

    新建vite.config.ts

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    export default defineConfig({
        plugins:[vue()]
    })
    復制代碼
    

    新建html文件

    @vitejs/plugin-vue 會默認加載examples下的index.html

    新建index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="main.ts" type="module"></script>
    </body>
    </html>
    復制代碼
    

    注意: vite 是基于esmodule的 所以type="module"

    新建app.vue模板

    <template>
        <div>
            啟動測試
        </div>
    </template>
    復制代碼
    

    新建main.ts

    import {createApp} from 'vue'
    import App from './app.vue'
    const app = createApp(App)
    app.mount('#app')
    復制代碼
    

    此時會發現編譯器會提示個錯誤:找不到模塊“./app.vue”或其相應的類型聲明

    因為直接引入.vue文件 TS會找不到對應的類型聲明;所以需要新建typings(命名沒有明確規定,TS會自動尋找.d.ts文件)文件夾來專門放這些聲明文件。

    typings/vue-shim.d.ts

    TypeScriptTS默認只認ES 模塊。如果你要導入.vue文件就要declare module把他們聲明出來。

    declare module '*.vue' {
        import type { DefineComponent } from "vue";
        const component:DefineComponent<{},{},any>
    }
    復制代碼
    

    配置腳本啟動項目

    最后在package.json文件中配置scripts腳本

    ...
    "scripts": {
        "dev": "vite"
      },
    ...
    復制代碼
    

    然后終端輸入我們熟悉的命令:pnpm run dev

    vite啟動默認端口為3000;在瀏覽器中打開localhost:3000 就會看我們的“啟動測試”頁面。

    本地調試

    新建包文件

    本節可能和目前組件的開發關聯不大,但是未來組件需要引入一些工具方法的時候會用到

    接下來就是要往我們的packages文件夾沖填充內容了。

    • utils包

    一般packages要有utils包來存放我們公共方法,工具函數等

    既然它是一個包,所以我們新建utils目錄后就需要初始化它,讓它變成一個包;終端進入utils文件夾執行:pnpm init 然后會生成一個package.json文件;這里需要改一下包名,我這里將name改成@kitty-ui/utils表示這個utils包是屬于kitty-ui這個組織下的。所以記住發布之前要登錄npm新建一個組織;例如kitty-ui

    {
      "name": "@kitty-ui/utils",
      "version": "1.0.0",
      "description": "",
      "main": "index.ts",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    復制代碼
    

    因為我們使用ts寫的,所以需要將入口文件index.js改為index.ts,并新建index.ts文件:(先導出一個簡單的加法函數)

    export const testfun = (a:number,b:number):number=>{
        return a + b
    }
    復制代碼
    
    • 組件庫包(這里命名為kitty-ui)

    components是我們用來存放各種UI組件的包

    新建components文件夾并執行 pnpm init 生成package.json

    {
      "name": "kitty-ui",
      "version": "1.0.0",
      "description": "",
      "main": "index.ts",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    復制代碼
    

    新建index.ts入口文件并引入utils包

    import {testfun} from '@kitty-ui/utils'
    const result = testfun (1,1)
    console.log(result)
    復制代碼
    
    • esno

    由于組件庫是基于ts的,所以需要安裝esno來執行ts文件便于測試組件之間的引入情況

    控制臺輸入esno xxx.ts即可執行ts文件

    npm i esno -g
    復制代碼
    

    包之間本地調試

    進入components文件夾執行

    pnpm install @kitty-ui/utils
    復制代碼
    

    你會發現pnpm會自動創建個軟鏈接直接指向我們的utils包;此時components下的packages:

    {
      "name": "kitty-ui",
      "version": "1.0.0",
      "description": "",
      "main": "src/index.ts",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "@kitty-ui/utils": "workspace:^1.0.1"
      }
    }
    復制代碼
    

    你會發現它的依賴@kitty-ui/utils對應的版本為:workspace:^1.0.0;因為pnpm是由workspace管理的,所以有一個前綴workspace可以指向utils下的工作空間從而方便本地調試各個包直接的關聯引用。

    到這里基本開發方法我們已經知道啦;接下來就要進入正題了,開發一個button組件

    試著開發一個button組件

    在components文件夾下新建src,同時在src下新建button組件目錄和icon組件目錄(新建icon為了便于調試);此時components文件目錄如下

    -- components
      -- src
        -- button
        -- icon
        -- index.ts
    -- package.json
    復制代碼
    

    讓我們先測試一下我們的button組件能否在我們搭建的examples下的vue3項目本引用~

    首先在button下新建一個簡單的button.vue

    <template>
        <button>測試按鈕</button>
    </template>
    復制代碼
    

    然后在button/index.ts將其導出

    import Button from './button.vue'
    export default Button
    復制代碼
    

    因為我們開發組件庫的時候不可能只有button,所以我們需要一個components/index.ts將我們開發的組件一個個的集中導出

    import Button from './button'
    export {
        Button
    }
    復制代碼
    

    好了,一個組件的大體目錄差不多就是這樣了,接下來請進入我們的examples來看看能否引入我們的button組件

    vue3項目使用button

    上面已經說過執行在workspace執行 pnpm i xxx的時候pnpm會自動創建個軟鏈接直接指向我們的xxx包。

    所以這里我們直接在examples執行:pnpm i kitty-ui

    此時你就會發現packages.json的依賴多了個

    "kitty-ui": "workspace:^1.0.0"
    復制代碼
    

    這時候我們就能直接在我們的測試項目下引入我們本地的components組件庫了,啟動我們的測試項目,來到我們的 examples/app.vue 直接引入Button

    <template>
        <div>
            <Button />
        </div>
    </template>
    <script lang="ts" setup>
    import { Button } from 'kitty-ui'
    </script>
    復制代碼
    

    不出意外的話你的頁面就會展示我們剛剛寫的button組件了

    好了萬事具...(其實還差個打包,這個后面再說~);接下來的工作就是專注于組件的開發了;讓我們回到我們的button組件目錄下(測試頁面不用關,此時我們已經可以邊開發邊調試邊看效果了)

    因為我們的button組件是需要接收很多屬性的,如type、size等等,所以我們要新建個types.ts文件來規范這些屬性

    在button目錄下新建types.ts

    import { ExtractPropTypes } from 'vue'
    export const ButtonType = ['default', 'primary', 'success', 'warning', 'danger']
    export const ButtonSize = ['large', 'normal', 'small', 'mini'];
    export const buttonProps = {
      type: {
        type: String,
        values: ButtonType
      },
      size: {
        type: String,
        values: ButtonSize
      }
    }
    export type ButtonProps = ExtractPropTypes<typeof buttonProps>
    復制代碼
    

    TIPS

    import type 表示只導入類型;ExtractPropTypes是vue3中內置的類型聲明,它的作用是接收一個類型,然后把對應的vue3所接收的props類型提供出來,后面有需要可以直接使用

    很多時候我們在vue中使用一個組件會用的app.use 將組件掛載到全局。要使用app.use函數的話我們需要讓我們的每個組件都提供一個install方法,app.use()的時候就會調用這個方法;

    我們將button/index.ts調整為

    import button from './button.vue'
    import type {App,Plugin} from "vue"
    type SFCWithInstall<T> = T&Plugin
    const withInstall = <T>(comp:T) => {
        (comp as SFCWithInstall<T>).install = (app:App)=>{
            //注冊組件
            app.component((comp as any).name,comp)
        }
        return comp as SFCWithInstall<T>
    }
    const Button = withInstall(button)
    export default Button
    復制代碼
    

    此時我們就可以使用app.use來掛載我們的組件啦

    其實withInstall方法可以做個公共方法放到工具庫里,因為后續每個組件都會用到,這里等后面開發組件的時候再調整

    到這里組件開發的基本配置已經完成,最后我們對我們的組件庫以及工具庫進行打包,打包之前如果要發公共包的話記得將我們的各個包的協議改為MIT開源協議

    ...
    "license": "MIT",
    ...
    復制代碼
    

    vite打包

    配置文件

    打包們這里選擇vite,它有一個庫模式專門為我們來打包這種庫組件的。

    前面已經安裝過vite了,所以這里直接在components下直接新建vite.config.ts(配置參數文件中已經注釋):

    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue"
    export default defineConfig(
        {
            build: {
                target: 'modules',
                //打包文件目錄
                outDir: "es",
                //壓縮
                minify: false,
                //css分離
                //cssCodeSplit: true,
                rollupOptions: {
                    //忽略打包vue文件
                    external: ['vue'],
                    input: ['src/index.ts'],
                    output: [
                        {
                            format: 'es',
                            //不用打包成.es.js,這里我們想把它打包成.js
                            entryFileNames: '[name].js',
                            //讓打包目錄和我們目錄對應
                            preserveModules: true,
                            //配置打包根目錄
                            dir: 'es',
                            preserveModulesRoot: 'src'
                        },
                        {
                            format: 'cjs',
                            entryFileNames: '[name].js',
                            //讓打包目錄和我們目錄對應
                            preserveModules: true,
                            //配置打包根目錄
                            dir: 'lib',
                            preserveModulesRoot: 'src'
                        }
                    ]
                },
                lib: {
                    entry: './index.ts',
                    formats: ['es', 'cjs']
                }
            },
            plugins: [
                vue()
            ]
        }
    )
    復制代碼
    

    這里我們選擇打包cjs(CommonJS)和esm(ESModule)兩種形式,cjs模式主要用于服務端引用(ssr),而esm就是我們現在經常使用的方式,它本身自帶treeShaking而不需要額外配置按需引入(前提是你將模塊分別導出),非常好用~

    其實到這里就已經可以直接打包了;components下執行:pnpm run build你就會發現打包了es和lib兩個目錄

    到這里其實打包的組件庫只能給js項目使用,在ts項目下運行會出現一些錯誤,而且使用的時候還會失去代碼提示功能,這樣的話我們就失去了用ts開發組件庫的意義了。所以我們需要在打包的庫里加入聲明文件(.d.ts)。

    那么如何向打包后的庫里加入聲明文件呢?其實很簡單,只需要引入vite-plugin-dts

    pnpm i vite-plugin-dts -D -w
    復制代碼
    

    然后修改一下我們的vite.config.ts引入這個插件

    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue"
    import dts from 'vite-plugin-dts'
    export default defineConfig(
        {
            build: {...},
            plugins: [
                vue(),
                dts({
                    //指定使用的tsconfig.json為我們整個項目根目錄下掉,如果不配置,你也可以在components下新建tsconfig.json
                    tsConfigFilePath: '../../tsconfig.json'
                }),
                //因為這個插件默認打包到es下,我們想讓lib目錄下也生成聲明文件需要再配置一個
                dts({
                    outputDir:'lib',
                    tsConfigFilePath: '../../tsconfig.json'
                })
            ]
        }
    )
    復制代碼
    

    因為這個插件默認打包到es下,我們想讓lib目錄下也生成聲明文件需要再配置一個dts插件,暫時沒有想到其它更好的處理方法~

    然后執行打包命令你就會發現你的es和lib下就有了聲明文件

    其實后面就可以進行發布了,發布之前更改一下我們components下的package.json如下:

    {
      "name": "kitty-ui",
      "version": "1.0.0",
      "main": "lib/index.js",
      "module":"es/index.js",
      "scripts": {
        "build": "vite build"
      },
      "files": [
        "es",
        "lib"
      ],
      "keywords": [
        "kitty-ui",
        "vue3組件庫"
      ],
      "author": "小月",
      "license": "MIT",
      "description": "",
      "typings": "lib/index.d.ts"
    }
    復制代碼
    

    解釋一下里面部分字段

    pkg.module

    我們組件庫默認入口文件是傳統的CommonJS模塊,但是如果你的環境支持ESModule的話,構建工具會優先使用我們的module入口

    pkg.files

    files是指我們1需要發布到npm上的目錄,因為不可能components下的所有目錄都被發布上去

    開始發布

    做了那么多終于到發布的階段了;其實npm發包是很容易的,就拿我們的組件庫kitty-ui舉例吧

    發布之前記得到npm[1]官網注冊個賬戶,如果你要發布@xx/xx這種包的話需要在npm新建個組織組織組織名就是@后面的,比如我建的組織就是kitty-ui,注冊完之后你就可以發布了

    首先要將我們代碼提交到git倉庫,不然pnpm發布無法通過,后面每次發版記得在對應包下執行 pnpm version patch你就會發現這個包的版本號patch(版本號第三個數) +1 了,同樣的 pnpm version major major和 pnpm version minor 分別對應版本號的第一和第二位增加。

    如果你發布的是公共包的話,在對應包下執行

    pnpm publish --access public
    復制代碼
    

    輸入你的賬戶和密碼(記得輸入密碼的時候是不顯示的,不要以為卡了)正常情況下應該是發布成功了

    注意

    發布的時候要將npm的源切換到npm的官方地址(registry.npmjs.org/[2]); 如果你使用了其它鏡像源的話

    樣式問題

    引入我們打包后的組件你會發現沒有樣式,所以你需要在全局引入我們的style.css才行;如 main.ts中需要

    import 'kitty-ui/es/style.css';
    復制代碼
    

    很顯然這種組件庫并不是我們想要的,我們需要的組件庫是每個css樣式放在每個組件其對應目錄下,這樣就不需要每次都全量導入我們的css樣式。

    下面就讓我們來看下如何把樣式拆分打包

    處理less文件

    首先我們需要做的是將less打包成css然后放到打包后對應的文件目錄下,我們在components下新建build文件夾來存放我們的一些打包工具,然后新建buildLess.ts,首先我們需要先安裝一些工具cpy和fast-glob

    pnpm i cpy fast-glob -D -w
    復制代碼
    
    • cpy

    它可以直接復制我們規定的文件并將我們的文件copy到指定目錄,比如buildLess.ts:

    import cpy from 'cpy'
    import { resolve } from 'path'
    const sourceDir = resolve(__dirname, '../src')
    //lib文件
    const targetLib = resolve(__dirname, '../lib')
    //es文件
    const targetEs = resolve(__dirname, '../es')
    console.log(sourceDir);
    const buildLess = async () => {
        await cpy(`${sourceDir}/**/*.less`, targetLib)
        await cpy(`${sourceDir}/**/*.less`, targetEs)
    }
    buildLess()
    復制代碼
    

    然后在package.json中新增命令

    ...
    "scripts": {
        "build": "vite build",
        "build:less": "esno build/buildLess"
      },
    ...
    復制代碼
    

    終端執行 pnpm run build:less 你就會發現lib和es文件對應目錄下就出現了less文件.

    但是我們最終要的并不是less文件而是css文件,所以我們要將less打包成css,所以我們需要用的less模塊.在ts中引入less因為它本身沒有聲明文件所以會出現類型錯誤,所以我們要先安裝它的 @types/less

    pnpm i --save-dev @types/less -D -w
    復制代碼
    

    buildLess.ts如下(詳細注釋都在代碼中)

    import cpy from 'cpy'
    import { resolve, dirname } from 'path'
    import { promises as fs } from "fs"
    import less from "less"
    import glob from "fast-glob"
    const sourceDir = resolve(__dirname, '../src')
    //lib文件目錄
    const targetLib = resolve(__dirname, '../lib')
    //es文件目錄
    const targetEs = resolve(__dirname, '../es')
    //src目錄
    const srcDir = resolve(__dirname, '../src')
    const buildLess = async () => {
        //直接將less文件復制到打包后目錄
        await cpy(`${sourceDir}/**/*.less`, targetLib)
        await cpy(`${sourceDir}/**/*.less`, targetEs)
        //獲取打包后.less文件目錄(lib和es一樣)
        const lessFils = await glob("**/*.less", { cwd: srcDir, onlyFiles: true })
        //遍歷含有less的目錄
        for (let path in lessFils) {
            const filePath = `${srcDir}/${lessFils[path]}`
            //獲取less文件字符串
            const lessCode = await fs.readFile(filePath, 'utf-8')
            //將less解析成css
            const code = await less.render(lessCode, {
                //指定src下對應less文件的文件夾為目錄
                paths: [srcDir, dirname(filePath)]
            })
            //拿到.css后綴path
            const cssPath = lessFils[path].replace('.less', '.css')
            //將css寫入對應目錄
            await fs.writeFile(resolve(targetLib, cssPath), code.css)
            await fs.writeFile(resolve(targetEs, cssPath), code.css)
        }
    }
    buildLess()
    復制代碼
    

    執行打包命令之后你會發現對應文件夾下多了.css文件

    現在我已經將css文件放入對應的目錄下了,但是我們的相關組件并沒有引入這個css文件;所以我們需要的是每個打包后組件的index.js中出現如:

    import "xxx/xxx.css"
    復制代碼
    

    之類的代碼我們的css才會生效;所以我們需要對vite.config.ts進行相關配置

    首先我們先將.less文件忽略**external: ['vue', /.less/],**這時候打包后的文件中如button/index.js就會出現

    import "./style/index.less";
    復制代碼
    

    然后我們再將打包后代碼的.less換成.css就大功告成了

    ...
    plugins: [
                ...
                {
                    name: 'style',
                    generateBundle(config, bundle) {
                        //這里可以獲取打包后的文件目錄以及代碼code
                        const keys = Object.keys(bundle)
                        for (const key of keys) {
                            const bundler: any = bundle[key as any]
                            //rollup內置方法,將所有輸出文件code中的.less換成.css,因為我們當時沒有打包less文件
                            this.emitFile({
                                type: 'asset',
                                fileName: key,//文件名名不變
                                source: bundler.code.replace(/\.less/g, '.css')
                            })
                        }
                    }
                }
            ...
            ]
    ...
    復制代碼
    

    我們最終的vite.config.ts如下

    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue"
    import dts from 'vite-plugin-dts'
    export default defineConfig(
        {
            build: {
                target: 'modules',
                //打包文件目錄
                outDir: "es",
                //壓縮
                minify: false,
                //css分離
                //cssCodeSplit: true,
                rollupOptions: {
                    //忽略打包vue和.less文件
                    external: ['vue', /\.less/],
                    input: ['src/index.ts'],
                    output: [
                        {
                            format: 'es',
                            //不用打包成.es.js,這里我們想把它打包成.js
                            entryFileNames: '[name].js',
                            //讓打包目錄和我們目錄對應
                            preserveModules: true,
                            //配置打包根目錄
                            dir: 'es',
                            preserveModulesRoot: 'src'
                        },
                        {
                            format: 'cjs',
                            //不用打包成.mjs
                            entryFileNames: '[name].js',
                            //讓打包目錄和我們目錄對應
                            preserveModules: true,
                            //配置打包根目錄
                            dir: 'lib',
                            preserveModulesRoot: 'src'
                        }
                    ]
                },
                lib: {
                    entry: './index.ts',
                    formats: ['es', 'cjs']
                }
            },
            plugins: [
                vue(),
                dts({
                    //指定使用的tsconfig.json為我們整個項目根目錄下掉,如果不配置,你也可以在components下新建tsconfig.json
                    tsConfigFilePath: '../../tsconfig.json'
                }),
                //因為這個插件默認打包到es下,我們想讓lib目錄下也生成聲明文件需要再配置一個
                dts({
                    outputDir: 'lib',
                    tsConfigFilePath: '../../tsconfig.json'
                }),
                {
                    name: 'style',
                    generateBundle(config, bundle) {
                        //這里可以獲取打包后的文件目錄以及代碼code
                        const keys = Object.keys(bundle)
                        for (const key of keys) {
                            const bundler: any = bundle[key as any]
                            //rollup內置方法,將所有輸出文件code中的.less換成.css,因為我們當時沒有打包less文件
                            this.emitFile({
                                type: 'asset',
                                fileName: key,//文件名名不變
                                source: bundler.code.replace(/\.less/g, '.css')
                            })
                        }
                    }
                }
            ]
        }
    )
    復制代碼
    

    最后我們將打包less與打包組件合成一個命令(package.json):

    ...
    "scripts": {
        "build": "vite build & esno build/buildLess"
      },
    ...
    復制代碼
    

    后續直接執行pnpm run build 即可完成所有打包啦

    直接使用

    如果你不想一步步的搭建,想直接使用現成的話,你可以直接把項目clone下來-> kittyui[3],然后你只需要以下幾步便可將其完成

    • 安裝pnpm npm i pnpm -g
    • 安裝esno npm i esno -g
    • 安裝所有依賴 pnpm install
    • 本地測試 進入examples文件夾執行 pnpm run dev 啟動vue3項目
    • 打包 pnpm run build

    寫在最后

    由于作者水平有限,難免會存在一些錯誤或不妥之處,希望各位能夠不吝指出,一定及時修改。如果你對這個項目有更好的想法或者建議也歡迎在評論區提出,不勝感激。

    后續我會對一些常用組件進行開發,每個組件的開發都會以文章的形式展現出來以供大家參考。也歡迎大家將項目fork下來,提交自己組件或者對kittyui的修改到kittyui[4]~

    創作不易,你的點贊就是我的動力!如果感覺對自己有幫助的話就請點個贊吧,感謝~

    vuetypescript
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前言隨著前端技術的發展,業界涌現出了許多的UI組件庫。例如我們熟知的ElementUI,Vant,AntDesign等等。本文將使用Vue3和TypeScript來編寫一個組件庫,使用Vite+Vue3來對這個組件庫中的組件進行調試,最后使用vite來對組件庫進行打包并且發布到npm上。最終的產物是一個名為kitty-ui的組件庫。
    前端采用 D2Admin 、Vue。后端采用 Python 語言 Django 框架。權限認證使用 Jwt,支持多終端認證系統。整合最新技術,模塊插件式開發,前后端分離,開箱即用。核心技術采用 Spring、MyBatis、Shiro 沒有任何其它重度依賴。包括二次封裝組件、utils、hooks、動態菜單、權限校驗、按鈕級別權限控制等功能。
    InfoWorld 公布了 2022 年最佳開源軟件榜單。InfoWorld 的 2022 年 Bossie 獎旨在表彰年度最重要和最具創新性的應用程序開發、devops、數據分析和機器學習工具。Redwood 允許針對各種部署環境,包括 Vercel 和 Netlify 等無服務器平臺。它允許拖放組件來構建儀表板、使用 JavaScript 對象編寫邏輯并連接到任何 API、數據庫或 GraphQL 源。Spinnaker 是一個持續交付平臺,它定位于將產品快速且持續的部署到多種云平臺上。Spinnaker 主要特性:配置一次,隨時運行;隨地部署,集中化管理;開源。
    每次遇到VUE框架都是令我特別頭疼的事情,因為在這個框架下面XSS實在是不太好搞,基本都被和諧了,如果對vue了解一點的話都知道,在vue里面的v-mode是神一般的存在著,而v-html幾乎已經雞雞了。 話不多說,看到整個頁面啥都沒有,唯一一個就是這個手記的功能,硬著頭皮嘗試一下吧。
    on=listeners=$listeners,會給我們帶來驚喜。下面是一個custom-image組件加載過程以及加載出錯的效果。一段摘自vue官網的介紹包含了父作用域中不作為prop被識別 (且獲取) 的attribute, 綁定 。bind=listeners的作用和用法與v-bind=listeners的作用和用法與v?bind=attrs的功效類似,只不過一個屬性一個是事件。
    前 言 歡迎各位大佬們給該項目點一個star https://github.com/flipped-aurora/gin-vue-admin/
    一、漏洞情況 近日,飛利浦發布安全漏洞通告,披露了Vue PACS診療平臺存在的15個安全漏洞,攻擊者可用利用這些漏洞執行任意代碼、更改系統的預期控制流程、訪問敏感信息或導致系統崩潰。本次披露的15個安全漏洞大部...
    近日,飛利浦發布安全漏洞通告,披露了Vue PACS診療平臺存在的15個安全漏洞,攻擊者可用利用這些漏洞執行任意代碼、更改系統的預期控制流程、訪問敏感信息或導致系統崩潰。建議受影響用戶及時更新至安全版本進行防護。
    目前,飛利浦官方已經發布了解決方案修復了漏洞,建議用戶及時確認是否受到漏洞影響,盡快采取修補措施。
    近日,飛利浦官方發布了多個安全漏洞的公告,包括Philips Vue PACS 安全漏洞(CNNVD-
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类