背景
export
是js中用于导出模块的关键字,用法如export const data
、export function getData
等,在项目版本迭代过程中,开发人员会出现把引用处代码给删了,但是忘记删除export const
的情况。
如下图,export const
没删的话,打包会把代码打进去增加代码体积。
本文提供一种方案,可以快速检测项目中无用export
的情况,开发人员可以根据实际考量进行清除。
export导出的检测
js
const { parse, Lang } = require('@ast-grep/napi')
const fg = require('fast-glob')
const path = require('node:path')
const fs = require('node:fs')
const files = fg.sync([path.join(__dirname, '/src/**/*.js')])
const data = []
const addData = (node) => {
const keyword = node?.getMatch?.('A')?.text?.() || ''
if (keyword) {
data.push(keyword)
}
}
files.forEach((file) => {
const source = fs.readFileSync(file, 'utf-8')
const ast = parse(Lang.JavaScript, source)
const root = ast.root()
let node = root.find('export const $A = $B')
addData(node)
node = root.find('export function $A ($B) { $C }')
addData(node)
node = root.find('export let $A = $B')
addData(node)
})
fs.writeFileSync('./data.json', JSON.stringify(data, null, 2))
上述代码很简单,执行流程就是用fast-glob
把所有js文件扫出来,然后借助ast-grep
,把所有的export const
代码找出来。
无用export的识别
第一步我们是把所有的export const
都找出来了,第二步我们需要从中识别出来哪些是有用的,哪些是无用的。从性能、执行速度考虑,我们采用golang实现本步骤,而不用js。
go
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
// 结构用于存储计数结果
type StringCount struct {
String string
Count int
}
func main() {
// 1. 读取 json 文件
jsonData, err := os.ReadFile("data.json")
if err != nil {
fmt.Printf("无法读取json: %v\n", err)
return
}
// 2. 解析 JSON 数据
var data []string
if err := json.Unmarshal(jsonData, &data); err != nil {
fmt.Printf("解析 JSON 失败: %v\n", err)
return
}
// 3. 存储结果的映射
resultMap := make(map[string]int)
var mutex sync.Mutex
var wg sync.WaitGroup
// 4. 遍历指定目录中的文件
searchDir := "./src"
fileCount := 0
isJsVue := func(filePath string) bool {
return strings.HasSuffix(filePath, ".js") || strings.HasSuffix(filePath, ".vue")
}
// 5. 文件处理函数
processFile := func(filePath string) {
defer wg.Done()
if !isJsVue(filePath) {
return
}
content, err := os.ReadFile(filePath)
if err != nil {
return
}
contentStr := string(content)
// 检查每个字符串在文件中的出现次数
for _, searchStr := range data {
count := strings.Count(contentStr, searchStr)
if count > 0 {
mutex.Lock()
resultMap[searchStr] += count
mutex.Unlock()
}
}
}
err = filepath.Walk(searchDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过目录
if info.IsDir() {
return nil
}
if isJsVue(path) {
fileCount++
wg.Add(1)
go processFile(path)
}
return nil
})
if err != nil {
fmt.Printf("目录遍历错误: %v\n", err)
return
}
// 等待所有文件处理完成
wg.Wait()
results := make([]StringCount, 0, len(resultMap))
for str, count := range resultMap {
if count == 1 {
results = append(results, StringCount{String: str, Count: count})
}
}
// 输出结果
for _, result := range results {
fmt.Printf("%s: %d次\n", result.String, result.Count)
}
}
不会golang也没关系,交给ai生成golang代码就行,上面的go代码其实我也是用ai生成的,哈哈哈。我们不必过于担忧ai会替代我们,而是拥抱ai,有ai这个强大的辅助,开发人员所要做的就是在开发过程中思考、挖掘可提升改进的点,需要自身深入业务深入项目,深入之后发挥自己的创新力。