比德勒科技

微信小程序view排列图片

日期:2024-08-05 01:45 / 作者:www.biddlecn.com

再谈微信小程序SEO关键词排名规则及怎么做上去?

作者:白杨SEO

为什么想到再来写写微信小程序SEO这个?因为最近有很多朋友问我微信小程序怎么做排名,所以写一下,如有帮助或启发,可以分享给需要的朋友。

本文大纲:

1、微信小程序SEO是什么意思?

2、微信小程序搜索排名规则有哪些?

3、微信小程序关键词排名怎么做上去?

4、新做的微信小程序怎么推广?

微信小程序SEO是什么意思?

SEO是指搜索引擎优化简称,微信小程序SEO是指微信小程序搜索引擎优化,

再白话一点就是指:优化用户在微信下拉用关键词搜小程序或在微信搜一搜里用关键词搜小程序的小微序排名靠前。

从微信下拉搜索截图,

从微信搜一搜搜索截图,

微信小程序搜索排名规则有哪些?

我们得先知道小程序排序规则吧,供参考:

1、上线时间(占比5%) 微信小程序的上线时间,上线时间越早,排名越靠前 小程序关键词占位非常重要,越早发布竞争越小,不过现在已经是白热化的阶段了,好关键字基本名字抢注完了。

2、小程序加载速度(占比5%) 前面有说到,小程序也是代码写出来的,谁的代码简洁,加载速度快。

3、关键字频次(10%):描述中完全匹配出现关键词次数越多,排名越靠前,有点像早期SEO关键词堆积了。

4、标题(35%):标题中关键词出现1次,且整体标题的字数越短,排名越靠前。这也是我们SEO中说的的相关性

5、使用量(占比45%) 微信小程序用户使用数量越多,排名越靠前

上面这5个因素是我2018年写的,我看市面上好多关于小程序排名规则什么的文章基本都引用了我那篇,小程序SEO这个词也有可能是我最先写。

不过过去快六年了,小程序搜索排名还是有一些变化,同时也从给别的企业或个人优化建议和实战经验再更新一下。重要度从高到低如下,依旧仅供参考哈。

1、小程序整体使用量(也就是小程序总用户量)预计占比50%左右;

2、小程序的用户行为(比如新用户搜索点击,停留时间,评价,小程序活跃度等)预计占比30%左右;

3、小程序的相关性命中率(即小程序名字/描述/服务等包含关键词匹配度)预计占比15%左右;

4、小程序上线时间,加载速度,小程序认证等等预计占比5%左右。

当然,比如小程序掉排名也会有诸如行业,政策,作弊等等因素,这个不在小程序排名规则里来讲哈。

微信小程序关键词排名怎么做上去?

上面我们讲了微信小程序排名规则影响因素占比大概,具体怎么做上去,我们可以对比来做,白杨SEO的做法也是参考哈。

常规微信小程序关键词排名优化方法:

1、做好微信小程序基础完善

这里一是指小程序上线前的代码,头像等,更多是指一些代码端优化,具体如下,来自微信官方文档建议:

小程序里跳转的页面 (url) 可被直接打开

小程序页面内的跳转url是我们爬虫发现页面的重要来源,且搜索引擎召回的结果页面 (url) 是必须能直接打开,不依赖上下文状态的。特别的:建议页面所需的参数都包含在url。

页面跳转优先采用navigator组件

小程序提供了两种页面路由方式:

a. navigator 组件

b. 路由 API,包括 navigateTo / redirectTo / switchTab / navigateBack / reLaunch 建议使用 navigator 组件,若不得不使用API,可在爬虫访问时屏蔽针对点击设置的时间锁或变量锁。

清晰简洁的页面参数

结构清晰、简洁、参数有含义的 querystring 对抓取以及后续的分析都有很大帮助,但是将 JSON 数据作为参数的方式是比较糟糕的实现。

不收录 web-view 中的任何内容

我们暂时做不到这一点,长期来看,我们可能也做不到。

设置一个清晰的标题和页面缩略图

