prototype

构造函数,原型,实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,实例都包含一个指向原型对象的内部指针。
原型链:一个对象的原型等于另一个对象的实例,此时原型对象将包含一个指向另一个原型的指针,层层递进,构成了实例与原型之间的链条,这就是原型链。
继承:依靠原型链来实现的。

  1. 原型链继承
    很少单独使用
    缺点:最主要的问题来自包含引用类型值的原型会被所有实例共享;创建子类型的实例时,不能向超类型传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function SuperType(){
    this.colors = ['red', 'blue', 'green'];
    }
    function SubType(){
    }
    SubType.protoype = new SuperType();
    var instance1 = new SubType();
    instance1.colors.push('black');
    console.log('colors', instance1.colors); //['red', 'blue', 'green', 'black']
    var instance2 = new SubType();
    console.log('colors', instance2.colors); //['red', 'blue', 'green', 'black']
  2. (经典继承)借用构造函数继承
    很少单独使用
    在子类构造函数的内部调用超类构造函数,解决引用类型共享的问题;可传递参数
    缺点:无法实现函数的复用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function SuperType(name){
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
    }
    function SubType(){
    SuperType.call(this, name);
    }
    var instance1 = new SubType();
    instance1.colors.push('black'); //['red', 'blue', 'green', 'black']
    console.log(instance1);
    var instance2 = new SubType();
    console.log(instance2);
  3. 组合继承(原型链和借用构造函数的组合)
    将原型链和借用构造函数的技术组合到一块,使用原型链实现对原型属性和方法的继承,通过借用构造行数来实现对实例属性的继承,既通过在原型上定义方法实现了函数复用,又能保证每个实例儿都有自己的属性
    常用的继承模式
    缺点: 无论什么情况下,都会调用两次超类型构造函数

  4. 原型式继承(同ECMAScript的Object.create)
    基于已有的对象,借助原型创建新对象
    应用场景:只想让一个对象与另一个对象保持类似的情况下。
    缺点:包含引用类型的值会共享

    1
    2
    3
    4
    5
    ```
    5. 寄生式继承
    创建一个仅用于封装继承过程的函数,任何能够返回新对象的函数都适用于此模式
    缺点:不能做到函数的复用
    ```javascript
  5. 寄生组合式继承
    最常用的继承模式
    借用构造函数来继承属性,通过原型链来继承方法。
    与组合式继承相比,它只调用了一次SuperType构造行数,并且因此避免了在SubType.prototype上创建不必要的,多余的属性。寄生组合式继承是引用类型最理想的继承方式

    
    

Javascript排序算法

  • 算法的稳定性
    所有相等的数经过算法的排序之后,仍能保持他们在排序之前的相对次序,这种排序算法是稳定的,反之,是不稳定的。
  • 算法的时间复杂度
    是指执行算法所需要的计算工作量
  • 算法的空间复杂度
    是指执行算法所需要的内存空间
算法 时间复杂度 空间复杂度 稳定性
冒泡排序 O(n*n) O(1) 稳定
选择排序 O(n*n) O(1) 不稳定
插入排序 O(n*n) O(1) 稳定
希尔排序 O(nlog^2 n) O(1) 不稳定
归并排序 O(nlogn) O(1) 稳定
快速排序 O(nlogn) O(n log n) 不稳定

冒泡排序,是最慢的一种排序

  • 原理:比较相邻的数据,根据排序要求进行互换。如按升序排,较大的数会移动的右侧,较小的数会移到左侧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
(function(){
var aa = [];
var nums = 10;
for(var i = 0; i< nums; i++){
aa[i] = Math.floor(Math.random()* nums + 1);
}
function swap(arr, index1, index2){
var tmp = arr[index1];
arr[index1] = arr[index2];
arr[index2]= tmp;
}
// 冒泡排序
function bubbleSort(arr){
var len = arr.length;
for(var outer = len -1; outer >= 0; outer--) {
for(var inner = 0; inner < outer; inner ++){
if(arr[inner] > arr[inner + 1]){
swap(arr, inner ,inner + 1);
}
}
}
return arr;
}
var start = new Date();
console.log('排序2前', aa);
bubbleSort(aa);
console.log('排序后', aa);
console.log('冒泡排序消耗时间: ', +new Date - start);
})();

选择排序

原理:外循环将数组元素挨个移动,内循环从循环中选中元素的下一个元素开始比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(function(){
var aa = [];
var nums = 10;
for(var i = 0; i< nums; i++){
aa[i] = Math.floor(Math.random()* nums + 1);
}
function swap(arr, index1, index2){
var tmp = arr[index1];
arr[index1] = arr[index2];
arr[index2]= tmp;
}
function selectionSort(arr){
var len = arr.length;
for(var outer = 0; outer < len; outer++){
for(var inner = outer+1; inner < len; inner++){
if(arr[outer] > arr[inner]){
swap(arr, outer, inner);
}
}
}
}
// 选择排序
var start = new Date();
console.log('排序前', aa);
selectionSort(aa);
console.log('排序后', aa);
console.log('选择排序消耗时间: ', +new Date - start);
})();

插入排序

原理:外循环将数组元素挨个移动,内循环对外循环中的元素和它前面的元素进行比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(function(){
var aa = [];
var nums = 10;
for(var i = 0; i< nums; i++){
aa[i] = Math.floor(Math.random()* nums + 1);
}
function swap(arr, index1, index2){
var tmp = arr[index1];
arr[index1] = arr[index2];
arr[index2]= tmp;
}
function insertSort(arr){
var len = arr.length;
for(var outer = 0; outer < len; outer++){
var inner = outer;
while(inner && arr[inner] < arr[inner - 1]) {
swap(arr, inner ,inner -1);
inner --;
}
}
}
// 插入排序
var start = new Date();
console.log('排序前', aa);
insertSort(aa);
console.log('排序后', aa);
console.log('插入排序消耗时间: ', +new Date - start);
})();

希尔排序

原理:通过定义一个间隔序列,根据间隔序列来比较元素,间隔的值会不断减小。

  • 固定间隔

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    (function(){
    var aa = [];
    var nums = 10;
    for(var i = 0; i< nums; i++){
    aa[i] = Math.floor(Math.random()* nums + 1);
    }
    function swap(arr, index1, index2){
    var tmp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2]= tmp;
    }
    var gaps = [5, 3,1];
    function shellSort(arr){
    var len = arr.length;
    for(var g = 0; g < gaps.length; g++){
    for(var outer = g; outer < len; outer++){
    for(var inner = outer; inner >= g && arr[inner] < arr[inner -g]; inner -= g){
    swap(arr, inner ,inner-g);
    }
    }
    }
    }
    var start = new Date();
    console.log('排序前', aa);
    shellSort(aa);
    console.log('排序后', aa);
    console.log('希尔排序消耗时间: ', +new Date - start);
    })();
  • 动态间隔

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    (function(){
    var aa = [];
    var nums = 10;
    for(var i = 0; i< nums; i++){
    aa[i] = Math.floor(Math.random()* nums + 1);
    }
    function swap(arr, index1, index2){
    var tmp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2]= tmp;
    }
    function shellSortByDynamic(arr){
    var len = arr.length;
    var g = 1;
    while(g < len / 3){
    g = 3 * g + 1;
    }
    while(g >= 1){
    for(var outer = g; outer < len; outer++){
    for(var inner = outer; inner >=g && arr[inner] < arr[inner -g]; inner -= g){
    swap(arr, inner ,inner - g);
    }
    }
    g = (g - 1) / 3;
    }
    }
    // 动态希尔排序
    var start = new Date();
    console.log('排序前', aa);
    shellSortByDynamic(aa);
    console.log('排序后', aa);
    console.log('动态希尔排序消耗时间: ', +new Date - start);
    })();

归并排序

原理:将数据分解为一组只有一个元素的数组,然后通过创建一组左右子数组将他们慢慢排序合并。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
(function(){
var aa = [];
var nums = 10;
for(var i = 0; i< nums; i++){
aa[i] = Math.floor(Math.random()* nums + 1);
}
function mergeSort(arr){
var len = arr.length;
if(len < 2) return;
var step = 1;
var left, right;
while(step < len){
left = 0;
right = step;
while(right + step <= len){
mergeArrays(arr, left, left+step, right, right+step);
left = right + step;
right = left + step;
}
if(right < len){
mergeArrays(arr, left, left+step, right, len);
}
step *= 2;
}
}
function mergeArrays(arr, leftStart, leftStop, rightStart, rightStop){
var leftArr = new Array(leftStop - leftStart + 1);
var rightArr = new Array(rightStop - rightStart + 1);
var k = leftStart;
for(var i = 0; i < leftStop-leftStart; i++){
leftArr[i] = arr[k];
k++
}
k = rightStart;
for(var i = 0; i < rightStop-rightStart; i++){
rightArr[i] = arr[k];
k++;
}
leftArr[leftArr.length -1] = Infinity;
rightArr[rightArr.length - 1] = Infinity;
var l = 0;
var r = 0;
for(var i = leftStart; i < rightStop;i++){
if(leftArr[l] <= rightArr[r]){
arr[i] = leftArr[l];
l++
}else {
arr[i] = rightArr[r];
r++;
}
}
}
// 归并排序
var start = new Date();
console.log('排序前', aa);
mergeSort(aa);
console.log('排序后', aa);
console.log('归并排序消耗时间: ', +new Date - start);
})();

快速排序

原理:在列表中选择一个元素作为基准值,列表中小于基准值的元素移动到左边,大于基准值的元素移动的右边,递归调用,合并左数组、基准值、有数组并返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
(function(){
var aa = [];
var nums = 10;
for(var i = 0; i< nums; i++){
aa[i] = Math.floor(Math.random()* nums + 1);
}
function quickSort(arr){
var len = arr.length;
if(arr && len == 0){
return [];
}
var point = arr[0];
var left = [];
var right = [];
for(var i = 1; i< len; i++){
if(arr[i] < point){
left.push(arr[i]);
}else {
right.push(arr[i]);
}
}
return quickSort(left).concat(point, quickSort(right));
}
// 快速排序
var start = new Date();
console.log('排序前', aa);
aa = quickSort(aa);
console.log('排序后', aa);
console.log('快速排序消耗时间: ', +new Date - start);
})();

当数组长度为1000时,以上排序算法消耗时间为

1
2
3
4
5
6
7
8
JavaScript内置sort排序消耗时间: 3
冒泡排序消耗时间: 4
选择排序消耗时间: 3
插入排序消耗时间: 2
希尔排序消耗时间: 2
动态希尔排序消耗时间: 1
归并排序消耗时间: 2
快速排序消耗时间: 2

当数组长度为10000时,以上排序算法消耗时间为

1
2
3
4
5
6
7
8
JavaScript内置sort排序消耗时间: 16
冒泡排序消耗时间: 180
选择排序消耗时间: 228
插入排序消耗时间: 57
希尔排序消耗时间: 58
动态希尔排序消耗时间: 3
归并排序消耗时间: 4
快速排序消耗时间: 16

当数据量较大的时候,希尔排序,归并排序,快速排序,速度较快。

闭包


  • 堆是一种无序的数据结构,数据的存取方式根据引用直接获取,现实生活中常见的堆的例子:书架与书。

  • 栈是一种高效的数据结构,只能在栈顶添加或删除数据,是一种后入先出(LIFO, last-in-first-out)的数据结构。现实生活常见的栈的例子:摞盘子。
    JavaScript没有严格意义上区分堆内存和栈内存。在JavaScript的执行环境在逻辑上实现了堆栈。
    执行环境中的变量对象保存在堆内存中。
    基础数据类型存储在栈内存中,基础数据类型是按值访问的,可以直接操作保存在变量终端额实际值。
    引用数据类型的值是保存在堆内存中,引用类型的值是按引用访问的,不允许直接访问对内存中的位置,操作对象时实际是操作的对象的引用。
比较 栈内存 堆内存
存储数据类型 基础数据类型 引用数据类型
存储值 大小固定 大小不固定,可动态调整
内存分配 编译器自动分配释放 程序分配
用途 主要用来执行程序 主要用来存放对象
特点 空间小,运行效率高 空间大,运行效率相对较低
存取方式 先进后出,后进先出 无序存储,可根据引用直接获取
  • 调用栈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function printCallStack() {
var i = 0;
var fun = arguments.callee;
do {
fun = fun.arguments.callee.caller;
console.log(++i + ': ' + fun);
} while (fun);
}
function c() {
console.log('c');
printCallStack();
}
function b() {
console.log('b');
c();
}
function a() {
console.log('a');
b();
}
a();
  • 执行环境(execution context)
    JavaScript执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
    每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中,我们编写的代码无法访问这个对象,但解析器在处理数据的时候会再后台使用它。
    某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁.全局执行环境是最外围的一个执行环境(全局执行环境直到应用程序推出才会被销毁)。
    JavaScript程序的执行流的机制: 每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入到一个环境栈中,当函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
  • 作用域链(scope chain):
    当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。
    作用域链的前端,始终是当前执行的代码所在环境的变量对象,作用域链中的下一个变量来自包含环境,一直延续到全局执行环境,全局执行环境的变量对象始终是作用域链中的最后一个对象。
    标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级向后,直到找到标识符为止(如果找不到标识符,通常会导致错误发生).
    延长作用域链的两种情况:
  • with语句
  • try/catch语句中catch语句
1
2
3
4
5
6
7
function buildUrl(){
var qs = "?debug=true";
with(location){
var url = href + qs;
}
return url;
}

with语句接收location对象,with语句的变量对象里包含了location对象,with语句引用href时,引用的是location.href

1
2
3
4
5
6
7
8
9
10
11
12
function lengthenScopeChain(){
try{
var dd = 2;
throw 'create error';
} catch(e) {
console.log('dd inner catch',dd); // 2
console.log('e', e); // create error
}
console.log('dd outer',dd); // 2
console.log('e outer', e); //报错,e is not defined
}
lengthenScopeChain();

catch语句接收e对象,catch语句的变量对象里包含了e对象

  • 作用域
  1. 块级作用域
    在一些类似C语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的,我们称为块级作用域。

  2. javascript函数作用域
    JavaScript中没有块级作用域,Javascript使用了函数作用域,就是函数的执行环境:变量在声明它们的函数体内以及这个函数体嵌套的任意函数体内都是有定义的。
    Javascript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的,这意味着变量在声明之前甚至已经可用,Javascript的这个特性被非正式地称为申明提前,即Javascript函数里声明的所有变量(但不涉及赋值)都被提前至函数体的顶部,函数声明提前这步操作是在Javscript引擎预编译时进行的,是在代码开始运行之前。
    在函数体内,局部变量的优先级高于同名的全局变量。
    如果在函数声明的一个局部变量或者函数参数中带有的变量和全局变量重名,局部变量会覆盖全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//函数内的变量会覆盖同名的全局变量
var name = 'global';
function checkScope(){
console.log(name);
var name = 'local';
}
checkScope() //undefined
//函数内的参数会覆盖同名的全局变量
var name = 'global';
function checkScope(name){
console.log(name);
}
checkScope(); //undefined
  • 作用域链

