前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vite 学习(四) - vite 插件开发预学习

Vite 学习(四) - vite 插件开发预学习

原创
作者头像
测不准
发布2022-02-19 17:14:42
2.1K0
发布2022-02-19 17:14:42
举报
文章被收录于专栏:与前端沾边

上一小节我们对 rollupesbuild 的使用有了基本的了解,了解了二者主要的 hook 使用。vite 插件需要兼容 rollupesbuild 的插件机制,虽然 vite 兼容大部分 rollup 插件,但不是所有钩子都支持,本小节介绍下 vite 中的钩子及插件开发流程。

插件命名

对于 rollup 插件的命名有两种,一种是 rollup-plugin-xxx,这种是社区通用的,不是官方团队开发的,一种是 @rollup/xxx,这种是官方的插件,@rollup 代表组织,个人还不能申请。我们自己开发的 vite 插件命名规则也保持 vite-plugin-xxx 格式。

兼容 rollup 的钩子

以下钩子在服务器启动时被调用:(devServer 启动时,就第一次触发)

  • options
  • buildStart

以下钩子会在每个传入模块请求时被调用:

  • resolveId 找到对应的文件
  • load 文件加载源码
  • transform 源码转化

以下钩子在服务器关闭时被调用:

  • buildEnd
  • closeBundle

如果我们想执行 rollup 特有的钩子,可以配置打包阶段

代码语言:txt
复制
vite.config.js
build: {
  rollupOptions:{
    plugins: [] // 可以执行所有 rollup 的钩子,因为打包用的 rollup
  }
}

注意:modulePased 不会被调用,防止 vite 对代码执行 ast 解析,因为 rollup 处理代码慢,插件执行完传给 esbuild 解析执行,速度效率高,rollup 只负责打包

如果想开发 rollup 插件兼容 vite,需要满足:

  • 没有 modulePased 钩子
  • 打包钩子和输出钩子间没有强耦合, output 阶段的钩子不会执行

vite 独有的钩子

  • config 返回对象,合并到 vite.config.js 中配置中。可以返回两种格式,一种是对象,一种为 promise// 跟 plugins 写的顺序有关系,后面的会覆盖 test() test('pre') config(userConfig) { return new Promise((resolve) => { resolve({ resolve: { alias: { '@/': enforce || '/src/', }, }, }) }) }, config(userConfig) { return { resolve: { alias: { '@/': '/src/', }, }, } }, configResolved(config) { // 真实最终的 config console.log(config.resolve, '-------------') },
  • configResolved 获取最终的 vite 配置,不可修改。
  • configServer devserver 中间件,拿到 server 实例configureServer(server) { console.log(server, '================') // 和 express 类似, 添加中间价,中间件最先执行,在 vite 的中间件前执行 server.middlewares.use((req, res, next) => { if(req.url === '/test') { res.end('hellp test') } else { next() } }) }, 返回一个函数,会在 vite 中间件之后执行 configureServer(server) { return () => { server.middlewares.use((req, res, next) => { if (req.url === '/test') { res.end('hellp test') } else { next() } }) } },
  • transformIndexHtml 获取入口的 html 文件,可以对其进行转换操作,动态修改
  • handleHotUpdate vite 中的热更新处理,打包会被过滤handleHotUpdate(ctx) { console.log(ctx, '==') ctx.server.ws.send({ // 通过 ws 发送数据 type: 'custom', event: 'test', data: { hell0: 'world', }, }) }, // 会被 treeshaking // 在页面进行热更新监听 if (import.meta.hot) { import.meta.hot.on('test', (val) => { console.log(val, '-----') }) }

vite 插件执行机制

可以通过变量控制 vite 插件执行时机,类似 loader 可以控制执行顺序

  • pre 最快被执行的插件, 在 rollup alias 插件后就被调用, 根据 plugins 顺序执行
  • normal vite 核心插件执行后,build 执行前执行执行
  • post vite build 之后,代码构建执行后执行,例如代码打包大小、时间分析工具
代码语言:txt
复制
// 插件执行是个函数,传参
plugins: [vue(), vueJsx(), testPlugins('post'),testPlugins(), testPlugins('pre')],

// 插件是个函数
export default (enforce?: 'pre' | 'post') => {
  return {
    name: 'test',
    enforce,
    // 启动就调用
    buildStart() {
      console.log('buildstart', enforce)
    },
    // 解析文件时调用,请求时;没有 return, undefined 意思当前插件没有找到相关文件
    // 后续走到了 vite 核心插件中,
    // resolveId() {
    //   console.log('resolveid', enforce) // 都是 pre
    // },
    load() {
      /**
       * 有 pre 和 post
       * 大部分不会再拆件中使用,内部核心插件用,
       */
      console.log('load', enforce)
    },
  }
}

hmr 更新机制

我们创建个没框架的 vite 项目,发现触发 render 页面刷新,我们这时去监听 import.meta.hot

代码语言:txt
复制
// 实现 hmr
// 必须要 导出
export function render() {
  document.querySelector("#app").innerHTML = `
<h1>Hello Vi33t99e!</h1>
<a href="https://vitejs.dev/guide/features.html" target="_blank">Documentation</a>
`;
}
render();

// 可能不存在 hot,  vite build,这段会被 treeshaking
if (import.meta.hot) {
  // 文件可以接受 自己的 热更新,内容改变就会热重载
  import.meta.hot.accept();
  // 只针对第一个参数中依赖的文件改变,才会热更新
  import.meta.hot.accept(['style.css'], (newM) => {
    newM.render() // 可以手动触发更新
  }) // 只有依赖的文件变了才更新
  // import.meta.hot.accept((newModule) => {
  // newModule.render()
  // });
}

这里 vite 热更新存在一个小问题,就是热更新前的逻辑会存在,我们需要手动关闭

代码语言:txt
复制
// 这里的 timer 会一直存在,热更新完后会存在多个
let timer = setInterval(() => {
  console.log(8);
}, 1000);
render();

webpack 中的热更新做的是代理,删掉旧的模块对象,用新的,所以不会有代码冲突,类似:

__webpack__module__renderA = new Proxy()vite 是直接执行 render,但是旧的没有删除,会一直保留,我们需要监听 dispose 方法:

代码语言:txt
复制
销毁时触发
import.meta.hot.dispose(() => {
  // 删除已有的
  if(timer) clearInterval(timer)
})

还有其他几种热更新钩子:

代码语言:txt
复制
import.meta.hot.invalidate() // 强制 accept module 之后重新加载应用,页面刷新

// 热更新数据缓存,避免每次热更新重新开始
let index = import.meta.hot.data.cache.getIndex() || 0
import.meta.hot.data.cache = {
  getIndex() {
    return index
  }
}

本节对 vite 中插件钩子和热更新操作做了介绍,和 rollup 开发的不同及注意事项,下一节我们开始实现一个 vite 插件,具体了解每个钩子的使用,如果有问题欢迎留言,谢谢阅读!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 插件命名
  • 兼容 rollup 的钩子
  • vite 独有的钩子
  • vite 插件执行机制
  • hmr 更新机制
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档