参考资料:
一、小程序框架wepy
常用框架:原生小程序、wepy(vue)、uniapp(vue)、taro(react)
- 考虑到原声api变更后,框架未及时变更情况,可使用原生小程序框架。
- 跨端和跨平台需求,建议使用小程序框架
wepy借鉴了vue语法功能,支持vue书写特征 - vue技术友好行
1、生命周期
wepy生命周期同原生小程序
-
应用周期
- onLaunch 首次打开
- onShow 初始化完成
- onHide 切换
-
页面周期
- onLoad 加载页面
- onShow 前后台切换
- onHide 前后台切换
- onUnLoad 重定向 / 路由切换
- onPullDownRefresh 下拉
- onReachBottom 上拉
- onShareAppMessage 分享
- ……
2、数据层-数据绑定
this.setData({label: 'label'}) //原生小程序
this.label = 'label1' //wepy
复制代码
- 如何做到监听数据改变,多次setData时候,通信次数是一次还是几次
- 在一次渲染周期内,收到多次setData的话,只会渲染一次
- jscore -> native ->web view
- 如何优化小程序数据通信,提升页面性能
- 减少setData的调用,合并多个setData
- 与界面渲染无关的数据最好不要设置在data
- 有些数据不在页面中展示,包含复杂数据结构或者超长字符串,则不应该使用setData来设置这些数据
- wepy如何做数据绑定优化
- wepy内部实现了一个脏数据检查机制,函数执行完成之后 -> data-check
- newValue 和 oldValue做比较,如果有变化,就会加入到readyToSet的队列中,最后统一做一个setData
- 同一时间只允许一个脏值检查流程进行
二、双重登录态
1、三种状态
访客态、游客态、会员态
- 访客态:未授权用户信息,未静默登录
- 游客态:授权用户信息,进行静默登录
- 会员态:授权手机号或使用账号密码登录,使用手机号进行账号关联
2、静默登录
1、登录流程
利用小程序登录机制,实现静默登录,对客户无感。
- 小程序:调用
wx.login()
获取临时登录凭证code
,并回传到开发者服务器。- code有效率期5min,短时间内多次获取code值不变,有缓存
- 小程序:使用wx.request调用开发者服务器接口,上送code值。
- 开发者服务器:调用
auth.code2Session
接口进行登录请求,获取用户唯一标识 OpenID
、 用户在微信开放平台帐号下的唯一标识UnionID
(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥session_key
。- 登录凭证校验接口:
https://api.weixin.qq.com/sns/jscode2session
- 上送:code + appid + appSecret
- 接收:openid + session_key + unionid
- 会话密钥
session_key
是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥
,code登录后会失效。
- 登录凭证校验接口:
- 开发者服务器:使用openid + unionid + session_key建立登录态,使用token鉴权或session鉴权,返回
token或者sessionid
。 - 小程序:将鉴权信息token或sessionid存入storage中,后续调用接口时携带鉴权信息。
- 开发者服务器:根据上送鉴权信息,校验成功后,回传数据。
2、api解析
- openid和unionid
- openid
- 客户在某一应用下的唯一标识:
同一平台,不同应用,编号不同
- 通过后台调用auth.code2Session登录凭证校验接口获取
- 客户在某一应用下的唯一标识:
- unionid
- 客户在同一个微信开放平台下的唯一标识:
同一平台,不同应用,编号相同
- 当前小程序已绑定到微信开放平台帐号可通过后台调用auth.code2Session接口获取
- 前期通过getUseInfo获取,目前可通过wx.login获取,接口隔离
- 客户在同一个微信开放平台下的唯一标识:
- openid
- wx.getUserInfo
- 存量:获取用户信息,获取unionid
- 现状:匿名头像昵称、默认性别地区、加密后的身份认证
- wx.getUserProfile
- 弹出个人信息授权弹框
- 成功后获取用户信息、调用失败
wx.getUserProfile({
desc: 'desc',
success: res => {
console.log('wx.userProfile', res)
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo
})
}
})
复制代码
- wx.login
- 获取code
- 获取unionid
wx.login({
success: res => {
console.log('wx.login', res)
const _code = res.code
app.globalData.code = _code
this.setData({
code: _code
})
}
})
复制代码
3、超时处理
- 小程序到开发服务器认证超时:校验sessionkey,重新进行登录或重新发起请求
- 开发者服务器到wx服务器认证超时:重新发起请求
- 小程序到开发者服务器登录后交易超时:重新发送请求即可
3、用户登录
操作具体业务,需要会员及用户体系时,添加用户登录流程,弹出登录弹框,通常使用微信一键登录或账号密码登录两种方式,一般使用手机号进行用户体系关联。
- 微信一键登录:提示用户授权手机号使用,getPhoneNumber
- 授权使用手机号,则用手机号进行账号关联绑定,进行登录操作
- 未授权使用手机号,登录失败,跳转至登录弹框
- 账号密码登录:使用账号和密码认证登录
- 登录成功后,进行账号关联绑定
- 登录失败返回登录弹框
4、用户体系
账号saas系统中,同一账号关联:账号体系、套餐、动态权限等,同一账号查找不同属性。
三、云开发
参考资料:
1、基础能力
- 云函数:在云端运行的代码,微信私有的天然鉴权,开发者只需编写自身业务逻辑代码,相当于后端的NodeJs服务。
- 作用:无需搭建服务器(省去了运维)。
- 具体应用
- 获取appId
- 获取openId
- 生成分享图
- 调用腾讯云SDK
- ...
- 云数据库: 一个可以在小程序端操作,也能够在云函数中读写的json数据库。
- 作用:无需自己建数据库。
- 具体应用:数据的增加,删除,修改,查询。
- 云存储: 可在小程序前端直接上传或下载云端文件,在云开发控制台可视化管理。
- 作用:无需自建存储和 CDN。
- 具体应用
- 管理文件
- 上传文件
- 下载文件
- 分享文件
- ...
- 云调用:基于云函数免鉴权使用小程序开放接口的能力,包括服务端调用、获取开放数据等能力。
- 作用: 原生微信服务集成。
- 具体应用
- 服务器端调用,在云函数中使用云调用,调用服务器接口无需换取 access_token
- 开放数据调用,对于返回一些敏感信息,例如数字签名秘钥,会话秘钥等
- 模板消息推送
2、小程序云开发
1、设置云函数的路径
在项目根目录找到 project.config.json
文件,新增 cloudfunctionRoot
字段,指定本地已存在的目录作为云函数的本地根目录(在根目录下手动创建一个cloudfunction的文件夹,然后在project.config.json中进行配置一下),这个目录相当于后端Nodejs,它是可以通过终端npm安装一些第三方模块的。
{
cloudfunctionRoot: './cloudfunction'
}
复制代码
2、初始化云环境
微信小程序的app.js中onLaunch生命周期中初始化云开发
onLaunch: function () {
//调用API从本地缓存中获取数据
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
if (!wx.cloud) {
console.error('当前环境不支持云能力,请升级')
} else {
// 云能力的初始化
wx.cloud.init({
/**
* env 参数说明:
* env 参数决定接下来小程序发起的云开发调用(wx.cloud.xx)会默认请求到哪个云环境的资源; 此处请填写环境ID,环境ID可打开云控制台查看
*/
traceUser: true
})
}
this.globalData = {}
},
复制代码
3、引用云数据库
// 引用云数据库
const db = wx.cloud.database()
function getData(db_name) {
const _userInfo = getApp().globalData.userInfo || {}
const _token = _userInfo.token;
return new Promise((resolve, reject) => {
// 查询当前环境,获取表
db.collection(db_name).where({
_openid: 'user_openId'
}).get({
success: res => {
console.log('[数据库][查询]:成功', res)
//可以一次性获取多条记录。通过调用集合上的 `where` 方法可以指定查询条件
const data = res.data[0];
console.log('find result', data)
resolve(data)
},
fail: err => {
wx.showToast({
title: '查询失败',
})
reject(err)
}
})
})
}
复制代码
- 数据渲染
getData: function(){
// 云化
api.getData('list').then(feed => {
const feed_data = feed.data
console.log('getListData', feed_data)
this.setData({
feed:feed_data,
feed_length: feed_data.length
});
})
}
复制代码
四、webpack工程化
参考资料:
1、 安装项目依赖包
- npm install --save-dev babel-core babel-loader babel-plugin-lodash babel-plugin-transform-runtime 安装babel,babel的作用是将es6的语法编译成浏览器认识的语法es5
- npm install --save-dev file-loader 用于打包文件
- npm install --save-dev sass-loader node-sass 用于编译sass
- npm install webpack webpack-cli clean-webpack-plugin copy-webpack-plugin 用于webpack.config.babel.js配置
- npm install --save lodash 一个一致性、模块化、高性能的 JavaScript 实用工具库
"dependencies": {
"lodash": "^4.17.21",
"moment": "^2.29.1"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/preset-env": "^7.16.11",
"@tinajs/mina-runtime-webpack-plugin": "^1.3.7",
"babel-loader": "^8.2.3",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^6.0.0",
"webpack": "^4.46.0",
"webpack-cli": "^4.9.2"
},
复制代码
2、新建webpack.config.js
用于编译打包小程序
const { resolve } = require("path")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const MinaRuntimePlugin = require("@tinajs/mina-runtime-webpack-plugin")
const webpack = require("webpack")
const debuggable = process.env.BUILD_TYPE !== "release"
module.exports = {
context: resolve("src"),
entry: {
"app": "./app.js",
"pages/index/index": "./pages/index/index.js",
"pages/logs/logs": "./pages/logs/logs.js"
},
output: {
path: resolve("dist"),
filename: "[name].js",
globalObject: "wx"
},
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader"
}
]
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: JSON.stringify(process.env.NODE_ENV) || "none",
BUILD_TYPE: JSON.stringify(process.env.BUILD_TYPE) || "debug"
}),
new CleanWebpackPlugin({
cleanStaleWebpackAssets: false
}),
new CopyWebpackPlugin({
patterns: [{
from: "**/*",
to: "./",
globOptions: {
ignore: ["**/*.js"],
}
}]
}),
new MinaRuntimePlugin({
// scriptExtenstions: [".js"],
// assetExtensions: [".less"]
})
],
optimization: {
splitChunks: {
chunks: "all",
name: "common",
minChunks: 2,
minSize: 0
},
runtimeChunk: {
name: "runtime"
}
},
mode: debuggable ? "none" : "production"
}
复制代码
3、在package.json文件中加入指令
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"serve": "webpack --watch --progress",
"build": "cross-env NODE_ENV=prodution BUILD_TYPE=release webpack"
},
复制代码
4、新建 .babelrc 文件,es语法转换
{
"presets": ["@babel/env"]
}
复制代码
五、微信小程序问题汇总
- 自定义tabbar在页面存在下拉更新(scrollview)的时候,页面被下拉,tabbar也会跟着下拉。
- 提前沟通,修改为原生tabbar
- require在小程序中不支持绝对路径,只能用相对路径去选取'../../../utils/tool.js'
//app.js
App({
require: function($uri) {
return require($url);
}
})
//comp.js
const Api = app.require('utils/tool.js'); //利用require返回uri带上/
复制代码
- 组件引用资源路径不能解析特殊字符或汉字
- 规范文件命名
- {{}}模板中不能执行特殊方法,只能处理简单的四则运算
//期望:'34万元'
const money = 345678;
<view>{{ money }}</view>
复制代码
- 方案 利用wxs的format vue wxs 实现format
const fnToFixed = function(num) {
return num.toFixed(2);
}
module.exports = {
fnToFixed
}
<wxs src='../../../xxx.wxs' module="filters">
<view>{{ filters.fnToFixed(money) }}</view>
复制代码
- wxs无法使用new Date()
- 方案: 使用getDate()方法
- setData过程中需要注意对象覆盖
data: {
a: '1',
b: {
c: 2,
d: 3
}
}
//会覆盖原对象b
this.setData({
b: {
c: 4
}
});
//解决办法1
const { b } = this.data;
b.c = 4;
this.setData({ b });
//解决办法2: wx-update-data库
复制代码
- IOS的date不支持2020-06-26格式,必须要转成2020/06/26
- wx接口不promise,可使用wx-promise-pro库。
六、相关面试题
-
小程序优势:无需下载安装,直接使用,运行速度快
-
小程序页面构成:index.js(js交互逻辑)\index.json(页面配置)\index.wxml(html)\index.wxss(样式)
-
为何部分npm包在小程序中无法直接使用?小程序开发中遇到过执行环境问题吗?
- 因为小程序的执行环境中,包解析编译时注入了上下文和全局环境
- 如何解决:通用方式直接克隆源码,手动编译;使用工程化webpack
-
小程序与H5的区别:
- 运行环境:H5的宿主环境是浏览器,小程序基于浏览器内核重构的内置解析器,无dom、bom对象
- 系统权限:小程序能获得更多的系统权限
- 渲染机制:小程序逻辑层和渲染层是分开的,而H5页面UI渲染和js执行都是一个线程,会出现阻塞。
-
小程序生命周期:
-
应用的生命周期
- 首次打开小程序,触发onLaunch
- 初始化完成,触发onShow方法,监听小程序显示
- 从后台进入前台,触发onShow方法
- 从前台进入后天,触发onHide方法
- 小程序后台运行一定时间,或系统资源占用过高,会被销毁
-
页面的生命周期
- 小程序注册完成,加载页面,触发onLoad方法
- 页面载入后触发onShow方法
- 首次显示页面,触发onReady方法,渲染页面元素和样式,一个页面只会调用一次
- 小程序由前台进入后台运行或跳转其他页面时,触发onHide方法
- 小程序由后台到前台或重新进入页面时,触发onShow方法
- 当使用重定向方法wx.redirectTo或关闭当前页面返回上一页wx.navigateBack(),触发onUnload 事件
-
-
小程序的双线程架构
- 小程序的渲染层和逻辑层分别由2个线程管理:
- 渲染层:页面渲染相关的任务全部放在webview线程里执行,一个小程序存在多个界面,所以渲染层存在多个webview线程
- 逻辑层:采用jsCore线程运行js脚本
- 视图层和逻辑层通过系统层的WeixinJsBridge进行通信:逻辑层把数据变化通知到视图层,触发视图层面的更新,视图层把触发的事件发送给逻辑层,进行业务处理。
- 页面渲染的具体流程是:在渲染层,宿主环境会吧wxml解析程js对象,在逻辑层发生数据变更的时候,我们通过宿主环境提供的setData方法把数据从逻辑层传到渲染层,再经过对比前后的差异,把差异应用在原来的dom树上,渲染出正确的ui页面。
-
小程序运行机制
- 热启动:用户已经打开某小程序,然后在一定时间内再次打开该小程序,此时无需重启启动,只需将后台的小程序切换到前台
- 冷启动:用户首次打开小程序或被微信主动销毁后再次打开,小程序重新加载启动。
- 销毁:只有当小程序进入后天一定时间,或者系统占用资源过高,才会被真正销毁。