JavaScript数组方法学习笔记

数组

在ECMAScript中数组是非常常用的引用类型了。ECMAScript所定义的数组和其他语言中的数组有着很大的区别。那么首先要说的就是数组也是一种对象。

特点:

  • 在JS中,”数组”即一组数据的集合长度可变,元素类型也可以不同!
  • 数组长度随时可变!随时可以修改!(length属性)

一般来说我们定义数组的方法很简单,直接使用[]即可,当然也可以使用new Array()这样的构造函数的形式,不过并不常用。对于数组本身本没有太多可以说的东西,还是直接来过一遍它的方法

Array实例的方法

  1. push:向数组尾部添加元素(可多个)

    1
    2
    3
    4
    var arr =[];
    var result = arr.push(1,true);
    // arr =[1,true]
    // result=2;push方法返回值为新数组的长度
  2. pop:从数组尾部移除一个元素

    1
    2
    3
    4
    var arr =[1,true];
    var result = arr.pop();
    // arr=[1]
    // result = true;pop方法返回值为被移除的元素
  3. shift:从数组头部删除一个元素

    1
    2
    3
    4
    var arr = [1,2,true,new Date()];
    var result = arr.shift();//
    // arr =[2, true, Fri May 20 2016 00:50:33 GMT+0800 (中国标准时间)]
    // result = 1;shift方法返回值为被移除的元素
  4. unshift:向数组头部添加元素(可多个)

    1
    2
    3
    4
    var arr = [];
    var result = arr.unshift(1,2,false);
    // arr = [1, 2, false]
    // result = 3 ; unshift方法返回新数组的长度
  5. splice:数组截取/插入的方法(操作数组本身)

    1. 第一个参数:截取的起始位置
    2. 第二个参数:表示截取的个数
    3. 第三个参数以后:从截取处插入新的元素(可多个)
      1
      2
      3
      4
      var arr = [1,2,3,4,5];
      var result = arr.splice(1,2,3,4,5);
      // arr = [1, 3, 4, 5, 4, 5];splice方法操作直接操作数组本身
      // result = [2,3];splice方法返回被截取的元素
  6. slice:数组截取(不操作数组本身)

    1
    2
    3
    4
    var arr = [1,2,3,4,5];
    var result = arr.slice(2,4);
    // arr = [1,2,3,4,5];不操作数组本身
    // result = [3,4];slice方法返回被截取的元素
  7. concat:链接两个数组的方法(不操作数组本身)

    1
    2
    3
    4
    5
    6
    var arr1 = [1,2,3];
    var arr2 = [true,5];
    var result = arr1.concat(arr2);
    // arr1 = [1, 2, 3];
    // arr2 = [true, 5];
    // result = [1, 2, 3, true, 5];concat方法返回链接后的新数组
  8. join:在每个元素之间加入内容(不操作数组本身)

    1
    2
    3
    4
    var arr = [1,2,3,4,5];
    var result = arr.join("-");
    // arr = [1, 2, 3, 4, 5];
    // result = "1-2-3-4-5"; join方法返回每个元素加入内容后的字符串
  9. sort:排序(直接操作数组本身)

    • sort方法默认按照字符串形式的utf编码进行排序,需要确切的正序或倒序排序时,需要写入自己的函数
      1
      2
      3
      4
      5
      6
      7
      8
      var arr = [1,345,23,22,10,9];
      var result1 = arr.sort();
      // arr = [1, 10, 22, 23, 345, 9]
      // result1 = [1, 10, 22, 23, 345, 9]sort方法返回排序后的数组
      var result2 = arr.sort(function(a,b){
      return a-b;
      });
      // result2 = [1, 9, 10, 22, 23, 345];正确的从小到大排列
  10. reverse:数组反转(直接操作数组本身)

    1
    2
    3
    4
    var arr = [12,324,543,10,9];
    var result = arr.reverse();
    // arr = [9, 10, 543, 324, 12];
    // result = [9, 10, 543, 324, 12]; //返回反转后的数组
  11. indexOf:正序查找元素的索引(0开始)

    • 只有一个参数时,表示待查找的元素
    • 两个参数时,第一个参数表示表示待查找的元素,
      第二个参数从第几个元素开始查
      1
      2
      3
      4
      5
      6
      7
      var arr = [1,2,3,4,5,4,3,2,1];
      var result1 = arr.indexOf(4)
      var result2 = arr.indexOf(4,4)
      // arr = [1,2,3,4,5,4,3,2,1];
      // result1 = 3;返回值为查找到的元素索引
      // result2 = 5
      // 没有返回-1
  12. lastIndexOf:倒序版本:倒序查找元素的索引(最后一个元素开始)

    • 只有一个参数时,表示待查找的元素
    • 两个参数时,第一个参数表示待查找的元素,第二个参数表示从倒数第几个元素开始查
      1
      2
      3
      4
      5
      6
      7
      var arr = [1,2,3,4,5,4,3,2,1];
      var result1 = arr.lastIndexOf(4)
      var result2 = arr.lastIndexOf(4,4)
      // arr = [1,2,3,4,5,4,3,2,1];
      // result1 = 5;返回值为查找到的元素索引
      // result2 = 3
      // 没有返回-1
  13. forEach:循环数组的每一个元素,并且对每一项执行一个方法。没有返回值(不操作数组本身)

    1
    2
    3
    4
    5
    6
    var arr = [1,2,3,4,5];
    var result = arr.forEach(function(item,index,arr){
    return item+1;
    })
    // arr = [1, 2, 3, 4, 5];
    // result = undefined;forEach方法没有返回值
  14. map:循环数组的每一个元素,并且对每一项执行一个方法,执行完毕后,新的数组返回(不操作数组本身)

    1
    2
    3
    4
    5
    6
    var arr = [1,2,3,4,5];
    var result = arr.map(function(item,index,arr){
    return item+1;
    })
    // arr = [1, 2, 3, 4, 5];
    // result = [2, 3, 4, 5, 6];map方法返回运行函数后的新数组
  15. filter:对于数组的每一个元素,进行一个函数的运行,给定条件去执行,返回过滤后的结果(不操作数组本身)

    1
    2
    3
    4
    5
    6
    var arr = [1,2,3,4,5];
    var result = arr.filter(function(item,index,arr){
    return item>2;
    })
    // arr = [1,2,3,4,5];
    // result = [3,4,5];返回满足条件的值
  16. every:对于数组的每一个元素都执行一个函数,如果都返回true,最后则返回true;如果有一个返回false,最后结果返回false.(不操作数组本身)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var arr = [1,2,3,4,5];
    var result1 = arr.every(function fun(item,index,arr){
    return item > 2;
    });
    var result2 = arr.every(function fun(item,index,arr){
    return item > 0;
    });
    // arr = [1,2,3,4,5];
    // result1 = false;不全部满足条件
    // result2 = true;全部满足条件
  17. some:对于数组的每一个元素都执行一个函数,如果有一项返回true,最后则返回true;如果每一项返回false,最后结果才返回false.(不操作数组本身)(与上一个every相反)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var arr = [1,2,3,4,5];
    var result1 = arr.some(function fun(item,index,arr){
    return item>=5;
    });
    var result2 = arr.some(function fun(item,index,arr){
    return item<0;
    });
    // arr = [1,2,3,4,5];
    // result1 = true;不全部满足条件
    // result2 = false;每一项都返回false
  18. reduce:最终构建一个返回值,接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始合并,最终为一个值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var arr = [1,2,3,4,5];
    /**
    * perv:前一个值
    * cur:当前值
    * index:当前索引位置
    * arr:原数组
    **/
    var result = arr.reduce(function(perv,cur,index,arr){
    return perv +cur;
    });
    // arr = [1,2,3,4,5];
    // result = 15;
  19. reduceRight:倒序版本:最终构建一个返回值,
    遍历的起始位置不同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var arr = [1,2,3,4,5];
    /**
    * perv:前一个值
    * cur:当前值
    * index:当前索引位置
    * arr:原数组
    **/
    var result = arr.reduceRight(function(perv,cur,index,arr){
    return perv +cur;
    });
    // arr = [1,2,3,4,5];
    // result = 15;

