开发微信小程序的基础知识

2019年05月13日 标签: 微信小程序 小程序开发 小程序

小程序有哪些基础知识?

一个完整的微信小程序是由一个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 = {
    data: {},
    // 页面生命周期的各个阶段
    onLoad: function(){},
    onShow: function(){},
    onReady: function(){},
    onHide: function(){},
    onUnload: function(){},
    onPullDownRefresh: function(){},
    onReachBottom: function(){},
    onShareAppMessage: function(){},

    // 自定义函数或者属性
    ...
};
Page(pageConfig);

// 获取页面堆栈,表示历史访问过的页面,最后一个元素为当前页面
const page = getCurrentPages();

关于各个生命周期的细节以及流程,参考下图,可以细细品味:


网站设计


app.json 和 page.json 维护了应用和页面的配置属性。App() 和 Page() 维护了应用和页面的各个生命周期以及数据。

那么,APP 和 Page 如何将数据传递到页面呢?页面又是如何渲染呢?

WXML模板语法,页面渲染

小程序虽然是hybrid模式,但并不使用HTML渲染,而是全部通过自定义标签来渲染页面。这样做的好处我不清楚,但难题却不少:不能跨浏览器、富文本解析困难,iframe视频不支持,没办法外链跳转。
和所有的模板语言一样,WXML支持数据绑定、条件渲染、循环、模块化等功能。

数据绑定

在 WXML 中可以使用{{}}将 Page 的变量或者表达式包裹起来,实现数据绑定,举个例子:

// 将message的值渲染到view中 {{ message }} // 将id的值渲染到view的id属性中 // 根据isSelected的值,输出不同的class HelloWorld // 结合template,可以传入更复杂的数据

条件渲染

条件渲染,恰好根据数据输出不同状态的 WXML,举个例子:

 1  2  3

循环渲染

循环渲染,恰好遍历数据输出多段 WXML,举个例子:

// wx:for 表示必需遍历的数据
// wx:key 使用唯一的字段来标识,有利于提升性能
// wx:for-index 表示数组的下标
// wx:for-item 表示数组的元素  {{idx}}: {{item.message}}

wx:key 有利于提升重新渲染时的效率,提示添加

模块化

WXML的模块化,可以让我们复用一些wxml片段,还挺重要的,举个例子:

// 引入wxml模块    // 调用wxml模块,同时可传入数据

数据和页面的状态是一一对应的,微信小程序中,设计一份好的数据结构,对于Page和页面的代码都有很大的帮助。
微信小程序并不支持a标签,那么多个页面之间如何跳转呢?


页面间的跳转

小程序以栈的形式维护了历史访问的所有页面,并提供了多种页面间的跳转方式;结合前文提到的App()和Page()的各个生命周期,不同的跳转方式和不同的生命周期关联,如下图:


网站建设


举个例子,Tab 切换对应的生命周期(以 A、B 页面为 Tabbar 页面,C 是从 A 页面打开的页面,D 页面是从 C 页面打开的页面为例)


网站制作


好了,APP和Page负责维护小程序的生命周期和数据,模板负责接受数据完成页面渲染,页面间的跳转负责将多个页面贯穿起来,那么,如何发生交互呢?接下来我们简单说一下事件。

交互事件

事件绑定

// bindtap 和 catchtap的区别在于
// bindtap 不会阻止事件冒泡
// catchtap会冒泡事件冒泡 Click me!  Click me! // 绑定的函数tapName只是一个函数名称,默认接受一个event对象作为参数
Page({
  tapName: function(event) {
    console.log(event)
  }
})

接下来,另一个难题是:tapName() 如何接受自定义参数呢?

事件传参

传递自定义参数主要有两种方式:
第一种:将参数绑定到wxml标签上,然后通过event.target.dataset获取
第二种:直接使用Page.data或其他数据

到当前为止,一个完整的小程序框架已经实现
? 小程序只有逻辑和视图两部分,而且不提供组件化解决方案
? 逻辑主要包含四个东西:两个配置文件 && 两个核心函数
? 视图很简单,模板语法稍微有点不完善
? 逻辑层的数据绑定到视图层是由小程序框架自动支持,数据变化,视图自动变化
? 视图层到逻辑层的,主要通过事件的方式来实现
? 视图之间的跳转,小程序也提供了它自己的方式,并不支持a标签

框架有了,小程序还提供了官方组件以便快速开发,提供了API以增强应用能力。

