微信小游戏开发者平台真机测试连不上,开发者工具却可以

利用 three.js 开发微信小游戏的尝试 - 为程序员服务
利用 three.js 开发微信小游戏的尝试
这是一次利用
开发微信小游戏的尝试,并不能算作是教程,只能算是一篇笔记吧。
微信 WeChat 6.6.1 开始引入了微信小游戏,初期上线了一批质量相当不错的小游戏。我在查阅各处的文章时候,发现其中有几款是基于
开发的,目前火爆朋友圈的《跳一跳》就是其中之一。这引起了我的注意,想起几年前也做过不少 WebGL 的尝试,于是禁不住想要弄到微信小游戏平台上试试。
最新版本的
首先应该具有一定的
开发经验,有之前写过的简单演示代码;
最新版本的“
另外补充一点:需要足够的耐心,微信开发者工具问题多多,编辑器也各种问题,我是使用 WebStorm 来编写代码,您不妨也试试。遇到奇怪的问题的时候,可能需要多启动几次开发者工具,非常令人恼火。
找一个之前的 WebGL 演示
我随便找了一个很久以前做过的演示代码,如下:
可以拖动旋转,
创建微信小游戏项目
好啦,现在可以进入开发者工具尝试移植了。
首先,创建一个标准的小游戏项目。
选择“小游戏”项目进行创建,并选择一个空的目录作为项目目录
点击“确定”就会打开一个模板项目,是一个射击游戏,大致的结构如下:
├── game.js
├── game.json
├── project.config.json
├── README.md
├── databus.js
├── main.js
├── base
├── animation.js
└── ...
├── libs
├── symbol.js
└── webapp-adapter.js
├── npc
└── enemy.js
├── player
├── bullet.js
└── ...
└── runtime
├── background.js
└── ...
├── audio
├── bgm.mp3
└── ...
└── images
├── Common.png
└── ...
基于这个模板,我们可以将不需要的内容暂时删除,以便跑我们自己的项目。其中,射击游戏相关的内容都可以移除了,但是我们要保留一些关键的代码和配置文件,清理以后,大概会是这个样子:
├── game.js
├── game.json
├── project.config.json
├── README.md
├── main.js
└── libs
├── symbol.js
└── webapp-adapter.js
└── images
目录里面放置我们需要使用到的图片,在这个例子中,我放了粒子图片和 indienova Logo 图片在里面;
将我们必然会用到的
three.min.js
也可以,但是后面需要修改这个文件,所以建议使用没有 minified 的版本)放入
如果有其它资源,比如音频文件什么的,也可以自建目录放进去;
然后需要修改一下配置文件,简单的说,只要修改
project.config.json
projectname
不变,这个是入口,会引入
清空,我们的新代码会在这里完成。
我们还保留了两个 js 文件,
webapp-adapter.js
。这里需要
webapp-adapter.js
很重要,官方解释如下:
小游戏的运行环境在 iOS 上是
,在 Android 上是
,都是没有 BOM 和 DOM 的运行环境,没有全局的 document 和 window 对象。因此当你希望使用 DOM API 来创建 Canvas 和 Image 等元素的时候,会引发错误。
这些使用 wx API 模拟 BOM 和 DOM 的代码组成的库称之为 Adapter。顾名思义,这是对基于浏览器环境的游戏引擎在小游戏运行环境下的一层适配层,使游戏引擎在调用 DOM API 和访问 DOM 属性时不会产生错误。Adapter 是一个抽象的代码层,并不特指某一个适配小游戏的第三方库,每位开发者都可以根据自己的项目需要实现相应的 Adapter。官方实现了一个 Adapter 名为
weapp-adapter
, 并提供了完整的源码,供开发者使用和参考。
除此之外 weapp-adapter 还模拟了以下对象和方法:
document.createElement
canvas.addEventListener
localStorage
XMLHttpRequest
需要强调的是,weapp-adapter 对浏览器环境的模拟远不完整的,仅仅只针对游戏引擎可能访问的属性和调用的方法进行了模拟,也不保证所有游戏引擎都能通过 weapp-adapter 顺利无缝接入小游戏。直接将 weapp-adapter 提供给开发者,更多地是作为参考,开发者可以根据需要在 weapp-adapter 的基础上进行扩展,以适配自己项目使用的游戏引擎。
原文请参阅:
可见,微信团队已经为我们开发游戏做好了一些准备,比如露出的
,我们到时候直接拿来使用就是。
将之前的代码移植到项目中
开始将之前写好的代码移植过来,注意由于要使用 ES 6(
)标准,所以之前的代码可能要做相应的调整,不过大部分都是语法的调整,有一些方法的使用需要增加
bind(this)
,具体的还请大家参阅我们提供的
,就算不看书,随便尝试一下也能很快有所了解。
的主体代码看起来是这样:
import * as THREE from 'libs/three.js'
= canvas.getContext('webgl')
let renderer
// ... 其它变量/常量 ...
* 游戏主函数
export default class Main {
constructor() {
this.start()
= new THREE.Scene()
= new THREE.WebGLRenderer({ context: ctx })
//... 其它代码块 ...
// 开始循环
this.loop()
// UPDATE 更新
update() {
// ... 数据更新代码块 ...
// RENDER 渲染
render() {
// ... 渲染代码块 ...
// 实现游戏帧循环
this.update()
this.render()
window.requestAnimationFrame( this.loop.bind(this), canvas )
一些要点:
由于微信已经为我们准备好了的
,所以我们无需自己再进行创建,只需要取得的
就可以了,这里我们使用的不是
let ctx = canvas.getContext('webgl')
然后我们在创建
WebGLRenderer
的时候,直接使用这个
就可以了。
= new THREE.WebGLRenderer({ context: ctx })
这两点做到了,基本上就不会有太大问题了。
一切正常的话,开发者工具里面应该就能跑得起来了。
能跑起来并不算完,还需要真机测试
真机调试不复杂,微信开发者工具提供了真机预览功能,只要点一下“预览”,就会上传代码,并生成二维码供测试。
开发者用微信扫描这个二维码,就可以打开测试。
如果您没有修改过 three.js 源文件,那么很有可能只看到一个黑屏。
还好,微信小游戏提供了一个调试开关,我们可以选择打开调试:
然后再次扫码进入,就可以查看调试信息了:
可以清晰的得知,
createElementNS
不被支持,那么我们可以将所有
createElementNS
createElement
(注意,这只是暂时解决方案,此方法并不是理想方案,我们以后应该会有更好的解决方法。通过修改 adapter 应该就可以减少 three.js 源文件的修改。)
document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
// 改为 ==&
document.createElement( 'canvas' );
不止这一处,而且也不止是针对 canvas 的创建,还有针对 img 的创建,可以都修改掉。
另外由于真机上跑的是 OpenGL ES 1.x/2.x/3.x,所以还有一句要处理:
var version = parseFloat( /^WebGL\ ([0-9])/.exec(gl.getParameter(gl.VERSION))[ 1 ] );
// 改为 ==&
var version = parseFloat( /^(WebGL|OpenGL ES)\ ([0-9])/.exec(gl.getParameter(gl.VERSION))[ 1 ] );
这样改过后,重新再尝试一下看,是不是已经可以了?
临时解决方案使用后,我后来尝试修改
webapp-adapter.js
,就不需要再对
进行修改了:
createElementNS: function createElementNS(nameSpace, tagName) {
return this.createElement(tagName)
方法很简单:我们在
的定义中添加
createElementNS
,然后忽略掉 NameSpace 即可。
好了,简单的介绍了一下怎么利用
开发微信小游戏,这并不是一篇教程,只是在目前信息和资料不完善情况下的一种尝试,也希望大家一起参与到开发和研究中来,互相交流。
欢迎加入我们的小组:
微信小游戏小组
indienova 小组
为了方便大家参考,特提供源代码。其中包括一个微信小游戏项目和原始的 WebGL 项目。
源代码下载
这几天研究这部分内容的时候,遇到不少奇怪的问题,这部分内容又很难依靠 Google 和 StackOverflow 来解决,还好找到了交流的 QQ 群,以及群中成员的交流心得,才算完成了这次尝试。
参与的微信小游戏 QQ 群:
参考的文章:
其它相关链接
请先阅读:
目前,Cocos、Egret、Laya 已经完成了自身引擎及其工具对小游戏的适配和支持,访问对应的官方文档可以更快地接入小游戏的开发
你的独立游戏第一站
原文地址:, 感谢原作者分享。
您可能感兴趣的代码微信小程序全面实战,架构设计 && 躲坑攻略 - 简书
微信小程序全面实战,架构设计 && 躲坑攻略
最近集中开发了两款微信小程序,分别是好奇心日历(每天一条辞典+一个小投票)和好奇心日报(轻量版),直接上图:
Paste_Image.png
本文将结合具体的实战经验,主要介绍微信小程序的基础知识、开发中遇到的难点、项目的架构设计、最佳实践以及踩过的坑。文章内容较多,如果想看架构设计和躲坑技巧,请直接浏览后面的正文,简书没有目录,也挺伤感的。
文末有好奇心日报小程序的二维码,欢迎围观。
值得再次声明的是:微信小程序的内容部分是hybrid模式,并非原生,所以性能并不好,绑定的tap事件也有明显的延迟。
每一个由边框围起来的部分,都是一个最小粒度的原生view
如上图所示,每一个由边框围起来的部分,都是一个最小粒度的原生view,可以看出,整个微信小程序的内容部分,就是一个原生view。
小程序有哪些基础知识?
一个完整的微信小程序是由一个App实例和多个Page实例构成,其中App实例表示该小程序应用,多个Page表示该小程序的多个页面。
此外,微信小程序并没有提供自定义组件的方式,这就导致微信小程序在开发较复杂应用时,可能会比较艰难。
微信小程序本身很简单,和一个模板语言的难度几乎相当,翻翻官方教程就可以开始动手搞。
我也建议大家先动起来,然后再细致啃啃官方文档。由于微信官方文档仍在不断大幅更新中,所以务必查看最新官方文档。
微信小程序的基础知识主要分为以下几个部分:
? 两种配置文件 && 两个核心函数
? WXML模板语法,页面渲染
? 页面间的跳转
? 交互事件
? 官方组件和官方API
后文会就每个部分简单介绍介绍...
两种配置文件 && 两个核心函数
app.json 应用的全局配置文件
app.json是针对微信小程序的全局配置,主要包含以下几个配置:
? pages:页面路径的数组,表示小程序要加载的所有页面,其中数组第一项代表小程序的初始页面。
? window:微信原生功能,定制化不强。可设置小程序的状态栏、导航条、标题以及窗口背景色。
? tabBar:微信原生功能,定制化不强。适用于常规的Tab应用,Tab栏可置于顶部或底部;tabBar是一个数组,仅支持2-5个tab。
? networkTimeout:配置小程序网络请求的超时时间。
? debug:调试模式开关,开发模式下建议开启,正式发布别忘了关闭。
app.json配置的主要区域
page.json 页面的全局配置文件
除了上面提到的全局配置,每个页面还可以单独配置page.json,page.json会覆盖app.json中的配置,并只对当前页面生效。
page.json只能对window配置,有两个比较有用的配置项分别是:
? enablePullDownRefresh:是否开启下拉刷新
? disableScroll:只能在page.json配置,禁止页面上下滚动,猜测可以实现完美滑屏滑动(未验证)
App() 小程序注册入口,全局唯一
App()用来注册一个小程序,全局只有一个,全局的数据也可以放到这里面来操作。
// 注册微信小程序,全局只有一个
let appConfig = {
// 小程序生命周期的各个阶段
onLaunch: function(){},
onShow: function(){},
onHide: function(){},
onError: function(){},
// 自定义函数或者属性
App(appConfig);
// 在别的地方可以获取这个全局唯一的小程序实例
const app = getApp();
小程序并没有提供销毁的方式,所以只有当小程序进入后台一定时间、或者系统资源占用过高的时候,才会被真正的销毁。
Page() 页面注册入口
Page()用来注册一个页面,维护该页面的生命周期以及数据。
// 注册微信小程序,全局只有一个
let pageConfig = {
// 页面生命周期的各个阶段
onLoad: function(){},
onShow: function(){},
onReady: function(){},
onHide: function(){},
onUnload: function(){},
onPullDownRefresh: function(){},
onReachBottom: function(){},
onShareAppMessage: function(){},
// 自定义函数或者属性
Page(pageConfig);
// 获取页面堆栈,表示历史访问过的页面,最后一个元素为当前页面
const page = getCurrentPages();
关于各个生命周期的细节以及流程,参考下图,可以细细品味:
Paste_Image.png
app.json 和 page.json 维护了应用和页面的配置属性。App() 和 Page() 维护了应用和页面的各个生命周期以及数据。
那么,APP 和 Page 如何将数据传递到页面呢?页面又是如何渲染呢?
WXML模板语法,页面渲染
小程序虽然是hybrid模式,但并不使用HTML渲染,而是全部通过自定义标签来渲染页面。这样做的好处我不清楚,但问题却不少:不能跨浏览器、富文本解析困难,iframe视频不支持,没办法外链跳转。
和所有的模板语言一样,WXML支持数据绑定、条件渲染、循环、模块化等功能。
在 WXML 中可以使用{{}}将 Page 的变量或者表达式包裹起来,实现数据绑定,举个例子:
// 将message的值渲染到view中
&view& {{ message }} &/view&
// 将id的值渲染到view的id属性中
&view id="item-{{id}}"& &/view&
// 根据isSelected的值,输出不同的class
&view class="{{isSelected ? 'selected' : ''}}"& HelloWorld &/view&
// 结合template,可以传入更复杂的数据
&template is="objectCombine" data="{{...article, categoty, tag: '埃隆马斯克'}}"&&/template&
条件渲染,适合根据数据输出不同状态的 WXML,举个例子:
&view wx:if="{{length & 5}}"& 1 &/view&
&view wx:elif="{{length & 2}}"& 2 &/view&
&view wx:else& 3 &/view&
循环渲染,适合遍历数据输出多段 WXML,举个例子:
// wx:for 表示需要遍历的数据
// wx:key 使用唯一的字段来标识,有利于提升性能
// wx:for-index 表示数组的下标
// wx:for-item 表示数组的元素
&view wx:for="{{[{id:1, message: 'HelloWorld1'}, {id:2, message: 'HelloWorld2'}]}}"
wx:key="id"
wx:for-index="idx"
wx:for-item="item"&
{{idx}}: {{item.message}}
wx:key 有利于提升重新渲染时的效率,建议添加
WXML的模块化,可以让我们复用一些wxml片段,还挺重要的,举个例子:
// 引入wxml模块
&import src="../../components/grid-article/index"&&/import&
&block wx:for="{{posts}}" wx:for-item="post" wx:key="id"&
// 调用wxml模块,同时可传入数据
&template is="grid-article" data="{{post}}"&&/template&
数据和页面的状态是一一对应的,微信小程序中,设计一份好的数据结构,对于Page和页面的代码都有很大的帮助。
微信小程序并不支持a标签,那么多个页面之间如何跳转呢?
页面间的跳转
小程序以栈的形式维护了历史访问的所有页面,并提供了多种页面间的跳转方式;结合前文提到的App()和Page()的各个生命周期,不同的跳转方式和不同的生命周期关联,如下图:
Paste_Image.png
举个例子,Tab 切换对应的生命周期(以 A、B 页面为 Tabbar 页面,C 是从 A 页面打开的页面,D 页面是从 C 页面打开的页面为例)
Paste_Image.png
好了,APP和Page负责维护小程序的生命周期和数据,模板负责接受数据完成页面渲染,页面间的跳转负责将多个页面贯穿起来,那么,如何发生交互呢?接下来我们简单说一下事件。
// bindtap 和 catchtap的区别在于
// bindtap 不会阻止事件冒泡
// catchtap会冒泡事件冒泡
&view id="tapTest" data-hi="WeChat" bindtap="tapName"& Click me! &/view&
&view id="tapTest" data-hi="WeChat" catchtap="tapName"& Click me! &/view&
// 绑定的函数tapName只是一个函数名称,默认接受一个event对象作为参数
tapName: function(event) {
console.log(event)
接下来,另一个问题是:tapName() 如何接受自定义参数呢?
传递自定义参数主要有两种方式:
第一种:将参数绑定到wxml标签上,然后通过event.target.dataset获取
第二种:直接使用Page.data或其他数据
到目前为止,一个完整的小程序框架已经实现
? 小程序只有逻辑和视图两部分,而且不提供组件化解决方案
? 逻辑主要包含四个东西:两个配置文件 && 两个核心函数
? 视图很简单,模板语法稍微有点不完善
? 逻辑层的数据绑定到视图层是由小程序框架自动支持,数据变化,视图自动变化
? 视图层到逻辑层的,主要通过事件的方式来实现
? 视图之间的跳转,小程序也提供了它自己的方式,并不支持a标签
框架有了,小程序还提供了官方组件以便快速开发,提供了API以增强应用能力。
这块就不具体介绍了,也需要注意小程序的官方文档还在大规模的更新中,务必查看最新版
官方组件:
微信小程序的基础知识就是以上的内容,并不复杂,边查边写都可以。
接下来会介绍更进阶一些的内容,内容主要结合好奇心日报这个小程序项目,先看效果:
好奇心日报实际效果图
如何设计微信小程序?
构建系统 && 目录结构
由于微信小程序本身对工程化几乎没有任何的支持,所以动手搭建一份:。
使用gulp进行编译构建,主要功能包括:
? 集成了Redux,数据管理更方便
? 开发过程中,使用xml取代wxml,对开发工具更友好
? 开发过程中,使用less取代wxss,功能更强大
? 引入es-promise,以便可以创建并使用Promise
? 添加promisify工具函数,可以便捷的将官方Api转换成Pormise模式
? 引入normalizr,可以将数据扁平化,更方便进行数据管理
? 引入babel自动进行ES2015特性转换
? 对wxml/wxss/js/img压缩,相对开发者工具提供的压缩,会减小一丢丢体积。
目录结构设计
按照pages、components、redux、vendors/libs、images几个核心部分拆分,直接上目录。
小程序工程目录
? dist目录:构建输出的文件存放到这个目录。
? src目录:开发模式的文件,包括app、页面、组件、图片等静态资源、辅助函数库、Redux数据管理器、第三方工具库。
? gulpfile.js:不用多说,gulp构建任务的入口文件。
? package.json:不用多说,管理者构建任务的依赖。
? thirdPlugins:由于小程序并不支持直接使用npm,我们可以自主拉取构建,然后拷贝到vendors里,有时候需要简单修改。
构建系统会将src目录下的代码,编译处理后输出到dist目录,小程序开发工具只需要引入dist目录即可。
有了构建工具,代码开发起来更舒心,但很快就遇到另外一个糟心的问题,那就是如何管理散布在各处的数据?
要知道,微信小程序并没有提供组件化方案,所以把组件写成无状态组件特别适合,但是页面管理太多数据很凌乱,有什么办法可以将数据集中管理呢?
引入Redux进行数据集中管理
关于Redux相关的内容,之前有三篇博客详细介绍,有兴趣的建议先移步:
Redux整体介绍:
对State进行横向和纵向拆分设计:
Reducer的最佳实践:
这儿就简单介绍一下,如何在微信小程序中引入Redux 以及 如何将微信小程序和Redux连接起来。
直接在 thirdPlugins目录 运行 yarn add redux / npm install redux,等redux安装好了之后,将 dist目录 的 redux.js/redux.min.js 拷贝到vendors目录中。
需要进行简单的修改才能使用,将压缩版208行代码 (i) 改成 (i || {})即可。
简单修改,Redux就可以正常使用
连接微信小程序和Redux
将Redux和微信小程序连接起来才会真的有用处。找了个现成的方案,可以直接使用。
一个完整的Redux方案如下,包括:将Store注入到App中、将state的数据和reducer的方法映射到Page中。一旦state发生变化,Page.data也会更新,进而触发页面的重新渲染。
// APP的逻辑
import { createStore, applyMiddleware, combineReducers } from './vendors/redux.js';
import thunk from './vendors/redux-thunk.js';
import { Provider } from './vendors/weapp-redux.js';
// import reducers
import { rootReducer } from './redux/reducer.js';
// 从Storage读取数据
let entities = wx.getStorageSync('entities') || {};
const store = createStore(
rootReducer, {
// 将读取的数据注入到store中
entities: entities
applyMiddleware(
let appConfig = {
onLaunch: function() {},
onHide: function() {
let state = store.getState(),
cacheEntities = {};
// 体积大于2M,直接清空,防止缓存占用过大体积
if (sizeof(state.entities) &= 2 * 1024 * 1024) {
cacheEntities = state.
// 退出时将entities缓存下来
wx.setStorageSync('entities', cacheEntities);
App(Provider(store)(appConfig));
// Page的逻辑
import { connect } from '../../vendors/weapp-redux.js';
import { fetchArticleDetail } from '../../redux/models/articles.js';
let pageConfig = {
postsHash: {}
onLoad: function(params) {
var me = this,
{ id, postsHash } = me.
me.fetchArticleDetail(id, function() {}, function() {});
// 考虑到列表页已经获取到部分数据
// 为了在详情页第一时间利用这些数据,我们将params传入
// 防止以后需要用data的数据,我们将data也一并传入
let mapStateToData = (state, params, data) =& {
id: params.id,
postsHash: state.entities.posts
let mapDispatchToPage = dispatch =& ({
fetchArticleDetail: (id, callback, errorCallback) =& dispatch(fetchArticleDetail(id, callback, errorCallback)),
pageConfig = connect(mapStateToData, mapDispatchToPage)(pageConfig)
Page(pageConfig);
需要注意的是,为了保证第一时间能拿到数据,我们对wechat-weapp-redux/src/connect.js做了优化调整,修改的地方如下
// 修改了以下两个函数
// 可以对照原项目修改,也可以直接拿我的模板项目使用
function handleChange(options) {
if (!this.unsubscribe) {
const state = this.store.getState();
// 将data也一并传入
const mappedState = mapState(state, options, this.data);
if (!this.data || shallowEqual(this.data, mappedState)) {
this.setData(mappedState)
function onLoad(options) {
this.store = app.
if (!this.store) {
warning("Store对象不存在!")
if (shouldSubscribe) {
this.unsubscribe = this.store.subscribe(handleChange.bind(this, options))
// 第一次处理的时候也传入options
handleChange.apply(this, [options])
if (typeof _onLoad === 'function') {
_onLoad.call(this, options)
引入Redux的优势
引入Redux的好处在于可以集中管理数据,并且让Page的代码保持绝对简单,让所有的组件都变成简单可复用的无状态组件。
此外,Redux还让离线缓存更方便,数据复用更简单。
引入Redux解决了数据散布各处的问题,参考Redux的管理思路,我们构思了一套简单组件化解决方案:假设我们把所有的组件都设计成无状态组件,而组件的数据来源是Page.data,那么我们是否也可以将组件数据的管理抽离到一个单独的文件中呢?接下来讲讲我们使用的简单的组件化解决方案。
简单的组件化解决方案
这份组件化解决方案的核心就在于把组件的关联数据集中起来管理,只暴露出默认数据和数据的操作函数。
比如好奇心日报的详情页有个Toolbar,该Toolbar并不复杂,主要提供返回和点赞功能,其中点赞功能只对文章详情有效,研究所详情页会将点赞功能隐藏。
Toolbar组件
// components/toolbar/index.js 文件
// 仅提供默认值,不需要和page中的数据保持同步
let defaultData = {
isPraised: false,
praiseCount: 0,
showPraiseIcon: false,
// 切换点赞状态
function togglePraise() {
// 本质上是修改Page.data中的toolbarData
// 返回上一级
function navigateToBack(wx) {
wx.navigateBack({ delta: 1 });
module.exports = {
defaultData,
togglePraise,
navigateToBack
// pages/articles/show.js 文件
import Toolbar from '../../components/toolbar/index.js';
let pageConfig = {
// 其他数据
// Toolbar数据,单独的一份数据,便于维护
toolbarData: Toolbar.defaultData
// 点赞或者取消赞
togglePraise: function() {
Toolbar.togglePraise.call(me);
// 这儿的组件化并不是真正的组件化
// 而是将组件相关的逻辑和函数抽离到单独的文件中,保证Page代码清晰。
// 同时也为这部分组件逻辑复用提供了可能。
// 本质上来说,抽离出的组件都是“操作Page.data的工具函数”,他们也是纯函数,和“操作state的reducer”类似。
这种Redux的组件化解决方案既简单又好用,保持一定的代码规范即可。这样设计当然是为了复用,同时也让Page的逻辑保持极度精简。
开发中遇到了哪些难点 && 微信小程序有多少坑?
微信小程序目前的确算不上公测的版本,开发者工具不完善、真机表现和开发环境差异很大、部分组件性能较差、部分功能有缺陷,只有经历了这些大坑,才会真的知道你有多“爱”微信小程序。这儿总结了开发中的难点以及微信小程序中遇到的比较明显的坑。
富文本解析
微信小程序并不支持HTML标签,所以对于富文本解析来说,难度较大,而且有些功能还没有办法实现,比如:iframe视频、连接跳转等
这块功能建议由后台统一转换,如果非得前端转换,建议参考下面的思路。
这款组件,替我省了不少时间。推荐大家使用,期间遇到一些问题,也分享给大家。
? wxParse 默认层级只支持10层html嵌套,如果想要支持更深的层级,在wxParse.xml复制几份template即可。
? wxParse 提供了图片加载成功的回调wxParseImgLoad,很好用。但如果富文本中的图片已经预设宽高比,那么可以不用依赖该回调,在html2jons.js中根据屏幕宽度直接计算出图片高度,先占位,可以避免页面频繁抖动的问题。
? 如果你的富文本中有自定义模块,对wxParse.xml中的template进行改造即可。
自定义模块样式
数据扁平化
具体如何扁平化,请移步上一篇博客 。
这儿只简单介绍下扁平化应用场景:
好奇心日报的研究所是三级表结构:papers & questions & options,后台返回的数据是三级嵌套数据,如果想要修改option.selected字段,需要三级嵌套循环!如果想要获取所有选中的option,需要三级嵌套循环!
页面展现速度优化
数据复用,比如复用列表页的数据,可以让详情页的标题等字段第一时间呈现出来。
离线缓存,同样可以让列表页和详情页第一时间呈现出来,甚至有可能减少请求数量。
无论是数据复用还是离线缓存,配合数据扁平化,都非常好用。
小程序默认设置代理,会和Shadowsocks等VPN冲突(最新版又坏了)
解决方法很简单,设置微信小程序不使用代理;或者临时关闭VPN即可。
上一版开发者工具已经解决该问题,最新版又坏了。
最新版微信小程序移除了对Promise的支持。
开发者自行引入兼容库即可,推荐。使用的时候,直接引入Promise即可。
// 引入Promise
import Promise from '../vendors/es6-promise.js';
// 用Promise封装wx.request网络请求
function request(method = 'GET') {
return function(url, data = {}) {
return new Promise(function(resolve, reject) {
wx.request({
'Content-Type': 'application/json'
success: function(res) {
let { statusCode, errMsg, data } =
if (statusCode == 200 && data.meta && data.meta.status == 200) {
resolve(data.response);
reject('网路请求错误,请稍后再试~');
fail: function(err) {
reject('网路请求不符合规范,请检查域名是否符合要求~');
export const GET = request('GET');
export const POST = request('POST');
export const PUT = request('PUT');
export const DELETE = request('DELETE');
// 用Promise封装小程序的其他API
export const promisify = (api) =& {
return (options, ...params) =& {
return new Promise((resolve, reject) =& {
api(Object.assign({}, options, { success: resolve, fail: reject }), ...params);
const getLocation = promisify(wx.getLocation);
不清楚微信为何会临时移除Promise,统一内置不也挺好?
小程序不能实现完美的fullpage效果,会出现上下拉扯的感觉(最新版预计已修复,待实际验证)。
小程序一旦滚动顶部或者底部,继续滑动的时候,就会出现拉扯现象。而这个拉扯现象还无法禁止。
最新版可以对页面配置disableScroll,预计可以修复这个问题,待实际验证。
fullpage效果
swiper组件不支持轮播,性能差,文档模糊(部分最新版已修复)
? swiper组件之前并不支持轮播,最新版已修复
? swiper组件之前是通过设置left属性来实现动画,在小米5、华为V8等高端等安卓机上能够感受到明显的卡顿,当然原因是X5内核引起的。最新版已修复,换成了transform,性能有一定的提升。
swiper性能提升
? 文档并未标记可以垂直轮播,但其实是可以的。
// 简单设置vertical即可,但由于官方文档并未备注,尽量不要使用。可以自己开发一个swiper组件。
&swiper vertical="true"&&/swiper&
? swiper组件的小圆点其实是可以定制化的,但是官方文档并未说明,而且开发者工具也看不出来,只有鼠标hover到元素上的时候可以看到相关的class,简单猜测一下,最后分析出来它的实现方式。
swiper圆点的实现原理
// 圆点的父元素,用来控制圆点间的间距
.wx-swiper-dot {
// 圆点,可以通过font-size修改圆点的大小,color修改圆点的颜色
&:before {
width: 100%;
display: inline-
font-size: 56
content: '圆点编码';
// active状态的圆点
&.wx-swiper-dot-active {
&:before {
color: #ffc81f;
小程序WXSS的font-face的url不接受路径作为参数
可以将字体文件转换为base64,然后引用。
font-face接受base64,不接受url
同样,如果想要使用iconfont,也可以使用类似的方案,将iconfont字体文件base64之后再引入。
小程序的margin表现有问题(最新版已经修复)
之前发生margin折叠的时候,会取小的那个值。会导致底部留白等设置失效。
canvas问题
canvas并没有深入研究,目前的发现的问题主要是两个,如下图标记:
? 层级问题,canvas总是会盖在其他元素上面。
? 支持度不好,在小米5、iPhone5s画图会出现畸形。
canvas绘制饼图有Bug
最后通过CSS3的方式绘制饼图
&template name="pie"&
&view class="com-pie"&
&!-- 小于50% --&
&view class="percent-1" style="transform: rotate(0.4turn);"&&/view&
&view class="percent-2"&&/view&
&!-- 大于50% --&
&view class="percent-1" style="transform: rotate(0.5turn);"&&/view&
&view class="percent-2" style="opacity:1; transform: rotate(0.3turn);"&&/view&
&/template&
.com-pie {
z-index: 0;
display: inline-
width: 100
height: 100
line-height: 100
border-radius: 50%;
color: #000;
background-color: #
background-image: linear-gradient(to right, transparent 50%, #cccccc 0);
.percent-1,
.percent-2 {
width: 60%;
height: 100%;
left: 50%;
transform-origin:
.percent-1 {
background-color:
z-index: -2;
.percent-2 {
height: 110%;
opacity: 0;
z-index: -1;
background-color: #
&.selected {
background-color: #ffe9a5;
background-image: linear-gradient(to right, transparent 50%, #ffc81f 0);
.percent-2 {
background-color: #ffc81f;
微信小程序的rpx会出现精度问题
设置margin-left/margin-right负值,可能导致页面能够左右晃动。猜测 是rpx导致的精度问题。
rpx本质上会转换为px,在不同宽度的设备上,实际的rpx值会转换为带小数的px值,四舍五入可能出现问题,之前使用rem布局的时候在QQ浏览器遇到过类似的问题。
rpx精度问题
wx.request表现不合理,并且携带特殊字符会报错
? 请求返回404错误,也会触发success回调。
不要想当然的认为会触发fail回调,判断一个请求成功或失败,请使用wx.request返回的状态来判断。只有不符合规范的请求,才会触发fail。
wx.request回调
? 请求的数据中,如果有特殊字符(比如\u2820),会报错。
只会在真机上出现,并且安卓调试模式并不输出错误日志,开发者工具没毛病。估计会有更多的特殊字符会导致这个问题,已反馈给微信小程序相关的开发者,静待修复。
特殊字符导致wx.request挂掉
在浏览器中查看HTML的时候能够发现这个字符,就是最后那个小红点。
HTML中的特殊字符
开发者工具,切换页面的时候,有时候wxml不会同步切换
希望微信什么时候能解决一下。
微信小程序给wxml模板赋值的时候,解构放到前面可能会报错
最新版会遇到这个问题,老版本虽然不会报错,但是在部分真机上会出现问题。
原因未知,遇到这个问题的朋友可以考虑绕过去。
解构赋值导致报错
微信小程序的scroll-view暴露的bindscroll函数并不能实时监听
依赖实时获取滚动位置的功能不能实现。比如滚动时toolbar的动态隐藏和显示。
最新版开发工具不能关掉自动刷新
微信小程序的会默认监听文件变化,然后自动刷新。
但不足的是每次都是全量刷新,而不是模块的热替换,反而会影响开发速度,尤其对于喜欢频繁Command + S的开发者,你会发现你的小程序在不断的刷新。建议关闭。
建议关闭监听文件变化
但最新版开发者工具,不勾选也会自动刷新。
微信小程序不支持requestAnimationFrame
微信小程序不支持requestAnimationFrame,所以部分性能优化做不了。不支持的原因未知。
Page.onload函数可以接受参数
该参数是有URL决定的,也就是URL携带的参数。
官方文档这块写的有点混淆,特意拿出来说一下。举个例子:url中传递的时候id=1,那么option.id=1,而不是什么option.query。
Page.onload参数文档描述混淆
不要给Page.data传入太多无用数据,会影响渲染效率,在iOS上表现特别明显
尽量传入精简的数据,保持Page.data和view间简单的绑定关系即可。
切换Tab后,小程序下拉刷新的 loading 动画会出现 Bug,iOS设备
默认下拉刷新的动画是三个点依次渐现,如果操作中切换了Tab,再切换回来后,下拉刷新就变成一个静止的点了。
最新版swiper组件在iPhone上自动轮动的时候,会闪烁!
原因未知,已经发布的小程序不受影响,未发布的最新版有这个问题,猜测是微信升级引起的。
真机上有概率卡死,目前不确定是代码问题还是小程序的问题。
有遇到类似问题的朋友欢迎指出。
总结说点啥?
本文主要围绕微信小程序的基础知识、如何设计微信小程序、开发过程中遇到的问题三个方面介绍。
微信小程序的基础知识主要包括:
? 两种配置文件 && 两个核心函数
? WXML模板语法,页面渲染
? 页面间的跳转
? 交互事件
? 官方组件和官方API
如何设计微信小程序的内容主要包括:
? 构建系统 && 目录结构
? 引入Redux进行数据集中管理
? 简单的组件化解决方案
最后还介绍开发过程中遇到的难点 以及 微信小程序的大小坑。
微信小程序本身并不复杂,开发过程却比较艰辛,尤其是第一次在真机上运行的时候,觉得这个世界恶意满满。
想体验小程序?看这儿
好奇心小程序
BAT搬过砖、创过业,做了无数的项目,更偏好于前端工程化、团队效率优化的工作。
最近集中开发了两款微信小程序,分别是好奇心日历(每天一条辞典+一个小投票)和好奇心日报(轻量版),直接上图: 本文将结合具体的实战经验,主要介绍微信小程序的基础知识、开发中遇到的难点、项目的架构设计、最佳实践以及踩过的坑。文章内容较多,如果想看架构设计和躲坑技巧,请直接浏览...
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金 相信有很多...
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金相信有很多朋友...
富人买入资产。穷人只有支出。中产阶级购买自以为是资产的负债。 我们工资越来越高,工作越来越努力,但是我们的负债也越来越多,银行的贷款和生活的开支迫使我们辞职了需要马上找到新的工作,要不然生活就不能正常运转。我们可以挣很多钱,但钱不能使我们富有,因为我们没有资产,当然就不会有...
精要主义序言反复强调,精要主义不是要让你原本做十件事,那么现在就做两件;计划要去十个目的地,现在只去三个目的地;不是简单的做减法。而是运用二八法则,或者说是关键环节原则,认真审视思考已经进入你的生命,或者说正要进入你生命的人事物,梳理清楚自己对他们态度,评价...
托马斯·麦卡锡从演员半路出家成为电影制作人后,一直在低调地为边缘人群发声。他首次担任编剧和导演的电影是2003年的《心灵驿站》,剧中彼特·丁拉基饰演一位霍博肯的流浪汉,为了独处而搬进一座废弃的火车站,最终却在那儿迎来人生的改变。2007年的《人生访客》中,理查德·詹金斯饰演...
杂言。快读完了《从领先到极致》讲述四季酒店40年发展历程,核心就是持续追求卓越的服务客户和企业员工文化。所以历经多次经济衰退的困境,却越挫越勇持续发展到至强!所谓的苦练内功、增强实力,细想,其实和军队很类似,顺利的时候大家都攻城略地,但逆境中经受生死考验时,只有英名坚定的将...
推荐!推荐!推荐!
大力向大家推荐《捕捉儿童敏感期》这本书,真的很好!
刚刚看完这本书, 真心觉得很不错,很有收获,下面来分享下我的心得体会:
首先,这本书是我的好朋友推荐给我看的,第一次看到这本书,就被书名吸引了,由于工作特殊性,我是蒙台梭利老...}

我要回帖

更多关于 微信小游戏开发者工具 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信