JavaScript作用域学习笔记

无论什么语言中,作用域都是一个十分重要的概念,在JavaScript中也不例外,作用域定义了变量或者函数有权访问的范围,决定了它们各自的行为。要理解JavaScript中的作用域首先就要知道:在let出现之前,JS中变量的作用域只有两种:全局作用域和局部作用域。(本文也只讨论这两种作用域)

全局作用域

全局作用域是最外围的一个执行环境,可以在代码的任何地方访问到。在浏览器中,我们的全局作用域就是window。因此在浏览器中,所有的全局变量和函数都是作为window对象的属性和方法创建的。

下面就来看看全局作用域的创建方式:

  1. 全局变量与全局函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var name = "小红";
function doSomething(){
var anotherName = "小黑";
function showName(){
console.info(name)
console.info(anotherName)
}
showName();
}

console.info(name);//小红
console.info(anotherName);//【脚本出错】
doSomething();//小红---小黑
showName();//【脚本出错】

通过代码可以很清楚的看出来,我在前面所说的 作用域定义了变量或者函数有权访问的范围 ,在这里我们定义了一个全局的变量name与全局函数doSomething(),他可以在任何地方被直接访问。但是我们又在函数内部创建了变量anotherName与函数showName(),通过代码中的调用情况可以发现,我们在外部调用它时提示【脚本出错】,因为他们处于局部作用域内(稍后讲),而 外部环境不能访问内部环境的任何变量与函数。这就涉及到了作用域的概念(稍后讲)

  1. 未声明直接定义的变量
1
2
3
4
5
6
7
8
function showName() {
var fullName = "小红";
anotherName = "小黑";
console.info(fullName)
}
showName();//小红
console.info(anotherName);//小黑
console.info(fullName);//【脚本出错】

