- 你的小程序总共引入了多少组件?【下篇】
你的小程序总共引入了多少组件?【下篇】 前言 在【上篇】中,我对小程序项目中的代码文件做了一次简单的解析,将页面文件中引入的组件以对象的键值对形式输出到 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 有需要的同学可以自行下载脚本文件,然后修改一下入口目录的路径,在本地用 node 命令执行脚本文件就可以跑起来了,这样就可以分析出你当前的小程序项目每个页面引用到的组件路径。 总结 关于本次对 如何分析小程序项目引入组件 的分享就到此结束了,这是我曾经接手一个老旧项目的时候所总结下来的一点小小经验; 当你要分析一个项目的时候,可以先通过编写脚本的方式去分析一下里面的代码逻辑,组件引入情况,这样可以避免你在不熟悉业务的前提下,对项目有一个整体的认知,为后期的项目优化提供一份资料作为参考。 欢迎大家继续关注。
2020-11-11 - 全自动、全面化收集 formid 思路,开启脑洞时刻
formid 基础知识 formid 是啥 小程序给用户发消息的唯一途径,所以前端要尽量多收集 formid 保存到服务端; formid 怎么收集 小程序要求必须用户提交表单才能得到formid; form 增加 report-submit 属性,并通过 button submit,前端 formSubmit 事件就能获得 formid ,如下方代码所示: [代码]<form bindsubmit="formSubmit" report-submit class="layout"> <button formType="submit" class="button" hover-class="none"> 。。。 </button> </form> [代码] formid 组件封装(初期思路) 我们将form与button 封装成组件(fomids),通过slot,可以显示任意元素,模拟form submit; 把页面上需要点击的元素用fomids包起来,fomids 将收集的 formid 放入cache,然后通过网络请求将 fomid 传给服务端; 大数小程序都是这么做的,这样做也没有问题,就是略微麻烦,form 与button 组合的样式有时不太好控制 formid 组件封装(进阶思路) 此思路是上面思路的延申,我们把 formids 这个组件做成layout级,把整个页面包在里面,利用事件自动冒泡的特性,只要有点击事件,就能收集到 formid 新建组件layout [代码]<!--components/layout.wxml--> <form bindsubmit="formSubmit" report-submit class="layout"> <button formType="submit" class="button" hover-class="none"> <view class="fixed"><slot></slot></view> </button> </form> [代码] [代码]/* components/layout.wxss */ .layout { display: inline-block; padding-left: 0; padding-right: 0; box-sizing: border-box; font-size: inherit; text-align: left; text-decoration: none; line-height: inherit; -webkit-tap-highlight-color: transparent; color: inherit; width: 100%; position: relative; } .layout .button{ display: inline-block; padding-left: 0; padding-right: 0; box-sizing: border-box; font-size: inherit; text-align: left; text-decoration: none; line-height: inherit; -webkit-tap-highlight-color: #000; color: inherit; width: 100%; position: relative; } .layout .button .fixed{ position:relative; z-index: 9999; width: 100%; } .layout .button:before,.layout .button:after{ border-width: 0; } [代码] [代码]// components/layout.js Component({ methods:{ formSubmit: function(e) { console.log('layout.formids',e.detail.formId) if("the formId is a mock one"!=e.detail.formId){ let formids=wx.getStorageSync('formids') || []; formids.push(e.detail.formId); formids=[...new Set(formids)]; wx.setStorage({key:'formids',data:formids}); } }, }, }) [代码] 将 layout 添加为全局组件 app.json 中增加 [代码]"usingComponents":{ "layout":"/components/layout" }, [代码] 在页面wxml中使用 [代码]<layout> <view class="pages">...</view> </layout> [代码] 怎么将 formid 提给服务端 首先你必须将wx.requxest 进行封装为myRequxest,页面上都是用myRequxest 进行网络请求; myRequxest 中 header 增加formids,从cache中获得放到header中;并删除formid缓存;(需要服务端从header中获得formids并存起来) [代码]function myRequxest(....){ let formids = wx.getStorageSync('formids'); if(formids){ wx.removeStorage({key:'formids'}); } let openid='';//用户openid wx.request({ ... header: { formids:formids.toString(), openid:openid, }, ... }) } [代码] 延申思考 从 写 form 收集 -> 封装 formids 组件 -> 封装 layout(formids) 组件,跳出思维固化,可以做更多可能; 希望这个思路能给你一些触发
2019-07-31 - 2019-02-26
- 「分享」高性能双列瀑布流极简实现(附示例)❤️
前言 在日常开发过程中,经常会有双列瀑布流场景的需求出现,如商品列表、文章列表等,本文将简单介绍这种情景下如何高效、精准的实现双列瀑布流场景,支持刷新、加载更多等,实现效果如下。 [图片] [图片] 开发思路 瀑布流视图有一种参差的美感,常规列表布局如 flex wrap 等由于存在行高度限制,无法让第二行的 item 对齐上一行最矮处,因此,瀑布流布局时采用双列 scrollview 的 flex 布局。 参差布局的实现,采用代码计算左右两列的高度,然后对左右两列总高度进行比较,新加入的 item 总是排在总高度较小的那列后面。 计算时可以尽可能的缓存高度,例如左右两列高度在每次计算时都缓存起来,有新的 item 加入列表时直接增加左右两列高度即可,不需要重新从头计算。 index.js [代码]const tplWidth = (750 - 24 - 8) / 2; const tplHeight = 595; // plWidth * 1.66 newPhotos.forEach(photo => { const { height, width } = photo let photoHeight = tplWidth if (height > width) { photoHeight = tplHeight photo.display = 'long' } else { photo.display = 'short' } if (leftHeight < rightHeight) { leftList.push(photo) leftHeight += photoHeight } else { rightList.push(photo) rightHeight += photoHeight } }) [代码] index.wxml [代码]<!-- list --> <view class='list'> <!-- left --> <view class='left-list'> <block wx:for="{{leftList}}" wx:key="{{item._id}}"> <cell photo="{{item}}" bindclick='onCellClicked' /> </block> </view> <!-- right --> <view class='right-list'> <block wx:for="{{rightList}}" wx:key="{{item._id}}"> <cell photo="{{item}}" bindclick='onCellClicked' /> </block> </view> </view> [代码] index.css [代码].list { display: flex; flex: 1; position: relative; flex-direction: row; justify-content: space-between; padding-left: 12rpx; padding-right: 12rpx; padding-top: 8rpx; } .left-list { display: flex; position: relative; flex-direction: column; width: 359rpx; } .right-list { display: flex; position: relative; flex-direction: column; width: 359rpx; } [代码]
2019-02-27 - 2019-02-22