文章目录

  • 微信原生小程序基础架构
  • 一、基本目录结构
  • 二、登录鉴权部分
  • 三、位置授权部分
  • 四、版本升级部分
  • 五、工具部分
  • MD5加密
  • 六、系统信息部分
  • 七、缓存处理
  • 八、CSS兼容问题处理与公共样式
  • 九、数据清洗与容错
  • 十、动态组件的渲染
  • component方式
  • template


微信原生小程序基础架构

项目介绍

做了哪些事

一、基本目录结构

├─assets
│  ├─images
│  └─img
├─component 公共组件
│  ├─footer
│  └─slot
│      └─pop-center
├─config 配置文件
│  ├─config 存放基础配置
│  └─constants 存储常量
├─pages
│  ├─index 首页
│  ├─login 登录
│  ├─member 权益
│  ├─my 我的
│  └─store 门店
├─serve 请求
│  ├─home 模块api
│  └─user 模块api
├─style 公共样式
└─utils 工具类

二、登录鉴权部分

  1. wx.login获取code换取openid
login() {
	// 登录
	return new Promise((resolve, reject) => {
		wx.login({
			success: (res) => {
				// 发送 res.code 到后台换取 openId
				this.store.setStorage({
					key: 'code',
					value: res.code,
				})
				// 获取openId
				Api.GetOpenId({ code: res.code })
					.then((res) => {
						let _openId = null
						if (res.result) {
							if (res.data) {
								_openId = res.data
								this.store.setStorage({
									key: 'openId',
									value: _openId,
								})
							}
						}
						resolve(res)
					})
					.catch((err) => {
						reject(res)
					})
			},
		})
	})
},

// 服务端通过auth.code2Session API 获取openid/session_key/unionid等信息
  1. 验证是否已经登录,并返回用户信息

基于系统是C端且是永久登录的,当用户信息已经缓存直接读缓存,否则读接口

validateLogin() {
	let _openId = this.store.getStorage('openId') || ''
	let _token = this.store.getStorage('token') || ''
	let _userInfo = this.store.getStorage('userInfo') || ''
	if (_token && _userInfo) {
		return new Promise((resolve, reject) => {
		    // 读缓存时,返回的用户信息模拟接口返回的数据结构
			let result = {
				result: true,
				data: {
					userToken: _token,
					user: _userInfo,
				},
			}
			resolve(result)
		})
	} else {
		return new Promise((resolve, reject) => {
			Api.GetValidateLogin({ openId: _openId })
				.then((res) => {
					if (res.result) {
						this.store.setStorage({
							key: 'token',
							value: res.data.userToken,
						})
						this.store.setStorage({
							key: 'userInfo',
							value: res.data,
						})
					}
					resolve(res)
				})
				.catch((err) => {
					reject(err)
				})
		})
	}
},
  1. 基于业务,决定在app.js里获取openid还是在页面入口获取openid,

基于我们业务是入口免登录的,可以直接先在app.js的onLaunch获取openid,然后在需要进行验证的地方再验证是否登录

// app.js
onLaunch({ path, scene, query, shareTicket }) {
    this.login().then((res) => {
    	if (!res.result) {
    		utils.showToast(res.errorMessage)
    	}
    })
}

// 在需要判断是否登录的页面 