在这样的情况下,变量anotherName拥有全局作用域,而fullName在函数外部无法访问到。(注:在高程中明确说明,不声明而直接初始化变量是错误做法,应该避免这样的情况严格模式下,初始化未声明的变量将报错

  1. 所有window对象上的属性都具有全局作用

这个实际上在上面已经提到了:所有的全局变量和函数都是作为window对象的属性和方法创建的。,自然window对象它本身所具有的属性和方法,同样是处于全局作用域,例如:window.locationwindow.name等等。

局部作用域

其实在上面的代码中,为了展示全局作用域的效果,我们就已经创造了局部作用域。局部作用域和全局作用域正好相反,局部作用域一般只在固定的代码片段内可访问到,最常见的就是函数内部,所以在很多地方就会有人把它称为函数作用域。(记住let之前无块级作用域)。我们再来看一下第一段代码:

1
2
3
4
5
6
7
8
9
10
11
var name = "小红";
function doSomething(){
var anotherName = "小黑";
function showName(){
console.info(name)
console.info(anotherName)
}
showName();
}
console.info(anotherName);//【脚本出错】
showName();//【脚本出错】

在这段代码中变量 anotherName,与函数 showName(),都拥有局部作用域。因此它不能被外部所访问,那么问题就来了,为什么全局变量他就能在局部作用域内被访问到呢?这就是 JavaScript 中的作用域链概念!

作用域链

在JS中:”一切皆是对象, 函数也是”。

在 JavaScript 中,每个函数都有着自己的作用域,在每次调用一个函数的时候 ,就会进入一个函数内的作用域,而当函数执行返回以后,就返回调用前的作用域。

当代码在一个作用域内执行时,就会根据其上下文创建一个作用域链,该作用域链的用途就是控制当前作用域对于内所有的变量与函数的有序访问。作用域链的最前端,始终都是当前执行代码所在的作用域的变量对象。

1
2
3
4
5
6
7
8
9
10
11
var name = "小红";

function changeName(){
if(name ==="小红"){
name="小黑";
}else{
name ="小红";
}
}
changeName();
console.info("新名字:"+name);//小黑

在这个例子中,changName()被定义在全局作用域下,他的作用域链包含着包含两个对象:1.它本身的变量对象(函数都会包含arguments对象),2.全局环境对象。之所以能在函数内部访问到变量name,就是因为在它的作用域中,能找到它。(JS的标识符解析,是沿着作用域链一级一级的查找搜索的过程,从作用域链的最前端开始直到全局环境,最终没有查找到时将报错。)我们再回过头来稍微改一下第一段代码:并且看看他们能访问到那些变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var name = "小红";
function doSomething(){
var anotherName = "小黑";
function showName(){
var author ="三省吾身丶丶";
console.info(name)
console.info(anotherName)
// 在这里可以访问到 name 、anotherName 、author
}
showName();
// 在这里可以访问到 name anotherName ,不能访问到 author
}
doSomething();
// 在这里只能访问到 name

要想理解这种作用域其实也很简单,作用域就像是一架 每一个台阶都是相对封闭(同级),并且只能上不能下的梯子,在越底层的台阶上,它能走的步数越多(作用域链越长)。为了找到它想要的东西,就开始爬台阶,每爬一步台阶,都能看到这一级台阶上有什么东西,直到最顶上的那一阶。(找到了就带回去一起玩耍,玩完了之后还得换回去,要是最后都没找到就掉下去摔死了)

坑与示例解析

在了解坑之前,其实只要记住权威指南里面的一句话,就可以躲过很多这方面的坑了,那就是:JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

下面就来看看这一个例子:

1
2
3
4
5
6
7
8
9
10
11
var name = '小红';
function showName() {
console.info(name);
}

function show() {
var name = '小黑';
showName();
}

show();

结果会是什么呢?

1
小红

如果你记住并且理解了上面的话,那么应该可以得到这个结果。用作用域链的角度解析:执行show()函数时,进入function show(){}的作用域内,然后执行showName()函数,再进入到function showName(){}的作用域内,要输出name,就在当前作用域找,但是找不到,然后就向上爬一层,在全局环境中找到了var name = '小红';,所以show()就输出了小红。

再来看一个这个例子的改动版本

1
2
3
4
5
6
7
8
9
10
11
var name = '小红';

function show() {
var name = '小黑';

function showName() {
console.info(name);
}
showName();
}
show();

结果是:小黑

解析:执行show()函数时,进入function show(){}的作用域内,然后执行showName()函数,再进入到function showName(){}的作用域内,要输出name,就在当前作用域找,发现本身找不到,就向上爬一层到了show()里面,发现已经找到了var name = '小黑';,那么就停止查找,输出了小黑

先到这,不知道有没有对作用域有了更多的了解呢?感觉有些地方还了解的不够透彻,希望在开发项目的过程中能有更深的理解。

如果有错误之处,请指正。谢谢!

思想汇报

闲来无事,翻了翻朋友圈,看到自己年初定下的几个小目标,突然想到原来王自如的思想汇报,我觉得自己也应该要反思一下自己,正好写下来做一个沉淀。

时间真快啊,到写博客的时候才发现上一篇博客还是三月份在广州实习的时候写的,到今天已经有96天了。沉默了那么长一段时间,也并没有憋什么大招,不是太满意,现在重新开始写,最低产量一个星期一篇吧,不过什么都可以写,都是自己心境的一个记录。

正好2016年已经过去一半了,那就先从年初定下的目标开始说起吧

说说目标

还记得当时是15年最后一天,本来东拼西凑写了千把字的总结,因为太散就只留下了下面这几个目标:

  1. 前端实习,答辩,工作。
  2. 前端技术与编程思想的学习。
  3. 继续消除自卑心理,提升自信,积极面对。
  4. 多看书,先定个最低目标吧,30本。
  5. 写作表达,开口说话,冷场说话,脸皮薄也要说话,挨打也要说话。
  6. 尽心尽力把每一件事情最好,但求问心无愧。

半年时间,对自己不够满意,但也算是完成了一些吧,如下:

  • 结束广州的前端实习,到重庆的毕业答辩,再到现在杭州的前端工作,辗转几个地方,第一个目标算是完成了。
  • 书这方面,无营养的网文看了不少,正儿八经的书就有点惨不忍睹了,看完的有:《大学之路(上、下册)》、《Web全栈开发师的自我修养》、《自卑与超越》、《慕课革命》。确实完成的不像话,后面这半年得加油了,书买回来就要看,自己定下的目标还是要去完成
  • 其他的几个目标其实说的比较泛泛,不是太好评价。
    • 前端技术和编程思想方面,其实个人觉得进步不多,不过在和老友一起做一个外包项目之后,明白以及确定以解决问题为完成工作的第一要务,而后再去考虑其他的种种因素,这也是一个进步吧!
    • 关于自卑和说话,现在已经好很多了,在分享与提出质疑中继续加油吧!大不了就是挨打不是么?

尽心尽力把每一件事情最好,但求问心无愧。,这个继续加油吧!

不知不觉中学习前端到这个月已经整整一年时间,算是正式跨入了互联网的行当里面。

其实第一次接触编程是大一,当时买了本C语言的书,对着教程就开始学,然后…就从入门到放弃了,直到去年再次想学习编程,接触到前端,因为易入门、浏览器的直观显示(至今我还更喜欢调界面,为了1px耗上个半天,毕竟处女座嘛)、再到了解到响应式网页开发的强大,才真正的喜欢上了编程。前路漫漫,继续加油!

思考和感悟

  1. 其实每次写博客都希望自己写的很好,写一些别人没有写过的东西出来,但是现在还没达到那样的水平,然后一味的想,导致根本没有产出。其实产出可以是对自己的,你把别人的东西学会、用出来,把过程记录下来,然后学到的东西不就是自己的了么?所以现在改变策略了,事无巨细,我更多的可能会把博客当成笔记来使用,只是把记的笔记使用讲述的方式来帮助我理解。

  2. 这半年看书确实看的有点少了,当初刚学前端的时候,学校安排到医院实习,除去正常的医院工作外,在办公室就是看书了。哦,不对,抄书。css一个一个属性抄过去,js秘密花园抄了一遍,结果后来开始找实习之后就没有这样了,看博客、敲代码。做了很多重复的事情,现在要改变策略了,又在知乎上看到CSS魔法在半年前对我说的:疯狂啃书,野蛮生长!,这半年没这样做到,就像乔老爷子说的那句话 Stay Hungry. Stay Foolish.,保持这种饥饿感,保持一种初学者的心态, 那么下半年就要加油了。

  3. 我学习有一个很大的问题想得太多,做的太少,可能真的是我一个特别需要去改进的地方吧,就像完成工作一样,首先一定是要把工作完成,然后再想着怎么去优化,改进。在这个新型技术一浪接着一浪的时代,一定要快速开发,功能都没有完成,想着优化有什么用呢?

  4. 质疑和沟通很重要,有什么问题在尝试过后,解决不了就和别人沟通,说不定是别人的问题,或者别人可能很轻松的帮你解决,记得把问题的解决记录下来。不要一个人闷着,做太多的额外功,等到别人催你之后再去和别人沟通,这时,项目的进度就被你拉下来了,这样不好。

  5. 做事情积极主动一点,手上工作完成了就想想能不能优化,和同事一起商量一下有没有更好的解决方案,同事那边有什么是你能帮上忙的,不要被动的被推着走,积极主动点没错的。

  6. 记下这句可以警醒的话

    这才是真正的人生,每个人都按照惯性进步或者滑落,更多是在煮沸温水中逐渐死去的青蛙,愚昧无知到连跳出去的欲望都欠奉。

  7. 嗯,还有都快130了。。。。这么胖,你能忍? 多跑步健身减肥,健康是最大的本钱,平安是福,对人对己都是。

今天就这样吧,最重要的是:

坚持去坚持。

webpack入坑之旅(六)配合vue-router实现SPA

这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新。下面是目前完成的列表:

在上面的练习当中我们已经成功的加载了一个.vue格式的单文件组件,并且实现了在使用vue情况下的自动刷新。

但是我们最终的目的还是要实现单页面应用程序,这个时候我们就必不可少的需要使用到路由管理器来进行SPA的开发,vue官方为我们提供了一个官方库vue-router,并且配有对应的中文文档。关于里面的内容大家自行前去观看。在这里,只会把我们需要的东西拿出来讲。

vue组件

官网对于组件讲解

Vue中定义一个组件非常简单,只需要一对自定义标签,在其中填入内容就可以进行我们的组件编写了,然后使用Vue.component()去注册我们的组件下面来看一个例子,来直观的看看vue的组件。

组件入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    <script src="js/vue.js"></script>
<body>
<div id="app">
<my-component></my-component>
<!-- 自定义标签作为组件名称 -->
<my-component></my-component>
<!-- 复用 -->
</div>
<script>
// 定义并且注册组件
// 在官方的示例中使用 Vue.extend({})先注册了一个定义模板,再引用,看个人喜好吧
Vue.component("my-component", {
template:"<h2>hello Vue component</h2>"
})
// 创建根实例
// 在这里 定义并且注册组件 必须创建根实例前,不然会报错,因为解析顺序的问题?
new Vue({
el:"#app"
});
</script>
</body>

上面就是最简单的定义组件的方式,**template属性中写的东西**:就是<my-component>这个自定义标签渲染后展现出来的样式,这里渲染为:

1
2
3
4
<div id="app">
<h2>hello Vue component</h2>
<h2>hello Vue component</h2>
</div>

组件的基础介绍就到这,更多详细内容请移步官网,有着非常清晰的讲解。

vue-router

刚刚已经对于vue的组件有了一定的了解。现在来结合vue-router,来进行一下动态的切换。

首先是安装,如果使用npm的形式的话,直接运行npm install vue-router --save,就可以看到vue-router,已经被添加到了项目依赖中。直接上ES6的语法来进行引入

1
2
3
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);