页面标题和缩略图对于我们理解页面和提高曝光转化有重要的作用。 通过 wx.setNavigationBarTitle或自定义转发内容 onShareAppMessage对页面的标题和缩略图设置,另外也为 video、audio 组件补齐 poster / poster-for-crawler 属性。

2、微信小程序账号、描述、服务名字关键词优化布局

现在微信小程序能优化关键词的地方只有账号名称(不是简称那里)30个字符(15个汉字)和描述120个字符(60个汉字)以及小程序里面功能页面标题上可以布局如图:

你可能会问,小程序账号名字是不是越长越好(布局关键词多),又或者是越短越好(完全匹配)?其实这两者都不对,因为小程序名字的相关性在我们上面规则说了,它占比并不是最大的。

所以白杨SEO建议,如果你是新的小程序应该考虑的是完全匹配更好,但也不能做流量很大的词。如果你是老的小程序,完全可以考虑不仅是账号,你的描述同样可以用起来。

3、小程序进行认证

这里是指小程序要做认证,现在微信小程序还要求备案之类。同等条件下企业主体权重要大于个人主体,但如果一个在使用量绝对大的上面,个人主体排名也可能排前面。

4、小程序用户行为数据优化

这里小程序用户行为数据优化,简单理解就是用正常的小程序用户行业去做一些关键词排名优化。白杨SEO说得再白一点就是,搜索点击,搜索点击使用,搜索点击使用到评价(好评)等这一切都是可以做的。

有人会想,如果大词是不是要很大的量。初步这么想没错,但是你要了解真正的词在微信搜索里真正的每天搜索量有多少呢?你也可以了解同行自然的数据是多少呢?

如何判断微信搜索里面关键词用户的真实搜索量,白杨SEO的建议是腾讯广告后台投搜一搜广告+微信指数里搜一搜占比结合起来即可判断。比如【SEO】这个词微信指数94万多,搜一搜占比5.09%,大约5万。

5、小程序整体用户量提升

小程序整体用户量的提升也有助于我们小程序关键词排名。如何提高我们的小程序总体用户量呢?主要有三个方法:

1)投小程序广告(公众号或者朋友圈广告)带来用户;

2)mai小程序用户(这种很多一些数据平台在做),不过这种最大风险在于用户本身机器fen之类,严重也构成影响微信正常生态,白杨SEO不建议。

3)做小程序自然流量全网推广。

上面讲的是一般常规小程序,比如有一些诸如品牌类小程序,服务类小程序,可以走服务类优化方法。

新做的微信小程序怎么推广?

新做的或者原有的微信小程序怎么做推广,四年多前我写了16种微信小程序推广方法。

当然你可能会问,这都过去四五年了,里面的推广方法现在能用吗?我想跟你说的是,即使再过四五年,有些方法还是能用,大同小异。

如果你现在有更好的微信小程序推广方法可以留言,这样可以帮助更多需要的朋友。

微信小程序学习总结(3)——“顿悟”页面布局

前情提要学写小程序,从了解源文件结构开始小程序全局文件小程序页面文件示例小程序页面文件结构关系对开发小程序的启发Flex布局

根据网上教程,微信小程序采用Flex布局,它的最大优点是可以根据屏幕大小伸缩,使得一套布局在任何尺寸的屏幕上看都很漂亮。

灵活的页面布局

我们看一些网上教程,通常学到的页面布局要么是垂直布局,要么是水平布局,但是实际上小程序的页面布局是非常灵活的。以我的小程序“YOYO小宝箱”为例,图1和图2展示了灵活的小程序页面布局。

图1. 算术小练习页面布局

图2. 形状小制作页面布局

这些实际应用场景中的页面布局,到底是垂直布局,还是水平布局呢?

一开始我被这个布局问题搞得很崩溃,在写图2“形状小制作”这个页面时,我基本是“撞大运”的状态,在写每个组件的布局时,垂直布局不行就改成水平的,水平布局不行又改成垂直的,就这么改了几个小时,还是达不到想要的效果。然后我困了,睡着了,睡梦中居然“顿悟”了小程序布局的精髓——垂直与水平的嵌套!

布局精髓——嵌套

仍以“算术小练习”和“形状小制作”这两个页面为例,垂直与水平布局的嵌套关系图3、图4所示。

