- 小程序原生高颜值组件库--ColorUI
[图片] 简介 ColorUI是一个Css类的UI组件库!不是一个Js框架。相比于同类小程序组件库,ColorUI更注重于视觉交互! 浏览GitHub:https://github.com/weilanwl/ColorUI [图片] 如何使用? 先下载源码包 → Github 引入到我的小程序 将 /demo/ 下的 colorui.wxss 和 icon.wxss 复制到小程序的根目录下 在 app.wxss 引入两个文件 [代码]@import "icon.wxss"; @import "colorui.wxss"; [代码] 使用模板全新开发 复制 /template/ 文件夹并重命名为你的项目,微信开发者工具导入为小程序就可以使用ColorUI了 体验沉浸式导航 [图片] App.js 获取系统参数并写入全局参数。 [代码]//App.js App({ onLaunch: function() { wx.getSystemInfo({ success: e => { this.globalData.StatusBar = e.statusBarHeight; let custom = wx.getMenuButtonBoundingClientRect(); this.globalData.Custom = custom; this.globalData.CustomBar = custom.bottom + custom.top - e.statusBarHeight; } }) } }) [代码] Page.js 页面配置获取全局参数。 [代码]//Page.js const app = getApp() Page({ data: { StatusBar: app.globalData.StatusBar, CustomBar: app.globalData.CustomBar, Custom: app.globalData.Custom } }) [代码] Page.wxml 页面构造导航。更多导航样式请下载Demo查阅 操作条组件。 [代码]<view class="cu-custom" style="height:{{CustomBar}}px;"> <view class="cu-bar fixed bg-gradual-pink" style="height:{{CustomBar}}px;padding-top:{{StatusBar}}px;"> <navigator class='action border-custom' open-type="navigateBack" delta="1" hover-class="none" style='width:{{Custom.width}}px;height:{{Custom.height}}px;margin-left:calc(750rpx - {{Custom.right}}px)'> <text class='icon-back'></text> <text class='icon-homefill'></text> </navigator> <view class='content' style='top:{{StatusBar}}px;'>操作条</view> </view> </view> [代码] 自定义系统Tabbar [图片] 按照官方 自定义 tabBar 配置好Tabbar (开发工具和版本库请使用最新版)。 使用ColorUI配置Tabbar只需要更改 Wxml 页的内容即可。 更多Tabbar样式请下载Demo查阅 操作条组件。 /custom-tab-bar/index.wxml [代码] <view class="cu-bar tabbar bg-white shadow"> <view class="action" wx:for="{{list}}" wx:key="index" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab"> <view class='icon-cu-image'> <image src='{{selected === index ? item.selectedIconPath : item.iconPath}}' class='{{selected === index ? "animation" : "animation"}}'></image> </view> <view class='{{selected === index ? "text-green" : "text-gray"}}'>{{item.text}}</view> </view> </view> [代码] 作者叨叨 ColorUI是一个高度自定义的Css样式库,包含了开发常用的元素和组件,元素组件之间也能相互嵌套使用。我也会不定期更新一些扩展到源码。 其实大家都在催我写文档,但这个库源码就在这,所见即所得,粘贴复制就可以得到你想要的页面。当然,文档我还是要写的,也希望大家多多提意见。 现在前端的开发方向基本都是奔着Js方向的,布局和样式大家讨论的有点少。以后我会在开发者社区多聊一聊关于开发中的布局和样式。 [图片] 感谢阅读。
2019-02-26 - 小程序云函数的高级玩法-路由
一般情况下,一个云函数完成单一的逻辑功能,就是一个类的方法一样,如图: [图片] 但是受限免费用户最多只能使用20个云函数,想要在单一云函数中实现多个复杂的功能就需要通过参数来区别,可读性差,不利于管理。通过路由,尝试将请求归类,一个云函数处理某一类的请求,比如有专门负责处理用户的,或者专门处理支付的云函数。如图: [图片] 为了方便大家试用,腾讯云 Tencent Cloud Base 团队开发了 tcb-router,云函数路由管理库方便大家使用。 基于 koa 风格的小程序·云开发云函数轻量级类路由库,主要用于优化服务端函数处理逻辑 使用 npm install --save tcb-router 云函数端 // 云函数的 index.js const TcbRouter = require(’./router’); exports.main = (event, context) => { const app = new TcbRouter({ event }); [代码]// app.use 表示该中间件会适用于所有的路由 app.use(async (ctx, next) => { ctx.data = {}; await next(); // 执行下一中间件 }); // 路由为数组表示,该中间件适用于 user 和 timer 两个路由 app.router(['user', 'timer'], async (ctx, next) => { ctx.data.company = 'Tencent'; await next(); // 执行下一中间件 }); // 路由为字符串,该中间件只适用于 user 路由 app.router('user', async (ctx, next) => { ctx.data.name = 'heyli'; await next(); // 执行下一中间件 }, async (ctx, next) => { ctx.data.sex = 'male'; await next(); // 执行下一中间件 }, async (ctx) => { ctx.data.city = 'Foshan'; // ctx.body 返回数据到小程序端 ctx.body = { code: 0, data: ctx.data}; }); // 路由为字符串,该中间件只适用于 timer 路由 app.router('timer', async (ctx, next) => { ctx.data.name = 'flytam'; await next(); // 执行下一中间件 }, async (ctx, next) => { ctx.data.sex = await new Promise(resolve => { // 等待500ms,再执行下一中间件 setTimeout(() => { resolve('male'); }, 500); }); await next(); // 执行下一中间件 }, async (ctx)=> { ctx.data.city = 'Taishan'; // ctx.body 返回数据到小程序端 ctx.body = { code: 0, data: ctx.data }; }); return app.serve(); [代码] } tips: 小程序云函数的 node 环境默认支持 async/await 语法,推荐涉及到的异步操作时像 demo 中那样使用 小程序端 // 调用名为 router 的云函数,路由名为 user wx.cloud.callFunction({ // 要调用的云函数名称 name: “router”, // 传递给云函数的参数 data: { $url: “user”, // 要调用的路由的路径,传入准确路径或者通配符* other: “xxx” } }); 完整的实例,请参考我的另一篇博客: 分享使用tcb-router路由开发的云函数短信平台SDK
2019-04-20 - 微信小程序实现时间轴和地区列表的功能
老规矩先看效果图 [图片] 先来看左图的地区列表是如何实现的。 我们在解析数据之前,要先看下数据结构 [代码][{ "_id": "XL28U3kPDdDCJ9m0", "item": { "diqu": "北京", "list": [{ "id": "XL27oeSiwXKAQuFR", "name": "清华大学", "img": "https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2693386884,1727296839\u0026fm=58\u0026bpow=1200\u0026bpoh=1200" }, { "id": "XL27oeSiwXKAQuF1", "name": "北京大学", "img": "https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2152123166,2178049351\u0026fm=58\u0026bpow=1080\u0026bpoh=1080" }, { "id": "XL27oeSiwXKAQuF2", "name": "人民大学", "img": "https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2642337058,1598432384\u0026fm=58\u0026w=121\u0026h=140\u0026img.PNG" }] } }, { "_id": "XL28U3kPDdDCJ9m1", "item": { "diqu": "杭州", "list": [{ "id": "XL27oeSiwXKAQuF3", "name": "杭州师范大学", "img": "https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2219745018,1861674512\u0026fm=58\u0026bpow=475\u0026bpoh=475" }, { "id": "XL27oeSiwXKAQuF4", "name": "浙江大学", "img": "https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=3694367183,2414886214\u0026fm=58\u0026bpow=995\u0026bpoh=995" }] } }] [代码] 这里有两条数据,一个是北京地区的,一个是杭州地区的,正好对应我们图上的地区。然后每条json数据里面包含一个学校list,比如北京地区有清华大学,北京大学,人民大学。而每个大学对象里又包含学校id,学校名,学校校徽。 有了上面的源数据,接下来我们就看具体的实现 首先是wxml文件,其实很简单,就是一个大的列表用来显示地区,大列表里面又有一个小的列表用来显示学校。 [代码]<!--index.wxml--> <!-- 列表 --> <block wx:for="{{dataList}}" wx:key="item"> <view class='item-root'> <text class='title'>{{item.item.diqu}}</text> <block wx:for="{{item.item.list}}" wx:key="item"> <view class='img-root' bindtap='goDetail' data-item='{{item}}'> <image class='img' src='{{item.img}}'></image> <text class='xuexiao'>{{item.name}}</text> </view> </block> </view> </block> [代码] 然后是wxss文件 [代码]/* pages/myorder/myorder.wxss */ page { background: #fff; } .item-root { display: flex; flex-direction: column; } .title { width: 100%; background: gainsboro; } .img-root { display: flex; margin-left: 15px; margin-top: 5px; border-bottom: solid 1px gainsboro; } .img { width: 30px; height: 30px; } .xuexiao { margin: 5px 10px; flex: 1; } [代码] 至于如何把源数据json解析并显示到列表中,可以参考我之前写的解析本地json到列表。 《列表功能实现和本地json数据解析》: https://www.kancloud.cn/java-qiushi/xiaochengxu/767304 [图片] 视频讲解: https://edu.csdn.net/course/play/9531/202161 [图片] 到这里我们的地区列表就轻松的实现了,再来看下时间轴列表的实现 小程序时间轴列表实现 [图片] 还是先看数据源,我们拿清华大学为例 [代码]{ "_id": "XL27oeSiwXKAQuFR", "name": "清华大学", "desc": "清华大学始建与1900年,位于北京", "img": "https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=2693386884,1727296839\u0026fm=58\u0026bpow=1200\u0026bpoh=1200", "wangzhi": "http://www.tsinghua.edu.cn", "diqu": "北京市海淀区", "newsList": [{ "time": "2019年4月1日", "content": "招聘职位:英语老师,数学老师,物理老师", "title": "逸夫楼3楼大厅北京新东方专场招聘会" }, { "time": "2019年3月25日", "title": "北京京东专场招聘", "content": "招聘岗位:管培生,总裁助理,总经理" }] } [代码] 可以看到我们是顶部的学校信息,和底部的newsList组成,newsList就是我们时间轴的具体数据源。下面我们就来看看实现代码。 wxml文件如下,注释里写的很清楚了 [代码]<view class='top-root'> <view class='img-root'> <image class='img' src='{{deatil.img}}'></image> </view> <view class='top-desc-root'> <text class='xiangqing'>{{deatil.name}}</text> <text class='xiangqing'>网址:{{deatil.wangzhi}}</text> <text class='xiangqing'>地区:{{deatil.diqu}}</text> </view> </view> <!-- 时间轴 --> <view class="listview-container"> <block wx:for="{{newsList}}" wx:key="item"> <view class="playlog-item" bindtap="itemTapped"> <view class="dotline"> <!-- 竖线 --> <view class="line"></view> <!-- 圆点 --> <view class="dot"></view> <!-- 时间戳 --> </view> <view class="content"> <text class="course">{{item.time}}</text> <text class="course">{{item.title}}</text> <text class="chapter">{{item.content}}</text> </view> </view> <ad unit-id="adunit-5abb45645905fc90" wx:if="{{index % 5 == 4}}"></ad> </block> </view> [代码] wxss样式文件如下 [代码]page { background: #fff; } .top-root { display: flex; flex-wrap: nowrap; flex-direction: row; } .img-root { height: 40px; width: 40px; margin: 5px; } .img { width: 100%; height: 100%; } .top-desc-root { flex: 1; display: flex; flex-direction: column; } .xiangqing { font-size: 28rpx; color: #000; } /*时间轴*/ /*外部容器*/ .listview-container { margin: 10rpx 10rpx; } /*行样式*/ .playlog-item { display: flex; } /*时间轴*/ .playlog-item .dotline { width: 35px; position: relative; } /*竖线*/ .playlog-item .dotline .line { width: 1px; height: 100%; background: #ccc; position: absolute; top: 0; left: 15px; } /*圆点*/ .playlog-item .dotline .dot { width: 11px; height: 11px; background: #30ac63; position: absolute; top: 10px; left: 10px; border-radius: 50%; } /*时间戳*/ .playlog-item .dotline .time { width: 100%; position: absolute; margin-top: 30px; z-index: 99; font-size: 12px; color: #777; text-align: center; } /*右侧主体内容*/ .playlog-item .content { width: 100%; display: flex; flex-direction: column; border-bottom: 1px solid #ddd; margin: 3px 0; } /*章节部分*/ .playlog-item .content .chapter { font-size: 30rpx; line-height: 68rpx; color: #444; white-space: normal; padding-right: 10px; } /*课程部分*/ .playlog-item .content .course { font-size: 28rpx; line-height: 56rpx; color: #999; } [代码] 到这里时间的样式就已经实现了,我们接下来要做的就是把数据源json数据解析到页面上。方式有如下三种 1,把json放本地 2,把json导入到云开发数据 3,把json放到我们自己的服务器后台 下面我简单已放在云开发数据库并请求解析为例 先看下我云开发后台数据库 [图片] 然后写个云函数去获取云开发数据库里的json数据源,就是上图红色框里的数据 [图片] 可以看到我们成功的请求到了云数据库里的数据到本地。并成功解析并渲染到页面上了。是不是很简单。 当然,实现这些你还需要有一定的云开发知识。 同样为大家提供了云开发视频讲解:https://edu.csdn.net/course/detail/9604 有任何关于编程的问题都可以加我微信2501902696(备注编程开发) 编程小石头,码农一枚,非著名全栈开发人员。分享自己的一些经验,学习心得,希望后来人少走弯路,少填坑。 [图片]
2019-04-24 - 路由的封装
小程序提供了路由功能来实现页面跳转,但是在使用的过程中我们还是发现有些不方便的地方,通过封装,我们可以实现诸如路由管理、简化api等功能。 页面的跳转存在哪些问题呢? 与接口的调用一样面临url的管理问题; 传递参数的方式不太友好,只能拼装url; 参数类型单一,只支持string。 alias 第一个问题很好解决,我们做一个集中管理,比如新建一个[代码]router/routes.js[代码]文件来实现alias: [代码]// routes.js module.exports = { // 主页 home: '/pages/index/index', // 个人中心 uc: '/pages/user_center/index', }; [代码] 然后使用的时候变成这样: [代码]const routes = require('../../router/routes.js'); Page({ onReady() { wx.navigateTo({ url: routes.uc, }); }, }); [代码] query 第二个问题,我们先来看个例子,假如我们跳转[代码]pages/user_center/index[代码]页面的同时还要传[代码]userId[代码]过去,正常情况下是这么来操作的: [代码]const routes = require('../../router/routes.js'); Page({ onReady() { const userId = '123456'; wx.navigateTo({ url: `${routes.uc}?userId=${userId}`, }); }, }); [代码] 这样确实不好看,我能不能把参数部分单独拿出来,不用拼接到url上呢? 可以,我们试着实现一个[代码]navigateTo[代码]函数: [代码]const routes = require('../../router/routes.js'); function navigateTo({ url, query }) { const queryStr = Object.keys(query).map(k => `${k}=${query[k]}`).join('&'); wx.navigateTo({ url: `${url}?${queryStr}`, }); } Page({ onReady() { const userId = '123456'; navigateTo({ url: routes.uc, query: { userId, }, }); }, }); [代码] 嗯,这样貌似舒服一点。 参数保真 第三个问题的情况是,当我们传递的参数argument不是[代码]string[代码],而是[代码]number[代码]或者[代码]boolean[代码]时,也只能在下个页面得到一个[代码]string[代码]值: [代码]// pages/index/index.js Page({ onReady() { navigateTo({ url: routes.uc, query: { isActive: true, }, }); }, }); // pages/user_center/index.js Page({ onLoad(options) { console.log(options.isActive); // => "true" console.log(typeof options.isActive); // => "string" console.log(options.isActive === true); // => false }, }); [代码] 上面这种情况想必很多人都遇到过,而且感到很抓狂,本来就想传递一个boolean,结果不管传什么都会变成string。 有什么办法可以让数据变成字符串之后,还能还原成原来的类型? 好熟悉,这不就是json吗?我们把要传的数据转成json字符串([代码]JSON.stringify[代码]),然后在下个页面把它转回json数据([代码]JSON.parse[代码])不就好了嘛! 我们试着修改原来的[代码]navigateTo[代码]: [代码]const routes = require('../../router/routes.js'); function navigateTo({ url, data }) { const dataStr = JSON.stringify(data); wx.navigateTo({ url: `${url}?jsonStr=${dataStr}`, }); } Page({ onReady() { navigateTo({ url: routes.uc, data: { isActive: true, }, }); }, }); [代码] 这样我们在页面中接受json字符串并转换它: [代码]// pages/user_center/index.js Page({ onLoad(options) { const json = JSON.parse(options.jsonStr); console.log(json.isActive); // => true console.log(typeof json.isActive); // => "boolean" console.log(json.isActive === true); // => true }, }); [代码] 这里其实隐藏了一个问题,那就是url的转义,假如json字符串中包含了类似[代码]?[代码]、[代码]&[代码]之类的符号,可能导致我们参数解析出错,所以我们要把json字符串encode一下: [代码]function navigateTo({ url, data }) { const dataStr = encodeURIComponent(JSON.stringify(data)); wx.navigateTo({ url: `${url}?encodedData=${dataStr}`, }); } // pages/user_center/index.js Page({ onLoad(options) { const json = JSON.parse(decodeURIComponent(options.encodedData)); console.log(json.isActive); // => true console.log(typeof json.isActive); // => "boolean" console.log(json.isActive === true); // => true }, }); [代码] 这样使用起来不方便,我们封装一下,新建文件[代码]router/index.js[代码]: [代码]const routes = require('./routes.js'); function navigateTo({ url, data }) { const dataStr = encodeURIComponent(JSON.stringify(data)); wx.navigateTo({ url: `${url}?encodedData=${dataStr}`, }); } function extract(options) { return JSON.parse(decodeURIComponent(options.encodedData)); } module.exports = { routes, navigateTo, extract, }; [代码] 页面中我们这样来使用: [代码]const router = require('../../router/index.js'); // page home Page({ onLoad(options) { router.navigateTo({ url: router.routes.uc, data: { isActive: true, }, }); }, }); // page uc Page({ onLoad(options) { const json = router.extract(options); console.log(json.isActive); // => true console.log(typeof json.isActive); // => "boolean" console.log(json.isActive === true); // => true }, }); [代码] route name 这样貌似还不错,但是[代码]router.navigateTo[代码]不太好记,[代码]router.routes.uc[代码]有点冗长,我们考虑把[代码]navigateTo[代码]换成简单的[代码]push[代码],至于路由,我们可以使用[代码]name[代码]的方式来替换原来[代码]url[代码]参数: [代码]const routes = require('./routes.js'); function push({ name, data }) { const dataStr = encodeURIComponent(JSON.stringify(data)); const url = routes[name]; wx.navigateTo({ url: `${url}?encodedData=${dataStr}`, }); } function extract(options) { return JSON.parse(decodeURIComponent(options.encodedData)); } module.exports = { push, extract, }; [代码] 在页面中使用: [代码]const router = require('../../router/index.js'); Page({ onLoad(options) { router.push({ name: 'uc', data: { isActive: true, }, }); }, }); [代码] navigateTo or switchTab 页面跳转除了navigateTo之外还有switchTab,我们是不是可以把这个差异抹掉?答案是肯定的,如果我们在配置routes的时候就已经指定是普通页面还是tab页面,那么程序完全可以切换到对应的跳转方式。 我们修改一下[代码]router/routes.js[代码],假设home是一个tab页面: [代码]module.exports = { // 主页 home: { type: 'tab', path: '/pages/index/index', }, uc: { path: '/pages/a/index', }, }; [代码] 然后修改[代码]router/index.js[代码]中[代码]push[代码]的实现: [代码]function push({ name, data }) { const dataStr = encodeURIComponent(JSON.stringify(data)); const route = routes[name]; if (route.type === 'tab') { wx.switchTab({ url: `${route.path}`, // 注意tab页面是不支持传参的 }); return; } wx.navigateTo({ url: `${route.path}?encodedData=${dataStr}`, }); } [代码] 搞定,这样我们一个[代码]router.push[代码]就能自动切换两种跳转方式了,而且之后一旦页面类型有变动,我们也只需要修改[代码]route[代码]的定义就可以了。 直接寻址 alias用着很不错,但是有一点挺麻烦得就是每新建一个页面都要写一个alias,即使没有别名的需要,我们是不是可以处理一下,如果在alias没命中,那就直接把name转化成url?这也是阔以的。 [代码]function push({ name, data }) { const dataStr = encodeURIComponent(JSON.stringify(data)); const route = routes[name]; const url = route ? route.path : name; if (route.type === 'tab') { wx.switchTab({ url: `${url}`, // 注意tab页面是不支持传参的 }); return; } wx.navigateTo({ url: `${url}?encodedData=${dataStr}`, }); } [代码] 在页面中使用: [代码]Page({ onLoad(options) { router.push({ name: 'pages/user_center/a/index', data: { isActive: true, }, }); }, }); [代码] 注意,为了方便维护,我们规定了每个页面都必须存放在一个特定的文件夹,一个文件夹的当前路径下只能存在一个index页面,比如[代码]pages/index[代码]下面会存放[代码]pages/index/index.js[代码]、[代码]pages/index/index.wxml[代码]、[代码]pages/index/index.wxss[代码]、[代码]pages/index/index.json[代码],这时候你就不能继续在这个文件夹根路径存放另外一个页面,而必须是新建一个文件夹来存放,比如[代码]pages/index/pageB/index.js[代码]、[代码]pages/index/pageB/index.wxml[代码]、[代码]pages/index/pageB/index.wxss[代码]、[代码]pages/index/pageB/index.json[代码]。 这样是能实现功能,但是这个name怎么看都跟alias风格差太多,我们试着定义一套转化规则,让直接寻址的name与alias风格统一一些,[代码]pages[代码]和[代码]index[代码]其实我们可以省略掉,[代码]/[代码]我们可以用[代码].[代码]来替换,那么原来的name就变成了[代码]user_center.a[代码]: [代码]Page({ onLoad(options) { router.push({ name: 'user_center.a', data: { isActive: true, }, }); }, }); [代码] 我们再来改进[代码]router/index.js[代码]中[代码]push[代码]的实现: [代码]function push({ name, data }) { const dataStr = encodeURIComponent(JSON.stringify(data)); const route = routes[name]; const url = route ? route.path : `pages/${name.replace(/\./g, '/')}/index`; if (route.type === 'tab') { wx.switchTab({ url: `${url}`, // 注意tab页面是不支持传参的 }); return; } wx.navigateTo({ url: `${url}?encodedData=${dataStr}`, }); } [代码] 这样一来,由于支持直接寻址,跳转home和uc还可以写成这样: [代码]router.push({ name: 'index', // => /pages/index/index }); router.push({ name: 'user_center', // => /pages/user_center/index }); [代码] 这样一来,除了一些tab页面以及特定的路由需要写alias之外,我们也不需要新增一个页面就写一条alias这么麻烦了。 其他 除了上面介绍的navigateTo和switchTab外,其实还有[代码]wx.redirectTo[代码]、[代码]wx.navigateBack[代码]以及[代码]wx.reLaunch[代码]等,我们也可以做一层封装,过程雷同,所以我们就不再一个个介绍,这里贴一下最终简化后的api以及原生api的映射关系: [代码]router.push => wx.navigateTo router.replace => wx.redirectTo router.pop => wx.navigateBack router.relaunch => wx.reLaunch [代码] 最终实现已经在发布在github上,感兴趣的朋友可以移步了解:mp-router。
2019-04-26 - 2000万公众号运营者笑了,从此关联小程序再无授权!
似乎很少人发现,在刚刚过去的小假期之前,一个新能力在微信开发者社区突然被官宣了。 公众号关联小程序将无需小程序管理员确认,且小程序可被无限数量的公众号关联。 这是继小程序可无限制内嵌公众号后,公众号又一次「改革开放」,让小程序真正成为公众号的左膀右臂。正如微软小程序高级产品经理张鹏对我们说:“小程序应该要帮公众号的”,这次真的帮大了! 1 公众号关联小程序 不是公众号文章内嵌小程序 标题是不是像绕口令?之所以要重点强调,是因为很多人将这两者混为一谈。 比如,今天就有用户称:这个功能我已经用了好几个月,为什么现在才官宣呢? [图片] 乍一听吓一跳,微信竟然官宣个几个月前上线的功能?经过进一步了解,晓程序观察(yinghoo-tech)发现,原来这位用户是将公众号文章内嵌小程序跟公众号关联小程序,这两个不同的功能混淆了,闹了个大乌龙,真是啼笑皆非。 这也难怪,毕竟这两功能的关系过于密切。 实际上,前一项能力早就在今年2月份就已释放,当时我们判断,小程序与公号的连通性大大加强,因为在公号文章以及自定义菜单栏都可以自由插入小程序,不需要再关联。 [图片] 既然文章内嵌小程序无需关联小程序,乍一看,好像关联不关联都没那么重要了,为什么微信还要将关联授权给取消呢? 这是因为,公众号如果没有与小程序关联,在一些玩法中依然有很多痛点,就像是横亘在二者之间的一根“刺”。 以自定义菜单为例,如果小程序不关联公众号,运营者将无法在自定义菜单栏,设置小程序特定路径,只可以转跳到主页。 比如,某公号在菜单栏底部设置抽奖,用户进入公号点击底部栏进行抽奖时,跳转的将不是抽奖页面,而是抽奖小程序的主页,用户还需要继续查询才能找到该公号的抽奖活动。 以前为了解决这个问题,很多公号运营者不得不找到小程序开发者,索要页面路径,对于一些即时性很强的活动,就会大大影响活动进度。如果想用的小程序是个人类目,运营者很难联系上开发者,工作甚至就此卡住。 现在,微信终于将这颗刺给拔掉了! 此后公号运营者如需关联小程序,只需在后台「小程序管理」 - 「关联小程序」中输入小程序名称/APPID/账号原始ID,即可关联小程序。减少等待小程序管理员确认授权的时间,极大提高工作效率。 不过想要授权,有2个前提: 1、遵守公众号只能关联3个不同主体小程序,和10个同主体的小程序规则; 2、该小程序必须已设置无需关联确认。 [图片] 否则,你的公号将无法关联小程序。 2 小程序无限关联公号 是把“双刃剑”? 众所周知,微信公号是个待开采的流量池,而小程序就是负责开采的机器。两者相结合,公众号呈现内容,引流吸粉,再到小程序转化成交,已经成为创业者轻车熟路的方式。 而小程序关联公众号的大门,并非一开始就彻底敞开,而是遵循用户需求,一步一步地开放。 起初,小程序只能关联50个公众号;之后,该数字拓展到了500个。随着小程序的普及,不少开奖、打卡类工具类小程序备受公号运营者欢迎,此时,用张鹏的话来说,“500个依旧不够,小程序都要开启择优授权公号了”。 于是微信为了解决这一问题,放出一个大招,公众号文章可无限制内嵌小程序了!从那一刻开始,公号运营者如需在文章里内嵌小程序时,只需搜索小程序全称或AppID,就能将小程序内嵌其中。 但这种方式依旧有“限制”,最大的限制莫过于其大大增加了开发者之间的沟通成本。直到现在,微信团队彻底解放小程序关联公众号的“门禁”,小程序终于可以在公众号里随心所欲地穿梭。 不过,一片叫好的同时,不同的声音也不绝于耳。 “别人关联我的小程序,还不用经过我的同意,真的好吗?”某工具类小程序开发者对我们表示。 虽说小程序被更多人知道,被更多公众号关联使用,是一种荣幸。但同时,有开发者发现,不少三俗公号会在小程序里上传一些敏感信息,如果现在他们可以随意关联小程序,可以进行更多操作,这对于开发者而言,显然弊大于利。 据了解,提出“抗议”的开发者已经不少。他们认为,既然登录需要授权、调取信息需要授权,为什么现在关联小程序却不用确认授权了。他们拿出了公众号文章作为举例,“如果转发原创公号文章时,无需不授权也能转发,何谈尊重原创”。 所以我们看到,微信团队在发布这项功能的同时,也给了这类开发者“后路”。如果开发者希望小程序在被关联时保留管理员确认环节,可前往“小程序管理后台-设置-基本设置-关联公众号设置”修改设置项。 当然,辩论的圆桌上有正方就有反方,张鹏就是坚决拥护“无需授权是件好事”的代表:“从本质上说,小程序是一个发布出来的网页,公众号关联我认为确实不需要确认。至于三俗的内容应该由微信的内容审核机制来判断,而不是小程序运营者”。 正反两方辩论得难解难分,站在小程序运营者角度来看,你会怎么看?
2019-04-10 - 微信小程序上线“页面收录”功能,真正的SEO时代来了!
“搜索一直应该是小程序的一个主要流量来源,小程序和APP的一个很大不同,APP是一个个的信息孤岛,互相之间没法交换信息。但小程序是可以被系统统一检索到,是可以直接搜索到小程序里面的内容的。” ——张小龙于2019微信公开课Pro 靴子终于落地。 3月29日,不少小程序开发者在后台收到了一条通知,通知表示,小程序新增页面收录功能,开发者可以设置小程序是否能被收录,或者通过配置实现特定页面被收录。 [图片] 乍一看有些云里雾里,实际上,这项能力在微信安卓7.0.4版中曾灰度测试过,让我们能一睹真容: 只要开启了该功能,小程序每一个页面都能被直接搜索到,比如搜索“计算器”,在内容一栏中就会展示页面中含有“计算器”的小程序,点击之后直达该页面。 [图片] 也就是说,理论上所有小程序页面都能被用户搜索,上百万个小程序的内容全部被打开,连成了一片信息的汪洋大海。 1 小程序SEO时代来了 对于这项能力,有开发者用了“太震撼”三个字来形容,并且认为它的重要性不亚于去年的下拉“小程序桌面”。 为了更深入了解它,我们不妨以搜索巨头百度作为参考。 众所周知,只要在搜索框里输入某个关键词,结果页就会展现出某个网站中含有该关键词的页面,点击直达该页面,这是页面收录功能的基本形式。 [图片] 我们可以小程序比作“网站”,以前只能搜索“整个网站”,现在可以搜索到里面的页面了。 百度小程序前段时间推出了一项能力,就是把小程序拆分为一个个网页,并被搜索引擎抓取。微信此项新能力与此类似,也是通过搜索直达小程序页面。 但百度智能小程序接入搜索的方式比较复杂,需在开发者工具中将小程序Web化,然后上传审核发布。 [图片] 另外,尽管以前微信也推出过搜索直达页面功能,即“服务直达”和“好物圈”,但都需要开发者主动申请配置,对于中小商家而言,有一定的技术门槛。 而这一门槛现已不复存在,因为小程序“页面收录设置”默认开启,所以商家不需要任何操作,就能被收录到微信搜索中。 [图片] 然而,收录仅仅是开始,更重要的是,此后所有商家都能通过对页面结构的优化,使小程序的排名更加靠前,获取更多搜索流量。 正如微信团队给我们的回复所说:“这一体验优化,可以帮助用户缩短寻找服务的路径,提升搜索效率,也可以帮助开发者的小程序获得更多曝光。” 换句话说,小程序SEO时代从这一刻真正到来了。 2 微信搜索是小程序的下一站 不过,在晓程序观察(yinghoo-tech)的调查中,也有商家表示质疑,用户有在微信里搜索的习惯吗?微信搜索的流量大吗?做小程序SEO有意义吗? 商家的疑惑情有可原,毕竟“流量”是所有商家都关心的话题,也是最原始驱动力之一。 确实,我们采访过的案例大都表示“功能直达”、“好物圈”等入口的量并不大,使用习惯尚待养成。 但我们认为,搜索未来必定成为微信小程序的重要入口,理由有两点: 1. 内容才是养成习惯的关键 从用户侧来说,互联网从PC端走到移动端,在交互体验、使用场景上都发生巨大变化,而搜索的习惯却继承了下来,像搜索巨头百度,每日需要响应60亿次的搜索请求,可见搜索依旧是获取信息和服务的重要方式。 并且,搜索行为往往与需求相伴,这与小程序“即用即走,触手可得”的特性也天然契合:需求产生——搜索小程序——使用服务——离开小程序,这条路格外顺畅。 但有人会说,“搜索是日常行为不假,但习惯在微信里搜索的人却不多,抢占搜索流量有意义么?” 我们不妨逆向思考这个问题,许多开发者表示,现在微信搜索最大的问题反倒不是使用习惯,而是缺乏内容,比如搜“羊毛大衣如何清洗”,完全得不到任何结果。 [图片] 但未来小程序页面收录功能全面上线后,所有有关“羊毛大衣如何清洗”的小程序页面都会被抓取到,可能是一篇文章,也可能就是羊毛大衣清洗服务,由此实现了搜—看—买的过程。 所以有理由相信,未来搜索结果的内容丰富度不再是问题,用户习惯的养成也就水到渠成。 2. 搜索将带来下一波流量红利 从商家侧看,搜索将带来下一波微信的流量红利。 以前,微信小程序的社交属性被过度放大,众多运营者们通过各种方式诱导用户转发分享,从而实现数据短时间内的爆炸增长,这对用户体验是巨大伤害。 所以我们发现,微信一直在通过诸如限制跳转数量、收回分享回调等方式,逐步规范小程序的社交裂变行为,2019年第一季度,确实没有再看到现象级小程序的刷屏事件。 有开发者表示,“社交流量带来的第一波红利期已经渐渐褪去,下一个取而代之的流量洼地应该是搜索”。 因为搜索能让所有商家不论大小,都站在同一起跑线上,流量获取更加平等化。 意思是在相同的搜索规则下,小团队开发的小程序通过页面结构的优化,提供更优质的服务,一样有机会排在头部小程序前面。 比如,在除去使用过、好友用过、功能直达等客观因素外,现在搜索“北京天气”,结果页是「墨迹天气」排第一。但在未来大家公平角逐时,「北京天气查询」这样的小程序,也有机会通过页面优化排在最前面,因为提供的服务更加精准。 [图片] 这对于中小开发者而言无疑一大助推。 微信一直在关注如何让优质的小程序被更好的发现,目前来看,让服务与产品质量更高的小程序通过搜索入口自然涌现,这是微信给出的答案。 3 小程序SEO,需要提前这几点 那么,商家如何提前落子,布局微信搜索呢?或许从传统搜索引擎的SEO方法中,可以获得一些思路。 “过去做SEO 的方法是:结合站点属性和需求词数据,根据搜索引擎平台提供的索引规范和页面结构标准指引,不断进行调优化,从而提高索引收录排名的可能性”飞虎商联产品负责人周圣伟说到,他有着多年的建站以及SEO经验。 但目前小程序页面收录能力仍处于内测,没有明确的索引规范,所以我们只能提出一些值得商家注意的地方。 1.先考虑清楚哪些页面允许被收录 需要强调,小程序页面收录开关是全局性的,意思是开启后小程序每个页面都能被搜索到,但有的小程序页面中包含开发者不想暴露的内容。 此时就要选择“关闭收录”,然后单独进行sitemap配置,sitemap配置就是注明哪些页面可以被收录,哪些不能被收录。 [图片] 2.根据索引规范优化页面结构 一般而言,索引规范会说明标题摘要、关键词的相关规则,例如“北京天气如何北京今天天气怎么样”这种关键词堆砌都是在禁止之列。另外,像页面内功能按钮的易用性,音频视频的使用情况,都要考虑在内。 [图片](百度智能小程序的索引规范) 3.原创保护与抓取周期也要注意 周圣伟告诉我们,格外需要注意的是品牌词与原创保护,比如,现在有许多小程序就是各类公号文章的集合,但这些小程序未来都有可能违反了原创保护,导致权重的下降。 最后,索引的周期性也值得关注,搜索引擎的抓取一般有周期性,如果周期内页面没有更新,就不容易被抓取到,所以及时更新页面就成为一个关键。 当然,上周推出的小程序评测能力,或许也与搜索排名有千丝万缕的联系,还有微信特有的社交关系,相信也是影响权重的重要指标。 [图片] 由此可见,未来小程序SEO将成为一门全新的产业,据我们了解,有很多小程序服务商已经嗅到了商机,都摩拳擦掌等待着在小程序SEO领域大干一场。 当然,更具体的细则以及一些疑问,都要等这项新能力正式上线时才能揭晓。
2019-04-03 - 新富文本组件
mp-html小程序富文本组件 news欢迎加入 QQ 交流群:699734691示例小程序添加获取组件包功能[图片] 功能介绍 支持在多个平台使用 支持丰富的标签(包括 table、video、svg 等) 支持丰富的事件效果(自动预览图片、链接处理等) 支持锚点跳转、长按复制等丰富功能 支持大部分 html 实体 丰富的插件(关键词搜索、内容编辑等) 效率高、容错性强且轻量化使用方法1. npm 方式 在项目根目录下执行 npm install mp-html 开发者工具中勾选 使用 npm 模块 并点击 工具 - 构建 npm 在需要使用页面的 json 文件中添加 { "usingComponents": { "mp-html": "mp-html" } } 在需要使用页面的 wxml 文件中添加 <mp-html content="{{html}}" /> 在需要使用页面的 js 文件中添加 Page({ onLoad() { this.setData({ html: 'Hello World!' }) } }) 2. 源码方式 将源码中的代码包(dist/mp-weixin)拷贝到 components 目录下,更名为 mp-html 在需要使用页面的 json 文件中添加 { "usingComponents": { "mp-html": "/components/mp-html/index" } } 后续步骤同上 获取github 链接:https://github.com/jin-yufeng/mp-html npm 链接:https://www.npmjs.com/package/mp-html 文档链接:https://jin-yufeng.gitee.io/mp-html
2022-03-04 - 富文本组件体验小程序
简介 上周发布的 新富文本显示组件 收获了许多关注,为方便大家了解和体验,[代码]demo[代码] 小程序上线啦 [图片] 大家可以在这里查看介绍和示例或者进行自定义的测试,发现任何问题都欢迎反馈哦 立即体验 [图片] GitHub链接 Github链接
2020-12-27