web 端到端测试工具 Nightwatch.js

Nightwath 介绍

说起end to end 测试,可以理解为模拟用户的行为观察页面、点击页面是否与预期的结果相符,一般常用的是Selenium,,它提供了API用于自动化的测试,Selenium使用的是一套标准的 W3C 的 Web Driver API (一套用于自动化测试的协议、该协议已经成了事实的标准 ),而Nightwatch(官网地址 http://nightwatchjs.org) 也是在这套协议基础之上提供的一套NodeJS的API,特点是语法简洁、使用方便,支持各种浏览器。官方文档写的非常详细,这里做一下简单的介绍。下面是官方给出的工作原理图,猫头鹰就是表示nightwatch,客户端浏览器通过Web Driver协议与 Selenium交互,Selenium与nightwatch相互通信来完成整个过程,nightwatch会往浏览器中注入代码,来操作Dom,然后通过websocket完成与测试服务端的通信来展示测试任务的结果。

环境搭建

安装 Nightwatch 。使用npm全局安装或者本地安装都行

npm install -g nigthwatch

或者
npm install nightwatch

Selenium Server

这里需要调用Selenium Server 提供的API,需要去下载Selenium Server的jar包(http://selenium-release.storage.googleapis.com/index.html), 下载最新的selenium-server-standalone-{VERSION}.jar即可,当然需要本机有Java环境 Java version要在7以上。

浏览器驱动下载

Selenium 支持不同的浏览器,对于制定的浏览器需要下载特定的 驱动,这里以chrome为例 下载地址 (https://sites.google.com/a/chromium.org/chromedriver/downloads)。
这里为了方便把下载的Selenium Server 以及 chromedriver 统一放到项目的 bin目录下 随项目一起提交了。

配置以及运行测试用例

新建一个配置文件内容如下

{
  "src_folders": [
    "e2e"
  ],
  "output_folder": "reports",
  "selenium": {
    "start_process": true,
    "server_path": "./bin/selenium-server.jar",
    "log_path": "",
    "port": 4444,
    "cli_args": {
      "webdriver.chrome.driver": "./bin/chromedriver"
    }
  },
  "test_settings": {
    "default": {
      "launch_url": "http://localhost",
      "selenium_port": 4444,
      "selenium_host": "127.0.0.1",
      "silent": true,
      "screenshots": {
        "enabled": true,
        "path": "."
      },
      "globals": {
        "waitForConditionTimeout": 1000 
      },
      "desiredCapabilities": {
        "browserName": "chrome",
        "chromeOptions": {
          "args": [
          "--user-agent=Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36",
            "--window-size=360,640" 
          ]
        },
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    }
  }
}

http://nightwatchjs.org/gettingstarted/ 官网上有配置文件的具体介绍,不在详细叙述,需要是设置selenium 以及 浏览器驱动的路径,这里用的是相对目录
新建 nightwatch.conf.js文件里面放入

module.exports = (function(settings) {
  settings.test_workers = false;
  return settings;
})(require('./nightwatch.json'));

此时在项目目录下运行 nightwatch 会自动启动自动化测试程序,运行测试用例,下面编写一个简单的测试用例,新建 e2e/test.js文件,内容如下

  module.exports = {
  'Demo test' : function (browser) {
    browser
      .url("http://www.baidu.com")
      .waitForElementVisible('body', 1000)
      .setValue('#index-kw',"面向对象")
      .click('#index-bn')
      .assert.containsText('body', '面向对象')
      .saveScreenshot('./test.png')
      .end();
  }
};

终端下执行 nightwatch 即可运行,查看测试报告,并得到一张截图。
如果是本地安装的nightwatch 执行./node_modules/.bin/nightwatch 也可以通过npm script 形式来执行。该项目的目录结构如下

── bin
│   ├── chromedriver
│   └── selenium-server.jar
├── e2e
│   └── test.js
├── nightwatch.conf.js
├── nightwatch.json
├── package.json

环境搭建好了,接下来就是编写测试用例,官方有详细的开发者指导,以及API文档。另外Nightwatch 也可以进行单元测试、与mocha 、chai等知名的工具结合使用。

踩坑

  • 英文的文档弄懂 nightwatch、Selenium Server、浏览器驱动 三者之前的关系,以及运行原理
  • 在chrome测试模拟点击的时候确保 被点击的dom是可见的,否则会报错。
  • 一些nightwatch 没有提供的模拟器操作,比如将某个dom scroll into View,需要使用 execute 方法 自己注入js代码来完成。

react-native与webview的双向通信

react native 在官网的文档中,并没有详细介绍http://facebook.github.io/react-native/docs/webview.html#onmessage如何实现RN与WebView中JavaScript的通信。 关于 onMessage的描述如下,

onMessage?: function 
A function that is invoked when the webview calls window.postMessage. Setting this property will inject a postMessage global into your webview, but will still call pre-existing values of postMessage.
window.postMessage accepts one argument, data, which will be available on the event object, event.nativeEvent.data. data must be a string.

从上只能看出,RN可以接受来自于webview的消息,然而并无法得知如何从RN往Webview内部发送消息,读源码的时候,发现了 RN的webview组件中除了onMessage 还有个posMessage方法,由此可以推出能搞定RN与Webview内H5的双向通信https://github.com/facebook/react-native/blob/0150bc76eb8d2baa84ba040aead8131228c1185e/Libraries/Components/WebView/WebView.ios.js#L506

  • 在RN中 onMessage负责接收Weview的消息 postMessage 负责发送消息给Webview
  • 通过监听message事件来接收RN的消息,通过window.postMessage在Webview中向RN中发送消息

于是写代码验证下。在html中放置两个输入框A、B,点击按钮把A、B的值发送给RN端,返会两个数的和并展示

H5的代码

1
  <div style="padding-top:50px;">
      <input id='A' /> 
      <input id='B' />
  </div>
<Button id='cacl'>计算</Button> <span>AB相加为</span><span id='sum'></span>
1
 var bt= document.getElementById('cacl');
 bt.addEventListener('click',function(){
   var valA = document.getElementById('A').value;
   var valB = document.getElementById('B').value;
   window.postMessage(JSON.stringify({
         type:'add',
         data:{
           A:valA,
           B:valB
         }
       }))
 });
document.addEventListener('message',function(e){
   document.getElementById('sum').innerText = JSON.parse(e.data).result;
})

RN 中的代码

1
 export default class RNWebbViewMessage extends Component {
  constructor(props){
    super(props);
      this.onMessage = this.onMessage.bind(this);
  }
   onMessage(e){
      var event =e.nativeEvent;
      var data=JSON.parse(event.data);
      if(data.type ==='add'){
        let  args= data.data;
        let a = Number(args.A);
        let b = Number(args.B);
        this.refs.webviewRef.postMessage(JSON.stringify({
            result:a+b
         }))
      }
    }
  render() {
    return (
      <View style={{flex:1}}>
          <WebView ref={'webviewRef'} automaticallyAdjustContentInsets={false} onMessage={this.onMessage} javaScriptEnabled={true} source={{uri:'http://localhost/work/study/rnwebviewmessage/index.html?ss'}} /> 
      </View>
    );
  }
}

经过验证结论是成立的。

  • webview网页可以使用window.postMessage 发送消息, document.addEventListener('message',callback)来监听消息
  • RN 中onMessage 通过 参数中nativeEvent.data获得webview传递过来的字符串。然后把结果通过webview实例的postMessage方法发送到H5中

以上只是通信api的介绍,如果生产环境中使用的话,还可以对调用形式进一步封装,比如H5中方法调用RN中的分享,并且要求知道分享结果是否成功,可以使用 webviewBridgecallRN('share',{title:'微信分享',content:"分享内容"},callback)这种类似的方式,同时RN中可根据事件的名字进行分模块来实现。

备注

React.createClass VS React.Component

创建react组件有两种方式,使用用 React.createClass 以及用es6模块化的方式extends React.Component 通过集成 Component 组件类的形式来实现。两者的作用类似,后者是新的语法也是趋势。于是赶紧对比下如何升级到新的语法

React.createClass

creatClass创建的组件赋值为const类型的变量,在组件内部实现必要的 render函数

import React from 'react';
const Contacts = React.createClass({
  render() {
    return (
      <div></div>
    );
  }
});
export default Contacts;

React.Component

通过ES6的类继承模式在 构造函数里面要 先通过super(props)调用父组件的构造函数

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div></div>
    );
  }
}
export default Contacts;

propTypes and getDefaultProps

React.createClass

import React from 'react';
const Contacts = React.createClass({
  propTypes: {

  },
  getDefaultProps() {
    return {

    };
  },
  render() {
    return (
      <div></div>
    );
  }
});
export default Contacts;

React.Component

给类增加属性的方式发生了变化,defaultProps 来替代 getDefaultProps() 似乎简单了不少

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div></div>
    );
  }
}
Contacts.propTypes = {
};
Contacts.defaultProps = {
};
export default Contacts;