图3. “算术小练习”布局嵌套关系图

图3整体上看是垂直布局,如最外层的红色虚线框所示。在垂直布局自上而下的第三个模块内部,是一个内嵌的水平布局,如中间的紫色虚线框所示。在水平布局的右侧模块内部,是一个内嵌的垂直布局(删除图标和OK图标)。

图4. “形状小制作”布局嵌套关系图

图4整体上看也是一个垂直布局,如最外层的红色虚线框所示,分为上下两个模块。下面模块内部是水平布局,如紫色虚线框所示,分为左右两个模块。左边模块内部是垂直布局,分为颜色选择区和文字显示区两块。右边模块内部整体上看也是垂直布局,分为图标区和画布区两块。图标区则是水平布局,水平排列着铅笔图标和橡皮图标。

在写页面布局的时候,只要按照上述嵌套关系把<view>嵌套起来,把模块写在对应的<view>里就可以了。

一款优雅的小程序拖拽排序组件实现

前言#

最近po主写小程序过程中遇到一个拖拽排序需求. 上网一顿搜索未果, 遂自行实现.

灵感#

首先由于并没有啥现成的小程序案例给我参考. 所以有点无从下手, 那就找个h5的拖拽实现参考参考. 于是在jquery插件网看了几个拖拽排序实现后基本确定了思路. 大概就是用 transform 做变换. 是的, 灵感这种东西就是借鉴过来的~~

确定需求#要能拖拽, 毕竟是拖拽排序嘛, 拖拽肯定是第一位.要能排序, 先有拖拽后有天 ~~ 跑偏了, 拖拽完了肯定是要排序的要不然和movable-view有啥区别呢.能自定义列数以实现不同形式展现, 这里考虑到有列表排序, 相册排序等不同情况需要的列数不同的需求.没有bug, 呃呃呃, 这个我尽量.实现思路#

首先能拖拽的元素最起码都要是一样的大小, 至于不规则大小, 或者大小成倍数关系的均不在本次实现范围.

然后我们对应需求找解决方案:

拖拽实现#

使用 movable-view 实现拖拽, 这种方式简单快捷, 但是由于我们的灵感是使用 transform 做变换, 而这里 movable-view 本身也是用 transform 来实现的, 所以会有冲突, 遂弃之.使用自定义手势, 如 touchstart, touchmove, touchend. 对的又是这三个基佬, 虽然我们在做下拉刷新时候采用用了 movable-view 而抛弃这三兄弟. 但是是金子总会发光的, 今天就是你们三兄弟展示自身本领的时候了(真香警告). 废话有点多, 言归正传, 使用自定义手势可以方便我们控制每一个细节, 美中不足的是他们的父亲并没有提供手动阻止冒泡的特性, 搞了个 catch 和 bind 事件, 然并不支持动态切换, 所以很坑爹, 不过这都不是问题, 就是坑了些罢了.

排序实现#

排序是基于拖拽的, 通过上面 touchstart, touchmove, touchend 这三兄弟拿到触摸信息后动态计算出当前元素的排序位置,然后根据当前激活元素的排序位置去动态更换数组内其他元素的位置. 大概意思就是十个兄弟做一排, 老大起来跑到老三的位置, 老三看了看往前移了移, 老二看了看也往前移了移. 当然这是正序, 还有逆序, 比如老十跑到了老大的位置, 那么老大到老九都得顺序后移一个位置.

自定义列数#

自定义列数, 到是没啥难度, 小程序组件暴露一个列属性, 然后把计算过程中的固定的列数改成该参数就可以了

实现分析#

先上 touchstart, touchmove, touchend 三兄弟

longPress#

这里为了体验把 touchstart 换成了 longpress 长按触发. 首先我们需要设置一个状态 touch 表示我们在拖拽了. 然后就是获取 pageX, pageY 注意这里获取 pageX, pageY 而不是 clientX, clientY 因为我们的 drag 组件有可能会有 margin 或者顶部仍有其他元素, 这时候如果获取 clientX, clientY 就会出现偏差了. 这里把当前 pageX, pageY 设置为初始触摸点 startX, startY.