起步

其实这一部分vue-router中文文档中已经讲的非常详细了。。在这里与它不同的是它用的CommonJS的规范来进行模块安装,而我使用ES6的import,有兴趣自己去看- -。其他的内容我就直接扒下来了。

html:

1
2
3
4
5
6
7
8
9
10
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用指令 v-link 进行导航。 -->
<a v-link="{ path: '/foo' }">Go to Foo</a>
<a v-link="{ path: '/bar' }">Go to Bar</a>
</p>
<!-- 路由外链 -->
<router-view></router-view>
</div>

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
// 定义组件
var Foo = Vue.extend({
template: '<p>This is foo!</p>'
})
var Bar = Vue.extend({
template: '<p>This is bar!</p>'
})
// 路由器需要一个根组件。
// 出于演示的目的,这里使用一个空的组件,直接使用 HTML 作为应用的模板
var App = Vue.extend({})
// 创建一个路由器实例
// 创建实例时可以传入配置参数进行定制,为保持简单,这里使用默认配置
var router = new VueRouter()
// 定义路由规则
// 每条路由规则应该映射到一个组件。这里的“组件”可以是一个使用 Vue.extend
// 创建的组件构造函数,也可以是一个组件选项对象。
// 稍后我们会讲解嵌套路由
router.map({
'/foo': {
component: Foo
},
'/bar': {
component: Bar
}
})
// 现在我们可以启动应用了!
// 路由器会创建一个 App 实例,并且挂载到选择符 #app 匹配的元素上。
router.start(App, '#app')