闭包就是内部函数可以访问定义他们的外部函数的变量和参数(除了this,arguments),也就是函数可以访问存在于该函数声明时的作用域内的变量和函数。
特点:

  1. 闭包可以访问函数声明时所在作用域内的变量和函数
  2. 即使外部函数已经返回,当前函数仍然可以引用外部函数所定义的变量
  3. 闭包可以更新外部变量的值,闭包存储的是外部变量的引用.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function scope(){
    var value;
    return {
    get: function(){
    return value;
    },
    set: function(newValue) {
    value = newValue;
    }
    }
    }
    var obj = scope();
    obj.get(); //undefined
    obj.set('marong');
    obj.get(); //marong

优点:
1.避免全局变量的污染
2.减少代码的数量和复杂性
缺点:
1.会比其他函数占内存。
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。

  • 内存
  • 内存的声明周期
  1. 分配你所需要的内存
  2. 使用分配到的内存(读,写)
  3. 不需要是将其释放\归还
  • Javascript垃圾回收机制
    JavaScript具有自动垃圾回收机制,执行环境会负责管理代码执行过程中使用的内存,开发人员不用关心内存的使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。这种垃圾收集机制的原理:找到那些不再继续使用的变量,然后释放其占用的内存。垃圾回收器会按照固定的时间间隔,周期性的执行这一操作。
    局部变量的生命周期,局部变量只在函数执行的过程中存在,在这个过程中,会为局部变量在堆或栈内存上分配相应空间,以便存储它们的值,然后在函数中使用这些变量,函数运行结束后,判断局部变量是否有用,无用变量进行标识,以备将来回收。
    浏览器中标识无用变量的策略,通常有两种。
  1. 标记清除
    JavaScript中最常用的垃圾收集方式是标记清除(mark-and-sweep)。当变量进入环境是,将这个变量标记为“进入环境”,当变量离开环境时,将其标记为“离开环境”。垃圾收集器在运行的时候会给内存种所有的变量都加上标记,然后它会去掉环境中的变量,根据标记删除无用变量,回收它们占用的内存空间。
    IE, Firefox,Opera,Chrome Safari的Javascript实现使用的是标记清除式的垃圾收集策略,垃圾收集的时间间隔不同。
  2. 引用计数
    引用计数的含义是跟踪记录每个值被引用的次用。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1,如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1.当这个值的引用次数变成0时。当垃圾收集器运行时,释放引用次数为0的值所占用的内存。
    引用计数的策略在循环引用的时候,会出现问题。
    1
    2
    3
    4
    5
    6
    function problem(){
    var objA = new Object();
    var objB = new Object();
    objA.someOther = objB;
    objB.someOther = objA;
    }

当函数执行完成之后,objA,objB的引用次数都为2,垃圾回收器无法回收,如果这个函数被多次调用,就会导致大量内存得不到回收。为此,Netscape在Navigator4.0中放弃了引用计数的策略,使用标记清除的策略。
IE中有些对象不是原生的Javascript,其中BOM/DOM对象是使用C++以COM(Component Object Model, 组件对象模型)形式实现的。COM对象的垃圾收集策略采用的引用计数的方式,只要在IE中设计到COM,就会存在循环引用的问题。

1
2
3
4
var element = document.getElementById('some');
var obj = new Object();
obj.element = element;
element.someobj = obj;

即使DOM中element元素被移除,element也不会被垃圾回收器回收。
为了避免类似这种循环引用的问题,最好在不使用它们的时候,手动断开连接
将变量值设为null,切断变量和它之前引用值的连接,当垃圾收集器下次运行时,就会删除这些值并回收它们的内存。
IE9中BOM/DOM对象都是原声的Javascript对象,避免了引用计数循环引用的问题,消除了常见内存泄漏的现象。

1
2
obj.element = null;
element.someobj = null;
  • 内存管理
    解除引用(dereferencing):将其中设置为null来释放其引用,解除一个值的引用的作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收
    分配给web浏览器的内存通常比一般的桌面应用程序少,出于安全的考虑,防止浏览器耗尽系统内存而崩溃。确保占用较少的内存,能保证页面的性能。
    优化内存占用的最佳方式,执行的代码中只保存必要的数据。一旦数据不再有用,可以解除引用。

    1
    2
    3
    4
    5
    6
    7
    8
    function createPerson(){
    return {
    name:'person'
    }
    }
    var globalPerson = createPerson;
    //dereferencing
    globalPerson = null;
  • 内存溢出
    程序申请内存时,没有足够的内存空间供其使用。

  • 内存泄漏
    是指程序申请内存后,又不能回收,无法释放已申请的内存空间,内存泄漏会降低页面性能,还可能会导致程序出错或浏览器崩溃。内存泄漏可能会导致内存溢出。

定时器

定时器

由于JavaScript是单线程的特性,Javascript是同一时间只能执行一段代码,异步事件的处理程序,需要在线程中没有代码执行的时候才能执行,定时器的延迟时间并不按照指定的时间间隔触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var count = 0;
var start = +new Date();
for(var n = 0; n < 1000; n++){
console.log('n');
}
console.log('n is over time ', +new Date - start);
setTimeout(function(){
console.log('setTimeout1', 'time ', (+new Date - start));
},10);
var interval = setInterval(function(){
count++
console.log('setInterval ',count, ' time ', (+new Date - start));
if(count == 100){
clearInterval(interval);
}
}, 10);
setTimeout(function(){
console.log('setTimeout2', 'time ', (+new Date - start));
},10);
console.log('start');
for(var m = 0; m< 1000; m++){
console.log('m');
}
console.log('m is over time', +new Date - start);
console.log('end');
//执行结果
n
n is over time 28
start
m
m is over time 50
end
setTimeout1 time 51
setInterval 1 time 52
setTimeout2 time 52
setInterval 2 time 60
setInterval 3 time 70
setInterval 4 time 80
setInterval 5 time 90
setInterval 6 time 100
setInterval 7 time 110
setInterval 8 time 120
setInterval 9 time 130
setInterval 10 time 140
setInterval 11 time 150

线程代码执行
由此可以看出,异步事件发生时,就会排队,只在线程空闲的时候才执行。
开始启动一个10毫秒的延迟的定时器,一个10毫秒的间隔定时器
主线代码执行了50毫秒,在10毫秒的时候,setTimeout1,setInterval, setTimeout2应该立即执行,但是线程里还在执行主线代码,setTimeout1,setInterval, setTimeout2排队等待线程空闲。50毫秒后,主线代码执行结束,开始执行setTimeout1,setInterval,setTimeout2。
浏览器只对一个setInterval的实例进行排队, 没有对多个实例进行排队,后续线程中没有排队的处理程序,setInterval每隔10毫秒执行。

wepback学习笔记

webpack学习笔记

1.css-loader是用来处理.css文件,style-loader是通过在页面中插入style标签处理样式

1
2
3
4
//js文件中加载css文件
require('style-loader!css-loader!./hello.css')
//在命令行中通过module-bind处理css文件的加载
webpack hello.js hello.bundle.js --module-bind 'css=style-loader!css-loader'

2.命令行中参数
–module-bind 打包文件需要用到的loader
–watch 监听文件的变化并重新打包
–progress 显示打包进度
–display-modules 打包的所有的模块
–display-reason 模块打包原因

webpack config

*在项目中创建webpack.congfig.js文件,在命令行中运行webpack,会默认执行webpack.config.js中的配置,如果webpack的配置文件不是webpack.config.js,在命令行中则需要配置文件的名字

1
2
3
4
5
6
7
8
//命令行
webpack --config webpack.dev.config.js
//可以用npm来给wepback执行的时候添加参数,在package.json
{
'scripts':{
'webpack':'webpack --config webpack.dev.config.js --progress --display-module --display-reasons --color'
}
}

webpack配置参数entry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//一个入口文件,entry的值为字符串
module.exports = {
entry:'./src/script/main.js',
output:{
path:'./dist/js',
filename:'bundle.js'
}
}
//多个入口文件,entry的值为数组,将main.js和a.js打包在一起,生成bundle.js
module.exports = {
entry:['./src/script/main.js','./src/script/a.js'],
output:{
path:'./dist/js',
filename:'bundle.js'
}
}
//多页面文件,entry的值为对象,name为entry中的key,hash为此次打包的hash值,chunkhash为文件的md5值,可以认为是文件的版本号
module.exports = {
entry:{
"main":"./src/script/main.js",
"a":"./src/script/a.js"
},
output:{
path:'./dist/js',
//filename:'[name]-[hash].js'
filename:'[name]-[chunkhash].js'
}
}

自动化打包生成页面中的html,动态打包的文件名是不确定的

利用webpack的html插件来解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//安装html-webpack-plugin插件
npm install html-webpack-plugin --save-dev
//使用html-webpack-plugin插件,webpack.dev.config.js
var htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:{
main:'./src/script/main.js',
a:'./src/script/a.js'
},
output:{
path:'./dist/js',
filename:'[name]-[chunkhash].js'
},
plugins:[
new htmlWebpackPlugin()
]
}
//此时执行npm run webpack 会在dist/js目录里生成一个引用a.js main.js的index.html文件,这个index.html并没有与项目根目录下创建的index.html有关联
//关联根目录下的index.html
module.exports = {
entry:{
main:'./src/script/main.js',
a:'./src/script/a.js'
},
output:{
path:'./dist/js',
filename:'[name]-[chunkhash].js'
},
plugins:[
new htmlWebpackPlugin({
template:'index.html'
})
]
}
// 打包后生成的index.html,a.js main.js都在dist/js/目录下,需要的目录结构应为
dist---index.html js/a.js js/main.js,此时需要修改output的path,filenam配置
var htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:{
'main':'./src/script/main.js',
'a':'./src/script/a.js'
},
output:{
path:'./dist',
filename:'js/[name]-[chunkhash].js'
},
plugins:[
new htmlWebpackPlugin({
filename:'index-[hash].html',//指定文件的名字
template:'index.html', //模板的名称
inject:'head', //js插入的位置
title:'webpack is good', //给模板index.html模板传递变量
date:new Date() //给模板传递date参数
})
]
}
//index.html引用htmlWebpackPlugin里的title
<title><%= htmlWebpackPlugin.options.title %></title>
<body><%= htmlWebpackPlugin.options.date %></body>
//在html中遍历出htmlWebpackPlugins的能获取的参数
<div><%= htmlWebpackPlugin.options.date %></div>
<% for(var key in htmlWebpackPlugin) { %>
<%= key %>
<% } %>
<% for (var key in htmlWebpackPlugin.files){ %>
<%= key%>: <%= JSON.stringify(htmlWebpackPlugin.files[key]) %>
<% } %>
<% for (var key in htmlWebpackPlugin.options){ %>
<%= key%>: <%= JSON.stringify(htmlWebpackPlugin.options[key]) %>
<% } %>
//htmlWebpackPlugin的key
files
options
//htmlWebpackPlugin.files的key/value
publicPath: ""
chunks: {"main":{"size":26,"entry":"js/main-41d4982751088424227f.js","hash":"41d4982751088424227f","css":[]},"a":{"size":18,"entry":"js/a-4f55e4d7524c54cdeb7a.js","hash":"4f55e4d7524c54cdeb7a","css":[]}}
js: ["js/main-41d4982751088424227f.js","js/a-4f55e4d7524c54cdeb7a.js"]
css: []
manifest:
//htmlWebpackPlugin.options的key/value
template: "/home/marong/webpack-demo/node_modules/html-webpack-plugin/lib/loader.js!/home/marong/webpack-demo/index.html"
filename: "index.html"
hash: false
inject: "head"
compile: true
favicon: false
minify: false
cache: true
showErrors: true
chunks: "all"
excludeChunks: []
title: "webpack is good"
xhtml: false
date: "2017-03-25T12:12:45.828Z"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:{
'main':'./src/script/main.js',
'a':'./src/script/a.js'
},
output:{
path:'./dist',
filename:'js/[name]-[chunkhash].js',
publicPath:'http://cdn.com/js' //上线打包地址占位,打包后生成的js文件地址是以此开头
},
plugins:[
new htmlWebpackPlugin({
//filename:'index-[hash].html',
filename:'index.html',
template:'index.html',
inject:fasle, //关闭自动注入js文件,手动在index.html中插入js
minify:{ //对html文件进行压缩
removeComments:true,//删除html中注释
collapseWhitespace:true //删除html中空格
},
title:'webpack is good',
date:new Date()
})
]
}
//index.html
<head>
<script src='<%= htmlWebpackPlugin.chunks.main.entry'></script>
<script src='<%= htmlWebpackPlugin.chunks.a.entry'></script>
</head>
//配置publicPath,生成的html
<script src="http://cdn.com/js/main-19a6af676da9ed429fb5.js"></script>
<script src="http://cdn.com/js/a-9b44f8a7189f4606479f.js"></script>

处理多页面应用的情况

假设有a.html b.html c.html三个页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//webpack.dev.config.js配置
var htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:{
'main':'./src/script/main.js',
'a':'./src/script/a.js',
'b':'./src/script/b.js',
'c':'./src/script/c.js'
},
output:{
path:'./dist',
filename:'js/[name]-[chunkhash].js',
publicPath:'http://cdn.com/'
},
plugins:[
new htmlWebpackPlugin({
//filename:'index-[hash].html',
filename:'a.html',
template:'index.html',
inject:false,
title:'This a.html',
date:new Date()
}),
new htmlWebpackPlugin({
//filename:'index-[hash].html',
filename:'b.html',
template:'index.html',
inject:false,
title:'This is b.html',
date:new Date()
}),
new htmlWebpackPlugin({
//filename:'index-[hash].html',
filename:'c.html',
template:'index.html',
inject:false,
title:'This is c.html',
date:new Date()
})
]
}
//index.html模板中js都固定引入chunks.a.entry,所以生成的三个页面都会有a.js
//解决方案,可以在webpack配置文件中传递变量给模板,模板来判断引入哪个js,
//这种方式比较麻烦.html-webpack-plugin中能设置chunks的传递值(默认传递所有的chunks),只传递对当前页面有用的chunks
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title><%= htmlWebpackPlugin.options.title %></title>
<script src="<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script>
<script src="<%= htmlWebpackPlugin.files.chunks.a.entry %>"></script>
</head>
<body>
<!-- 文章主体start -->
<h1>webpack title</h1>
</body>
</html>

多页面应用通过设置chunks参数,不同页面通过同一个模板加载相应的js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//webpack.dev.config.js
var htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:{
'main':'./src/script/main.js',
'a':'./src/script/a.js',
'b':'./src/script/b.js',
'c':'./src/script/c.js'
},
output:{
path:'./dist',
filename:'js/[name]-[chunkhash].js',
publicPath:'http://cdn.com/'
},
plugins:[
new htmlWebpackPlugin({
//filename:'index-[hash].html',
filename:'a.html',
template:'index.html',
inject:true,//打开自动注入js
title:'This a.html',
chunks:['main','a'] //对a.html有用的chunks
//excludeChunks:['b','c']//如果页面中需要加载大多数chunks,可以采用排除的方式,排除当前页面不需要的chunks
}),
new htmlWebpackPlugin({
//filename:'index-[hash].html',
filename:'b.html',
template:'index.html',
inject:true,
title:'This is b.html',
chunks:['b']
}),
new htmlWebpackPlugin({
//filename:'index-[hash].html',
filename:'c.html',
template:'index.html',
inject:true,
title:'This is c.html',
chunks:['c']
})
]
}
//index.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 文章主体start -->
<h1>webpack title</h1>
</body>
</html>