然后需要计算下初始化的激活元素的偏移位置 tranX 和 tranY, 这里为了优化体验在列数为1的时候初始化 tranX 不做位移, tranY 移动到当前激活元素中间位置, 多列的时候把 tranX 和 tranY 全部位移到当前激活元素中间位置.

最后设置当前激活元素的索引 cur 以及偏移量 tranX, tranY. 然后震动一下下 wx.vibrateShort() 体验美美哒.

/** * 长按触发移动排序 */longPress(e) { this.setData({ touch: true }); this.startX = e.changedTouches[0].pageX this.startY = e.changedTouches[0].pageY let index = e.currentTarget.dataset.index; if(this.data.columns === 1) { // 单列时候X轴初始不做位移 this.tranX = 0; } else { // 多列的时候计算X轴初始位移, 使 item 水平中心移动到点击处 this.tranX = this.startX - this.item.width / 2 - this.itemWrap.left; } // 计算Y轴初始位移, 使 item 垂直中心移动到点击处 this.tranY = this.startY - this.item.height / 2 - this.itemWrap.top; this.setData({ cur: index, tranX: this.tranX, tranY: this.tranY, }); wx.vibrateShort();}

touchMove#

touchmove 每次都是故事的主角, 这次也不列外. 看这满满的代码量就知道了. 首先进来需要判断是否在拖拽中, 不是则需要返回.

然后判断是否超过一屏幕. 这是啥意思呢, 因为我们的拖拽元素可能会很多甚至超过整个屏幕, 需要滑动来处理. 但是我们这里使用了 catch:touchmove 事件所以会阻塞页面滑动. 于是我们需要在元素超过一个屏幕的时候进行处理, 这里分两种情况. 一种是我们拖拽元素到页面底部时候页面自动向下滚动一个元素高度的距离, 另一种是当拖拽元素到页面顶部时候页面自动向上滚动一个元素高度的距离.

接着我们设置已经重新计算好的 tranX 和 tranY, 并获取当前元素的排序关键字 key 作为初始 originKey, 然后通过当前的 tranX 和 tranY 使用 calculateMoving 方法计算出 endKey.

最后我们调用 this.insert(originKey, endKey) 方法来对数组进行排序

touchMove(e) { if (!this.data.touch) return; let tranX = e.touches[0].pageX - this.startX + this.tranX, tranY = e.touches[0].pageY - this.startY + this.tranY; let overOnePage = this.data.overOnePage; // 判断是否超过一屏幕, 超过则需要判断当前位置动态滚动page的位置 if(overOnePage) { if(e.touches[0].clientY > this.windowHeight - this.item.height) { wx.pageScrollTo({ scrollTop: e.touches[0].pageY + this.item.height - this.windowHeight, duration: 300 }); } else if(e.touches[0].clientY < this.item.height) { wx.pageScrollTo({ scrollTop: e.touches[0].pageY - this.item.height, duration: 300 }); } } this.setData({tranX: tranX, tranY: tranY}); let originKey = e.currentTarget.dataset.key; let endKey = this.calculateMoving(tranX, tranY); // 防止拖拽过程中发生乱序问题 if (originKey == endKey || this.originKey == originKey) return; this.originKey = originKey; this.insert(originKey, endKey);}

calculateMoving 方法#

通过以上介绍我们已经基本完成了拖拽排序的主要功能, 但是还有两个关键函数没有解析. 其中一个就是 calculateMoving 方法, 该方法根据当前偏移量 tranX 和 tranY 来计算 目标key.

具体计算规则:

根据列表的长度以及列数计算出当前的拖拽元素行数 rows根据 tranX 和 当前元素的宽度 计算出 x 轴上的偏移数 i根据 tranY 和 当前元素的高度 计算出 y 轴上的偏移数 j判断 i 和 j 的最大值和最小值根据公式 endKey = i + columns * j 计算出 目标key判断 目标key 的最大值返回 目标key

/** * 根据当前的手指偏移量计算目标key */calculateMoving(tranX, tranY) { let rows = Math.ceil(this.data.list.length / this.data.columns) - 1, i = Math.round(tranX / this.item.width), j = Math.round(tranY / this.item.height); i = i > (this.data.columns - 1) ? (this.data.columns - 1) : i; i = i < 0 ? 0 : i; j = j < 0 ? 0 : j; j = j > rows ? rows : j; let endKey = i + this.data.columns * j; endKey = endKey >= this.data.list.length ? this.data.list.length - 1 : endKey; return endKey}

insert 方法#

拖拽排序中没有解析的另一个主要函数就是 insert方法. 该方法根据 originKey(起始key) 和 endKey(目标key) 来对数组进行重新排序.

具体排序规则:

首先判断 origin 和 end 的大小进行不同的逻辑处理循环列表 list 进行逻辑处理如果是 origin 小于 end 则把 origin 到 end 之间(不包含 origin 包含 end) 所有元素的 key 减去 1, 并把 origin 的key值设置为 end如果是 origin 大于 end 则把 end 到 origin 之间(不包含 origin 包含 end) 所有元素的 key 加上 1, 并把 origin 的key值设置为 end调用 getPosition 方法进行渲染

/** * 根据起始key和目标key去重新计算每一项的新的key */insert(origin, end) { let list; if (origin < end) { list = this.data.list.map((item) => { if (item.key > origin && item.key <= end) { item.key = item.key - 1; } else if (item.key == origin) { item.key = end; } return item }); this.getPosition(list); } else if (origin > end) { list = this.data.list.map((item) => { if (item.key >= end && item.key < origin) { item.key = item.key + 1; } else if (item.key == origin) { item.key = end; } return item }); this.getPosition(list); }}

getPosition 方法#

以上 insert 方法中我们最后调用了 getPosition 方法, 该方法用于计算每一项元素的 tranX 和 tranY 并进行渲染, 该函数在初始化渲染时候也需要调用. 所以加了一个 vibrate 变量进行不同的处理判断.

该函数执行逻辑:

首先对传入的 data 数据进行循环处理, 根据以下公式计算出每个元素的 tranX 和 tranY (this.item.width, this.item.height 分别是元素的宽和高, this.data.columns 是列数, item.key 是当前元素的排序key值)item.tranX = this.item.width * (item.key % this.data.columns);item.tranY = Math.floor(item.key / this.data.columns) * this.item.height;设置处理后的列表数据 list判断是否需要执行抖动以及触发事件逻辑, 该判断用于区分初始化调用和insert方法中调用, 初始化时候不需要后面逻辑首先设置 itemTransition 为 true 让 item 变换时候加有动画效果然后抖一下, wx.vibrateShort(), 嗯~, 这是个好东西最后copy一份 listData 然后出发 change 事件把排序后的数据抛出去

最后注意, 该函数并未改变 list 中真正的排序, 而是根据 key 来进行伪排序, 因为如果改变 list 中每一个项的顺序 dom结构会发生变化, 这样就达不到我们要的丝滑效果了. 但是最后 this.triggerEvent('change', {listData: listData}) 时候是真正排序后的数据, 并且已经去掉了 key, tranX, tranY 的原始数据信息(这里每一项数据有key, tranX, tranY 是因为初始化时候做了处理, 所以使用时无需考虑)

/** * 根据排序后 list 数据进行位移计算 */getPosition(data, vibrate = true) { let list = data.map((item, index) => { item.tranX = this.item.width * (item.key % this.data.columns); item.tranY = Math.floor(item.key / this.data.columns) * this.item.height; return item }); this.setData({ list: list }); if(!vibrate) return; this.setData({ itemTransition: true }) wx.vibrateShort(); let listData= []; list.forEach((item) => { listData[item.key] = item.data }); this.triggerEvent('change', {listData: listData});}

touchEnd#

写了这么久, 三兄弟就剩最后一个了, 这个兄dei貌似不怎么努力嘛, 就两行代码?

是的, 就两行... 一行判断是否在拖拽, 另一行清除缓存数据

touchEnd() { if (!this.data.touch) return; this.clearData();}

clearData 方法#

因为有重复使用, 所以选择把这些逻辑包装了一层.

/** * 清除参数 */clearData() { this.originKey = -1; this.setData({ touch: false, cur: -1, tranX: 0, tranY: 0 });}

init 方法#

介绍完三兄弟以及他们的表亲后, 故事就剩我们的 init 方法了.

init 方法执行逻辑:

首先就是对传入的 listData 做处理加上 key, tranX, tranY 等信息然后设置处理后的 list 以及 itemTransition 为 false(这样初始化就不会看见动画了)获取 windowHeight获取每一项 item 的宽高等属性 并设置为 this.item 留做后用初始化执行 this.getPosition(this.data.list, false)设置动态计算出来的父级元素高度 itemWrapHeight, 因为这里使用了绝对定位和transform所以父级元素无法获得高度, 故手动计算并赋值最后就是获取父级元素 item-wrap 的节点信息并计算是否超过一屏, 并设置 overOnePage 值

init() { // 遍历数据源增加扩展项, 以用作排序使用 let list = this.data.listData.map((item, index) => { let data = { key: index, tranX: 0, tranY: 0, data: item } return data }); this.setData({ list: list, itemTransition: false }); this.windowHeight = wx.getSystemInfoSync().windowHeight; // 获取每一项的宽高等属性 this.createSelectorQuery().select(".item").boundingClientRect((res) => { let rows = Math.ceil(this.data.list.length / this.data.columns); this.item = res; this.getPosition(this.data.list, false); let itemWrapHeight = rows * res.height; this.setData({ itemWrapHeight: itemWrapHeight }); this.createSelectorQuery().select(".item-wrap").boundingClientRect((res) => { this.itemWrap = res; let overOnePage = itemWrapHeight + res.top > this.windowHeight; this.setData({ overOnePage: overOnePage }); }).exec(); }).exec();}

wxml#

以下是整个组件的 wxml, 其中具体渲染部分使用了抽象节点 <item item="{{item.data}}"></item> 并传入了每一项的数据, 使用抽象节点是为了具体展示的效果和该组件本身代码解耦. 如果要到性能问题或者觉得麻烦, 可直接在该组件下编写样式代码.

<view> <view style="overflow-x: {{overOnePage ? 'hidden' : 'initial'}}"> <view class="item-wrap" style="height: {{ itemWrapHeight }}px;"> <view class="item {{cur == index? 'cur':''}} {{itemTransition ? 'itemTransition':''}}" wx:for="{{list}}" wx:key="{{index}}" id="item{{index}}" data-key="{{item.key}}" data-index="{{index}}" style="transform: translate3d({{index === cur ? tranX : item.tranX}}px, {{index === cur ? tranY: item.tranY}}px, 0px);width: {{100 / columns}}%" bind:longpress="longPress" catch:touchmove="touchMove" catch:touchend="touchEnd"> <item item="{{item.data}}"></item> </view> </view> </view> <view wx:if="{{overOnePage}}" class="indicator"> <view>滑动此区域滚动页面</view> </view></view>

wxss#

这里我直接把 scss 代码拉出来了, 这样看的更清楚, 具体完整代码文末会给出地址

@import "../../assets/css/variables";.item-wrap { position: relative; .item { position: absolute; width: 100%; z-index: 1; &.itemTransition { transition: transform 0.3s; } &.cur { z-index: 2; background: $mainColorActive; transition: initial; } }}.indicator { position: fixed; z-index: 99999; right: 0rpx; top: 50%; margin-top: -250rpx; padding: 20rpx; & > view { width: 36rpx; height: 500rpx; background: #ffffff; border-radius: 30rpx; box-shadow: 0 0 10rpx -4rpx rgba(0, 0, 0, 0.5); color: $mainColor; padding-top: 90rpx; box-sizing: border-box; font-size: 24rpx; text-align: center; opacity: 0.8; }}写在结尾#

还有就是该组件受限制于微信本身的 api 以及一些特性, 在超出一屏时候会无法滑动. 这里我做了个判断超出一屏时候加了个指示器辅助滑动, 使用时可对样式稍做修改(因为感觉有点丑...)

其他的好像没啥了...

补充一句, 该组件基本上没怎么使用太多小程序相关的特性, 所以按照这个思虑用h5实现应该也是可以的, 如果有h5方面的需求应该也是可以满足的...