// onLoad里或函数内部
app.validateLogin().then((res) => { //...处理登录成功和失败事件 })

如业务需要在入口页就要判断是否登录

// 入口页 如index.js
const app = getApp()
onLoad(options) {
    app.login().then((res) => {
    	if (res.result) {
    		app.validateLogin().then((res) => {//...处理登录成功和失败事件})
    	}
    })
}
  1. 获取用户个人信息

获取用户个人信息可以直接调用Api唤起授权弹窗

// 获取用户个人信息
  // 1. 一键登录
  clickLogin(e) {
    wx.getUserProfile({
      desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        this.setData({
          userInfo: res.userInfo,
          isOpen: true, // 获取用户手机号需要使用button组件,所以需要打开个弹窗
        });
      },
      fail: (err) => {
        console.log(err);
      },
    });
  },
  1. 获取用户手机号

获取用户手机号需要使用button组件,因此加了个弹窗

<button style="width: 520rpx; padding: 0; margin: 65rpx auto 36rpx auto;" class="authorize" open-type="getPhoneNumber"  bindgetphonenumber="getPhoneNumber" bindtap="clickCloseMask">授权登录开启美发之旅</button>
// 2. 获取手机号
 getPhoneNumber(e) {
    let _phoneInfo = e.detail;
    if (_phoneInfo.errMsg == "getPhoneNumber:ok") {
        this.setData(
        {
            isOpen: false,
            phoneInfo: e.detail,
        },
        () => {
            this.postLogin();
        }
      );
    }
  },
  
  
// 3. 暂不授权-关闭弹窗
clickCloseMask() {
    this.setData({
        isOpen: false,
    });
},

// 4. 登录
postLogin() {
    // ...调用登录接口
}

三、位置授权部分

入口页调起位置授权弹窗,当用户拒绝授权,再次进入入口页需引导用户打开位置授权

  1. 封装WXAPI
// api.js
/*
    apiName 方法名
    options 参数
*/
function Api(apiName, ...options){
  // 调用wx方法
  if(wx[apiName]){
      wx[apiName](...options);
  }else{
      console.log('ERRO:apiName=>'+ apiName +' is not wx function');
  }
  
}
export default Api;
  1. 封装引导打开位置授权部分
// check-auth.js
const App = getApp();
import WxApi from './api.js';
/**
 * @param {option.scope} String, 必填, description: 需要验证的权限名
 * @param {option.title} String, 可选, description: 跳转打开权限页面的标题
 * @param {option.msg} String, 可选, description: 跳转打开权限页面的文本
 * @param {option.success} Object, 可选, description: 取得权限成功(权限的请求需自行在callback中执行)
 * @param {option.fail} String, 可选, description: 取得权限失败
 * @param {option.showModalTitle} String, 可选, 
 * @param {option.showModalMsg} String, 可选, 
 */
function start(option) {
    // 获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限
	WxApi('getSetting', {
		complete: (res) => {
			if (!res.authSetting[option.scope]) {
                // 提前向用户发起授权请求
				WxApi('authorize', {
					scope: option.scope,
					success: () => {
						if (option.success) option.success();
					},
					fail: (err) => {
						WxApi('showModal', {
							title: (option.showModalTitle) ? option.showModalTitle : option.title,
							content: (option.showModalMsg) ? option.showModalMsg : option.msg,
							showCancel: (!option.showCancel) ? option.showCancel : true,
							success: (smRes) => {
								if (smRes.confirm) {
                                    // 调起客户端小程序设置界面,返回用户设置的操作结果
									WxApi('openSetting', {
										success: (res) => {
											if (res.authSetting[option.scope]) {
												if (option.success) option.success();
											} else {
												if (option.fail) option.fail();
											}
										},
									})
								} else {
									if (option.fail) option.fail();
								}
							},
              fail: (err) => {}
						})
					}
				})

			} else {
				if (option.success) option.success();
			}
		}
	})
}
const CheckAuth = {
	start: start
}
export default CheckAuth;
  1. 定位授权
// 定位授权
	initCheckGps() {
		return new Promise((resolve, reject) => {
			CheckAuth.start({
				scope: 'scope.userLocation',
				title: '获取你的地理位置',
				msg: '您的位置信息将用于获取附近服务门店',
				showModalMsg:
					'柯蕾洛需要你的位置信息才能提供优质服务,请打开手机GPS,并允许获取你的位置信息。',
				showModalTitle: '友情提示',
				showCancel: false,
				success: (res) => {
					wx.getLocation({
						type: 'gcj02',
						success: (res) => {
                            // qqMap存在频率限制,暂不使用
							let _gps = {
								longitude: res.longitude,
								latitude: res.latitude,
							}
							this.globalData.location = _gps
							this.store.setStorage({
								key: 'location',
								value: _gps,
							})
                            resolve(_gps)
                            // 此为QQMap定位,因存在频率限制已弃用,
							// qqMap
							// 	.regionAddress(res.latitude, res.longitude)
							// 	.then((val) => {
							// 		let _gps = {
							// 			longitude: val.result.location.lng,
							// 			latitude: val.result.location.lat,
							// 		}
							// 		this.globalData.location = _gps
							// 		// console.log(_gps)
							// 		this.store.setStorage({
							// 			key: 'location',
							// 			value: _gps,
							// 		})
							// 		resolve(_gps)
							// 	})
						},
					})
				},
				fail: (err) => {
					reject(err)
				},
			})
		})
	},
  1. 使用
app.initCheckGps().then(res => {
    // ...处理逻辑
})
  • 番外

鉴于QQMap定位所表现的良好定位效果,后续可能会沿用,也将代码粘出来

// location.js
import configApi from "../serve/config"
var QQMapWX = require('qqmap/qqmap-wx-jssdk.js'); // 直接去官方下载 https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview

function regionAddress(lat, lng) {
    return new Promise(function (resolve, reject) {
        console.log("位置加载中")
        var qqmapsdk = new QQMapWX({
            key: configApi.mapKey, //柯蕾洛 IQGBZ-J5PKR-ZXMWS-WQXN6-LAD63-XQFXI 蒂梵尼 4XUBZ-SSICG-DQPQR-I7D34-WNEAV-H3BIG  
        })
        var options = {
            "location": {
                "latitude": lat,
                "longitude": lng
            }
        }
        qqmapsdk.reverseGeocoder({
            options: options,
            success: function (res) {
                resolve(res)
            },
            fail: function (res) {
              console.log(res);
                resolve(null)
            },
            complete: function (res) {

                resolve(null)
            }
        })
    })
}

module.exports={
    regionAddress:regionAddress
}

四、版本升级部分

目前正式有效,体验码暂时无效

onLaunch({ path, scene, query, shareTicket }) {
    if (wx.canIUse('getUpdateManager')) {
			const updateManager = wx.getUpdateManager()
			updateManager.onCheckForUpdate(function (res) {
				// 请求完新版本信息的回调
				if (res.hasUpdate) {
					updateManager.onUpdateReady(function () {
						wx.showModal({
							title: '更新提示',
							content: '新版本已经准备好,是否重启应用?',
							success: function (res) {
								// res: {errMsg: "showModal: ok", cancel: false, confirm: true}
								if (res.confirm) {
									// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
									updateManager.applyUpdate()
								}
							},
						})
					})
					updateManager.onUpdateFailed(function () {
						// 新的版本下载失败
						wx.showModal({
							title: '已经有新版本了哟~',
							content:
								'新版本已经上线啦~,请您删除当前小程序,重新搜索打开哟~',
						})
					})
				}
			})
		}
}

五、工具部分

MD5加密

  1. md5.js
function Md5(string) {
	var x = Array();
	var k, AA, BB, CC, DD, a, b, c, d;
	var S11 = 7,
		S12 = 12,
		S13 = 17,
		S14 = 22;
	var S21 = 5,
		S22 = 9,
		S23 = 14,
		S24 = 20;
	var S31 = 4,
		S32 = 11,
		S33 = 16,
		S34 = 23;
	var S41 = 6,
		S42 = 10,
		S43 = 15,
		S44 = 21;
	string = uTF8Encode(string);
	x = convertToWordArray(string);
	a = 0x67452301;
	b = 0xEFCDAB89;
	c = 0x98BADCFE;
	d = 0x10325476;
	for(k = 0; k < x.length; k += 16) {
		AA = a;
		BB = b;
		CC = c;
		DD = d;
		a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
		d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
		c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
		b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
		a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
		d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
		c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
		b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
		a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
		d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
		c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
		b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
		a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
		d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
		c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
		b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
		a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
		d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
		c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
		b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
		a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
		d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
		c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
		b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
		a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
		d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
		c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
		b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
		a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
		d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
		c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
		b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
		a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
		d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
		c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
		b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
		a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
		d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
		c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
		b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
		a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
		d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
		c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
		b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
		a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
		d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
		c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
		b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
		a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
		d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
		c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
		b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
		a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
		d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
		c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
		b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
		a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
		d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
		c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
		b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
		a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
		d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
		c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
		b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
		a = addUnsigned(a, AA);
		b = addUnsigned(b, BB);
		c = addUnsigned(c, CC);
		d = addUnsigned(d, DD);
	}
	var tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
	return tempValue.toLowerCase();
}

var rotateLeft = function (lValue, iShiftBits) {
	return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
}

var addUnsigned = function (lX, lY) {
	var lX4, lY4, lX8, lY8, lResult;
	lX8 = (lX & 0x80000000);
	lY8 = (lY & 0x80000000);
	lX4 = (lX & 0x40000000);
	lY4 = (lY & 0x40000000);
	lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
	if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
	if (lX4 | lY4) {
		if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
		else return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
	} else {
		return (lResult ^ lX8 ^ lY8);
	}
}

var F = function (x, y, z) {
	return (x & y) | ((~x) & z);
}

var G = function (x, y, z) {
	return (x & z) | (y & (~z));
}

var H = function (x, y, z) {
	return (x ^ y ^ z);
}

var I = function (x, y, z) {
	return (y ^ (x | (~z)));
}

var FF = function (a, b, c, d, x, s, ac) {
	a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac));
	return addUnsigned(rotateLeft(a, s), b);
};

var GG = function (a, b, c, d, x, s, ac) {
	a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac));
	return addUnsigned(rotateLeft(a, s), b);
};