状态定义 State

import React from 'react';
const Contacts = React.createClass({
  getInitialState () {
    return {

    };
  },
  render() {
    return (
      <div></div>
    );
  }
});
export default Contacts;

定义默认的状态在构造函数中实现来取代了 getInitialState,语法更简单

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }
  render() {
    return (
      <div></div>
    );
  }
}
export default Contacts;

this的含义不同

React.createClass中,给组件绑定事件的函数中的 this 指向当前的组件 ,在 React.Component 中事件函数中的 this 默认指向为空

import React from 'react';
const Contacts = React.createClass({
  handleClick() {
    console.log(this); // 但前的组件实例
  },
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
});
export default Contacts;

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
  }
  handleClick() {
    console.log(this); // null
  }
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
}
export default Contacts;

可以在绑定事件的时候使用 JS中的bind来绑定this 对象 <div onClick={this.handleClick.bind(this)}></div>也可以在 构造函数里面手动的 bind this.handleClick = this.handleClick.bind(this);

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log(this); // React Component instance
  }
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
}
export default Contacts;

Mixins

使用mixins可以提高复用性,React.Component 不再支持 Mixins

import React from 'react';
var SomeMixin = {
  doSomething() {
  }
};
const Contacts = React.createClass({
  mixins: [SomeMixin],
  handleClick() {
    this.doSomething(); // use mixin
  },
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
});
export default Contacts;