我个人感觉这部分还是很好理解的,官方也给了一系列的例子:查看仓库的 README.md 来运行它们。很好的展现了它的路由切换。

简单的介绍到这,下面最重要的部分到了,看看如何结合我们定义的.vue单文件组件。

首先来看我们的文件目录结构:

01-webpack-vuerouter

定义路由规则

** 最主要是main.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
55
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 引入vue以及vue-router的。
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来

// 引入组件!
import App from './App.vue'
import Index from './components/index.vue'
import List from './components/list.vue'
import Hello from './components/hello.vue'

// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{
path: '/index',
component: Index,
children: [
{ path: 'hello', component: Hello }
]
},
{
path: '/list',
component: List
},
{
path: '*',
redirect: '/index'
}
]

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
new Vue({
el: '#app',
router,
render: h => h(App)
})

// 现在,应用已经启动了!

App.vue 需要有用于渲染匹配的组件,如下

1
2
3
<template>
<router-view></router-view>
</template>

现在当我们运行 npm start 进入http://localhost:8080/就会自动跳转到http://localhost:8080/#/index,并且读取里面的内容。

实现路由跳转

主要抽出index.vue中的内容来讲解,的内容是:(list.vue里面的内容自行设置查看吧)

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
<template>
<div>
<h1>姓名:{{name}}</h1>
<h2>{{age}}</h2>
<button @click="golist">$route.push查看</button>
<router-link :to="{ path: '/list' }">v-link查看列表</router-link>
<router-link :to="{ path: '/index' }">回去主页</router-link>
<router-link :to="{ path: '/index/hello' }">嵌套的路由</router-link>
<hello></hello>
<router-view></router-view>
</div>
</template>
<script>
// import hello from "./hello.vue"
export default {
data () {
return {
name:"guowenfh",
age:"21"
}
},

methods :{
golist (){
this.$router.push({path:"/list"})
// this.$route.router.go({name:"list"});
}
}
}
</script>
<style></style>
<!-- 样式自行设置,或者直接看源码就好 -->

