当前位置:网站首页>[vite] 1371 - develop vite plug-ins by hand
[vite] 1371 - develop vite plug-ins by hand
2022-07-05 10:32:00 【pingan8787】
Preface
Hello everyone , I'm Master Yi , Now vite Tools are beginning to prevail , Can we do something meaningful , For example, write a vite plug-in unit , How did you like it? ?
We can just take advantage of vite plug-in unit The ecology is not yet very mature , Be a person who makes you happy , Let leaders appreciate , A plug-in to make the community happy , Go hand in hand with it .
If you're right vite If you are interested, you can go to the column : 《Vite From entry to mastery 》:juejin.cn/column/7074954144817086472[1]
Through this article, you can learn
How to create a
vite Plug in templatesvite The plug-in
Each hook functionsvite The plug-in
Hook execution sequenceHow to write your own plug-in
understand vite plug-in unit
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 ?
Quick experience
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 :github.com/jeddygong/vite-templates/tree/master/vite-plugin-template[2]
plug-in unit github: Experience entrance :github.com/jeddygong/vite-plugin-progress[3]
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
Copy code 1.2 install typescript
pnpm i typescript @types/node -D
Copy code 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"
]
}
Copy code 1.4 install vite
// Get into package.json
{
...
"devDependencies": {
"vite": "*"
}
...
}
Copy code 2. To configure eslint and prettier( Optional )
install
eslint
pnpm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-devTo configure
.eslintrc: configure connections [4]install
prettier( Optional )
pnpm i prettier eslint-config-prettier eslint-plugin-prettier --save-devTo configure
.prettierrc: configure connections [5]
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 ');
},
}
}
Copy code 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:github.com/jeddygong/vite-templates/tree/master/vite-plugin-template/examples/vite-react[6]
vite-vue2:github.com/jeddygong/vite-templates/tree/master/vite-plugin-template/examples/vite-vue2[7]
vite-vue3:github.com/jeddygong/vite-templates/tree/master/vite-plugin-template/examples/vite-vue3[8]
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-vue3In the project vite Version and root directoryvite-plugin-templateConsistent versions of ;At the same time
examples/vite-vue3In the projectvite-plugin-templatePoint 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 installBe careful :
examples/vite-vue2andexamples/vite-reactThe 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 -DIn the root directory
package.jsonMiddle 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 devfunction
examplesAny item in ( With vite-vue3 For example )
pnpm run example:vue3Be 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 :

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
Copy code To configure `package.json`
{
...
"scripts": {
...
"prepublishOnly": "pnpm run build",
"release": "npx bumpp --push --tag --commit && pnpm publish",
},
...
}
Copy code Run the release after developing the plug-in
# First step
pnpm run prepublishOnly
# The second step
pnpm run release
Copy code So over here , our vite Plug in templates It's already written , You can clone directly vite-plugin-template Templates [9] 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 bepreorpost,preCompared withpostExecute first ;apply: Values can bebuildorserveIt can also be a function , Indicates that they are available only inbuildorserveCalled 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): It is mainly used to configure the development 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 )

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

Plug-in address :github[10] If you feel good, welcome star ️
The plug-in has been vite Official collection to official documents : Link address [11]
Because the focus of this article is not on the detailed implementation process of this plug-in , So this article will only post the source code for your reference , The detailed introduction will be explained in the next article , Please wait and see !
`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)
}
}
Copy code `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 => {
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) => {
!isExists && fs.mkdirSync(dirPath);
fs.writeFileSync(filePath, JSON.stringify(data));
};
Copy code Last
The series will be a continuous update series , On the whole 《Vite From entry to mastery 》 special column [12], I will mainly explain from the following aspects , Please wait and see !!!

Babies , You can see it here , Why don't you like it
About this article
come from : Master Yi
https://juejin.cn/post/7103165205483356168
边栏推荐
- flink cdc不能监听mysql日志,大家遇到过这个问题吧?
- Applet image height adaptation and setting text line height
- 重磅:国产IDE发布,由阿里研发,完全开源!
- Learning Note 6 - satellite positioning technology (Part 1)
- 橫向滾動的RecycleView一屏顯示五個半,低於五個平均分布
- 一个程序员的职业生涯到底该怎么规划?
- Flink CDC cannot monitor MySQL logs. Have you ever encountered this problem?
- 面试:List 如何根据对象的属性去重?
- [paper reading] ckan: collaborative knowledge aware autonomous network for adviser systems
- Constrained layout flow
猜你喜欢

@SerializedName注解使用

How to plan the career of a programmer?

钉钉、企微、飞书学会赚钱了吗?

《微信小程序-基础篇》小程序中的事件与冒泡

Design and Simulation of fuzzy PID control system for liquid level of double tank (matlab/simulink)

Today in history: the first e-book came out; The inventor of magnetic stripe card was born; The pioneer of handheld computer was born

Workmanager learning 1

StaticLayout的使用详解

Have you learned to make money in Dingding, enterprise micro and Feishu?

学习笔记4--高精度地图关键技术(下)
随机推荐
TypeError: Cannot read properties of undefined (reading ‘cancelToken‘)
学习笔记5--高精地图解决方案
AtCoder Beginner Contest 258「ABCDEFG」
[论文阅读] KGAT: Knowledge Graph Attention Network for Recommendation
The Alipay in place function can't be found, and the Alipay in place function is offline
CSDN always jumps to other positions when editing articles_ CSDN sends articles without moving the mouse
"Everyday Mathematics" serial 58: February 27
微信小程序触底加载与下拉刷新的实现
《通信软件开发与应用》课程结业报告
La vue latérale du cycle affiche cinq demi - écrans en dessous de cinq distributions moyennes
Today in history: the first e-book came out; The inventor of magnetic stripe card was born; The pioneer of handheld computer was born
非技術部門,如何參與 DevOps?
How did automated specification inspection software develop?
Redis如何实现多可用区?
GO项目实战 — Gorm格式化时间字段
NAS与SAN
LDAP概述
A high density 256 channel electrode cap for dry EEG
小程序中自定义行内左滑按钮,类似于qq和wx消息界面那种
学习笔记6--卫星定位技术(上)