在 React.Component 官方推荐 HOC(高阶组件)的形式 来替换,如果一定要使用 mix的话 也可以使用 react-mixin

ubuntu 下使用n 管理node 版本遇到的问题

ubuntu 中使用n 来管理node,首先根据githubhttps://github.com/tj/n上面的提示进行安装

在非 root 用户的话 ,运行node提示找不到命令(root下可以,然而使用 Linux是不鼓励在root 下操作的)
可以确认环境变量没有生效,可是装n之后会自动把
export N_PREFIX="$HOME/n"; [[ :$PATH: == *":$N_PREFIX/bin:"* ]] || PATH+=":$N_PREFIX/bin"
添加到.bashrc下了。来自动设置了相关的环境变量。
于是 运行下 source ~/.bashrc 就可以了 环境变量生效了.可以正常运行弄的,然而 重新打开一个终端以后 又失效了。

在登录的时候会系统自动执行~/.profile ,在~/.profile

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

就是终端连接的时候会自动执行 ~/.bashrc,理论上来说是环境变量会自动生效才是。可以猜测在执行 ~/.bashrc 的时候 出了问题 ,导致 n相关的环境变量没有生效。
于是把~/.bashrc的那一行代码 export N_PREFIX="$HOME/n"; [[ :$PATH: == *":$N_PREFIX/bin:"* ]] || PATH+=":$N_PREFIX/bin"
拷贝到 ~/.profile 下即可使环境变量生效了。重新连接以后 运行node 就可以了。。

vue 1.0 的主要变化

目前工作中还在使用 0.12.x版本,突然发现 1.0在使用上有不小的变化,于是参考 官方的更新日志https://github.com/vuejs/vue/releases/tag/1.0.0总结如下

  • v-repeat 替换为 v-for
    使用方法 v-for="item in items" 不能使用 v-for="imtes" 可以使用$index来获取当前项在数组中的索引
<ul id='example-1'>
<li v-for='item in items'>
      $index { {item.message} }
</li>
<ul>

