- 发布时间
编译系统结构
- 作者

- Name
- wenjuGao
- github
- @Github
小程序
小程序发展史
小程序与普通网页开发的区别
以下内容引用微信小程序文档【小程序简介 > 小程序与普通网页开发的区别】
网页开发渲染线程和脚本线程是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应,而在小程序中,二者是分开的,分别运行在不同的线程中。网页开发者可以使用到各种浏览器暴露出来的 DOM API,进行 DOM 选中和操作。而如上文所述,小程序的逻辑层和渲染层是分开的,逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的 DOM API 和 BOM API。这一区别导致了前端开发非常熟悉的一些库,例如 jQuery、 Zepto 等,在小程序中是无法运行的。同时 JSCore 的环境同 NodeJS 环境也是不尽相同,所以一些 NPM 的包在小程序中也是无法运行的。
网页开发者需要面对的环境是各式各样的浏览器,PC 端需要面对 IE、Chrome、QQ 浏览器等,在移动端需要面对 Safari、Chrome 以及 iOS、Android 系统中的各式 WebView 。而小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具,小程序中三大运行环境也是有所区别的,如下表所示。
| 运行环境 | 逻辑层 | 渲染层 |
|---|---|---|
| iOS | javascriptCore | WKWebView |
| 安卓 | V8 | chromium 定制内核 |
| 小程序开发者工具 | NWJS | Chrome WebView |
开发的准备流程