var HH = function (a, b, c, d, x, s, ac) {
	a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac));
	return addUnsigned(rotateLeft(a, s), b);
};

var II = function (a, b, c, d, x, s, ac) {
	a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac));
	return addUnsigned(rotateLeft(a, s), b);
};

var convertToWordArray = function (string) {
	var lWordCount;
	var lMessageLength = string.length;
	var lNumberOfWordsTempOne = lMessageLength + 8;
	var lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64;
	var lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16;
	var lWordArray = Array(lNumberOfWords - 1);
	var lBytePosition = 0;
	var lByteCount = 0;
	while (lByteCount < lMessageLength) {
		lWordCount = (lByteCount - (lByteCount % 4)) / 4;
		lBytePosition = (lByteCount % 4) * 8;
		lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
		lByteCount++;
	}
	lWordCount = (lByteCount - (lByteCount % 4)) / 4;
	lBytePosition = (lByteCount % 4) * 8;
	lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
	lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
	lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
	return lWordArray;
};

var wordToHex = function (lValue) {
	var WordToHexValue = '',
		WordToHexValueTemp = '',
		lByte, lCount;
	for (lCount = 0; lCount <= 3; lCount++) {
		lByte = (lValue >>> (lCount * 8)) & 255;
		WordToHexValueTemp = '0' + lByte.toString(16);
		WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2);
	}
	return WordToHexValue;
};

