91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

怎么手寫vite插件

發布時間:2022-06-23 09:42:52 來源:億速云 閱讀:180 作者:iii 欄目:開發技術

這篇文章主要講解了“怎么手寫vite插件”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么手寫vite插件”吧!

    1. 什么是 vite 插件

    vite 其實就是一個由原生 ES Module 驅動的新型 Web 開發前端構建工具。

    vite 插件 就可以很好的擴展 vite 自身不能做到的事情,比如 文件圖片的壓縮、 對 commonjs 的支持、 打包進度條 等等。

    2. 為什么要寫 vite 插件

    相信在座的每位同學,到現在對 webpack 的相關配置以及常用插件都了如指掌了吧;

    vite 作為一個新型的前端構建工具,它還很年輕,也有很多擴展性,那么為什么我們不趁現在與它一起攜手前進呢?做一些于你于我于大家更有意義的事呢?

    要想寫一個插件,那必須從創建一個項目開始,下面的 vite 插件通用模板 大家以后寫插件可以直接clone使用;

    插件通用模板 github:體驗入口

    插件 github:體驗入口

    建議包管理器使用優先級:pnpm > yarn > npm > cnpm

    長話短說,直接開干 ~

    創建  vite 插件通用模板

    1. 初始化

    1.1 創建一個文件夾并且初始化:初始化按照提示操作即可

    mkdir vite-plugin-progress && cd vite-plugin-progress && pnpm init

    1.2 安裝 typescript

    pnpm i typescript @types/node -D

    1.3 配置 tsconfig.json

    {
      "compilerOptions": {
        "module": "ESNext",
        "target": "esnext",
        "moduleResolution": "node",
        "strict": true,
        "declaration": true,
        "noUnusedLocals": true,
        "esModuleInterop": true,
        "outDir": "dist",
        "lib": ["ESNext"],
        "sourceMap": false,
        "noEmitOnError": true,
        "noImplicitAny": false
      },
      "include": [
        "src/*",
        "*.d.ts"
      ],
      "exclude": [
        "node_modules",
        "examples",
        "dist"
      ]
    }

    1.4 安裝 vite

    // 進入 package.json
    {
        ...
        "devDependencies": {
            "vite": "*"
        }
        ...
    }

    2. 配置 eslint 和 prettier(可選)

    安裝 eslint

    pnpm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev

    配置 .eslintrc:配置連接

    安裝 prettier (可選)

    pnpm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev

    配置 .prettierrc :配置連接

    3. 新增 src/index.ts 入口

    import type { PluginOption } from 'vite';
    export default function vitePluginTemplate(): PluginOption {
      return {
        // 插件名稱
        name: 'vite-plugin-template',
        // pre 會較于 post 先執行
        enforce: 'pre', // post
        // 指明它們僅在 'build' 或 'serve' 模式時調用
        apply: 'build', // apply 亦可以是一個函數
        config(config, { command }) {
          console.log('這里是config鉤子');
        },
        configResolved(resolvedConfig) {
          console.log('這里是configResolved鉤子');
        },
        configureServer(server) {
          console.log('這里是configureServer鉤子');
        },
        transformIndexHtml(html) {
          console.log('這里是transformIndexHtml鉤子');
        },
      }
    }

    其中的 vite 插件函數鉤子會在下面詳細詳解 ~

    到這里,那么我們的基本模版就建好了,但是我們現在思考一下,我們應該怎么去運行這個插件呢?

    那么我們就需要創建一些 examples 例子來運行這個代碼了;

    4. 創建 examples 目錄

    我這里創建了三套項目 demo,大家直接 copy 就行了,這里就不詳細介紹了

    vite-react

    vite-vue2

    vite-vue3

    如果你的插件需要多跑一些 demo,自行創建項目即可;

    那么下面我們就需要配置 examples 下的項目與當前根目錄的插件做一個聯調了(下面以 examples/vite-vue3 為例)。

    5. 配置 examples/vite-vue3 項目

    修改 examples/vite-vue3/package.json

    {
        ...
        "devDependencies": {
            ...
            "vite": "link:../../node_modules/vite",
            "vite-plugin-template": "link:../../"
        }
    }

    上面意思就是說:

    要把 examples/vite-vue3 項目中的 vite 版本與根目錄 vite-plugin-template 的版本一致;

    同時要把 examples/vite-vue3 項目中的 vite-plugin-template 指向你當前根目錄所開發的插件;

    引入插件: examples/vite-vue3/vite.config.ts

    import template from 'vite-plugin-template';
    export default defineConfig({
        ...
        plugins: [vue(), template()],
        ...
    });

    安裝: cd examples/vite-vue3 && pnpm install

    cd examples/vite-vue3 && pnpm install

    注意:

    examples/vite-vue2 和 examples/vite-react 的配置與這一致

    思考:

    到這里,我們再思考一下,我們把 examples/vite-vue3 中的項目配置好了,但是我們應該怎么去運行呢?

    直接去 examples/vite-vue3 目錄下運行 pnpm run build 或者 pnpm run dev ?

    這樣顯然是不能運行成功的,因為我們的根目錄下的 src/index.ts 是沒法直接運行的,所以我們需要把 .ts 文件轉義成 .js 文件;

    那么我們怎么處理呢?

    那么我們不得不去試著用用一個輕小且無需配置的工具 tsup 了。

    6. 安裝 tsup 配置運行命令

    tsup 是一個輕小且無需配置的,由 esbuild 支持的構建工具;

    同時它可以直接把 .ts、.tsx 轉成不同格式 esm、cjs、iife 的工具;

    安裝 tsup

    pnpm i tsup -D

    在根目錄下的 package.json 中配置

    {
      ...
      "scripts": {
        "dev": "pnpm run build -- --watch --ignore-watch examples",
        "build": "tsup src/index.ts --dts --format cjs,esm",
        "example:react": "cd examples/vite-react && pnpm run build",
        "example:vue2": "cd examples/vite-vue2 && pnpm run build",
        "example:vue3": "cd examples/vite-vue3 && pnpm run build"
      },
      ...
    }

    7. 開發環境運行

    開發環境運行:實時監聽文件修改后重新打包(熱更新)

    pnpm run dev

    運行 examples 中的任意一個項目(以 vite-vue3 為例)

    pnpm run example:vue3

    注意:

    如果你的插件只會在 build 時運行,那就設置

    "example:vue3": "cd examples/vite-vue3 && pnpm run build" ;

    反之就運行

    pnpm run dev

    輸出:

    怎么手寫vite插件

    到這里你就可以 邊開發邊運行 了,尤雨溪看了都說爽歪歪 ~

    8. 發布

    安裝 bumpp 添加版本控制與 tag

    pnpm i bumpp -D

    配置 package.json

    {
      ...
      "scripts": {
        ...
        "prepublishOnly": "pnpm run build",
        "release": "npx bumpp --push --tag --commit && pnpm publish",
      },
      ...
    }

    開發完插件后運行發布

    # 第一步
    pnpm run prepublishOnly
    # 第二步
    pnpm run release

    那么到這里,我們的 vite 插件模板 就已經寫好了,大家可以直接克隆 vite-plugin-template 模板 使用;

    如果你對 vite 的插件鉤子 和 實現一個真正的 vite 插件 感興趣可以繼續往下面看;

    vite 的插件鉤子 hooks 們

    1. vite 獨有的鉤子

    • enforce :值可以是pre 或 post , pre 會較于 post 先執行;

    • apply :值可以是 build 或 serve 亦可以是一個函數,指明它們僅在 build 或 serve 模式時調用;

    • config(config, env) :可以在 vite 被解析之前修改 vite 的相關配置。鉤子接收原始用戶配置 config 和一個描述配置環境的變量env;

    • configResolved(resolvedConfig) :在解析 vite 配置后調用。使用這個鉤子讀取和存儲最終解析的配置。當插件需要根據運行的命令做一些不同的事情時,它很有用。

    • configureServer(server) :主要用來配置開發服務器,為 dev-server (connect 應用程序) 添加自定義的中間件;

    • transformIndexHtml(html) :轉換 index.html 的專用鉤子。鉤子接收當前的 HTML 字符串和轉換上下文;

    • handleHotUpdate(ctx):執行自定義HMR更新,可以通過ws往客戶端發送自定義的事件;

    2. vite 與 rollup 的通用鉤子之構建階段

    • options(options) :在服務器啟動時被調用:獲取、操縱Rollup選項,嚴格意義上來講,它執行于屬于構建階段之前;

    • buildStart(options):在每次開始構建時調用;

    • resolveId(source, importer, options):在每個傳入模塊請求時被調用,創建自定義確認函數,可以用來定位第三方依賴;

    • load(id):在每個傳入模塊請求時被調用,可以自定義加載器,可用來返回自定義的內容;

    • transform(code, id):在每個傳入模塊請求時被調用,主要是用來轉換單個模塊;

    • buildEnd():在構建階段結束后被調用,此處構建結束只是代表所有模塊轉義完成;

    3. vite 與 rollup 的通用鉤子之輸出階段

    • outputOptions(options):接受輸出參數;

    • renderStart(outputOptions, inputOptions):每次 bundle.generate 和 bundle.write 調用時都會被觸發;

    • augmentChunkHash(chunkInfo):用來給 chunk 增加 hash;

    • renderChunk(code, chunk, options):轉譯單個的chunk時觸發。rollup 輸出每一個chunk文件的時候都會調用;

    • generateBundle(options, bundle, isWrite):在調用 bundle.write 之前立即觸發這個 hook;

    • writeBundle(options, bundle):在調用 bundle.write后,所有的chunk都寫入文件后,最后會調用一次 writeBundle;

    • closeBundle():在服務器關閉時被調用

    4. 插件鉤子函數 hooks 的執行順序(如下圖)

    怎么手寫vite插件

    5. 插件的執行順序

    • 別名處理Alias

    • 用戶插件設置enforce: 'pre'

    • vite 核心插件

    • 用戶插件未設置enforce

    • vite 構建插件

    • 用戶插件設置enforce: 'post'

    • vite 構建后置插件(minify, manifest, reporting)

    手擼一個 vite 插件

    下面以 vite 打包進度條 插件為例

    inde.ts

    import type { PluginOption } from 'vite';
    import colors from 'picocolors';
    import progress from 'progress';
    import rd from 'rd';
    import { isExists, getCacheData, setCacheData } from './cache';
    type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
    type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
    type PluginOptions = Merge<
        ProgressBar.ProgressBarOptions,
        {
            /**
             * total number of ticks to complete
             * @default 100
             */
            total?: number;
            /**
             * The format of the progress bar
             */
            format?: string;
        }
    >;
    export default function viteProgressBar(options?: PluginOptions): PluginOption {
        const { cacheTransformCount, cacheChunkCount } = getCacheData()
        let bar: progress;
        const stream = options?.stream || process.stderr;
        let outDir: string;
        let transformCount = 0
        let chunkCount = 0
        let transformed = 0
        let fileCount = 0
        let lastPercent = 0
        let percent = 0
        return {
            name: 'vite-plugin-progress',
            enforce: 'pre',
            apply: 'build',
            config(config, { command }) {
                if (command === 'build') {
                    config.logLevel = 'silent';
                    outDir = config.build?.outDir || 'dist';
                    options = {
                        width: 40,
                        complete: '\u2588',
                        incomplete: '\u2591',
                        ...options
                    };
                    options.total = options?.total || 100;
                    const transforming = isExists ? `${colors.magenta('Transforms:')} :transformCur/:transformTotal | ` : ''
                    const chunks = isExists ? `${colors.magenta('Chunks:')} :chunkCur/:chunkTotal | ` : ''
                    const barText = `${colors.cyan(`[:bar]`)}`
                    const barFormat =
                        options.format ||
                        `${colors.green('Bouilding')} ${barText} :percent | ${transforming}${chunks}Time: :elapseds`
                    delete options.format;
                    bar = new progress(barFormat, options as ProgressBar.ProgressBarOptions);
                    // not cache: Loop files in src directory
                    if (!isExists) {
                        const readDir = rd.readSync('src');
                        const reg = /\.(vue|ts|js|jsx|tsx|css|scss||sass|styl|less)$/gi;
                        readDir.forEach((item) => reg.test(item) && fileCount++);
                    }
                }
            },
            transform(code, id) {
                transformCount++
                // not cache
                if(!isExists) {
                    const reg = /node_modules/gi;
                    if (!reg.test(id) && percent < 0.25) {
                        transformed++
                        percent = +(transformed / (fileCount * 2)).toFixed(2)
                        percent < 0.8 && (lastPercent = percent)
                      }
                    if (percent >= 0.25 && lastPercent <= 0.65) {
                        lastPercent = +(lastPercent + 0.001).toFixed(4)
                    } 
                }
                // go cache
                if (isExists) runCachedData()
                bar.update(lastPercent, {
                    transformTotal: cacheTransformCount,
                    transformCur: transformCount,
                    chunkTotal: cacheChunkCount,
                    chunkCur: 0,
                })
                return {
                    code,
                    map: null
                };
            },
            renderChunk() {
                chunkCount++
                if (lastPercent <= 0.95) 
                    isExists ? runCachedData() : (lastPercent = +(lastPercent + 0.005).toFixed(4))
                bar.update(lastPercent, {
                    transformTotal: cacheTransformCount,
                    transformCur: transformCount,
                    chunkTotal: cacheChunkCount,
                    chunkCur: chunkCount,
                })
                return null
            },
            closeBundle() {
                // close progress
                bar.update(1)
                bar.terminate()
                // set cache data
                setCacheData({
                    cacheTransformCount: transformCount,
                    cacheChunkCount: chunkCount,
                })
                // out successful message
                stream.write(
                    `${colors.cyan(colors.bold(`Build successful. Please see ${outDir} directory`))}`
                );
                stream.write('\n');
                stream.write('\n');
            }
        };
        /**
         * run cache data of progress
         */
        function runCachedData() {
            if (transformCount === 1) {
                stream.write('\n');
                bar.tick({
                    transformTotal: cacheTransformCount,
                    transformCur: transformCount,
                    chunkTotal: cacheChunkCount,
                    chunkCur: 0,
                })
            }
            transformed++
            percent = lastPercent = +(transformed / (cacheTransformCount + cacheChunkCount)).toFixed(2)
        }
    }

    cache.ts

    import fs from 'fs';
    import path from 'path';
    const dirPath = path.join(process.cwd(), 'node_modules', '.progress');
    const filePath = path.join(dirPath, 'index.json');
    export interface ICacheData {
        /**
         * Transform all count
         */
        cacheTransformCount: number;
        /**
         * chunk all count
         */
        cacheChunkCount: number
    }
    /**
     * It has been cached
     * @return boolean
     */
    export const isExists = fs.existsSync(filePath) || false;
    /**
     * Get cached data
     * @returns ICacheData
     */
    export const getCacheData = (): ICacheData =&gt; {
        if (!isExists) return {
            cacheTransformCount: 0,
            cacheChunkCount: 0
        };
        return JSON.parse(fs.readFileSync(filePath, 'utf8'));
    };
    /**
     * Set the data to be cached
     * @returns 
     */
    export const setCacheData = (data: ICacheData) =&gt; {
        !isExists &amp;&amp; fs.mkdirSync(dirPath);
        fs.writeFileSync(filePath, JSON.stringify(data));
    };

    感謝各位的閱讀,以上就是“怎么手寫vite插件”的內容了,經過本文的學習后,相信大家對怎么手寫vite插件這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節

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

    AI

    五大连池市| 诸暨市| 定西市| 宝兴县| 武清区| 海原县| 滁州市| 韩城市| 旌德县| 黑龙江省| 区。| 文登市| 峨眉山市| 鸡东县| 兴山县| 出国| 江城| 奎屯市| 平定县| 常熟市| 顺平县| 冀州市| 社会| 隆昌县| 通山县| 镇雄县| 九寨沟县| 潼南县| 桦川县| 甘肃省| 云安县| 甘南县| 罗甸县| 黄骅市| 长岛县| 黄龙县| 铁岭县| 合山市| 温泉县| 东丰县| 龙南县|