SegmentFault 又踏杨花过谢桥最新的文章
2017-07-17T16:42:20+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
关于javascript sort()排序的一点理解
https://segmentfault.com/a/1190000010222058
2017-07-17T16:42:20+08:00
2017-07-17T16:42:20+08:00
黑阔大人
https://segmentfault.com/u/viiiii
2
<h2>sort()排序的原理</h2>
<p>最近在leetcode刷题的时候遇到一个排序问题之前一直都忽略了sort排序的原理,让我们看下w3c对于sort()的说明:<br>如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照<code>字符编码</code>的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。<br>这样就可以解释为什么<code>[0,1,5,10,8]</code>的升序排列会是<code>[0,1,10,5,8]</code>了<br><img src="/img/bVQ3br?w=902&h=386" alt="clipboard.png" title="clipboard.png"></p>
<h2>sort()排序参数的使用</h2>
<p>w3c对于参数的使用还有如下一段说明:<br>如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:</p>
<ul>
<li><p>若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。</p></li>
<li><p>若 a 等于 b,则返回 0。</p></li>
<li><p>若 a 大于 b,则返回一个大于 0 的值。</p></li>
</ul>
<p>所以得出如果想要升序排列的话通过a-b就可以实现 <code>sort((a,b)=>a-b)</code>,反序也可以通过这个来做到:</p>
<p><img src="/img/bVQ3nF?w=538&h=290" alt="clipboard.png" title="clipboard.png"></p>
我为什么要升级到Ionic3
https://segmentfault.com/a/1190000009465902
2017-05-18T14:52:04+08:00
2017-05-18T14:52:04+08:00
黑阔大人
https://segmentfault.com/u/viiiii
2
<p>2017年4月5日 ionic3正式发布了,对于从ionic2 rc版本开始用的我也进行了这次升级并且平稳运行在项目上,在我看来升级ionic3是很有必要的</p>
<h2>Ionic3带来的新特性</h2>
<ul>
<li><p><strong>Angular 4.0</strong><br> 新的版本下,改进 AOT 编译器,分离 animations 包,缩小生成后的代码量,运行更快,改进 <em>ngIf 和 </em>ngFor 等具体内容可以访问<a href="https://link.segmentfault.com/?enc=FrLh%2Blro8ZkGIS41VgSA4Q%3D%3D.LZ%2Bgf%2FdB4ka3P3QiNfwEFcmhnCjrHJLVfHKR3NInzanHh%2BlNe14u4yW%2Fu7RkLc9Ia8B%2BCQgT7pA9tPjs6IcQ3TNOqqi2L5XoaSUAlLY%2Ba7w%3D" rel="nofollow">angular4更新</a>来查看。</p></li>
<li><p><strong>typescript 2.1, 2.2的支持</strong><br> 这一次的更新将提升typescript应用构建和类型检查的速度并且引入了对mix-in的支持等具体可以访问<a href="https://link.segmentfault.com/?enc=jJNIkRVi5qW%2BrPGccJfjBQ%3D%3D.aU9ddrZk%2FJRfjkcy7n6N12jk7x75HSvDD5OnzS27SgRXdsp87tPuuPEulae%2F%2B%2FT8i6ryI3bFPNzK4GSHiDJBYJKKq51Krwf3Zt3S0qTe3xM%3D" rel="nofollow">TypeScript release notes</a>来查看。</p></li>
<li><p><strong>@IonicPage装饰器</strong><br> ionic2中导航器不是基于url的,如果想使用url访问就要通过DeepLinker来实现,这是非常麻烦的,而在新版本我们可以通过@IonicPage装饰器来实现。并且可以更轻松的在项目中设置延迟加载,设置延迟加载页面的优先级,并为每个页面自定义配置。</p></li>
<li><p><strong>懒加载</strong><br> Ionic3.0版本开始,支持了延迟加载,我们可以将某些模块设置为延时加载,只有用户打开相关的页面的时候,这个模块所在的js才会被下载,这样能减少用户初次下载的文件的大小。</p></li>
</ul>
<p>总的来说,升级<code>Ionic3</code>将使我们的项目变得更小,更快,而更吸引我的则是懒加载,不仅仅是加快了app首次的启动时间,更多的是配合上<code>@IonicPage</code>可以非常方便部署web版本,让每次进入不用去请求庞大的js文件,做到首屏的快速加载,<code>write once run anywhere</code>,这些就是我升级<code>Ionic3</code>的原因。</p>
<h2>怎样升级到Ionic3</h2>
<ol>
<li><p>首先访问<a href="https://link.segmentfault.com/?enc=JwpPoGJkLKjh5sBs9NISnQ%3D%3D.oNjJPeUyP0lLSCIRaZzjL7zuJdXS3Z9IpoWWaZ76FQeShCr%2FqFHUzv0dAuj8RSh5" rel="nofollow">ionic-app-base</a>复制<code>package.json</code>的<code>dependencies</code>和<code>devDependencies</code>到自己的项目中后删除掉原本的<code>node_modules</code>文件夹,运行<code>npm install</code>重新下载依赖。</p></li>
<li><p>将BrowserModule加入你的app/app.module.ts <code>import { BrowserModule } from '@angular/platform-browser';</code></p></li>
<li><p>在app.module.ts中将BrowserModule添加进imports中。<br> imports: [ BrowserModule, IonicModule.forRoot(MyApp) ],</p></li>
<li><p>由于ionic3将ionic-native拆开成个各种小的包@ionic-native/*,<code>splash-screen</code>,<code>status-bar</code>等之前ionic-native中的模块都需要重新引入具体可以参照<a href="https://link.segmentfault.com/?enc=ODY2qx8A8l7iX%2FqzETu%2FKg%3D%3D.vAeTi5AcHk5iECC6Dzpxd9mCbpEObYnmOicPbkqdIfB4LBU8rRLYF8kzvPagdPRF" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=RU4Jf5oYv4E5TjJaZ77sHQ%3D%3D.xDWEa%2FMPHoqSLre%2FCaRJdg0m0IP8U0lohrVSsWnEO9jml%2B2%2Btnbk9dgjRJfUt4sl" rel="nofollow">http://ionicframework.com/doc...</a>来对号入座。</p></li>
</ol>
<p>最后运行<code>ionic serve</code>开始享受ionic3带来的改变吧!</p>
ionic2 接入友盟统计
https://segmentfault.com/a/1190000008557664
2017-03-03T15:16:25+08:00
2017-03-03T15:16:25+08:00
黑阔大人
https://segmentfault.com/u/viiiii
1
<h2>SDK下载</h2>
<p>ionic2是基于cordova来实现移动app开发的,因此我们需要先下载友盟的cordova sdk:<a href="https://link.segmentfault.com/?enc=x94J%2FOAvqN6L12F5qqwlOQ%3D%3D.XbEJIOBkk7vFm2%2FKtCaobKASOXktf7ZI%2F2AXsAebitdcYccUDMJ%2FueraYF5ZA5x2" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=tQsppyJ6%2Fm9BGWXeAeUhpw%3D%3D.uQ1PdLAyqUtmjvHldf1gQbN7SsHCom4zWh2LgePhuKtRxTuDSqMTliLmKEEy6Ql9" rel="nofollow">http://dev.umeng.com/analytic...</a></p>
<p><img src="/img/bVJAR7?w=1023&h=364" alt="clipboard.png" title="clipboard.png"></p>
<h2>Android配置</h2>
<p>访问umeng-plugin-android/plugin.xml文件将UMENG_APPKEY 和 UMENG_CHANNEL设置为你友盟账号的对应信息。<br><img src="/img/bVJAYi?w=1308&h=182" alt="clipboard.png" title="clipboard.png"><br>运行:<code>cordova plugins add */umeng-plugin-android/</code>将插件集成进来。<br>在app.component.ts中通过</p>
<pre><code>let mobclickAgent = (<any>window).MobclickAgent;
if(mobclickAgent){
mobclickAgent.init();
mobclickAgent.setDebugMode(false);
}</code></pre>
<p>来进行初始化</p>
<h2>IOS配置</h2>
<p>在进行sdk的下载后进行集成,由于plugin的名字相同,因此可以手动将ios的plugin添加进去。<br>打开plugins文件夹找到Umeng的插件所在目录<br><img src="/img/bVJAZQ?w=462&h=272" alt="clipboard.png" title="clipboard.png"><br>先将ios sdk中plugin.xml的ios部分<platform name="ios">...</platform>copy到进插件所在的plugin.xml</p>
<p><img src="/img/bVJA0t?w=1464&h=1586" alt="clipboard.png" title="clipboard.png"><br>然后将下载的iossdk中src的ios模块copy到已经集成进去的Umeng文件夹下的src中如下图所示<br><img src="/img/bVJ39f?w=574&h=786" alt="clipboard.png" title="clipboard.png"><br>然后在platforms/ios/你的项目/Classes/AppDelegate.m文件中<br>导入<code>#import "UMMobClick/MobClick.h"</code><br>然后找到方法 (BOOL)application:(UIApplication<em>)application didFinishLaunchingWithOptions:(NSDictionary</em>)launchOptions,添加下面的代码:</p>
<pre><code> UMConfigInstance.appKey = @"Your Appkey";
UMConfigInstance.channelId = @"Your ChannelId";"
UMConfigInstance.eSType=E_UM_GAME;//友盟游戏统计,如不设置默认为应用统计
[MobClick startWithConfigure:UMConfigInstance];</code></pre>
<p>如下图按项目实际情况填写参数</p>
<p><img src="/img/bVJ4fZ?w=2042&h=1468" alt="clipboard.png" title="clipboard.png"></p>
<p>至此友盟SDK接入成功</p>
<p><img src="/img/bVJ4om?w=1307&h=941" alt="clipboard.png" title="clipboard.png"></p>
Ionic2 分享(微信分享+QQ分享+复制到剪贴板+微博分享)
https://segmentfault.com/a/1190000008263093
2017-02-06T14:14:06+08:00
2017-02-06T14:14:06+08:00
黑阔大人
https://segmentfault.com/u/viiiii
1
<h2>ionic2 集成微信sdk</h2>
<p>github地址:<a href="https://link.segmentfault.com/?enc=VMJunMjyNm56atLsXEiITQ%3D%3D.g9xI4zwgXG%2FVggi8TsvuJB838xlM04d9LuEajSoBocgs%2FPWyRulEBhgrjYd2HjPw" rel="nofollow">https://github.com/xu-li/cord...</a><br>使用示例:<br>cordova plugin add cordova-plugin-wechat --variable wechatappid=YOUR_WECHAT_APPID(你的微信开放平台id)<br>cordova build ios or cordova build android<br>分享朋友圈:(其中toastService是我自己封装的toast方法替换成自己对应的方法就可以了)</p>
<pre><code>shareWxSession(){
let wechat = (<any>window).Wechat;
wechat.isInstalled(function (installed) {
if(!installed){
this.toastService.show('您没有安装微信!');
return ;
}
}, function (reason) {
this.toastService.show("Failed: " + reason);
});
wechat.share({
message: {
title: this.shareImg,
description: this.shareDesc,
thumb: this.shareImg,
media: {
type: wechat.Type.LINK,
webpageUrl: this.shareUrl
}
},
scene: wechat.Scene.SESSION // share to SESSION
}, function () {
this.toastService.show('分享成功');
}, function (reason) {
console.log("Failed: " + reason);
});
}</code></pre>
<p>分享微信好友:</p>
<pre><code>shareWxTimeLine(){
let wechat = (<any>window).Wechat;
wechat.isInstalled(function (installed) {
if(!installed){
this.toastService.show('您没有安装微信!');
return ;
}
}, function (reason) {
this.toastService.show("Failed: " + reason);
});
wechat.share({
message: {
title: this.shareImg,
description: this.shareDesc,
thumb: this.shareImg,
media: {
type: wechat.Type.LINK,
webpageUrl: this.shareUrl
}
},
scene: wechat.Scene.TIMELINE // share to Timeline
}, function () {
this.toastService.show('分享成功','bottom',4000);
}, function (reason) {
console.log("Failed: " + reason);
});
}
</code></pre>
<h2>ionic2集成QQsdk</h2>
<p>github地址:<a href="https://link.segmentfault.com/?enc=0midr7q6pY31e578zKiVZw%3D%3D.whEg6w8afB0taqiU9rkhNQqgCEv16LUK7MOZwTFjYrOkZEEsO2N951bJEFPKkF6k" rel="nofollow">https://github.com/iVanPan/Co...</a><br>cordova plugin add <a href="https://link.segmentfault.com/?enc=QkL54tAlyn4HTsafUwfhXQ%3D%3D.xqFDNc3uP9WZkP84ZdvnrI7qTsxEKyg9Bq8P0kQuXX4mjIcTm9vows3EbRuoE%2BYI" rel="nofollow">https://github.com/iVanPan/Co...</a> --variable QQ_APP_ID=YOUR_QQ_APPID (QQ开放平台appid)<br>分享QQ:</p>
<pre><code>shareQQ(){
let qq = (<any>window).QQSDK;
let that = this;
qq.checkClientInstalled(function () {
var args:any = {};
args.scene = qq.Scene.QQ;//QQSDK.Scene.QQZone,QQSDK.Scene.Favorite
args.url = that.shareUrl;
args.title = that.shareTitle;
args.description = that.shareDesc;
args.image = that.shareImg;
qq.shareNews(function () {
this.toastService.show('分享成功');
}, function (failReason) {
// alert(failReason);
},args);
}, function () {
// if installed QQ Client version is not supported sso,also will get this error
this.toastService.show('您没有安装QQ!');
});
}</code></pre>
<p>分享QQ空间:</p>
<pre><code> shareQZone(){
let qq = (<any>window).QQSDK;
let that = this;
qq.checkClientInstalled(function () {
var args:any = {};
args.scene = qq.Scene.QQZone;//QQSDK.Scene.QQZone,QQSDK.Scene.Favorite
args.url = that.shareUrl;
args.title = that.shareTitle;
args.description = that.shareDesc;
args.image = that.shareImg;
qq.shareNews(function () {
this.toastService.show('分享成功','bottom',4000);
}, function (failReason) {
// alert(failReason);
},args);
}, function () {
// if installed QQ Client version is not supported sso,also will get this error
this.toastService.show('您没有安装QQ!');
});
}
</code></pre>
<h2>复制到剪贴板</h2>
<p>文档地址:<a href="https://link.segmentfault.com/?enc=nfrqTeKC38VmUicq%2BzGtSA%3D%3D.8M%2F98jXR%2FIyDIRHSctuhX%2FhsQkCyAOZrc0MsBN7pmi6PUwaDCkb0JHI5kenSjWnQPzhRNyZfXnSqi893Yehr4Q%3D%3D" rel="nofollow">http://ionicframework.com/doc...</a><br>添加Clipboard的插件:ionic plugin add <a href="https://link.segmentfault.com/?enc=zKMLv%2FvafDQcOFMXBQvCwA%3D%3D.9mvin4oCwv%2FrBglTkvvwtzEkmZKamOGSNfyFtGKZWzIeNVT6rClKTO%2BbGZ4wE4ACq5b8e4PB7G6rmA%2B5i36LHQ%3D%3D" rel="nofollow">https://github.com/VersoSolut...</a><br>使用方法:</p>
<pre><code> clipboard() {
Clipboard.copy(this.shareUrl);
this.toastService.show('已复制到剪贴板');
}</code></pre>
<h2>微博分享</h2>
<p>github地址:<a href="https://link.segmentfault.com/?enc=Y6iXYohWgSTRAy42c8oIxw%3D%3D.1OnYxtNOOfZ3YKs%2BhD2YwvU4yTVZ6G8w263BbN4F8ZF7ZayT0DQISTo%2BVaNdz85Z" rel="nofollow">https://github.com/iVanPan/co...</a></p>
<pre><code>cordova plugin add https://github.com/iVanPan/cordova_weibo.git --variable WEIBO_APP_ID=YOUR_WEIBO_APPID or cordova plugin add cordova-plugin-weibosdk --variable WEIBO_APP_ID=YOUR_WEIBO_APPID</code></pre>
<p>使用方法:</p>
<pre><code>shareWb(){
let wb = (<any>window).YCWeibo;
let that = this;
wb.checkClientInstalled(function(){
var args:any = {};
args.url = that.shareUrl;
args.title = that.shareTitle;
args.description = that.shareDesc;;
args.imageUrl = that.shareImg;//if you don't have imageUrl,for android http://www.sinaimg.cn/blog/developer/wiki/LOGO_64x64.png will be the defualt one
args.defaultText = "";
wb.shareToWeibo(function () {
this.toastService.show('分享成功','bottom',4000);
}, function (failReason) {
// console.log(failReason);
}, args);
}
,function(){
this.toastService.show('您没有安装Weibo!');
});
}`</code></pre>
ReactNative WebView高度自适应
https://segmentfault.com/a/1190000007502470
2016-11-16T17:08:33+08:00
2016-11-16T17:08:33+08:00
黑阔大人
https://segmentfault.com/u/viiiii
3
<p>在ReactNative项目中可能会遇到展示HTML代码的情况,通常我们会采用WebView来展示html代码,但ReactNative中的WebView需要设定高度才能展示出来,因此需要用js来计算文档高度做到高度自适应<br>具体代码实现如下:</p>
<pre><code>//这里我在初始化阶段定义初使高度
constructor(props) {
super(props);
this.state={
height:500,
}
}
//下面是WebView的代码。`${}`这个ES6中新加入的特性,允许通过反引号 ` 来创建字符串
//获取高度原理是当文档加载完后js获取文档高度然后添加到title标签中。这时通过监听导航状态变化的函数 `onNavigationStateChange` 来将title的值读取出来赋值给this.state.height从而使webview的高度做到自适应。
<View style={{height:this.state.height}}>
<WebView
source={{html: `<!DOCTYPE html><html><body>${htmlContent}<script>window.onload=function(){window.location.hash = 1;document.title = document.body.clientHeight;}</script></body></html>`}}
style={{flex:1}}
bounces={false}
scrollEnabled={false}
automaticallyAdjustContentInsets={true}
contentInset={{top:0,left:0}}
onNavigationStateChange={(title)=>{
if(title.title != undefined) {
this.setState({
height:(parseInt(title.title)+20)
})
}
}}
>
</WebView>
</View></code></pre>
<p>S61116-171231.jpg<br><img src="/img/bVFDVE?w=1536&h=2560" alt="clipboard.png" title="clipboard.png"></p>
移动端从入坑到出坑——2016.8.2 更新
https://segmentfault.com/a/1190000006091031
2016-07-27T17:14:42+08:00
2016-07-27T17:14:42+08:00
黑阔大人
https://segmentfault.com/u/viiiii
3
<h2>1.局部滚动</h2>
<p>项目名称:果之友<br>地址:<a href="https://link.segmentfault.com/?enc=sZz785WZEERLhpLWdkkYSg%3D%3D.10QCMh4iSPQhvv2APSF61SL%2FSIgYXL5zz3ycsV11kx80kZGfhAkPzyPenufdBYsAp%2FQG2yYutUAgLNx0hNnQhw%3D%3D" rel="nofollow">http://www.guozhiyou.com/Inde...</a><br>问题描述:要将红色的这块做局部滚动。<br><img src="/img/bVydDA" alt="clipboard.png" title="clipboard.png"><br>解决办法:先通过<code>window.innerHeight</code>读取到当前屏幕的高度,然后通过减去header和footer的高度,得到div的高度并且赋值上去,同时设置<code>overflow:scroll</code>,成功实现弹性滚动。但设置为局部滚动后会发现ios端和android端失去了滚动弹性,这里我们采取下面css来使滚动条恢复弹性。</p>
<pre><code>overflow:auto;/* android4+ */
-webkit-overflow-scrolling: touch; /* ios5+ */
</code></pre>
<h2>2.移动端页面Input虚拟键盘弹出Fixed元素跟随页面滚动,fixed属性失效</h2>
<p>bug页面布局如下:</p>
<pre><code>index.html
<!-- fixed定位的头部 -->
<div class="wrap">
<header>
</header>
<!-- 可以滚动的区域 -->
<main>
<div ng-repeat="index in vm.arr">
{{index}}
</div>
</main>
<!-- fixed定位的底部 -->
<footer>
</footer>
</div>
</code></pre>
<pre><code> index.css
//px计算
@function px2rem($px) {
$rem: 75px;
@return ($px/$rem) + rem;
}
*{
margin: 0;
padding: 0;
}
html,body{
height:100%;
width:100%;
}
.wrap{
width:100%;
height:100%;
font-size: px2rem(32px);
}
header {
position: fixed;
height: px2rem(100px);
line-height:px2rem(100px);
left: 0;
right: 0;
top: 0;
background-color: #efefef;
}
footer {
position: fixed;
height: px2rem(80px);
left: 0;
right: 0;
bottom: 0;
background-color: #efefef;
input{
height:px2rem(60px);
margin-top:px2rem(10px);
}
}
main {
margin-top: px2rem(100px);
margin-bottom: px2rem(80px);
// height: px2rem(2000px);
font-size:px2rem(36px);
}</code></pre>
<p>在input虚拟键盘未触发时fixed属性正常,如下图:<br><img src="/img/bVzHcb" alt="clipboard.png" title="clipboard.png"><br>然后激活虚拟键盘,fixed属性失效,如下图:</p>
<p><img src="/img/bVzHcN" alt="clipboard.png" title="clipboard.png"></p>
<p><img src="/img/bVzHcO" alt="clipboard.png" title="clipboard.png"></p>
<p>解决方法:由于是在虚拟键盘激活后,页面 fixed 元素失效,导致跟随页面一起滚动,那么 如果页面不会过长出现滚动,那么即便 fixed 元素失效,也无法跟随页面滚动,也就不会出现上面的问题了。因此我们用flex布局的方式将body的全局滚动替换为main的局部滚动从而避免整个页面发生滚动。<br>html结构不变更改css如下:</p>
<pre><code>//px计算
@function px2rem($px) {
$rem: 75px;
@return ($px/$rem) + rem;
}
*{
margin: 0;
padding: 0;
}
html,body{
height:100%;
width:100%;
}
.wrap{
display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;
-webkit-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;
width:100%;
height:100%;
font-size: px2rem(32px);
}
header {
width:100%;
height: px2rem(100px);
line-height:px2rem(100px);
background-color: #efefef;
}
footer {
width:100%;
height: px2rem(80px);
background-color: #efefef;
input{
height:px2rem(60px);
margin-top:px2rem(10px);
}
}
main {
-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;
overflow:auto;
-webkit-overflow-scrolling: touch; //为局部滚动恢复弹性
}
</code></pre>
<p><img src="/img/bVzIGa" alt="clipboard.png" title="clipboard.png"><img src="/img/bVzIIu" alt="图片描述" title="图片描述"><br>如上图,虚拟键盘弹出后页面仍然正常。<br>后续还会持续更新,移动端遇到bug的小伙伴们也欢迎在评论中提出,会尽力帮助解决。</p>
<h2>3.移动端rem适配后横竖屏切换时页面过大或者过小</h2>
<p>解决方法:监听orientationchange当横竖屏切换后重新计算html的fontSize值。</p>
<pre><code> window.addEventListener('orientationchange', function(event){
if ( window.orientation == 180 || window.orientation==0 ) {
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
}
if( window.orientation == 90 || window.orientation == -90 ) {
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
}</code></pre>
ReactNative 踩坑之旅 --2016-11-10更新
https://segmentfault.com/a/1190000005698689
2016-06-12T16:57:01+08:00
2016-06-12T16:57:01+08:00
黑阔大人
https://segmentfault.com/u/viiiii
5
<p>从年中到现在也用ReactNative上线了一个电商项目了,再实战过程中也经历了很多坑,因此记录下来,希望帮助到更多的人。前两个是刚接触React时接触到的,但由于转到负责ReactNative的项目,因此后面记录的都是在ReactNative实战中遇到的问题。</p>
<h2>1.webpack使用babel-loader后编译报错</h2>
<p>报错<br><code>ERROR in ./entry.js Module build failed: SyntaxError: /Users/yixin/Desktop/react/entry.js: Unexpected token (2:8)</code></p>
<p><img src="/img/bVx4Ay?w=477&h=138" alt="clipboard.png" title="clipboard.png"></p>
<p>说是 "<" 这个符号有问题,原因是babel6分离为多个包,并且默认不支持react和es2015。<br>因此除了<code>npm install babel-loader --save-dev</code>以外还需要<br><code>npm install babel-preset-es2015 babel-preset-react --save-dev</code>。<br>然后在loaders中添加:</p>
<pre><code>var babelPresets = {presets: ['react', 'es2015']};
loaders: [
{
test: /\.js|jsx$/,
loaders: ['babel', 'babel-loader?'+JSON.stringify(babelPresets)]
}
]</code></pre>
<p>则可以正常运行。</p>
<h2>2.Target container is not a DOM element</h2>
<pre><code> 找不到dom节点:
Uncaught Invariant Violation: _registerComponent(...): Target container is not a DOM element.
</code></pre>
<p>原因是js在头部引入,由于src是同步下载,然后立即执行,而此时你的渲染树还未构建完毕,因此找不到dom节点。解决方法是在页面底部进行调用。</p>
<h2>3.Super expression must either be null or a function, not undefined</h2>
<p>我是按照之前买的用JavaScript开发移动应用的例子来编写的,然后报了这个错。我的头部声明是这样的</p>
<pre><code>var React = require('react-native');
var {
Text,
View
} = React;</code></pre>
<p>经过查询后是由于'React'和'Component'从'react native'分离到了'react'模块。所以这里我们只引入'react native'的模块是不够的,改成这样:</p>
<pre><code>import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';</code></pre>
<p>成功运行。另外推荐RN的ES5和ES6的语法对照表,作为初学者还是很有必要了解的<br><a href="https://link.segmentfault.com/?enc=%2FE92%2Bq%2BpJYCsZV961BDgjQ%3D%3D.sqfSOH5UXh1q0k8X66ppfOEg2YqIStP8c0SMPlFZMrBa%2Ba0YZhwxz7U0tKJK725%2BSPeQjG06VV2XgphHThdgFwRocIr5xyuvSvjKZ6uUaZ5a736mse5uOoV4iaGhhh8nG6EDlesmUDW%2FVj0CoKIBS4TBYVgQ5n2wxoPrxUZjXpU%3D" rel="nofollow">React/React Native 的ES5 ES6写法对照表</a></p>
<p>后面还会持续更新。。。</p>
<h2>4.ReactNative 安卓环境 Could not resolve all dependencies for configuration ':app:_debugCompile'</h2>
<p>问题如下:</p>
<pre><code>* What went wrong:
A problem occurred configuring project ':app'.
> Could not resolve all dependencies for configuration ':app:_debugCompile'.
> Could not find com.android.support:support-v4:23.2.1.
Searched in the following locations:
file:/Users/yixin/.m2/repository/com/android/support/support-v4/23.2.1/support-v4-23.2.1.pom
file:/Users/yixin/.m2/repository/com/android/support/support-v4/23.2.1/support-v4-23.2.1.jar
</code></pre>
<p>需要在sdk管理器中安装 Android Support Repository模块<br><img src="/img/bVzwjS?w=561&h=25" alt="clipboard.png" title="clipboard.png"></p>
<h2>5.ReactNative 安卓环境 Execution failed for task ':app:installDebug'.</h2>
<p>问题如下:</p>
<pre><code>* What went wrong:
Execution failed for task ':app:installDebug'.
> com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: device '76UBBKT22AZQ' not found
</code></pre>
<p>需要更改android的build.gradle,将第8行gradle的版本号更改为1.2.3<br><code>classpath 'com.android.tools.build:gradle:1.2.3'</code> 就可以正常运行了。</p>
<p><img src="/img/bVzwmD?w=1742&h=942" alt="图片描述" title="图片描述"></p>
<h2>6.安卓模拟器运行报错 emulator: ERROR: x86 emulation currently requires hardware acceleration!Please ensure Intel HAXM is properly installed and usable.CPU acceleration status: HAXM must be updated (version 1.1.5 < 6.0.1).</h2>
<p>解决办法:在sdkTools安装Intel x86 Emulator Accelerator(HAXM installer)的情况下,<br><img src="/img/bVzBdX?w=640&h=34" alt="clipboard.png" title="clipboard.png"><br>访问sdk目录下的extra->intel->Hardware_Accelerated_Execution_Manager下执行intelHAXM.dmg,重启android studio可以正常运行模拟器</p>
<p><img src="/img/bVzCco?w=684&h=555" alt="clipboard.png" title="clipboard.png"></p>
<h2>7.reactNative getInitialState: function() {}报错</h2>
<p>原因:在ES5语法下,React Native 组件的状态变量是在 getInitialState函数中初始化的</p>
<pre><code>let MyComponent = React.createClass({
getInitialState: function() {
return {
scrollTop: new Animated.Value(0),
};
},
});</code></pre>
<p>而在ES6语法下,React Native 团队修改了状态变量的初始化方式,取消了单独的 getInitialState 函数,将初始化放在构造函数中,并提供 this.state实例变量用来存储状态变量。<br>解决办法:</p>
<pre><code>class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
scrollTop: new Animated.Value(0),
};
}
}</code></pre>
<h2>7.React Native在android和ios平台上的差异</h2>
<ol><li><p>当Text组件的fontSize等于height的时候,由于安卓和ios在Text组件中上方都自动留白,这时候会发现底部已经超出了组建范围而被遮挡。如果想设置Text垂直居中的效果建议可以用View做嵌套再通过flex布局来做到垂直居中</p></li></ol>
<pre><code>import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image
} from 'react-native';
var aImage = require('./tab.png');
class reactNative06 extends Component {
render() {
return (
<View style={styles.container}>
<View style={{borderWidth:1,}}>
<Text style={styles.welcome}>
Ajfg你好
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome:{
width:200,
fontSize:50,
borderWidth:1,
textAlign:'center'
}
});
AppRegistry.registerComponent('reactNative06', () => reactNative06);</code></pre>
<ol>
<li><p>borderWidth属性在Android的Text组件上失效,解决办法也如上段代码一样使用View嵌套然后在View组建上添加borderWidth属性</p></li>
<li><p>TextInput组件通过multiline={true}设置为多行后,在iphone手机上TextInput组件的onSubmitEditing事件永远不会被处罚,它的回调函数也不会被执行,而安卓上正常。当TextInput失去焦点的时候建议选择<code>onEndEditing</code>回调函数而不是<code>onBlur</code>。</p></li>
</ol>
<h2>8.TextInput组件文字被遮挡</h2>
<p>先说下TextInput在android和ios两个平台的区别。</p>
<ol>
<li><p>只指定fontSize不指定height:<br>在iOS平台上,没有指定样式中的height键值的TextInput组件不会显示。在android平台上则为正常</p></li>
<li><p>height等于fontSize:<br>android平台上字体的上方有部分会被遮挡,在iOS平台上底部会稍微有点被遮挡,这时候可以设置<code>paddingTop:0,paddingBottom:0</code>来解决。</p></li>
<li><p>height大于fontSize,有时候也会有遮挡,解决办法如上</p></li>
</ol>
<h2>9.安装app报错:signatures do not match the previously installed version; ignoring!</h2>
<p>reactnative新签名的apk在手机上安装不了(是因为之前调试的应用没删干净)<br><img src="/img/bVDfaH?w=563&h=212" alt="clipboard.png" title="clipboard.png"><br>解决办法:<code>adb uninstall "com.pepperrn"</code> 手动通过包名来清理app。<br>然后再安装就正常了</p>
<h2>10.ReactNative调试模式下正常,release版本本地图片显示不出来</h2>
<p>问题描述:ReactNaitve调试模式下运行正常,打包release版本时没将静态资源文件打包进去,导致本地图片加载不出来<br>解决办法:可以将android目录删除掉然后运行<code>react-native android</code>,重新构建安卓目录,再打包恢复正常。</p>
nodejs爬虫实战(一):抽屉新热榜
https://segmentfault.com/a/1190000005343112
2016-05-25T14:15:00+08:00
2016-05-25T14:15:00+08:00
黑阔大人
https://segmentfault.com/u/viiiii
3
<h2>什么是nodeJs</h2>
<p>Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。</p>
<h2>开启我们的第一个nodejs项目</h2>
<p>首先可以去nodejs官网来下载nodejs并安装<a href="https://link.segmentfault.com/?enc=30x0mMhxjap6N3RgE6jdbg%3D%3D.4prM9NrVw8k1%2FArtzGQIW1O953xiIKdpd20lCm%2FW%2BbU%3D" rel="nofollow">http://nodejs.cn/</a>。<br>安装完成后,通过npm来安装我们的express框架 <code>npm install express --save</code>。</p>
<pre><code>//app.js
//引入 `express` 模块
var express = require('express');
//调用 express 实例并将这个变量赋予 app 变量。
var app = express();
// app 本身有很多方法,其中包括最常用的 get、post、put/patch、delete,在这里我们调用其中的 get 方法,为我们的 `/` 路径指定一个 handler 函数。
// 这个 handler 函数会接收 req 和 res 两个对象,他们分别是请求的 request 和 response。
// request 中包含了浏览器传来的各种信息,比如 query 啊,body 啊,headers 啊之类的,都可以通过 req 对象访问到。
// res 对象,我们一般不从里面取信息,而是通过它来定制我们向浏览器输出的信息,比如 header 信息,比如想要向浏览器输出的内容。这里我们调用了它的 #send 方法,向浏览器输出一个字符串。
app.get('/',function(req,res){
res.send('hello world');
})
// 定义好我们 app 的行为之后,让它监听本地的 3000 端口。这里的第二个函数是个回调函数,会在 listen 动作成功后执行,我们这里执行了一个命令行输出操作,告诉我们监听动作已完成。
app.listen(3000, function () {
console.log('app is listening at port 3000');
});
</code></pre>
<p>运行 <code>node app.js</code> 并且访问<a href="https://link.segmentfault.com/?enc=t5kHek3oumo4QD6%2Fyw%2B4WA%3D%3D.1uhu8KmRjeSPBk1NJCmJfiho4qjn9q4iiONlHDzxucg%3D" rel="nofollow">http://localhost:3000/</a>即可看到 <code>hello world</code></p>
<p><img src="/img/bVww2U" alt="clipboard.png" title="clipboard.png"></p>
<h2>爬虫依赖</h2>
<p>所谓 工欲善其事必先利其器,完成nodejs爬虫还需要加两个库:<br>superagent(<a href="https://link.segmentfault.com/?enc=DJ6a1UifQrNQgeySklV50w%3D%3D.7te5kwj9EGu3Fw7w8hB3ka%2FfBFR3Kwo2CXpEZlRxGS0QXyYXfW%2F4I%2F9hdmuZnapI" rel="nofollow">http://visionmedia.github.io/superagent/</a> ) 是个 http 方面的库,可以发起 get 或 post 请求。<br>cheerio(<a href="https://link.segmentfault.com/?enc=RL9%2FJPskFmHhI9H%2B%2Fzlsdw%3D%3D.ZWVWgUU8lkO3SGJkVUc8VyUZLL0UJWNbEf40kHiKpC82kzunOZ5m6n7CSCeEfQtP" rel="nofollow">https://github.com/cheeriojs/cheerio</a> ) 大家可以理解成一个 Node.js 版的 jquery,用来从网页中以 css selector 取数据,使用方式跟 jquery 一样一样的。<br>分别安装<code>npm install superagent --save</code> <code>npm install cheerio --save</code>。</p>
<h2>抓取抽屉新热榜数据</h2>
<p>先从简单的开始,我们来以抽屉新热榜为例子。访问 <a href="https://link.segmentfault.com/?enc=B1b5YhqiNcxUn7dKnTIUIw%3D%3D.WDh%2FqeidXWPuDz8P44QMSoOjUONbVgEu0T2BU6IgYJA%3D" rel="nofollow">http://dig.chouti.com/</a>,通过浏览器的调试器来看抽屉的dom结构,可以看到.part2的类上有用于分享的属性,我们就可以直接通过jquery的语法来读取标题,图片和超链接这几个属性</p>
<p><img src="/img/bVwysk" alt="clipboard.png" title="clipboard.png"><br>可以看到新热榜数据都在 id为'content-list'的div中,于是我们展开content-list来继续查看数据</p>
<p><img src="/img/bVwysz" alt="clipboard.png" title="clipboard.png"><br>发现有个class为'part2'的div里面有 share</p>
<pre><code>app.js
//引入 `express` 模块
var express = require('express');
//引入 `superagent` 库
var superagent = require('superagent');
//引入 `cheerio` 库
var cheerio = require('cheerio');
//调用 express 实例并将这个变量赋予 app 变量。
var app = express();
// app 本身有很多方法,其中包括最常用的 get、post、put/patch、delete,在这里我们调用其中的 get 方法,为我们的 `/` 路径指定一个 handler 函数。
// 这个 handler 函数会接收 req 和 res 两个对象,他们分别是请求的 request 和 response。
// request 中包含了浏览器传来的各种信息,比如 query 啊,body 啊,headers 啊之类的,都可以通过 req 对象访问到。
// res 对象,我们一般不从里面取信息,而是通过它来定制我们向浏览器输出的信息,比如 header 信息,比如想要向浏览器输出的内容。这里我们调用了它的 #send 方法,向浏览器输出一个字符串。
app.get('/', function (req, res, next) {
// 用 superagent 去抓取 http://dig.chouti.com/ 的内容
superagent.get('http://dig.chouti.com/')
.end(function (err, sres) {
// 常规的错误处理
if (err) {
return next(err);
}
// sres.text 里面存储着网页的 html 内容,将它传给 cheerio.load 之后
// 就可以得到一个实现了 jquery 接口的变量,我们习惯性地将它命名为 `$`
// 剩下就都是 jquery 的内容了
var $ = cheerio.load(sres.text);
var items = [];
$('#content-list .part2').each(function (idx, element) {
var $element = $(element);
items.push({
title: $element.attr('share-title'),
href: $element.attr('href'),
img: $element.attr('share-pic')
});
});
res.send(items);
});
});
// 定义好我们 app 的行为之后,让它监听本地的 3000 端口。这里的第二个函数是个回调函数,会在 listen 动作成功后执行,我们这里执行了一个命令行输出操作,告诉我们监听动作已完成。
app.listen(3000, function () {
console.log('app is listening at port 3000');
});
</code></pre>
<p>运行 <code>node app.js</code> 访问: <a href="https://link.segmentfault.com/?enc=pLrHc1KjEzpRkCUUCDaSjQ%3D%3D.FOlLp%2BhVvjlFf0kKclK6tTbIDSPI8IfkURk%2BF5ffQfA%3D" rel="nofollow">http://localhost:3000/</a>,可以看到抓取的数据直接以json格式显示出来了。</p>
<p><img src="/img/bVwzIu" alt="clipboard.png" title="clipboard.png"></p>
angularjs底部自动加载
https://segmentfault.com/a/1190000005147295
2016-05-18T10:10:35+08:00
2016-05-18T10:10:35+08:00
黑阔大人
https://segmentfault.com/u/viiiii
0
<h2>起因</h2>
<p>最近在整理公司的前端库避免出现重复造轮子的情况,这里是angularjs滚动条到底部后自动加载数据的demo</p>
<h2>infinite-scroll.directive.js</h2>
<p>底部自动加载是根据 ngInfiniteScroll 更改的,由于Angular Style Guide提倡手动注入解耦,因此ngInfiniteScroll原来的js文件直接集成不了,这里新建directive。因为是多人开发,要考虑到代码合并后变量冲突因此每个模块应该使用闭包。更改后的<strong>ngInfiniteScroll</strong>:</p>
<pre><code>(function(){
'user strict' //声明使用js严格模式
angular
.module('app')
.directive('infiniteScroll', infiniteScroll)
.value('THROTTLE_MILLISECONDS', null);
//通过$inject来手动注入依赖从而避免重复注入和其他angular找不到变量的原因,增加了可读性。
infiniteScroll.$inject = ['$rootScope', '$window', '$interval', 'THROTTLE_MILLISECONDS'];
function infiniteScroll ($rootScope, $window, $interval, THROTTLE_MILLISECONDS) {
var directive = {
scope: {
infiniteScroll: '&',
infiniteScrollContainer: '=',
infiniteScrollDistance: '=',
infiniteScrollDisabled: '=',
infiniteScrollUseDocumentBottom: '=',
infiniteScrollListenForEvent: '@'
},
link: link,
restrict: 'EA'
};
return directive;
function link(scope, element, attrs) {
/* */
var changeContainer, checkInterval, checkWhenEnabled, container, handleInfiniteScrollContainer, handleInfiniteScrollDisabled, handleInfiniteScrollDistance, handleInfiniteScrollUseDocumentBottom, handler, height, immediateCheck, offsetTop, pageYOffset, scrollDistance, scrollEnabled, throttle, unregisterEventListener, useDocumentBottom, windowElement;
windowElement = angular.element($window);
scrollDistance = null;
scrollEnabled = null;
checkWhenEnabled = null;
container = null;
immediateCheck = true;
useDocumentBottom = false;
unregisterEventListener = null;
checkInterval = false;
height = function(element) {
element = element[0] || element;
if (isNaN(element.offsetHeight)) {
return element.document.documentElement.clientHeight;
} else {
return element.offsetHeight;
}
};
offsetTop = function(element) {
if (!element[0].getBoundingClientRect || element.css('none')) {
return;
}
return element[0].getBoundingClientRect().top + pageYOffset(element);
};
pageYOffset = function(element) {
element = element[0] || element;
if (isNaN(window.pageYOffset)) {
return element.document.documentElement.scrollTop;
} else {
return element.ownerDocument.defaultView.pageYOffset;
}
};
handler = function() {
var containerBottom, containerTopOffset, elementBottom, remaining, shouldScroll;
if (container === windowElement) {
containerBottom = height(container) + pageYOffset(container[0].document.documentElement);
elementBottom = offsetTop(element) + height(element);
} else {
containerBottom = height(container);
containerTopOffset = 0;
if (offsetTop(container) !== void 0) {
containerTopOffset = offsetTop(container);
}
elementBottom = offsetTop(element) - containerTopOffset + height(element);
}
if (useDocumentBottom) {
elementBottom = height((element[0].ownerDocument || element[0].document).documentElement);
}
remaining = elementBottom - containerBottom;
shouldScroll = remaining <= height(container) * scrollDistance + 1;
if (shouldScroll) {
checkWhenEnabled = true;
if (scrollEnabled) {
if (scope.$$phase || $rootScope.$$phase) {
return scope.infiniteScroll();
} else {
return scope.$apply(scope.infiniteScroll);
}
}
} else {
if (checkInterval) {
$interval.cancel(checkInterval);
}
return checkWhenEnabled = false;
}
};
throttle = function(func, wait) {
var later, previous, timeout;
timeout = null;
previous = 0;
later = function() {
previous = new Date().getTime();
$interval.cancel(timeout);
timeout = null;
return func.call();
};
return function() {
var now, remaining;
now = new Date().getTime();
remaining = wait - (now - previous);
if (remaining <= 0) {
$interval.cancel(timeout);
timeout = null;
previous = now;
return func.call();
} else {
if (!timeout) {
return timeout = $interval(later, remaining, 1);
}
}
};
};
if (THROTTLE_MILLISECONDS != null) {
handler = throttle(handler, THROTTLE_MILLISECONDS);
}
scope.$on('$destroy', function() {
container.unbind('scroll', handler);
if (unregisterEventListener != null) {
unregisterEventListener();
return unregisterEventListener = null;
}
});
handleInfiniteScrollDistance = function(v) {
return scrollDistance = parseFloat(v) || 0;
};
scope.$watch('infiniteScrollDistance', handleInfiniteScrollDistance);
handleInfiniteScrollDistance(scope.infiniteScrollDistance);
handleInfiniteScrollDisabled = function(v) {
scrollEnabled = !v;
if (scrollEnabled && checkWhenEnabled) {
checkWhenEnabled = false;
return handler();
}
};
scope.$watch('infiniteScrollDisabled', handleInfiniteScrollDisabled);
handleInfiniteScrollDisabled(scope.infiniteScrollDisabled);
handleInfiniteScrollUseDocumentBottom = function(v) {
return useDocumentBottom = v;
};
scope.$watch('infiniteScrollUseDocumentBottom', handleInfiniteScrollUseDocumentBottom);
handleInfiniteScrollUseDocumentBottom(scope.infiniteScrollUseDocumentBottom);
changeContainer = function(newContainer) {
if (container != null) {
container.unbind('scroll', handler);
}
container = newContainer;
if (newContainer != null) {
return container.bind('scroll', handler);
}
};
changeContainer(windowElement);
if (scope.infiniteScrollListenForEvent) {
unregisterEventListener = $rootScope.$on(scope.infiniteScrollListenForEvent, handler);
}
handleInfiniteScrollContainer = function(newContainer) {
if ((newContainer == null) || newContainer.length === 0) {
return;
}
if (newContainer.nodeType && newContainer.nodeType === 1) {
newContainer = angular.element(newContainer);
} else if (typeof newContainer.append === 'function') {
newContainer = angular.element(newContainer[newContainer.length - 1]);
} else if (typeof newContainer === 'string') {
newContainer = angular.element(document.querySelector(newContainer));
}
if (newContainer != null) {
return changeContainer(newContainer);
} else {
throw new Error("invalid infinite-scroll-container attribute.");
}
};
scope.$watch('infiniteScrollContainer', handleInfiniteScrollContainer);
handleInfiniteScrollContainer(scope.infiniteScrollContainer || []);
if (attrs.infiniteScrollParent != null) {
changeContainer(angular.element(element.parent()));
}
if (attrs.infiniteScrollImmediateCheck != null) {
immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck);
}
return checkInterval = $interval((function() {
if (immediateCheck) {
handler();
}
return $interval.cancel(checkInterval);
}));
}
};
})()</code></pre>
<p><img src="/img/bVvKnK" alt="clipboard.png" title="clipboard.png"><br>directive的引用方式还是和之前相同,这里注意给directive传值的参数名由于每个大写字母被认为是一个独立的词,而每个词之前是以一个连字符分隔的,infiniteScroll对应的是infinite-scroll。</p>
<h2>newGoodsList.controller.js</h2>
<p>这里是用商品列表做的例子。</p>
<pre><code>(function (){
'use strict'
angular
.module('app')
.controller('newgoodsListController',newgoodsListController);
newgoodsListController.$inject = ['$stateParams', 'goodsListService'];
function newgoodsListController($stateParams, goodsListService){
var vm = this;
vm.return = true;
vm.cateid = $stateParams.cateid;
//定义默认参数
var keyword = '';
//当前页
var pageIndex = 1;
//每页记录数
var pageSize = 7;
//推荐状态 -1(全部)0(不推荐)1(推荐)
var recommendStatus = -1;
//自定义分类ID(热门标签)
var custom_cateId = '';
//品牌ID
var brand_id = '';
//分类ID
var cate_id = '';
//tag_id 标签ID
var tag_id = '';
//minPrice 最小价格
var minPrice = '';
//maxPrice 最大价格
var maxPrice = '';
// 排序 1:时间 2:价格 3:销量
var order = 1;
//true(升序)false(降序).
var isAsc = false;
//声明自动加载的锁,当自动加载的数据在请求的时候不会发起新请求
var busy = false;
getGoodsList();
//排序选择
vm.choose = function (num){
if(order == num){
isAsc = !isAsc;
vm.isAsc = isAsc;
}else{
order = num;
vm.filter_num = num;
}
console.log(order+':'+num+':'+isAsc);
getGoodsList();
}
//商品数据请求
function getGoodsList(){
return goodsListService.getGoodsList(keyword, pageIndex, pageSize, recommendStatus, custom_cateId, brand_id, cate_id, tag_id, minPrice, maxPrice, order, isAsc)
.then(function (data){
console.log(data);
vm.goodsList = data.data;
load();//确保自动加载功能在第一次请求成功后再执行
});
}
function load() {
//读取总页数,并且向上取整
var pageTotal = Math.ceil(vm.goodsList.data.rowCount/pageSize);
//页数自加
pageIndex ++;
//自动加载
vm.loadmore = function(){
console.log('jiazai');
//当请求进行中时不发起新请求
if(busy) return;
//发起请求并且阻止新请求
busy=true;
//当请求的页码不大于总页数时
if(pageIndex<=pageTotal){
return goodsListService.getGoodsList(keyword, pageIndex, pageSize, recommendStatus, custom_cateId, brand_id, cate_id, tag_id, minPrice, maxPrice, order, isAsc)
.then(function (data){
console.log(data.data.data.rows);
//将新请求和数据和原来的数据合并
Array.prototype.push.apply(vm.goodsList.data.rows,data.data.data.rows);
busy=false;
pageIndex++;
});
}
}
}
}
})();</code></pre>
<p><img src="/img/bVvLcC" alt="clipboard.png" title="clipboard.png"></p>
<p><img src="/img/bVvLcE" alt="clipboard.png" title="clipboard.png"></p>
<p>下拉到底部自动加载OK!</p>
Gulp构建Angularjs应用
https://segmentfault.com/a/1190000005015099
2016-04-27T09:32:53+08:00
2016-04-27T09:32:53+08:00
黑阔大人
https://segmentfault.com/u/viiiii
2
<h2>使用Gulp构建Angularjs应用的缘由</h2>
<p>2016在前端已经大热的今年也决定跟上时代的步伐进入前端自动化的行列,刚好公司框架采用的是angularjs,css采用的是scss预编译语言,所谓做事三思而后行:可不可以不做? 可不可以明天做?可不可以给别人做?因此在众多自动化工具中选择了gulp,其中还有看重他的轻量级和众多的插件的原因。</p>
<h2>初始化Gulp</h2>
<p>首先需要安装node,然后生成package.json:<code>npm init</code>。<br>安装gulp:npm install gulp --save-dev 将gulp的包依赖加入到package.json中。<br>然后再是gulp的插件:<br>gulp-jshint gulp-sass gulp-concat gulp-uglify gulp-rename。</p>
<pre><code>gulpfile.js:
var gulp = require('gulp');
//引入gulp组件
//js语法检查
var jshint = require('gulp-jshint');
//sass预处理
var sass = require('gulp-sass');
//文件合并
var concat = require('gulp-concat');
//js压缩
var uglify = require('gulp-uglify');
//重命名
var rename = require('gulp-rename');</code></pre>
<h2>目录结构划分</h2>
<p>然后是我们的目录结构划分,之前是想按照项目模块来划分,就好比登陆的模块就是login的文件夹下面是他的html文件和css文件。但之后考虑到因为是三级分销加商城的系统项目模块比较多还是选择按功能来划分如下图:<br><img src="/img/bVvbLz" alt="clipboard.png" title="clipboard.png"></p>
<h2>gulpfile任务配置</h2>
<p>先添加gulp的相关依赖:</p>
<pre><code>var gulp = require('gulp');
//引入gulp组件
//js语法检查
var jshint = require('gulp-jshint');
//sass预处理
var sass = require('gulp-sass');
//文件合并
var concat = require('gulp-concat');
//js压缩
var uglify = require('gulp-uglify');
//重命名
var rename = require('gulp-rename');
//server服务
browserSync = require('browser-sync').create();
</code></pre>
<p>值得注意的是 <strong>browserSync</strong> 号称前端页面调试神器:<br>Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。更重要的是 Browsersync可以同时在PC、平板、手机等设备下进项调试。</p>
<p>因为是angularjs项目当然需要angularjs的包和ui-router的路由(ui-router对于路由的处理相对于ng-router还是强大太多,不过也看个人习惯是使用ui-router还是ng-router),项目中因为npm现在的包管理也还不错所以我也就没使用bower,这个也是看个人习惯吧。这里手动加入项目依赖的两个包,然后创建任务做代码的合并和压缩处理。</p>
<pre><code>var jsFiles = [
'./node_modules/angular/angular.js',
'./node_modules/angular-ui-router/release/angular-ui-router.js'
];
//合并、压缩来自npm的js资源文件
gulp.task('npmscripts', function() {
return gulp.src(jsFiles)
.pipe(concat('npm.js'))
.pipe(gulp.dest('./dist/js'))
.pipe(rename('npm.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist/js'));
})</code></pre>
<p>下面则是对于生产环境编写的js和css的处理:</p>
<pre><code>//检查脚本
gulp.task('lint', function() {
gulp.src('./app/**/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'));
})
//编译Sass
gulp.task('sass', function() {
console.log('sass!');
gulp.src('./app/style/*.scss')
.pipe(sass())
.pipe(gulp.dest('./app/style'));
gulp.src('./app/style/*.css')
.pipe(concat('style.css'))
.pipe(gulp.dest('./dist/css'))
})
//合并、压缩js文件
gulp.task('scripts', function() {
gulp.src('./app/**/*.js')
.pipe(concat('all.js'))
.pipe(gulp.dest('./dist/js'))
.pipe(rename('all.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist/js'));
});</code></pre>
<p>启动browserSync服务</p>
<pre><code>//使用connect启动一个Web服务器
gulp.task('browserSync', function () {
browserSync.init({
server: {
baseDir: "./app/"
}
});
});</code></pre>
<p>配置gulp默认任务</p>
<pre><code>//默认任务
gulp.task('default', function() {
gulp.run('npmscripts');
//初始化browserSync
browserSync.init({
server: {
baseDir: "./"
}
});
//监听js变化,如过发生变化则执行lint和scripts两个task
gulp.watch('./app/**/*.js', ['lint', 'scripts']);
//监听js变化,如过发生变化则执行sass task
gulp.watch('./app/style/**/*.scss', ['sass']);
//监听当app文件夹下任何文件发生变化,则自动刷新浏览器
gulp.watch('./app/**', function() {
console.log('reload');
browserSync.reload();
});
})</code></pre>
<h2>配置Angularjs</h2>
<p>配置路由</p>
<pre><code>app.routes.js
(function() {
'use strict';
angular
.module('app', ['ui-router'])
.config(config);
function config ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/index');
$stateProvider
.state('/index', {
url: "/index",
templateUrl: "views/index.html"
})
}
})()
</code></pre>
<p>配置首页</p>
<pre><code>index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<title></title>
<link rel="stylesheet" href="../dist/css/style.css" charset="utf-8">
<script src="../dist/js/npm.js"></script>
<script src="../dist/js/all.js"></script>
</head>
<body>
<div ui-view class="Index">
</div>
</body>
</html>
</code></pre>
<p>在引用css和js的时候就可以直接引用在dest目录下合并了的文件了。<br>运行gulp,构建成功!并且修改代码浏览器内容自动刷新,更加方便了移动端的调试。<br><img src="/img/bVvcOo" alt="clipboard.png" title="clipboard.png"></p>
<p>下面贴一个完整的gulpfile.js</p>
<pre><code>var gulp = require('gulp');
//引入gulp组件
//js语法检查
var jshint = require('gulp-jshint');
//sass预处理
var sass = require('gulp-sass');
//文件合并
var concat = require('gulp-concat');
//js压缩
var uglify = require('gulp-uglify');
//重命名
var rename = require('gulp-rename');
//server服务
browserSync = require('browser-sync').create();
var jsFiles = [
'./node_modules/angular/angular.js',
'./node_modules/angular-ui-router/release/angular-ui-router.js'
];
//检查脚本
gulp.task('lint', function() {
gulp.src('./app/**/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'));
})
//编译Sass
gulp.task('sass', function() {
console.log('sass!');
gulp.src('./app/style/*.scss')
.pipe(sass())
.pipe(gulp.dest('./app/style'));
gulp.src('./app/style/*.css')
.pipe(concat('style.css'))
.pipe(gulp.dest('./dist/css'))
})
//合并、压缩js文件
gulp.task('scripts', function() {
gulp.src('./app/**/*.js')
.pipe(concat('all.js'))
.pipe(gulp.dest('./dist/js'))
.pipe(rename('all.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist/js'));
});
//合并、压缩来自npm的js资源文件
gulp.task('npmscripts', function() {
return gulp.src(jsFiles)
.pipe(concat('npm.js'))
.pipe(gulp.dest('./dist/js'))
.pipe(rename('npm.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist/js'));
})
//合并、压缩css文件
// gulp.task('css', function() {
// gulp.src('./app/style/*.css')
// .pipe(concat('style.css'))
// .pipe(gulp.dest('./dist/css'))
// // .pipe(rename('style.min.css'))
// // .pipe(uglify())
// // .pipe(gulp.dest('./dist/css'));
// });
//使用connect启动一个Web服务器
gulp.task('browserSync', function () {
browserSync.init({
server: {
baseDir: "./app/"
}
});
});
//默认任务
gulp.task('default', function() {
gulp.run('npmscripts');
//监听js变化
browserSync.init({
server: {
baseDir: "./"
}
});
gulp.watch('./app/**/*.js', ['lint', 'scripts']);
gulp.watch('./app/style/**/*.scss', ['sass']);
// gulp.watch('./app/style/**/*.css', ['css'])
gulp.watch('./app/**', function() {
console.log('reload');
browserSync.reload();
});
})
</code></pre>
Ionic制作Hybrid App系列二:Mac环境下Ionic release版本APK的签名与发布
https://segmentfault.com/a/1190000004449024
2016-02-18T00:42:30+08:00
2016-02-18T00:42:30+08:00
黑阔大人
https://segmentfault.com/u/viiiii
0
<h2>Android应用程序签名</h2>
<p>在前一章节里面,通过ionic run使app成功在手机上面跑起来,并且将android-debug.apk放到手机上后发现也能正常的安装,debug版本和release版本到底什么区别呢,这里就需要了解安卓的apk签名:<code>为了保证每个应用程序开发商合法ID,防止部分开放商可能通过使用相同的Package Name来混淆替换已经安装的程序,我们需要对我们发布的APK文件进行唯一签名,保证我们每次发布的版本的一致性(如自动更新不会因为版本不一致而无法安装)。</code>如果使用没有统一签名的debug版本进行覆盖安装时将会报错,提示:应用未安装。<br><img src="/img/bVsPxx" alt="图片描述" title="图片描述"></p>
<p>进行签名的好处都有啥呢:</p>
<ol>
<li><p>应用程序升级:如果你希望用户无缝升级到新的版本,那么你必须用同一个证书进行签名。这是由于只有以同一个证书签名,系统才会允许安装升级的应用程序。如果你采用了不同的证书,那么系统会要求你的应用程序采用不同的包名称,在这种情况下相当于安装了一个全新的应用程序。如果想升级应用程序,签名证书要相同,包名称要相同!</p></li>
<li><p>应用程序模块化:Android 系统可以允许同一个证书签名的多个应用程序在一个进程里运行,系统实际把他们作为一个单个的应用程序,此时就可以把我们的应用程序以模块的方式进行部署,而用户可以独立的升级其中的一个模块</p></li>
<li><p>代码或者数据共享:Android 提供了基于签名的权限机制,那么一个应用程序就可以为另一个以相同证书签名的应用程序公开自己的功能。以同一个证书对多个应用程序进行签名,利用基于签名的权限检查,你就可以在应用程序间以安全的方式共享代码和数据了。</p></li>
<li><p>不同的应用程序之间,想共享数据,或者共享代码,那么要让他们运行在同一个进程中,而且要让他们用相同的证书签名。</p></li>
</ol>
<hr>
<p>签名使用到的工具是Keytool:<code>keytool 是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整性以及认证服务。它还允许用户储存他们的通信对等者的公钥(以证书形式)</code>。<br>首先进入到工程目录后输入<br>$ keytool -genkey -v -keystore my-release-key.keystore -alias 你的应用名称 -keyalg RSA -keysize 2048 -validity 10000<br><img src="/img/bVsPxX" alt="图片描述" title="图片描述"></p>
<p>成功生成签名文件<br><img src="/img/bVsPx0" alt="图片描述" title="图片描述"></p>
<hr>
<h3>发布Release版本APK</h3>
<p>Releaese版本和Debug版本的区别:</p>
<ol>
<li><p>debug签名的应用程序不能在AndroidMarket上架销售,它会强制你使用自己的签名;Debug模式下签名用的证书(默认是Eclipse/ADT和Ant编译)自从它创建之日起,1年后就会失效。</p></li>
<li><p>debug.keystore在不同的机器上所生成的可能都不一样,就意味着如果你换了机器进行apk版本升级,那么将会出现上面那种程序不能覆盖安装的问题,相当于软件不具备升级功能!</p></li>
</ol>
<p>使用命令<code>ionic build android --release</code>进行release版本的发布。在apk目录下选中我们需要的版本<img src="/img/bVsPyi" alt="图片描述" title="图片描述"></p>
<p>输入命令:<code>jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore sangcan.apk sangcan</code><br>这里后面的sangcan.apk是我们的目标apk版本,这里是个人习惯将前面的目标文件重命名为sangcan.apk。而后面的sangcan则是之前在生成签名时填写的应用名称。<br><img src="/img/bVsPyn" alt="图片描述" title="图片描述"></p>
<p>签名成功后可以看到成功进行覆盖安装<br><img src="/img/bVsPys" alt="图片描述" title="图片描述"></p>
<p>到这里Release版本的APK成功发布。<br><img src="/img/bVsPyu" alt="图片描述" title="图片描述"></p>
Angularjs Promise 解决异步获取数据导致return返回为空的问题
https://segmentfault.com/a/1190000004094977
2015-12-04T11:34:25+08:00
2015-12-04T11:34:25+08:00
黑阔大人
https://segmentfault.com/u/viiiii
1
<p>最近在开发项目的时候。我在service中请求数据返回给控制器的时候,由于数据是异步请求的,这里需要知道javascript的运行环境是单线程的,一次只能执行一个任务,但是单线程坏处就是如果前一个任务执行时间较长就会导致整个页面的阻塞,因此javascript提供了异步请求,使任务可以不用等待上一个任务执行完成。但就是这个异步的请求,导致我的data数据还没返回就已经return数据到控制层了,从而返回值为空。</p>
<h2>goodsService</h2>
<pre><code>app.factory('goodsService', function($http,$q) {
var goodsData=111;
function get() {
$http({
method:'get',
url:'test/goods.json'
}).success(function(data,status,headers,config){
console.log('get success...');
console.log(goodsData);
goodsData=data;
return goodsData;
}).error(function(data,status,headers,config){
console.log('get error...');
})
}
function set(data) {
$http({
method:'post',
url:'test/goods.json'
}).success(function(data,status,headers,config){
goodsData=data;
console.log('get success...');
console.log(data);
}).error(function(data,status,headers,config){
console.log('get error...');
})
}
return {
set: set,
get: get
}
});
</code></pre>
<h2>controller.js</h2>
<pre><code>$scope.goods=goodsService.get();
console.log('scope.goods='+$scope.goods);</code></pre>
<p><img src="/img/bVrkvg" alt="图片描述" title="图片描述"></p>
<p>当然很多人都有很多种解决办法。比如说函数引用:</p>
<pre><code>$.get(/test/goods.json', function(data) {
return data;
});</code></pre>
<p>当然都是可行的。但是当处理比较复杂的多个异步进行的时候那就真是看的头晕眼花。<br>因此Promise应运而生。</p>
<h2>Promise的身份之谜</h2>
<p>Promise有两部分:</p>
<ul>
<li><p>Deferred定义工作单元,<br> 用来定义工作单元的开始,处理和结束三部分</p></li>
<li><p>Promise接收Deferred返回的数据。<br> 有状态和句柄。Promise 不同于回调的很重要的一个点是,你可以在 Promise 状态变成执行(resolved)之后追加处理句柄。这就允许你传输数据,而忽略它是否已经被应用获取,然后缓存它,等等之类的操作,因此你可以对数据执行操作,而不管它是否已经或者即将可用。</p></li>
</ul>
<p>在Angularjs使用Promise的时候需要用到内置服务$q。</p>
<ul>
<li><p>$q服务受到Kris Kowal的Q库的启发,所以类似于那个库,但是并没有包含那个库的所用功能。</p></li>
<li><p>$q是跟AngularJS的$rootScope模板集成的,所以在AngularJS中执行和拒绝都很快。 $q</p></li>
<li><p>promise是跟AngularJS模板引擎集成的,这意味着在视图中找到任何Promise都会在视图中被执行或者拒绝。<br>$q实现了上面提到的所有的Deferred和Promise方法。</p></li>
</ul>
<p>$q.defer()提供给我们一个创建Deferred对象的方法。这个Deffered对象有个promise属性,这个属性带有6个方法:</p>
<ul>
<li><p>resolve(value):用来执行deferred promise,value可以为字符串,对象等。</p></li>
<li><p>reject(value):用来拒绝deferred promise,value可以为字符串,对象等。</p></li>
<li><p>notify(value):获取deferred promise的执行状态,然后使用这个函数来传递它。</p></li>
<li><p>then(successFunc, errorFunc,notifyFunc):无论promise是成功了还是失败了,当结果可用之后,then都会立刻异步调用successFunc,或者'errorFunc',在promise被执行或者拒绝之前,notifyFunc可能会被调用0到多次,以提供过程状态的提示。</p></li>
<li><p>catch(errorFunc)</p></li>
<li><p>finally(callback)<br>更加形象点。就比如说我现在遇到的这个问题。当我的goodsService请求商品数据的时候,我先用$q.defer()创建了一个deferred对象。然后通过该对象的promise属性获取到一个promise对象。这时我进行数据请求,定义请求成功和请求失败的计划分别是deferred.resolve(data)和deferred.reject()。然后将这个promise返回给请求service的控制层。控制层通过.then创建一个执行链,它允许我们中断基于更多功能的应用流程,可以借此导向不同的的结果,再来进行不同的操作。</p></li>
</ul>
<p><strong>goodsService</strong></p>
<pre><code>app.factory('goodsService', function($http,$q) {
function get() {
var deferred=$q.defer();
var promise=deferred.promise;
$http({
method:'get',
url:'test/goods.json'
}).success(function(data,status,headers,config){
deferred.resolve(data);//执行成功
}).error(function(data,status,headers,config){
deferred.reject();//执行失败
})
console.log('return promise');
return promise;
}
function set(data) {
$http({
method:'post',
url:'test/goods.json'
}).success(function(data,status,headers,config){
deferred.resolve(data);//执行成功
console.log('get success...');
}).error(function(data,status,headers,config){
console.log('get error...');
})
}
return {
set: set,
get: get
}
});</code></pre>
<p><strong>productListCtrl</strong></p>
<pre><code>goodsService.get().then(function(result){
console.log('goodsService get successed');
$scope.goods=result;
console.log('scope.goods1='+$scope.goods);
},function(){
console.log('goodsService get error');
});
console.log('scope.goods2='+$scope.goods);
});
</code></pre>
<p><img src="/img/bVrlqR" alt="图片描述" title="图片描述"></p>
<p>数据显示正常。也可以从下面的scope.goods1和scope.goods2看出promise的特性,在等待返回选择不同计划的时候不会阻塞其他的任务执行。</p>
CSS3 Flexbox在移动端的应用
https://segmentfault.com/a/1190000004070556
2015-11-30T15:04:16+08:00
2015-11-30T15:04:16+08:00
黑阔大人
https://segmentfault.com/u/viiiii
0
<p>FlexBox(CSS Flexible Box Layout Module)是css3新添加一个用于页面布局的全新CSS3模块功能。它可以把列表放在同一个方向(从左到右或从上到下排列),并且让这些列表能延伸到占用可用的空间。较为复杂的布局可以通过嵌套一个伸缩容器(flex container)来辅助实现,它的出现可以解决我们通过N多结构、css实现的布局方式。<br>我们可以通过<a href="https://link.segmentfault.com/?enc=tERRUXD6toU7RjMjB5KeQA%3D%3D.HEJGAfuNdIh4EPO7kT8zKfTtRW5jTow%2B9AX5A2Be6DuN%2B%2F76OzD9C%2ByEXsMeDn62" rel="nofollow">can i use</a>来进行兼容性查询<br><img src="/img/bVreRy" alt="图片描述" title="图片描述"></p>
<p>为什么我这篇文章是强调flexbox在移动端的应用。总所周知flexbox是有几个版本的。<br>从最初的2009年发布的旧版本的语法在声明时使用的是:display:box;<br>到2011年变换为:display:flex box;<br>到了现在使用的是:display:flex。</p>
<h2>新老版本语法对比</h2>
<p><img src="/img/bVre4f" alt="图片描述" title="图片描述"></p>
<p><img src="/img/bVre4g" alt="图片描述" title="图片描述"></p>
<p>尽管使用旧的语法并不是一个聪明的做法。相对来说新的语法更容易理解和更深入、更一致的实现效果。但在移动端由于新语法还没完全确定,所以并不支持。因此在移动端使用的时候建议采用display:-webkit-box来进行定义。</p>
<pre><code>新版本语法:
display: flex;
display: inline-flex;
flex-direction:column;
justify-content:space-between;
align-items:flex-start;
旧版本语法:
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-pack: justify;</code></pre>
<p><img src="/img/bVre5V" alt="图片描述" title="图片描述"></p>
<p>这里就是实现一个纵向靠两边的排列功能。经过测试在使用新版本语法的时候在android和ios的微信浏览器上都会失效。使用老版本的语法测试都能够正常显示。虽然flexbox在现在已经是个很成熟的属性了。但在移动端的兼容性上面还是要多加注意。</p>
Ionic制作Hybrid App系列一:Mac环境下Ionic的搭建
https://segmentfault.com/a/1190000004038405
2015-11-24T15:17:35+08:00
2015-11-24T15:17:35+08:00
黑阔大人
https://segmentfault.com/u/viiiii
0
<p>最近公司项目要做app。由于开发时间较短,并且要出web,android和ios三个版本,经过考虑后决定尝试用Hybrid App的开发方式。说到Hybrid App(混合应用),什么是混合应用?<br>混合应用(Hybrid App)相当于是利用Web开发技术编写的原生应用,如HTML5、CSS、JavaScript都是进入原生容器(Native Container)的比较常用的语言,原生应用包含了一个链接到HTML文件的WebView隐藏浏览器。而使用Cordova、PhoneGap或其他类似的解决方案,不但使整合HTML和原生代码成为可能,甚至不费吹灰之力便能做到,也让在应用商店中部署App更加容易。相信以下这两张图就能很好的说明Hybrid App的优势。<img src="/img/bVq6Ii" alt="bVliZ1" title="bVliZ1"></p>
<p><img src="/img/bVq6Im" alt="bVli0a" title="bVli0a"></p>
<p>而我采用的Ionic Framework更是Hybrid App中的佼佼者。首先他又很漂亮的ui,并且是基于现在非常流行的Angularjs,因此他拥有非常优异的性能。并且将主流的Cordova API或者Cordova插件封装为AngularJS扩展,使用非常方便。还有开源的Icon Font库和异常活跃的在线社区进行讨论。对了,还拥有强大的命令行功能(基于nodejs)。</p>
<p>使用Ionic首先需要安装nodeJS <a href="https://link.segmentfault.com/?enc=mRS0CkO1ca0DtqPUEop9kg%3D%3D.MEwLeMIXwMgJhGTM4lwaDUlG7%2BgiUBEHyIjjI%2BbNrjA%3D" rel="nofollow">https://nodejs.org/en/</a> 这是nodeJs的下载地址。然后使用NPM(NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。)安装最新的Cordova 和 Ionic 。</p>
<pre><code>$ npm install -g cordova ionic
</code></pre>
<p>然后可以通过 使用Ionic官方提供的现成的应用程序模板,或一个空白的项目创建一个Ionic应用。</p>
<pre><code>$ ionic start myApp tabs
</code></pre>
<p>这里我选择的是tabs的模版。运行后可以看到项目已经自动构件出来了。<br><img src="/img/bVq6Is" alt="Center" title="Center"></p>
<p>这里可以看到自动构件的myApp的项目结构。进入www目录后打开我们的index.html页面。发现报错,如下图。<br><img src="/img/bVq6Ix" alt="Center" title="Center"></p>
<p>这里是由于引用了AngularJs的路由导致出现跨域问题。这里可以选择把项目部署在服务器环境下或者屏蔽掉现代浏览器的安全设置。</p>
<p>以google浏览器为例:打开终端,输入下面命令:open -a "Google Chrome" --args --disable-web-security或者在windows环境下修改chrome的快捷方式"C:\Program Files\Google\Chrome\Application\chrome.exe" --args --disable-web-security 在后面添加--args --disable-web-security 。这时候再打开index.html就可以看到显示正常了。怎么样这ui美如画吧。<img src="/img/bVq6IM" alt="图片描述" title="图片描述"></p>
<p>到此时,ionic的项目构件就完成了。接下来就是构建和运行ionic应用。以构件android项目为例:</p>
<pre><code>$ cd myApp
$ ionic platform add android
$ ionic build android
$ ionic emulate android</code></pre>
<p>很多小伙伴们到了ionic build android这步的时候可能会报错。<img src="/img/bVq6Jf" alt="Center" title="Center"></p>
<p>这里是因为安卓sdk的版本过低。需要更新安卓的sdk。如果没有安卓环境的可以去安装android studio。</p>
<p><a href="https://link.segmentfault.com/?enc=hO85gFH5IE1QbezMo0hs1g%3D%3D.2%2FPLT%2FBQ73va6Vzm0wr4KdZ9k4O7Z9OgCWYpmbVzOy8%3D" rel="nofollow">http://www.androiddevtools.cn</a> 首先在这里下载jdk环境windows安装比较简单,但是mac环境下由于我升级的系统是Mac osx el capitan由于不是原版的mac导致jdk识别不了osx el capitan 报了Oracle 的Java 要求mac os x 10.7.3或更高版本<br>这里需要将idk的包用命令pkgutil --expand /Volumes/JDK\ 8\ Update\ 05/JDK\ 8\ Update\ 05.pkg /tmp/JDK8.unpkg 解压出来。然后修改其中的名为:Distribution的文件中的</p>
<pre><code>function pm_install_check() {
if(!(checkForMacOSX('10.7.3') == true)) {
my.result.title = 'OS X Lion required';
my.result.message = 'This Installer is supported only on OS X 10.7.3 or Later.';
my.result.type = 'Fatal';
return false;
}
return true;
}
</code></pre>
<p>方法。将pm_install_check()直接改为return true;就可以绕过检测进行安装了。<br>然后下载android studio。还是在之前的网站<a href="https://link.segmentfault.com/?enc=g73txn2vLYmNRAleyaAHPg%3D%3D.RWYi4FCTs6pRQeXhQxV2qGByji2WXMoWenamnLN6kqY%3D" rel="nofollow">http://www.androiddevtools.cn</a> ,安装完成后更新安卓的sdk。然后再执行,成功执行!。</p>
<pre><code>$ ionic build android</code></pre>
<p><img src="/img/bVq6Jx" alt="Center" title="Center"></p>
<p>最后连接上手机开启调试模式运行 ,运行成功!</p>
<pre><code>$ ionic run android </code></pre>
<p><img src="/img/bVq6JG" alt="Center" title="Center"></p>