申请帐号
- 邮箱注册
- 主体类型: 个人、企业、企业(个体工商户)、政府、媒体、其他组织
- 除个体工商户类型可认证 5 个小程序外,其他类型一个主体可认证 50 个小程序
基础语法
配置
{
"pages": [ // 页面路径列表
"pages/index/index",
],
"entryPagePath": "pages/index/index", // 小程序默认启动首页
"window": { // 全局的默认窗口表现
"backgroundColor": "#F6F6F6",
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#F6F6F6",
"navigationBarTitleText": "前端小书童",
"navigationBarTextStyle": "black"
},
"tabBar": { // 底部 tab 栏的表现
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/logs/logs",
"text": "我的"
}
]
},
"sitemapLocation": "sitemap.json",
"style": "v2",
"lazyCodeLoading": "requiredComponents",
"networkTimeout": { // 网络超时时间
"request": 10000,
"downloadFile": 10000
},
"debug": true, // 是否开启 debug 模式,默认关闭
"functionalPages": false, // 是否启用插件功能页,默认关闭
"requiredBackgroundModes": ["audio", "location"] // 申明需要后台运行的能力,类型为数组 audio: 后台音乐播放 location: 后台定位
"plugins":{}, // 使用到的插件
"resizable": true
}
{
"navigationBarBackgroundColor": "#ffffff", // 导航栏背景颜色
"navigationBarTextStyle": "black", // 导航栏标题颜色
"navigationBarTitleText": "前端小书童", // 导航栏标题文字内容
"navigationStyle": "default", // 导航栏样式,仅支持以下值:default 默认样式;custom 自定义导航栏,只保留右上角胶囊按钮
"homeButton": true, // 在非首页、非页面栈最底层页面或非 tabbar 内页面中的导航栏展示 home 键
"backgroundColor": "#eeeeee", // 窗口的背景色
"backgroundTextStyle": "light", // 下拉 loading 的样式,仅支持 dark / light
"enablePullDownRefresh": true, // 是否开启当前页面下拉刷新
}
WXML(模板)
WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。
对比 Vue 模板语法
- WXML 数据绑定
<view> {{message}} </view>
- Vue 数据绑定
<div>{{message}}</div>
:::tip WXML 不支持 html 标签(div、span 等),对于使用 view、block、text :::
- WXML 循环渲染
<view wx:for="{{array}}" wx:for-index="index" wx:for-item="item" wx:key="index">
{{index}}: {{item.message}}
</view>
- Vue 循环渲染
<div v-for="(item,index) in array" :key="index">
{{index}}: {{item.message}}
</div>
- WXML 条件渲染
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
- Vue 条件渲染
<div v-if="length > 5">
1
</div>
<div v-if="length > 2">
2
</div>
<div v-else>3</div>
组件(模板)-引用
定义组件(模板)
<!-- child.wxml -->
<template name="child">
<text>{{text}}</text>
</template>
在另外一个模板文件中引用该组件(模板)
<!-- parent.wxml -->
<import src="child.wxml"/>
<template is="child" data="{{text: 'forbar'}}"/>
:::tip 在开发中组件的引入注册使用在页面的 json 文件中通过 usingComponents 引入会更易于维护 :::
WXS(脚本)
WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。
WXS 与 javascript 是不同的语言,有自己的语法,并不和 javascript 一致。
通过以下方式引入 WXML 文件中
<wxs src="./demo.wxs" module="demoWxs"></wxs>
微信小程序全局事件订阅
在Vue开发中,我们可能用过eventBus来解决全局范围内的事件订阅及触发逻辑,在微信小程序的开发中我们可能也也会遇到同样的需求,那么我们尝试下在小程序(原生小程序开发)中实现类似eventBus的事件订阅功能。
全局事件订阅
- 全局实例 在Vue中我们有new Vue得到的全局对象,小程序中对应的则是app对象,在小程序组件或者页面中可以通过getApp()获取;
- 事件订阅 声明对象存储事件,示例中使用map存储eventMap,向存储器中存放需要被触发的事件
// 注意 开发阶段热跟新时,eventMap的声明和触发可能存在异步问题,需要阻断eventMap声明在触发之后的情况,这个问题仅限开发阶段存在
on(action, event) {
if (eventMap && !eventMap.has(action)) {
eventMap.set(action, event)
}
}
- 事件触发 当业务逻辑需要触发时,调用emit触发指定事件
emit(action, arg) {
if (eventMap && eventMap.has(action)) {
eventMap.get(action) && eventMap.get(action)(arg)
}
}
- 事件卸载 当订阅的事件过多或者确定事件不在被触发时,及时卸载事件可以减少内存压力
off(action) {
if (eventMap && eventMap.has(action)) {
eventMap.delete(action)
}
}
整体代码如下(文件:app.js):
const eventMap = new Map()
App({
globalData: {
count: 1,
},
// 事件订阅
on(action, event) {
if (eventMap && !eventMap.has(action)) {
eventMap.set(action, event)
}
},
// 事件卸载
off(action) {
if (eventMap && eventMap.has(action)) {
eventMap.delete(action)
}
},
// 事件触发
emit(action, arg) {
if (eventMap && eventMap.has(action)) {
eventMap.get(action) && eventMap.get(action)(arg)
}
},
})
页面或者组件中使用
- 订阅on,订阅自定义事件countAdd(自定义事件名),并且传入事件被触发后需要被触发的逻辑,这里的changeCount就是在事件被触发是订阅触发的数据,当然触发事件的参数可以来自emit也可以无参数
const app = getApp()
Page({
data: {
count: app.globalData.count,
},
created() {
// 注册事件
app.on('countAdd', this.changeCount.bind(this))
},
changeCount(count) {
this.setData({
count,
})
},
})
- 发布emit,发布自定义事件countAdd(自定义事件名)来触发所有监听该事件的订阅者(既注册了on的组件或者页面),emit携带的参数也会被传递给自定义事件
const app = getApp()
Component({
data: {
count: app.globalData.count,
},
// 触发事件
bindEvent() {
app.emit('countAdd', this.data.count++)
},
})
这里changeCount是最终被触发的事件,countAdd是在订阅服务中自定义的事件名,之所以不使用相同的事件名,主要是区分下。
整体事件触发逻辑如下
- 先订阅事件 changeCount
- 业务需要触发的时候触发bindEvent
- emit到全局来调用监听的事件