或者 给索引取一个别名 如下 v-for 块中的内容可以使用其父作用域中的属性

   <ul id='example-1'>
   <li v-for='(index,item) in items'>
           { {index} } { {item.message} }
   </li>
   <ul>


var example=new Vue({
   el:'#example',
   data:{
       items:[
           {message:'Fool'},
           {message:"Bar"}
       ]
   }
   })
  • v-on=’action:fun’ 替换为 v-on:action=’func’ 缩写 @action=’func’,新增了两个事件修饰符号 .prevent与stop

    <button v-on:click="doThis"></button>
    <button v-on:click="doThat('hello', $event)"></button>
    
    <!-- 缩写 -->
    <button @click="doThis"></button>
    
    <!-- 停止冒泡 -->
    <button @click.stop="doThis"></button>
    
    <!-- 阻止默认-->
    <button @click.prevent="doThis"></button>
    
    <!-- 组织默认的提交表单 -->
    <form @submit.prevent></form>
    
    <!-- 链式执行绑定的方法并阻止冒泡 -->
    <button @click.stop.prevent="doThis"></button>
    
    <!-- 绑定键盘事件 -->
    <input @keyup.enter="onEnter">
    
    <!-- 缩写形式 -->
    <input @keyup.13="onEnter">
    <button v-on:click="dothis" v-on:keyup="dothat"></button>
    
  • v-attr=’attr:val’ 替换为 v-bind:attr=”val” 缩写 :attr=”val”

  • 新增 v-else 指令 与 v-if / v-show 成对使用
  • v-el 指令,注册指令 可以在VM中通过 $els 来获得注册DOM的哈希对象

    <span v-el:msg>hello</span>
    <span v-el:other-msg>world</span>
    
    this.$els.msg.textContent // -> "hello"
    this.$els.otherMsg.textContent // -> "world"
    
  • v-pre 跳过VUE的处理 比如使用 直接显示 这样的形式
  • v-component指令过时了,建议使用is<component :is="componentId"></component> 更多关于 组件的内容参考 http://vuejs.org/guide/components.html
  • props 的传递变更为

    <!-- 单向绑定 -->
    <child :msg='parentMsg'></child>  
    <!-- 双向绑定 -->
    <child :msg.sync='parentMsg'></child> 
    <!--  单次绑定 -->
    <child :msg.once='parentMsg'></child>
    
  • v-class and v-style 过时了 推荐使用属性绑定使用v-bind方式 v-bind:classv-bind:style

    <!-- 切换class-->
    <div v-bind:class="{ 'class-a': true, 'class-b': false }"></div>
    <!-- 应用样式列表 -->
    <div v-bind:class="[ dynamicClass, 'literal-class' ]"></div>
    
    <!-- 接受驼峰式属性 -->
    <div v-bind:style="{ fontSize: '14px', color: 'red' }"></div>
    <!--  支持样式的绑定-->
    <div v-bind:style="[ styleObjectA, styleObjectB ]"></div>
    <a v-bind-href=""></a>
    <a v-bind-src=""></a>
    
  • v-model 支持checkbox与数组绑定 (终于支持了 记得以前使用ng的时候用了一个插件来完成这个功能

    <input type="checkbox" value="Jack" v-model="checkedNames">
    <input type="checkbox" value="John" v-model="checkedNames">
    <input type="checkbox" value="Mike" v-model="checkedNames">
    被选中的项的value值将会与 checkedNames的数组绑定
    
  • 过滤器
    1、debounce是空闲时间的间隔控制,空闲时间必须大于或等于 一定值的时候,才会执行调用方法。。比如我们做autocomplete,这时需要我们很好的控制输入文字时调用方法时间间隔。一般时第一个输入的字符马上开始调用,根据一定的时间间隔重复调用执行的方法。

    <input @keyup="onKeyup | debounce 500">
    

mac上常用的工具

我的mac (OS X)上常用的工具。。。

常用软件

  • sublime
  • webstorm
  • ADT/android studio android环境
  • git
  • source tree git客户端
  • charles
  • Photoshop
  • liteIDE go开发工具
  • Appzapper 软件卸载
  • phpstorm PHP IDE
  • xcode (IOS开发工具 ObjectC、swift)
  • iTools
  • QQ
  • skype
  • android transfer android文件传输
  • MplayerX 视频播放器
  • EasyHtml5video 视频格式转换
  • 迅雷
  • 搜狗输入法
  • Dash
  • Chrome
  • Firefox Developer Edition
  • office
  • 为之笔记
  • virtual box (win7 虚拟机)
  • gliffy Diagram 绘图工具
  • izip unarchiver 解压工具 可以解压rar格式
  • Mysql/navicat
  • dash 查API的工具
  • iterm+oh myzsh 终端工具(内置的终端界面不好看,体验也不好)
  • vim (内置工具)

语言环境

  • node/iojs
  • go
  • dart
  • ruby (内置 2.0) mac上的软件管理工具 brew 基于ruby的。
  • python (内置 2.7.6)
  • java (内置了 open JDK)
  • php/Yii (内置的不好用,自己编译,然后单独下载xdebug 并编译,有了xdebug可以在phpstorm下断点调试)

其他

  • cordova/phonegap
  • grunt/gulp
  • fis
  • hexo 博客系统

其他内置工具 safira Apache 邮件客户端

常见的配置 文件

与环境变量相关的配置文件

etc/profile ,/etc/bashrc;~/.bash_profile ;~/.bashrc;/etc/paths; /etc/paths.d 相关链接

  • 软件相关配置目录在 home目录下 隐藏文件 以.开头 比如.adobe .android .cache .cnpmrc .composer .config .cordova .dbshell .eclipse .gem .gradle .npm等等.

apache 配置文件

  • 主配置文件 /etc/apache2/httpd.conf
  • 虚拟主机配置 /etc/apache2/extra/httpd-vhosts.conf 参考

  • node 版本 webServer快速配置 适合前端开发使用,代码变化之后自动刷新页面 配置文件

  • 2015-四月开始使用atom 代码编辑器,感觉比sublime还要爽,不过打开大文件会卡。
    需要配合一些插件。比如git-plus,autocomplete-plus,color-picker,git-log,language-jade,open-last-project,terminal-panel(shift+enter)
  • 然而用了 atom 半年之久实在太卡了,最终转回来了sublime3 换了个好看的主题 需要配合一些插件 比如 less/sass jade jsformat terminal emmet git等

ECMAScript 6 新特性总结

angluarJS 指令与控制器中的方法的调用。

指令调用控制器中的方式(给指令的事件绑定执行函数)

angluarJS中,如果指令内部的事件触发,这个时候需要调用外部controller中的方法,可以在指令的隔离作用域中使用&将controller中的方法传递到指令中,供指令调用,需要注意的事,如果传递参数的话(比如自定义的ngChange事件触发,会把当前的选择项作为参数传递到控制器中)这个时候在声明指令时候,需要填写方法调用的函数表达式,并且如果包含函数的话,需要在调用改方法的时候指明参数,该参数名字与在指令中调用中传递的参数JSON的key一致(指令中必须传递JSON的形式来调用)

    angular.module("app1", [])
.controller("Ctrl1", function($scope, $element)
{
  $scope.someThingChanged = function(arg1,arg2)
  {
       console.log(arg1,arg2);
  }
})
.directive("myDirective", function($timeout)
{
  return {
    scope: {
      ngChange: '&'
    },
    link: function(scope, element, attrs)
    {
      $timeout(function()
      {
        scope.ngChange({
          name:'张三',
          age:28
        });
      });
    }
  }
})

声明指令

<div my-directive ng-change="someThingChanged(name,age)"></div>
调用时候上面输出 :  张三 28

控制器调用指令的方法。

使用双向绑定的形式,首先在控制器新建一个空对象,在指令中将方法赋值给这个对象的一个属性,这样在控制器用就可以获取这个方法,进而进行调用。

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
  $scope.focusinControl = {
  };
});
app.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:</div>',
    scope: {
      control: '='
    },
    link  : function (scope, element, attrs) {
      scope.internalControl = scope.control || {};
      scope.internalControl.takenTablets = 0;
      scope.internalControl.takeTablet = function() {
        scope.internalControl.takenTablets += 1;  
      }
    }
  };
});