如果追求页面的性能,减少http的请求数,想在页面中以inline的形式插入js

html-webpack-plugin的作者给出了解决方法,在https://github.com/jantimon/html-webpack-plugin/blob/master/examples/inline/template.jade
中,利用webpack暴露出的变量compilation.assets在页面中插入chunks中的entry,由于webpack配置了publicPath,chunk的ernty都带着publicPath,
compilation.assert中存储的是不带publicPath的entry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title><%= htmlWebpackPlugin.options.title %></title>
<script>
<%= compilation.assets[htmlWebpackPlugin.files.main.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %>
</script>
</head>
<body>
<!-- 文章主体start -->
<h1>webpack title</h1>
</body>
</html>

loader

处理资源文件,返回新的资源。
loader的处理方式有三种,require加载文件相应文件的时,在webpack配置文件里,在命令行里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//require的时候
require('style-loader!css-loader!./src/style/common.css')
//webpack的配置文件
{
module:{
loaders:[
{
test:/\.jade$/,loader:"jade"
},
{
test:/\.css$/,loader:'style!css'
},
{
test:/\.css$/, loaders:["style","css"]
}
]
}
}
//命令行列
webpack --module-bind jade --module-bind 'css=style!css'

webpack babel

处理es6语法,es6每年都会修订,es2015/es2016/es2017,latest插件包含所有特性(es2015/es2016/es2017)
通过preset设置babel-loader转换特性,插件需要通过npm来安装
给babel指定参数有四种方式,一种是在babel-loader后直接跟参数,一种是在webpack配置文件中通过query设置,
还可以通过创建.babelrc配置文件,在package.json中配置babel

设置babel-loader, exclude,include时候,需要绝对路径,并且同时设置,不然会报错
couldn’t find preset latest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//安装babel
npm install --save-dev babel-loader bable-core
//安装latest插件
inpm install --save-dev bable-preset-latest
//babel-loader直接跟参数
//配置文件中设置参数
var htmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
module.exports = {
entry:'./src/app.js',
output:{
path:'./dist',
filename:'js/[name].bundle.js'
},
module:{
loaders:[
{
test:/\.js$/,
loader:'babel',
exclude:path.resolve(__dirname, 'node_modules'),//排除babel-loader编译文件夹
include:path.resolve(__dirname, 'src'),//babel-loader很耗时,指定编译的文件夹
query:{
presets:["latest"]
}
}
]
},
plugins:[
new htmlWebpackPlugin({
filename:'index.html',
template:'index.html',
inject:'body'
})
]
}
//.babelrc配置文件
{
"presets":["latest"]
}
//package.json配置babel
"babel":{
"presets":["latest"]
},

webpack处理css

  • postcss-loader css后处理loader,postcss常用插件autoprefixer
    style-loader css-loader post-css 如果在css文件中@import ‘common.css’, webpack会用style-loader,css-loader处理,但不会用postcss-loader来处理,
    并把通过import引入的css新建一个style标签来插入到页面中
    需要css-loader配置参数,importLoaders=num,设置在css-loader之后,指定几个数量的loader来处理import的css
    1
    npm install --save-dev style-loader css-loader postcss-loader autoprefixer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//webpack.dev.config.js
var htmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
module.exports = {
entry:'./src/app.js',
output:{
path:'./dist',
filename:'js/[name].bundle.js'
},
module:{
loaders:[
{
test:/\.js$/,
loader:'babel',
exclude:path.resolve(__dirname, 'node_modules'),
include:path.resolve(__dirname, 'src'),
query:{
presets:["latest"]
}
},
{
test:/\.css$/,
loader:'style-loader!css-loader?importLoaders=1!postcss-loader'
}
]
},
//postcss:function(){//postcss是数组,填写postcss-loader需要的插件,必填也可直接写成一个数组的格式
//return [
//require('autoprefixer')({
//broswers:['last 5 versions']
//})
//]
//},
postcss:[
require('autoprefixer')({
broswers:['last 5 versions']
})
],
plugins:[
new htmlWebpackPlugin({
filename:'index.html',
template:'index.html',
inject:'body'
})
]
}

webpack 处理less sass文件

如果是less文件中import less文件,能正常编译,不需要给css-loader配置importLoaders参数

1
npm install less less-loader sass-loader --save-dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//webpack 配置
module:{
loaders:[
{
test:/\.js$/,
loader:'babel',
exclude:path.resolve(__dirname, 'node_modules'),
include:path.resolve(__dirname, 'src'),
query:{
presets:["latest"]
}
},
{
test:/\.css$/,
loader:'style-loader!css-loader?importLoaders=1!postcss-loader'
},
{
test:/\.less/,
//loader:'style-loader!css-loader!postcss-loader!less-loader'
loader:'style!css!postcss!less'//可以省略-loader
}
]
},

webpack 处理项目中的模板文件

*有多种模板文件loader, html-loader, ejs-loader

1
2
//html-loader把模板处理为字符串
npm install --save-dev html-loader ejs-loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 文章主体start -->
<h1>webpack title</h1>
<div id="app"></div>
</body>
</html>
//lader.html
<div class="layer">
<div>This is a layer</div>
</div>
//layer.tpl
<div class="layer">
<div><%= name %></div>
<% for (var i = 0; i < arr.length;i++) {%>
<%= arr[i] %>
<% } %>
</div>
//layer.js
//import tpl from './layer.html';
import tpl from './layer.tpl';
import './layer.less';
function layer(){
return {
name:'layer',
tpl:tpl
}
}
export default layer;
//app.js
import './css/common.css';
import Layer from './components/layer/layer.js';
const App = function(){
var layer = new Layer();
var app = document.getElementById('app');
//html string
app.innerHTML = layer.tpl;
//ejs function
app.innerHTML = layer.tpl({
name:'ejs',
arr:['tpl','html', 'loader']
})
}
new App();
//wepback配置文件
var htmlWebpackPlugin = require('html-webpack-plugin')
var path = require('path');
module.exports = {
entry:'./src/app.js',
output:{
path:'./dist',
filename:'js/[name].bundle.js'
},
module:{
loaders:[
{
test:/\.js$/,
loader:'babel',
exclude:path.resolve(__dirname, 'node_modules'),
include:path.resolve(__dirname, 'src'),
query:{
presets:["latest"]
}
},
{
test:/\.css$/,
loader:'style-loader!css-loader?importLoaders=1!postcss-loader'
},
{
test:/\.less$/,
//loader:'style-loader!css-loader!postcss-loader!less-loader'
loader:'style!css!postcss!less'
},
{
test:/\.tpl/,
loader:'ejs-loader'
},
{
test:/\.html$/,
loader:'html-loader'
}
]
},
//postcss:function(){
//return [
//require('autoprefixer')({
//broswers:['last 5 versions']
//})
//]
//},
postcss:[
require('autoprefixer')({
broswers:['last 5 versions']
})
],
plugins:[
new htmlWebpackPlugin({
filename:'index.html',
template:'index.html',
inject:'body'
})
]
}

webpack 处理图片及其他文件

file-loader 处理图片
url-loader 当图片大小小于limit的时候,转为base64,大于limit的时候,交由file-loader处理
image-webpack-loader 压缩图片大小,与file-loader,url-loader配合使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
npm install --save-dev file-loader url-loader image-webpack-loader
{
test:/\.(jpg|png|svg|gif)/i,
//loader:'file-loader',
//query:{
// name:'assets/[name]-[hash:5].[ext]'
//}
//loader:'url-loader',
//query:{
// limit:100000,
// name:'assets/[name]-[hash:5].[ext]'
//}
loaders:[
'url-loader?limit=100000&name=assets/[name]-[hash-5].[ext]',
'image-webpack'
]
}

webpack-dev-server

  • webpack-dev-server是一个小型Node.js Express服务器,它使用webpack-dev-middleware中间件来为通过wepback打包生成的资源文件提供web服务。它还有一个通过Socket.IO连接着webpack-dev-server服务器的小型运行时程序。
  • publicPath
  • 启动webpack-dev-server后,目标文件中看不到编译后的文件,实时编译后的文件都保存到了内存中。

postcss-sprites 自动拼合图片

能自动拼合图片,能生成单个或多个sprites图片,并能自动输出图片的大小

1
npm install --save-dev postcss-sprites

配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
var postcss = require('postcss')
var postcssSprites = require('postcss-sprites')
var postcssAssets = require('postcss-assets')
var postcssNext = require('postcss-cssnext')
var updateRule = require('postcss-sprites/lib/core').updateRule;
new webpack.LoaderOptionsPlugin({
options: {
postcss:function(){
return [
postcssAssets({
loadPaths: ['./src/images/'],
relative: true
}),
postcssNext({//代替autoprefixer
browsers: ["> 1%", "last 2 version", "Android >= 4.0"]
}),
postcssSprites({
//需要处理样式表的文件夹
stylesheetPath: './src/css',
//生成sprites图片的文件夹
spritePath: './src/images/outputsprite',
outputDimensions: true,
skipPrefix: true,
//过滤需要合并的图片
filterBy: function(image) {
if (!/sprite/.test(image.url)) {
return Promise.reject();
}
return Promise.resolve();
},
//可以根据匹配规则,合并某组文件为一个sprites
groupBy: function(image) {
//图片文件名中有shapes合并为一个sprite.shape.png
// if (image.url.indexOf('shapes') === -1) {
// return Promise.reject(new Error('Not a shape image.'));
// }
// return Promise.resolve('shapes');
//根据文件夹拼合一组图片,如果有/images/sprites/index /images/sprites/login 这两个文件夹,会生成三张图片,sprites.index.png,sprites.login.png, sprites.other.png 三张图片
let groups = /\/images\/sprites\/(.*?)\/.*/gi.exec(image.url);
let groupName = groups ? groups[1] : 'other';
image.retina = true;
image.ratio = 1;
if (groupName) {
let ratio = /@(\d+)x$/gi.exec(groupName);
if (ratio) {
ratio = ratio[1];
while (ratio > 10) {
ratio = ratio / 10;
}
image.ratio = ratio;
}
}
return Promise.resolve(groupName);
},
hooks: {
//onUpdateRule控制输入到css的内容,下面的代码回在生成的css中带有图片的宽高
onUpdateRule: function(rule, token, image) {
// Use built-in logic for background-image & background-position
updateRule(rule, token, image);
['width', 'height'].forEach(function(prop) {
var value = image.coords[prop];
if (image.retina) {
value /= image.ratio;
}
rule.insertAfter(rule.last, postcss.decl({
prop: prop,
value: value + 'px'
}));
});
}
}
})
]
},

extract-text-webpack-plugin

把样式单独打包成一个样式文件,并插入到页面中原来注入style标签的位置,对css、less、sass文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var DeployFtpWebpackPlugin = require('./deploy.plugin');
// process.traceDeprecation = true;
module.exports = {
entry:'./src/main.js',
output:{
path: path.resolve(__dirname, 'dist'),
filename: 'bundle-[hash].js'
},
module: {
rules: [
{
test:/\.js$/,
loader:'babel-loader',
exclude: /node_modules/
},
{
test:/\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use:'css-loader?importLoaders=1!postcss-loader'
})
// loader: 'style-loader!css-loader?importLoaders=1!postcss-loader'
},
{
test:/\.scss/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader!postcss-loader!sass-loader'
})
// loader:'style-loader!css-loader!postcss-loader!sass-loader'
},
{
test:/\.(jpg|png|svg|gif)$/,
loader: 'url-loader',
query: {
limit:10,
name: 'assets/[name]-[hash:5].[ext]'
}
}
]
},
plugins: [
new ExtractTextPlugin("style.css"),
...

第三方库不打包处理,webpack.IgnorePlugin

1
2
3
4
var ignoreFiles = new Webpack,IgnorePlugin(/\/jquery.js$/);
module.exports = {
plugins: [ignoreFiles]
}

提取多入口文件中公共的代码,打包成一个独立的文件webpack.optimize.CommonsChunkPlugin

  1. 可以提取多入口文件中公共的代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    entry:{
    main:'./src/main.js',
    main2:'./src/main2.js',
    vender:Object.keys(packageJson.dependencies)
    },
    ...
    new webpack.optimize.CommonsChunkPlugin({
    name:'common',
    filename:'common.js',
    chunks: ['main', 'main2']
    }),
    //最后会生成三个文件common.js main.js main2.js vender.js

webpack.optimize.OccurenceOrderPlugin

  • 给组件分配ID,通过这个插件webpack可以分析考虑使用最多的模块,并未他们分配最小的ID
    在webpack2中改为webpack.optimize.OcurrenceOrderPlugin,在webpack2中默认开启,不用在配置文件中写

plugins: [
// webpack 1

  • new webpack.optimize.OccurenceOrderPlugin()
    // webpack 2
  • new webpack.optimize.OccurrenceOrderPlugin()
    ]

wepback.optimize.UglifyJsPlugin压缩JS代码

1
npm install uglifyjs-webpack-plugin --save-dev
1
2

遇到的问题

  1. 配置loader时不能省略-loader
  2. postcss配置报错,在webpack2中不能包含自定义配置项来配置loader,postcss用LoaderOptionsPlugin来给loader配置
    报错信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
    - configuration has an unknown property 'postcss'. These properties are valid:
    object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry, externals?, loader?, module?, name?, node?, output?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, stats?, target?, watch?, watchOptions? }
    For typos: please correct them.
    For loader options: webpack 2 no longer allows custom properties in configuration.
    Loaders should be updated to allow passing options via loader options in module.rules.
    Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
    plugins: [
    new webpack.LoaderOptionsPlugin({
    // test: /\.xxx$/, // may apply this only for some modules
    options: {
    postcss: ...
    }
    })
    ]

webpack2中需替换为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
new webpack.LoaderOptionsPlugin({
options: {
postcss:function(){
return [
require('autoprefixer')({
broswers:['last 5 version']
}),
require('postcss-assets')({
loadPaths: ['./src/images/'],
relative: true
}),
require("postcss-cssnext")({
browsers: ["> 1%", "last 2 version", "Android >= 4.0"]
}),
require('postcss-sprites')({
stylesheetPath: './src/css',
spritePath: './src/images/sprite.png',
outputDimensions: true,
skipPrefix: true,
filterBy: function(img) {
return /\/sp\-/.test(img.url);
},
groupBy: function(img) {
var match = img.url.match(/\/(sp\-[^\/]+)\//);
return match ? match[1] : null;
}
})
]
},
}
})

toString和valueOf的用法

数值、布尔值、字符串和对象都有toString(),valueOf方法。
null和undefined没有这两个方法。
toString():返回对象的字符串表示
valueOf():返回对象的字符串、数值或布尔值表示。
在javascript中,很多操作都会在后台隐式调用对象的toString()和valueOf()方法,以便取得可操作的值。
内置函数和操作符的一般执行流程:首先会调用对象的valueOf方法,然后确定该方法返回的值是否可以按照相应的规则转换,如果不能,则基于这个返回值再调用toString()方法,再操作返回的字符串。

type valueOf toString
undefined 无此方法 无此方法
null 无此方法 无此方法
boolean boolean “true”/“false”
string string string
object object string
Date number string
function function string

toString和valueOf的应用

应用1:利用输入函数名,会隐式调用valueOf(),toString()方法实现add()()()…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function add(){
var result = 0;
var args = [].slice.call(arguments);
args.forEach(function(num){
result += num;
});
var fn = function(){
var args = [].slice.call(arguments);
args.push(result);
return add.apply(null,args);
};
fn.valueOf = fn.toString = function(){
return result;
};
return fn;
}
add(1,3) //4
add(1)(3) //4
add(1,3)(1)(3) //8
//方法二,给函数添加result属性,用来保存每次计算的结果
function add(){
var args= [].slice.call(arguments);
var result = 0;
args.forEach(function(num){
result += num;
});
result += ~~add;
add.result = result;
return add;
}
add.valueOf = add.toString = function(){
return add.result;
};
add(1,3) //4
//以后每次调用需要重置result的值
add(1)(3) //4

阿里云服务器centos配置

购买ECS实例(申请香港vpc(专有网络服务器带固定公网IP,在服务器内部通过ifconfig查看不到外网网卡eth1))

登录ECS实例

2.1登录方法:
2.1.1.通过阿里远程终端登录
管理实例-远程连接-输入远程终端密码-如果屏幕卡住,按任意键–>输入用户名root,密码–>成功链接到远程实例
2.1.2 通过PuTTY链接linux系统
输入公网ip->输入用户名、密码

##.格式化和挂载数据盘(没有购买数据盘,暂时使用系统盘)
df -h :查看磁盘容量的使用情况
fdisk -l : 查看当前磁盘的情况

vpn服务器创建步骤

先看看你的主机是否支持pptp,返回结果为yes就表示通过。

modprobe ppp-compress-18 && echo yes

先更新一下再安装。

yum install update

安装ppp和pptpd

yum -y install ppp pptpd

配置pptpd

1.编辑配置文件 vi /etc/pptpd.conf
localip为vpn的网关地址
remoteip为vpn拨号获取的地址段,即在cmd里ipconfig里,ppp适配器vpn 192.168.8.2

1
2
localip 192.168.8.1
remoteip 192.168.8.2-254

2.编辑 vi /etc/ppp/options.pptpd,添加dns

1
2
ms-dns 8.8.8.8
ms-dns 8.8.4.4

3.配置vpn账号和密码,编辑/etc/ppp/chap-secrets

1
username1 pptpd password1 *

4.打开内核转发,编辑/tc/sysctl.conf

1
net.ipv4.ip_forward = 1

重启
sysctl -p
5.添加iptables转发规则,因为是专有网络的服务器,在服务器内部是看不到外网网卡eth1
修改/etc/sysconfig/iptables 里nat表

1
2
3
4
*filter
-A FORWARD -i ppp+ -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j TCPMSS --set-mss 1356
*nat
-A POSTROUTING -s 192.168.8.0/24 -j MASQUERADE

重启防火墙
service iptables save
service iptables restart

服务配置

1.重启pptpd服务

1
service iptables restart

2.设置pptp和iptables自启动

1
2
chkconfig pptpd on
chkconfig iptables on

vpn连接成功

遇到的问题

1.配置完成后,一直显示,无网络访问权限
[ 问题现象 ]
配置vpn后但是无法访问外网

[ 问题实例 ]
47.91.156.125

[ 处理意见 ]
目前我们检查您的主机47.91.156.125ping是正常的,您连接vpn后无法访问外网是您的配置问题导致的,具体需要您进行排查下,但是具体的您可以参考文档https://help.aliyun.com/knowledge_detail/41345.html进行核实下
按照参考文档重新设置了iptables的转发规则,成功了
2.用了两个安装pptpd vpn的脚本

参考链接

1.https://help.aliyun.com/knowledge_detail/41345.html
3.https://blog.linuxeye.com/412.html

安装FTP服务

  1. 检测是否安装了FTP # rpm -q vsftpd
  2. 安装FTP, yum install vsftpd
  3. 设置为开机启动,chkconfig vsftpd on
  4. 配置ftp参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # setsebool -P ftpd_disable_trans=1
    修改/etc/vsftpd/vsftpd.conf,在最后一行处添加local_root=/
    service vsftpd restart
    #mkdir /tmp/test 创建一个ftp上传时的目录
    //1、创建一个账号为test的账户,-s /sbin/nologin是让其不能登陆系统,-d 是指定用户目录为/tpm/test ,即该账户只能登陆ftp,却不能用做登陆系统用。
    #adduser -d /tmp/test -g ftp -s /sbin/nologin test
    #passwd test
    修改/etc/vsftpd/vsftpd.conf
    chroot_list_enable=YES
    chroot_list_file=/etc/vsftpd/chroot_list
    新增一个文件: /etc/vsftpd/chroot_list
    内容写需要限制的用户名:
    test
    重新启动vsftpd
    #service vsftpd restart
  5. 文件上传

    1
    2
    3
    4
    5
    lcd d:/frontend/baby/common2015/
    cd /tmp/test/
    chmod -R 777 /tmp/test
    put project.json
    //可以上传一个tar.gz的压缩包,上传到服务器后,再解压缩
  6. 文件下载

    1
    2
    3
    lcd d:/frontend/baby/common2015/
    cd /tmp/test/
    get index.html

    6.客户端ftp命令行
    win+R ftp 打卡ftp命令行
    open 47.91.156.125 打开ftp服务器,输入用户名, 密码

    lcd 进入客户端的文件夹
    !dir 显示客户端当前目录的文件信息
    cd 进入服务器的文件夹
    dir 显示服务器端当前目录的文件信息
    get 下载一个文件
    mget . 下载当前服务器文件下的所有的文件
    put 上传一个文件
    mput . 上传全部文件,不包括文件夹
    bye 退出ftp服务器
    ? 查看更多命令

    7.遇到的问题

    • 文件上传的时候,遇到vsftp上传文件出现553 Could not create file,是因为/tmp/test文件夹没有读写的权限,chmod -R 777,修改上传文件夹的读写权限
    • 文件下载的时候, ftp报错 200 port command successful. consider using pasv 425 failed to establish connection,关闭客户端电脑的防火墙就好了

nginx 安装步骤

  1. 检查安装环境

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //nginx 需要gcc编译
    rpm -qa gcc
    // nginx的Rewrite和HTTP模块会哟用到PCRE
    rpm -qa pcre
    //nginx的Gzip用到zlib
    rpm -qa zlib
    rpm -qa openssl
    //还需要安装pcre-devel openssl-devel,不然后续编译的时候,会报错
    ./configure: error: the HTTP rewrite module requires the PCRE library.
    yum -y install pcre-devel openssl openssl-devel
  2. 下载nginx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    //下载
    cd /usr/local/src
    wget http://nginx.org/download/nginx-1.10.3.tar.gz
    //解压
    tar -zxvf nginx-1.10.3.tar.gz
    //安装环境检查和安装配置
    cd nginx-1.10.3
    ./configure --prefix=/usr/local/nginx
    //编译
    make
    //安装
    make install
    //查看安装
    whereis nginx
    //启动nginx
    /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    //查看nginx进程状态
    ps -A | grep nginx
    //打开ssi
    server{
    location / {
    ssi on;
    ssi_silent_errors on;
    }
    }
    //重启nginx
    cd /usr/local/nginx/sbin
    ./nginx -s reload
  3. 参考链接
    http://www.cnblogs.com/heacool/p/6406664.html
    http://williamx.blog.51cto.com/3629295/958398

安装nodejs环境

  1. 安装nvm

    1
    2
    3
    4
    //https://github.com/creationix/nvm
    wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
    //打印NVM_DIR
    echo $NVM_DIR
  2. 安装nodejs

  3. nvm命令
    • nvm list-remote:列出所有可安装的版本;
    • nvm install v0.12.4:安装指定版本的nodejs
    • nvm ls 查看当前已安装的版本
    • nvm use v0.12.4 切换版本
    • nvm alias default v0.12.4 设置默认版本
  4. nodejs项目部署
    在服务器上创建测试代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const http = require('http')
    const hostname = '127.0.0.1';//专有网络不能填写公网ip地址
    const port = 9000;
    const server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.end('hello, this is my first test nodejs project');
    });
    server.listen(port, hostname, () => {
    console.log(`Server running at http://${hostname}:${port}/`);
    });