var uTF8Encode = function (string) {
	string = string.replace(/\x0d\x0a/g, '\x0a');
	var output = '';
	for (var n = 0; n < string.length; n++) {
		var c = string.charCodeAt(n);
		if (c < 128) {
			output += String.fromCharCode(c);
		} else if ((c > 127) && (c < 2048)) {
			output += String.fromCharCode((c >> 6) | 192);
			output += String.fromCharCode((c & 63) | 128);
		} else {
			output += String.fromCharCode((c >> 12) | 224);
			output += String.fromCharCode(((c >> 6) & 63) | 128);
			output += String.fromCharCode((c & 63) | 128);
		}
	}
	return output;
};

export default Md5;
  1. login.js
import Md5 from '../../utils/md5.js';
let md5Password = Md5(this.data.password)

六、系统信息部分

  1. 获取系统信息
wx.getSystemInfo({
	success: (res) => {
		this.globalData.systemInfo = res
	},
})
  1. 获取appid
wx.getAccountInfoSync() // object { miniProgram }
  • miniProgram 的结构

属性

类型

说明

appId

string

小程序 appId

envVersion

string

小程序版本 develop(开发)/trial(体验)/release(正式)

version

string

线上小程序版本号

七、缓存处理

// store.js

//合法的strage储存key值
const StorageKeys = [
	'code', // 登录code
	'openId', //用户openId信息
	'unionId', //
	'token', //
	'userInfo', //
	'location', // 位置经纬度
]

