当前位置:网站首页>[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 templates
vite The plug-in
Each hook functions
vite The plug-in
Hook execution sequence
How 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-dev
To configure
.eslintrc
: configure connections [4]install
prettier
( Optional )
pnpm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev
To 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-vue3
In the project vite Version and root directoryvite-plugin-template
Consistent versions of ;At the same time
examples/vite-vue3
In the projectvite-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
andexamples/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 :
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 bepre
orpost
,pre
Compared withpost
Execute first ;apply
: Values can bebuild
orserve
It can also be a function , Indicates that they are available only inbuild
orserve
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)
: 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
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 ;
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
边栏推荐
- 学习笔记4--高精度地图关键技术(下)
- 《通信软件开发与应用》课程结业报告
- SLAM 01.人类识别环境&路径的模型建立
- Design and Simulation of fuzzy PID control system for liquid level of double tank (matlab/simulink)
- AD20 制作 Logo
- SAP UI5 ObjectPageLayout 控件使用方法分享
- isEmpty 和 isBlank 的用法区别
- Qt实现json解析
- How does redis implement multiple zones?
- 九度 1480:最大上升子序列和(动态规划思想求最值)
猜你喜欢
《天天数学》连载58:二月二十七日
Pseudo class elements -- before and after
SAP UI5 ObjectPageLayout 控件使用方法分享
ConstraintLayout的流式布局Flow
isEmpty 和 isBlank 的用法区别
How do programmers live as they like?
@Serializedname annotation use
Redis如何实现多可用区?
How to plan the career of a programmer?
B站大量虚拟主播被集体强制退款:收入蒸发,还倒欠B站;乔布斯被追授美国总统自由勋章;Grafana 9 发布|极客头条...
随机推荐
Go项目实战—参数绑定,类型转换
最全是一次I2C总结
《通信软件开发与应用》课程结业报告
【黑马早报】罗永浩回应调侃东方甄选;董卿丈夫密春雷被执行超7亿;吉利正式收购魅族;华为发布问界M7;豆瓣为周杰伦专辑提前开分道歉...
《微信小程序-基础篇》小程序中的事件与冒泡
QT VT100 parser
TypeError: Cannot read properties of undefined (reading ‘cancelToken‘)
Swift tableview style (I) system basic
[JS] array dimensionality reduction
各位大佬,我测试起了3条线程同时往3个mysql表中写入,每条线程分别写入100000条数据,用了f
ConstraintLayout的流式布局Flow
vscode的快捷键
Redis如何实现多可用区?
《天天数学》连载58:二月二十七日
非技术部门,如何参与 DevOps?
钉钉、企微、飞书学会赚钱了吗?
请问大佬们 有遇到过flink cdc mongdb 执行flinksql 遇到这样的问题的么?
uniapp + uniCloud+unipay 实现微信小程序支付功能
Shortcut keys for vscode
《剑来》语句摘录(七)