微信小程序开发06-一个业务页面的完成
前言
github地址:https://github.com/yexiaochai/wxdemo
这里来说一说我们的理念,我们也学习小程序开发有一周多了,从近期的使用上来说,小程序可以作为底层,但是缺少一个框架层,这个框架层需要提供:
① 组件库
② 更好的代码组织方式,也就是让我们可以做到轻松的组件化开发
我们从最开始到现在,都在沿着这个方向去分解小程序学习,其实小程序本身的东西差不多了,但是我们代码过程中有时候却越高越复杂,多了很多封装,其实这所有的复杂都是为了设置一个基本的架构,一个标准的开发模式,让后面写业务代码的同学能更高效的写代码,经过一年多的发展,事实上这种较为成熟的框架已经有了,比如我们正在使用的:
https://tencent.github.io/wepy/
但是,可以看到小程序基本还是原生JS,这其实是个非常好的学习整理机会,所以我这边一步步和大家对小程序进行了拆分,期望能形成一套还能用的雏形,帮助大家理解,所以我们继续今天的学习吧,为了降低单页面难度,我们将首页进行下改造。
首页
首页做了一点改造,变成了这个样式了:
这里需要三个点击时间点,因为日历组件,我们昨天就做好了,而他这个出发日期事实上就是我们日历组件的selecedDate,处理这块逻辑:
<template name="searchbox">
<view class="c-row search-line" data-flag="start">
<view class="c-span3">
出发</view>
<view class="c-span9 js-start search-line-txt">
请选择出发地</view>
</view>
<view class="c-row search-line" data-flag="arrive">
<view class="c-span3">
到达</view>
<view class="c-span9 js-arrive search-line-txt">
请选择到达地</view>
</view>
<view class="c-row search-line" data-flag="arrive">
<view class="c-span3">
出发日期</view>
<view class="c-span9 js-arrive search-line-txt">
{{calendarSelectedDate || '请选择出发日期'}} </view>
</view>
<view class="c-row " data-flag="arrive">
<span class="btn-primary full-width js_search_list">查询</span>
</view>
</template>
<view class="c-row search-line" data-flag="arrive">
<view class="c-span3">
出发日期</view>
<view class="c-span9 js-arrive search-line-txt">
{{calendarSelectedDate || '请选择出发日期'}} </view>
</view>
点击时候我们弹出我们的日历,这个时候我们日历模块释放一个事件显示日历:
PS:template不与页面级别WXML共享一个作用域,所以我暂时都采用的include引入
<view class="c-row search-line" data-flag="start">
<view class="c-span3">
出发</view>
<view class="c-span9 js-start search-line-txt">
请选择出发地</view>
</view>
<view class="c-row search-line" data-flag="arrive">
<view class="c-span3">
到达</view>
<view class="c-span9 js-arrive search-line-txt">
请选择到达地</view>
</view>
<view class="c-row search-line" data-flag="arrive" ontap="showCalendar">
<view class="c-span3">
出发日期</view>
<view class="c-span9 js-arrive search-line-txt">
{{calendarSelectedDateStr}}</view>
</view>
<view class="c-row " data-flag="arrive">
<span class="btn-primary full-width js_search_list">查询</span>
</view>
<include src="./mod/calendar.wxml" />
<include src="../../utils/abstract-page.wxml" />
<view class="c-row search-line" data-flag="arrive" ontap="showCalendar">
<view class="c-span3">
出发日期</view>
<view class="c-span9 js-arrive search-line-txt">
{{calendarSelectedDateStr}}</view>
</view>
/*
事实上一个mod就只是一个对象,只不过为了方便拆分,将对象分拆成一个个的mod
一个mod对应一个wxml,但是共享外部的css,暂时如此设计
所有日历模块的需求全部再此实现
*/
const util = require('../../../utils/util.js') let selectedDate = new Date(); module.exports = {
showCalendar: function () {
this.setData({
isCalendarShow: ''
});
},
onCalendarDayTap: function (e) {
let data = e.detail;
var date = new Date(data.year, data.month, data.day);
console.log(date)
this.setData({
calendarSelectedDate: date,
calendarSelectedDateStr: util.dateUtil.format(date, 'Y年M月D日')
});
},
data: {
isCalendarShow: 'none',
calendarDisplayMonthNum: 2,
calendarDisplayTime: new Date(),
calendarSelectedDate: selectedDate,
calendarSelectedDateStr: util.dateUtil.format(selectedDate, 'Y年M月D日')
}
}
显然,这里的日历这样摆设有点丑,我们这里将其封装成一个弹出层,所以我们这里再做一个容器类组件,专门用于装载页面样式用:
<view class="cm-modal " style="z-index: {{uiIndex}}; position: fixed; display: {{isShow}}; ">
<slot ></slot>
</view>
<view class="cm-overlay" bindtap="onMaskEvent" style="z-index: {{maskzIndex}}; display: {{isShow}}" >
</view>
<ui-container bindonContainerHide="onContainerHide" is-show="{{isCalendarShow}}" >
<view class="calendar-wrapper-box">
<view class="box-hd">
<text class="fl icon-back js_back "></text>
<text class="fr icon-next js_next"></text>
</view>
<ui-calendar bindonDayTap="onCalendarDayTap" displayTime="{{calendarDisplayTime}}"
selectedDate="{{calendarSelectedDate}}" displayMonthNum="{{calendarDisplayMonthNum}}"
is-show="{{isCalendarShow}}"></ui-calendar>
</view>
</ui-container>
但是这里也引起了其他问题,因为引入了shadow-dom概念,我的样式不能重用,组件内部样式与外部是不能通信的,但是这里是页面级别容器,内容的样式肯定是来源页面的,这里没什么问题,所以我们这里显示的是正确的,但是我这里想做一个出格一点的操作,我想用样式将这里日历月标题换个位置:
而日历组件和外部是不能通信的,我们这里该如何处理呢,我这里想了两个方案:
① 设置一个全局使用的组件库样式,让所有组件继承,但是不知道这里对性能是否有影响,因为这样的话体积不会太小
② 小程序设计了可以传入组件的方法,比如我们这里的日历组件我们可以这样改变其样式
.calendar-cm-month {
position: absolute;
top:;
height: 90rpx;
line-height: 90rpx;
width: 100%;
color: #00b358;
text-align: center;
}
Component({
2 externalClasses: ['ex-class'],
behaviors: [
View
],
properties: {
displayMonthNum: {
type: Number
},
displayTime: {
type: Date
},
selectedDate: {
type: Date
}
},
data: {
weekDayArr: ['日', '一', '二', '三', '四', '五', '六'],
}, attached: function () {
//console.log(this)
// debugger
},
methods: {
onDayTap: function (e) {
this.triggerEvent('onDayTap', e.currentTarget.dataset)
}
}
})
<ui-container bindonContainerHide="onContainerHide" is-show="{{isCalendarShow}}" >
<view class="calendar-wrapper-box">
<view class="box-hd">
<text class="fl icon-back js_back "></text>
<text class="fr icon-next js_next"></text>
</view>
<ui-calendar ex-class="calendar-cm-month" bindonDayTap="onCalendarDayTap"
displayTime="{{calendarDisplayTime}}" selectedDate="{{calendarSelectedDate}}"
displayMonthNum="{{calendarDisplayMonthNum}}" is-show="{{isCalendarShow}}"></ui-calendar>
</view>
</ui-container>
具体各位去github上查看,总而言之,我们的页面变成了这个样子了:
PS:这里发现一个不知道是不是坑点的点,我们这里属性传递的是一个date对象,但是到了组件层之间变成了对象,不知微信底层做了什么:
calendarDisplayTime: new Date()
好像变成了一个空对象,这里可能发生的情况是,经过传递的日期对象会被某种特殊处理,但是具体发生了什么事情就不知道了,这个却引起了我们不小的麻烦,这里大概去翻开了一下源码:
极有可能,小程序本身就不支持date属性的传递,我们的日历组件能跑起来的原因是什么,我这里都有点疑惑了......
而且就算以对象方式传递到组件的date类型都会变成莫名其妙的东西:
ttt: {
key: 'date',
value: selectedDate
},
这个特性有点令人抓不住头脑了,这里根据探查,很有可能Component将date对象传入WXML解释时候,自动转为了日期字符串了,所以我们这里看上去是对象的东西其实是字符串,这里的建议是:跟组件的date传递,暂时全部使用字符串代替,以免自我麻烦,然后我们先将之前的日历操作全部变成字符串,再为我们的前后按钮加上事件:
module.exports = {
showCalendar: function () {
this.setData({
isCalendarShow: ''
});
},
hideCalendar: function () {
this.setData({
isCalendarShow: 'none'
});
},
preMonth: function () { this.setData({
calendarDisplayTime: util.dateUtil.preMonth(this.data.calendarDisplayTime).toString()
});
},
nextMonth: function () {
this.setData({
calendarDisplayTime: util.dateUtil.nextMonth(this.data.calendarDisplayTime).toString()
});
},
onCalendarDayTap: function (e) {
let data = e.detail;
var date = new Date(data.year, data.month, data.day);
console.log(date)
this.setData({
isCalendarShow: 'none',
calendarSelectedDate: date.toString(),
calendarSelectedDateStr: util.dateUtil.format(date, 'Y年M月D日')
});
},
onContainerHide: function () {
this.hideCalendar();
}, data: {
ttt: {
key: 'date',
value: selectedDate
},
isCalendarShow: '',
calendarDisplayMonthNum: 1,
calendarDisplayTime: new Date(2018, 9).toString(),
calendarSelectedDate: selectedDate,
calendarSelectedDateStr: util.dateUtil.format(new Date(selectedDate), 'Y年M月D日')
}
}
虽然看上去恶心了一点,但是总是不会出什么明显的问题,忍一忍吧......日期部分基本结束了,还有些小的限制没有做上,比如哪些时段能选,哪些不能,这块就有待各位发现吧,我们这里毕竟是学习,做细了很花功夫,我们接下来做出发目的地选择部分。
数据请求
城市列表
城市列表这里看起来需要新开一个页面,但是我这里想做在一个页面中,考虑篇幅,我们使用弹出层容器组件看并且尽量削弱一些特性,几天下来别说写的还有些累......
这个又作为首页的一个模块而存在:
<view style="display: {{isCityShow}}; " class="city-wrapper" >
<view class="city-list">
<view class="list-name">A</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
</view>
<view class="city-list">
<view class="list-name">A</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
<view class="list-item">成都</view>
</view>
</view>
/*
事实上一个mod就只是一个对象,只不过为了方便拆分,将对象分拆成一个个的mod
一个mod对应一个wxml,但是共享外部的css,暂时如此设计
所有日历模块的需求全部再此实现
*/
const util = require('../../../utils/util.js') let selectedDate = new Date().toString(); module.exports = {
showCitylist: function (e) {
let flag = e.currentTarget.dataset.flag; if(flag === 'start') { } else { }
},
//用于设置城市数据
setCityData: function (data) { },
showCity: function () {
this.setData({
isCityShow: ''
});
},
shideCity: function () {
this.setData({
isCityShow: 'none'
});
},
data: {
isCityShow: ''
}
}
首页调用代码:
//获取公共ui操作类实例
const _page = require('../../utils/abstract-page.js');
let modCalendar = require('./mod/calendar.js');
let modCity = require('./mod/city.js'); //获取应用实例
const app = getApp() Page(_page.initPage({
data: {
},
// methods: uiUtil.getPageMethods(),
methods: {
},
onShow: function () {
global.sss = this;
let scope = this;
},
onLoad: function () {
// this.setPageMethods();
}
}, {
modCalendar: modCalendar,
modCity: modCity
}))
这里我们开始有数据请求模块了,小程序使用这个接口请求数据,这里比较尴尬的是他要设置域名白名单:
wx.request(OBJECT)
而我们使用的是测试账号没有可以设置的地方,所以我们还是去申请个小程序账号吧...配置成功,我们继续代码:
可以看到数据请求已经回来了,但是我们一般来说一个接口不止会用于一个地方,每次重新写好像有些费事,加之我这里想将重复的请求缓存起来,所以我们这里封装一套数据访问层出来
数据缓存(持久层)
之前在浏览器中,我们一般使用localstorage存储一些不太更改的数据,微信里面提供了接口处理这一切:
wx.setStorage(OBJECT)
我们这里需要对其进行简单封装,便与后面更好的使用,一般来说有缓存就一定要有过期,所以我们动态给每个缓存对象增加一个过期时间:
class Store {
constructor(opts) {
if(typeof opts === 'string') this.key = opts;
else Object.assign(this, opts); //如果没有传过期时间,则默认30分钟
if(!this.lifeTime) this.lifeTime = 1; //本地缓存用以存放所有localstorage键值与过期日期的映射
this._keyCache = 'SYSTEM_KEY_TIMEOUT_MAP'; }
//获取过期时间,单位为毫秒
_getDeadline() {
return this.lifeTime * 60 * 1000;
} //获取一个数据缓存对象,存可以异步,获取我同步即可
get(sign){
let key = this.key;
let now = new Date().getTime();
var data = wx.getStorageSync(key);
if(!data) return null;
data = JSON.parse(data);
//数据过期
if (data.deadLine < now) {
this.removeOverdueCache();
return null;
} if(data.sign) {
if(sign === data.sign) return data.data;
else return null;
}
return null;
} /*产出页面组件需要的参数
sign 为格式化后的请求参数,用于同一请求不同参数时候返回新数据,比如列表为北京的城市,后切换为上海,会判断tag不同而更新缓存数据,tag相当于签名
每一键值只会缓存一条信息
*/
set(data, sign) {
let timeout = new Date();
let time = timeout.setTime(timeout.getTime() + this._getDeadline());
this._saveData(data, time, sign);
}
_saveData(data, time, sign) {
let key = this.key;
let entity = {
deadLine: time,
data: data,
sign: sign
};
let scope = this; wx.setStorage({
key: key,
data: JSON.stringify(entity),
success: function () {
//每次真实存入前,需要往系统中存储一个清单
scope._saveSysList(key, entity.deadLine);
}
});
}
_saveSysList(key, timeout) {
if (!key || !timeout || timeout < new Date().getTime()) return;
let keyCache = this._keyCache;
wx.getStorage({
key: keyCache,
complete: function (data) {
let oldData = {};
if(data.data) oldData = JSON.parse(data.data);
oldData[key] = timeout;
wx.setStorage({
key: keyCache,
data: JSON.stringify(oldData)
});
}
});
}
//删除过期缓存
removeOverdueCache() {
let now = new Date().getTime();
let keyCache = this._keyCache;
wx.getStorage({
key: keyCache,
success: function (data) {
if(data && data.data) data = JSON.parse(data.data);
for(let k in data) {
if(data[k] < now) {
delete data[k];
wx.removeStorage({key: k, success: function(){}});
}
}
wx.setStorage({
key: keyCache,
data: JSON.stringify(data)
});
}
});
} } module.exports = Store
这个类的使用也非常简单,这里举个例子:
sss = new global.Store({key: 'qqq', lifeTime: 1})
sss.set({a: 1}, 2)
sss.get()//因为没有秘钥会是null
sss.get(2)//sss.get(2)
这个时候我们开始写我们数据请求的类:
首先还是实现了一个抽象类和一个业务基类,然后开始在业务层请求数据:
class Model {
constructor() {
this.url = '';
this.param = {};
this.validates = [];
}
pushValidates(handler) {
if (typeof handler === 'function') {
this.validates.push(handler);
}
}
setParam(key, val) {
if (typeof key === 'object') {
Object.assign(this.param, key);
} else {
this.param[key] = val;
}
}
//@override
buildurl() {
return this.url;
}
onDataSuccess() {
}
//执行数据请求逻辑
execute(onComplete) {
let scope = this;
let _success = function(data) {
let _data = data;
if (typeof data == 'string') _data = JSON.parse(data); // @description 开发者可以传入一组验证方法进行验证
for (let i = 0, len = scope.validates.length; i < len; i++) {
if (!scope.validates[i](data)) {
// @description 如果一个验证不通过就返回
if (typeof onError === 'function') {
return onError.call(scope || this, _data, data);
} else {
return false;
}
}
} // @description 对获取的数据做字段映射
let datamodel = typeof scope.dataformat === 'function' ? scope.dataformat(_data) : _data; if (scope.onDataSuccess) scope.onDataSuccess.call(scope, datamodel, data);
if (typeof onComplete === 'function') {
onComplete.call(scope, datamodel, data);
}
};
this._sendRequest(_success);
} //删除过期缓存
_sendRequest(callback) {
let url = this.buildurl();
wx.request({
url: this.buildurl(),
data: this.param,
success: function success(data) {
callback && callback(data);
}
});
}
}
module.exports = Model
let Model = require('./abstract-model.js'); class DemoModel extends Model {
constructor() {
super();
let scope = this;
this.domain = 'https://apikuai.baidu.com';
this.param = {
head: {
version: '1.0.1',
ct: 'ios'
}
}; //如果需要缓存,可以在此设置缓存对象
this.cacheData = null; this.pushValidates(function(data) {
return scope._baseDataValidate(data);
});
} //首轮处理返回数据,检查错误码做统一验证处理
_baseDataValidate(data) {
if (typeof data === 'string') data = JSON.parse(data);
if (data.data) data = data.data;
if (data.errno === 0) return true;
return false;
} dataformat(data) {
if (typeof data === 'string') data = JSON.parse(data);
if (data.data) data = data.data;
if (data.data) data = data.data;
return data;
} buildurl() {
return this.domain + this.url;
} getSign() {
let param = this.getParam() || {};
return JSON.stringify(param);
}
onDataSuccess(fdata, data) {
if (this.cacheData && this.cacheData.set)
this.cacheData.set(fdata, this.getSign());
} //如果有缓存直接读取缓存,没有才请求
execute(onComplete, ajaxOnly) {
let data = null;
if (!ajaxOnly && this.cacheData && this.cacheData.get) {
data = this.cacheData.get(this.getSign());
if (data) {
onComplete(data);
return;
}
}
super.execute(onComplete);
} } class CityModel extends DemoModel {
constructor() {
super();
this.url = '/city/getstartcitys';
}
} module.exports = {
cityModel: new CityModel }
业务基类
接下来是实际调用代码:
let model = models.cityModel;
model.setParam({
type: 1
});
model.execute(function(data) {
console.log(data);
debugger;
});
数据便请求结束了,有了这个类我们可以做非常多的工作,比如:
① 前端设置统一的错误码处理逻辑
② 前端打点,统计所有的接口响应状态
③ 每次请求相同参数做数据缓存
④ 这个对于错误处理很关键,一般来说前端出错很大可能都是后端数据接口字段有变化,而这种错误是比较难寻找的,如果我这里做一个统一的收口,每次数据返回记录所有的返回字段的标志上报呢,就以这个城市数据为例,我们可以这样做:
class CityModel extends DemoModel {
constructor() {
super();
this.url = '/city/getstartcitys';
}
//每次数据访问成功,错误码为0时皆会执行这个回调
onDataSuccess(fdata, data) {
super.onDataSuccess(fdata, data);
//开始执行自我逻辑
let o = {
_indate: new Date().getTime()
};
for(let k in fdata) {
o[k] = typeof fdata[k];
}
//执行数据上报逻辑
console.log(JSON.stringify(o));
}
}
这里就会输出以下信息:
{"_indate":1533436847778,"cities":"object","hots":"object","total":"number","page":"string"}
如果对数据要求非常严苛,对某些接口做到字段层面的验证,那么加一个Validates验证即可,这样对接口的控制会最大化,就算哪次出问题,也能很好从数据分析系统之中可以查看到问题所在,如果我现在想要一个更为具体的功能呢?我想要首次请求一个接口时便将其数据记录下来,第二次便不再请求呢,这个时候我们之前设计的数据持久层便派上了用处:
let Store = require('./abstract-store.js'); class CityStore extends Store {
constructor() {
super();
this.key = 'DEMO_CITYLIST';
//30分钟过期时间
this.lifeTime = 30;
}
} module.exports = {
cityStore: new CityStore
}
class CityModel extends DemoModel {
constructor() {
super();
this.url = '/city/getstartcitys';
this.cacheData = Stores.cityStore;
}
//每次数据访问成功,错误码为0时皆会执行这个回调
onDataSuccess(fdata, data) {
super.onDataSuccess(fdata, data);
//开始执行自我逻辑
let o = {
_indate: new Date().getTime()
};
for(let k in fdata) {
o[k] = typeof fdata[k];
}
//执行数据上报逻辑
console.log(JSON.stringify(o));
}
}
这个时候第二次请求时候便会直接读取缓存了
接下来便可以回到我们的页面渲染逻辑了,这个时候就变得非常简单了:
<view style="display: {{isCityShow}}; " class="city-wrapper">
<block wx:for="{{cityData}}" wx:key="k">
<view class="city-list">
<block wx:for="{{item}}" wx:key="kk" wx:for-index="key" wx:for-item="value">
<view class="list-name">{{key}}</view>
<block wx:for="{{value}}" wx:key="kkk" wx:for-index="i" wx:for-item="v">
<view class="list-item" data-cnname="{{v.name}}" data-id="{{v.regionid}}">{{v.cnname}}</view>
</block>
</block>
</view>
</block>
</view>
//用于设置城市数据
setCityData: function(data) {
data = data.cities;
let citys = {}, sortCitys = [];
let k, gname, name, i, tmp = {}, index; //首先处理每个name生成唯一K
for (k in data) {
name = data[k].name;
if (!name) {
continue;
}
gname = name[0].toUpperCase();
if (!citys[gname]) citys[gname] = [];
citys[gname].push(data[k]);
} for (i = 65; i < 91; i++) {
tmp = {};
tmp[String.fromCharCode(i)] = [];
sortCitys.push(tmp);
} for (k in citys) {
index = k.charCodeAt() - 65;
tmp = {};
tmp[k] = citys[k];
sortCitys[index] = tmp;
} this.setData({
cityData: sortCitys,
isCityShow: ''
});
},
然后我们这里为组件绑定事件等就比较简单了,大家可以自己看github,于是我们首页的功能便完成了:
经过一个多星期的学习,我们慢慢的完成了我们的首页,好像也就几个元素,但是后面的一切却不简单啊,我们明天继续完成list页面逻辑,便开始总结小程序开发
微信小程序开发06-一个业务页面的完成的更多相关文章
- 微信小程序开发--路由切换,页面重定向
这段时间开发了一个微信小程序,虽然小程序的导航API 官方文档写得很详细,但是在具体开发过程中还是会遇到很多不明白,或者一时转不过弯的地方. 1.页面切换传参,参数读取 1.1 wx.navigat ...
- 微信小程序开发 [06] 一些补充的知识点
0.写在前面的话 前几章的内容串联起来,基本上已经能写比较基础的小程序页面逻辑了,当然,wxml和wxss的我并没有写,因为前端我也并不擅长.这个章节,准备随便叨叨,然后补充一些之前没有提到的基础知识 ...
- 微信小程序开发——以简单易懂的浏览器页面栈理解小程序的页面路由
前言: 对于小程序的页面路由,如果没有一定开发经验的话,理解起来还是会有些困难的.哪怕是有一定小程序开发经验的开发者,能够完全理解掌握的恐怕也不多. 这里就以另外一种方式来详细的介绍小程序的页面栈及路 ...
- 微信小程序开发2-第一个小程序开发准备
1.首先在官网上注册一个账号( https://mp.weixin.qq.com/ )申请一个AppID(类似于人的身份证,小程序也需要身份证) 注册过程不多说 2.安装开发工具( https://m ...
- 微信小程序开发(四)页面跳转
承接上篇博客. 通过点击按钮跳转到新的页面. 先创建新页面home: 代码如下: // home.js Page({}) // 注册页面 // home.json {} // home.wxml &l ...
- 微信小程序开发 如何退出当前页面
默认是在首页 wx.navigateBack({ delta: -1 }); 详情参考. https://mp.weixin.qq.com/debug/wxadoc/dev/api/u ...
- 微信小程序开发07-列表页面怎么做
接上文:微信小程序开发06-一个业务页面的完成 github地址:https://github.com/yexiaochai/wxdemo 我们首页功能基本完成,我对比了下实际工作中的需求,完成度有7 ...
- 微信小程序开发03-这是一个组件
编写组件 基本结构 接上文:微信小程序开发02-小程序基本介绍 我们今天先来实现这个弹出层: 之前这个组件是一个容器类组件,弹出层可设置载入的html结构,然后再设置各种事件即可,这种组件有一个特点: ...
- 【云开发】10分钟零基础学会做一个快递查询微信小程序,快速掌握微信小程序开发技能(轮播图、API请求)
大家好,我叫小秃僧 这次分享的是10分钟零基础学会做一个快递查询微信小程序,快速掌握开发微信小程序技能. 这篇文章偏基础,特别适合还没有开发过微信小程序的童鞋,一些概念和逻辑我会讲细一点,尽可能用图说 ...
随机推荐
- ROC曲线的概念和意义
ROC曲线 受试者工作特征曲线 (receiver operating characteristic curve,简称ROC曲线),又称为感受性曲线(sensitivity curve).得此名的原因 ...
- qtchooser
qtchooser 的配置目录: /usr/lib/x86_64-linux-gnu/qtchooser qtchooser 的真实配置目录: /usr/share/qtchooser qtchoos ...
- monkey------模块组合测试
由于项目基本功能和预置APK都很多,单个模块跑消耗机器数量很大,效果也不佳.而且monkey测试经常要过夜测试,所以组合模块试用较多,而且发现问题量也更大.组合模块就是按照测试标准要求和模块特性,按照 ...
- 挑子学习笔记:DBSCAN算法的python实现
转载请标明出处:https://www.cnblogs.com/tiaozistudy/p/dbscan_algorithm.html DBSCAN(Density-Based Spatial Clu ...
- Windbg分析高内存占用问题
1. 问题简介 最近产品发布大版本补丁更新,一商超客户升级后,反馈系统经常奔溃,导致超市的收银系统无法正常收银,现场排队付款的顾客更是抱怨声声.为了缓解现场的情况, 客户都是手动回收IIS应用程序池才 ...
- 你真的了解String吗?(修正版)
修正前:new出来的对象,会在堆中存放真正的值: 大错特错!!!! 修正后:new出来的对象,堆存放的并不是真正的值,而是常量池中字符串常量的地址. 一.抛砖引玉 不知道大家在做面试题时是否会遇到 ...
- WebView,我已经长大了,知道自己区分是否安全了!
一.前言 如果你在用 Android 原生系统(Google Play 服务),在使用 WebView 加载某些网页时,一定遇到过以下的安全警告红屏. 这是 WebView 的安全浏览保护策略,在 A ...
- DSAPI多功能.NET函数库组件
DSAPI.dll不定期更新,增加功能,故无法每次都发到网上,如果需要获得最新版DSAPI.dll的,请到QQ群:419130936群共享里下载. 简介 dsapi.dll是一款基于.net平 ...
- CentOS7中搭建cobbler自动装机服务
一.实验环境 一台centos7 epel源网址 https://fedoraproject.org/wiki/EPEL?rd=Epel 使用nat模式 二.实验步骤 1.下载epel源后进行文件夹挂 ...
- openlayers4 入门开发系列之地图标绘篇(附源码下载)
前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...