- wxs能不能对object友好点?
- 需求的场景描述(希望解决的问题) - 希望提供的能力 希望能支持对象for..in循环,以及Object.getOwnPropertyNames()和Object.keys(),目前不知道有什么方法可以获取到对象的key的数组呢
2018-11-19 - 【插件已经停止维护】【微信小程序-插件开发实战】学校选择器 11.9更新
插件已经停止维护 插件使用效果图: [图片] 学校选择器,起源于我个人开发的项目中的一个实际需求:从列表中选择高校。本质上只是一个选择列表,有很多种呈现方法,而我想让这一环节呈现的更舒适一点(就是带图片咯~),于是开发了这个组件。但我觉得应该有其他开发者会用到同样的需求,那何不分享出来?把它插件化并开源。 一、插件的配置 首先新建一个插件,而不是小程序,如下图。 [图片] 微信提供的插件模板分为两个模块,一个是miniprogram,是用于模拟业务环境的,你可以在这里模拟下使用插件的小程序业务页面;另一个模块是plugin,这就是插件的实际开发环境了,最新的基础库已经支持插件里面有自定义组件和多个page页面。 [图片] 改动改动一些模板的变量名,如plugin.json的参数(如要插件的页面、自定义组件等)后,后面就可以开始在plugin里面写插件代码! { "pages": { "chooseSchool": "pages/chooseSchool" }, "main": "index.js" } 在插件的配置方面,官方文档介绍的非常详细,我就不再赘述了:https://developers.weixin.qq.com/miniprogram/dev/framework/plugin/development.html#%E5%88%9B%E5%BB%BA%E6%8F%92%E4%BB%B6%E9%A1%B9%E7%9B%AE 二、学校选择器 下面开始写插件的业务代码。 学校选择器主要由三部分组成:一个固定的搜索框、懒加载的列表、搜索失败的提示框。 1、固定的搜索框 input配置了bindinput,让每次键入字符时,都将包含该字符的学校名加入渲染的列表里 /** * 搜索学校 */ search_school(e){ var value = e.detail.value var schoolList = this.data.schoolList var schoolList_now = [] for(var i = 0; i < schoolList.length; i++){ if(schoolList[i].name.indexOf(value) != -1){ schoolList_now.push(schoolList[i]) } } this.setData({ schoolList_now:schoolList_now }) } 2、懒加载的列表 {{item.name}} 985 211 其中的image配置了lazy-load以允许懒加载,这样可以加快渲染速度。 3.搜索失败的提示框 没有搜索到结果 可能是尚未收录该学校 你可以自己输入学校名字 配置了wx:if,当待渲染列表长度为0时就渲染出现。 三、插件的调用 如何调用插件呢(这是你必须要告知插件使用者的一件事),微信小程序提供的插件模板已经为你配置好了:你只需要在小程序端的app.json中配置plugin的值如下:"choose-school"是插件的名字,provider填写插件的appId,version是版本号(开发时使用的是dev,但正式版需要正式的版本号) "plugins": { "choose-school": { "version": "dev", "provider": "wxddd52601ccb94739" } } 如果插件中还使用了自定义组件,小程序端的对应页面的json中也需要配置对应的usingComponents,我这个插件是没有用到自定义组件的。 小程序端(也就是插件的使用者)该如何进入插件页面呢?进入插件有两种方法,一种是wxml中的navigator组件,模板中自带,你只需要改动一下跳转的路径即可,如下图: Go to Plugin page 另一种方法是使用wx.navigateTo,我比较推荐这一种方法,因为我是用这种方法才能拿到回传。如下所示,你只要把navigateTo绑定在任意一个组件的事件上就行,它需要配置两个参数,url需要按插件的规则配置:plugin: // + 插件名 + 插件页面。events需要配置acceptDataFromOpenedPage这个参数以获取返回值。 wx.navigateTo({ url: 'plugin://choose-school/chooseSchool', events: { // 监听返回数据 acceptDataFromOpenedPage: function(res) { console.log('回调成功:',res) that.setData({ school:res.data }) }, }, }) 到这一步,插件的调用就结束了,下面是获取它的返回值。 四、插件的返回 当插件的业务逻辑和调用都准备就绪时,这个时候最关键的一步来了,小程序调用你的插件后,总是要获取返回值的,那如何传递这个值呢? 微信官方给我们提供了EventChannel这个路由方法,它是页面间事件通信通道。我们在插件页面的学校view上绑定一个点击事件choose,点击后获取该学校的数据并返回给上一个页面,如下图所示: choose(e){ var item = e.currentTarget.dataset.item var school = this.data.school school.name = item.name school.is985 = item.is985 school.is211 = item.is211 this.setData({ school:school }) const eventChannel = this.getOpenerEventChannel() eventChannel.emit('acceptDataFromOpenedPage', {data: school}); wx.navigateBack({ delta: 1, }) }, 想要将值返回,就新建一个通信信道eventChannel,并且用它发射(emit)一次数据给小程序端开放的信道监控事件acceptDataFromOpenedPage,并把数据school作为data的值传入。然后再调用navigateBack返回小程序端,传值完毕。 五、本插件的实机调用 11.8号我的插件就通过审核啦,我用另一款我已上线的小程序作为例子,给大家分享一下如何调用“学校选择器”。 第一步:登录小程序后台——设置——第三方设置 [图片] 第二步:点击添加插件,搜索“高校选择器”,点击添加,即可发送申请[图片] 第三步:等待申请审核通过(是我审核,如果我没有及时通过,你可以直接私信我) [图片] 第四步:点击“详情”——开发文档,查看插件的使用说明 [图片] 第五步:插件申请通过后,在小程序app.json里面如下配置:(version需要填写1.0.1)(provider一定是填我的插件id:wxddd52601ccb94739,不是小程序的appId),插件名字写“choose-school”(我甚至忘了是在哪里设置这个名字的,以至于我都改不了) "plugins": { "choose-school": { "version": "1.0.1", "provider": "wxddd52601ccb94739" } }, [图片] 第六步:打开开发者工具的详情,查看插件信息里是否有“高校选择器”,如果没有就重启一下开发者工具 [图片] 第七步:在任意地方绑定如图函数,函数触发后就会跳转到插件,选择学校后数据可回调到res里。 wx.navigateTo({ url: 'plugin://choose-school/chooseSchool', events: { // 监听返回数据 acceptDataFromOpenedPage: function(res) { console.log('回调成功:',res) }, }, }) [图片] 第八步:测试是否能拿到回调。 [图片] [图片] 第九步:上线使用~,有任何建议都可以在任何联系到我的地方反馈,我会及时回复! 六、项目源码 组件的源码放在了Gitee上,欢迎下载。 https://gitee.com/cao-mengliang98/school/tree/master 七、其他 如果你想用自己的呈现方式,甚至不使用在微信小程序上,你可以通过这个url来获得学校的logo:http://www.ming13.cn:5000/schoolImages/学校的名字.jpg 不要漏掉.jpg,这是我个人的服务器,个人开发者可以直接使用。以及从源码仓库中获取学校名单.xlxs。 (logo和名字均暂时只有985211高校,后续会补充完整国内高校)
2024-07-02 - app.onLaunch与page.onLoad异步问题
问题:相信很多人都遇到过这个问题,通常我们会在应用启动app.onLaunch() 去发起静默登录,同时我们需要在加载页面的时候,去调用一个需要登录态的后端 API 。由于两者都是异步,往往page.onload()调用API的时候,app.onLaunch() 内调用的静态登录过程还没有完成,从而导致请求失败。 解决方案:1. 通过回调函数// on app.js App({ onLaunch() { login() // 把hasLogin设置为 true .then(() => { this.globalData.hasLogin = true; if (this.checkLoginReadyCallback) { this.checkLoginReadyCallback(); } }) // 把hasLogin设置为 false .catch(() => { this.globalData.hasLogin = false; }); }, }); // on page.js Page({ onLoad() { if (getApp().globalData.hasLogin) { // 登录已完成 fn() // do something } else { getApp().checkLoginReadyCallback = () => { fn() } } }, }); ⚠️注意:这个方法有一定的缺陷(如果启动页中有多个组件需要判断登录情况,就会产生多个异步回调,过程冗余),不建议采用。 2. 通过Object.defineProperty监听globalData中的hasLogin值 // on app.js App({ onLaunch() { login() // 把hasLogin设置为 true .then(() => { this.globalData.hasLogin = true; }) // 把hasLogin设置为 false .catch(() => { this.globalData.hasLogin = false; }); }, // 监听hasLogin属性 watch: function (fn) { var obj = this.globalData Object.defineProperty(obj, 'hasLogin', { configurable: true, enumerable: true, set: function (value) { this._hasLogin = value; fn(value); }, get: function () { return this._hasLogin } }) }, }); // on page.js Page({ onLoad() { if (getApp().globalData.hasLogin) { // 登录已完成 fn() // do something } else { getApp().watch(() => fn()) } }, }); 3. 通过beautywe的状态机插件(项目中使用该方法) // on app.js import { BtApp } from '@beautywe/core/index.js'; import status from '@beautywe/plugin-status/index.js'; import event from '@beautywe/plugin-event/index.js'; const app = new BtApp({ onLaunch() { // 发起静默登录调用 login() // 把状态机设置为 success .then(() => this.status.get('login').success()) // 把状态机设置为 fail .catch(() => this.status.get('login').fail()); }, }); // status 插件依赖于 beautywe-plugin-event app.use(event()); // 使用 status 插件 app.use(status({ statuses: [ 'login' ], })); // 使用原生的 App 方法 App(app); // on page.js Page({ onLoad() { // must 里面会进行状态的判断,例如登录中就等待,登录成功就直接返回,登录失败抛出等。 getApp().status.get('login').must().then(() => { // 进行一些需要登录态的操作... }) }, }); 具体实现 具体实现可以参考我的商城小程序项目 项目体验地址:体验 代码:代码
2021-05-20 - 2021-03-19
- 自定义navigationBar顶部导航栏,兼容适配所有机型(附完整案例)
前言 navigationBar相信大家都不陌生把?今天我们就来说说自定义navigationBar,把它改变成我们想要的样子(搜索框+胶囊、搜索框+返回按钮+胶囊等)。 思路 隐藏原生样式 获取胶囊按钮、状态栏相关数据以供后续计算 根据不同机型计算出该机型的导航栏高度,进行适配 编写新的导航栏 引用到页面 正文 一、隐藏原生的navigationBar window全局配置里有个参数:navigationStyle(导航栏样式),default=默认样式,custom=自定义样式。 [代码]"window": { "navigationStyle": "custom" } [代码] 让我们看看隐藏后的效果: [图片] 可以看到原生的navigationBar已经消失了,剩下孤零零的胶囊按钮,胶囊按钮是无法隐藏的。 二、准备工作 1.获取胶囊按钮的布局位置信息 我们用wx.getMenuButtonBoundingClientRect()【官方文档】获取胶囊按钮的布局位置信息,坐标信息以屏幕左上角为原点: [代码]const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); [代码] width height top right bottom left 宽度 高度 上边界坐标 右边界坐标 下边界坐标 左边界坐标 下面是官方给的示意图,方便大家理解几个坐标。 [图片] 2.获取系统信息 用wx.getSystemInfoSync()【官方文档】获取系统信息,里面有个参数:statusBarHeight(状态栏高度),是我们后面计算整个导航栏的高度需要用到的。 [代码]const systemInfo = wx.getSystemInfoSync(); [代码] 三、计算公式 我们先要知道导航栏高度是怎么组成的, 计算公式:导航栏高度 = 状态栏高度 + 44。 实例 【源码下载】 自定义导航栏会应用到多个、甚至全部页面,所以封装成组件,方便调用;下面是我写的一个简单例子: app.js [代码]App({ onLaunch: function(options) { const that = this; // 获取系统信息 const systemInfo = wx.getSystemInfoSync(); // 胶囊按钮位置信息 const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); // 导航栏高度 = 状态栏高度 + 44 that.globalData.navBarHeight = systemInfo.statusBarHeight + 44; that.globalData.menuRight = systemInfo.screenWidth - menuButtonInfo.right; that.globalData.menuBotton = menuButtonInfo.top - systemInfo.statusBarHeight; that.globalData.menuHeight = menuButtonInfo.height; }, // 数据都是根据当前机型进行计算,这样的方式兼容大部分机器 globalData: { navBarHeight: 0, // 导航栏高度 menuRight: 0, // 胶囊距右方间距(方保持左、右间距一致) menuBotton: 0, // 胶囊距底部间距(保持底部间距一致) menuHeight: 0, // 胶囊高度(自定义内容可与胶囊高度保证一致) } }) [代码] app.json [代码]{ "pages": [ "pages/index/index" ], "window": { "navigationStyle": "custom" }, "sitemapLocation": "sitemap.json" } [代码] 下面为组件代码: /components/navigation-bar/navigation-bar.wxml [代码]<!-- 自定义顶部栏 --> <view class="nav-bar" style="height:{{navBarHeight}}px;"> <input class="search" placeholder="输入关键词!" style="height:{{menuHeight}}px; min-height:{{menuHeight}}px; line-height:{menuHeight}}px; left:{{menuRight}}px; bottom:{{menuBotton}}px;"></input> </view> <!-- 内容区域: 自定义顶部栏用的fixed定位,会遮盖到下面内容,注意设置好间距 --> <view class="content" style="margin-top:{{navBarHeight}}px;"></view> [代码] /components/navigation-bar/navigation-bar.json [代码]{ "component": true } [代码] /components/navigation-bar/navigation-bar.js [代码]const app = getApp() Component({ properties: { // defaultData(父页面传递的数据-就是引用组件的页面) defaultData: { type: Object, value: { title: "我是默认标题" }, observer: function(newVal, oldVal) {} } }, data: { navBarHeight: app.globalData.navBarHeight, menuRight: app.globalData.menuRight, menuBotton: app.globalData.menuBotton, menuHeight: app.globalData.menuHeight, }, attached: function() {}, methods: {} }) [代码] /components/navigation-bar/navigation-bar.wxss [代码].nav-bar{ position: fixed; width: 100%; top: 0; color: #fff; background: #000;} .nav-bar .search{ width: 60%; color: #333; font-size: 14px; background: #fff; position: absolute; border-radius: 50px; background: #ddd; padding-left: 14px;} [代码] 以下是调用页面的代码,也就是引用组件的页面: /pages/index/index.wxml [代码]<navigation-bar default-data="{{defaultData}}"></navigation-bar> [代码] /pages/index/index.json [代码]{ "usingComponents": { "navigation-bar": "/components/navigation-bar/navigation-bar" } } [代码] /pages/index/index.js [代码]const app = getApp(); Page({ data: { // 组件参数设置,传递到组件 defaultData: { title: "我的主页", // 导航栏标题 } }, onLoad() { console.log(this.data.height) } }) [代码] 效果图: [图片] 好了,以上就是全部代码了,大家可以文中复制代码,也可以【下载源码】,直接到开发者工具里运行,记得appid用自己的或者测试哦! 下面附几张其它小程序的效果图,大家也可以尝试照着做: [图片][图片] 总结 本文写了自定义navigationBar的一些基础性东西,里面涉及组件用法、参数传递、导航栏相关。 由于测试环境有限,大家在使用时如果发现有什么问题,希望及时反馈,以供及时更新帮助更多的人! 大家有什么疑问,欢迎评论区留言!
2022-06-23 - canvas iPoneX canvas 圆角无效?
canvas绘制圆角,开发工具,显示正确,IponeX无效。 预期效果 [图片] bug表现 [图片] 手机型号:iPhone X (GSM+CDMA)<iPhone10,3> 运行系统:ios12.1 微信版本:7.0.4 基础库版本:2.7.7 代码片段已给出,求官方回复!!!
2019-12-16