编写一个webpack插件,统计使用了哪些Ant-Design-Vue组件

背景

项目全量引入了Ant-Design-Vue,需要修改为按需引入,需要统计项目中使用了哪些Ant-Design-Vue组件。不想肉眼去搜索,所以写一个webpack插件,去统计。

在开发中,有考虑写babel插件去处理,但探索的过程中发现Vue单文件的template部分不经过babel-loader处理。

webpack 插件基本结构

class WebpackCustomPlugin {
    apply(compiler) {
     // ...
    }
}

确定执行时机

通过多次尝试,最终选择在compiler的compilation钩子、compilation的succeedModule钩子下执行。

如果在compilation的buildModule钩子下执行,此时的resource._source为null,resource._source为模块对应的代码;

在compilation的succeedModule钩子下执行,此时的temple模版已经被Vue编译成了render函数。

class WebpackCustomPlugin {
    apply(compiler) {
      complier.hooks.compilation.tap('CustomPluginCompiler', ( compilation, compilationParams) => {
           compilation.hooks.succeedModule.tap('CustomPluginCompilation', (module) => {
               // ...
           })
          
      })
    }
}

确定执行范围

我们不需要处理所有的模块,通过一些判断缩小处理范围。

compilation.hooks.succeedModule.tap的参数是module;

通过 if (module.resource.includes('node_modules')) return 排除node_modules下的模块;

通过 if (!module.resource.includes('.vue?vue&type=template')) return 排除不是template模块的代码。

获取模块对应的脚本

通过 module._source._value 获取

使用 @babel/parser 转换为ast

const source = module._source._value
const ast = require('@babel/parse').parse(source, {
    sourceType: 'module'
})

使用 @babel/traverse 遍历AST

const traverse = require('@babel/traverse')
traverse(ast, {
    // ...
})

通过 astexplorer 辅助获取节点类型

在这里我们需要选择@babel/parser,astexplorer默认是acorn,它们转换的结果不一样。

astexplorer: astexplorer.net/

babel types: babeljs.io/docs/babel-…

统计Ant组件的核心代码

const _this = this
traverse(ast, {
    StringLiteral(path) {
        const tag = path.node.value
        if (tag.startsWith('a-')) {
            // #tags是在类中定义的私有属性,用来存储所有用到的Ant组件
            if (_this.#tags.indexOf(tag) === -1) {
                _this.#tags.push(tag)
            }
        }
    }
})

在新的compiler钩子下输出统计结果

compiler.hooks.afterCompile.tap('afterCompile', () => {
    console.log(this.#tags)
})

最后#tags输出为:

['a-alert', 'a-table']

优化输出:

compiler.hooks.afterCompile.tap('afterCompile', () => {
    const tags = this.#tags.map(tag => {
        return tag.split('-').slice(1).map(item => item.charAt(0).toUpperCase() + item.slice(1))
        console.log(tags)
    })
})

去除无效组件

在开发过程中发现tags收集的部分tag,不是对外的组件,不需要导入,比如FormModelItem,只需要导入FormModel就可以了。所以需要过滤。通过判断是否是有效路径来过滤。

const fs = require('fs')
const path = require('path')
function isValidPath(tag) {
    let folderName = tag.split('-').slice(1).join('-')
    let fulPath = `node_modules/ant-design-vue/es/${folderName}`
    fullPath = path.resolve(process.cmd(), fullPath)
    try {
        fs.statSync(fullPath)
        return true
    } catch(error) {
        return false
    }
}

实现Ant-Design-Vue按需加载

import {
    // 复制粘贴上面tags打印的结果
}
from 'ant-design-vue'

const components = [ // 复制粘贴上面tags打印的结果 ]
components.forEach(component => Vue.use(component))

上面这种方式导入,依然会全量导入,需要配合babel-plugin-import 来进行按需加载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值