html

<focusin control="focusinControl"></focusin>

less常用特性

less并非多么高大上的东西,无非就是个编写CSS的工具,让我们像编程一样来书写CSS,关于less常用的特性无非几个,下面简单将常用这些特性简单记录。方便以后查阅

less编译环境

less->CSS的方法有多种,比如使用 grunt或者lgulp等构建工具配合相应的插件来完成less的自动编译。以gulp为例,gulpfile.js的代码如下。

var less = require('gulp-less');
var path = require('path');
var gulp=require('gulp');
gulp.task('less', function () {
  gulp.src('less/*.less')
    .pipe(less({
      paths: [ path.join(__dirname, 'less', 'includes') ]
    }))
    .pipe(gulp.dest('css'));
});

gulp.watch(['less/*.less'], ['less']) 
gulp.task('default', ['less']);

通过watch 来见监听less文件的变化自动运行less任务。从而完成less->css的自动编译,可以查看生成的CSS文件

我常用的less 特性。

@import

在标准的CSS中,@import必须在所有其他类型的规则之前。但是Less不在乎你把@import语句放在什么位置。
@import语句会通过Less依赖文件扩展名的方式区别对待不同的文件:

  • 如果文件有一个.css扩展名,则将它作为CSS对象,同时@import语句保持不变
  • 如果有其他扩展名,则作为Less对象,然后导入它。
  • 如果没有扩展名,则插入.less,然后将它作为Less文件导入包含进来。
  • 使用@import (reference)导入外部文件,但是不添加导入的样式到编译输出中,只引用。

