
【技术分享】使用 Webpack、Vue、Dcloud 开发 App
介绍如何使用 Webpack、Vue技术栈 开发 Webapp,构建单页面应用
为各位提供另一种思路
使用 Dcloud 进行打包后,单个 webview 承载所有页面
目录:
介绍
环境搭建
使用 vue-cli 快速构建
实践推荐
进行开发
打包 App
优势:
Vue 生态的组件库比 MUI 更好用(个人认为)
开发模式更先进
使用 ES6 语法
要求开发者:
了解 npm、webpack,ES6
熟悉 Vue技术栈、Dcloud
可以先了解一下 Vue技术栈 的上限:
打开 Chrome 控制台,使用移动端模式观看,效果更佳
打包后,用单个 webview 承载,也是同样的效果
介绍如何使用 Webpack、Vue技术栈 开发 Webapp,构建单页面应用
为各位提供另一种思路
使用 Dcloud 进行打包后,单个 webview 承载所有页面
目录:
介绍
环境搭建
使用 vue-cli 快速构建
实践推荐
进行开发
打包 App
优势:
Vue 生态的组件库比 MUI 更好用(个人认为)
开发模式更先进
使用 ES6 语法
要求开发者:
了解 npm、webpack,ES6
熟悉 Vue技术栈、Dcloud
可以先了解一下 Vue技术栈 的上限:
打开 Chrome 控制台,使用移动端模式观看,效果更佳
打包后,用单个 webview 承载,也是同样的效果
收起阅读 »
支付方法的封装
**最下面有源码**
这个是我封装的payment方法 封装的有点深
入过的坑
1.微信一直返回-1代码错误 可能是因为微信的配置出来问题还有可能需要用正式签名打包
2.集成支付宝失败 提示是系统繁忙,请稍后再试( ALI64) 这个我记得好像是也是支付的配置出了问题 具体的可能忘了 可以联系支付宝客服解决
3. 注意layerOpen这个是我找的模仿ios的一个提示窗口 需要的找我要
在其他页面调用之前需要导入
<script type="text/javascript" src="utilsJs/paymentUtils.js"></script>
var paymentUtils = null;
function plusReady() {
paymentUtils = new PaymentUtils();
paymentUtils.paymentWayAlert({
order_id: order._id,
onSuccess: function () {
//支付成功
},
onFailed: function () {
//支付失败
}
});
}
if (window.plus) {
plusReady();
} else {
document.addEventListener("plusready", plusReady, false);
}
这个是 PaymentUtils文件的代码
var PaymentUtils = function () {
this.reportLocation = null;
};
/**
*检查是否安装了支付软件
* @param options
*/
PaymentUtils.prototype.checkPaymentWay = function (options) {
options = options || {};
plus.payment.getChannels(function (paymentWay) {
if (paymentWay) {
options.onSuccess && options.onSuccess(paymentWay);
} else {
options.onFailed && options.onFailed('获取支付通道列表失败');
}
}, function (error) {
options.onFailed && options.onFailed('获取支付通道列表失败' + error.message);
});
};
/**
*调用支付并选择支付方式
* @param options
*/
PaymentUtils.prototype.paymentWayAlert = function (options) {
var _this = this;
options = options || {};
var installPayment = function (content, channel) {
layerOpen({
"title": "是否安装支付支持服务",
"content": content,
"btn": ["取消", "确定"],
"shadeClose": true,
"event": [null, function () {
channel.installService();
}]
});
};
this.checkPaymentWay({
onSuccess: function (paymentWay) {
var bts = [];
var bids = [];
var content = '';
for (var index in paymentWay) {
var channel = paymentWay[index];
bids.push(channel);
switch (channel.id) {
case 'alipay':
bts.push({title: '支付宝支付'});
break;
case 'wxpay':
bts.push({title: '微信支付'});
break;
case 'appleiap':
break;
case 'qhpay':
break;
default:
break;
}
}
plus.nativeUI.actionSheet({
cancel: "取消",
buttons: bts
},
function (e) {
var i = e.index;
if (i > 0) {
var channel = bids[i - 1];
switch (channel.id) {
case 'alipay':
if (!channel.serviceReady) {
content = '检测到系统未安装“支付宝快捷支付服务”,无法完成支付操作,是否立即安装?';
installPayment(content, channel);
return;
}
break;
case 'wxpay':
if (!channel.serviceReady) {
content = '系统未安装“' + channel.description + '”服务,无法完成支付,是否立即安装?';
installPayment(content, channel);
return;
}
break;
case 'appleiap':
break;
case 'qhpay':
break;
default:
break;
}
_this.paymentHandle({
order_id: options.order_id,
channel: channel,
onSuccess: function (result) {
options.onSuccess && options.onSuccess(result);
},
onFailed: function (error) {
options.onFailed && options.onFailed(error);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
}
}
);
},
onFailed: function (error) {
options.onFailed && options.onFailed(error);
}
});
};
/**
*向服务器发起请求
* @param options
*/
PaymentUtils.prototype.paramsHandle = function (options) {
var params = '';
options = options || {};
var order_id = options.order_id;
var isAilPay = options.isAilPay;
//判断是否是支付宝还是微信
if (isAilPay) {
//请求服务器获取支付接口
http.xxxx({
order_id: order_id,
onSuccess: function (responseJson) {
options.onSuccess && options.onSuccess(params);
},
onFailed: function (errorJson) {
options.onFailed && options.onFailed(errorJson);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
} else {
//请求服务器获取支付接口
http.xxxx({
order_id: order_id,
onSuccess: function (responseJson) {
options.onSuccess && options.onSuccess(params);
},
onFailed: function (errorJson) {
options.onFailed && options.onFailed(errorJson);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
}
};
/**
*调起payment方法
* @param options
*/
PaymentUtils.prototype.paymentHandle = function (options) {
var _this = this;
var isAilPay;
options = options || {};
var order_id = options.order_id;
var channel = options.channel;
switch (channel.id) {
case 'alipay':
isAilPay = true;
break;
case 'wxpay':
isAilPay = false;
break;
default:
break;
}
if (isAilPay == undefined) {
return;
}
this.paramsHandle({
order_id: order_id,
isAilPay: isAilPay,
onSuccess: function (order_params) {
//官方调起支付的api
plus.payment.request(channel, order_params, function (result) {
//支付成功后 逻辑
}, function (error) {
ApiConfig.staticShowToast('支付失败:' + error.code);
options.onFailed && options.onFailed(error);
});
},
onFailed: function (error) {
options.onFailed && options.onFailed(error);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
};
/**
* 查询支付成功后的订单状态
* @param options
*/
PaymentUtils.prototype.selectPaymentStatus = function (options) {
}
/**
* 获取订单详情
* @param options
*/
PaymentUtils.prototype.getOrderSuccess = function (options) {
}
**最下面有源码**
这个是我封装的payment方法 封装的有点深
入过的坑
1.微信一直返回-1代码错误 可能是因为微信的配置出来问题还有可能需要用正式签名打包
2.集成支付宝失败 提示是系统繁忙,请稍后再试( ALI64) 这个我记得好像是也是支付的配置出了问题 具体的可能忘了 可以联系支付宝客服解决
3. 注意layerOpen这个是我找的模仿ios的一个提示窗口 需要的找我要
在其他页面调用之前需要导入
<script type="text/javascript" src="utilsJs/paymentUtils.js"></script>
var paymentUtils = null;
function plusReady() {
paymentUtils = new PaymentUtils();
paymentUtils.paymentWayAlert({
order_id: order._id,
onSuccess: function () {
//支付成功
},
onFailed: function () {
//支付失败
}
});
}
if (window.plus) {
plusReady();
} else {
document.addEventListener("plusready", plusReady, false);
}
这个是 PaymentUtils文件的代码
var PaymentUtils = function () {
this.reportLocation = null;
};
/**
*检查是否安装了支付软件
* @param options
*/
PaymentUtils.prototype.checkPaymentWay = function (options) {
options = options || {};
plus.payment.getChannels(function (paymentWay) {
if (paymentWay) {
options.onSuccess && options.onSuccess(paymentWay);
} else {
options.onFailed && options.onFailed('获取支付通道列表失败');
}
}, function (error) {
options.onFailed && options.onFailed('获取支付通道列表失败' + error.message);
});
};
/**
*调用支付并选择支付方式
* @param options
*/
PaymentUtils.prototype.paymentWayAlert = function (options) {
var _this = this;
options = options || {};
var installPayment = function (content, channel) {
layerOpen({
"title": "是否安装支付支持服务",
"content": content,
"btn": ["取消", "确定"],
"shadeClose": true,
"event": [null, function () {
channel.installService();
}]
});
};
this.checkPaymentWay({
onSuccess: function (paymentWay) {
var bts = [];
var bids = [];
var content = '';
for (var index in paymentWay) {
var channel = paymentWay[index];
bids.push(channel);
switch (channel.id) {
case 'alipay':
bts.push({title: '支付宝支付'});
break;
case 'wxpay':
bts.push({title: '微信支付'});
break;
case 'appleiap':
break;
case 'qhpay':
break;
default:
break;
}
}
plus.nativeUI.actionSheet({
cancel: "取消",
buttons: bts
},
function (e) {
var i = e.index;
if (i > 0) {
var channel = bids[i - 1];
switch (channel.id) {
case 'alipay':
if (!channel.serviceReady) {
content = '检测到系统未安装“支付宝快捷支付服务”,无法完成支付操作,是否立即安装?';
installPayment(content, channel);
return;
}
break;
case 'wxpay':
if (!channel.serviceReady) {
content = '系统未安装“' + channel.description + '”服务,无法完成支付,是否立即安装?';
installPayment(content, channel);
return;
}
break;
case 'appleiap':
break;
case 'qhpay':
break;
default:
break;
}
_this.paymentHandle({
order_id: options.order_id,
channel: channel,
onSuccess: function (result) {
options.onSuccess && options.onSuccess(result);
},
onFailed: function (error) {
options.onFailed && options.onFailed(error);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
}
}
);
},
onFailed: function (error) {
options.onFailed && options.onFailed(error);
}
});
};
/**
*向服务器发起请求
* @param options
*/
PaymentUtils.prototype.paramsHandle = function (options) {
var params = '';
options = options || {};
var order_id = options.order_id;
var isAilPay = options.isAilPay;
//判断是否是支付宝还是微信
if (isAilPay) {
//请求服务器获取支付接口
http.xxxx({
order_id: order_id,
onSuccess: function (responseJson) {
options.onSuccess && options.onSuccess(params);
},
onFailed: function (errorJson) {
options.onFailed && options.onFailed(errorJson);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
} else {
//请求服务器获取支付接口
http.xxxx({
order_id: order_id,
onSuccess: function (responseJson) {
options.onSuccess && options.onSuccess(params);
},
onFailed: function (errorJson) {
options.onFailed && options.onFailed(errorJson);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
}
};
/**
*调起payment方法
* @param options
*/
PaymentUtils.prototype.paymentHandle = function (options) {
var _this = this;
var isAilPay;
options = options || {};
var order_id = options.order_id;
var channel = options.channel;
switch (channel.id) {
case 'alipay':
isAilPay = true;
break;
case 'wxpay':
isAilPay = false;
break;
default:
break;
}
if (isAilPay == undefined) {
return;
}
this.paramsHandle({
order_id: order_id,
isAilPay: isAilPay,
onSuccess: function (order_params) {
//官方调起支付的api
plus.payment.request(channel, order_params, function (result) {
//支付成功后 逻辑
}, function (error) {
ApiConfig.staticShowToast('支付失败:' + error.code);
options.onFailed && options.onFailed(error);
});
},
onFailed: function (error) {
options.onFailed && options.onFailed(error);
},
onCompleted: function () {
options.onCompleted && options.onCompleted();
}
});
};
/**
* 查询支付成功后的订单状态
* @param options
*/
PaymentUtils.prototype.selectPaymentStatus = function (options) {
}
/**
* 获取订单详情
* @param options
*/
PaymentUtils.prototype.getOrderSuccess = function (options) {
}
收起阅读 »

mui如何可以显示出底部选项卡?
我在index页面中写了底部选项卡,跳转至底部页面的第一个页面。
如何使我点击顶部选项卡的时候,底部选项卡依旧在。
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link href="css/mui.min.css" rel="stylesheet" />
<script src="js/iconfont.js"></script>
<style>
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
#movie_body {
background-color: #A3C9FF;
}
#ticket {
color: #FFFFFF;
}
#movie_list1 {
height: 100px;
background-color: #FFFFFF;
}
</style>
</head>
<body>
<header id="movie_body" class="mui-bar mui-bar-nav">
<span class="mui-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-dianyingpiaoiocn"></use>
</svg>
</span>
<span id="ticket">
电影票
</span>
<a style="float:right;" href="address.html">
<span style="color: #FFFFFF;">
昆明市
</span>
<span class="mui-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-weizhi"></use>
</svg>
</span>
</a>
</header>
<div class="mui-content">
<div style="padding: 10px 10px;">
<div id="sliderSegmentedControl" class="mui-segmented-control">
<a class="mui-control-item mui-active" href="tab-movie_home.html" data-wid="tab-movie_home">影院热映</a>
<a class="mui-control-item" href="the_upcoming.html" data-wid="the_upcoming">即将上映</a>
</div>
</div>
<div id="slider" class="mui-slider">
<div class="mui-slider-group mui-slider-loop">
<!-- 额外增加的一个节点(循环轮播:第一个节点是最后一张轮播) -->
<div class="mui-slider-item mui-slider-item-duplicate">
<a href="#">
<img src="images/movie1.jpg">
</a>
</div>
<!-- 第一张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie2.jpg">
</a>
</div>
<!-- 第二张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie3.jpg">
</a>
</div>
<!-- 第三张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie4.jpg">
</a>
</div>
<!-- 第四张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie4.jpg">
</a>
</div>
<!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) -->
<div class="mui-slider-item mui-slider-item-duplicate">
<a href="#">
<img src="images/movie1.jpg">
</a>
</div>
</div>
</div>
<!--<div>
<ul id="list" class="mui-table-view mui-table-view-chevron">
<li class="mui-table-view-cell">
<a class="mui-navigate-right" href="#">
<span>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-dianshijucai"></use>
</svg>
</span>
<span class="mui-tab-label">电视剧</span>
</a>
</li>
</ul>
<div id="movie_list1" class="mui-scroll-wrapper mui-slider-indicator mui-segmented-control mui-segmented-control-inverted">
<div class="mui-scroll">
<a class="mui-control-item mui-active">
<img style="width: 80px;" src="images/ms1.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms2.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms3.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms4.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms5.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms6.jpg" />
</a>
</div>
</div>-->
</div>
<script src="js/mui.min.js"></script>
<script src="js/webviewGroup.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
mui.init();
mui.plusReady(function() {
var group = new webviewGroup("tab-movie_home.html", {
items: [{
id: "tab-movie_home", //这是子页1的路径
url: "tab-movie_home.html",
extras: {}
}, {
id: "the_upcoming", //这是子页2的路径
url: "the_upcoming.html",
extras: {}
}],
onChange: function(obj) {
var c = document.querySelector(".mui-control-item.mui-active");
if(c) {
c.classList.remove("mui-active");
}
document.querySelector(".mui-segmented-control .mui-control-item:nth-child(" + (parseInt(obj.index) + 1) + ")").classList.add("mui-active");
}
});
mui(".mui-segmented-control").on("tap", ".mui-control-item", function(e) {
var wid = this.getAttribute("data-wid");
group.switchTab(wid);
});
});
mui.back = function() {
var _self = plus.webview.currentWebview();
_self.close("auto");
}
</script>
</body>
</html>
我在index页面中写了底部选项卡,跳转至底部页面的第一个页面。
如何使我点击顶部选项卡的时候,底部选项卡依旧在。
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link href="css/mui.min.css" rel="stylesheet" />
<script src="js/iconfont.js"></script>
<style>
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
#movie_body {
background-color: #A3C9FF;
}
#ticket {
color: #FFFFFF;
}
#movie_list1 {
height: 100px;
background-color: #FFFFFF;
}
</style>
</head>
<body>
<header id="movie_body" class="mui-bar mui-bar-nav">
<span class="mui-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-dianyingpiaoiocn"></use>
</svg>
</span>
<span id="ticket">
电影票
</span>
<a style="float:right;" href="address.html">
<span style="color: #FFFFFF;">
昆明市
</span>
<span class="mui-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-weizhi"></use>
</svg>
</span>
</a>
</header>
<div class="mui-content">
<div style="padding: 10px 10px;">
<div id="sliderSegmentedControl" class="mui-segmented-control">
<a class="mui-control-item mui-active" href="tab-movie_home.html" data-wid="tab-movie_home">影院热映</a>
<a class="mui-control-item" href="the_upcoming.html" data-wid="the_upcoming">即将上映</a>
</div>
</div>
<div id="slider" class="mui-slider">
<div class="mui-slider-group mui-slider-loop">
<!-- 额外增加的一个节点(循环轮播:第一个节点是最后一张轮播) -->
<div class="mui-slider-item mui-slider-item-duplicate">
<a href="#">
<img src="images/movie1.jpg">
</a>
</div>
<!-- 第一张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie2.jpg">
</a>
</div>
<!-- 第二张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie3.jpg">
</a>
</div>
<!-- 第三张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie4.jpg">
</a>
</div>
<!-- 第四张 -->
<div class="mui-slider-item">
<a href="#">
<img src="images/movie4.jpg">
</a>
</div>
<!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) -->
<div class="mui-slider-item mui-slider-item-duplicate">
<a href="#">
<img src="images/movie1.jpg">
</a>
</div>
</div>
</div>
<!--<div>
<ul id="list" class="mui-table-view mui-table-view-chevron">
<li class="mui-table-view-cell">
<a class="mui-navigate-right" href="#">
<span>
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-dianshijucai"></use>
</svg>
</span>
<span class="mui-tab-label">电视剧</span>
</a>
</li>
</ul>
<div id="movie_list1" class="mui-scroll-wrapper mui-slider-indicator mui-segmented-control mui-segmented-control-inverted">
<div class="mui-scroll">
<a class="mui-control-item mui-active">
<img style="width: 80px;" src="images/ms1.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms2.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms3.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms4.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms5.jpg" />
</a>
<a class="mui-control-item">
<img style="width: 80px;" src="images/ms6.jpg" />
</a>
</div>
</div>-->
</div>
<script src="js/mui.min.js"></script>
<script src="js/webviewGroup.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
mui.init();
mui.plusReady(function() {
var group = new webviewGroup("tab-movie_home.html", {
items: [{
id: "tab-movie_home", //这是子页1的路径
url: "tab-movie_home.html",
extras: {}
}, {
id: "the_upcoming", //这是子页2的路径
url: "the_upcoming.html",
extras: {}
}],
onChange: function(obj) {
var c = document.querySelector(".mui-control-item.mui-active");
if(c) {
c.classList.remove("mui-active");
}
document.querySelector(".mui-segmented-control .mui-control-item:nth-child(" + (parseInt(obj.index) + 1) + ")").classList.add("mui-active");
}
});
mui(".mui-segmented-control").on("tap", ".mui-control-item", function(e) {
var wid = this.getAttribute("data-wid");
group.switchTab(wid);
});
});
mui.back = function() {
var _self = plus.webview.currentWebview();
_self.close("auto");
}
</script>
</body>
</html>
收起阅读 »

关于云打包android证书生成填坑,已解决建议加精
一开始一直按网上的方法生成证书发现生成出来的证书一直是无效的证书。
然后就想到应该是hbuilder解析不了证书格式
于是开始折腾
先去下载了hbuilder官方的证书,用keytool查看
keytool -list -v -keystore ./HBuilder.keystore
发现
签名算法名称: SHA1withRSA
主体公共密钥算法:1024 位 RSA 密钥
密钥库类型:JKS
果然和默认生成不一样,下面直接给出解决方案。
第一步生成正常的证书:
keytool -genkey -alias yourapp.keystore -keyalg RSA -sigalg SHA1WithRSA -validity 20000 -keysize 1024 -keystore yourapp.keystore -v
上面的关键的地方
-sigalg SHA1WithRSA
-keysize 1024
第二步,更改密钥库类型:
keytool -importkeystore -srckeystore ./yourapp.keystore -destkeystore ./yourapp.keystore -deststoretype JKS
然后使用这个证书就可以云打包了
注意把yourapp改成你自己的名字
一开始一直按网上的方法生成证书发现生成出来的证书一直是无效的证书。
然后就想到应该是hbuilder解析不了证书格式
于是开始折腾
先去下载了hbuilder官方的证书,用keytool查看
keytool -list -v -keystore ./HBuilder.keystore
发现
签名算法名称: SHA1withRSA
主体公共密钥算法:1024 位 RSA 密钥
密钥库类型:JKS
果然和默认生成不一样,下面直接给出解决方案。
第一步生成正常的证书:
keytool -genkey -alias yourapp.keystore -keyalg RSA -sigalg SHA1WithRSA -validity 20000 -keysize 1024 -keystore yourapp.keystore -v
上面的关键的地方
-sigalg SHA1WithRSA
-keysize 1024
第二步,更改密钥库类型:
keytool -importkeystore -srckeystore ./yourapp.keystore -destkeystore ./yourapp.keystore -deststoretype JKS
然后使用这个证书就可以云打包了
注意把yourapp改成你自己的名字
收起阅读 »
js闭包
闭包
简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。
为什么需要闭包
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
特点
占中更多内存
不容易被释放
何时使用
既想反复使用,又想避免全局污染。
如何使用
1.定义外层函数,封装被保护的局部变量。
- 定义内层函数,执行对外部函数变量的操作。
- 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
函数生命周期
要记住函数对象、作用域链对象、执行环境(EC)和活动(AO)这几个东西都什么时候出现,什么时候消失。

例子:
看此函数,函数对象的地址仅作标识不代表真实的地址
var getNum;
function getCounter () {
var n = 1;
var inner = functionn () {
return n ++ ;
}
return inner;
}
getNum = getCounter();
console.log(getNum());
console.log(getNum());
程序运行到2的时候:
运行到程序3的时候:
运行到4的时候,外层函数调用结束,AO对象释放,图中红线断了;
4处的代码执行时:
4处的代码执行完:
总结:可以看到内层函数对象被全局变量getNum引用和外层函数的AO对象引用,外层函数的AO对象,内层函数本身的作用域对象在函数调用完都无法被内存回收,因此占用了更多的内存空间,但是这样持久的保存了需要的n。
来源:https://zhuanlan.zhihu.com/p/27857268
闭包
简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。
为什么需要闭包
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
特点
占中更多内存
不容易被释放
何时使用
既想反复使用,又想避免全局污染。
如何使用
1.定义外层函数,封装被保护的局部变量。
- 定义内层函数,执行对外部函数变量的操作。
- 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
函数生命周期
要记住函数对象、作用域链对象、执行环境(EC)和活动(AO)这几个东西都什么时候出现,什么时候消失。
例子:
看此函数,函数对象的地址仅作标识不代表真实的地址
var getNum;
function getCounter () {
var n = 1;
var inner = functionn () {
return n ++ ;
}
return inner;
}
getNum = getCounter();
console.log(getNum());
console.log(getNum());
程序运行到2的时候:
运行到程序3的时候:
运行到4的时候,外层函数调用结束,AO对象释放,图中红线断了;
4处的代码执行时:
4处的代码执行完:
总结:可以看到内层函数对象被全局变量getNum引用和外层函数的AO对象引用,外层函数的AO对象,内层函数本身的作用域对象在函数调用完都无法被内存回收,因此占用了更多的内存空间,但是这样持久的保存了需要的n。
来源:https://zhuanlan.zhihu.com/p/27857268
收起阅读 »
安卓获取手机拨打状态和号码
var main = plus.android.runtimeMainActivity();
var Context = plus.android.importClass("android.content.Context");
var telephonyManager = plus.android.importClass("android.telephony.TelephonyManager");
var telephonyManager=plus.android.runtimeMainActivity().getSystemService(Context.TELEPHONY_SERVICE);
var receiver=plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: function(context, intent) { //实现onReceiver回调函数
plus.android.importClass(intent);
console.log(intent.getAction());
var telephonyManager = plus.android.importClass("android.telephony.TelephonyManager");
var telephonyManager=plus.android.runtimeMainActivity().getSystemService(Context.TELEPHONY_SERVICE);
var phonetype=telephonyManager.getCallState();
var phoneNumber = intent.getStringExtra(telephonyManager.EXTRA_INCOMING_NUMBER);
/**
* public static final int CALL_STATE_IDLE = 0; //空闲状态
* public static final int CALL_STATE_RINGING = 1; //振铃状态
* public static final int CALL_STATE_OFFHOOK = 2; //至少有一个通话存在
*/
console.log("phonetype:"+phonetype);
console.log("phonetype:"+phoneNumber);
if(phonetype==2){
main.unregisterReceiver(receiver);
}
}
});
var IntentFilter = plus.android.importClass('android.content.IntentFilter');
var filter = new IntentFilter();
filter.addAction(telephonyManager.ACTION_PHONE_STATE_CHANGED); //监听开关
main.registerReceiver(receiver, filter); //注册监听
亲测可用!
var main = plus.android.runtimeMainActivity();
var Context = plus.android.importClass("android.content.Context");
var telephonyManager = plus.android.importClass("android.telephony.TelephonyManager");
var telephonyManager=plus.android.runtimeMainActivity().getSystemService(Context.TELEPHONY_SERVICE);
var receiver=plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: function(context, intent) { //实现onReceiver回调函数
plus.android.importClass(intent);
console.log(intent.getAction());
var telephonyManager = plus.android.importClass("android.telephony.TelephonyManager");
var telephonyManager=plus.android.runtimeMainActivity().getSystemService(Context.TELEPHONY_SERVICE);
var phonetype=telephonyManager.getCallState();
var phoneNumber = intent.getStringExtra(telephonyManager.EXTRA_INCOMING_NUMBER);
/**
* public static final int CALL_STATE_IDLE = 0; //空闲状态
* public static final int CALL_STATE_RINGING = 1; //振铃状态
* public static final int CALL_STATE_OFFHOOK = 2; //至少有一个通话存在
*/
console.log("phonetype:"+phonetype);
console.log("phonetype:"+phoneNumber);
if(phonetype==2){
main.unregisterReceiver(receiver);
}
}
});
var IntentFilter = plus.android.importClass('android.content.IntentFilter');
var filter = new IntentFilter();
filter.addAction(telephonyManager.ACTION_PHONE_STATE_CHANGED); //监听开关
main.registerReceiver(receiver, filter); //注册监听
亲测可用!
收起阅读 »
Android - 获取机子总内存,剩余内存
群友问的,帮忙翻译的 0.0
安卓获取机子总内存:
var localFileReader = plus.android.newObject('java.io.FileReader','/proc/meminfo')
var localBufferedReader = plus.android.newObject('java.io.BufferedReader',localFileReader,8192)
var str = localBufferedReader.readLine().toString()
console.log(str)
安卓获取机子剩余:
var localFileReader = plus.android.newObject('java.io.FileReader','/proc/meminfo')
var localBufferedReader = plus.android.newObject('java.io.BufferedReader',localFileReader,8192)
var str = ''
var i = 0
while(i<3){
str = localBufferedReader.readLine().toString();
i++
}
console.log(str)
两者输出的str需要做一下文本格式化,单位为kb,自己除以1024即可。
====== 2018-01-09 修复一个可能存在的问题,重新封装下
function getMemInfo(){
plus.android.importClass('java.io.BufferedReader')
var localFileReader = plus.android.newObject('java.io.FileReader','/proc/meminfo')
var localBufferedReader = plus.android.newObject('java.io.BufferedReader',localFileReader,8192)
var str = localBufferedReader.readLine().toString();
var totalMemStr = str;
var avaMemStr = ''
var i = 0
while(i<2){
str = localBufferedReader.readLine().toString();
avaMemStr = str;
i++
}
totalMemStr = (parseInt(totalMemStr.toUpperCase().replace(/(( )|(:)|[A-Z])/gi,''))/1024).toFixed(0)
avaMemStr = (parseInt(avaMemStr.toUpperCase().replace(/(( )|(:)|[A-Z])/gi,''))/1024).toFixed(0)
console.log(totalMemStr);
console.log(avaMemStr);
return {total:totalMemStr,ava:avaMemStr}
}
getMemInfo();
getMemInfo返回一个对象,total是总内存,ava是剩余内存,单位为MB。
顺带自己的Github项目,一个前端跨平台方法兼容库
Sh.js github地址
群友问的,帮忙翻译的 0.0
安卓获取机子总内存:
var localFileReader = plus.android.newObject('java.io.FileReader','/proc/meminfo')
var localBufferedReader = plus.android.newObject('java.io.BufferedReader',localFileReader,8192)
var str = localBufferedReader.readLine().toString()
console.log(str)
安卓获取机子剩余:
var localFileReader = plus.android.newObject('java.io.FileReader','/proc/meminfo')
var localBufferedReader = plus.android.newObject('java.io.BufferedReader',localFileReader,8192)
var str = ''
var i = 0
while(i<3){
str = localBufferedReader.readLine().toString();
i++
}
console.log(str)
两者输出的str需要做一下文本格式化,单位为kb,自己除以1024即可。
====== 2018-01-09 修复一个可能存在的问题,重新封装下
function getMemInfo(){
plus.android.importClass('java.io.BufferedReader')
var localFileReader = plus.android.newObject('java.io.FileReader','/proc/meminfo')
var localBufferedReader = plus.android.newObject('java.io.BufferedReader',localFileReader,8192)
var str = localBufferedReader.readLine().toString();
var totalMemStr = str;
var avaMemStr = ''
var i = 0
while(i<2){
str = localBufferedReader.readLine().toString();
avaMemStr = str;
i++
}
totalMemStr = (parseInt(totalMemStr.toUpperCase().replace(/(( )|(:)|[A-Z])/gi,''))/1024).toFixed(0)
avaMemStr = (parseInt(avaMemStr.toUpperCase().replace(/(( )|(:)|[A-Z])/gi,''))/1024).toFixed(0)
console.log(totalMemStr);
console.log(avaMemStr);
return {total:totalMemStr,ava:avaMemStr}
}
getMemInfo();
getMemInfo返回一个对象,total是总内存,ava是剩余内存,单位为MB。
顺带自己的Github项目,一个前端跨平台方法兼容库
Sh.js github地址

集成环信聊天,目前可以发送和接接收语音、图片、文本功能
第一步首先要集成环信。要注册,我是使用的sdk3,开始的看其他人写的,但是我自己用的时候全都是坑,弄了半天也没有成功,最后干脆自己看文档,自己去实践。实践才是验证真理的唯一标准啊。
注册```javascript
var options = {
username: 用户名,
password: "密码",
nickname: ‘名称’,
appKey: WebIM.config.appkey,
success: function(result) {
//注册成功;
console.log(JSON.stringify(result));
mui.toast('注册成功');
},
error: function(e) {
//注册失败;
console.log(JSON.stringify(e));
mui.toast('注册失败:' + e.error);
},
apiUrl: WebIM.config.apiURL
};
var conn = new WebIM.connection();
conn.registerUser(options);
然后就是集成聊天了看代码首先写布局,这里大家要是写聊天的话一般都应该把布局写完了,我就在这里不写了。这个是我们接收 消息```javascript
/**
* 环信监听获取信息
*/
conn.listen({
onOpened: function(message) { //连接成功回调
// 如果isAutoLogin设置为false,那么必须手动设置上线,否则无法收消息
// 手动上线指的是调用conn.setPresence(); 如果conn初始化时已将isAutoLogin设置为true
// 则无需调用conn.setPresence();
},
onClosed: function(message) {}, //连接关闭回调
onTextMessage: function(message) {
console.log(JSON.stringify(message));
console.log(JSON.stringify(message.data));
var msg = message.data;
pushMessageToJson('receiver', 'text', msg);
msgShow('receiver', 'text', msg);
msgScrollTop();
}, //收到文本消息
onEmojiMessage: function(message) {}, //收到表情消息
onPictureMessage: function(message) {
console.log(JSON.stringify(message));
var options = {
url: message.url
};
options.onFileDownloadComplete = function(data) {
console.log(JSON.stringify(data));
console.log(data);
}
options.onFileDownloadError = function() {
// 图片下载失败
console.log('Image download failed!');
};
WebIM.utils.download.call(conn, options);
msgShow('receiver', 'img', options.url);
pushMessageToJson("receiver", "img", options.url);
msgScrollTop();
}, //收到图片消息
onCmdMessage: function(message) {}, //收到命令消息
onAudioMessage: function(message) {}, //收到音频消息
onLocationMessage: function(message) {}, //收到位置消息
onFileMessage: function(message) {}, //收到文件消息
onVideoMessage: function(message) {
var node = document.getElementById('privateVideo');
var option = {
url: message.url,
headers: {
'Accept': 'audio/mp4'
},
onFileDownloadComplete: function(response) {
var objectURL = WebIM.utils.parseDownloadResponse.call(conn, response);
node.src = objectURL;
},
onFileDownloadError: function() {
console.log('File down load error.')
}
};
WebIM.utils.download.call(conn, option);
}, //收到视频消息
onPresence: function(message) {}, //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息
onRoster: function(message) {}, //处理好友申请
onInviteMessage: function(message) {}, //处理群组邀请
onOnline: function() {}, //本机网络连接成功
onOffline: function() {}, //本机网络掉线
onError: function(message) {}, //失败回调
onBlacklistUpdate: function(list) { //黑名单变动
// 查询黑名单,将好友拉黑,将好友从黑名单移除都会回调这个函数,list则是黑名单现有的所有好友信息
console.log(list);
},
onReceivedMessage: function(message) {}, //收到消息送达服务器回执
onDeliveredMessage: function(message) {}, //收到消息送达客户端回执
onReadMessage: function(message) {}, //收到消息已读回执
onCreateGroup: function(message) {}, //创建群组成功回执(需调用createGroupNew)
onMutedMessage: function(message) {} //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员
});
```发送文本消息var sendText = function() {
var msg = ui.boxMsgText.value.replace(new RegExp('\n', 'gm'), '<br/>');
var validateReg = /^\S+$/;
//获取键盘焦点
msgTextFocus();
if(validateReg.test(msg)) {
//显示消息
msgShow('sender', 'text', msg);
//发送文本给第三方服务器,
sendPrivateText(msg);
//todo存储信息到本地
pushMessageToJson('sender', 'text', msg);
//清空文本
ui.boxMsgText.value = "";
//恢复高度
ui.footer.style.height = '50px';
//回复输入内容
mui.trigger(ui.boxMsgText, 'input', null);
// 这一句让内容滚动起来
msgScrollTop();
} else {
mui.toast("文本消息不能为空");
}
}
发送语音消息var sendAudio = function(audioData, filename) {
var id = conn.getUniqueId(); // 生成本地消息id
var msg = new WebIM.message('audio', id); // 创建图片消息
var blob = dataURLtoBlob(audioData);
var url = window.URL.createObjectURL(blob);
var input = blobToFile(blob, filename);
var urlPath = window.URL.createObjectURL(input);
var file = {
url: urlPath,
filename: '',
filetype: '',
data: input
}
var allowType = {
'mp3': true,
'amr': true,
'wmv': true
};
file.filename = input.name || '';
var index = file.filename.lastIndexOf('.');
if(index != -1) {
file.filetype = file.filename.substring(index + 1).toLowerCase();
}
if(file.filetype.toLowerCase() in allowType) {
var option = {
apiUrl: WebIM.config.apiURL,
file: file,
to: '13269698989', // 接收消息对象
roomType: false,
chatType: 'singleChat',
onFileUploadError: function() { // 消息上传失败
console.log('onFileUploadError');
},
onFileUploadComplete: function() { // 消息上传成功
console.log('onFileUploadComplete');
},
success: function() { // 消息发送成功
console.log('Success');
},
flashUpload: WebIM.flashUpload
};
msg.set(option);
conn.send(msg.body);
}
}
发送图片消息
/**
* 发送图片
* @param {Object} path
*/
var sendPrivateImg = function(imgData, path, filename, filetype) {
var id = conn.getUniqueId(); // 生成本地消息id
var msg = new WebIM.message('img', id); // 创建图片消息
var blob = dataURLtoBlob(imgData);
var url = window.URL.createObjectURL(blob);
var input = blobToFile(blob, filename);
var urlPath = window.URL.createObjectURL(input);
console.log('path:' + urlPath);
var file = {
url: urlPath,
filename: filename,
filetype: filetype,
data: input
}
file.url = window.URL.createObjectURL(input);
file.filename = input.name || '';
var index = file.filename.lastIndexOf('.');
if(index != -1) {
file.filetype = file.filename.substring(index + 1).toLowerCase();
}
var allowType = {
'jpg': true,
'gif': true,
'png': true,
'bmp': true
};
if(file.filetype.toLowerCase() in allowType) {
var option = {
apiUrl: WebIM.config.apiURL,
file: file,
to: '13269698989', // 接收消息对象
roomType: false,
chatType: 'singleChat',
onFileUploadError: function() { // 消息上传失败
console.log('onFileUploadError');
plus.nativeUI.closeWaiting();
msgScrollTop();
},
onFileUploadComplete: function() { // 消息上传成功
console.log('onFileUploadComplete');
plus.nativeUI.closeWaiting();
msgScrollTop();
},
success: function() { // 消息发送成功
console.log('Success');
plus.nativeUI.closeWaiting();
msgScrollTop();
},
flashUpload: WebIM.flashUpload
};
msg.set(option);
conn.send(msg.body);
}
}
第一步首先要集成环信。要注册,我是使用的sdk3,开始的看其他人写的,但是我自己用的时候全都是坑,弄了半天也没有成功,最后干脆自己看文档,自己去实践。实践才是验证真理的唯一标准啊。
注册```javascript
var options = {
username: 用户名,
password: "密码",
nickname: ‘名称’,
appKey: WebIM.config.appkey,
success: function(result) {
//注册成功;
console.log(JSON.stringify(result));
mui.toast('注册成功');
},
error: function(e) {
//注册失败;
console.log(JSON.stringify(e));
mui.toast('注册失败:' + e.error);
},
apiUrl: WebIM.config.apiURL
};
var conn = new WebIM.connection();
conn.registerUser(options);
然后就是集成聊天了看代码首先写布局,这里大家要是写聊天的话一般都应该把布局写完了,我就在这里不写了。这个是我们接收 消息```javascript
/**
* 环信监听获取信息
*/
conn.listen({
onOpened: function(message) { //连接成功回调
// 如果isAutoLogin设置为false,那么必须手动设置上线,否则无法收消息
// 手动上线指的是调用conn.setPresence(); 如果conn初始化时已将isAutoLogin设置为true
// 则无需调用conn.setPresence();
},
onClosed: function(message) {}, //连接关闭回调
onTextMessage: function(message) {
console.log(JSON.stringify(message));
console.log(JSON.stringify(message.data));
var msg = message.data;
pushMessageToJson('receiver', 'text', msg);
msgShow('receiver', 'text', msg);
msgScrollTop();
}, //收到文本消息
onEmojiMessage: function(message) {}, //收到表情消息
onPictureMessage: function(message) {
console.log(JSON.stringify(message));
var options = {
url: message.url
};
options.onFileDownloadComplete = function(data) {
console.log(JSON.stringify(data));
console.log(data);
}
options.onFileDownloadError = function() {
// 图片下载失败
console.log('Image download failed!');
};
WebIM.utils.download.call(conn, options);
msgShow('receiver', 'img', options.url);
pushMessageToJson("receiver", "img", options.url);
msgScrollTop();
}, //收到图片消息
onCmdMessage: function(message) {}, //收到命令消息
onAudioMessage: function(message) {}, //收到音频消息
onLocationMessage: function(message) {}, //收到位置消息
onFileMessage: function(message) {}, //收到文件消息
onVideoMessage: function(message) {
var node = document.getElementById('privateVideo');
var option = {
url: message.url,
headers: {
'Accept': 'audio/mp4'
},
onFileDownloadComplete: function(response) {
var objectURL = WebIM.utils.parseDownloadResponse.call(conn, response);
node.src = objectURL;
},
onFileDownloadError: function() {
console.log('File down load error.')
}
};
WebIM.utils.download.call(conn, option);
}, //收到视频消息
onPresence: function(message) {}, //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息
onRoster: function(message) {}, //处理好友申请
onInviteMessage: function(message) {}, //处理群组邀请
onOnline: function() {}, //本机网络连接成功
onOffline: function() {}, //本机网络掉线
onError: function(message) {}, //失败回调
onBlacklistUpdate: function(list) { //黑名单变动
// 查询黑名单,将好友拉黑,将好友从黑名单移除都会回调这个函数,list则是黑名单现有的所有好友信息
console.log(list);
},
onReceivedMessage: function(message) {}, //收到消息送达服务器回执
onDeliveredMessage: function(message) {}, //收到消息送达客户端回执
onReadMessage: function(message) {}, //收到消息已读回执
onCreateGroup: function(message) {}, //创建群组成功回执(需调用createGroupNew)
onMutedMessage: function(message) {} //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员
});
```发送文本消息var sendText = function() {
var msg = ui.boxMsgText.value.replace(new RegExp('\n', 'gm'), '<br/>');
var validateReg = /^\S+$/;
//获取键盘焦点
msgTextFocus();
if(validateReg.test(msg)) {
//显示消息
msgShow('sender', 'text', msg);
//发送文本给第三方服务器,
sendPrivateText(msg);
//todo存储信息到本地
pushMessageToJson('sender', 'text', msg);
//清空文本
ui.boxMsgText.value = "";
//恢复高度
ui.footer.style.height = '50px';
//回复输入内容
mui.trigger(ui.boxMsgText, 'input', null);
// 这一句让内容滚动起来
msgScrollTop();
} else {
mui.toast("文本消息不能为空");
}
}
发送语音消息var sendAudio = function(audioData, filename) {
var id = conn.getUniqueId(); // 生成本地消息id
var msg = new WebIM.message('audio', id); // 创建图片消息
var blob = dataURLtoBlob(audioData);
var url = window.URL.createObjectURL(blob);
var input = blobToFile(blob, filename);
var urlPath = window.URL.createObjectURL(input);
var file = {
url: urlPath,
filename: '',
filetype: '',
data: input
}
var allowType = {
'mp3': true,
'amr': true,
'wmv': true
};
file.filename = input.name || '';
var index = file.filename.lastIndexOf('.');
if(index != -1) {
file.filetype = file.filename.substring(index + 1).toLowerCase();
}
if(file.filetype.toLowerCase() in allowType) {
var option = {
apiUrl: WebIM.config.apiURL,
file: file,
to: '13269698989', // 接收消息对象
roomType: false,
chatType: 'singleChat',
onFileUploadError: function() { // 消息上传失败
console.log('onFileUploadError');
},
onFileUploadComplete: function() { // 消息上传成功
console.log('onFileUploadComplete');
},
success: function() { // 消息发送成功
console.log('Success');
},
flashUpload: WebIM.flashUpload
};
msg.set(option);
conn.send(msg.body);
}
}
发送图片消息
/**
* 发送图片
* @param {Object} path
*/
var sendPrivateImg = function(imgData, path, filename, filetype) {
var id = conn.getUniqueId(); // 生成本地消息id
var msg = new WebIM.message('img', id); // 创建图片消息
var blob = dataURLtoBlob(imgData);
var url = window.URL.createObjectURL(blob);
var input = blobToFile(blob, filename);
var urlPath = window.URL.createObjectURL(input);
console.log('path:' + urlPath);
var file = {
url: urlPath,
filename: filename,
filetype: filetype,
data: input
}
file.url = window.URL.createObjectURL(input);
file.filename = input.name || '';
var index = file.filename.lastIndexOf('.');
if(index != -1) {
file.filetype = file.filename.substring(index + 1).toLowerCase();
}
var allowType = {
'jpg': true,
'gif': true,
'png': true,
'bmp': true
};
if(file.filetype.toLowerCase() in allowType) {
var option = {
apiUrl: WebIM.config.apiURL,
file: file,
to: '13269698989', // 接收消息对象
roomType: false,
chatType: 'singleChat',
onFileUploadError: function() { // 消息上传失败
console.log('onFileUploadError');
plus.nativeUI.closeWaiting();
msgScrollTop();
},
onFileUploadComplete: function() { // 消息上传成功
console.log('onFileUploadComplete');
plus.nativeUI.closeWaiting();
msgScrollTop();
},
success: function() { // 消息发送成功
console.log('Success');
plus.nativeUI.closeWaiting();
msgScrollTop();
},
flashUpload: WebIM.flashUpload
};
msg.set(option);
conn.send(msg.body);
}
}
收起阅读 »

mui 下拉刷新回到头部位置解决办法
//自动滚动到头部位置#refreshContainer = '你的滚动区域id',scrollTo(x,y,'滚动时间毫秒数');
mui('#refreshContainer').pullRefresh().scrollTo(0, 0, 500);
//自动滚动到头部位置#refreshContainer = '你的滚动区域id',scrollTo(x,y,'滚动时间毫秒数');
mui('#refreshContainer').pullRefresh().scrollTo(0, 0, 500);