因为自刷新的缘故,直接切换到浏览器。

点击上面使用的router-link,与this.$router.push的方式都可以跳转到list定义的路由。(观察浏览器地址栏的变化)在这里我们使用 { path: '/list' } ,如有别名的话,使用的{name:"list"},会有同样的效果。

Vue组件的嵌套

在第一小点里面我们看到了在页面内的组件的使用方法,第二小点中学习到了vue-router的制定路由规则。

看过这两个地方之后,我们把思维发散开来,应该就能触类旁通的想到如何在页面中嵌套加载别的组件了。
我们创建一个hello.vue ,里面内容随意。现在我们如果要在app.vue中加载它,那么只需要在app.vue中使用import hello from "./hello.vue"(其实这个达到了使用require两步的效果。引入赋值)。

引入之后,只需要如下注册:

1
2
3
4
5
6
export default {
//其它的就
components:{
hello//若还有更多的组件,只需要在import的情况下,以逗号分割,继续注册就好
}
}

最后在app.vue中添加<hello></hello>这一对自定义标签,就可以实现加载hello.vue中的内容。

组件的嵌套也就是这样,很简单的描述完了,但是怎么样去抽离组件,在工作中积累可以复用的组件才是我们真正需要去思考的。

那么先到这,关于组件之间通信的问题,留到以后慢慢了解。

路由嵌套

还是刚刚的代码与目录结构,我们已经实现了组件之间的嵌套,但是有时并不希望组件直接就加载进来,而是在用户点击后才展现在页面中,这是就需要使用到路由嵌套。

为了偷懒,这里就直接使用hello.vue。实现嵌套路由主要有以下几步:

第一步:制定嵌套路由规则:

main.js下面这部分的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
path: '/index',
component: Index,
// 在/index下设置一个子路由
children: [
// 当匹配到/index/hello时,会在index的<router-view>内渲染
{
path: 'hello',
name:'hello',//可有可无,主要是为了方便使用
// 一个hello组件
component: Hello
}
]
}

第二步:在组件中添加<router-view>

来自官网的解释:<router-view> 用于渲染匹配的组件,它基于Vue的动态组件系统,所以它继承了一个正常动态组件的很多特性。

<router-view>写在app.vue<template></template>标签中。

第三步:写入跳转路径

还是在index.vue中:

1
2
3
<router-link :to="{ path: '/index' }">回去主页</router-link>
<!-- 点击这两个标签就会实现页面内的切换效果 -->
<router-link :to="{ path: '/index/hello' }">嵌套的路由</router-link>

,切换到浏览器,点击该嵌套的路由即可让hello.vue中的展现出来,在这里直接使用了router-link来实现跳转 ,当然$router.push同理。(注意在点击两个不同的文字时,地址栏的变化,以及展现内容的切换)

注意:

在我的源码中是在<style scoped></style>标签中定义样式的,请注意scoped的使用,它表示在该style中定义的样式只会在当前的组件中起到效果,而不会去影响全局的css样式。

最简单的理解应该就是:

未写该scoped属性的所有组件中的样式,在经过vue-loader编译之后拥有全局作用域。相当于共用一份css样式表。

而写了该属性的的组件中定义的样式,拥有独立作用域。相当于除去引入了公用的一份css样式表外,但单独拥有一份css的样式表。

webpack入坑之旅(五)加载vue单文件组件

这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新。下面是目前完成的列表:

需要什么?

在经过前面的四个练习,相信已经对于webapck有了一定的了解,现在我们就来一个综合案例,进一步加深对于webpack的理解。

首先我们应该思考要解析.vue类型的文件,需要什么样的东西?应该按照什么样的步骤来?我们应该怎么去搭建这个项目?

开始

第一步:初始化项目目录

我们需要创建如下目录及文件夹,最终目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
- dist //文件生成目录
-- //自动生成
- node_module //自动安装
-- ...
- src //文件入口
-- components //组件存放
-- app.vue //主.vue
-- main.js //主.js
- index.html //主.html
- package.json //npm 配置
- webpack.cofig.js // webpack配置

第二步:安装项目依赖

如果你上面没有创建package.json文件的话,可以直接使用npm init来初始化我们的package.json文件的配置。