还需要配置nginx,并重启

1
2
3
4
5
location / {
proxy_pass http://127.0.0.1:9000;
}

pptpd vpn 查看当前在线用户

1
last | grep still | grep ppp

命令

  • 系统
    uname -a 查看内核/操作系统/CPU信息
    hostname 查看计算机名
    clear 清屏
    who 显示在线登录用户
    whoami 显示当前操作用户
    env 查看环境变量
  • 资源
    free -m 查看内存使用量和交换区使用量
    df -h 查看各分区使用情况
    du -sh <目录名> 查看指定目录大小
    grep MemTotal /proc/meminfo 查看内存总量
    grep MemFress /proc/meminfo 查看空闲内存量
    uptime 查看系统运行时间、用户数、负载
    cat /proc/loadavg 查看系统负载
  • 用户
    w 查看活动用户
    last 查看用户登录日志

  • 进程
    ps -ef 查看所有进程
    top 动态显示当前耗费资源最多的进程信息

  • 服务
    chkconfig –list 列出所有系统服务
    chkconfig –list | grep on 列出所有启动的系统服务
  • 程序
    rpm -qa 查看所有安装的软件包

安装Git

1
2
3
yum install git
git --version
yum remove git //卸载git

安装L2TP VPN

wget https://git.io/vpnsetup-centos -O vpnsetup.sh && sudo sh vpnsetup.sh
IPsec PSK: eRc…

查看秘钥
vi /etc/ipsec.secrets

// 添加VPN账号
/etc/ppp/chap-secrets

https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#os-x

git生成多个ssh配置

ssh是一种网络协议,用于计算机之间的加密登录。

github 生成ssh key

  1. 账户setting/SSH and GPG keys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ssh-keygen -t rsa -b 4096 -C "hollymarong@example.com"
    //输入两次密码
    enter password twice
    //打开ssh-agent
    eval "$(ssh-agent -s)"
    //在ssh-agent中添加SSH key
    ssh-add ~/.ssh/id_rsa
    //复制ssh key,并在github账户中添加ssh key
    clip < ~/.ssh/id_rsa.pub

gitlab 生成ssh key

  1. 账户setting/SSH Keys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ssh-keygen -t rsa -C "marong@corp.netease.com"
    //重命名文件~/.ssh/id_rsa_netease_nex,不要覆盖~/.ssh/id_rsa
    //输入两次密码
    enter password twice
    //打开ssh-agent
    eval "$(ssh-agent -s)"
    //在ssh-agent中添加SSH key
    ssh-add ~/.ssh/id_rsa_netease_nex
    //复制ssh key,并在github账户中添加ssh key
    clip < ~/.ssh/id_rsa_netease_nex.pub

在~/.ssh文件夹中,新建config文件

1
2
3
4
5
6
7
8
9
10
11
12
//新建config文件
touch config
//配置config文件
# github user(marong@corp.netease.com) netease nex
Host gitlab.ws.netease.com
User marong
IdentityFile ~/.ssh/id_rsa_netease_nex
# Default github user(hollymarong@gmail.com) 默认配置,一般可以省略
Host github.com
User hollymarong
Identityfile ~/.ssh/id_rsa

检测ssh是否配置成功

1
2
3
4
ssh -T git@github.com
//Hi hollymarong! You've successfully authenticated, but GitHub does not provide shell access.
ssh -T git@gitlab.ws.netease.com
//ssh: connect to host gitlab.ws.netease.com port 22: Connection refused,提示连接不上,是权限的问题,不影响具体项目

git bash 客户端的问题

配置后以上之后,如果gitbash 出现error 证书的问题,这时候卸载git bash 重新安装之后就能拉取项目了

emacs-command

info

  • M-x info(C-h i) –> Emacs lisp Intro

Shell

  • eshell

Magit

  • Magit is a full Git interface

快捷键

  • Ctrl-H t Built in Tutorial
  • Ctrl-x Ctrl-c 退出ecma
  • Ctrl-v 翻到下一页
  • Alt-v 翻到上一页
  • Ctl-l 光标所在行的文字移动到中间,再次输入,移动到顶端,再次输入,移动到底端
  • Ctrl-p 上一行
  • Ctrl-n 下一行
  • Ctrl-f 向右移动
  • Ctrl-b 向左移动
  • M-f 对英文以单词为单位向右移动,对中文以标点符号为单位向右移动
  • M-b 对英文以单词为单位向左移动,对中文以标点符号为单位向左边移动
  • Ctrl-a 移动到一行的头部
  • Ctrl-e 移动到一行的尾部
  • M-a 移动到一句的头部
  • M-e 移动到一句的尾部
  • M-< 将光标移动到头部(Alt-Shift-<)
  • M-> 将光标移动到尾部(Alt-Shift->)
  • Ctrl-u-数字 Ctrl-f 将光标向右移动8个单词
  • Ctrl-u-8 m –在当前光标下输入8个m
  • Ctrl-g 用来终止命令
  • Ctrl-/|Ctrl_(Ctrl) 撤销命令
  • Ctrl-d 删除贯标后的一个字符
  • M-d 删除贯标后的一个单词
  • M-del 删除光标前的一个单词
  • del 删除高标前的一个字符
  • Ctrl-k 移除从光标到行尾的字符
  • M-k 移除从光标到行尾的字符
  • Ctrl-@ 移动到要删除的字符处 Ctrl+w
  • Ctrl-y 粘贴
  • C-h k C-p 在新视图中查看命令的详细说明
  • C-h f 函数名 解释一个函数名
  • C-h k 按键 查看快捷键
  • C-h v

窗口

  • Ctrl-x-1 只保留一个窗口
  • Ctrl-x-2 将屏幕垂直划分成两个窗格,光标停留在上方的窗格

文件

  • Ctrl-x Ctrl-f 查找文件
  • Ctrl-x Ctrl-s 存储文件
  • 创建新文件的方法,就是查找一个不存在的文件,然后保存
  • Ctrl-x Ctrl-b 列出缓冲区
  • Ctrl-x b+缓冲区的名字(按tab自动补全) 跳转到之前编辑的文件
  • Ctrl-x s 保存多个缓冲区
  • Ctrl-z 暂时离开emacs,不杀死emacs的进程
  • Ctrl-s 向前搜索
  • Ctrl-r 向后搜索
  • M-x kill-buffer 从buffer list删除buffer文件
  • M-x ibuffer 显示buffer list , D 删除buffer
  • C-x C-e 执行lisp语句

批量重命名文件

  • C-x C-j 打开当前文件所在目录 Dired模式
  • Shift+6 跳转到上级目录
  • C-; iedit模式在dired模式下快捷键失效,启用重新绑定的M-s e
  • C-x C-q 进入到dired模式的Editable编辑模式–>C-=(选中要重命名的文件)–>M-s e(同时编辑)-C-c C-c(编辑完成保存)
  • C-c Esc 退出修改

