- 错误码61007: api is unauthorized to component,怎么解决?
出现该问题的背景:一般是小程序/公众号授权给第三方平台,由他们代开发的过程调取某些接口或者功能的时候出现这个错误。 微信错误码列表地址:https://developers.weixin.qq.com/doc/oplatform/Return_codes/Return_code_descriptions_new.html 排查及解决(已小程序为例) 检查小程序授权第三方平台的权限集(小程序后台-设置-第三方设置) [图片] 查看小程序授权三方平台时候的所有权限集 小程序权限集:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Before_Develop/third_party_authority_instructions.html 出现这个错误基本就是需要的权限集没有出现在已授权的第三方权限集里,这个时候就需要去开放平台修改对应的权限集。具体如下:【参考地址同2中的小程序权限集】 [图片] 后续遇到unauthorized相关问题可以先通过检查下该公众号/小程序是否已经给第三方平台授予了该接口对应的权限集。可通过调用api_get_authorizer_info接口查询,返回的func_info中是否包含了对应的权限id。 api_get_authorizer_info接口文档请查看:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/api/api_get_authorizer_info.html 权限集说明文档请参考:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Before_Develop/third_party_authority_instructions.html 微信错误码列表请参考:https://developers.weixin.qq.com/doc/oplatform/Return_codes/Return_code_descriptions_new.html
2021-01-20 - 代码规范
组建团队最容易被忽略,又挺重要的事情,就是确定代码规范。 代码规范不是编写有效的代码硬性规定,而是统一代码风格、避免出错的最佳实践。 每个人都有自己的编码风格,并且JavaScript语言极度灵活,约束较少,而且是弱类型语言。如果不对编码的风格做一定的约束,必然出现千差万别的风格,虽然都是正确可执行的代码,但这会让代码的可阅读性非常差。 最好的结果就是每个人写得代码都是一样的。 前端的特殊性 若是后端或者其他岗位,可能就涉及一种编程语言,也就确定一种代码规范即可。 但是前端,涉及到编程语言相对较多,并且不同框架或者runtime也可能导致不同的语法风格,因此需要覆盖的规范也比较多: 编程语言:JavaScript、CSS、HTML 框架:Vue、React runtime:Node.js、小程序、浏览器 笔者认为,最基本的编程语言的代码规范是必须要有的,其他可以陆续完善 实施方案 如果只是制定的代码规范文档,但是没有可实施方案,依靠人为的自觉,必然出现不遵守规则的漏网之鱼。因此,必须落地实施方案,拒绝不符合规范的代码合入代码仓库。 比较常见的方案为Git + GitLab(GitHub) + ESLint。 Git是常用的分布式版本控制系统,GitLab则是开源的仓库管理工具。而ESLint则为主流的JavaScript代码检测工具 每个仓库均可设置多个分支,在GitLab上对关键分支(比如master)的权限做严格把控,比如: 不允许任何人直接push到关键分支(Allowed to push: No one) [图片] 仅允许通过pipeline的 合并请求(merge requests) 进行合并 [图片] 最后在pipeline中添加一个Job,使用ESLint检测当前的代码即可。 快速开始 实施方案确定了,下一步即是制定详细的代码规范。如果重头开始梳理规范,将是极为庞大的工作。 ESLint 好在ESLint提供了推荐方案,在配置文件[代码].eslintrc[代码]这样写即可: [代码]{ "extends": "eslint:recommended" } [代码] 或者采用业界比较出名的公司规范也可以,比如:Google、Airbnb。 使用[代码]ESlint --init[代码]即可开启交互式初始化ESLint配置。 对于不同的runtime,都是使用ESLint作为检测工具,只不过需要下载第三方插件来扩展检测能力 HTMLHint 因为[代码]HTML[代码]不算真正的编程语言,而是标记语言,因此可以检测的规范不会太多,因此可以手动梳理一便,同时也支持自定义规则。全部的规则:HTMLHint Rules 通过配置文件[代码].htmlhintrc[代码]配置,默认配置如下: [代码]{ "tagname-lowercase": true, "attr-lowercase": true, "attr-value-double-quotes": true, "doctype-first": true, "tag-pair": true, "spec-char-escape": true, "id-unique": true, "src-not-empty": true, "attr-no-duplication": true, "title-require": true } [代码] stylelint 类似于[代码]ESLint[代码],stylelint也提供了 标准配置(standard configuration),安装方式: [代码]npm install --save-dev stylelint stylelint-config-standard [代码] 在项目根目录创建配置文件[代码].stylelintrc.json[代码]: [代码]{ "extends": "stylelint-config-standard" } [代码] 执行检测: [代码]npx stylelint "**/*.css" [代码]
2020-04-12 - 2020-06-09
- 2020-04-20
- 模块化——加速小程序开发
阅读基础:有小程序项目经验,有查阅官方文档习惯的小伙伴 随着公司小程序项目日益繁多,仅仅靠着官方提供的框架、组件、API,已经远远不能满足项目高效迭代的要求了,于是我们组内萌生了对小程序进行模块化的想法。 实际项目中我们对小程序模块化已经涉及各个模块,我总结一下,从三个方向跟大家分享我们不一样的模块化思路:[代码]Page+[代码],[代码]basePage[代码],[代码]适配层[代码]。 Page+:赋予页面更多的功能 [代码]Page()[代码]作为页面的入口,我们可以通过对其入参对象的封装实现:生命周期的改造、全局状态管理和新增页面功能。 官方删除了小程序分享回调 complete,一起来尝试将其恢复吧。一般我们的逻辑是这样的: [代码]// pages/index/index.js Page({ // 数据初始化 data: { shareFlag: false, //页面是否处于分享中 shareComplete: false //分享回调事件 }, // onShow 生命周期 onShow: function () { const { shareFlag, shareComplete } = this.data if( shareFlag ){ this.data.shareFlag = false //变量不涉及页面渲染,不使用 setData shareComplete && shareComplete() } }, // 分享事件 onShareAppMessage: function () { let shareInfo = { title: '分享测试标题', path: '', complete: function () { console.log('页面分享成功啦~') } } this.data.shareFlag = true this.data.shareComplete = typeof (shareInfo.complete) == 'function' ? shareInfo.complete : false return shareInfo } }) [代码] 在单页面内实现分享回调这样操作是可行的,如果多页面、多项目都要实现该功能,重复拷贝代码,则显格外得繁琐。 我们来将这个功能抽离封装一下吧。 [代码]// pages/index/index.js import PagePlus from './pagePlus.js' PagePlus({ // 分享事件 onShareAppMessage: function () { return { title: '分享测试标题', path: '', complete: function () { console.log('页面分享成功啦~') } } } }) [代码] [代码]// pages/index/pagePlus.js const PagePlus = (pageObj) => { const _onShow = pageObj.onShow, _onShareAppMessage = pageObj.onShareAppMessage, _data = { shareStatus: false, //页面是否处于分享中 shareComplete: false //分享回调事件 } Object.assign(_data, pageObj.data) delete pageObj.data pageObj.onShow = function () { typeof _onShow == 'function' && _onShow.apply(this) const { shareStatus, shareComplete } = this.data if (shareStatus) { this.data.shareStatus = false //变量不涉及页面渲染,不使用 setData shareComplete && shareComplete() } } pageObj.onShareAppMessage = function () { const shareInfo = typeof _onShareAppMessage == 'function' && _onShareAppMessage.apply(this) this.data.shareStatus = true shareInfo && (this.data.shareComplete = shareInfo.complete) return shareInfo } Page({ data: _data, ...pageObj }) } export default PagePlus [代码] 我们来增加一个新的生命周期回调——[代码]onReshow[代码](页面非首次显示回调,常用于详情页操作影响上一页列表数据的场景)。 [代码]// pages/index/index.js import PagePlus from './pagePlus.js' PagePlus({ // 监听页面非首次显示 onReshow: function(){ console.log('onReshow lifeCallBack') }, onShareAppMessage: function () { return { title: '分享测试标题', path: '', complete: function () { console.log('页面分享成功啦~') } } } }) [代码] [代码]// pages/index/pagePlus.js class BasePage{ data = { pagePlus: { shareStatus: false, //页面是否处于分享中 shareComplete: false, //分享回调事件 firstEnter: true //第一次进入页面 } } methods = { onShow: this.onShow, onShareAppMessage: this.onShareAppMessage, onReshow: this.onReshow } onShow(){ const { shareStatus, shareComplete, firstEnter } = this.data.pagePlus if (firstEnter) { this.data.pagePlus.firstEnter = false } else { this.onReshow() } if (shareStatus) { this.data.pagePlus.shareStatus = false shareComplete && shareComplete() } } onShareAppMessage(shareInfo){ this.data.pagePlus.shareStatus = true shareInfo && (this.data.pagePlus.shareComplete = shareInfo.complete) } } const PagePlus = (pageObj) => { const basePage = new BasePage() for (var i in basePage.methods) { basePage.methods[i] = (() => { const key = i const _temFn = basePage.methods[key] return function () { if (key == 'onShareAppMessage') { const shareInfo = typeof pageObj[key] == 'function' && pageObj[key].apply(this, arguments) _temFn.apply(this, [shareInfo]) return shareInfo } typeof pageObj[key] == 'function' && pageObj[key].apply(this, arguments) typeof _temFn == 'function' && _temFn.apply(this, arguments) } })() } Object.assign(basePage.data, pageObj.data) delete pageObj.data Page({ data: basePage.data, ...pageObj, ...basePage.methods }) } export default PagePlus [代码] 自此,我们修改了原生的生命周期回调和增加了新的生命周期回调。当然我们还能为 Page+ 赋予更多的功能,例如: [代码]页面刷新[代码]:下拉自动刷新当前页。 [代码]定时器自动清除[代码]:离开页面时,自动清除页面执行的定时器。 [代码]全局状态管理[代码]:页面间数据共享,相关数据关联的组件即时渲染更新。 相关的代码实现,大家可以自己思考一下怎么实现;我的实现细节,如果大家感兴趣的话就在下方给我留言吧,你们的回复是我更新的动力哦。 basePage:公共 Component 管理器 小程序页面彼此独立,使用 Component 都需要各自引用,为了实现页面公共 Component 的统一管理,这个时候就可以引入 basePage 的概念:以 basePage 作为父组件,其他公共 Component 作为子组件,页面通过 basePage 对公共 Component 进行管理。 实现原理 1、定义一个 Component ,作为 basePage 。 2、每个页面统一引用 basePage ,且规定页面的元素都需要写到 <basePage/> 标签内部 。 3、通过 basePage 引用页面公共的 Component ,并进行业务逻辑编辑。 实现细节 实际使用过程中,我发现有两个问题: 1、Page 和 basePage 通信是非常频繁的,需要通过 WXML 数据绑定和 triggerEvent 触发事件,略显麻烦。 2、setTimeout、webSocket 等后台进程,可能触发[代码]非当前显示页面[代码]的渲染更新,而绝大部分情况,我们只需要[代码]当前显示页面[代码]的渲染更新。 针对这两种场景的优化,我们可以把当前显示页面的 basePage 实例对象赋值到 global 的某个具体变量;每当 Page 触发 show 生命周期回调的时候,我们就对这个变量赋值的实例对象进行更新,这样我们就可以通过 global 的变量直接操作当前显示页面的 basePage 了。 部分代码示例 [代码]{ "文件路径": "pages/index/index.json", "usingComponents": { "basePage": "../../components/basePage/index" } } [代码] [代码]<!--pages/index/index.wxml--> <basePage> <!-- 页面元素 --> </basePage> [代码] [代码]// components/basePage/index.js Component({ /** * Component 所在页面的生命周期函数 */ pageLifetimes: { show: function () { global.basePage = this }, hide: function () { global.basePage = null } } }) [代码] [代码]{ "文件路径": "components/basePage/index.json", "说明": "在此处统一引入页面公共的 Component", "component": true, "usingComponents": {} } [代码] [代码]<!--components/basePage/index.wxml--> <slot /> [代码] 适配层:让代码适应更多的场景 如果你的项目对代码后续维护、迭代和可移植性有较高需求,或者需要多项目并行,这个时候通过适配层去调用各个功能模块就显得尤为重要。适配层方面我做的还是比较粗糙的,如果有建议欢迎指出。 适配层的时机 项目不是 bugfix 级别的迭代,都有适配层设计的必要。 如果是[代码]新项目[代码],心底不认为自己是“咸鱼”而是代码的“亲爹”,[代码]适配层完全可以作为标配[代码]去实现;这就是展现自己对代码全局观的时候了,把自己对代码的理解都用适配层去诠释吧。 如果是[代码]旧项目迭代[代码],在项目排期允许的情况下,尽可能理解原代码的基本实现细节;对比新的项目是要束手束脚一些,适配层的设计要在[代码]尽可能少改变原有代码[代码]的情况下进行;如果排期比紧急,适配层的完整实现[代码]可以在几个版本迭代中逐步实现[代码]。 模块设计必须高内聚低耦合 如果功能模块的设计过于松散、耦合复杂,这就意味着适配层将需要做各种兼容,这和适配层设计的初衷背道而驰,不做也罢。 配置文件 如果你的代码有移植性要求,为这些不同环境准备对应的配置文件吧,配置文件可以通过自制脚手架实现,也可以粗暴地手动替换,在保证尽可能不出错的情况下实现即可。 功能模块的入口 所有整合的功能模块都需要通过适配层进行调用,适配层就是你的“王之财宝”。 规范 && 文档 适配层是从代码的全局考虑,如果是项目是分工完成,项目的开发人员都需要遵守适配层规范进行代码开发;文档我一直都认为都是非常必要的,但还是经常会懈怠,没有进行完整的文档编写,但我基本会在所有项目成员都能理解适配层的情况下,进行简单的口头说明。 因为开心说一些废话 一次需求迭代中,几乎涉及手头上的所有小程序项目;刚好就在需求前的半个月,我们小组完成了对所有项目模块化改造;虽然需求来得很急,我们还是很完美的实现了。毕竟[代码]模块化之前,每个项目的改造都是独立的工作量;模块化之后,就只有适配层迭代的工作量了[代码]。不过真是辛苦了测试小伙伴,因为对所有项目进行模块化改造,意味着测试小伙伴对所有项目进行回归测试,感谢测试小伙伴,比心! 这篇文章,对 Page+ 的具体实现展示比较详细,感觉对 basePage 和适配层讲的都比较偏概念。毕竟这部分内容都和业务逻辑联系比较紧密,很难抽象深入讲解。刚好还有假期还有一段时间,如果自己还有时间就再写一篇关于最近项目的模块化剖析吧,哈哈。
2019-10-09 - 2019-02-26
- 小程序图片展示是否能做出这样的效果?
[图片] [图片]
2019-02-15 - 微信小程序开发-76种动画 animate.css
1、微信小程序动画有自己的方法:官方链接 但需要自己去写动画效果,比较麻烦。 2、本文介绍的是把animate.css这个非常棒的css库引入到小程序内使用。 animate.css包含76种动画,使用非常简单。animate.css官网 : https://daneden.github.io/animate.css/ 3、由于小程序对代码大小限制比较大,所以删除了animate.css中 所有@-webkit-部分css,减少了一半体积 再把文件后缀名改为wxss,以后出来的百度小程序、支付宝小程序、今日头条小程序估计修改对应的后缀名就可以直接使用了。 下载地址:http://nodejs999.com/animate.wxss 4、放到小程序代码中,然后@import到app.wxss文件中。 我项目是把animate.wxss文件放在utils文件夹下。 所以在app.wxss中加入 @import './utils/animate.wxss'; 即可。 就可以像animate.css一样使用了。
2018-11-01