一、WX小程序的优势和劣势
1.优势
(1)微信助理,容易推广。在微信中,小程序拥有众多入口,例如附近的小程序、小程序码、分享、发现-小程序等五十多个的入口。这些入口有助于企业更好的获取流量,从而进行转化、变现。
(2)使用便捷。用户在使用小程序时,只需要轻轻点一下就可以使用,更加符合用户对使用方便、快捷的需求,所以小程序的用户数量不断增加。
(3)体验良好,有接近原生app的体验。在微信生态里,小程序在功能和体验上是可以秒杀掉 H5 页面的,H5 页面经常出现卡顿、延时、加载慢、权限不足等原因,而这些问题在小程序里都不会出现。
(4)成本更低,从开发成本到运营推广成本,小程序的花费仅为APP的十分之一,无论是对创业者还是传统商家来说都是一大优势。
2.不足
(1)单个包大小限制为2M,这导致无法开发大型的应用,采用分包最大是20M(这个值一直在变化,以官网为准)。
(2)需要像app一样审核上架,这点相对于H5的发布要麻烦一些。
(3)处处受微信限制。例如不能直接分享到朋友圈,涉及到积分,或者虚拟交易的时候,小程序也是不允许的。
二、WX小程序的项目结构
1.pages:
(1)wxml: 编写小程序界面结构的地方
(2)wxss: 编写小程序样式的地方
(3)json:编写界面配置的地方
(4)js:编写界面逻辑的地方
2.utils: 编写工具类的地方
3.app.js:创建程序实例的位置
4.app.json: 编写全局配置地方
5.app.wxss: 编写全局样式的地方
6.project.config.json: 项目的配置文件
三、小程序中的js和浏览器以及node中的区别
1.浏览器中的js
(1)ES
(2)DOM
(3)BOM
2.node中的js
(1)ES
(2)NPM
(3)Native
3.wx小程序中的js
(1)ES
(2)小程序框架
(3)小程序API
小程序的执行的入口文件是 app.js 。并且会根据其中 require 的模块顺序决定文件的运行顺序。
四、小程序中的数据渲染与浏览器中有什么不同
1.浏览器中渲染时单线程的
2.小程序中的运行环境分为渲染层和逻辑层,WXML和WXSS工作在渲染层,js工作在逻辑层
在页面中展示时通过插值表达式{{ }}
五、简述一下小程序的通讯模型
小程序的渲染层和逻辑层分别由2个线程管理:
渲染层的界面使用了WebView 进行渲染;
逻辑层采用JsCore线程运行JS脚本。
一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发。
六、如何优化小程序的首次加载速度(分包加载)
1.什么是分包加载
某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。首次访问小程序的时候会加载主包的内容,只有在访问到分包界面的时候,才会去加载分包的内容
2.为什么要使用分包
(1)小程序单包大小限制为2M,使用分包最大可以扩展到20M(构建大型APP的时候)
(2)优化首次加载小程序的速度,可以把不需要首次加载的内容放在分包里,可以提升小程序的使用性能(对性能要求比较高的时候)
3.如何使用分包
(1)目录结构
(2)配置(只需要在app.json中配置即可)
{
"pages":[ "pages/index", "pages/logs" ],
"subpackages": [
{
"root": "packageA",
"pages": [ "pages/cat", "pages/dog" ]
},
{
"root": "packageB",
"name": "pack2",
"pages": [ "pages/apple", "pages/banana"]
}
]
}
(3)分包的限制
a.分包之间不能相互引用js
b.主包不能引用分包的js
(4)独立分包
不依赖主包的分包
配置分包的时候加上independent的配置即可声明为独立分包
(5)分包预加载
在路由到某个界面的时候,可以需要加载某个分包的内容,以提升访问这个分包的速度。
在app.json中配置即可
{
"pages": ["pages/index"],
"subpackages": [
{
"root": "important",
"pages": ["index"]
},{
"root": "sub1",
"pages": ["index"],
},{
"name": "hello",
"root": "path/to",
"pages": ["index"]
}, {
"root": "sub3",
"pages": ["index"]
},{
"root": "indep",
"pages": ["index"],
"independent": true
}
],
"preloadRule": {
"pages/index": {
"network": "all",
"packages": ["important"]
},
"sub1/index": {
"packages": ["hello", "sub3"]
},
"sub3/index": {
"packages": ["path/to"]
},
"indep/index": {
"packages": ["__APP__"]
}
}
}
七、配置tabbar的方式及注意事项
1.配置tabbar(需要注意的是,tabbar的图标不能是线上的地址,需要提前准备好放到项目里,一般情况下,这些静态资源只能放在assets文件夹下)
2.写在根目录的app.json文件中,需要注意list中最少要有两个,最多只能有五个
3.list中的每个对象有四个属性
(1)pagePath:对应页面的路径
(2)text:显示的文字
(3)iconPath:未选中时图标的位置路径
(4)selectedIconPath:选中时图标的位置路径
"tabBar": {
"color":"",
"selectedColor": "#f00",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "./assets/shouye.png",
"selectedIconPath": "./assets/shouye1.png"
},
{
"pagePath": "pages/logs/logs",
"text": "log",
"iconPath": "./assets/bianji.png",
"selectedIconPath": "./assets/bianji1.png"
},
{
"pagePath": "pages/home/home",
"text": "我的",
"iconPath": "./assets/grzl.png",
"selectedIconPath": "./assets/grzl1.png"
}
]
}
八、wxml中常用的组件(标签)
view标签是我们开发过程中最常用的标签了,这个就相当于Html中的div。
text标签也是我们开发中常用的,这个相当于Html中的span
image标签相当于我们Html中的img。
九、wxss相比较css的区别
与 CSS 相比,WXSS 扩展的特性有:
1.尺寸单位:rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素
2.样式导入:使用@import
语句可以导入外联样式表,@import
后跟需要导入的外联样式表的相对路径,用;
表示语句结束。
例如:要导入的对象
/** common.wxss **/
.small-p {
padding:5px;
}
导入的位置
/** app.wxss **/
@import "common.wxss";
.middle-p {
padding:15px;
}
内联样式
十、微信小程序中的事件绑定及传参
1.事件定义:在小程序中绑定事件可以以bind开头然后跟上事件的类型,如bindtap绑定一个点击事件,对应的值是一个字符串,需要在page构造器中定义同名函数,每次触发事件之后就会执行对应函数的内容
<view bindtap="handleTap">点击事件</view>
<view bind:tap="handleTap">另一种写法</view>
// pages/my/index.js
Page({
handleTap(){
console.log("执行了点击事件");
}
})
2.阻止事件冒泡:在小程序中除了通过bind之外,还可以通过catch进行事件绑定,通过catch绑定的事件不会触发事件冒泡。
3.事件捕获:事件的触发分为两个阶段,首先是捕获阶段,其次是冒泡阶段。默认情况下事件都是在冒泡阶段触发。如果希望事件可以在捕获阶段触发,可以通过capture-bind进行事件绑定
4.事件传参:在小程序中进行事件传参不能像传统的Web项目中一样,在括号里写参数。在小程序中需要在标签上通过data-方式定义事件所需的参数。
<!-- data-参数名=’参数值’ -->
<view bindtap="handleTap" data-msg="我是事件的参数">点击事件</view>
每个事件回调触发时,都会收到一个事件对象,通过这个对象可以获取路由传递的参数
handleTap(e){
console.log("执行了点击事件");
// 通过currentTarget中的dataset属性可以获取时间参数
console.log(e.currentTarget.dataset.msg);
}
十一、逻辑渲染与列表渲染的方式
1.逻辑渲染
WXML 中,使用 wx:if="{{condition}}" 来判断是否需要渲染该代码块
<view wx:if="{{condition}}"> True </view>
使用 wx:elif 和 wx:else 来添加一个 else 块:
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。如果要一次性判断多个组件标签,可以使用一个 <block/> 标签将多个组件包装起来,并在上边使用 wx:if 控制属性。
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
除此之外微信小程序还可以通过hidden属性进行条件渲染。wx:if在不满足条件的时候会删除掉对应的DOM,hidden属性则是通过display属性设置为none来进行条件渲染。
<view hidden="{{condition}}">
隐藏
</view>
2.列表渲染
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item。
<!-- array 是一个数组 -->
<view wx:for="{{array}}">
{{index}}: {{item.name}}
</view>
使用 wx:for-item 指定数组当前元素的变量名,使用 wx:for-index 指定数组当前下标的变量名。(在嵌套循环时需要自定义)
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.name}}
</view>
注意:使用 wx:key 来指定列表中项目的唯一的标识符
wx:key 的值以两种形式提供:
(1)字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变
(2)保留关键字 this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字
设置key值的好处:当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率
十二、App.js的生命周期以及page中的生命周期、组件的生命周期
1.app.js的生命周期
onLaunch: 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
onShow:当小程序启动,或从后台进入前台显示,会触发 onShow
onHide:当小程序从前台进入后台,会触发 onHide
onError:当小程序发生脚本错误,或者 API 调用失败时,会触发 onError 并带上错误信息。
2.page中的生命周期
onLoad:生命周期函数--监听页面加载,触发时机早于onShow和onReady
onReady:生命周期函数--监听页面初次渲染完成
onShow:生命周期函数--监听页面显示,触发事件早于onReady
onHide:生命周期函数--监听页面隐藏
onUnload:生命周期函数--监听页面卸载
3.组件的生命周期
生命周期 | 参数 | 描述 | 最低版本 |
---|---|---|---|
created | 无 | 在组件实例刚刚被创建时执行 | 1.6.3 |
attached | 无 | 在组件实例进入页面节点树时执行 | 1.6.3 |
ready | 无 | 在组件在视图层布局完成后执行 | 1.6.3 |
moved | 无 | 在组件实例被移动到节点树另一个位置时执行 | 1.6.3 |
detached | 无 | 在组件实例被从页面节点树移除时执行 | 1.6.3 |
error | Object Error | 每当组件方法抛出错误时执行 | 2.4.1 |
十三、用户交互反馈的方式(loading、wx.showToast、wx.showModal)
1.使用button组件的loading属性,在按钮的文字前边出现一个Loading
2.wx.showToast显示提示(一般搭配wx.hideToast使用)
wx.showToast({
title: '已发送',
icon: 'success',
duration: 1500
})
//例如点击显示提示,在请求成功后通过wx.hideToast关闭提示
wx.hideToast()
3.wx.showModal模态对话框
wx.showModal({
title: '标题',
content: '告知当前状态,信息和解决方法',
confirmText: '主操作',
cancelText: '次要操作',
success: function(res) {
if (res.confirm) {
console.log('用户点击主操作')
} else if (res.cancel) {
console.log('用户点击次要操作')
}
}
})
十四、微信小程序中本地存储的方式
(一)同步
1.存储:wx.setStorageSync('list', {age:5})
2.获取:wx.getStorageSync('list')
//本地同步缓存
syncSet(){
console.log('这是同步缓存');
wx.setStorageSync('sync', {content:'这是同步缓存'})
},
//本地同步获取
syncGet(){
console.log(wx.getStorageSync('sync'));
},
(二)异步
1.存储:wx.setStorage({ })
2.获取:wx.getStorage({ })
//本地异步存储
asyncSet(){
wx.setStorage({
key:'async',
data:'这是异步存储的数据',
success(){
console.log('异步存储');
}
})
},
//本地异步获取
asyncGet(){
wx.getStorage({
key:'async',
success(res){
console.log(res);
}
})
},
十五、小程序间页面的跳转
1.wx.navigateTo
保留当前页面,只能打开非 tabBar 页面,返回时返回该页面
wx.navigateTo({
url: '路径地址',
})
2.wx.redirectTo
关闭卸载当前页面,只能打开非 tabBar 页面,
wx.redirectTo({
url: '路径地址'
})
3.wx.switchTab
关闭所有非tabbar页面, 只能打开 tabBar 页面
wx.switchTab({
url: '路径地址'
})
4.wx.reLaunch
关闭卸载所有页面,可以打开任意页面
wx.reLaunch({
url: '路径地址'
})
5.wx.navigateBack
返回前面的页面,可以指定返回多少页,如果用过redirectTo,那么被关闭的页面将返回不去
wx.navigateBack({
delta: 2 //返回的页面数,如果 delta 大于现有页面数,则返回到首页。
})
6.navigator标签
navigator标签中加的url地址可以跳转到非tabBar页面
若要跳转到tabBar页面可以增加一个open-type='switchTab'则可以跳转到tabBar页面,实质相当于wx.switchTab函数
十六、小程序页面的传参
1.路由跳转传参
路由跳转传参可以通过?的方式拼接参数。
wx.switchTab({
url: '../todolist/todolist?id=789',
})
//或者navigator标签
<navigator url="../detail/detail?id=666">带参数去detail</navigator>
跳转到指定界面之后,可以在该页面的onLoad方法中的options参数(本身是个对象)拿到路由跳转的参数。
onLoad(options) {
console.log(options);
},
2.若要传递标签中的数据参数,可以自定义属性,在对应事件中通过e来获取该参数,再拼接到事件跳转的路径中
//wxml中
<button bindtap="navigateTo" data-num="10">wx.navigateTo</button>
//js中
navigateTo(e){
console.log(e.target.dataset.num);
wx.navigateTo({
url: '/pages/detail/detail?id=999&num='+e.target.dataset.num,
})
},
十七、wx.request的使用方式以及封装
1.http的使用 (wx.request)
url 开发者服务器接口地址。注意这里需要配置域名(注意项目配置、域名信息需要更改,去网页小程序开发者管理,修改,多个之间通过封号(;)分割)
data 请求的参数
header 设置请求的 header,header 中不能设置 Referer,默认header['content-type'] = 'application/json'
method(需大写)有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
dataType json 回包的内容格式,如果设为json,会尝试对返回的数据做一次 JSON解析
success 收到开发者服务成功返回的回调函数。
fail 接口调用失败的回调函数
complete 接口调用结束的回调函数(调用成功、失败都会执行)
wx.request({
url: 'https://showme.myhope365.com/api/cms/article/open/list',
method: "POST",
data: {
pageNum: 1,
pageSize: 10
},
header: {
"content-type": "application/x-www-form-urlencoded"
},
success: res => {
console.log(res.data.rows)
}
})
2.http请求的封装
作用:
- 添加统一的请求配置
- 可以添加请求拦截器和响应拦截器,在请求和响应之前加一些通用的处理。
function request(options) {
// 请求拦截器
// ...
// 1. 加一些统一的参数,或者配置
if (!options.url.startsWith("https://") && !options.url.startsWith("http://")) {
options.url = "https://showme.myhope365.com" + options.url
}
// 默认的请求头
let header = {
"content-type": "application/x-www-form-urlencoded",
};
if (options.header) {
header = {
...header,
...options.header
}
}
return new Promise((reslove, reject) => {
// 调用接口
wx.request({
// 默认的配置
// 加载传入的配置
...options,
header,
success(res) {
// 响应拦截器,所有接口获取数据之前,都会先执行这里
// 1. 统一的错误处理
if (res.statusCode != 200) {
wx.showToast({
title: '服务器异常,请联系管理员',
})
}
reslove(res)
},
fail(err) {
reject(err)
}
})
})
}
export function get(url, options = {}) {
return request({
url,
...options
})
}
export function post(url, data, options = {}) {
return request({
url,
data,
method: "POST",
...options
})
}
十八、小程序中组件以及插槽的使用
(一)组件的使用
1.新建components文件夹
2. 新建文件夹生成component
Component({
/*** 组件的属性列表
*/
properties: {
},/**
* 组件的初始数据
*/
data: {
},/**
* 组件的方法列表
*/
methods: {
}})
3.在要使用的文件的json文件中加入
{
"usingComponents": {
"组件名":"组件路径"
}
}
(二)插槽的使用
(1)在组件内直接写的文本会放到默认插槽中
(2)默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。
Component({
options: {
multipleSlots: true // 在组件定义时的选项中启用多 slot 支持
},
properties: { /* ... */ },
methods: { /* ... */ }
})
//组件中
<slot name="before"></slot>
<view>这里是组件的内部细节</view>
<slot name="after"></slot>
//使用的地方
<son1>这是放到默认插槽的文本
<!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 -->
<view slot="before">这里是插入到组件slot name="before"中的内容</view>
<!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 -->
<view slot="after">这里是插入到组件slot name="after"中的内容</view>
</son1>
十九、父子组件传参的方式
1.父传子:在父组件中使用的子组件标签上加自定义属性,在子组件中通过properties去接受,然后就可以再子组件中使用
//父组件
<son1 canshu='kaixuan'>这是放到默认插槽的文本
<!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 -->
<view slot="before">这里是插入到组件slot name="before"中的内容</view>
<!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 -->
<view slot="after">这里是插入到组件slot name="after"中的内容</view>
</son1>
//子组件js
properties: {
canshu:{
type:[Number,String],
value:'默认值'
}
},
//子组件wxml
<view style="color: red;"> 父传子 {{canshu}}</view>
2.子传父:给子组件绑定一个方法,把给方法写到子组件js的methods中,通过this.triggerEvent来传递参数,再在父组件中用bind:给子组件标签绑定一个自定义事件,事件类型与子组件中的保持一致,事件名随便写,然后在父组件的对应事件中即可拿到并改变父组件的值从而使用
(1)给子组件绑定一个方法,把给方法写到子组件js的methods中,通过this.triggerEvent来传递参数
methods: {
//子传父
toFather(){
this.triggerEvent('myevent','子传父的数据')
},
}
(2)再在父组件中用bind:给子组件标签绑定一个自定义事件,事件类型与子组件中的保持一致,事件名随便写
<son1 bind:myevent='myevent'>这是放到默认插槽的文本
<!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 -->
<view slot="before">这里是插入到组件slot name="before"中的内容</view>
<!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 -->
<view slot="after">这里是插入到组件slot name="after"中的内容</view>
</son1>
(3)然后在父组件的对应事件中即可拿到并改变父组件的值从而使用
//获取子传父的值
myevent(res){
console.log(res);
this.setData({
fromSon:res.detail
})
},
// 在父组件中使用
<view>{{fromSon}}</view>
二十、Behavior的使用方式
Behaviors 注册一个 behavior,接受一个 Object 类型的参数
module.exports = Behavior({
created(){
console.log(1111);
},
})
在要使用的组件的js中引入
import Behavior from "../../behavior/behavior"
Component({
behaviors:[Behavior],
...
})
注意,生命周期都会走,但是如果混入和组件内的数据重名,下面的会覆盖上面的
同名字段的覆盖和组合规则
组件和它引用的 behavior
中可以包含同名的字段,对这些字段的处理方法如下:
- 如果有同名的属性 (properties) 或方法 (methods):
- 若组件本身有这个属性或方法,则组件的属性或方法会覆盖
behavior
中的同名属性或方法; - 若组件本身无这个属性或方法,则在组件的
behaviors
字段中定义靠后的behavior
的属性或方法会覆盖靠前的同名属性或方法; - 在 2 的基础上,若存在嵌套引用
behavior
的情况,则规则为:引用者 behavior
覆盖被引用的 behavior
中的同名属性或方法。
- 若组件本身有这个属性或方法,则组件的属性或方法会覆盖
- 如果有同名的数据字段 (data):
- 若同名的数据字段都是对象类型,会进行对象合并;
- 其余情况会进行数据覆盖,覆盖规则为:
引用者 behavior
>被引用的 behavior
、靠后的 behavior
>靠前的 behavior
。(优先级高的覆盖优先级低的,最大的为优先级最高)
- 生命周期函数和 observers 不会相互覆盖,而是在对应触发时机被逐个调用:
- 对于不同的生命周期函数之间,遵循组件生命周期函数的执行顺序;
- 对于同种生命周期函数和同字段 observers ,遵循如下规则:
behavior
优先于组件执行;被引用的 behavior
优先于引用者 behavior
执行;靠前的 behavior
优先于靠后的 behavior
执行;
- 如果同一个
behavior
被一个组件多次引用,它定义的生命周期函数和 observers 不会重复执行。
二十一、WebSocket的使用
详情见我单独的另一篇文章连接如下: