算法

前端面试中的常见的算法问题

虽说我们很多时候前端很少有机会接触到算法。大多都交互性的操作,然而从各大公司面试来看,算法依旧是考察的一方面。实际上学习数据结构与算法对于工程师去理解和分析问题都是有帮助的。如果将来当我们面对较为复杂的问题,这些基础知识的积累可以帮助我们更好的优化解决思路。下面罗列在前端面试中经常撞见的几个问题吧。

Q1 判断一个单词是否是回文?

回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环。比如 mamam redivider .

很多人拿到这样的题目非常容易想到用for 将字符串颠倒字母顺序然后匹配就行了。其实重要的考察的就是对于reverse的实现。其实我们可以利用现成的函数,将字符串转换成数组,这个思路很重要,我们可以拥有更多的自由度去进行字符串的一些操作。

function checkPalindrom(str) {  
    return str == str.split('').reverse().join('');
}

Q2 去掉一组整型数组重复的值

比如输入: [1,13,24,11,11,14,1,2] 
输出: [1,13,24,11,14,2]
需要去掉重复的11 和 1 这两个元素。

这道问题出现在诸多的前端面试题中,主要考察个人对Object的使用,利用key来进行筛选。
let unique = function(arr) {
let hashTable = {};
let data = [];
for(let i=0,l=arr.length;i<l;i++) {
if(!hashTable[arr[i]]) {
hashTable[arr[i]] = true;
data.push(arr[i]);
}
}
return data

}

module.exports = unique;  

Q3 统计一个字符串出现最多的字母

给出一段英文连续的英文字符窜,找出重复出现次数最多的字母

输入 : afjghdfraaaasdenas 

输出 : a

前面出现过去重的算法,这里需要是统计重复次数。

function findMaxDuplicateChar(str) {  
      if(str.length == 1) {
    return str;
      }
      let charObj = {};
      for(let i=0;i<str.length;i++) {
        if(!charObj[str.charAt(i)]) {
          charObj[str.charAt(i)] = 1;
        }else{
          charObj[str.charAt(i)] += 1;
    }
  }
      let maxChar = '',
      maxValue = 1;
      for(var k in charObj) {
        if(charObj[k] >= maxValue) {
              maxChar = k;
              maxValue = charObj[k];
            }
      }
      return maxChar;

}

module.exports = findMaxDuplicateChar;  

Q4 排序算法

如果抽到算法题目的话,应该大多都是比较开放的题目,不限定算法的实现,但是一定要求掌握其中的几种,所以冒泡排序,这种较为基础并且便于理解记忆的算法一定需要熟记于心。冒泡排序算法就是依次比较大小,小的的大的进行位置上的交换。

function bubbleSort(arr) {  
    for(let i = 0,l=arr.length;i<l-1;i++) {
           for(let j = i+1;j<l;j++) { 
              if(arr[i]>arr[j]) {
                let tem = arr[i];
                arr[i] = arr[j];
                arr[j] = tem;
            }
        }
    }
    return arr;
}
module.exports = bubbleSort;  

除了冒泡排序外,其实还有很多诸如 插入排序,快速排序,希尔排序等。每一种排序算法都有各自的特点。全部掌握也不需要,但是心底一定要熟悉几种算法。 比如快速排序,其效率很高,而其基本原理如图(来自wiki):

算法参考某个元素值,将小于它的值,放到左数组中,大于它的值的元素就放到右数组中,然后递归进行上一次左右数组的操作,返回合并的数组就是已经排好顺序的数组了。

function quickSort(arr) {

if(arr.length<=1) {
    return arr;
}

let leftArr = [];
let rightArr = [];
let q = arr[0];
for(let i = 1,l=arr.length; i<l; i++) {
    if(arr[i]>q) {
        rightArr.push(arr[i]);
    }else{
        leftArr.push(arr[i]);
    }
}

return [].concat(quickSort(leftArr),[q],quickSort(rightArr));
}

module.exports = quickSort;  

安利大家一个学习的地址,通过动画演示算法的实现。

HTML5 Canvas Demo: Sorting Algorithms

Q5 不借助临时变量,进行两个整数的交换

输入 a = 2, b = 4 输出 a = 4, b =2
这种问题非常巧妙,需要大家跳出惯有的思维,利用 a , b进行置换。

主要是利用 + - 去进行运算,类似 a = a + ( b - a) 实际上等同于最后 的 a = b;

function swap(a , b) {  
      b = b - a;
      a = a + b;
      b = a - b;
      return [a,b];
}

module.exports = swap;  

Q6 使用canvas 绘制一个有限度的斐波那契数列的曲线?


数列长度限定在9.

斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列主要考察递归的调用。我们一般都知道定义

fibo[i] = fibo[i-1]+fibo[i-2];  

生成斐波那契数组的方法

function getFibonacci(n) {  
      var fibarr = [];
      var i = 0;
      while(i<n) {
        if(i<=1) {
              fibarr.push(i);
        }else{
              fibarr.push(fibarr[i-1] + fibarr[i-2])
        }
        i++;
      }

      return fibarr;
}

剩余的工作就是利用canvas arc方法进行曲线绘制了

DEMO

Q7 找出下列正数组的最大差值比如:

输入 [10,5,11,7,8,9]

输出 6

这是通过一道题目去测试对于基本的数组的最大值的查找,很明显我们知道,最大差值肯定是一个数组中最大值与最小值的差。

  function getMaxProfit(arr) {

       var minPrice = arr[0];
    var maxProfit = 0;

    for (var i = 0; i < arr.length; i++) {
        var currentPrice = arr[i];

        minPrice = Math.min(minPrice, currentPrice);

        var potentialProfit = currentPrice - minPrice;

        maxProfit = Math.max(maxProfit, potentialProfit);
    }

    return maxProfit;
}

Q8 随机生成指定长度的字符串

实现一个算法,随机生成指制定长度的字符窜。

比如给定 长度 8  输出 4ldkfg9j
function randomString(n) {  
      let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
      let tmp = '',
      i = 0,
      l = str.length;
      for (i = 0; i < n; i++) {
            tmp += str.charAt(Math.floor(Math.random() * l));
      }
      return tmp;
}

module.exports = randomString;  

Q9 实现类似getElementsByClassName 的功能

自己实现一个函数,查找某个DOM节点下面的包含某个class的所有DOM节点?不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供DOM查找函数。

function queryClassName(node, name) {  
      var starts = '(^|[ \n\r\t\f])',
       ends = '([ \n\r\t\f]|$)';
      var array = [],
    regex = new RegExp(starts + name + ends),
    elements = node.getElementsByTagName("*"),
    length = elements.length,
    i = 0,
    element;

while (i < length) {
    element = elements[i];
    if (regex.test(element.className)) {
        array.push(element);
    }

    i += 1;
}

return array;

}

Q10 使用JS 实现二叉查找树(Binary Search Tree)

一般叫全部写完的概率比较少,但是重点考察你对它的理解和一些基本特点的实现。 二叉查找树,也称二叉搜索树、有序二叉树(英语:ordered binary tree)是指一棵空树或者具有下列性质的二叉树:

  • 任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 任意节点的左、右子树也分别为二叉查找树;
  • 没有键值相等的节点。二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。

在写的时候需要足够理解二叉搜素树的特点,需要先设定好每个节点的数据结构

class Node {  
      constructor(data, left, right) {
        this.data = data;
        this.left = left;
        this.right = right;
      }

}

树是有节点构成,由根节点逐渐延生到各个子节点,因此它具备基本的结构就是具备一个根节点,具备添加,查找和删除节点的方法.

class BinarySearchTree {

      constructor() {
        this.root = null;
      }

  insert(data) {
    let n = new Node(data, null, null);
    if (!this.root) {
          return this.root = n;
    }
    let currentNode = this.root;
    let parent = null;
    while (1) {
          parent = currentNode;
          if (data < currentNode.data) {
            currentNode = currentNode.left;
            if (currentNode === null) {
                  parent.left = n;
                  break;
            }
      } else {
        currentNode = currentNode.right;
        if (currentNode === null) {
              parent.right = n;
              break;
            }
      }
    }
  }

  remove(data) {
    this.root = this.removeNode(this.root, data)
  }

  removeNode(node, data) {
    if (node == null) {
          return null;
    }

if (data == node.data) {
  // no children node
  if (node.left == null && node.right == null) {
    return null;
  }
  if (node.left == null) {
    return node.right;
  }
  if (node.right == null) {
    return node.left;
  }

  let getSmallest = function(node) {
    if(node.left === null && node.right == null) {
      return node;
    }
    if(node.left != null) {
      return node.left;
    }
    if(node.right !== null) {
      return getSmallest(node.right);
    }

  }
  let temNode = getSmallest(node.right);
  node.data = temNode.data;
  node.right = this.removeNode(temNode.right,temNode.data);
  return node;

} else if (data < node.data) {
  node.left = this.removeNode(node.left,data);
  return node;
} else {
  node.right = this.removeNode(node.right,data);
  return node;
}

}

  find(data) {
    var current = this.root;
    while (current != null) {
      if (data == current.data) {
        break;
      }
      if (data < current.data) {
        current = current.left;
      } else {
        current = current.right
      }
    }
    return current.data;
  }

}

module.exports = BinarySearchTree;  

完整代码 Github

扩展阅读
https://www.interviewcake.com/question/javascript/rectangular-love
http://stackoverflow.com/questions/21853967/get-elements-by-class-a-or-b-in-javascript

http://codepen.io/Jack_Pu/pen/EgrXBp

http://javascript-html5-tutorial.com/algorithms-and-data-structures-in-javascript.html

引自:http://www.jackpu.com/qian-duan-mian-shi-zhong-de-chang-jian-de-suan-fa-wen-ti/

jszhengze

正则表达式

正则表达式 - 语法

正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。

  • 列目录时, dir .txt或ls .txt中的.txt就不是一个正则表达式,因为这里与正则式的*的含义是不同的。
  • 构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与运算符可以将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。