想要去编译其他的文件比如reactcoffce等等,就必须要加载很多对应的loader。要想加载一个.vue文件。当然也是同样的道理。
建议用npm install xxx-loader --save-dev这样的命令一条一条的敲。在命令行中,会有提示,可以帮助理解webpack的中的依赖管理关系。我的配置清单如下:
在实际项目中,json文件中不能出现注释,在这里为了方便大家了解里面设置项的含义,就直接使用注释的方式加载后面了。

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
{
"name": "05-five-vue", //项目名称
"version": "1.0.0", //版本
"description": "vue+webapck", //描述
"main": "index.js", //主文件
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot"
}, //scripts指定了运行脚本命令的npm命令行缩写,比如这是的start指定了运行npm run start时,所要执行的命令。
"dependencies": { //项目依赖
"vue": "^2.5.17"
},
// 编译成的 es 版本
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"devDependencies": { //各种各样的loader,用来解析想相应的文件格式。要解析vue并且完成相应的功能,这些基本都是必须的。
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-0": "^6.24.1",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.2.0",
"css-loader": "^1.0.0",
"file-loader": "^1.1.11",
"node-sass": "^4.9.2",
"sass-loader": "^7.1.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"vue-loader": "^14.2.3",
"vue-style-loader": "^4.1.1",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.16.4",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5"
},
"author": "guowenfh", //作者
"license": "MIT", //开源协议
"keywords": [ //关键字
"vue",
"webpack"
]
}

如果你想省事的话,直接复制上面的devDependencies,dependencies字段,并且填写到你的package.json文件中。然后运行npm install就会自动安装所有的模块以及依赖。

第三步:配置webpack

文件已经设置好了,接下来就到了我们关键的一步,配置webpack.config.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
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
const path = require('path')
const webpack = require('webpack')
// NodeJS中的Path对象,用于处理目录的对象,提高开发效率。
// 模块导入
module.exports = {
// 入口文件地址,不需要写完,会自动查找
entry: './src/main',
// 输出
output: {
path: path.join(__dirname, './dist'),
// 文件地址,使用绝对路径形式
filename: 'build.js',
//[name]..这里是webpack提供的根据路口文件自动生成的名字
publicPath: '/dist/'
// 公共文件生成的地址
},
mode:'development',
// 加载器
module: {
// 加载器,loaders
rules: [
// 编译css
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
//.scss 编译
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
],
},
{
test: /\.sass$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader?indentedSyntax'
],
},
// 解析.vue文件
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
'scss': [
'vue-style-loader',
'css-loader',
'sass-loader'
],
'sass': [
'vue-style-loader',
'css-loader',
'sass-loader?indentedSyntax'
]
}
}
},
// 转化ES6的语法
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
options:{
presets: [['env', { modules: false }], 'stage-0']
}
},
// 图片转化,
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'url-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
// 别名,可以直接使用别名来代表设定的路径以及其他
alias: {
vue$: 'vue/dist/vue.esm.js',
filter: path.join(__dirname, './src/filters'),
components: path.join(__dirname, './src/components')
},
// require时省略的扩展名,如:require('module') 不需要module.js
extensions: ['*', '.js', '.vue', '.json']
},
// 服务器配置相关,自动刷新!
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
// 开启source-map,webpack有多种source-map,在官网文档可以查到
devtool: '#eval-source-map'
}

请详细查看这里面的设置,我这里都是很简单的配置,在你的项目中,还可以更进一步的对于入口文件和输出文件进行更加深入的定制。

并且在这里生成的css文件还会插到js中,有时我们需要更进一步的把它独立出来,然后在html中引入这时就会用到webpack的插件,在这里先不说(因为我暂时没用到,没有试验过,好像也不麻烦,可以的话下篇再试试)

第四步:编写代码

接下来就是我们要展示的文件的编写了,我直接把代码贴上来了。

index.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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>webpack vue</title>
<style>
*, *:before, *:after {
box-sizing: border-box;
}
body, html {
height: 100%;
overflow: hidden;
}
#app {
margin: 20px auto;
width: 800px;
height: 600px;
}
</style>
</head>

<body>

<div id="app"></div>

<script src="./dist/build.js"></script>
</body>

</html>

这里是main.js的内容:

1
2
3
4
5
6
7
8
9
10
11
12
//es6语法:
import Vue from "vue";
//引入我们编写的测试用vue文件。
import App from './components/app';

Vue.config.debug = true;//开启错误提示

new Vue({
el: '#app',
render: h => h(App)
})

这里是app.vue:

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
<template>
<div id="app">
<h1>姓名:{{name}}</h1>
<h2>{{age}}</h2>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
name: 'guowenfh',
age: '23'
}
}
}
</script>
<style lang="scss" >
$qwe: #098;
body {
background-color: $qwe;
h1 {
background-color: #eee;
color: yellowgreen;
transform: translate(10%, 10%);
}

h1:hover {
height: 100px;
}

h2 {
background-color: #999;
}
}
</style>

第五步:修改自动刷新设置

下面再单独的再谈一下关于自动刷新的实现,首先需要说明,在上一篇博客中的自动刷新实现,是有问题的。只能改变css样式,使颜色进行变化。对于html里面的内容改变时,浏览器并不会自动刷新。

注意点一:
首先我们看到package.jsonscripts字段中的"start": "cross-env NODE_ENV=development webpack-dev-server --open --hot"。 这里开启了 热加载 以及自动打开浏览器。

注意点二:
webpack.cofig.js中还有其余对于devServer进行一些配置,如下:

1
2
3
4
5
6
7
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true,
inline: true,
progress: true, // 进度
},

这样安装设置完成之后,就有了自动局部更新了!!

结束

步骤都走完了,因为在上面的package.json中已经进行了scripts项的配置。运行npm start,打开localhost:8080