这块就不具体介绍了,也必需留意小程序的官方文档还在大规模的更新中,务必查看最新版
官方组件:组件 · 小程序
官方API:API · 小程序

微信小程序的基础知识就是以上的博中网投诚信者app内容,并不复杂,边查边写都可以。

接下来会介绍更进阶一些的博中网投诚信者app内容,内容主要结合好奇心日报这个小程序项目,先看效果:

(无法上传 GIF,点 链接 查看)

如何设计微信小程序?

构建系统 && 目录结构

构建系统

  • 由于微信小程序本身对工程化几乎没有任何的支持,所以动手搭建一份:wxapp-redux-starter。
    使用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相关的博中网投诚信者app内容,之前有三篇博客详细介绍,有兴趣的提示先移步:
Redux整体介绍:Redux 入门教程,应用的状态管理器
对State进行横向和纵向拆分设计:State设计,Redux 开发第一步
Reducer的最佳实践:Reducer 最佳实践,Redux 开发最重要的部分

这儿就简单介绍一下,如何在微信小程序中引入Redux 以及 如何将微信小程序和Redux连接起来。

引入Redux

直接在 thirdPlugins目录 运行 yarn add redux / npm install redux,等redux安装好了之后,将 dist目录 的 redux.js/redux.min.js 拷贝到vendors目录中。
必需进行简单的修改才能使用,将压缩版208行代码 (i) 改成 (i || {})即可。


网站设计

简单修改,Redux就可以平常使用
连接微信小程序和Redux


将Redux和微信小程序连接起来才会真的有用处。找了个现成的方案charleyw/wechat-weapp-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(
        thunk
    )
);