变量

LESS 允许开发者自定义变量,变量可以在全局样式中使用,变量使得样式修改起来更加简单。变量@开头,全局的变量可以在多个less文件之间共用

@light-blue: @nice-blue + #111;
#header {
  color: @light-blue;
}

生成的CSS 如下

.mythemes tableBorder { 
  border: 1px solid #b5bcc7; 
 }

同时还支持变量的计算。

@width: 10px + 5;
  .btn{
    width:@width;
  }

编译为:

@width: 10px + 5;
  .btn{
    width:@width;
  }

变量查找以及变量作用域:在定义一个变量两次时,只会使用最后定义的变量,Less会从当前作用域中向上搜索。这个行为类似于CSS的定义中始终使用最后定义的属性值。

@var: 0;
.class {
  @var: 1;
  .brass {
    @var: 2;
    three: @var;
    @var: 3;
  }
  one: @var;
}

编译后的CSS为:

.class {
  one: 1;
}
.class .brass {
  three: 3;
}

Mixins(混入)

在 LESS 中,混入是指在一个 CLASS 中引入另外一个已经定义的 CLASS,就像在当前 CLASS 中增加一个属性一样。

less源文件

.bordered {
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}
#menu a {
  color: #111;
  .bordered;
}

编译后的CSS文件:

.bordered {
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}
#menu a {
  color: #111;
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}

如果不想在CSS中保留原来的 .bordered

可以将 .bordered 修改为 bordered() ,同时还支持混入参数,支持默认参数。

less文件

.bordered(@width:5px) {
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
  width:@width;
}
#menu a {
  color: #111;
  .bordered;
}
.post a {
  color: red;
  .bordered(10px);
}

编译后的文件

#menu a {
  color: #111;
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
  width: 5px;
}
.post a {
  color: red;
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
  width: 10px;
}

像 JavaScript 中 arguments一样,Mixins 也有这样一个变量:@arguments。@arguments 在 Mixins 中具是一个很特别的参数,当 Mixins 引用这个参数时,该参数表示所有的变量,很多情况下,这个参数可以省去你很多代码。

.box1(@x:0,@y:0,@blur:1px,@color:#000){ 
   -moz-box-shadow: @arguments; 
   -webkit-box-shadow: @arguments; 
   box-shadow: @arguments; 
 } 
 .mybox { 
     .box1(10px,12px,13px,red); 
 }

编译后:

.mybox {
  -moz-box-shadow: 10px 12px 13px red;
  -webkit-box-shadow: 10px 12px 13px red;
  box-shadow: 10px 12px 13px red;
}

嵌套

在写CSS的时候 为了避免造成CSS污染 我们会采用命名空间的机制,在less中可以通过嵌套来解决。

less文件