const Store = {
	/**
	 * 储存storage
	 * @param {option.key} String, 必填, description: Storage数据对应的key值,只能存放在StorageKeys中声明过的值
	 * @param {option.value} Any, 必填, description: 需要存放的数据
	 */
	setStorage: (option) => {
		if (StorageKeys.indexOf(option.key) >= 0) {
			try {
				wx.setStorageSync(option.key, option.value)
			} catch (e) {}
		} else {
			console.log('ERRO:key=>' + key + ' is uncreate in option')
		}
	},

	/**
	 * 获取storage
	 * @param {key} String, 必填, description: 获取Storage数据对应的key值
	 */
	getStorage: (key) => {
		try {
			let _val = wx.getStorageSync(key)
			if (_val) return _val
		} catch (e) {
			console.log('ERRO:key=>' + key + ' is undefined')
		}
	},

	/**
	 * 删除storage
	 * @param {key} String, 必填, description: 删除torage数据对应的key值
	 */
	removeStorage: (key) => {
		try {
			wx.removeStorageSync(key)
		} catch (e) {
			console.log('ERRO:key=>' + key + ' is undefined')
		}
	},

    /**
	 * 清理所有本地数据缓存
	 */
	clearStorage: () => {
		try {
			wx.clearStorageSync()
		} catch (e) {
			// Do something when catch error
		}
	},

	/**
	 * 获取当前storage的相关信息
	 * @returns {res.keys} Array.<string>, 当前 storage 中所有的 key
	 * @returns {res.keys} number, 当前占用的空间大小, 单位 KB
	 * @returns {res.keys} number, 限制的空间大小,单位 KB
	 */
	getStorageInfo: () => {
		try {
			const res = wx.getStorageInfoSync()
			return res
		} catch (e) {
			// Do something when catch error
		}
	},
}
export default Store

八、CSS兼容问题处理与公共样式

