你的小程序总共引入了多少组件?【下篇】
前言
在【上篇】中,我对小程序项目中的代码文件做了一次简单的解析,将页面文件中引入的组件以对象的键值对形式输出到 csv 格式的文件中,数据结构格式如下所示:
{
file: 'xxx.page',
components: [
{
name: 'xxx',
path: 'yyy'
}
]
}
但是,我还没有对页面对应的 json 文件所引入组件列表做进一步的分析,也就是对页面的 wxml 文件是否 都全部使用到该组件列表里面的组件 做进一步的分析。
这样做的目的有以下几点:
1. 分析页面的无用组件引入,做进一步的删除代码优化,减少小程序包体积;
2. 统一项目中的组件ui样式,为后期的项目组件化打下根基(尤其是一些老旧项目的代码,错综复杂,代码过于冗余和繁琐)。
准备工作
思路
我在这里简单阐述一下这次编码过程的思路:
- 在【上篇】文章中我已经可以获取到了每个页面所对应的组件数组列表;
- 读取 wxml 文件内容字符串,将当前的字符串通过
html2json
工具库转换成 json 结构的对象; - 遍历组件数组列表,然后__递归查找当前 wxml文件转化后的 json 对象__,判断是否存在组件的标签字符,存在就输出为__true__,不存在就输出为__false__。
我们可以简单看看wxml文件转化后的json对象结构,如下所示:
{
node: 'root',
child: [
{
node: 'element',
tag: 'div',
attr: { id: '1', class: 'foo' },
child: [
{
node: 'element',
tag: 'h2',
child: [
{ node: 'text', text: 'sample text with ' },
{ node: 'element', tag: 'code', child: [{ node: 'text', text: 'inline tag' }] }
]
},
{
node: 'element',
tag: 'pre',
attr: { id: 'demo', class: ['foo', 'bar'] },
child: [{ node: 'text', text: 'foo' }]
},
{
node: 'element',
tag: 'pre',
attr: { id: 'output', class: 'goo' },
child: [{ node: 'text', text: 'goo' }]
},
{
node: 'element',
tag: 'input',
attr: { id: 'execute', type: 'button', value: 'execute' }
}
]
}
]
}
安装工具包
安装 html2json 包
npm install --save-dev html2json
编码
【上篇】文章已经讲解过的逻辑就不再阐述了,有需要的老哥可以移步到这里看看,我主要针对这次主要的 __递归比对json对象的逻辑__阐述一下:
1. 比对页面json对象是否存在某标签字符
/**
* 判断当前页面是否引入该组件标签
* @param {*} pageJsonData
* @param {*} tagName
*/
const isWxmlImportComponent = (pageJsonData, tagName) => {
if (!pageJsonData.child) return false
tagName = tagName.toLowerCase()
for (let item of pageJsonData.child) {
// 判断标签名是否一致
if (item.tag === tagName) {
return true
}
// 递归判断子节点的标签
if (item.child) {
const flag = isWxmlImportComponent(item, tagName)
if (flag) {
return true
}
}
}
return false
}
这是这次分享的核心代码,从 html2json
对象解析出来的对象我们可以发现,如果父标签有子标签的话,会有一个 child
的数组属性,于是我们就要对父标签进行递归操作:
// 递归判断子节点的标签
if (item.child) {
const flag = isWxmlImportComponent(item, tagName)
if (flag) {
return true
}
}
并且每个标签都会有一个 tag
的属性,属性值就是标签名,于是由此得知,如果当前传入的标签名如果和当前标签一样,就可以退出当前的查找流程:
// 判断标签名是否一致
if (item.tag === tagName) {
return true
}
2. 组装拼接文件输出对象数组
我们来看看完整的脚本文件:
const path = require('path');
const { isWxmlImportComponent } = require('./tool/parseUtils');
const { getAllFiles, getFilterFiles, listComponents, getFileJsonData } = require('./tool/fileUtils');
const ObjectsToCsv = require('objects-to-csv');
(async function() {
// 解析入口目录
const entryDir = path.resolve(__dirname + '/../dist/pages')
const allFiles = getAllFiles(entryDir)
if (allFiles.length === 0) return
const filterFiles = getFilterFiles(allFiles, ['wxml', 'json'])
// 组装导出对象数组数据
const pageWithComponents = filterFiles.reduce((acc, { jsonFile }) => {
const current = path.basename(jsonFile, '.json')
const currentDir = path.dirname(jsonFile)
const components = listComponents(jsonFile) || []
if (components.length == 0) {
return [...acc, {
page: current,
directory: currentDir,
}]
} else {
// 输入wxml地址,转化为json标签对象
const fileJsonData = getFileJsonData(currentDir + `/${current}.wxml`)
const childs = components.reduce((childAcc, { name, path }) => {
const used = isWxmlImportComponent(fileJsonData, name)
return [...childAcc, {
page: current,
directory: currentDir,
component: name,
componentPath: path,
used: used ? 'true' : 'false',
}]
}, [])
return [...acc, ...childs]
}
}, [])
// 导出csv文件
const csv = new ObjectsToCsv(pageWithComponents)
await csv.toDisk(__dirname + '/component_status_stat.csv')
})()
文件导出效果
我这次对导出的数据结构做了一下优化,相比较【上篇】文章的导出格式更为直观易懂,一目了然,大家可以看看:
项目地址
项目地址:https://github.com/csonchen/wx-mall-components
脚本文件地址:https://github.com/csonchen/wx-mall-components/blob/master/shell/component-status.stat.js
脚本导出文件地址:https://github.com/csonchen/wx-mall-components/blob/master/shell/component_status_stat.csv
有需要的同学可以自行下载脚本文件,然后修改一下入口目录的路径,在本地用 node 命令执行脚本文件就可以跑起来了,这样就可以分析出你当前的小程序项目每个页面引用到的组件路径。
总结
关于本次对 如何分析小程序项目引入组件 的分享就到此结束了,这是我曾经接手一个老旧项目的时候所总结下来的一点小小经验;
当你要分析一个项目的时候,可以先通过编写脚本的方式去分析一下里面的代码逻辑,组件引入情况,这样可以避免你在不熟悉业务的前提下,对项目有一个整体的认知,为后期的项目优化提供一份资料作为参考。
欢迎大家继续关注。