搜索和替换

  • search speed:ag(Linux)>pt(所有平台)>ack>grep
  • helm-ag 安装
  • M-x helm-do-ag-project-root <字段 !需要屏蔽的字段> –> C-c C-e (打开buffer 能直接编辑搜索出的结果)
  • C-c p s 快捷键绑定搜索
  • C-c c-k 取消
  • 报错Error running time helm-ag–do-ag-cand..,是因为没有安装ag
  • 项目下全局搜索和替换
    1
    SPC s a f --> <keyword> ---> C-c C-e 能编辑接口 ---> C-c C-c 提交修改

安装ag

1
2
brew install the_silver_searcher
ag --version

helm-ag

  • 搜索工具,linux下工具,在windows下需要Cygwin工具
  • 用babun安装,编译完成后把ag.exe放到bin下,添加环境变量
    C:\Users\marong.babun\cygwin\bin
    1
    2
    3
    4
    5
    6
    7
    $pact install libpcre-devel
    $pact install liblzma-devel
    $git clone https://github.com/ggreer/the_silver_searcher.git
    $cd the_silver_searcher
    $./build.sh

imenu

  • M-x imenu 列出函数列表,能列出lisp,不能列出js函数列表
  • M-x counsel-imenu 能列出js函数列表
  • M-s i:列出函数列表,调用counsel-imenu

flycheck 代码检查

  • 安装全局的eslint

    1
    2
    3
    4
    npm install -g eslint
    //配置文件~/.eslintrc
    //找到一个js文件,flycheck-verify-setup --> javascript-eslint --> 就会看到eslint打开了
    // M-x flycheck-mode就能看到错误提示
  • 使用项目中安装和配置的eslint

    1
    2
    SPC e S(flycheck-set-checker-executable)--> javascript-eslint --> 项目下/node_modules/eslint/bin/eslint
    --> M-x flycheck-mode就能看到错误提示
  • 只对js文件进行代码检查,windows系统不好用

  • 安装过程玩后报错require let-alist not provided,在elpa文件下有let-alist的包,最后发现let-alist.el文件没有内容,卸载后重新下载后,flycheck能用了
  • M-x flycheck-verify-setup

    1
    (add-hook 'js2-mode-hook 'flycheck-mode)
  • M-x flycheck-list-errors 开启一个新的buffer来展示错误,在buffer具体错误上回车,可以跳转到相应的代码

evil 安装过程遇到的问题

  • 安装evil后报错提示undo-tree goto-chg两个package未安装,分别安装两个packages
  • M-x evil-mode找不到evil模式,重新卸载M-x package-list-packages –> d –> x 卸载之后,重新安装后,就能打开evil模式了

yasnippet

  • 可以自定义
  • al Tab —> alert
  • f Tab —> function
  • com Tab —> / /
  • log Tab —> console.log

auto-yasnippet

  • M-x aya-create
  • M-x aya-expand
    1
    2
    3
    (add-to-list~AAA path~AAA name~AAA)
    M-x aya-create
    M-x aya-expand

模式

  • Ctrl—x text-mode
  • M-x fundamental-mode 缺省模式
  • M-x auto-fill-mode 自动换行模式
  • 设置自动换行边界数字:Ctrl-u 70 Ctrl-x f
  • M-x linum-mode 设置行号
  • M-x recentf-mode 该模式保存最近打开的文件

Org Mode

  • Tab 展开/关闭
  • C-c C-t Todo/Done/None切换
  • M-Return 自动排序号
  • <s Tab 自动补全 #+BEGIN_SRC #+END_SRC
  • C-c C-c 保存
  • C-x C-e 导出html/LaTex(PDF)格式 –> ho(导出html,并打开)
  • C-c a s 搜索日程

M-x org-capture 记笔记

创建todo

  • SPC C(capslock) c –> t –>新建TODO事项
  • M-x org-capture –>t–>新建TODO事项
  • C-c C-s 设置日程开始时间
  • C-c C-s 设置日程结束时间
  • C-c a-a 查看日程
  • C-c C-c 退出

org-pomodoro

  • 时间管理工具
  • 安装完成后需要require
  • 在gtd.org里C-c a –>打开agenda–> 在一个gtd里输入M-x org-pomodoro—>在状态栏里就能看到时间管理

Occur模式

  • 搜索模式
  • M-s o <搜索内容>
  • 回车跳转到搜索的地方
  • C-x o在搜索的buffer和popwin之间切换
  • M-x occur-edit-mode(e) 能直接在搜索结果里进行修改
  • 增强occurmode,M-s o能直接抓取光标所在的单词,或选中的字段
  • SPC o d 搜索光标所在单词

web-mode

  • M-x customize-group –> web-mode –> 定制
  • C-h v web-mode-markup-indent-offset 查看html 当前缩进值
  • C-c t i 切换当前缩进值2/4
  • C-M \ 整个buffer缩进
  • M-; toggle 注释

js2-refactor

  • 提取函数:选中重构的函数,C-c C-m –> ef
  • 提取方法函数:C-c C-m –> em
  • 提取本地变量为函数参数: C-c C-m –> ip
  • 提取函数参数为函数内变量: C-c C—m –>lp