@charset "UTF-8";
/**flex 布局 公共样式**/
.flex-rowaCenter {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-rowajCenter {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-pack: center;
	-webkit-justify-content: center;
	justify-content: center;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-rowaCenterjSpaceb {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-pack: justify;
	-webkit-justify-content: space-between;
	justify-content: space-between;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-flexstart {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-pack: start;
	-webkit-justify-content: flex-start;
	justify-content: flex-start;
}

.flex-rowaCenterjFlexs {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-pack: start;
	-webkit-justify-content: flex-start;
	justify-content: flex-start;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-rowaCenterjFlexe {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-pack: end;
	-webkit-justify-content: flex-end;
	justify-content: flex-end;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-columnaCenter {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-orient: vertical;
	-webkit-box-direction: normal;
	-webkit-flex-direction: column;
	flex-direction: column;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-columnajCenter {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-orient: vertical;
	-webkit-box-direction: normal;
	-webkit-flex-direction: column;
	flex-direction: column;
	-webkit-box-pack: center;
	-webkit-justify-content: center;
	justify-content: center;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-columnajEnd {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-orient: vertical;
	-webkit-box-direction: normal;
	-webkit-flex-direction: column;
	flex-direction: column;
	/* justify-content: flex-end; */
	-webkit-box-align: end;
	-webkit-align-items: flex-end;
	align-items: flex-end;
}

.flex-column {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-orient: vertical;
	-webkit-box-direction: normal;
	-webkit-flex-direction: column;
	flex-direction: column;
}

.flex-columnaCenterjSpaceb {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-orient: vertical;
	-webkit-box-direction: normal;
	-webkit-flex-direction: column;
	flex-direction: column;
	-webkit-box-pack: justify;
	-webkit-justify-content: space-between;
	justify-content: space-between;
	-webkit-box-align: center;
	-webkit-align-items: center;
	align-items: center;
}

.flex-columnSpaceAround {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-justify-content: space-around;
	justify-content: space-around;
	-webkit-box-orient: horizontal;
	-webkit-box-direction: normal;
	-webkit-flex-direction: row;
	flex-direction: row;
}

/**超出缩略**/
.over-elipse {
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* 超出两行缩略 */
.over-two-elipse {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	line-clamp: 2;
	overflow: hidden;
	text-overflow: ellipsis;
}

九、数据清洗与容错

  1. 示例
/**
     * 功能:处理列表
     * @param list
     * @returns {Array}
     */
    authorList: function (list = []) {
        let result = [];
        list.forEach(item => {
            result.push({
                guid: item.recommend_obj_id || '',
                type: item.recommend_type || '',
                logo: (item.theme_pic || '').trim() || '',
                title: item.title || ''
            });
        });
        return result;
    }
  1. 作用:
  • 清洗数据,避免setData()时有过多的脏数据
  • 错误数据的兼容,添加数据缺省值,增加代码健壮性

十、动态组件的渲染

方案的选择: template 和 component

  • template更简洁,更适合本页面小组件,逻辑处理少的组件,不便于复用
  • component,有自己的生命周期,便于复用

component方式

  1. index.json
"usingComponents": {
    "home-swiper": "../../component/home/homeSwiper/homeSwiper",
    "home-address": "../../component/home/homeAddress/homeAddress",
    "home-hot-activity":"../../component/home/homeHotActivity/homeHotActivity",
    "home-group-work": "../../component/home/homeGroupWork/homeGroupWork",
    "group-popup": "../../component/home/groupPopup/groupPopup"
}
  1. index.wxml
<view class="home">
  <block wx:for="{{templateList}}" wx:key="index">
    <block  wx:if="{item.moduleType === 'home-swiper'}}" >
      <home-swiper info="{{item}}" />
    </block>
    <block  wx:if="{{item.moduleType === 'home-address'}}" >
      <home-address info="{{item}}" />
    </block>
    <block  wx:if="{{item.moduleType === 'home-hot-activity'}}" >
      <home-hot-activity info="{{item}}"></home-hot-activity>
    </block>
  <block wx:if="{{item.moduleType === 'home-group-work'}}">
    <home-group-work info="{{item}}"></home-group-work>
  </block>
  </block>
</view>

template

  1. index.wxml
<template name="time">
  <view>
    <text> Time组件: {{time}} </text>
  </view>
</template>

<template name="msg-item">
  <view>
    <text> {{index}}通知组件: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>

<view class="warp">
  <block wx:for="{{list}}" wx:key="index">
    <view class="tmp">
      <template is="{{item.name}}" data="{{...info}}"/>
    </view>
  </block>
</view>
  1. index.js
data: {
    info: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    },
    list: [
      {name: 'msg-item'},
      {name: 'time'},
      {name: 'msg-item',data: []}, 
      {name: 'time'},
      {name: 'msg-item'},
      {name: 'time'},
    ]
},
  1. wxss
.warp {
  margin-top: 30rpx;
}

.tmp {
  margin-bottom: 20rpx;
}