let appConfig = {
    onLaunch: function() {},

    onHide: function() {
        let state = store.getState(),
            cacheEntities = {};

        // 体积大于2M,直接清空,防止缓存占用过大体积
        if (sizeof(state.entities)  {
    return {
        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) {
        return
    }

    const state = this.store.getState();
    // 将data也一并传入
    const mappedState = mapState(state, options, this.data);
    if (!this.data || shallowEqual(this.data, mappedState)) {
        return;
    }
    this.setData(mappedState)
}

function onLoad(options) {
    this.store = app.store;
    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 = {
    data: {
        // 其他数据
        id: 0,
        // Toolbar数据,单独的一份数据,便于维护
        toolbarData: Toolbar.defaultData
    },
    // 点赞或者取消赞
    togglePraise: function() {
        let me = this;

        Toolbar.togglePraise.call(me);
    }
}

// 这儿的组件化并不是真正的组件化
// 而是将组件相关的逻辑和函数抽离到单独的文件中,保证Page代码清晰。
// 同时也为这部分组件逻辑复用提供了或许。
// 本质上来说,抽离出的组件全是“操作Page.data的工具函数”,他们也是纯函数,和“操作state的reducer”类似。

这种Redux的组件化解决方案既简单又好用,保持一定的代码规范即可。这样设计当然是为了复用,同时也让Page的逻辑保持极度精简。

开发中遇到了哪些难点 && 微信小程序有多少坑?

微信小程序当前的确算不上公测的版本,开发者工具不完善、真机表现和开发环境差异很大、部分组件性能较差、部分功能有缺陷,只有经历了这些大坑,才会真的知晓你有多“爱”微信小程序。这儿总结了开发中的难点以及微信小程序中遇到的比较明显的坑。

富文本解析

微信小程序并不支持HTML标签,所以对于富文本解析来说,难度较大,而且有些功能还没有办法实现,例如:iframe视频、连接跳转等
这块功能提示由后台统一转换,假如非得前端转换,提示参考下面的思路。

  • 非常感谢 wxParse 这款组件,替我省了不少时间。推荐各位使用,期间遇到一些难题,也分享给各位。
    ? wxParse 默认层级只支持10层html嵌套,假如想要支持更深的层级,在wxParse.xml复制几份template即可。
    ? wxParse 提供了图片加载成功的回调wxParseImgLoad,很好用。但假如富文本中的图片已经预设宽高比,那么可以不用依赖该回调,在html2jons.js中根据屏幕宽度直接计算出图片高度,先占位,可以避免页面频繁抖动的难题。
    ? 假如你的富文本中有自定义模块,对wxParse.xml中的template进行改造即可。


网站设计

自定义模块样式


数据扁平化

具体如何扁平化,请移步上一篇博客 State设计,Redux 开发第一步。
这儿只简单介绍下扁平化应用场景:
好奇心日报的研究所是三级表结构:papers > questions > options,后台返回的数据是三级嵌套数据,假如想要修改option.selected字段,必需三级嵌套循环!假如想要获取所有选中的option,必需三级嵌套循环!

页面展现速度优化

数据复用,例如复用列表页的数据,可以让详情页的标题等字段第一时间呈现出来。
离线缓存,同样可以让列表页和详情页第一时间呈现出来,甚至有或许减少请求数量。

无论是数据复用还是离线缓存,配合数据扁平化,都非常好用。

小程序默认设置代理,会和Shadowsocks等VPN冲突(最新版又坏了)

解决办法很简单,设置微信小程序不使用代理;或者临时关闭VPN即可。
上一版开发者工具已经解决该难题,最新版又坏了。

最新版微信小程序移除了对Promise的支持。

开发者自行引入兼容库即可,推荐es6-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({
                url,
                data,
                method,
                header: {
                    'Content-Type': 'application/json'
                },
                success: function(res) {
                    let { statusCode, errMsg, data } = res;

                    if (statusCode == 200 && data.meta && data.meta.status == 200) {
                        resolve(data.response);
                    } else {
                        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,预计可以修复这个难题,待实际验证。


v2-42211579d3b600b1f2fe4cb1fa997d51_hd.jpg

fullpage效果


swiper组件不支持轮播,性能差,文档模糊(部分最新版已修复

? swiper组件之前并不支持轮播,最新版已修复
? swiper组件之前是通过设置left属性来实现动画,在小米5、华为V8等高端等安卓机上能够感受到明显的卡顿,当然原因是X5内核引起的。最新版已修复,换成了transform,性能有一定的提升。


网站设计

swiper性能提升


? 文档并未标记可以垂直轮播,但本来是可以的。

// 简单设置vertical即可,但由于官方文档并未备注,尽量不要使用。可以自己开发一个swiper组件。

? swiper组件的小圆点本来是可以定制化的,但是官方文档并未说明,而且开发者工具也看不出来,只有鼠标hover到元素上的时候可以看到相关的class,简单猜测一下,最后分析出来它的实现方式。


网站建设

swiper圆点的实现原理


// 圆点的父元素,用来控制圆点间的间距
.wx-swiper-dot {
    width: 30rpx;
    // 圆点,可以通过font-size修改圆点的大小,color修改圆点的颜色
    &:before {
        width: 100%;
        display: inline-block;
        font-size: 56rpx;
        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的方式绘制饼图

            
                        
                    .com-pie {
    position: relative;
    z-index: 0;
    display: inline-block;
    width: 100rpx;
    height: 100rpx;
    line-height: 100rpx;
    border-radius: 50%;
    color: #000;
    background-color: #ebebeb;
    background-image: linear-gradient(to right, transparent 50%, #cccccc 0);
    overflow: hidden;
    .percent-1,
    .percent-2 {
        position: absolute;
        top: 0;
        width: 60%;
        height: 100%;
        left: 50%;
        transform-origin: center left;
    }
    .percent-1 {
        background-color: inherit;
        z-index: -2;
    }
    .percent-2 {
        height: 110%;
        opacity: 0;
        z-index: -1;
        background-color: #cccccc;
    }
    &.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挂掉


开发者工具,切换页面的时候,有时候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间简单的绑定关系即可。

真机上有概率卡死,当前不确定是代码难题还是小程序的难题。

有遇到类似难题的朋友欢迎指出。

总结说点啥?

本文主要围绕微信小程序的基础知识、如何设计微信小程序、开发过程中遇到的难题三个方面介绍。

  • 微信小程序的基础知识主要包括:
    ? 两种配置文件 && 两个核心函数
    ? WXML模板语法,页面渲染
    ? 页面间的跳转
    ? 交互事件
    ? 官方组件和官方API

  • 如何设计微信小程序的博中网投诚信者app内容主要包括:
    ? 构建系统 && 目录结构
    ? 引入Redux进行数据集中管理
    ? 简单的组件化解决方案

最后还介绍开发过程中遇到的难点 以及 微信小程序的大小坑。

微信小程序本身并不复杂,开发过程却比较艰辛,尤其是第一次在真机上运行的时候,觉得这个世界恶意满满。

微信小程序开发有难题也可以咨询重庆网站建设公司重庆博中网投诚信者。

? 本文链接地址:/marketing/4254.html ? 转载请注明出处,谢谢。