Dired模式

  • Dired模式下可以拷贝、删除、重命名文件
  • C-x d <文件> 显示制定文件夹下文件详情 大小、时间等
    • 创建文件夹和文件,回车进入新建的文件夹里
  • C-x C-f : create file
  • g: refresh dired buffer
  • C: copy file
  • D: delete file
  • d: mark delete file
  • u: cancle mark
  • x: execute
  • R: rename filename
    1
    2
    //能打开当文件所在的目录C-x C-j
    (required 'dired-x)

expand-region

  • C-= 能选中光标所在单词,按=逐级往外扩展选中
  • SPC v

iedit

  • M-x iedit-mode 定位光标所在的单词,同时编辑多个区域
  • C-; 绑定的快捷键 多编辑toggle
  • M-s . (搜索光标所在的单词), C-s 正向查找, C-r反向查找
  • M-s o (搜索内容) -> e 进入过occur编辑模式 —>C-=(expand-region) 选中要编辑单词 -> M-s e 编辑多个区域

evil

  1. vim 80% 的功能
  2. the difference between Normal and Emacs state
    • C-u vim中向上翻页 Emacs中Universal args, 设置之后Ctrl-u/d 可以上下翻页了
  3. evil state
    • evil normal state
    • evil insert state
    • evil visual state
    • evil motion state
    • evil operator state
  4. C-z(M-x evil-emacs-state) toggle between Normal and Emacs state

evil-leader

1
2
3
4
(evil-leader/set-key
"e" 'find-file
"b" 'switch-to-buffer
"k" 'kill-buffer)

window-numbering

  • M- 窗口跳转

powerline

  • 增强mode-bar

evil-surround

  • viw S “|’ 给单词加上双引号
  • cs ‘“ 把单引号换成双引号
  • cs “( 把双引号换成括号,如果按9上的(会有空格
  • cs “) 把双引号换成括号,如果按0上的)不会有空格
  • SPC v s

evil-nerd-commenter

  • M; 注释切换
  • V jk –> M;
  • gv 选择上次选中的区域,M; –>取消注释

use-package

  • 更安全的require
    1
    2
    3
    4
    ;; 会报错
    (require 'xxx)
    ;; 不会报错
    (use-package xxx)

###spacemacs

  • 直接glone 到d:/Emacs24.4/.emacs.d目录
  • 安装初始化选项,在安装的过程中
  • spc+f+j 打开当前文件所在的目录
  • spc+qq 退出emacs
  • SPC-b-f 在finder中打开文件
  • SPC-f-e-R 安装包
  • SPC-f-e-d 打开.spacemacs.d/init.el
  • SPC-f-e-i 打开.emacs.d/init.el
  • SPC-f-e-r 安装包
  • SPC-h-SPC 查看layer安装的packages
  • Ctrl-s 搜索
  • M-x (SPC-SPC)命令
  • C-h f查看lisp函数定义的文档
  • C-h v查看lisp的变量
  • C-h C-f 查看函数
  • SPC-s-j 查看函数列表文件
  • SPC-h-l 查看安装的elpa package文件
  • SPC-h-r 查看spacemacs全部文档
  • SPC-h-R 查看spacemacs 安装包的文档
  • SPC-f-f find file
  • SPC-f-j dired mode
  • SPC-o-y 有道搜索单词
  • ,-d-m-e 展开宏函数,q退出宏函数
  • SPC- 窗口间的跳转
  • SPC-w-d 删除打开的窗口
  • SPC-p-f 查找项目下的文件
  • SPC-f-f 查找当前项目下的文件夹
  • SPC-f-L 查找本地文件
  • SPC-J-= 格式化全文
  • C-h 返回上级目录
  • insert 模式下
    • C-a 移动到行首
    • C-w 移动到行尾

spacemacs 文件操作

  • SPC-p-f 查找当前文件下的目录
  • SPC-f-L 查找整个系统的文件
  • SPC-f-l
  • SPC-f-o 使用外部程序打开文件
  • SPC-f-E sudo-edit 以root权限编辑文件host
  • SPC-f-d 删除当前文件及buffer
  • SPC-f-j 打开当前buffer的dired目录
  • SPC-f-r 打开当前常用的文件
  • SPC-f-R 重命名当前文件
  • SPC-f-v 给当前项目设置lisp的变量
  • SPC-f-y 查看当前buffer的路径并复制
  • SPC-f-e-d 打开/.spacemacs.d/init.el文件
  • SPC-f-e-i 打开/.emacs.d/init.el 文件
  • SPC-f-c copy file
  • SPC-f-b jump bookmark
  • SPC-f-s 保存当前buffer
  • SPC-f-S 保存所有的buffer

spacemacs buffer 操作

  • SPC-b-. 显示buffer操作提示n/p切换buffer
  • SPC-b-b 查找buffer
  • SPC-b-d 删除buffer

    spacemacs 创建自己的layer

2048game

  • C-z 切换Normal–> Emacs state

Tips and Tricks

  • Use “Search” instead of cursor movement
  • Ctrl-S and word to jump to
  • Alt-N to jump to same word cursor is on
  • F13 to jump to a character
  • Repeat a command with a prefixed number, i.e.
    • Alt-9 Alt-B to jump back 9 words
    • Ctrl-5 Ctrl-P to jump up 5 lines
    • linum-relative mode is helpful

插件

  • Evil:EvilVim 模拟器
  • Options/Manage Emacs Packages/Company/install
    • 安装完成之后,在文件中开启M-x company-mode 打开自动补全功能 M-n M-p上下选择要补全的内容
    • M-x global-company-mode 打开的任何文件都会自动补全
  • 换color theme
    • M-x load-theme monokai

packages

  • M-x package-list-packages === Options/Manage Emacs Packages(因为init.el中添加了melpa源,packagelist 里有3000多个包)
  • 选择要安装的包,光标放在要安装的包上,i
  • 取消选择包,光标放在要取消的包上,u
  • 搜索包,C-s 包名
  • 选择要删除的包,光标放在要删除的包上,d
  • 执行命令,x
  • 更新所有的packages, U

custorm-group

  • 修改插件配置
    • M-x customize-group
    • js2-mode
    • external-varialable 点击向下的箭头
    • 修改颜色为darkgrey
    • 点击state -> Save for future sessions
    • q 退出
    • 未定义的js变量为灰色
    • 在init.el 最后面,多了一行
      1
      2
      (custom-set-faces
      '(js2-external-variable ((t (:foreground "dark gray"))))

nodejs-repl

M-x nodejs-repl
,sb 运行当前buffer的代码

smex 安装步骤

  • 在d:/Emacs24.4/.emacs.d/init.el中包列表中加入smex
  • 增加M-x的插件,C-s切换选项
    1
    2
    3
    4
    5
    6
    //加载
    (require 'smex) ; Not needed if you use package.el
    (smex-initialize) ; Can be omitted. This might cause a (minimal) delay
    ; when Smex is auto-initialized on its first run.
    //配置快捷键
    (global-set-key (kbd "M-x") 'smex)

swiper 插件的安装

  • 在d:/Emacs24.4/.emacs.d/init.el中包列表中加入swiper
  • 配置完快捷键后,发现与counsel有关的函数不能执行
  • 用M-x package-list-packages 重新安装的counsel

调试

– emacs –debug-init
– emacs -Q
– 这样Emacs就不会加载任何配置文件, 你可以对比Emacs默认的行为来判断是否真的是因为配置文件引起的问题。

mac 下安装步骤

brew install emacs –with-cocoa –with-gnutls
ln -s /usr/local/Cellar/emacs/25.3/Emacs.app /Applications

windows下安装步骤

  • 到这个网址可以下载到Emacs的windows版本:http://ftp.gnu.org/pub/gnu/emacs/windows/
  • 双击bin文件夹里的addpm.exe进行安装
  • 在D创建文件夹Emacs24.4
    • 为了方便全局调用,请添加bin路径到环境变量(比如我的,D:\Emacs24.4\bin)。
  • 在cmd里,输入emacs -nw,以终端模式来运行emacs;只输入emacs,以GUI模式来运行

  • 启动emacs,用鼠标点击Options菜单,随便点击一两个选项,比如点击一下Active Region Highlighting,然后点击Save Options,这样做的目的是让emacs自动创建.emacs.d目录以及.emacs文件

  • 在Emacs窗口的最后一行,显示Wrote C:\Users\marong\AppData\Roaming.emacs

遇到的问题

  • windows 下flycheck不太兼容
  • tern libray not found

    1
    2
    //window 下
    npm install -g tern

macos ,在.spacemacs.d/init.el的user-init里添加

1
2
(setenv "PATH" (concat (getenv "PATH") ":/Users/marong/.nvm/versions/node/v7.10.0/bin"))
(setq exec-path (append exec-path '("/Users/marong/.nvm/versions/node/v7.10.0/bin")))

国内镜像

  • 用清华的镜像稳定一点,elpachina不太稳定
    1.popkit
    1
    2
    (setq package-archives
    '(("popkit" . "http://elpa.popkit.org/packages/")))

2.elpachina
http://elpa.emacs-china.org/

1
2
(setq package-archives '(("gnu" . "http://elpa.emacs-china.org/gnu/")
("melpa" . "http://elpa.emacs-china.org/melpa/")))

elisp(emacs lisp)

  • 写elisp
  • 导航栏里/Buffers/scratch 默认开启Lisp Interaction mode
  • 一般计算表达
  • 输入(+ 2 2) — C-x C-e 在最下面提示4
  • 输入;2 +3*4 回车之后 输入内容居中
  • C-h m 查看当前模式 内容居中的原因是开启了electric-indent mode
  • M-x electric-indent-mode 关闭该模式
  • 重新输入; 2 + 3 * 4 — 分号是注释的意思
  • (+ 2 (* 3 4) —>C-x C-e —>结果14
  • 设置变量
  • 设置变量(setq my-name “marong”) C-x C-e
  • 输出变量(message my-name) C-x C-e
  • 设置函数
    1
    2
    3
    4
    5
    6
    7
    8
    (defun my-func() //没有变量
    //定义成交互式函数,就能M-x my-func
    (interactive)
    (message "hello, %s" my-name)) //%s加上是为了输入my-name
    (my-func)
    //为my-func函数执行绑定快捷键
    (global-set-key (kbd "<f2>") 'my-func)
  • M-x eval-buffer 执行整个buffer文件

    macro

  • 可以生成代码的代码

markdown编辑

1.markdown中写表格|name|age|gender ->回车 ->生成表格
2.Tab 切换表格单元格
3.Shfit-Tab 切换到上一个单元格

对齐问题

不同mode对齐缩进需要单独设置
2/4空格的切换:,ti => SPC = j

email 邮箱设置

设置邮箱的发送方式:send mail

打开光标坐在路径文件open file path under cursor

SPC f l === M-x find-file-at-point

编辑

S s e: 编辑光标所在单词
选中多行,M-x -> edit line: 编辑多行

vim-command

gvim 配置环境变量

PATH:添加上D:\Program Files\Vim\vim80
在cmd/GitBash 中输入gvim [文件],在gvim中打开

  • gvim deps.html +30 打开文件之后,光标定位在30行

normal模式:

  • w 以单词为单位的移动,移动到下一个单词的首字母
  • b 以单词为单位的后退,后退到上一个单词的首字母
  • e 以单位为单位的移动,移动到下一个单词的尾字母
  • ge 以单词为单位的移动,移动到上一个单词的尾字母
  • cw 从光标到这个单词的结尾,并进入插入模式,c表示更改
  • cc 将一行替换为想替换的文本

Visual模式:

  • v—>h/l移动选择—d>删除
  • 使用标记
    • `` 以G为命令的跳转,会记得起跳的位置
    • Ctrl+o o:older 回到之前操作的地方
    • Ctrl+i 回到后来停置光标的位置
    • :jumps 列出曾经跳转位置的列表,>为最后一个跳转的位置
    • m{a-z} 可以自己定义标记的名字
    • `{a-z} 跳会自己定义标记的地方
    • V-j-j-<||> 5>> || 5<< (normal 模式下缩进) 选择多行并缩进

替换模式:

  • r 替换当前光标下的字符
  • R 进入替换模式,按Esc退出替换模式

命令行模式下(Esc):

:version 查看vim的版本
j:光标向下移动
k:光标向上移动
h:光标向右移动
l:光标向左移动
w:以单词为单位移动:
ctrl+F:向下翻一页
ctrl+B:向上翻一页
ctrl+D:向下翻半页
ctrl+U:向上翻半页
ctrl+E:增加底部行数,方便查看底部代码
zt:滚动至屏幕顶端
zb:滚动至屏幕底端
zz:滚动至屏幕中间
:w [path]:保存文件到指定路径 :w Desktop/hellowvim.html(保存到桌面文件)
G:跳转到文件尾
gg:跳转到文件头部
%:光标移动到行尾
^:光标移动到行首
[num]G:跳转到具体行数
o:在当前光标下方新增一行
O:
^/$:move to the beginning/end of line
I/A:move to the beginning/end of line and insert
D/C:delete/change until end of lline
V:高亮显示当前行
删除/复制行

  • dd 剪切一行 ,按p进行粘贴。
  • 3dd 删除当前光标所在行的以下三行,前面加个数字可以剪切n行,按p进行粘贴。
  • yy 复制一行,,按p进行粘贴。
  • 3yy 复制三行,前面加个数字可以复制n行,按p进行粘贴。
  • p 粘贴
  • v键进入选择模式,然后按h,j,k,l移动光标,选择文本,然后按y进行复制,按p进行粘贴。
  • 全部删除:按esc后,然后ggdG
  • 全部复制:按esc后,然后ggyG
    撤消与撤消的撤消
  • u 撤消
  • Ctrl+R 撤消的撤消
  • :earlier 2m 回车 回到2分钟之前的操作
    查找字符串
  • :/string #正向查找string 按n显示下一次
  • :%string #逆向查找string
  • 在当前字符串上按*,正向查找当前字符串
  • 在当前字符串上按#,反向查找当前字符串
  • :noh || set nohlsearch取消搜索高亮
    单词替换
    • yiw —->viw
      替换字符串
  • :s/keyword/replaceword: 替换光标所在行第一个出现的keyword为replaceword
    • :s/keyword/replaceword: 替换光标所在行u
      macro 宏
  • .(dot command)不是所有的上一次的操作都能重复的,宏可以录制,并重复
  • 记录一个宏

    1
    2
    3
    q{register} //开始记录一个宏
    do something //当前宏的操作
    q //退出当前宏的记录
  • 播放一个宏

    1
    @{register} //播放一个宏

    将光标定位到age = 26这一行 —->qw —->开始记录当前宏 —–>进行一系列的操作,改为 var age = 26;操作完成之后—–>q–>完成当前录制——>将光标移动到favourite行——>
    @w——>favourite这行就变成了var favourite = tea; —–>对name行执行相同的操作@w
    如果想选中三行,并播放宏。—-先将光标定位到name行—>V—->jj—->选中三行—->:normal @w—->播放宏
    w就是register

    1
    2
    3
    var name = marong,
    age = 26,
    favourite = tea;

分屏操作

  • Ctrl-w c 关闭当前屏幕
  • Ctrl-w s 上下分屏当前打开的文件
  • Ctrl-w v 左右分屏当前打开的文
  • Ctrl-w q 关闭当前窗口,如果只剩最后一个了,则退出Vim
  • ctrl-w w 光标自动在左右侧窗口切换 #!!!
  • ctrl-W K 当期那窗口上移动同时占据整个Vim程序窗口的宽度
  • ctrl-W H 向左移动窗口
  • ctrl-W J 向下移动窗口
  • ctrl-W L 向右移动窗口动
  • ctrl-w = 调整所有窗口至相同
  • ctrl-w + 增加当前窗口的高度
  • ctrl-W > 增加当前窗口的宽度
  • ctrl-W < 减小当前窗口的宽度
  • ctrl-W x 将当前窗口与下一个窗口交换
  • ctrl-w t 将光标移动到最左边的窗口
  • ctrl-w b 将光标移动到最右边的窗口
  • ctrl-w p 将光标移动到前一个(最后打开的)窗口
  • {num}ctrl-w + 将当前窗口增加num行的高度
  • :split 新增一个窗口上下分割,光标定位到上个窗口
  • :vsplit 新增一个窗口左右分割,新窗口出现在当前窗口的左边
  • :vnew 垂直分割窗口,并打开一个新的缓冲区
    折叠
    • zc 折叠某一个
    • zo 展开某一个折叠
    • zm 折叠全部
    • zR 展开全部折叠
      文字断行
  • :set wrap 所有文字断行
  • :set nowrap 所有文字强制一行
    文件搜索
  • 在打开的文件中,光标放在有文件路径的那一个行,按下gf,文件就自动跳转到路径下的文件,页面中的include引入的片段也可以,要在另一窗口中打开指定的文件的话, 可以以”ctrl-w f”
  • gd 把光标放在函数上,按下gd,跳转到函数定义的地方
    字符类相关
  • guu || vu: 把一行的文字全都变成小写
  • gUU || vU: 把一行的文字全都变成大写
  • ga: 查看光标出字符的ASCII码
  • -g8: 查看贯标出字符的utf-8码
  • x 删除当前光标下的字符
  • X 删除当前光标之前的字符
  • D 删除光标至行尾的内容
  • dw 删除当前光标至下一个word的开头
  • db 删除当前光标至前一个word的开始
  • diw 删除当前光标所在的word(不包括空白字符)
  • daw 删除当前光标所在的word(包括空白字符)
  • dG 删除当前行至文尾的内容
  • dgg 删除当前行至文头的内容
  • p/P 在光标所在行的下方/上方粘贴
  • f/F[字符] 向后移动到/向前移动到某个字符
  • [num]f/F[字符] 向后移动到/向前移动到某个字符的第几个
  • t/T[字符] 向后移动到/向前移动到某个字符的前一个字符
  • yiw:复制当前光标所在单词,p粘贴
  • yi} || yi] || yi) || yi” ||yi’:删除光标所在花括号、中括号、小括号里的内容
  • di} || di] || di) || di” ||di’:删除光标所在花括号、中括号、小括号里的内容
  • ci} || ci] || ci) || ci’ ||ci”:删除光标所在花括号、中括号、小括号里的内容,并进入insert模式
  • yt[字符]:复制当前光标直到某个字符,不包括某个字符
  • dt[字符]:删除当前光标直到某个字符,不包括某个字符
  • ct[字符]:删除当前光标直到某个字符,不包括某个字符
  • df [字符]:删除当前光标知道某个字符,比如df.(删除当前光标直到.),dfn(删除当前光标直到n)
  • c:删除,插入并替换
  • caw:删除光标所在整个单词,并进入insert模式
  • cw:删除光标所在字符到单词的结尾
  • va” 选在双引号之间的内容,并连同双引号,选中之后可以y,c,d之类操作
  • va’ 选择单引号之间的内容,并连同单引号,选中之后可以y,c,d之类操作
  • . (repeat last command,重复上一次操作)
    奖贯标定位到aa的单引号内,ci’—->YYYY-MM-DD HH:mm:ss —>normal command —–>光标一定到bb的单引号内—>.—>就和aa的操作结果是一样的
    1
    2
    3
    4
    var aa = 'YYYY-MM-DD';
    var bb = 'YYYY-MM-DD';
    var cc = 'YYYY-MM-DD';
    var dd = 'YYYY-MM-DD';

缩进

  • << :向后、向前缩进

  • = :缩进当前行,对齐缩进
  • =% :在代码的语句块重,把光标移动到{括号上,按下=%,对齐代码块中的代码
  • G=gg || gg=G :缩进整个文件(G是到文件结尾,gg是到文件开头

插入模式(i):

  • Ctrl + n:自动补齐
  • Ctrl + p: 安装上下箭头,选择要补齐的字段
  • Ctrl +x Ctrl + f Ctrl + p:自动补齐文件名
  • Ctrl + X 和 Ctrl + D 宏定义补齐
  • Ctrl + X 和 Ctrl + ] 是Tag 补齐
  • Ctrl + X 和 Ctrl + F 是文件名 补齐
  • Ctrl + X 和 Ctrl + I 也是关键词补齐,但是关键后会有个文件名,告诉你这个关键词在哪个文件中
  • Ctrl + X 和 Ctrl +V 是表达式补齐
  • Ctrl + X 和 Ctrl +L 这可以对整个行补齐
  • i:从光标当前位置开始输入文件。
  • a:从目前光标所在位置的下一个位置开始输入文字。
  • o:插入新的一行,从行首开始输入文字。
  • I:在光标所在行的行首插入。
  • A:在光标所在行的行末插入。
  • O:在光标所在的行的下面插入一行。
  • s:删除光标后的一个字符,然后进入插入模式。
  • S:删除光标所在的行,然后进入插入模式。

底行模式

  1. :Explore
    • 浏览项目目录,hj移动项目,回车选择项目或文件
    • 输入-回到上级目录
    • R:重命名文件
    • tab键可以来回切换目录
    • d 新建文件夹
  2. :ls 查看当前打开的文件
    • :e#文件编号,编辑当前选择的文件
  3. :f [filename]修改当前打开的文件名
  4. :new [filename]新建文件
  5. 文件保存和退出
    • :w 保存不退出
    • :q 退出文件
    • :wq 保存并退出
    • :q! 不保存并退出
    • :x 退出,如果文件更改则保存
    • shift + Z +Z 退出,如果文件更改则保存(按住Shift,再按两次z)
  6. 文件对比
    • 在一个窗口里输入:diffthis
    • 在另一个窗口里输入:diffthis

缓冲区

  • :ls :files :buffers 列出缓冲
  • :ball 编辑所有参数或缓冲区
  • :buffer n 移向缓冲区n
  • :bdelete 卸载缓冲区,并从缓冲区列表中删除

emmet-vim

  • html:5 生成html片段
  • ! 生成html片段

nerdtree快捷键

  • o: 打开当前文件夹或直接打开文件
  • go:打开文件,当光标留在nerdtree
  • t:在新 Tab 中打开选中文件/书签,并跳到新 Tab
  • T:在新 Tab 中打开选中文件/书签,但不跳到新 Tab
  • i:split 一个新窗口打开选中文件,并跳到该窗口
  • gi:split 一个新窗口打开选中文件,但不跳到该窗口
  • ?:来回切换帮助文档
  • 设置某个路径为当前的nerdtree,打开gvim后,输入:cd D:/frontend/modules/,回车—>cd—>选择打开当前目录—>C,设置当前目录为nerdtress
  • :NERDTree : 打开nerdtree
  • B:打开或关闭bookmark,打开bookmark,之后,双击项目
  • :Bookmark [] 创建bookmark
  • 删除标签,双击选中标签,按下D,输入y
  • r: 刷新当前光标所在的文件
  • R:刷新当前根节点
  • p: 返回父级
  • A:全屏幕或默认状态显示nerdtree
    • m—>a directory name\ 创建一个文件夹
    • m—>a filename 创建一个文件
    • m—>d 删除某个节点
    • m—>m 重命名或移动某个节点

nerd commenter

  • \cc 注释,多行注释,V–L–\cc
  • \cu 取消注释,取消多行注释,V–l–\cu

vim-multiple-cursors

  • 替换单词,光标移动到单词,Ctrl+n —>Ctrl+n—->c—>replaceword

tab 标签切换

  • gt: 标签切换到下一tab
  • gT: 标签切换到上一个tab
  • {i} gt :i是数字,到指定页,比如:5 gt 就是到第5页
  • :tabm {n}
  • :tabc: 关闭当前标签
  • :tabs: 查看所有打开的tab
  • :tabo: 关闭所有其他的 tab:
  • :tabp: 前一个tab
  • :tabn: 后一个tab
  • :tabclose [i] – 如果后面指定了数字,那就关闭指定页,如果没有就关闭当前页
  • :q : 关闭当前页
  • Ctrl + O: 向后回退你的光标移动,tab级别光标移动
  • Ctrl + I:向前追赶你的光标移动,tab级别光标移动

这两个快捷键很有用,可以在Tab页和Windows中向前和向后trace你的光标键,这也方便你跳转光标。

读取shell命令

  • :r!date 上面这个命令,:r 是:read的缩写,!是表明要运行一个shell命令,意思是我要把shell命令的输出读到vim里来。

ctrlp 插件的使用

  • :CtrlP 再输入要查找的文件名,选择文件
  • Ctrl + p 输入要查找的文件名、后缀,模糊搜索–>Ctrl+j/k上线移动选择文件 –>Ctrl+T(在tab中打开)|| Ctrl+v(垂直窗口打开) || Ctrl+x(水平窗口打开)
  • 一次打开多个文件:Ctrl+P —>Ctrl+k/j –>Ctrl+z(选中当前文件)—–>Ctrl+k/j –>Ctrl+z(选中另一个当前文件)—> Ctrl+o(打开多个文件)
  • ctrl + p ctrl + f: 在 在普通文件、Buffers或者MRU文件模式切换

EasyGrey 插件的使用

  • 按下的速度要快,不然动作会被分解,不会执行
  • :Grep targetword
  • \vv || :Grep word 搜索当前光标下的单词
  • \vV || :Grep !word 全词搜索匹配
  • \vr || :Replace /target/replacement/ || :Replace target replace 替换词
  • :ReplaceUndo 撤销替换,只能撤销上一次的替换
  • 如果想查询指定文件夹下的某个词:
    1
    2
    3
    4
    5
    6
    //进入要查询的文件夹
    :cd d:/frontend/modules
    //搜索关键词,不区分大小写,递归子文件夹查找
    :Grep foreach -ir
    //打开EasyGrep选项,输入?显示全部操作面板,e查看当前操作的文件夹
    :GrepOptions ---> ? ----> a

fugitive 插件的使用

vim 配置, _vimrc

打开配置文件的时候,自动折叠,设置 vim 相关文件打开后默认折叠方式为marker,约定俗成的用三个花括号注释包裹起来,配置文件看起来就简洁多了

1
2
3
4
5
6
7
8
" Startup {{{
filetype indent plugin on
" vim 文件折叠方式为 marker
augroup ft_vim
au!
au FileType vim setlocal foldmethod=marker
augroup END
" }}}

设置编码

1
2
3
4
5
6
7
" Lang & Encoding {{{
set fileencodings=utf-8,gbk2312,gbk,gb18030,cp936
set encoding=utf-8 //暂时删除,菜单会乱码
set langmenu=zh_CN//暂时删除,菜单会乱码
let $LANG = 'en_US.UTF-8'//暂时删除,菜单会乱码
"language messages zh_CN.UTF-8
" }}}

设置gui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
" GUI {{{
colorscheme Tomorrow-Night
source $VIMRUNTIME/delmenu.vim
source $VIMRUNTIME/menu.vim
set cursorline
set hlsearch
set number
" 窗口大小
set lines=35 columns=140
" 分割出来的窗口位于当前窗口下边/右边
set splitbelow
set splitright
"不显示工具/菜单栏
set guioptions-=T
set guioptions-=m
set guioptions-=L
set guioptions-=r
set guioptions-=b
" 使用内置 tab 样式而不是 gui
set guioptions-=e
" set nolist
set listchars=trail:·,extends:>,precedes:<
set guifont=Inconsolata:h12:cANSI
set statusline=%f
set statusline+=%m
set statusline+=\ %{fugitive#head()}
set statusline+=%=
set statusline+=%{''.(&fenc!=''?&fenc:&enc).''}
set statusline+=/
set statusline +=%{&ff} "file format
set statusline+=\ -\ " Separator
set statusline+=%l/%L
set statusline+=[%p%%]
set statusline+=\ -\ " Separator
set statusline +=%1*\ %y\ %*
" }}}

vundle安装

在windows系统中,vim编辑器配置的文件夹是vimfiles、_vimrc
在linux系统中,vim编辑器配置的文件夹是.vim, .vimrc
参考链接:http://allotory.github.io/posts/2016/02/25/vimrc_config/
git链接:https://github.com/allotory/doricha
步骤:

  1. 备份好vimfiles, _vimrc文件
  2. git clone –recursive git@github.com:allotory/doricha.git vimfiles
  3. 在_vimrc文件中添加以下代码,
    1
    2
    3
    4
    5
    6
    7
    if(has('win32') || has('win64'))
    source $VIM/vimfiles/vundle_vimrc
    source $VIM/vimfiles/basic_vimrc
    else
    source ~/.vim/vundle_vimrc
    source ~/.vim/basic_vimrc
    endif

更新插件:BundleUpdate
清除不再使用的插件:BundleClean,
列出所有插件:BundleList
查找插件:BundleSearch

安装插件步骤

  1. 安装nerdtree插件

    • 在vundle_vimrc中添加插件 Plugin

      1
      Plugin 'scrooloose/nerdtree'
    • 在gvim中输入:PluginInstall

    • 在basic_vimrc配置nerdtress

      1
      2
      autocmd vimenter * NERDTree
      let NERDTreeWinSize=25
    • 将已安装的插件以子模块的形式进行管理

      1
      2
      $ git submodule add https://github.com/scrooloose/nerdtree.git bundle/nerdtree
      ` Adding existing repo at 'bundle/nerdtree' to the index
      1
      git submodule init //每次add之后,都需要init
    • 此时我们已经完成了所有的安装配置操作,可以正常的提交到 Github。

  2. 安装vim-tomorrow-night theme

    • 在vundle_vimrc中添加插件

      1
      Plugin 'chriskempson/vim-tomorrow-theme'
    • 在gvim中输入:PluginInstall

    • 在basic_vimrc配置vim-tomorrow-night

      1
      colorscheme Tomorrow-Night
    • 将已安装的插件以子模块的形式进行管理

      1
      2
      $ git submodule add https://github.com/chriskempson/vim-tomorrow-theme.git bundle/vim-tomorrow-theme
      ` Adding existing repo at 'bundle/vim-tomorrow-theme' to the index
      1
      git submodule init //每次add之后,都需要init
    • 此时我们已经完成了所有的安装配置操作,可以正常的提交到 Github。
  3. 安装youcompleteme
    • 参照官网windows下的安装步骤
      1. 如何判断是否支持python3,命令符输入vim –version 看看里面是否有+python3,如果是-python3则表示不支持
    • 安装Vundle,具体步骤请看https://github.com/VundleVim/Vundle.vim
    • 通过Vundle安装YouCompleteMe
    • Plugin Valloric/YouCompleteMe加入vimrc
    • 启动Vim,:e PluginInstall,等待安装完成
    • 下载python3最新版本安装,ycm官方推荐python3
    • 下载安装CMake,安装过程中勾选加入path比较容易
    • 下载安装7zip,编译ycm的时候需要用
    • 下载安装Visual Studio 2015 Community版本
    • 打开VS2015,创建一个新C++ Project, 会提示你下载安装C++ tools,选择安装,此过程需要每个盘符4GB空间
    • 由于包比较大,子模块下载的时候容易出错,我后来就手动安装,需要网络好的情况下

tern_for_vim

  • ternjs/tern_for_vim
  • 需要安装 tern_for_vim 就可以获得 JavaScript 的语义补全
  • 安装完成后,在bundle/tern_for_vim里执行npm install
  • 在项目目录里,需要配置.tern-project文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
    "libs": [
    "browser",
    "jquery"
    ],
    "loadEagerly": [
    "importantfile.js"
    ],
    "plugins": {
    "requirejs": {
    "baseURL": "./",
    "paths": {}
    },
    "node":{},
    "angularjs":{}
    }
    }

git删除一个submodule

需要手动删除.gitmodules里面repo的配置

1
2
3
4
5
6
git submodule deinit <asubmodule>
git rm <asubmodule>
# Note: asubmodule (no trailing slash)
# or, if you want to leave it in your working tree
git rm --cached <asubmodule>
rm -rf .git/modules/<asubmodule>

vim配置过程中报错

  1. vundle #begin方法没有找到
    • 因为vundle没有clone到本地,在vim执行:echo globpath(&rtp, ‘autoload/vundle.vim’),查看vundle路径,什么都没有返回
    • 解决办法:git clone –recursive git@github.com:allotory/doricha.git vimfiles
  2. NERDTree使用m–>d删除某个文件夹的时候,报错,最后发现是因为用git bash命令行窗口打开的gvim,删除文件夹的命令不好用,直接打开gvim,删除文件的命令就不报错了
  3. 如果有时候执行submodule add [url] [path],在.gitmodules没有改模块的配置,No submodule mapping found in .gitmodules for path
    • 解决方法:执行git rm –cached [path]
      4.YouCompleteMe:安装过程遇到的问题
      1.执行git submodule update –init –recursive 的时候,注意是否有的子模块下载失败,需要网络好的情况下,由于模块多,文件大,需要足够很长的时间和耐心,如果执行失败,就多执行几次,或者进入子模块中执行。在安装过程中,ycmd模块中的子模块较多,而且子模块中有子模块,多执行几次。
      2.所有文件编译好之后,还是会报错:setUpPython Pythons site module could not be loaded。这是因为电脑上有python-2.7.11导致的,卸载掉python-2.7.11就好了。编译时用的python3.5.2

TODO

  1. 替换一个文件中某一行的keyword
  2. 某个项目下的keyword
  3. 快速注释
  4. 安装YouCompleteMe 代码片段snipe
  5. 多光标同时编辑

svn-commandline

svn命令行

  • svn help : 查看svn命令
  • svn add
  • svn blame
  • svn cat
  • svn changelist
  • svn checkout
  • svn commit
  • svn copy
  • svn delete
  • svn diff
  • svn export
  • svn import
  • svn info
  • svn list
  • svn lock
  • svn log
  • svn merge
  • svn mergeinfo
  • svn mkdir
  • svn move < mv, rename, ren>
  • svn patch
  • svn propdel
  • svn propedit
  • svn propget
  • svn proplist
  • svn propset
  • svn relocate
  • svn resolve
  • svn resolved
  • svn revert
  • svn status
  • svn switch
  • svn unlock
  • svn update
  • svn upgrade

    放弃本地所有修改

    1
    2
    3
    4
    5
    svn revert -R .
    svn up
    svn revert index.html //恢复某个文件
    svn revert modules/tab/tab.css modules/head/head.css ... //恢复多个文件
    svn revert module/tabs/ --depth==infinity //恢复某个文件夹

svn显示最近几条log

1
2
3
svn --limit <n> // 显示最近n条log
svn -l <n> // 显示最近n条log
svn -l 4 // 显示最近4条log

svn 提交多个文件

  • 方法一:
    • svn commit –m”log msg” mydir/dir1/file1.c mydir/dir2/myfile1.h mydir/dir3/myfile3.
  • 方法二:
    • $ svn changelist my-changelist mydir/dir1/file1.c mydir/dir2/myfile1.h
      $ svn changelist my-changelist mydir/dir3/myfile3.c etc.
      … (add all the files you want to commit together at your own rate)
    • $ svn commit -m”log msg” –changelist my-changelist

git-commandline

Git操作

  • git init : 初始化仓库
  • git log: 查看提交日子

git别名配置

1
2
3
4
5
6
7
8
9
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git unstage fileA
git reset HEAD fileA
git config --global alias.last 'log -1 HEAD'
git last

git clone远程分支

git clone 地址 
//如果发现只有一个.git目录,需要checkout了,查看branch信息
git branch -r  
//origin/andrio
//origin/iso
//origin/pc
//如果此时我们需要时andriod分支的代码,那么就要进行checkout了

git checkout origin/andriod

git 拉取远程分支到本地

git checkout origin/remoteName -b localName

git 提交远程分支

git push origin 远程分支的名字

git 新建分支

git branch test

git 切换分支

git 新建并切换到分支

git checkout -b test

git 更新某个分支

git fetch [主机名] [分支名]
git fetch origin master

git 删除某个分支

git branch -d [分支名]

git branch -d dev

git 合并分支

git merge [分支名]: git merge andrio

git 提交

git add -A || git add -all
git commit -a -m 'first commit'

git不上传空文件夹设置

1
2
//方法1
find . -type d -empty -exec touch {}/.gitignore \;

git push

1
2
git push origin <branch> //提交远程分支
git push origin :<branch> //删除远程分支

git branch

1
2
3
4
5
6
git branch // 查看所有分支
git branch -r// 查看远程分支
git branch -d <branch> //删除本地分支
git checkout -b <branch> origin/<branch>
git branch -m <newbranch name> //修改当前分支名为newbranch name
git branch -m <old branch> <new branch> //修改old-branch 为 new-branch

git pull

1
2
git pull
git pull origin <branch> //更新分支代码

git checkout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git checkout master //切换到master分支
git checkout <file> //将暂存区的文件覆盖工作区的文件
git checkout <commit> <file> //将工作目录中的替换成commit中的file文件,并加入到缓存区
git checkout <commit>
git checkout -p <branch> //对比当前分支和branch分支的区别,并选择是否将这些差异应用到当前分支上
git checkout -b <new branch name> <commit id> //根据特定的commit创建分支并合并
git checkout -b <branch> origin/<branch> //拉取全程分支到本地
//等同于下面两个命令
git checkout <commit id>
git checkout -b <new branch name>
git checkout -- . //放弃本地修改
git clean -df //删除新增文件

git merge

1
2
3
4
5
//将master分支合并到feature分支最简单的办法就是用下面这些命令:
git checkout feature
git merge master
//或者,你也可以把它们压缩在一行里。
git merge master feature

git rebase

1
2
3
4
5
git checkout new-feature
git rebase -i master
git checkout master
git merge new-feature

git revert

撤销

git reset

重做

1
git reset --hard <commit id> //reset到指定的commit

git log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git log andrio //查看某个分支的log
git log <branch> -- //查看分支log
git log -- <directory> //查看directory文件夹的log
git log master..test //查看test分支的提交记录但不包含master分支记录
git log test..master //查看master分支记录但不包含test分支记录
git log test...master //查看test和master分支记录
git log test --not master //屏蔽master分支log
git log <path> // 查看某些文件或目录的历史提交
git log -p //查看每次内容提交的差异
git log -p [filename] //查看文件的log
git log -p -2 //查看最近两次提交的内容差异
git log -stat -1 //查看最近一次提交的文件增删数量
git log --author marong02 // 查看提价者的日志
git log --grep=test //通过提交说明信息包含test字段过滤提交日志
git log --pretty=oneline // 将提交日志压缩到一行

git stash

1
2
3
4
git stash
git stash list
git stash apply 重新应用刚刚的存储
git stash apply stash@{2} 重新应用更早的存储

git clean

从工作目录中移除未跟踪的文件

1
git clean -df //删除未跟踪的文件,但不删除.gitignore中忽略的文件

git diff -w [filename]: 查看文件的合并情况

如果希望保留生产服务器上所做的改动,仅仅并入新配置项, 处理方法如下:

git stash
git pull
git stash pop

如果希望用代码库中的文件完全覆盖本地工作版本. 方法如下:

git reset –hard
git pull

#Setting your branch to exactly match the remote branch can be done in two steps:
git fetch origin
git reset –hard origin/master

git放弃某个文件的修改

git checkout

合并多个commmit为一个,不好看

git rebase -i 允许合并除了root commit的多条提交日志
git rebase -i –root 允许合并包含root commit的多条提交日志
在提交PR之前,用rebase合并提交,PR提交后,任何来自其他开发者的更改要有merge,
如果用base会造成Git和其他开发者难以找到这个分支接下来的任何提交

1
2
3
4
5
6
7
8
9
10
11
12
//方案一
git rebase -i <commit id> //要合并的版本之前的版本号,输入的版本不参与合并
//在交互式窗口中,将第二行开始的pick,手动改成s或f,改完后wq保存退出
//如果出现冲突,需要解决,解决完成后,git add . => git rebase --continue
//成功后,如果想放弃本次修改,可以git rebase --abort
git push --force origin master //提交到远程分支
//方案二
git rebase -i @~4
修改下面的三个中开头的pick为f,然后保存,合并多个commit为一个
git push origin --delete feature/FECHI-4927 //删除分支
git push origin feature/FECHI-4927 //重新提交分支

git提PR步骤

合并好commit之后
进入branch –> create pull request –>
输入reviewers,在控制栏里执行

1
document.querySelector('.select2-offscreen').value="duzhaoyang|!|zhengwenbing|!|zhangcongjie|!|chenwenbin03|!|huxiaoyue|!|wangzengdi|!|fuhongjuan|!|chengang07|!|wangyue08|!|chenzeqing|!|zhangchunxiao|!|chenshenqian|!|shiwenzhe"

–> Create

git 合并master

git merge origin/master //merge 不需要重新提PR git pull –rebase origin master //rebase 需要重新提 PR

git cat-file

git stash

git reflog 能召回git reset的操作,,如果垃圾回收了git reflog,就永远找不回了

多人在一个分支开发

1
2
git pull
git rebase

git grep “” 搜素内容

git commit –amend –date=now 修改日期

正则表达式

  • String.prototype.match()
  • String.prototype.replace()

  • RegExp.prototype.exec()

  • RegExp.prototype.test()

元字符

字符 含义
^ 匹配字符串的开头,多行检索中,匹配一行的开头
$ 匹配字符串的结尾,多行检索中,匹配一行的结尾
\b 匹配一个单词的边界
\B 匹配而非单词的边界
(?=p) 零宽正向先行断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符
(?!p) 零宽负向先行断言,要求接下来的字符不与p匹配
(?<=p) javscript不支持
(?<!p) javascript不支持
(?>p) 固化分组, javascript不支持

量词

字符 含义
? 0次货1次
+ 一次或多次
* 0次或多次
{n} n次
{n,} n次或更多次
{n, m} 次数在n,m之间,至少n次,但不能超过m次

.

. 不能匹配回车和换行\n, \r

匹配包含p, 但不包含h的所有单词

1
2
3
var web_development = "python php ruby javascript jsonp perhapsphpisoutdated";
var reg = /\bp(?!h)\w*\b/g;
web_development.match(reg); //["python", "perhapsphpisoutdated"]

零宽断言

js 中,对于四种零宽断言,只支持 零宽度正预测先行断言 和 零宽度负预测先行断言 这两种。

js 中,正则表达式后面可以跟三个 flag,比如 /something/igm。

他们的意义分别是,

i 的意义是不区分大小写
g 的意义是,匹配多个
m 的意义是,是 ^ 和 $ 可以匹配每一行的开头。

javascript不支持“零宽度正回顾后发断言”

(?: 子表达式)

  • 定义非捕获组,只分组不捕获
    1
    2
    3
    4
    5
    6
    7
    8
    Write(?:Line)
    var reg = /Write(?:Line)/;
    var dd = 'Console.WriteLine()';
    dd.match(reg) //['WriteLine']
    var reg = /Write(Line)/;
    var dd = 'Console.WriteLine()';
    dd.match(reg) //['WriteLine', 'Line'];

(?= 子表达式)

  • 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,\w+(?=\d) 与后跟数字的单词匹配,而不与该数字匹配
    1
    2
    3
    var ss = /^\W*(?=空气质量)|[^>]*(?=<\/b>)/g;
    var dd = '北京空气质量:<b>90</b>';
    var rr = dd.match(ss); //["北京", "90", ""]

(?<= 子表达式)

  • (零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。
    1
    2
    3
    4
    var str = 'Homes Tea is good, hometown is beautiful';
    var reg = /(?<=\bhome)(?=s\b)/gi;
    var reg = /(?=s\b)(?<=\bhome)/gi;//位置变化没有影响
    str.replace(reg, "'");

格式化数字

1
2
3
4
5
6
7
8
9
10
11
var num = '1234876';
var reg = /(?<=\d)(?=(\d\d\d)+$)/gi;
num.replace(reg, ',');
var num = 'hello, here is 1234876 str';
var reg = /(?<=\d)(?=(\d\d\d)+\b)/gi;
num.replace(reg, ',');
var num = 'hello, here is 1234876kg str';
var reg = /(?<=\d)(?=(\d\d\d)+(?!\d))/gi;
num.replace(reg, ',');

匹配空格间隔的字符串

1
2
3
4
5
6
7
8
9
10
11
12
var cls="item current";
var reg = /(?<=\s*)([^\s]+)(?=\s*)/;
cls.match(reg);
```
1.[...]字符组
一个字符组只能匹配目标文本的单个字符,在字符组内部,只有连字符才是元字符(如果连字符出现在字符组的开头,只表示一个普通的字符),?.等元字符在字符组内部就是一个普通字符, \在字符组内需要转义。
```javascript
//真正的特殊字符只有两个连字符
var reg = /[1-9A-Z_!.?]/;

  1. | 或,子表达式称为多选分支
    1
    2
    3
    4
    //匹配gray,grey
    var reg = /gray|grey/;
    var reg = /gr(a|e)y/;
    var reg = /gr[ae]y/;

3.反向引用
匹配与表达式先前部分匹配的同样的文本。在一个表达式中,可以使用多个括号,\1,\2,\3等用来表示第一,第二,第三组括号匹配的文本。括号是按照开括号(从左到右的出现顺序进行的。

1
2
3
4
//匹配字符串中重复出现的单词
var ss = 'hello world world;
var reg = /\b([A-Za-z]+) +\1\b/;
reg.exec(ss)

例子

  • 匹配引号内的字符串

    1
    2
    3
    var ss = 'Some on said "Programing is fun"';
    var reg = /"[^"]*"/;
    var result = reg.exec(ss);
  • 匹配时间

    1
    2
    3
    4
    //9:17 am 12:30pm
    var reg = /(0?[1-9]|1[012]):[0-5][0-9] *(a|m)p/;
    //09:59 24小时制
    var reg = /([01]?[0-9]|2[0-3]):[0-5][0-9]/;

正则表达式的匹配原则

  • 量词? * + {min, max}都是贪婪匹配的,尽可能多的匹配字符,直到达到匹配上限为止。
  • 量词?? *? +? {min, max}?都是懒惰匹配的,尽可能少的匹配字符,只需满足匹配下限。
  • 占有优先量词: ?+ *+ ++ {min, max}+都是贪婪匹配的,在匹配的过程中不会交还任何匹配到的字符(javascript不支持)。
  • 先来先服务原则
    先满足先来的量词的贪婪匹配,后来的量词满足最基本的匹配.
    .会贪婪匹配整个字符串,只有在全局匹配需要的情况下,才会被迫交出一些字符
    为了满足\d{4}的匹配,强迫.\
    从右到左交还字符(保证.*匹配的最基本的条件),直到满足匹配,下面能匹配2017.
    1
    2
    3
    4
    var str = "this year is 2017";
    var reg = /^.*+(\d{4})/;
    var result = reg.exec(str);
    result[1] //2017

下面的结果只能匹配到7,根据先来先服务的原则,满足\d+最基本的匹配一次

1
2
3
4
var str = "this year is 2017";
var reg = /^.*(\d+)/;
var result = reg.exec(str);
result[1] //7

下面的结果什么都匹配不到,根据先来先服务的原则,满足\d*最基本的匹配0次

1
2
3
4
5
var str = "this year is 2017";
var reg = /^.*(\d*)/;
reg.exec(str);
var result = reg.exec(str);
result[1] //''

排除类和排除环视

1
2
3
var str = 'Tom said "hello what\'s your name?", Jim answered:"hello, my name is Jim"';
var reg = /"(.*)"/;
reg.exec(str); //"hello what\'s your name?", Jim answered:"hello, my name is Jim"
  • 采用排除法
    如果匹配一个字符用排除法

    1
    2
    3
    var str = 'Tom said "hello what\'s your name?", Jim answered:"hello, my name is Jim"';
    var reg = /"([^"]*)"/;
    reg.exec(str); //"hello what\'s your name?"
  • 采用.*?,忽略优先量词的匹配

    1
    2
    3
    var str = 'Tom said "hello what\'s your name?", Jim answered:"hello, my name is Jim"';
    var reg = /"(.*?)"/;
    reg.exec(str); //"hello what\'s your name?"
  • 采用排除环视,来匹配某个字符串
    匹配出之间的内容,排除法,忽略匹配优先的量词的方法都不能满足;
    排除法针对的单个字符,而不是有着特定顺序的字符串
    忽略匹配优先的量词的方法,对于..这种情况匹配不正确;

    1
    2
    3
    var str = "<b>hello <em>marong></em></b> <b>wow</b>";
    var reg = /<b>((?!</?b>).)*<\/b>/;
    reg.exec(ss);

固化分组(?>)和占有优先的量词(?+,*+, {min, max}+)

固化分组和占有优先的量词能提高效率
固化分组匹配成功后,会抛弃固化分组里的备用状态

1
2
3
4
5
//perl
$str = "hello";
$str =~ /(\w+):/;//\w+匹配整个字符串后,不断后退回溯,来匹配:,直到匹配到e,表达式宣告失败,效率低
$str =~ /(?>\w+):/; //固化分组
$str =~ /\w++:/; //占有优先

1
2
3
4
5
6
7
//perl
$num = 27.624300009;
# $num =~ s/(\.\d\d[1-9]?)\d*/$1/; //27.624, 对于正好三位数27.625也会进行匹配,效率不高
# $num =~ s/(\.\d\d[1-9]?)\d+/$1/; //27.62, 对于27.625不能正确匹配
$num =~ s/(\.\d\d(?>[1-9]?))\d+/$1/;//27.624, 固化分组,匹配成功后,不会再释放
$num =~ s/(\.\d\d([1-9]?+))\d+/$1/;//27.624, 占有优先量词,匹配成功后,不会再释放
print "\$num: $num .\n";

javascript不支持,固化分组和占有优先的量词,但可以通过环视模拟来实现固化分组
对于环视,只会匹配成功或失败,不会保留备用状态,跟固化分组的行为一直,只是环视虽然能捕获,但不是全局匹配的一部分。

1
2
3
4
var str ="hello";
//var reg = /(?>\w+)/;//如果javascript支持固化分组
var reg = /(?=(\w+))\1/; //模拟捕获
reg.exec(str);

1
2
3
4
var num = 23.624;
var reg = /(\.\d\d[1-9]?)\d*/;
var reg = /(\.\d\d(?=[1-9])\2)\d*/;
reg.exec(num);

多选结构|

需要仔细安排多选结构的顺序

1
2
3
4
$str = "Jan 31 is Dad's birthday";
# $str =~ /Jan (0?[0-9]|[12][0-9]|3[01])/; //Jan 3,第一个表达式能匹配到3
$str =~ /Jan (3[01]|[12][0-9]|0?[0-9])/; //Jan 31,调整多选的顺序
print "\$str: $&";

正则实例

  • 匹配路径和文件名

    1
    2
    3
    4
    5
    //javascript
    var path = '/Users/marong/test/index.js';
    var path = '/Users//marong/test///index.js';
    var reg = /(.*\/)?(.*)/;
    reg.exec(path);
  • 匹配一个浮点数
    下面这个正则表达式能匹配到任何数字或字符串,

    1
    2
    3
    4
    5
    6
    var num = 2.12;
    var reg = /-?[0-9]*\.?[0-9]*/; //能匹配到任何数组,因为表达式的每一项都不是必须的,能匹配到任何字符串开头的空字符
    var reg = /-?([0-9]+(\.[0-9]+)?|\.[0-9]+)/; //能匹配到任何数
    var reg = /^-?[0-9]*\.?[0-9]*$/; //能匹配到任何数字和空字符串
    var reg = /^-?([0-9]+(\.[0-9]+)?|\.[0-9]+)$/; //能匹配到任何数
    reg.test(num);
  • 匹配双引号之间的内容

    1
    2
    3
    var str = 'Dash Symbol: "/-|-\\" or "[*_*]"';
    var reg = /"(\\.|[^"])*"/;
    reg.exec(str);
  • 去掉首尾的字符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    var str = " hello world, I will test some regexp speed, check the regexp speed ";
    var regStart = /^\s+/;
    var regEnd = /\s+$/;
    var result;
    var start = new Date();
    var count = 1000000;
    for(var i = 0;i < count; i++){
    result = str.replace(regStart,'').replace(regEnd, '');
    }
    console.log('normal time', +new Date - +start, 'result', result);//317
    var reg = /^\s+(.*?)\s+$/;
    start = new Date();
    for(var i = 0;i < count; i++){
    result = str.replace(reg,'$1');
    }
    console.log('? time', +new Date - +start, 'result', result);// 314
    var reg = /^\s+|\s+$/g;
    start = new Date();
    for(var i = 0;i < count; i++){
    result = str.replace(reg,'');
    }
    console.log('| time', +new Date - +start, 'result', result);//309
    var reg = /^\s+((?:.*\S)?)\s+$/;
    start = new Date();
    for(var i = 0;i < count; i++){
    result = str.replace(reg,'$1');
    }
    console.log('\\S time', +new Date - +start, 'result', result);//260
  • 匹配HTML标签

    1
    2
    3
    4
    5
    var str = '<span data-sp=">" class="item" title=""></span>';
    var reg = /<[^>]*>/g;//不能匹配标签里含有>的情况
    var reg = /<("[^"]"|'[^']'|[^'">])*>/g;//能匹配标签里含有>的情况
    var result = str.match(reg);
    console.log(result); //result [ '<span data-sp=">" class="item" title="">', '</span>' ]
  • 匹配HTML Link

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var str = '<a class="navitem" data-sp=">" data-name=test href="http://marong.me/blog" title="marong blog" >hello marong</a>';
    // var regA = /<a\b([^>]*)>(.*?)<\/a>/;
    var regA = /<a\b(('[^']*'|"[^"]*"|[^'">])*)>(.*?)<\/a>/;
    var aResult = str.match(regA);
    var regHref = /href\s*=\s*('[^']*'|"[^"]*"|[^'">\s]*/;
    var hrefResult = aResult && aResult[1] && aResult[1].match(regHref);;
    console.log('aResult',aResult);;
    console.log('hrefResult',hrefResult);
    console.log('href=',hrefResult[1], ' value=',aResult[3]);
    //href = "http://marong.me/blog" value = hello marong

优化正则表达式

  • 字符组比多选结构的效率高
    多选结构可能需要回溯,捕获型的括号处理也需要时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var timesTodo = 100000;
    var testStr = 'ababadedfg';
    for(var i = 0; i < 10000; i++){
    testStr += testStr;
    }
    var count = timesTodo;
    var start = new Date();
    while(count--){
    /^(a|b|c|d|e|f|g)$/.test(testStr);
    }
    console.log('alternation takes seconds.\n', new Date - start);//107
    var count = timesTodo;
    var start = new Date();
    while(count--){
    /^[a-g]$/.test(testStr);
    }
    console.log('charactor class takes seconds.\n', new Date - start);//12
  • 使用非捕获型括号(?:)
    如果不需要引用括号内的文本,节省捕获的时间,坚守回溯使用的状态的数量。

  • 使用起始锚点
    如果要匹配某个字符串的开头位置字符,添加起始锚点,能提高匹配的速度。
  • 将文字文本独立出来,提高引擎识别的可能性
  • 从量词中提取必须的元素

    1
    2
    /x+/ ---> /xx*/
    /-{5,7}/ ---> /-----{0, 2}/
  • 提取多选结构开头必须的元素

    1
    /(?:that|this)/ ---> /th(?:at|is)/
  • 匹配优先,忽略匹配优先,排除性字符组
    /.:/
    匹配优先,匹配到最后一个冒号,如果分号比较接近结尾,速度更快
    /.
    ?:/
    忽略优先匹配,匹配到第一个冒号,如果分号比较接近开头,速度更快
    /[^:]*:/
    匹配到第一冒号,一般比忽略匹配优先效率要高