当前位置:网站首页>How to write vite plug-ins

How to write vite plug-ins

2022-06-23 13:23:00 Yisu cloud

How to write vite plug-in unit

This article mainly explains “ How to write vite plug-in unit ”, The explanation in the text is simple and clear , Easy to learn and understand , Next, please follow Xiaobian's ideas and go deeper slowly , Study and learn together “ How to write vite plug-in unit ” Well !

    1. What is? vite plug-in unit

    vite In fact, it is an original  ES Module  The new drive Web Develop front-end build tools .

    vite plug-in unit It can be well extended vite Things you can't do by yourself , such as File image compression 、 Yes commonjs Support for 、 Pack progress bar wait .

    2. Why write vite plug-in unit

    I believe that every student here , Up to now, yes webpack You know the relevant configurations and common plug-ins of ;

    vite As a new front-end building tool , It's still very young , It also has a lot of scalability , So why don't we join hands with it now ? Do something more meaningful to you, me and everyone ?

    To write a plug-in , That must start with creating a project , Below vite Plug in common template You can write plug-ins directly in the future clone Use ;

    Plug in common template github: Experience entrance

    plug-in unit github: Experience entrance

    It is recommended that the package manager use priority :pnpm > yarn > npm > cnpm

    Cut a long story short , Let's go straight to work ~

    establish  vite Plug in common template

    1. initialization

    1.1 Create a folder and initialize : Initialize and follow the prompts

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

    1.2 install typescript

    pnpm i typescript @types/node -D

    1.3 To configure 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 install vite

    //  Get into  package.json{    ...    "devDependencies": {        "vite": "*"    }    ...}

    2. To configure eslint and prettier( Optional )

    install eslint

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

    To configure .eslintrc: configure connections

    install prettier ( Optional )

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

    To configure .prettierrc : configure connections

    3. newly added src/index.ts entrance

    import type { PluginOption } from 'vite';export default function vitePluginTemplate(): PluginOption {  return {    //  The plug-in name     name: 'vite-plugin-template',    // pre  Compared with  post  Execute first     enforce: 'pre', // post    //  Indicates that they are available only in  'build'  or  'serve'  Called when in mode     apply: 'build', // apply  It can also be a function     config(config, { command }) {      console.log(' Here is config hook ');    },    configResolved(resolvedConfig) {      console.log(' Here is configResolved hook ');    },    configureServer(server) {      console.log(' Here is configureServer hook ');    },    transformIndexHtml(html) {      console.log(' Here is transformIndexHtml hook ');    },  }}

    Among them vite The plug-in function hook will be explained in detail below ~

    Come here , Then our basic template will be built , But let's think about it now , How should we run this plug-in ?

    So we need to create some examples Example to run this code ;

    4. establish examples Catalog

    I have created three sets of projects here demo, Everybody directly copy That's it , I won't go into details here

    vite-react

    vite-vue2

    vite-vue3

    If your plug-in needs to run more demo, Create your own project ;

    Then we need to configure examples The project under and the plug-in of the current root directory have been debugged ( Let's say examples/vite-vue3 For example ).

    5. To configure examples/vite-vue3 project

    modify examples/vite-vue3/package.json

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

    What the above means is :

    To put examples/vite-vue3 In the project vite Version and root directory vite-plugin-template Consistent versions of ;

    At the same time examples/vite-vue3 In the project vite-plugin-template Point to the plug-ins developed in your current root directory ;

    Introducing plug-ins : examples/vite-vue3/vite.config.ts

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

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

    cd examples/vite-vue3 && pnpm install

    Be careful :

    examples/vite-vue2 and examples/vite-react The configuration of is consistent with this

    reflection :

    Come here , Let's think about it again , We put examples/vite-vue3 The items in are configured , But how should we run it ?

    Go directly to examples/vite-vue3 Run in directory pnpm run build perhaps pnpm run dev ?

    Obviously, this will not run successfully , Because our root directory src/index.ts It can't run directly , So we need to take .ts The file is escaped to .js file ;

    So what do we do ?

    So we have to try to use a light and small tool without configuration tsup 了 .

    6. install tsup Configure run command

    tsup Is a light, small and non configurable , from esbuild Supported build tools ;

    At the same time, it can directly put .ts、.tsx Convert to a different format esm、cjs、iife Tools for ;

    install tsup

    pnpm i tsup -D

    In the root directory package.json Middle configuration

    {  ...  "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. Development environment running

    Development environment running : The real-time monitoring file is repackaged after modification ( Hot update )

    pnpm run dev

    function examples Any item in ( With vite-vue3 For example )

    pnpm run example:vue3

    Be careful :

    If your plug-in will only be in build run , Then set up.

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

    On the contrary, it runs

    pnpm run dev

    Output :

     How to write vite plug-in unit

    Here you can Run while developing 了 , Youyuxi looked at it and said it was refreshing ~

    8. Release

    install bumpp Add version control and tag

    pnpm i bumpp -D

    To configure package.json

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

    Run the release after developing the plug-in

    #  First step pnpm run prepublishOnly#  The second step pnpm run release

    So over here , our vite Plug in templates It's already written , You can clone directly vite-plugin-template Templates Use ;

    If you are right about vite Plug-in hook and Achieve a real vite plug-in unit Interested can continue to look down ;

    vite Plug-in hook hooks People

    1. vite Unique hook

    • enforce : Values can be pre  or  post , pre Compared with post Execute first ;

    • apply : Values can be build or serve It can also be a function , Indicates that they are available only in build or serve Called when in mode ;

    • config(config, env) : Can be in vite Modify before being parsed vite Related configuration of . The hook receives the original user configuration config And a variable that describes the configuration environment env;

    • configResolved(resolvedConfig) : In parsing vite Call after configuration . Use this hook to read and store the final parsed configuration . When a plug-in needs to do something different according to the command it runs , It's very useful .

    • configureServer(server) : Mainly used to configure development The server , by dev-server (connect Applications ) Add custom middleware ;

    • transformIndexHtml(html) : transformation index.html Special hook for . The hook receives the current HTML String and conversion context ;

    • handleHotUpdate(ctx): Perform customization HMR to update , Can pass ws Send custom events to the client ;

    2. vite And rollup The construction phase of the universal hook

    • options(options) : Called when the server starts : obtain 、 manipulation Rollup Options , Strictly speaking , It executes before it belongs to the construction phase ;

    • buildStart(options): Call each time you start a build ;

    • resolveId(source, importer, options): Called on each incoming module request , Create a custom confirmation function , Can be used to locate third-party dependencies ;

    • load(id): Called on each incoming module request , You can customize the loader , Can be used to return customized content ;

    • transform(code, id): Called on each incoming module request , It is mainly used to convert a single module ;

    • buildEnd(): Called at the end of the build phase , The end of construction here just means that all modules have been escaped ;

    3. vite And rollup The output phase of the universal hook

    • outputOptions(options): Accept output parameters ;

    • renderStart(outputOptions, inputOptions): Every time bundle.generate and bundle.write It will be triggered when calling ;

    • augmentChunkHash(chunkInfo): To give chunk increase hash;

    • renderChunk(code, chunk, options): Translate single chunk Trigger when .rollup Output every chunk File will be called ;

    • generateBundle(options, bundle, isWrite): Calling bundle.write Trigger this immediately before hook;

    • writeBundle(options, bundle): Calling bundle.write after , be-all chunk After writing to the file , It will be called once finally writeBundle;

    • closeBundle(): Called when the server shuts down

    4. Plug in hook function hooks Execution order of ( Here's the picture )

     How to write vite plug-in unit

    5. The order in which the plug-ins are executed

    • The alias processing Alias

    • User plug-in settings enforce: 'pre'

    • vite Core plug-ins

    • User plug-in is not set enforce

    • vite Build plug-ins

    • User plug-in settings enforce: 'post'

    • vite Build post plug-ins (minify, manifest, reporting)

    Roll a hand vite plug-in unit

    Let's say vite Pack progress bar Plug in as an example

    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));};

    Thank you for reading , That's all “ How to write vite plug-in unit ” Content. , After learning this article , I believe everyone knows how to write vite I have a deeper understanding of the problem of plug-ins , The specific use needs to be verified by practice . This is billion speed cloud , Xiaobian will push you articles with more relevant knowledge points , Welcome to your attention !

    原网站

    版权声明
    本文为[Yisu cloud]所创,转载请带上原文链接,感谢
    https://yzsam.com/2022/174/202206231247396296.html

    随机推荐