正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为”元字符”)组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

非打印字符

非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:

字符 描述
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。

特殊字符

所谓特殊字符,就是一些有特殊含义的字符,如上面说的”.txt”中的,简单的说就是表示任何字符串的意思。如果要查找文件名中有的文件,则需要对进行转义,即在其前加一个\。ls *.txt。
许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符”转义”,即,将反斜杠字符 () 放在它们前面。下表列出了正则表达式中的特殊字符:

特别字符 描述
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline属性,则 $ 也匹配 ‘\n’ 或 ‘\r’。要匹配 $ 字符本身,请使用 \$。
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。
* 匹配前面的子表达式零次或多次。要匹配 字符,请使用 \
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。
. 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 . 。
[ 标记一个中括号表达式的开始。要匹配 [,请使用 [。
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n’ 匹配字符 ‘n’。’\n’ 匹配换行符。序列 ‘\‘ 匹配 “\”,而 ‘(‘ 则匹配 “(“。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
{ 标记限定符表达式的开始。要匹配 {,请使用 {。
指明两项之间的一个选择。要匹配 ,请使用 \

限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。
正则表达式的限定符有:

字符 描述
* 匹配前面的子表达式零次或多次。例如,zo 能匹配 “z” 以及 “zoo”。 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,”do(es)?” 可以匹配 “do” 或 “does” 中的”do” 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,’o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,’o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。’o{1,}’ 等价于 ‘o+’。’o{0,}’ 则等价于 ‘o*’。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,”o{1,3}” 将匹配 “fooooood” 中的前三个 o。’o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。

定位符

定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。
定位符用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。
正则表达式的限定符有:

字符 描述
^ 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。
$ 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。
\b 匹配一个字边界,即字与空格间的位置。
\B 非字边界匹配。

正则表达式 -运算符优级

字符 描述
\ 转义符
(), (?:), (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, \任何元字符、任何字符 定位点和序列(即:位置和顺序)
(或” “) 替换,”或”操作字符具有高于替换运算符的优先级,使得”m food”匹配”m”或”food”。若要匹配”mood”或”food”,请使用括号创建子表达式,从而产生”(m f)ood”。

常见正则表达式:

整数


[0-9]+

###逗号分隔的整数


\b[0-9]{1,3}(,[0-9]{3})*\b

浮点数


(\+?(\d+|\.\d+|\d+\.\d+)|-?(\d+|\d+\.\d+))

0-255之间的数字


^([0-9]|[0-9]{2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$

身份证


^[1-9]\d{14}(\d{2}[0-9x])?$

邮箱


^[-\w.]{0,64}@([a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$

固定电话


(\(?0[1-9]{2,3}\)?-?)?[1-9][0-9]\{6,7}(-[0-9]{1,6})?

邮编


[1-9][0-9]{5}

ISBN


((ISBN(-13)?:?\s)?97[89][-\s]?[0-9][-\s]?[0-9]{3}[-\s]?[0-9]{5}[-\s]?[0-9]|(ISBN(-10)?:?\s)?[0-9][-\s]?[0-9]{3}[-\s]?[0-9]{5}[-\s]?[0-9x])

手机号


(0|\+86)?(13[0-9]|15[0-356]|18[025-9])\d{8}

成对的html tag


<code>test</code>
<([^>]+)>[\s\S]*?<\/\1>

a


<a\s+href\s*=\s*["']?([^"'\s]+)["']?>([^<]+)<\/a>


<head>([^>]+)<\/head>

图片


<img\s[^>]*?src=['"]?([^"']+)["']?[^>]*>

附:正则指引思维导图

参考文档:http://www.lixuejiang.me/2016/10/13/%E5%B8%B8%E8%A7%81%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/

http://www.runoob.com/regexp/regexp-tutorial.html

cssCenter

css

CSS的垂直居中和水平居中总结

知乎专栏&&简书专题:前端进击者(知乎) 前端进击者(简书)

前言

CSS居中一直是一个比较敏感的话题,为了以后开发的方便,楼主觉得确实需要总结一下了,总的来说,居中问题分为垂直居中和水平居中,实际上水平居中是很简单的,但垂直居中的方式和方法就千奇百怪了。

内联元素居中方案

水平居中设置:

  1. 行内元素 设置 text-align:center;

  2. Flex布局 设置display:flex;justify-content:center;(灵活运用)

垂直居中设置:

  1. 父元素高度确定的单行文本(内联元素) 设置 height = line-height;
  2. 父元素高度确定的多行文本(内联元素) a:插入 table (插入方法和水平居中一样),然后设置 vertical-align:middle; b:先设置 display:table-cell 再设置 vertical-align:middle;

    块级元素居中方案

水平居中设置:

  1. 定宽块状元素 设置 左右 margin 值为 auto;

  2. 不定宽块状元素 a:在元素外加入 table 标签(完整的,包括 table、tbody、tr、td),该元素写在 td 内,然后设置 margin 的值为 auto; b:给该元素设置 display:inine 方法; c:父元素设置 position:relative 和 left:50%,子元素设置 position:relative 和 left:50%;

垂直居中设置:

  • 1.使用position:absolute(fixed),设置left、top、margin-left、margin-top的属性;

    .box{
        position:absolute;/*或fixed*/
        top:50%;
        left:50%;
        margin-top:-100px;
        margin-left:-200px;
    }
    
  • 2.利用position:fixed(absolute)属性,margin:auto这个必须不要忘记了;

    .box{
        position: absolute;或fixed
        top:0;
        right:0;
            bottom:0;
        left:0;
        margin: auto;
    }
    
  • 3.利用display:table-cell属性使内容垂直居中;

    .box{
    display:table-cell;
        vertical-align:middle;
    text-align:center;
        width:120px;
    height:120px;
    background:purple;
    }
    
  • 4.使用css3的新属性transform:translate(x,y)属性;

    .box{
        position: absolute;
        transform: translate(50%,50%);
        -webkit-transform:translate(50%,50%);
        -moz-transform:translate(50%,50%);
        -ms-transform:translate(50%,50%);
    }
    
  • 5.最高大上的一种,使用:before元素;

    .box{
    position:fixed;
    display:block;
    background:rgba(0,0,0,.5);
    }
    .box:before{
    content:'';
    display:inline-block;
    vertical-align:middle;
       height:100%;
    }
    .box.content{
    width:60px;
    height:60px;
    line-height:60px;
    color:red;
    
  • 6.Flex布局;

    .box{
    display: -webkit-box;
    display: -webkit-flex;
    display: -moz-box;
    display: -moz-flex;
    display: -ms-flexbox;
    display: flex;
    水平居中
    -webkit-box-align: center;
    -moz-box-align: center;
    -ms-flex-pack:center;
    -webkit-justify-content: center;
    -moz-justify-content: center;
        justify-content: center;
     垂直居中
    -webkit-box-pack: center;
    -moz-box-pack: center;
        -ms-flex-align:center;
    -webkit-align-items: center;
    -moz-align-items: center;
    align-items: center;
    

    }

    结语

    博主暂时掌握了这些居中方法,读者如果还有好方法或是觉得那个地方不对,欢迎评论,不吝感谢。
    转载自:http://gold.xitu.io/post/582c04032f301e00594327d4?utm_source=gold_browser_extension

sass

sass

SASS用法指南

一、什么是SASS

SASS是一种CSS的开发工具,提供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单和可维护。
本文总结了SASS的主要用法。我的目标是,有了这篇文章,日常的一般使用就不需要去看官方文档了。

二、安装和使用

2.1 安装

SASSRuby语言写的,但是两者的语法没有关系。不懂Ruby,照样使用。只是必须先安装Ruby,然后再安装SASS。
假定你已经安装好了Ruby,接着在命令行输入下面的命令:

gem install sass

 
然后,就可以使用了。

2.2 使用

SASS文件就是普通的文本文件,里面可以直接使用CSS语法。文件后缀名是.scss,意思为Sassy CSS。
下面的命令,可以在屏幕上显示.scss文件转化的css代码。(假设文件名为test。)

sass test.scss

如果要将显示结果保存成文件,后面再跟一个.css文件名。 

sass test.scss test.css

SASS提供四个编译风格的选项:

  • nested:嵌套缩进的css代码,它是默认值。
  • expanded:没有缩进的、扩展的css代码。
  • compact:简洁格式的css代码。
  • compressed:压缩后的css代码。
    生产环境当中,一般使用最后一个选项。
sass --style compressed test.sass test.css

你也可以让SASS监听某个文件或目录,一旦源文件有变动,就自动生成编译后的版本。

// watch a file
sass --watch input.scss:output.css
// watch a directory
sass --watch app/sass:public/stylesheets

SASS的官方网站,提供了一个在线转换器。你可以在那里,试运行下面的各种例子。

三、基本用法

3.1 变量

SASS允许使用变量,所有变量以$开头.

$blue : #1875e7; 
div {
color : $blue;
    }

如果变量需要镶嵌在字符串之中,就必须需要写在#{}之中.

$side : left;
.rounded {
    border-#{$side}-radius: 5px;
    }
3.2 计算功能

SASS允许在代码中使用算式.

body {
 margin: (14px/2);
 top: 50px + 100px;
 right: $var * 10%;
 }
3.3 嵌套

SASS允许选择器嵌套。比如,下面的CSS代码:

div h1 {
color : red;
}

可以写成

div {
    hi {
        color:red;            
    }
}

属性也可以嵌套,比如border-color属性,可以写成:

p {
    border: {
     color: red;
     }
 }

注意,border后面必须加上冒号。
在嵌套的代码块内,可以使用&引用父元素。比如a:hover伪类,可以写成:

a {
    &:hover { color: #ffb3ff; }
}
3.4 注释

SASS共有两种注释风格。
标准的CSS注释 / comment / ,会保留到编译后的文件。
单行注释 // comment,只保留在SASS源文件中,编译后被省略。
在/*后面加一个感叹号,表示这是”重要注释”。即使是压缩模式编译,也会保留这行注释,通常可以用于声明版权信息.

/*!
 重要注释!
 */

四、代码的重用

4.1 继承

SASS允许一个选择器,继承另一个选择器。比如,现有class1:

 .class1 {
     border: 1px solid #ddd;
 }

class2要继承class1,就要使用@extend命令:

.class2 {
    @extend .class1;
     font-size:120%;
 }
4.2 Mixin

Mixin有点像C语言的宏(macro),是可以重用的代码块。
使用@mixin命令,定义一个代码块。

@mixin left {
    float: left;
    margin-left: 10px;
}

使用@include命令,调用这个mixin。

div {
     @include left;
 }

mixin的强大之处,在于可以指定参数和缺省值。

@mixin left($value: 10px) {
    float: left;
    margin-right: $value;
}

使用的时候,根据需要加入参数:

div {
    @include left(20px);
}

下面是一个mixin的实例,用来生成浏览器前缀。

@mixin rounded($vert, $horz, $radius: 10px) {
    border-#{$vert}-#{$horz}-radius: $radius;
    -moz-border-radius-#{$vert}#{$horz}: $radius;
    -webkit-border-#{$vert}-#{$horz}-radius: $radius;
}

使用的时候,可以像下面这样调用

 #navbar li { @include rounded(top, left); }
 #footer { @include rounded(top, left, 5px); }
4.3 颜色函数

SASS提供了一些内置的颜色函数,以便生成系列颜色。

lighten(#cc3, 10%) // #d6d65c
darken(#cc3, 10%) // #a3a329
grayscale(#cc3) // #808080
complement(#cc3) // #33c
4.4 插入文件

@import命令,用来插入外部文件。

@import "path/filename.scss";

如果插入的是.css文件,则等同于css的import命令。

@import "foo.css";

五、高级用法

5.1 条件语句

@if可以用来判断:

p {
    @if 1 + 1 == 2 { border: 1px solid; }
    @if 5 < 3 { border: 2px dotted; }
}

配套的还有@else命令:

@if lightness($color) > 30% {
 background-color: #000;
 } @else {
  background-color: #fff;
 }
5.2 循环语句

SASS支持for循环:

@for $i from 1 to 10 {
.border-#{$i} {
    border: #{$i}px solid blue;
    }
}

也支持while循环:

$i: 6;
@while $i > 0 {
    .item-#{$i} { width: 2em * $i; }
    $i: $i - 2;
}

each命令,作用与for类似:

 @each $member in a, b, c, d {
 .#{$member} {
     background-image: url("/image/#{$member}.jpg");
      }
 }
5.3 自定义函数

SASS允许用户编写自己的函数。

 @function double($n) {
         @return $n * 2;
 }
 #sidebar {
     width: double(5px);
 }

nodeHeartModule

Node.js 手册查询-1-核心模块方法

模块

核心模块

核心模块是被编译成二进制代码,引用的时候只需require表示符即可

os 系统基本信息

os模块可提供操作系统的一些基本信息

1.返回系统临时目录

 os.tmpdir()
结果如: C:\Users\ADMINI~1\AppData\Local\Temp

2.返回 CPU 的字节序,可能的是 “BE” 或 “LE”

 os.endianness()
结果如: LE   (inter i3)

3.返回操作系统的主机名

 os.hostname()
结果如: QH-20141006HJKT

4.返回操作系统名称

 os.type()
结果如: Windows_NT

5.返回操作系统平台

 os.platform()
结果如: win32

6.返回操作系统 CPU 架构,可能的值有 “x64”、“arm” 和 “ia32”

 os.arch()
结果如: x64

7.返回操作系统的发行版本

 os.release()
结果如: 6.1.7601

8.返回操作系统运行的时间,以秒为单位

 os.uptime()
结果如: 7847.6797442

9.返回一个包含 1、5、15 分钟平均负载的数组

 os.loadavg()
结果如: 6.1.7601

10.返回系统内存总量,单位为字节

os.totalmem()
结果如: 3931602944

11.返回操作系统空闲内存量,单位是字节

 os.freemem()
结果如: 1307422720

12.返回一个对象数组,包含所安装的每个 CPU/内核的信息:型号、速度(单位 MHz)、时间(一个包含 user、nice、sys、idle 和 irq 所使用 CPU/内核毫秒数的对象)

os.cpus()

13.获取网络接口的一个列表信息

 os.networkInterfaces()
结果如: 1307422720

14.一个定义了操作系统的一行结束的标识的常量

os.EOL

process 进程管理

process 是一个全局变量.无需 require;

1.返回应用程序当前目录

 process.cwd()
结果如: c:\Users\Administrator\MongoDb_Test

2.改变应用程序目录

process.chdir(“目录”)

3.标准输出流.将内容打印到输出设备上.console.log就是封装了它

process.stdout.write(‘aa\n’)
结果如: aa

4.标准错误流

 process.stderr.write(‘aa\n’)
结果如: aa

5.进程的输入流. 通过注册事件的方式来获取输入的内容

 process.stdin.on(‘readable’, function() {
var chunk = process.stdin.read();
if (chunk !== null) {
process.stdout.write('data: ’ + chunk);
}
});

6.结束进程

 process.exit(code);
参数code为退出后返回的代码,如果省略则默认返回0

7.注册事件

 process.stdout.on(‘data’,function(data){
console.log(data);
});

8.为事件循环设置一项任务,Node.js 会在下次事件循环调响应时调用 callback

process.nextTick(callback)

8.设置编码, 默认 utf8. node.js编码格式只支持UTF8、ascii、base64,暂时不支持GBK、gb2312

 process.stdin.setEncoding(编码);
process.stdout.setEncoding(编码);
process.stderr.setEncoding(编码);

fs 文件管理

fs 模块提供了异步和同步2个版本 fs.readFile() fs.readFileSync()

1.写入文件内容

fs.writeFile(‘test.txt’, ‘Hello Node’ , [encoding], [callback]);

2.追加写入

fs.appendFile(‘test.txt’,‘Hello Node’,[encoding],[callback]);

3.文件是否存在

fs.exists(‘test.txt’,[callback]);

4.修改文件名

fs.rename(旧文件,新文件,[callback]);

5.移动文件. 没有专门函数,通过修改文件名可以达到目的

fs.rename(oldPath,newPath,[callback]);

6.读取文件内容

fs.readFile(‘test.txt’, [encoding], [callback]);

7.删除文件

fs.unlink(‘test.txt’, [callback]);

8.创建目录

 fs.mkdir(路径, 权限, [callback]);
路径:新创建的目录。
权限:可选参数,只在linux下有效,表示目录的权限,默认为0777,    表示文件所有者、文件所有者所在的组的*用户、*所有用户,都有权限进行读、写、执行的操作。

9.删除目录

fs.rmdir(path, [callback]);

10.读取目录. 读取到指定目录下所有的文件

fs.readdir(path, [callback]);

11.打开文件

fs.open(path,flags, [mode], [callback(err,fd)])

12.关闭文件

 fs.close(fd, [callback(err)])
fd 是打开文件后的标示符

13.读取文件

fs.read(fd,buffer,offset,length,position,[callback(err, bytesRead, buffer)])

14.写入文件

fs.writeFile(filename, data,[encoding],[callback(err)])

15.获取真实路径

fs.realpath(path, [callback(err,resolvedPath)])

16.更改所有权

fs.chown(path, uid, gid, [callback(err)])

17.更改权限

fs.fchmod(fd, mode, [callback(err)])

18.获取文件信息

fs.stat(path, [callback(err, stats)])

19.创建硬链接

fs.link(srcpath, dstpath, [callback(err)])

20.读取链接

fs.readlink(path, [callback(err,linkString)])

21.修改文件时间戳

fs.utimes(path, atime, mtime, [callback(err)])

22.同步磁盘缓存

fs.fsync(fd, [callback(err)])
url 文件管理

1.解析url,返回一个json格式的数组

 url.parse(‘http://www.baidu.com’);
结果如: { protocol: ‘http:’,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: null,
query: null,
pathname: ‘www.baidu.com’,
path: ‘www.baidu.com’,
href: ‘http://www.baidu.com’ }

2.解析url - 条件解析

 url.parse(‘http://www.baidu.com?page=1’,true);
结果如: { protocol: ‘http:’,
slashes: true,
auth: null,
host: ‘www.baidu.com’,
port: null,
hostname: ‘www.baidu.com’,
hash: null,
search: ‘?page=1’,
query: { page: ‘1’ },
pathname: ‘/’,
path: ‘/?page=1’,
href: ‘http://www.baidu.com/?page=1’ }

3.解析主机

 url.parse(‘http://www.baidu.com/news’,false,true);
结果如:{ protocol: ‘http:’,
slashes: true,
auth: null,
host: ‘www.baidu.com’,
port: null,
hostname: ‘www.baidu.com’,
hash: null,
search: null,
query: null,
pathname: ‘/news’,
    path: ‘/news’,
href: ‘http://www.baidu.com/news’ }

4.格式化为url. 将json数组逆向成url

 url.format({
protocol: ‘http:’,
hostname:‘www.baidu.com’,
port:‘80’,
pathname :’/news’,
query:{page:1}
});
结果如: http://www.baidu.com/news?page=1

5.封装路径

 url.resolve(‘http://example.com/’, ‘/one’)  // 'http://example.com/one’
url.resolve(‘http://example.com/one’, ‘/two’) // ‘http://example.com/two’
path 路径管理

用于处理和转换文件路径的工具集,用于处理目录的对象

1.格式化路径. 将不符合规范的路径经过格式化转换为标准路径,解析路径中的.与…外,还能去掉多余的斜杠

 path.normalize(’/path///normalize/hi/…’);
结果如: ‘/path/normalize/’ 标准化之后的路径里的斜杠在Windows系统下是,而在Linux系统下是/

2.组合路径

path.join(’///you’, ‘/are’, ‘//beautiful’);
结果如:  ‘/you/are/beautiful’

3.返回路径中的目录名

 path.dirname(’/foo/strong/cool/nice’);
结果如: ‘/foo/strong/cool’

4.返回路径最后一部分,还可以排除指定字符串

 path.basename(’/foo/strong/basename/index.html’);
path.basename(’/foo/strong/basename/index.html’,’.html’);
结果如: index.html 和 index

5.返回路径后缀

 path.extname(‘index.html’);
结果如: .html

Query String 字符串转换

用于实现URL参数字符串与参数对象之间的互相转换

1.序列化对象.将对象类型转换成一个字符串类型(默认的分割符(“&”)和分配符(“=”))

 1 querystring.stringify({foo:‘bar’,cool:[‘xux’, ‘yys’]});
结果如: foo=bar&cool=xux&cool=yys
1
2
2 重载1: querystring.stringify({foo:‘bar’,cool:[‘xux’, ‘yys’]},’*’,’$’);
结果如: foo$bar*cool$xux*cool$yys

2.反序列化

querystring.parse(‘foo@bar$cool@xux$cool@yys’,’@’,’$’);
结果如: { foo: ‘bar’ ,  cool: [‘xux’, ‘yys’] }

util 使用工具

提供常用函数的集合,用于弥补核心JavaScript的一些功能过于精简的不足。并且还提供了一系列常用工具,用来对数据的输出和验证

1.任意对象转化为字符串,用于调试输出

util.inspect(object,[showHidden],[depth],[colors])

2.格式化字符串. 就像c语言的 printf函数
支持的占位符有:“%s(字符串)”、“%d(数字<整型和浮点型>)”、“%j(JSON)”、“%(单独一个百分号则不作为一个参数)”

 util.format(’%s:%s’, ‘foo’);
结果如: foo:%s
1
2
util.format(’%s:%s’, ‘foo’, ‘bar’, ‘baz’);
结果如: foo:bar baz 额外的参数将会调用util.inspect()转换为 字符串连接在一起
util.format(1, 2, 3);
结果如: 1 2 3 , 第一个参数是一个非格式化字符串,则会把所有的参数转成字符串并以空格隔开拼接在一块

3.验证函数

 util.isArray(object);
判断对象是否为数组类型,是则返回ture,否则为false
1
2
util.isDate(object);
判断对象是否为日期类型,是则返回ture,否则返回false
util.isRegExp(object);
判断对象是否为正则类型,是则返回ture,否则返回false
,util.isArray()、util.isRegExp()、util.isDate()、util.isError()  util.format()、util. debug()

4.继承

 util.inherits( 子类, 基类)
实现对象间原型继承

child_process 子进程

node.js是基于单线程模型架构,这样的设计可以带来高效的CPU利用率,但是无法却利用多个核心的CPU,为了解决这个问题,node.js提供了child_process模块,通过多进程来实现对多核CPU的利用. child_process模块提供了四个创建子进程的函数,分别是spawn,exec,execFile和fork

1.用给定的命令发布一个子进程,带有‘args’命令行参数

 child_process.spawn(command, [args], [options])
command {String}要运行的命令行
args {Array} 字符串参数列表
options {Object}
cwd {String} 子进程的当前的工作目录
stdio {Array|String} 子进程 stdio 配置. (参阅下文)
customFds {Array} Deprecated 作为子进程 stdio 使用的 文件标示符. (参阅下文)
env {Object} 环境变量的键值对
detached {Boolean} 子进程将会变成一个进程组的领导者. (参阅下文)
uid {Number} 设置用户进程的ID. (See setuid(2).)
gid {Number} 设置进程组的ID. (See setgid(2).)
返回: {ChildProcess object}

2.创建子进程

child_process.exec(command, [options], callback)

3.创建进程 - 直接运行指定文件

child_process.execFile( file);

4.直接运行Node.js模块

 child_process.fork( modulePath );
fork函数只支持运行JavaScript代码

http HTTP

2个方法

 http.createServer([requestListener])  创建HTTP服务器
http.createClient([port], [host])     创建HTTP客户端
http.Server

由 http.createServer 创建所返回的实例

http.Server 事件

request 客户端请求到来
提供2个参数: req, res 分别是http.ServerRequest 和 http.ServerResponse 的实例.表示请求和响应消息

connection TCP建立连接时触发
提供1个 socket 参数 net.Socket 实例

close 服务器关闭时触发
无参数

还有checkContinue 、 upgrade 、 clientError 等事件. request 经常使用,所以包含在了 createServer函数中

http.ServerRequest 对象

HTTP请求的消息, 一般由 http.Server的 request 事件发送

属性:

complete 客户端请求是否已经发送完成

httpVersion HTTP 协议版本,通常是 1.0 或 1.1

method HTTP 请求方法,如 GET、POST、PUT、DELETE 等

url 原始的请求路径,例如 /static/image/x.jpg 或 /user?name=byvoid

headers HTTP 请求头

trailers HTTP 请求尾(不常见)

connection 当前 HTTP 连接套接字,为 net.Socket 的实例

socket connection 属性的别名

client client 属性的别名

GET:

的请求是直接嵌入路径中的. 解析?后面的路径就行了. url 模块中 parse 函数提供了这个功能

POST:
1
2
3
4
5
**HTTP 请求分: 请求头. 请求体**
http.ServerRequest 提供3个事件控制请求体传输
data: 请求体数据来到时. 参数 chunk 表示接收到的数据
end: 请求体数据传输完成时
close 用户当前请求结束时
var post = '';
req.on('data', function(chunk) {    post += chunk;    });
req.on('end', function() {    post = querystring.parse(post);
res.end(util.inspect(post));});
http.ServerResponse 对象

http.ServerResponse是返回给客户端的信息,决定了用户最终看到的结果. 有3个重要的成员函数. 用于响应头,响应内容以及结束请求

1.向客户端发送响应头

 res.writeHead(statusCode, [headers])
statusCode, 是HTTP状态码. 如200,404.

headers 类似数组的对象,表示响应头的每个属性
{
“Content-Type”: “text/html”,
“Connection”: “keep-alive”
}

2.发送响应内容, 如果是字符串,需要制定编码方式, 默认 utf-8

res.write(data, [encoding])

3.结束响应,告知客户端所有响应完成. 此函数必须调用一次

res.end([data] , [encoding] )

文件模块

文件模块,则是指js文件、json文件或者是.node文件

核心

事件

挂接模块

var EventEmitter = require(‘events’).EventEmitter;

1.实例化一个EventEmitter对象

var event = new EventEmitter();

2.注册事件

 emitter.on( ‘Event_Name’ , callBack_Fun )
emitter.once( ‘Event_Name’ , callBack_Fun )    //注册一个单次监听器,触发一次后立刻解除

3.发射事件

event.emit(‘Event_Name’ , 参数1,参数2);

4.移除事件

 emitter…removeListener(‘Event_Name’ , callBack_Fun)
emitter.removeAllListeners(  [‘Event_Name’] )    //如果指定了事件名,就移除指定的,否则移除所有事件

模块和包

Node.js 的模块和包机制的实现参照了 CommonJS 的标准,但并未完全遵循。

#### 包
包是在模块基础上更深一步的抽象, 类似于 c/c++ 的函数库.
Node.js 包是一个目录. 其中包含一个JSON格式的说明文件     package.json
CommonJS 规范特征:
->   package.json 必须在包的顶层目录下
->   二进制文件在bin目录下
->   JavaScript代码在lib目录下
->   文档应该在doc目录下
->   单元测试在test目录下




#### require('Modile_Name')     
功能:    加载其他模块
说明:    不会重复加载以加载的模块


#### exports.setName
功能:    公开一个模块中的函数或对象
说明:    exports 本身仅仅是一个普通的空对象,即 {}. 所以     exports.函数  就是给它加了函数
module.exports  则是用一个对象取代 exports  对象. (不可以对 exports 直接赋值替代此功能)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
方式1:
//使用
exports.SayName = function(thyName) {console.log(thyName)};
//调用
var test = require('./fileName');
test.SayName('XueYou');
方式2:
//使用
function hello(){
var name;
this.setNam(){};
this.SayName(){};
}
module.exports = hello;
//调用
var test = require('./fileName');
test = new test(); //注意因为是对象,所以要new
test.SayName();

其他

express 与 webstorm 配置

Node interpreter : node.exe 路径
Node parameters : ./bin/www
Working directory : 工作目录
JavaScript file : app.js

mongoose

mongo

Mongoose全面理解

一、创建schemas

创建schemas的方式:

var userSchema = new mongoose.Schema({
    name: String,
    email: String,
       createdOn: Date
});

schemas中的数据类型有以下几种:

  • String
  • Number
  • Date
  • Boolean
  • Buffer
  • ObjectId
  • Mixed
  • Array

特别需要说明一下ObjectId类型和Mixed类型以及Array类型,在schemas中声明这几种类型的方式如下:

1 //ObjectId就类似于唯一键值
 2 projectSchema.add({
3     owner: mongoose.Schema.Types.ObjectId
4 });
 5 //混合类型,顾名思义,就是说里面可以放置任意类型的数据,有两种方式创建该类型数据
6 //方式一:直接赋予一个空的字面量对象
7 vardjSchema= new mongoose.Schema({
8     mixedUp: {}
9 });
10 //方式二:根据Schemas.Types中值来赋予
11 vardjSchema= new mongoose.Schema({
12     mixedUp: Schema.Types.Mixed
13 });
14 //Array类型数据有两种创建方式,一种是简单数组创建:
15 var userSchema = new mongoose.Schema({
16     name: String,
17     emailAddresses: [String]
18 });
19 //第二种方式就是复杂类型数据数组,例如我们可以再数组中添加不同类型的schemas:
20 var emailSchema = new mongoose.Schema({
21     email: String,
22     verified: Boolean
23 });
24 var userSchema = new mongoose.Schema({
25     name: String,
26     emailAddresses: [emailSchema]
27 });
28 //注意:如果定义一个空的数据的话,则会创建为一个混合类型数据的数组:
29 var emailSchema = new mongoose.Schema({
30     email: String,
31     verified: Boolean
32 });
33 var userSchema = new mongoose.Schema({
34     name: String,
35     emailAddresses: [emailSchema]
36 });

我们可以给schema创建静态方法,这个静态方法将来会用在Model中,创建该静态方法需要在创建完成schema之后,在Model编译之前:

1 projectSchema.statics.findByUserID = function (userid, callback) {
2   this.find({ createdBy: userid }, '_id projectName', {sort: 'modifiedOn'}, callback);
3 };

在其对应的模型创建完成并编译后,我们就可以像下面这样来调用该静态方法了:
Model.findByUserID(userid,callback);
该静态方法会返回一个JSON格式的数据,这在我们使用AJAX技术来加载网页数据的时候会比较方便,就像下面这样:

 1 //路由规则:app.get('/project/byuser/:userid', project.byUser);
 2 exports.byUser = function (req, res) {
 3     console.log("Getting user projects");
 4     if (req.params.userid){
 5         Project.findByUserID(req.params.userid,function (err, projects) {
 6             if(!err){
 7                 console.log(projects);
 8                 res.json(projects);
 9             }else{
10                 console.log(err);
11                 res.json({"status":"error", "error":"Error finding projects"});
12             }
13         });
14     }else{
15         console.log("No user id supplied");
16         res.json({"status":"error", "error":"No user     id supplied"});
17     }
18 };

二、创建Model

创建Model很简单:

Mongoose.Model(‘User’, userSchema);

参数一为Model的名字,参数二为生成Model所需要的schema,Model就像是schema所编译而成的一样。

mongoose连接数据库是有两种方式的:

 1 //方式一:
 2 var dbURI = 'mongodb://localhost/mydatabase';
 3 mongoose.connect(dbURI);
 4 //方式二:
5 var dbURI = 'mongodb://localhost/myadmindatabase';
6 var adminConnection = mongoose.createConnection(dbURI);
7 //如果需要声明端口号:
 8 var dbURI = 'mongodb://localhost:27018/mydatabase';
 9 //如果需要定义用户名和密码:
10 var dbURI = 'mongodb://username:password@localhost/mydatabase';
11 //也可以像下面这样传一个对象类型的参数:
12 var dbURI = 'mongodb://localhost/mydatabase';
13 var dbOptions = {'user':'db_username','pass':'db_password'};
14 mongoose.connect(dbURI, dbOptions);

根据连接数据库的方式,我们可以得到第二种创建Model的方式,就是使用数据库连接的引用名来创建:

adminConnection.model( ‘User’, userSchema );

默认情况下mongoose会根据我们传入的Model名字来生成collection名字,在上面的代码中就会生成名为users(全为小写字母)的collection(集合);

有两种方法能让我们自定义collection的名字。

 1 //方式一,在创建schema的时候定义collection的名字:
 2 var userSchema = new mongoose.Schema({
 3     name: String,
 4     email: {type: String, unique:true}
 5 },
 6 {
 7     collection: 'myuserlist'
 8 });
 9 //方式二,在创建Model的时候定义collection的名字:
10 mongoose.model( 'User', userSchema, 'myuserlist' );

创建Model实例:

var user = new User({ name: ‘Simon’ });

user就是模型User的一个实例,它具有mongoose中模型所具有的一些方法,例如保存实例:

1 user.save(function (err) {
2     if (err) return handleError(err);
3 });

模型也具有一些常用的增删查改的方法:

 1 User.findOne({'name' : 'Sally', function(err,user) {
 2     if(!err){
 3         console.log(user);
 4     }
 5 });
 6 User.find({}, function(err, users) {
 7     if(!err){
 8         console.log(users);
 9     }
10 });

可以使用链式方式使用这些方法,例如:

1 var newUser = new User({
2     name: 'Simon Holmes',
3     email: 'simon@theholmesoffice.com',
4     lastLogin : Date.now()
5 }).save( function( err ){
6     if(!err){
7         console.log('User saved!');
8     }
9 });

上面的代码创建了一个模型实例,然后进行保存。我们有一个更为简介的方式来完成这项工作,就是使用Model.create()方法:

1 User.create({
 2     name: 'Simon Holmes',
 3     email: 'simon@theholmesoffice.com',
 4     lastLogin : Date.now()
 5 }, function( err, user ){
 6     if(!err){
 7         console.log('User saved!');
 8         console.log('Saved user name: ' + user.name);
 9         console.log('_id of saved user: ' + user._id);
10     }
11 });

三、查找数据和读取数据的方法

1.使用QueryBuilder接口来查找数据

先看看下面的代码:

1 var myQuery = User.find({'name' : 'Simon Holmes'});
2 myQuery.where('age').gt(18);
3 myQuery.sort('-lastLogin');
4 myQuery.select('_id name email');
5 myQuery.exec(function (err, users){
6     if (!err){
7         console.log(users); // output array of users found
8     }
9 });

代码中,我们查找名字为”Simon Holmes”,并且年龄大于18岁,查找结果根据lastLogin降序排列,只获取其中的_id, name, email三个字段的值,上面的代码只有在调用exec方法后才真正执行数据库的查询。

当然我们可以使用链式的方式来改写上面的代码,代码会更加简洁:

1 User.find({'name' : 'Simon Holmes'})
2 .where('age').gt(18)
3 .sort('-lastLogin')
4 .select('_id name email')
5 .exec(function (err, users){
6     if (!err){
7         console.log(users); // output array of users found
8     }
9 });

上面代码中的第一行创建了一个queryBuilder.通过使用这个queryBuilder,我们就可以执行一些比较复杂的查找工作,

在创建完成这个queryBuilder之后,查询操作并没有马上执行,而是待到执行exec方法时才会去执行数据库的查找。

当然也有另外一种方式能够直接查找数据库的,就是直接在查找方法中添加回调函数,使用方式为:
Model.find(conditions, [fields], [options], [callback])

下面举一个简单例子:

1 User.find({'name', 'simon holmes'}, function(err, user) {});

另一个稍微复杂的例子:

1 User.find({'name', 'simon holmes'}, 'name email',function(err, user) {
2     //console.log('some thing');
3 });

另一个更加复杂的例子,包含查询结果的排序:

1 User.find({'name' : 'Simon Holmes'},
2     null, // 如果使用null,则会返回所有的字段值
3     {sort : {lastLogin : -1}}, // 降序排序
4     function (err, users){
5         if (!err){console.log(users);}
6     });

列举几个比较实用的查找方法:

1 Model.find(query);
2 Model.findOne(query);//返回查找到的所有实例的第一个
3 Model.findById(ObjectID);//根据ObjectId查找到唯一实例

例如:

1 User.findOne({'email' : req.body.Email},
2 '_id name email',
3 function(err, user) {
4     //todo
5 });

2.更新数据

有三种方式来更新数据:

(1)update(conditions,update,options,callback);

该方法会匹配到所查找的内容进行更新,不会返回数据;

(2)findOneAndUpdate(conditions,update,options,callback);

该方法会根据查找去更新数据库,另外也会返回查找到的并未改变的数据;

(3)findByIdAndUpdate(conditions,update,options,callback);

该方法跟上面的findOneAndUpdate方法功能一样,不过他是根据ID来查找

文档并更新的。

三个方法都包含四个参数,一下稍微说明一下几个参数的意思:
conditions:查询条件

update:更新的数据对象,是一个包含键值对的对象

options:是一个声明操作类型的选项,这个参数在下面再详细介绍

callback:回调函数

对于options参数,在update方法中和findOneAndUpdate、

findByIdAndUpdate两个方法中的可选设置是不同的;

1 //在update方法中,options的可选设置为:
 2 {
 3 safe:true|false,  //声明是否返回错误信息,默认true
4 upsert:false|true, //声明如果查询不到需要更新的数据项,是否需要新插入一条记录,默认false
5 multi:false|true,  //声明是否可以同时更新多条记录,默认false
6 strict:true|false  //声明更新的数据中是否可以包含在schema定义之外的字段数据,默认true
 7 }
 8 //对于findOneAndUpdate、findByIdAndUpdate这两个方法,他们的options可选设置项为:
9 {
10 new:true|false, //声明返回的数据时更新后的该是更新前的,如果为true则返回更新后的,默认true
11 upsert:false|trure, 
12 sort:javascriptObject, //如果查询返回多个文档记录,则可以进行排序,在这里是根据传入的javascript object对象进行排序
13 select:String //这里声明要返回的字段,值是一个字符串
14 }

下面举个例子:

User.update({_id:user._id},{$set: {lastLogin: Date.now()}},function(){});

3.数据删除

跟更新数据一样,也有三种方法给我们删除数据:

remove();

findOneAndRemove();

findByIdAndRemove();

remove方法有两种使用方式,一种是用在模型上,另一种是用在模型实例上,例如:

 1 User.remove({ name : /Simon/ } , function (err){
 2     if (!err){
 3         // 删除名字中包含simon的所有用户
 4     }
5 });
6 
 7 User.findOne({ email : 'simon@theholmesoffice.com'},function (err,user){
 8     if (!err){
 9         user.remove( function(err){
10             // 删除匹配到该邮箱的第一个用户
11         });
12     }
13 });

接下来看一下findOneAndRemove方法:

1 User.findOneAndRemove({name : /Simon/},{sort : 'lastLogin', select : 'name email'},function (err, user){
2     if (!err) {
3         console.log(user.name + " removed");
4         // Simon Holmes removed
5     };
6 });

另外一个findByIdAndRemove方法则是如出一辙的。
1 User.findByIdAndRemove(req.body._id,function (err, user) {
2 if(err){
3 console.log(err);
4 return;
5 }
6 console.log(“User deleted:”, user);
7 });

四、数据验证

1.mongoose内置数据验证

在mongoose中,数据验证这一层是放在schema中的,mongoose已经帮我们做了很多内置的数据验证,有一些验证是针对某些数据类型的,也有一些是针对所有数据类型的。

能够作用在所有数据类型上的验证有require,意思就是该字段是否是必须的,例如:

email: { type: String, unique: true, required: true }

上面的代码就定义了一个email是必须的schema.

下面再分别介绍一下mongoose内置的一些数据验证类型。

数字类型schemasType,对于Number类型的数据,具有min,max提供用来界定最大最小值:

1 var teenSchema = new Schema({
2     age : {type: Number, min: 13, max:19}
3 });

字符串类型SchemasType,对于该类型数据,mongoose提供了两种验证器:
match:可使用正则表达式来匹配字符串是否符合该正则表达式的规则
enum:枚举出字符串可使用的一些值
分别举例如下:

1 var weekdaySchema = new Schema({
2     day : {type: String, match: /^(mon|tues|wednes|thurs|fri)day$/i}
3 });
4 
5 var weekdays = ['monday', 'tuesday', 'wednesday', 'thursday','friday'];
6 var weekdaySchema = new Schema({
7     day : {type: String, enum: weekdays}
8 });

在我们进行一些数据库的时候,如果有错误,可能会返回一些错误信息,这些信息封装在一个对象中,该对象的数据格式大致如下:

1 { 
2     message: 'Validation failed',
 3     name: 'ValidationError',
 4     errors:{ 
 5         email:{
 6             message: 'Validator "required" failed for path email',
 7             name: 'ValidatorError',
 8             path: 'email',
 9             type: 'required' 
10         },
11         name:{ 
12             message: 'Validator "required" failed for path name',
13             name: 'ValidatorError',
14             path: 'name',
15             type: 'required' 
16         } 
17     } 
18 }

知道该错误信息的具体格式之后,我们可以从中得出我们想要的信息并反馈到控制台。

1 if(err){
2     Object.keys(err.errors).forEach(function(key) {
3         var message = err.errors[key].message;
4         console.log('Validation error for "%s": %s', key, message);
5     });
6 }

2.自定义数据验证

最简单的自定义数据验证方式就是定义一个数据验证的函数,并将它传递给schema;

1 var lengthValidator = function(val) {
2     if (val && val.length >= 5){
3         return true;
4     }
5     return false;
6 };
7 //usage:
8 name: {type: String, required: true, validate: lengthValidator }

可以看到,我们只需要在schema中添加validate键值对即可,validate对应的值便是我们自定义的验证方法;

但是该形式的数据验证无法给我们提供完整的错误信息,比如errors信息中返回的type值就会成为undefined;

在此基础上如果希望错误信息中能返回一个错误描述,那我们可以稍微进行一点修改:

1 //code 1
2 validate: { validator: lengthValidator, msg: 'Too short' }
3 
4 //code 2
5 var weekdaySchema = new Schema({
6     day : {type: String, validate: {validator:/^(mon|tues|wednes|thurs|fri)day$/i, msg: 'Not a day' }
7 });
将validate的值修改为一个对象,并且该对象包含验证器和错误描述。

我们也可以使用另一种方式在写这些验证器,就是将验证器卸载schema外部,例如:

1 var validateLength = [lengthValidator, 'Too short' ];
2 var validateDay = [/^(mon|tues|wednes|thurs|fri)day$/i, 'Not a day' ];
3 //usage:
4 name: {type: String, required: true, validate: validateLength }
5 day : {type: String, validate: validateDay }

眼睛放大,一看再看,确实没错,在validate中我们传入的是一个数组了,而不是原来的对象了。
其实就validateLength这个东东来说,他就是一个简写来的,你也可以改成下面这样:

var validateLength = [
2     {validator: lengthValidator, msg: 'Too short'}
3 ];

恩,到这里,应该能明白了,将对象改为数组之后,我们便可以传递多个验证器给我们的schema了,的确如此。

1 var validateUsername = [
2     {validator: lengthValidator, msg: 'Too short'} ,
3     {validator: /^[a-z]+$/i, msg: 'Letters only'}
4 ];

我们还有另外一种方法给我们的schema提供验证器:

1 userSchema.path('name').validate(lengthValidator, 'Too short');
2 userSchema.path('name').validate(/^[a-z]+$/i, 'Letters only');

摘录至:http://www.cnblogs.com/jayruan/p/5123754.html

gulp

#gulp

入门指南

1. 全局安装 gulp:

$ npm install --global gulp

2. 作为项目的开发依赖(devDependencies)安装:

$ npm install --save-dev gulp

3. 在项目根目录下创建一个名为 gulpfile.js 的文件:

var gulp = require('gulp');

gulp.task('default', function() {
  // 将你的默认的任务代码放在这
});

4. 运行 gulp:

$ gulp

默认的名为 default 的任务(task)将会被运行,在这里,这个任务并未做任何事情。

想要单独执行特定的任务(task),请输入 gulp

gulp API 文档

gulp.src(globs[, options])

输出(Emits)符合所提供的匹配模式(glob)或者匹配模式的数组(array of globs)的文件。 将返回一个 Vinyl filesstream 它可以被 piped 到别的插件中。

1
2
3
4
gulp.src('client/templates/*.jade')
.pipe(jade())
.pipe(minify())
.pipe(gulp.dest('build/minified_templates'));

glob 请参考 node-glob 语法 或者,你也可以直接写文件的路径。

globs

类型: StringArray

所要读取的 glob 或者包含 globs 的数组。

options

类型: Object

通过 glob-stream 所传递给 node-glob 的参数。

除了 node-glob 和 glob-stream 所支持的参数外,gulp 增加了一些额外的选项参数:

options.buffer

类型: Boolean 默认值: true

如果该项被设置为 false,那么将会以 stream 方式返回 file.contents 而不是文件 buffer 的形式。这在处理一些大文件的时候将会很有用。注意:插件可能并不会实现对 stream 的支持。

options.read

类型: Boolean 默认值: true

如果该项被设置为 false, 那么 file.contents 会返回空值(null),也就是并不会去读取文件。

options.base

类型: String 默认值: 将会加在 glob 之前 (请看 glob2base)

如, 请想像一下在一个路径为 client/js/somedir 的目录中,有一个文件叫 somefile.js :

gulp.src(‘client/js/*/.js’) // 匹配 ‘client/js/somedir/somefile.js’ 并且将 base 解析为 client/js/
.pipe(minify())
.pipe(gulp.dest(‘build’)); // 写入 ‘build/somedir/somefile.js’

gulp.src(‘client/js/*/.js’, { base: ‘client’ })
.pipe(minify())
.pipe(gulp.dest(‘build’)); // 写入 ‘build/js/somedir/somefile.js’

gulp.dest(path[, options])

能被 pipe 进来,并且将会写文件。并且重新输出(emits)所有数据,因此你可以将它 pipe 到多个文件夹。如果某文件夹不存在,将会自动创建它。

1
2
3
4
5
gulp.src('./client/templates/*.jade')
.pipe(jade())
.pipe(gulp.dest('./build/templates'))
.pipe(minify())
.pipe(gulp.dest('./build/minified_templates'));

文件被写入的路径是以所给的相对路径根据所给的目标目录计算而来。类似的,相对路径也可以根据所给的 base 来计算。 请查看上述的 gulp.src 来了解更多信息。

#####path

类型: String or Function

文件将被写入的路径(输出目录)。也可以传入一个函数,在函数中返回相应路径,这个函数也可以由 vinyl 文件实例 来提供。

options

类型: Object

options.cwd

类型: String 默认值: process.cwd()

输出目录的 cwd 参数,只在所给的输出目录是相对路径时候有效。

options.mode

类型: String 默认值: 0777

八进制权限字符,用以定义所有在输出目录中所创建的目录的权限。

gulp.task(name[, deps], fn)

定义一个使用 Orchestrator 实现的任务(task)。

gulp.task('somename', function() {
  // 做一些事
});
name

任务的名字,如果你需要在命令行中运行你的某些任务,那么,请不要在名字中使用空格。

deps

类型: Array

一个包含任务列表的数组,这些任务会在你当前任务运行之前完成。

gulp.task('mytask', ['array', 'of', 'task', 'names'], function() {
  // 做一些事
});

注意: 你的任务是否在这些前置依赖的任务完成之前运行了?请一定要确保你所依赖的任务列表中的任务都使用了正确的异步执行方式:使用一个 callback,或者返回一个 promise 或 stream。

fn

该函数定义任务所要执行的一些操作。通常来说,它会是这种形式:gulp.src().pipe(someplugin())

异步任务支持

任务可以异步执行,如果 fn 能做到以下其中一点:

接受一个 callback

1
2
3
4
5
6
7
8
9
// 在 shell 中执行一个命令
var exec = require('child_process').exec;
gulp.task('jekyll', function(cb) {
// 编译 Jekyll
exec('jekyll build', function(err) {
if (err) return cb(err); // 返回 error
cb(); // 完成 task
});
});

返回一个 stream

1
2
3
4
5
6
gulp.task('somename', function() {
var stream = gulp.src('client/**/*.js')
.pipe(minify())
.pipe(gulp.dest('build'));
return stream;
});

返回一个 promise

1
2
3
4
5
6
7
8
9
10
11
12
var Q = require('q');
gulp.task('somename', function() {
var deferred = Q.defer();
// 执行异步的操作
setTimeout(function() {
deferred.resolve();
}, 1);
return deferred.promise;
});

注意: 默认的,task 将以最大的并发数执行,也就是说,gulp 会一次性运行所有的 task 并且不做任何等待。如果你想要创建一个序列化的 task 队列,并以特定的顺序执行,你需要做两件事:

  • 给出一个提示,来告知 task 什么时候执行完毕,
  • 并且再给出一个提示,来告知一个 task 依赖另一个 task 的完成。

对于这个例子,让我们先假定你有两个 task,”one” 和 “two”,并且你希望它们按照这个顺序执行:

  • 在 “one” 中,你加入一个提示,来告知什么时候它会完成:可以再完成时候返回一个 callback,或者返回一个 promise 或 stream,这样系统会去等待它完成。

  • 在 “two” 中,你需要添加一个提示来告诉系统它需要依赖第一个 task 完成。

因此,这个例子的实际代码将会是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var gulp = require('gulp');
// 返回一个 callback,因此系统可以知道它什么时候完成
gulp.task('one', function(cb) {
// 做一些事 -- 异步的或者其他的
cb(err); // 如果 err 不是 null 或 undefined,则会停止执行,且注意,这样代表执行失败了
});
// 定义一个所依赖的 task 必须在这个 task 执行之前完成
gulp.task('two', ['one'], function() {
// 'one' 完成后
});
gulp.task('default', ['one', 'two']);

gulp.watch(glob [, opts], tasks) 或 gulp.watch(glob [, opts, cb])

监视文件,并且可以在文件发生改动时候做一些事情。它总会返回一个 EventEmitter 来发射(emit) change 事件。

gulp.watch(glob[, opts], tasks)

glob

类型: String or Array

一个 glob 字符串,或者一个包含多个 glob 字符串的数组,用来指定具体监控哪些文件的变动。

opts

类型: Object

传给 gaze 的参数。

tasks

类型: Array

需要在文件变动后执行的一个或者多个通过 gulp.task() 创建的 task 的名字,

1
2
3
4
5
6
var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
watcher.on('change', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
gulp.watch(glob[, opts, cb])
`
glob

类型: String or Array

一个 glob 字符串,或者一个包含多个 glob 字符串的数组,用来指定具体监控哪些文件的变动。

opts

类型: Object

传给 gaze 的参数。

cb(event)

类型: Function

每次变动需要执行的 callback。

1
2
3
gulp.watch('js/**/*.js', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

callback 会被传入一个名为 event 的对象。这个对象描述了所监控到的变动:

event.type

类型: String

发生的变动的类型:added, changed 或者 deleted

event.path

类型: String

触发了该事件的文件的路径。

引自:http://www.gulpjs.com.cn/

js_screen

JS,Jquery获取各种屏幕的宽度和高度

javascript:

document.write(
"屏幕分辨率为:"+screen.width+"*"+screen.height
+"<br />"+
"屏幕可用大小:"+screen.availWidth+"*"+screen.availHeight
+"<br />"

网页可见区域宽: document.body.clientWidth
网页可见区域高: document.body.clientHeight
网页可见区域宽: document.body.offsetWidth (包括边线的宽)
网页可见区域高: document.body.offsetHeight (包括边线的高)
网页正文全文宽: document.body.scrollWidth
网页正文全文高: document.body.scrollHeight
网页被卷去的高: document.body.scrollTop
网页被卷去的左: document.body.scrollLeft
网页正文部分上: window.screenTop
网页正文部分左: window.screenLeft
屏幕分辨率的高: window.screen.height
屏幕分辨率的宽: window.screen.width
屏幕可用工作区高度: window.screen.availHeight
屏幕可用工作区宽度: window.screen.availWidth

juery

$(document).ready(function(){
alert($(window).height()); //浏览器当前窗口可视区域高度
alert($(document).height()); //浏览器当前窗口文档的高度
alert($(document.body).height());//浏览器当前窗口文档body的高度
alert($(document.body).outerHeight(true));//浏览器当前窗口文档body的总高度 包括border padding margin

alert($(window).width()); //浏览器当前窗口可视区域宽度
alert($(document).width());//浏览器当前窗口文档对象宽度
alert($(document.body).width());//浏览器当前窗口文档body的宽度
alert($(document.body).outerWidth(true));//浏览器当前窗口文档body的总宽度 包括border padding margin

})

ydshipei

移动适配

移动适配的几种方式

1.直接利用js适配

(function (doc, win) {
var docEl = doc.documentElement,
  resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
  recalc = function () {
      var clientWidth = docEl.clientWidth;
      if (!clientWidth) return;
      docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
  };
if (!doc.addEventListener) return;
 win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

例如:100px=1rem;10px=0.1rem;1px=0.01rem;

2.利用js+less适配

(function (win) {
function setUnitA() {
        document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + "px";
}
        var h = null;
    window.addEventListener("resize", function () { clearTimeout(h); h = setTimeout(setUnitA, 300); }, false);
setUnitA();
})(window);

less:文件顶部定义@unit: 750/10rem,然后css全文件的单位直接用@unit。

例如:100px=100/@unit;10px=10/@unit;1px=1/@unit;

3.利用less适配。

html {
font-size: 20px;
}
@media only screen and (min-width: 401px) {
        html {
    font-size: 25px !important;
    }
}
@media only screen and (min-width: 428px) {
    html {
    font-size: 26.75px !important;
    }
}
@media only screen and (min-width: 481px) {
    html {
        font-size: 30px !important;
    }
}
@media only screen and (min-width: 569px) {
    html {
        font-size: 35px !important;
    }
}
@media only screen and (min-width: 641px) {
    html {
        font-size: 40px !important;
    }
}
@unit: 40rem;

例如:100px=100/@unit;10px=10/@unit;1px=1/@unit;

作者:sky
链接:https://zhuanlan.zhihu.com/p/23837333
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Express

Express

Express 应用生成器

通过应用生成器工具 express 可以快速创建一个应用的骨架。
通过如下命令安装:

$ npm install express-generator -g

-h 选项可以列出所有可用的命令行选项:

$ express -h

      Usage: express [options] [dir]

          Options:

           -h, --help          output usage information
        -V, --version       output the version number
            -e, --ejs           add ejs engine support (defaults to jade)
        --hbs           add handlebars engine support
        -H, --hogan         add hogan.js engine support
        -c, --css <engine>  add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
        --git           add .gitignore
        -f, --force         force on non-empty directory

例如,下面的示例就是在当前工作目录下创建一个命名为 myapp 的应用。
个命名为 myapp 的应用。

$ express myapp

       create : myapp
       create : myapp/package.json
       create : myapp/app.js
       create : myapp/public
       create : myapp/public/javascripts
       create : myapp/public/images
       create : myapp/routes
       create : myapp/routes/index.js
       create : myapp/routes/users.js
       create : myapp/public/stylesheets
       create : myapp/public/stylesheets/style.css
       create : myapp/views
       create : myapp/views/index.jade
       create : myapp/views/layout.jade
       create : myapp/views/error.jade
       create : myapp/bin
       create : myapp/bin/www

然后安装所有依赖包:

$ cd myapp 
$ npm install

启动这个应用(MacOS 或 Linux 平台):

$ DEBUG=myapp npm start

Windows 平台使用如下命令:

> set DEBUG=myapp & npm start

然后在浏览器中打开 http://localhost:3000/ 网址就可以看到这个应用了。i

通过 Express 应用生成器创建的应用一般都有如下目录结构:

.
├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js
│   └── users.js
└── views
        ├── error.jade
    ├── index.jade
    └── layout.jade

7 directories, 9 files

路由

路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。

路由是由一个 URI、HTTP 请求(GET、POST等)和若干个句柄组成,它的结构如下: app.METHOD(path, [callback…], callback), app 是 express 对象的一个实例, METHOD 是一个 HTTP 请求方法, path 是服务器上的路径, callback 是当路由匹配时要执行的函数。

下面是一个基本的路由示例:

var express = require('express');
var app = express();

// respond with "hello world" when a GET request is made to the homepage
app.get('/', function(req, res) {
      res.send('hello world');
});

路由方法

路由方法源于 HTTP 请求方法,和 express 实例相关联。

下面这个例子展示了为应用跟路径定义的 GET 和 POST 请求:

// GET method route
app.get('/', function (req, res) {
      res.send('GET request to the homepage');
});

// POST method route
    app.post('/', function (req, res) {
      res.send('POST request to the homepage');
});

Express 定义了如下和 HTTP 请求对应的路由方法: get, post, put, head, delete, options, trace, copy, lock, mkcol, move, purge, propfind, proppatch, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch, search, 和 connect。

有些路由方法名不是合规的 JavaScript 变量名,此时使用括号记法,比如: app['m-search']('/', function ...

app.all() 是一个特殊的路由方法,没有任何 HTTP 方法与其对应,它的作用是对于一个路径上的所有请求加载中间件。

在下面的例子中,来自 “/secret” 的请求,不管使用 GET、POST、PUT、DELETE 或其他任何 http 模块支持的 HTTP 请求,句柄都会得到执行。

app.all('/secret', function (req, res, next) {
      console.log('Accessing the secret section ...');
      next(); // pass control to the next handler
});

路由路径

路由路径和请求方法一起定义了请求的端点,它可以是字符串、字符串模式或者正则表达式。

Express 使用 path-to-regexp 匹配路由路径,请参考文档查阅所有定义路由路径的方法。 Express Route Tester 是测试基本 Express 路径的好工具,但不支持模式匹配。
查询字符串不是路由路径的一部分。

使用字符串的路由路径示例:

// 匹配根路径的请求
app.get('/', function (req, res) {
      res.send('root');
});

// 匹配 /about 路径的请求
app.get('/about', function (req, res) {
      res.send('about');
});

// 匹配 /random.text 路径的请求
app.get('/random.text', function (req, res) {
      res.send('random.text');
});

使用字符串模式的路由路径示例:

// 匹配 acd 和 abcd
    app.get('/ab?cd', function(req, res) {
      res.send('ab?cd');
});

// 匹配 abcd、abbcd、abbbcd等
app.get('/ab+cd', function(req, res) {
      res.send('ab+cd');
});

// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
app.get('/ab*cd', function(req, res) {
      res.send('ab*cd');
});

// 匹配 /abe 和 /abcde
app.get('/ab(cd)?e', function(req, res) {
     res.send('ab(cd)?e');
});

字符 ?、+、* 和 () 是正则表达式的子集,- 和 . 在基于字符串的路径中按照字面值解释。

使用正则表达式的路由路径示例:

// 匹配任何路径中含有 a 的路径:
app.get(/a/, function(req, res) {
      res.send('/a/');
});

// 匹配 butterfly、dragonfly,不匹配 butterflyman、    dragonfly man等
app.get(/.*fly$/, function(req, res) {
      res.send('/.*fly$/');
});

路由句柄

可以为请求处理提供多个回调函数,其行为类似 中间件。唯一的区别是这些回调函数有可能调用 next(‘route’) 方法而略过其他路由回调函数。可以利用该机制为路由定义前提条件,如果在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。

路由句柄有多种形式,可以是一个函数、一个函数数组,或者是两者混合,如下所示.

使用一个回调函数处理路由:

app.get('/example/a', function (req, res) {
      res.send('Hello from A!');
});

使用多个回调函数处理路由(记得指定 next 对象):

app.get('/example/b', function (req, res, next) {
      console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
      res.send('Hello from B!');
});

使用回调函数数组处理路由:

var cb0 = function (req, res, next) {
      console.log('CB0');
      next();
}

var cb1 = function (req, res, next) {
      console.log('CB1');
      next();
}

var cb2 = function (req, res) {
      res.send('Hello from C!');
}

app.get('/example/c', [cb0, cb1, cb2]);

混合使用函数和函数数组处理路由:

var cb0 = function (req, res, next) {
     console.log('CB0');
      next();
}

var cb1 = function (req, res, next) {
      console.log('CB1');
      next();
}

app.get('/example/d', [cb0, cb1], function (req, res,     next) {
     console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
      res.send('Hello from D!');
});

响应方法

下表中响应对象(res)的方法向客户端返回响应,终结请求响应的循环。如果在路由句柄中一个方法也不调用,来自客户端的请求会一直挂起。

方法 描述
res.download() 提示下载文件。
res.end() 终结响应处理流程。
res.json() 发送一个 JSON 格式的响应。
res.jsonp() 发送一个支持 JSONP 的 JSON 格式的响应。
res.redirect() 重定向请求。
res.render() 渲染视图模板。
res.send() 发送各种类型的响应。
res.sendFile 以八位字节流的形式发送文件。
res.sendStatus() 设置响应状态代码,并将其以字符串形式作为响应体的一部分发送。

app.route()

可使用 app.route() 创建路由路径的链式路由句柄。由于路径在一个地方指定,这样做有助于创建模块化的路由,而且减少了代码冗余和拼写错误。请参考 Router() 文档 了解更多有关路由的信息。

下面这个示例程序使用 app.route() 定义了链式路由句柄。

app.route('/book')
      .get(function(req, res) {
    res.send('Get a random book');
  })
  .post(function(req, res) {
    res.send('Add a book');
  })
  .put(function(req, res) {
    res.send('Update the book');
  });

express.Router

可使用 express.Router 类创建模块化、可挂载的路由句柄。Router 实例是一个完整的中间件和路由系统,因此常称其为一个 “mini-app”。

下面的实例程序创建了一个路由模块,并加载了一个中间件,定义了一些路由,并且将它们挂载至应用的路径上。

在 app 目录下创建名为 birds.js 的文件,内容如下:

var express = require('express');
var router = express.Router();

// 该路由使用的中间件
router.use(function timeLog(req, res, next) {
      console.log('Time: ', Date.now());
  next();
});
// 定义网站主页的路由
router.get('/', function(req, res) {
      res.send('Birds home page');
});
// 定义 about 页面的路由
router.get('/about', function(req, res) {
      res.send('About birds');
});

module.exports = router;

然后在应用中加载路由模块:

var birds = require('./birds');
...
app.use('/birds', birds);

应用即可处理发自 /birds 和 /birds/about 的请求,并且调用为该路由指定的 timeLog 中间件。

在 Express 中使用模板引擎

需要在应用中进行如下设置才能让 Express 渲染模板文件:

  • views, 放模板文件的目录,比如: app.set(‘views’, ‘./views’)
  • view engine, 模板引擎,比如: app.set(‘view engine’, ‘jade’)

然后安装相应的模板引擎 npm 软件包。

$ npm install jade --save

和 Express 兼容的模板引擎,比如 Jade,通过 res.render() 调用其导出方法 __express(filePath, options, callback) 渲染模板。 有一些模板引擎不遵循这种约定,Consolidate.js 能将 Node 中所有流行的模板引擎映射为这种约定,这样就可以和 Express 无缝衔接。

一旦 view engine 设置成功,就不需要显式指定引擎,或者在应用中加载模板引擎模块,Express 已经在内部加载,如下所示。

app.set('view engine', 'jade');

在 views 目录下生成名为 index.jade 的 Jade 模板文件,内容如下:

html
      head
        title!= title
      body
        h1!= message

然后创建一个路由渲染 index.jade 文件。如果没有设置 view engine,您需要指明视图文件的后缀,否则就会遗漏它。

app.get('/', function (req, res) {
      res.render('index', { title: 'Hey', message: 'Hello there!'});
});

此时向主页发送请求,“index.jade” 会被渲染为 HTML。

集成数据库

为 Express 应用添加连接数据库的能力,只需要加载相应数据库的 Node.js 驱动即可。这里将会简要介绍如何为 Express 应用添加和使用一些常用的数据库 Node 模块。

  • Cassandra
  • CouchDB
  • LevelDB
  • MySQL
  • MongoDB
  • Neo4j
  • PostgreSQL
  • Redis
  • SQLite
  • ElasticSearch

这些数据库驱动只是其中一部分,可在 npm 官网 查找更多驱动。

Cassandra

模块: cassandra-driver
安装
$ npm install cassandra-driver
示例
var cassandra = require('cassandra-driver');
var client = new cassandra.Client({ contactPoints: ['localhost']});

client.execute('select key from system.local', function(err, result) {
      if (err) throw err;
      console.log(result.rows[0]);
});

CouchDB

模块: nano
安装
$ npm install nano
示例
var nano = require('nano')('http://localhost:5984');
nano.db.create('books');
var books = nano.db.use('books');

//Insert a book document in the books database
books.insert({name: 'The Art of war'}, null, function(err, body) {
      if (!err){
        console.log(body);
      }
});

//Get a list of all books
books.list(function(err, body){
      console.log(body.rows);
}

LevelDB

模块: levelup
安装
$ npm install level levelup leveldown

######示例

var levelup = require('levelup');
var db = levelup('./mydb');

db.put('name', 'LevelUP', function (err) {

      if (err) return console.log('Ooops!', err);
          db.get('name', function (err, value) {
    if (err) return console.log('Ooops!', err);
        console.log('name=' + value)
      });

});

MySQL

模块: mysql
安装
$ npm install mysql
示例
var mysql      = require('mysql');
var connection = mysql.createConnection({
      host     : 'localhost',
      user     : 'dbuser',
      password : 's3kreee7'
});

connection.connect();

connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
      if (err) throw err;
          console.log('The solution is: ', rows[0].solution);
    });

connection.end();

MongoDB

模块: mongoskin
安装
$ npm install mongoskin
示例
var db = require('mongoskin').db('localhost:27017/animals');

db.collection('mamals').find().toArray(function(err, result) {
      if (err) throw err;
      console.log(result);
});

If you want a object model driver for MongoDB, checkout Mongoose.

Neo4j

模块: apoc
安装
$ npm install apoc
示例
var apoc = require('apoc');

apoc.query('match (n) return n').exec().then(
      function (response) {
        console.log(response);
      },
      function (fail) {
            console.log(fail);
      }
);

PostgreSQL

模块: pg
安装
$ npm install pg
示例
var pg = require('pg');
var conString = "postgres://username:password@localhost/database";

pg.connect(conString, function(err, client, done) {

      if (err) {
        return console.error('error fetching client from pool', err);
      }
      client.query('SELECT $1::int AS number', ['1'], function(err, result) {
            done();
        if (err) {
             return console.error('error running query', err);
        }
        console.log(result.rows[0].number);
      });

});

Redis

模块: redis
安装
$ npm install redis
示例
var client = require('redis').createClient();

client.on('error', function (err) {
      console.log('Error ' + err);
});

client.set('string key', 'string val', redis.print);
client.hset('hash key', 'hashtest 1', 'some value', redis.print);
client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print);

client.hkeys('hash key', function (err, replies) {

     console.log(replies.length + ' replies:');
      replies.forEach(function (reply, i) {
        console.log('    ' + i + ': ' + reply);
      });

      client.quit();

});

SQLite

模块:sqlite3
安装
$ npm install sqlite3
示例
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:');

db.serialize(function() {

  db.run('CREATE TABLE lorem (info TEXT)');
  var stmt = db.prepare('INSERT INTO lorem VALUES (?)');

 for (var i = 0; i < 10; i++) {
    stmt.run('Ipsum ' + i);
 }

  stmt.finalize();

  db.each('SELECT rowid AS id, info FROM lorem', function(err,     row) {
    console.log(row.id + ': ' + row.info);
      });
});

db.close();

ElasticSearch

模块: elasticsearch
安装
$ npm install elasticsearch
示例
var elasticsearch = require('elasticsearch');
var client = elasticsearch.Client({
      host: 'localhost:9200'  
});

client.search({
      index: 'books',
      type: 'book',
      body: {
        query: {
             multi_match: {
                query: 'express js',
                fields: ['title', 'description']
              }
        }
      }
}).then(function(response) {
      var hits = response.hits.hits;
}, function(error) {
      console.trace(error.message);
});
很惭愧<br><br>只做了一点微小的工作<br>谢谢大家