可以看到设置的背景色已经出来了,去改变一下背景颜色?data?template?

看看浏览器会不会自动刷新?


添加

开发环境可以了,但是生产环境呢?在 webpack.config.js我们再来增加一些配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (process.env.NODE_ENV === 'production') {
module.exports.mode = 'production',
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

然后再加一个 npm script 。 "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
运行 npm run build 我们就可以将我们刚刚开发的内容进行压缩打包了。

如果你按照我的步骤,并且,npm包安装没有错误的话,应该就能成功了。

不行的话,请再仔细对照去看看有什么地方没有编写对吧!

webpack入坑之旅(四)扬帆起航

这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新。下面是目前完成的列表:

加载图片

现在来我们来试试加载图片,首先第一件事情,肯定是安装对应的loader。它会将样式中引用到的图片转为模块来处理,使用该加载器需要先进行安装:

1
npm install url-loader --save-dev

当然你也可以在package.json添加依赖,然后再npm nstall一样的效果。

现在去我们的项目目录中添加img文件夹,添加两张图片,一张大图jpg,一张小图png。

然后在我们的webpack.config.js中添加这段:

1
2
3
4
5
6
7
8
9
10
11
12
rules: [
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 8192,
name: path.posix.join('','img/[name].[hash:7].[ext]')
}
// 添加到这并且会按照文件大小, 或者转化为 base64, 或者单独作为文件
//在大小限制可以name属性/[name].[ext],会将我们的文件生成在设定的文件夹下。
},
]

在html中添加:

1
2
3
<img src="img/logo.png" alt="">
<div id="qwe"></div>
<div id="asd"></div>

在我们的css中添加:

1
2
3
4
5
6
7
/*记得写宽高。。*/
#qwe{
background-image: url(img/logo.png);/*3.2k*/
}
#asd{
background-image: url(img/5.jpg);
}

继续运行webpack如果正确的话,打开我们的浏览器,就可以看到我们正确的图片显示。

如果不正确,请运行npm install file-loader -D,再进行尝试。

现在我们打开浏览器的调试工具,可以看到小于8K的 背景图片 图片已经被转化成了base64的编码,而大于8k的图片则并没有转化(注意它的地址的变化!)。
直接使用img导入的图也并没有进行base64的转化。

热加载

当项目逐渐变大,webpack 的编译时间会变长,可以通过参数让编译的输出内容带有 进度颜色

1
webpack --progress --colors

下面还有一些其他常用的命令:

1
2
3
4
5
6
7
webpack #最基本的启动webpack命令
webpack -w #提供watch方法,实时进行打包更新
webpack -p #对打包后的文件进行压缩
webpack -d #提供SourceMaps,方便调试
webpack --colors #输出结果带彩色,比如:会用红色显示耗时较长的步骤
webpack --profile #输出性能数据,可以看到每一步的耗时
webpack --display-modules #默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块

我们已经把webpack的内容了解了一部分了,那么在生产环境中,我不想每一次进行改变,都去命令行中运行我们的webpack的命令,我们应该怎么样实现改变后自动更新呢?

webpack 为我们提供了一个webpack --watch,他会启动监听模式。开启监听模式后,没有变化的模块会在编译后缓存到内存中,而不会每次都被重新编译,所以监听模式的整体速度是很快的。

去运行这个命令试试吧!!

在我们改变代码之后,命令行中可以看到直接就自动编译了,但是显然不够智能,还需要我们手动去刷新浏览器,(其实用liveloadhack成自动刷新!)。

我反正不能忍,还要手动刷新浏览器。所以使用webpack-dev-server会是一个更好的办法!

它将在localhost:8080启动一个express静态资源web服务器,并且会以监听模式自动运行webpack,在浏览器打开http://localhost:8080/http://localhost:8080/webpack-dev-server/ 可以浏览项目中的页面和编译后的资源输出,并且通过一个socket.io服务实时监听它们的变化并自动刷新页面。

1
2
3
4
5
# 安装
npm install webpack-dev-server -D

# 运行
npx webpack-dev-server --config ./webpack.config.js

试试vue

我们来试试使用vue能配合webpack能不能实现自动刷新。(有关vuejs的知识,大家可以可以先自行上官网查看)

首先运行npm install vue -save将vue添加到我们的项目依赖中去。

首先在我们的entry.js这个入口文件中进行添加:

1
2
3
4
5
6
7
8
9
10
11
// import Vue form ("vue") //如果你安装了babel-loader的话,可以直接使用ES6的语法

const Vue =require('vue/dist/vue');

new Vue({
el: '#main',
data: {
message: 'hello vue.js'
}
})

同样在index.html中添加{{ meassge }}来响应vue的数据绑定。

运行webpack-dev-server。去浏览器查看试试效果吧!http://localhost:8080。 任意改变message中的值,可以看到浏览器会自动刷新。并且将改变的值展示在眼前。(有可能只在http://localhost:8080/webpack-dev-server/才会自动刷新)

自动刷新都配好了。下面我们就来试试怎么加载vue的文件,来实现单文件组件!

使用搜索:谷歌必应百度