.myspace{
     .class1{
         width:20px
     }
     .class2{
         height:30px
     }
 }

生成的CSS文件

.myspace .class1 {
  width: 20px;
}
.myspace .class2 {
  height: 30px;
}

在嵌套使用时可以通过 &访问上一级元素

#bundle {
  .button {
    display: block;
    border: 1px solid black;
    background-color: grey;
    &:hover {
      background-color: white
    }
  }
}

编译之后为

#bundle .button {
  display: block;
  border: 1px solid black;
  background-color: grey;
}
#bundle .button:hover {
  background-color: white;
}

在less中可以如果将mix和嵌套相结合。可以使用>来进行导入

比如 less文件

.myspace{
   .class1{
   width:20px
   }
   .class2{
   height:30px
   }
 }
 .redbutton{
     color:red;
     .myspace > .class2;
 }

CSS 文件

myspace .class1 {
  width: 20px;
}
.myspace .class2 {
  height: 30px;
}
.redbutton {
  color: red;
  height:
}

另外还有一些关于运算的函数 感觉意义不大 比如颜色计算的fadeIn等。

Comments(注释)

LESS 对注释也提供了支持,JavaScript 中的注释方法一样,主要有两种方式:单行注释和多行注释,这与 ,我们这里不做详细的说明,只强调一点:LESS 中单行注释 (// 单行注释 ) 是不能显示在编译后的 CSS 中,所以如果你的注释是针对样式说明的请使用多行注释

extend

extend是一个Less伪类,它会合并它所在的选择其和它所匹配的引用。

nav ul {
  &:extend(.inline);
  background: blue;
}

在上面设置的规则中,:extend选择器会在.inline类出现的地方在.inline上应用”扩展选择器”(也就是nav ul)。声明块保持原样,不会带有任何引用扩展(因为扩展并不是CSS)。

extend即为把当前的选择的器应用到相应的选择器定义的样式中,有点像组合的意思,跟混合的作用类似,基本是复用之前定义的样式,mix是将选择的样式引进来,extend是将当前的选择器。添加到之前定义的样式处。(有点绕啊)看代码吧

less文件

nav ul {
      &:extend(.inline);
      background: blue;
    }
    .inline {
      color: red;
    }

输出CSS:

nav ul {
  background: blue;
}
.inline,
nav ul {
  color: red;
}

另外还有一些关于运算的函数 感觉意义不大 比如颜色计算的fadeIn等。

Comments(注释)

LESS 对注释也提供了支持,JavaScript 中的注释方法一样,主要有两种方式:单行注释和多行注释,这与 ,我们这里不做详细的说明,只强调一点:LESS 中单行注释 (// 单行注释 ) 是不能显示在编译后的 CSS 中,所以如果你的注释是针对样式说明的请使用多行注释

ionic 环境搭建 (mac)

android基础环境 JAVA 、ADT (Android SDK tools) (该过程不详细叙述)

配置环境变量 sudo vim ~/.bash_profile 或者sudo vim .profile

export  PATH=${PATH}:/Users/adsage/work/adt/sdk/platform-tools:/Users/adsage/work/adt/sdk/tools
ANDROID_HOME=$HOME/work/adt/sdk
export ANDROID_HOME

使环境变量生效 source .bash_profile

将android SDK目录权限设置为可写

chmod -R 777 sdk/

android构建依赖ant

brew update &&brew install ant

下载android API 和 镜像

- 使用android SDK manager 下载android的API 和镜像(当然也可以离线下载)
- 使用Android Virtual manager 创建android的虚拟机

安装nodeJS 以及相关包

sudo npm install -g cordova ionic

创建一个项目

ionic start myApp tabs

编译并在真机上运行

cd myApp
$ ionic platform add android
$ ionic build android
$ ionic run android 或者ionic emulate android 在模拟上跑

程序已部署至真机 完成。

ios

另外 如果是IOS环境下 需要有IOS环境 在app store上下载安装XCODE即可, 另外在构建过程中还会提示缺少相关的npm包 比如

sudo npm install -g ios-sim
sudo npm install -g ios-deploy

然后 运行

ionic platform add ios
ionic build ios
ionic run ios 或者ionic emulate ios 在模拟上跑