vue学习笔记(b站王红元老师网课笔记)

1.vue的template模板

可以把一整块的代码块放入template中,这样就可以方便以后调用。

<div id="app">
	{
   
   {message}}
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello'
		}
	})
</script>

以上即为一个模板,把它复制后进入设置,找到editor->live templates->vue,再点击右侧加号,把内容复制进去,同时给这个模板取个名字。记得要在界面下方的这里设置勾选html,再点击apply即可。
在这里插入图片描述

2. vue的mustache语法

如何把data,methods中的文本数据,插入到HTML代码中?我们可以用到mustache语法,也就是这样双大括号的形式:

<div id="app">
	{
   
   {message}}
</div>

这样的数据是响应式的。在mustache里还可以进行字符串的拼接,数值的计算等操作。

要注意的是,mustache语法是写在内容中的,而不能写在标签里。

3. vue插值操作的其他语法

3.1 v-once

如上的数据是响应式的,但是有时,我们不希望数据随着用户输入而改变。这时,就需要在代码里加入“v-once”。如下:

<div id="app">
	<h2 v-once>
        {
   
   {message}}
    </h2>
</div>

3.2 v-html

有时,我们从服务器请求到的数据是html代码,这时候用mustache语法解析会把html代码输出(比如下图里会把a标签也展示出来),但是我们希望得到的是按照html格式来进行解析的内容,这时,就要用到v-html。如下:

<div>
    <h2 v-html="url">
        {
   
   {message}}
    </h2>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        url:'<a href="...">xxx</a>'
		}
	})
</script>

3.3 v-text

<div>
	<h2 v-text="message"></h2>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        url:'<a href="...">xxx</a>'
		}
	})
</script>

展示效果与mustache相同,但是一般不用,不如mustache灵活,无法进行拼接。

3.4 v-pre

v-pre类似于

标签,里面的东西可以原封不动地表现出来,用于跳过这个元素和这个元素里的内容。如,我们想在页面上显示一个双括号:

<div>
	<h2 v-pre>{
   
   {message}}</h2>
</div>

3.5 v-cloak(斗篷)

html的代码是从上至下解析的,所以当之后的js代码卡住时,前面未被js解释的mustache语法就会显示出来。一般情况下,我们不希望用户看到这种信息,所以就有了v-cloak,用法如下:

<div id="app" v-cloak>
	{
   
   {message}}
</div>

我们可以把v-cloak当成一个属性,加入到div里。在vue解析之前,div里有一个属性v-cloak,解析之后就没了。所以,可以根据有无v-cloak属性来判断js代码是否执行了。我们用style来实现这个功能:

<style>
    [v-cloak] {
     
     
		display:none;
    }
</style>

4. v-bind

4.1 基本使用

网页中某些属性我们希望动态决定,如商城里的商品轮播图等。一些网址等,在开发中也很少会写死,大多都是从服务器请求得来的。我们把这样的数据在vue里暂存中转,在html里利用v-bind来实现动态绑定。

<div id="app">
    <img v-bind:src="imgURL">
    <a v-bind:href="ahref"></a>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        imgURL:'website1,website2……'
        ahref:'website1,website2……'
		}
	})
</script>

v-bind有个语法糖(简写),只留下一个冒号,格式如下:

<div id="app">
    <img :src="imgURL">
    <a :href="ahref"></a>
</div>

4.2 v-bind动态绑定class

4.2.1 对象语法

v-bind和class的对象用法如下:

<style>
    .active {
     
     
		color:red;
    }
</style>
<div id="app">
	<h2 :class="active">
        {
   
   {message}}
    </h2>
    <h2 v-bind:class="{key1:value1, key2:value2}"><!--大括号里存放对象-->
        {
   
   {message}}
    </h2>
    <h2 v-bind:class="{类名1:boolean, 类名2:boolean……}"><!--当boolean的值为true时,是元素拥有该类名,否则没有-->
        {
   
   {message}}
    </h2>
    <h2 v-bind:class="{active:isActive, line:isLine}"><!--这是常用情况,即把boolean的值存入vue中-->
        {
   
   {message}}
    </h2>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        active:'active'
        isActive:true,
        isLine:true,
		}
	})
</script>

假设有这样的一个需求:有一个button,按下后使文字变成红色,再按一下又变回黑色,如此往复,怎么实现?

<style>
    .active {
     
     
		color:red;
    }
</style>
<div>
    <h2 v-bind:class="{active:isActive, line:isLine}">
        {
   
   {message}}
    </h2>
    <button v-on:click="Click"></button>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        active:'active'
        isActive:true,
        isLine:true,
		}
   	methods:{
     
     
		Click: function(){
     
     
        this.isActive=!this.isActive
   	 	}	
    }
	})
</script>

如上代码,我们可以在button里定义一个点击事件click=”Click“,再在vue里定义方法Click。这样,每当我们按下button,就等于在vue里让isActive的值取反,从而实现message反复拥有、不拥有active类——即颜色变化的效果。

此外,v-bind决定的class不与直接定义的class(固定的)冲突,可以共存。如:

<h2 class="title" v-bind:class="{active:isActive, line:isLine}">
        {
   
   {message}}
    </h2>

这里,title类是固定不变的,其余两个active和line类都是可变的。

4.2.2 数组语法

v-bind和class的数组用法如下(不常用):

<h2 class="title" v-bind:class="['active','line']">
        {
   
   {message}}
    </h2>

4.3 v-bind动态绑定style

什么时候会用到动态绑定style?比如说,我们在做网站时,我们会把一个搜索栏做成一个组件。而这个搜索栏再主页、分页里样式不同,所以需要动态绑定。

4.3.1 对象语法

v-bind和style的对象用法如下:

<div id="app">
	<h2 :style="{
       
       key(css属性名):value(属性值)}">
        {
   
   {message}}
    </h2>
    <h2 :style="{
       
       fontSize:'50px'}"><!--记得加单引号,不加的话会被vue认为是变量,但是类名不用加-->
        {
   
   {message}}
    </h2>
    <h2 :style="{
       
       fontSize: finalSize}">
        {
   
   {message}}
    </h2>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        finalSize: '100px'
		}
	})
</script>

4.3.2 数组语法

<h2 :style="[baseStyle,baseStyle2]">
    {
   
   {message}}
</h2>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        baseStyle:{
     
     backgroundColor:'red'},
        baseStyle:{
     
     fontSize:'100px'}
		}
	})
</script>

5. 计算属性

5.1 计算属性的基本使用

当我们需要对data里的数据进行处理并显示时,需要用到computed(计算属性),它虽然在vue里通过函数实现,但在html里要当作属性来使用(不用加括号)。

计算属性的基本使用:

<h2>
    {
   
   {getFullName()}}<!--通过方法获取-->
    {
   
   {fullName}}<!--计算属性,不用加括号-->
</h2>

<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        firstName:'Lebron',
        lastName:'James'
	},
    methods:{
     
     
        getFullName(){
     
     
        return this.firstName+' '+this.lastName
    	}
    },
    computed:{
     
     <!--按照属性名取函数的名字-->
    	fullName: function(){
     
        
            return this.firstName+' '+this.lastName
        }   
    }
	})
</script>

computed还可以实现一些不用计算属性,只用mustache语法难以实现的功能:

<h2>
    {
   
   {totalPrice}}
</h2>

<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		books:[
            {
     
     id:100,name:'unix',price:120},
            {
     
     id:101,name:'c++',price:110},
            {
     
     id:102,name:'java',price:100},
        ]
	},
    computed:{
     
     
        totalPrice: function(){
     
     
            let result=0;
            for(let i=0;i<this.books.length;i++)
                {
     
     
                    result+=books[i].price
                }
            return result
        }
    }
	})
</script>

5.2 计算属性的getter和setter方法

每个计算属性都包含一个getter和setter方法。这是计算属性的完整版本(5.1里是简写版本)。一般情况下,我们不用写set方法。

<div id="app">
    <h2>
        {
   
   {fullName}}
    </h2>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
    el:'#app',
	data:{
     
     
		firstName:'Kobe',
        lastName:'Bryant'
	},
    computed:{
     
     
        fullName:{
     
     
            set:function(newValue){
     
     
                const names = newValue.split(' ');
                this.firstName=names[0];
                this.lastName=names[1];
            },
            get:function(){
     
     
        		return this.firstName+' '+this.lastName
   			}
        }
    }
    })
</script>

5.3 计算属性和methods的对比

为什么我们更多使用计算属性而不是methods方法?因为当我们使用(调用)属性时,methods每次调用属性都要调用methods方法,而计算属性只调用一次。所以计算属性性能更好。

<h2>
    {
   
   {getFullName()}}<!--通过方法获取-->
    {
   
   {fullName}}<!--计算属性-->
</h2>

<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',
        firstName:'Lebron',
        lastName:'James'
	},
    methods:{
     
     
        getFullName(){
     
     
        return this.firstName+' '+this.lastName
    	}
    },
    computed:{
     
     <!--按照属性名取函数的名字-->
    	fullName: function(){
     
        
            return this.firstName+' '+this.lastName
        }   
    }
	})
</script>

6. ES6基本语法补充

6.1 块级作用域 let var

变量作用域:变量在什么范围内可用。

ES5之前,由于if和for都没有块级作用域,必须借助function来解决引用外部变量的问题。ES6中加入了let,它拥有if和for块级作用域。

下面是var和let的区别,方便我们理解块级作用域:

const btns = document.getElementsByTagName('button')
for(var i = 0; i < btns.length; i++){
	btns[i].addEventListener('click',function(){
    	console.log("第"+i+"个元素被打印");
    })
}
for(let i = 0; i < btns.length; i++){
	btns[i].addEventListener('click',function(){
    	console.log("第"+i+"个元素被打印");
    })
}

当我们用var指定i时,点击第一个按键,console会打印“第5个元素被打印”,这是因为在执行console.log语句之前,由于var没有for的块级作用域,var已经被加到了5,再去进入执行console.log语句,而let则不会。let每个循环语句里有独立的i,var的情况下,每当进入下一个循环,由于没有块级作用域,改变i值(i++)会把i赋给之前所有的循环里的i,所以最终所有的i都变成了最后一个i值。当我们点击时,回调console.log,自然得不到正确的结果。

6.2 const的使用

const用以保证数据安全性。在开发中,优先使用const,只在需要改变标识符时才使用let.

需要注意的是:const指向的对象不能修改,但是其内部属性可以。如:

const obj = {
	name:'fyk'
}
obj.name = 'james';

这时,obj对象的属性已被改变。

6.3 对象字面量的增强写法

什么是字面量?当我们创建一个对象时,可以new一个对象,也可以用大括号代替。这就是字面量。

const obj = new Object();
const obj = {} <!--字面量-->

字面量的增强写法是一个十分方便的功能。在ES5及之前的版本里,当我们想要对一个对象的属性赋以一个对象外的值时,我们需要这样操作:

const name= 'fyk';
const height= 1.83
const obj = {
	name: name,
	height: height
}

ES6里可以采取增强写法:

const obj = {
	name,
	height
}

除了对属性的增强写法,我们还可以对函数进行增强写法。下面是对比:

const obj = {
	run : function(){

	}
}
<!--ES5-->
const obj = {
	run(){
	
	}
}
<!--ES6-->

7. v-on

7.1 v-on基本使用和语法糖

前端开发需要经常和用户交互。这时就要监听用户事件,比如点击、拖拽等。在vue中,我们使用v-on指令来监听事件。

<div id="app">
    <h2>
    	{
   
   {counter}}
	</h2>
    <button v-on:click="increasement">
        +
    </button>
    <button v-on:click="sub">
        -
    </button>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		counter: 0
	},
    methods: {
     
     
        increasement(){
     
     
            this.counter++
        },
        sub(){
     
     
        	this.counter--	
    	}
    }
	})
</script>

v-on的语法糖:

@click <!--v-on:click,直接把v-on:变成@-->

7.2 v-on的参数传递

当事件监听时使用的方法没有参数,那么不用加括号,直接写方法名调用即可。

但是除这种情况外,还有很多情况:如有参数但无括号

<div id="app">
    <button v-on:click="Click1"><!--这种情况下,写方法时省略了小括号,但是方法是需要参数的,那么此时vue会默认把浏览器产生的事件作为参数传入方法-->
        按钮1
    </button>
    <button v-on:click="Click2(123,$event)"><!--调用方法时,要手动获取浏览器产生的事件对象:$event-->
        按钮2
    </button>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		counter: 0
	},
    methods: {
     
     
       Click1(event){
     
     
           console.log(event);
       } ,
       Click2(abc,event){
     
     
           
       }
	})
</script>

7.3 v-on的修饰符

<div @click="divClick">
    <button @click="buttonClick">
        按钮
    </button>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		counter: 0
	},
    methods: {
     
     
       divClick(){
     
     
           
       },
       buttonClick(){
     
     
           
       }
	})
</script>

当上述代码块执行后,若我们点击div块(非按钮区域),则divClick和buttonClick都会被执行。有时,我们在点击一块区域时并不想让其中的子区域的点击事件也被触发,那么我们就可以使用stop修饰符,如:

<div @click="divClick">
    <button @click.stop="buttonClick">
        按钮
    </button>
</div>

当我们想要监听某个键盘的键的键入时,我们可以用修饰符来帮助我们:

<div>
    <input type="text" @keyup.enter="keyUp">
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		counter: 0
	},
    methods: {
     
     
      keyUp(){
     
     
          
      }
	})
</script>

上述代码块执行时,当我们按下并松开键盘的键,不会执行keyUp方法,但是当我们按下并松开了回车键,就会执行keyUp方法。在keyup或keydown后加特定的修饰符可以用来监听特定按键的键入。

8. v-if,v-else-if和v-else

8.1 v-if

当我们想要根据一些条件来判断是否显示一些内容时,可以使用v-if.

<div>
    <h2 v-if="isShow">
        abc
    </h2>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		isShow: true
	}
})
</script>

8.2 v-if和v-else结合使用

<div>
    <h2 v-if="isShow">
        abc
    </h2>
    <h1 v-else>
        isShow为false时显示此句
    </h1>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		isShow: true
	}
})
</script>

8.3 v-else-if

<div id="app">
	<h2 v-if="score>=90">
        优秀
    </h2>
    <h2 v-else-if="score>=80">
        良好
    </h2>
    <h2 v-else-if="score>=60">
        及格
    </h2>
    <h2 v-else>
        不及格
    </h2>
    
    <h1> <!--计算属性的写法-->
        {
   
   {result}}
    </h1>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		score:99
	},
    computed:{
     
        <!--复杂情况更推荐写在计算属性里-->
        result(){
     
     
            let show = '';
            if(this.score>=90){
     
     
                show='优秀'
            }
            else if(...)
            return show
        }
    }
})
</script>

下面是依靠v-if实现的一个登录切换的小案例:

<div id="app">
    <span v-if="isUser">
        用户账号
        <input type="text" id="username">
    </span>
    <span v-else>
        用户邮箱
    	<input type="text" id="email">
    </span>
    <button @click="change">
        切换登录类型
    </button>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		isUser: true
	},
    methods:{
     
     
        change(){
     
     
            this.isUser=!this.isUser
        }
    }
})
</script>

这里有个小问题:如果我们在有输入内容的情况下,切换了类型,我们会发现此时文本框内依然存在着我们之前输入的内容。但是按理来说,我们应该切换到另一个input元素中了。而在另一个input元素中,我们并没有输入内容,为什么会出现这个问题?

这是因为vue的虚拟dom出于性能考虑,遇到互斥条件时,会复用input。为了解决这个问题,可以在input里加入key,当key值不同时,不会复用:

<div id="app">
    <span v-if="isUser">
        用户账号
        <input type="text" id="username" key="username">
    </span>
    <span v-else>
        用户邮箱
    	<input type="text" id="email" key="email">
    </span>
    <button @click="change">
        切换登录类型
    </button>
</div>

8.4 v-show

v-show和v-if很类似,都能决定元素是否显示。区别在于条件为false时,v-if修饰的元素根本就不会存在dom中,而v-show只是新增一个行内样式display,并把display属性设为none.条件为true时,v-if会再创建一个元素,v-show只用把display属性删去即可。

开发时,当需要频繁切换时,使用v-show更好。反之,v-if常用。

9. v-for

9.1 v-for遍历数组和对象

v-for遍历数组:

<div id="app">
    <ul><!--不加索引的遍历-->
        <li v-for="item in names">{
   
   {item}}</li>
    </ul>
    <ul><!--加上索引的遍历-->
        <li v-for="(item,index) in names">{
   
   {index}}.{
   
   {item}}</li>
    </ul>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		names:['harden','westbrook','tucker']
	},
   
})
</script>

v-for遍历对象:

<div id="app">
    <ul><!--只获取一个值的遍历获取到的是value-->
        <li v-for="item in info">{
   
   {item}}</li>
    </ul>
    <ul><!--加上key值的遍历-->
        <li v-for="(value, key) in info">{
   
   {key}}.{
   
   {value}}</li>
    </ul>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		info:{
     
     
            name:'kevin',
            height:1.83,
            weight:63
        }
	},
   
})
</script>

9.2 v-for绑定和非绑定key区别

<div id="app">
	<ul>
        <li v-for="item in letters" :key="item">{
   
   {item}}</li><!--注意不要把key的value写成index,因为当新数据插入后,index和item不是一一对应的-->
    </ul>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		letters:['a','b','c']
	},
   
})
</script>

9.3 数组中哪些方法是响应式的

push():从末尾加入数组元素(可一次加入多个元素)

pop():删除数组末尾元素

shift():删除数组第一个元素

unshift():从最前面加入数组元素(可一次加入多个元素)

splice(arg1,arg2,…):删除、插入、替换元素

删除:从第arg1位置开始,删除arg2个参数。如果第二个参数未定义,则默认从arg1开始全部删除。

插入:arg2=0,从arg1位置开始,插入n个元素(第三个参数及以后)

替换:从arg1位置开始,删除arg2个参数,再加入n个元素(第三个参数及以后)

sort():排序

reverse():反转数组

这些方法都是响应式的方法。

要注意的是,直接通过索引值改变数组元素不是响应式的。要改动指定位置的元素,可以使用splice()来实现。也可以使用Vue的set方法。

Vue.set(this.letters,0,'abc')<!--arg1是数组名,arg2是改变的元素位置,arg3是改变后的值-->

9.4 购物车案例(综合运用)

以下是一个购物车的案例,综合运用了v-if,v-for,v-bind,v-on等知识,同时用到了过滤器。目标是这样的:
在这里插入图片描述

<head>
    ...
    <link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app">
	<div v-if="books.length"><!--当购物车为空时,不显示这些,而是显示字符串“购物车为空”-->
        <table>
        <thead>
        	<tr>
            	<th>书籍名称</th>
                <th>出版日期</th>
                <th>价格</th>
                <th>购买数量</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
        	<tr v-for="(item,index) in books">
                <td>{
   
   {item.id}}</td>
            	<td>{
   
   {item.name}}</td>
                <td>{
   
   {item.date}}</td>
                <td>{
   
   {item.price|showPrice}}</td>
                <td>
                    <button @click="increasment(index)">
                        +
                    </button>
                    {
   
   {item.quantity}}
                	<button @click="sub(index)" v-bind:disabled="item.count<=1">
                        -<!--当数量等于1时无法再减少-->
                    </button>
                </td>
                <td>
                	<button @click="remove(index)">
                        移除
                    </button>
                </td>
            </tr>
        </tbody>
    </table>
    <h2>
    	{
   
   {totalPrice|showPrice}}    
    </h2>
    </div>
    <h2 v-else>
        购物车为空
    </h2>
</div>
    
<script src="vue.js"></script>
<script src="main.js"></script>
</body>

const app = new Vue({
    
    
    el:'#app',
    data:{
    
    
        books:[
            {
    
    
                id:1,
                name:'algorithm introduction',
                date:'2006-9',
                price:85.00,
                count:1
            },
            {
    
    
                id:2,
                name:'unix',
                date:'2006-2',
                price:59.00,
                count:1
            }
        ]
    }
    methods:{
    
    
    	increasment(index){
    
    
    		this.books[index].count++
		},
		sub(index){
    
    
            this.books[index].count--
        },
        remove(index){
    
    
			this.books.splice(index,1)
        }
	}
    computed:{
    
    
        totalPrice(){
    
    <!--利用计算属性计算总价格-->
            let totalPrice=0;
      		for(let i=0;i<this.books.length;i++){
    
    
            	totalPrice+=this.books.[i].price*this.books[i].quantity;
        	}
        	return totalPrice
        }
    }
    filters:{
    
    
    	showPrice(price){
    
    
    		return '$'+price.toFixed(2);<!--保留两位小数,使用过滤器,可以自动传参,比methods方便-->
		}
	}
})
table{
    
    
    border:1px solid #e9e9e9;
    border-collapse: collapse;
    border-spacing :0;
}
th, td{
    
    
    padding: 8px 16px;
    border:1px solid #e9e9e9;
    text-align: left
}
th{
    
    
  background-color:;
    color:;
    font-weight:;
}

10. JavaScript高阶函数(对于数组的方法)

10.1 filter

filter需要回调函数,它要求必须返回一个布尔值。当返回的是true,函数内部会自动把这次回调的n(数组里的每一个值或对象)加入到一个新的数组里。当返回的是false,函数内部不会把n加入新数组(过滤)。如果我们想把一个数组里小于100的值提取出来,可以利用filter这样写,比写for循环方便许多:

const nums = [10,20,50,110,220]
let newNums = nums.filter(function(n)({
	return n < 100
}))

10.2 map

如果我们想对数组里的每一个元素进行操作,可以使用map函数。map函数也需要一个回调函数,与filter类似,它会把每一个回调的n按照我们的指令进行操作并加入到新的数组里。如果我们想要把上一个例子里的数组的元素值都乘以两倍,那么可以利用map这样写,比for循环方便许多:

let newNums2 = newNums.map(function(n){
	return n * 2
})

10.3 reduce

reduce函数的作用是对数组中的所有内容进行汇总。

a.reduce(arg1,arg2)<!--arg2是初始值initialValue-->
a.reduce(function(preValue,n){
   
   <!--n就是数组里代表元素的变量-->
	return 1
},0)
<!--第一次:preValue=0,n=20-->
<!--第一次:preValue=1,n=40-->
<!--第一次:preValue=1,n=80-->
let total = newNums2.reduce(function(preValue,n){
	return preValue+n
},0)
<!--初始值设为0,每循环一次加一次n,而n刚好是数组里每一个值,从而实现累加操作-->

let total = this.books.reduce(function(preValue,book){
	return preValue+book.price*book.quantity
},0)
<!--购物车案例里总价格的reduce函数实现-->

这几个函数,就体现了函数式编程的思想。

11. v-model

11.1 v-model基本使用

v-model用以实现表单元素和数据的双向绑定。如下的代码运行后,我们可以在网页中看到,虽然我们没有输入内容,但此时表单里已经有默认的值”hello“,当我们改动data里的message,表单里的内容也会随着改变。同时,当我们改动表单里的内容(输入我们想输入的内容),data里的message也会随之改变。这就是双向绑定,而不是v-bind那种单向绑定——单向绑定的情况下,只有改动data里的数据才会让网页元素改变,改变网页元素无法改变data里的数据。

<div id="app">
    <input type="text" v-model="message">
    {
   
   {message}}
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello'	
	}
   
})
</script>

v-model其实就是这两个语法的结合:1.v-bind绑定一个value属性。2.v-on指令给当前元素绑定input事件

<div id="app">
    <input type="text" :value="message" @input="valueChange"><!--这里的valueChange不用加参数,遇到event事件会自动传参-->
    {
   
   {message}}
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello'	
	},
    methods:{
     
     
        valueChange(event){
     
     
            this.message = event.target.value
        }
    }
   
})
</script>

11.2 v-model结合radio类型

当我们在radio里想要让选项互斥,就要把input的name属性设置为相同的。而如果我们只是单纯地设两个相同的name,我们就无法获取其值(后端要用),所以就需要再在input标签里加入value属性。v-model就可以把name和value放到一起,比较便捷。

<div id="app">
	<label for="male"><!--注意label for的值与input的id对应-->
    	<input type="radio" id="male" v-model="sex"></label>
    <label for="female">
        <input type="radio" id="female" v-model="sex"></label>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello'	
        sex:'男'<!--默认选择是男-->
	}
   
})
</script>

11.3 v-model结合checkbox类型

checkbox分为单选框和多选框。我们分别举例:

单选框,比如说同意协议,且同意了才能进行下一步:(v-model具备了获得input的value属性的能力,理解时要注意这个)

<div id="app">
	<label for="agree">
    	<input type="checkbox" id="agree" v-model="isAgree">
    </label>
    <button :disabled="!isAgree">
        next step
    </button>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello'	
        isAgree:false//单选框对应布尔类型
	}
   
})
</script>

多选框:

<div id="app">
	<input type="checkbox" value="basketball" v-model="hobbies">basketball
    <input type="checkbox" value="football" v-model="hobbies">football
    <input type="checkbox" value="badminton" v-model="hobbies">badminton
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello'	
        hobbies:[]//多选框对应数组类型
	}
   
})
</script>

11.4 v-model结合select类型

select下拉选择框也分为单选和多选。我们分别举例:

单选:

<div id="app">
	<select v-model="fruit">
        <option value="apple">apple</option>
        <option value="orange">orange</option>
        <option value="banana">banana</option>
    </select>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',	
        fruit:'apple'
	}
   
})
</script>

多选:

<div id="app">
	<select v-model="fruit" mutiple><!--多选只要加上mutiple-->
        <option value="apple">apple</option>
        <option value="orange">orange</option>
        <option value="banana">banana</option>
    </select>
</div>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
	el:'#app',
	data:{
     
     
		message:'hello',	
        fruit:[]
	}
   
})
</script>

11.5 v-model修饰符

11.5.1 lazy

默认情况下,v-model是在input事件中同步输入框的数据的。一旦有数据改变,data里的数据就会自动随之改变。但有时,我们不想让data里的数据这么快改变,那么就可以添加lazy修饰符,从而让数据在失去焦点(点击其他地方)或回车时才会更新。

<div id="app">
	<input type="text" v-model.lazy="message">
</div>

11.5.2 number

当我们往input里输入数字,Vue会自动把这些数字当成是字符串。如果想要以数字的格式存储它,那就要添加number修饰符。

<div id="app">
	<input type="text" v-model.number="age">
</div>

11.5.3 trim

当我们往input里输入内容时,有时会输入一些空格。当我们想要删去其中的空格时,就要添加trim修饰符。

<div id="app">
	<input type="text" v-model.trim="message">
</div>

12. 组件化

如果我们把一个页面里所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,且不利于后期的管理、扩展。但如果我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

12.1 组件化实现和使用步骤

组件使用分成三个步骤:

1.创建组件构造器 Vue.extend()

2.注册组件 Vue.component()

3.使用组件

12.2 组件化的基本使用过程

首先创建组件构造器对象(注意这里template用的是tab键上方的引号来包含字符串):

<script src="../vue.js"></script>
<script>
	const cpnConstructor = Vue.extend({
     
     
        template:`
			<div>
				<h2></h2>
			</div>`
    })
</script>

然后注册组件:

Vue.component('my_cpn',cpnConstructor)
<!--第一个参数是组件名,第二个参数是我们之前创建的组件构造器对象名-->

注册完成之后,我们就可以这样使用组件:

<div id="app"><!--注意组件要放在挂载的实例之下-->
	<my_cpn></my_cpn>
</div>

12.3 全局组件和局部组件

像12.2里的组件注册完成后就是全局组件。局部组件定义如下,它只能在对应的实例下(下例中的app)起作用:

<script src="../vue.js"></script>
<script>
	const app = new Vue({
     
     
        el:'#app',
        components:{
     
     
            cpn:cpnConstructor<!--组件名:构造器名-->
        }
    })
</script>

在开发里,局部组件比较常用。

12.4 父组件和子组件的区分

我们可以把一个组件b放到另一个组件a的构造器里去,这样组件a在构造时同时会构造组件b。而如果要使用这个组件,只需要注册一个组件a即可,因为组件b已被包含在其中。

但是要注意的是,如果这里我们在id为app的div下使用,网页会报错并不予显示。这是因为,一个组件要想被使用,要么是一个全局组件(在全局被注册),要么在局部的Vue里被注册。父组件里的子组件无法直接使用,要想使用可以在局部Vue的components里再加入子组件的注册。

<div id="app">
    <cpn2></cpn2><!--会显示cpn1和cpn2里的内容-->
</div>
<script src="../vue.js"></script>
<script>
    const cpnC1 = Vue.extends({
     
     
        template:`
			<div>
				<h2>headline</h2>
			</div>`
    })
    
	const cpnC2 = Vue.extend({
     
     
        template:`
			<div>
				<h2>headline</h2>
			</div>`,
        components:{
     
     
            cpn1:cpnC1
        }
    })
    
    const app = new Vue({
     
     
        el:'#app',
        components:{
     
     
            cpn2:cpnC2
        }
    })
</script>

12.5 注册组件的语法糖写法

注册组件的过程可能有些繁琐,为了简化这个过程,Vue提供了注册的语法糖,省去了Vue.extend()的步骤,直接用一个对象来代替。

<script src="../vue.js"></script>
<script>
    const cpnC1 = Vue.extend({
     
     
        template:`
			<div>
				<h2>headline</h2>
			</div>`
    })
   Vue.components('cpn1',cpnC1)<!--普通写法-->
   Vue.components('cpn1',{
     
     
        template:`
			<div>
				<h2>headline</h2>
			</div>`
    })<!--全局组件语法糖,省去extend语句,把template直接写到第二个参数的位置,即直接把构造器的内容写到构造器的位置处-->
    
    const app = new Vue({
     
     
        el:'#app',
        components:{
     
     
            cpn2: {
     
     
        template:`
			<div>
				<h2>headline</h2>
			</div>`
  		  }
        }
    })<!--局部组件的语法糖-->
</script>

12.6 组件模板的抽离写法

每次在Vue里写模板很麻烦,所以可以把模板抽离出去写,再在全局组件处注册即可,不过记得挂载template

<!--第一种写法-->
<script type="text/x-template" id="cpn">
	<div>
		...
	</div>
</script>
<script src="../vue.js"></script>
<script>
	Vue.component('cpn',{
     
     
        template:'#cpn'
    })
</script>

<!--第二种写法,个人感觉比较方便-->
<template id="cpn">
	<div>
        ...
    </div>
</template>
<script src="../vue.js"></script>
<script>
	Vue.component('cpn',{
     
     
        template:'#cpn'
    })
</script>

12.7 组件中的数据存放问题

组件是一个单独功能模块的封装,这个模块有它自己的html模板,也有自己的data属性,组件中是不能直接访问Vue实例中(如挂载app的Vue)的data的。

其实,组件对象也有一个data属性,(也有methods等属性)只是它的data属性是个函数,而且这个函数返回一个对象,对象的内部保存着我们组件里要用到的数据。

<template id="cpn">
	<div>
        {
   
   {message}}
    </div>
</template>
<script src="../vue.js"></script>
<script>
	Vue.component('cpn',{
     
     
        template:'#cpn',
        data(){
     
     
            return {
     
     
                message:'hello'
            }
        }
    })
</script>

为什么组件里的data必须是个函数呢?因为如果data是一个对象,那么每次调用组件,这些组件实例的数据就会相互影响,因为事实上这些实例的data对象都是同一个。改动一个组件实例里的数据会让其他组件实例里的数据跟着改变。而如果我们使用的是函数,函数每次返回一个对象,实质上是在每次调用这个data函数的时候,data函数都会创建一个新的对象并返回,所以实例之间相互不会影响。

12.8 父子组件的通信(难点)

子组件不能引用父组件或者Vue实例的数据。但是在开发中,往往一些数据确实需要从上层传递到下层。比如说在一个页面中,我们从服务器请求到了很多数据,其中一部分书不是由大组件来展示,而是由小组件展示(比如淘宝的商品列表等),这时,不会让子组件再次发送请求,而是让大组件(父组件)把数据传递给小组件(子组件)。

父子组件间的通信方式有两种:一是父组件通过props向子组件传递,二是子组件通过(自定义)事件向父组件发送消息。

12.8.1 父组件向子组件传递数据

下面是父组件向子组件通过props传递数据的一个例子:

<div id="app"><!--3.在要使用子组件的地方,往子组件里加入如下通过v-bind实现的代码,把子组件props里定义的属性和父组件里的data动态绑定-->
    <cpn :childrenmovies="movies"></cpn>
</div>
<template id="cpn">
	<div><!--4.最后就可以在子组件里使用父组件的数据了-->
        {
   
   {childrenmovies}}
    </div>
</template>
<script src="../vue.js"></script>
<script>
	Vue.component('cpn',{
     
     
        template:'#cpn',
        props:['childrenmovies']<!--2.再在子组件里加入props属性,变量名字自定义,这里设置为字符串数组,方便传入多个父组件的data-->
        data(){
     
     
            return {
     
     
                message:'hello'
            }
        }
    })
    const app = new Vue({
     
     
        el:'#app',
        data:{
     
     
            movies:['海贼王','火影忍者']
        }<!--1.先在父组件里写好要传送的数据,注意Vue实例可以算是所有组件的父组件(root)-->
    })
</script>

​ props写成字符串形式可能有些复杂,我们还有更清晰的对象写法:

<script>
	Vue.component('cpn',{
     
     
        template:'#cpn',
        props:['childrenmessages']<!--第一种写法,字符串形式-->
		props:{
     
     
        	childrenmessages:{
     
     
        		type:String,
            	default:'aaa',
        		required:true<!--在使用子组件时必传的值,不传会报错-->
    		}
            childrenmovies:{
     
     
            	type:Array,
                default(){
     
     
        			return []
    			}
            }<!--当传递的类型时数组或者对象时,我们的默认值不能直接写成数组或是对象,而是要写一个函数,返回对应的类型-->
    	}<!--第二种形式,对象形式,在对象里我们还能定义传入数据的类型和默认值,比较常用-->
        data(){
     
     
            return {
     
     
                message:'hello'
            }
        }
    })
</script>

要注意的是,我们在写子组件的props的属性时,不能直接用驼峰标识的形式写,而是要转换成”-“。比如:childMessage要写成child-message.

12.8.2 子组件访问父组件

<div id="app">
    <cpn></cpn>
</div>
<template id="cpn">
	<div>
        <button @click="btnClick">
            button1
        </button>
    </div>
</template>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
        el:'#app',
        data:{
     
     
            message:'hello'
        },
        components:{
     
     
            cpn:{
     
     <!--子组件-->
                template:'#cpn',
                methods:{
     
     
                    btnClick(){
     
     
                        console.log(this.$parent);
        				console.log(this.$root);<!--直接访问根组件-->
                    }
                }
            }
        }
    })
</script>

13. 插槽(slot)——组件化高级运用

13.1 slot的基本使用

为每一次组件的使用定义更多的拓展性(每一次使用组件都在组件template的模板上拥有自己独特的地方)。

<div id="app">
    <cpn><button>按钮</button></cpn><!--此时,slot的位置被一个按钮代替(不管你写多少东西都会代替到这个slot的位置)-->
    <cpn></cpn>
</div>
<template id="cpn">
	<div>
        <h2>
        </h2>
    </div>
    <slot></slot><!--插槽-->
    <slot><span>默认值</span></slot><!--可以直接往slot里加默认值,则使用组件时默认显示该默认值-->
</template>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
        el:'#app',
        data:{
     
     
            message:'hello'
        },
        components:{
     
     
            cpn:{
     
     
                template:'#cpn'
            }
        }
    })
</script>

比如,我们在写网页的导航栏时,通常把导航栏封装成为一个组件。但是,每个网页的导航栏又有所不同。这时,我们只需要把这个导航栏组件分成几个插槽即可。如下图:

在这里插入图片描述

总的来说,就是把共性的东西写入组件,不同的东西所在的位置,就预留为插槽。

13.2 具名插槽

<div id="app">
    <cpn><span slot="left"></span></cpn><!--可以通过具名插槽来指定替换哪一个插槽,不然会引起混乱-->
    <cpn><button slot="middle"></button></cpn>
</div>
<template id="cpn">
	<slot name="left"></slot>
    <slot name="center"></slot>
    <slot name="right"></slot>
</template>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
        el:'#app',
        data:{
     
     
            message:'hello'
        },
        components:{
     
     
            cpn:{
     
     
                template:'#cpn'
            }
        }
    })
</script>

13.3 作用域

<div id="app">
	<cpn v-show="isShow"></cpn><!--此时isShow为true,因为它在大的vue实例里(父组件)-->
</div>
<template id="cpn">
	<p>
        abc
    </p>
    <span v-show="isShow">def</span><!--此时isShow为false,因为它在小的组件里-->
</template>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
        el:'#app',
        data:{
     
     
            message:'hello',
            isShow:true<!--父级作用域-->
        },
        components:{
     
     
            cpn:{
     
     
                template:'#cpn',
                data(){
     
     
                    return{
     
     
                        isShow:false
                    }
                }
            }
        }
    })
</script>

只能在自己的作用域里查找变量名。

13.4 作用域插槽

一句话总结作用域插槽:父组件替换插槽标签,内容由子组件决定。

我们可以看这个例子:子组件里有一个Language属性,子组件中默认以列表形式展示这个数组。但是当我们想要在父组件(大的div里)换一种方式展示数组时,我们不能直接用另一种方式替换slot,因为父组件无法直接使用子组件数据。所以要把子组件的数据绑定一个属性A(下面这个属性命名为data),然后再在父组件里用slot-scope这个属性,写{ {slot.A}}就可以使用到子组件的数据。

<div id="app">
    <cpn>
    	<template slot-scope="slot">
        	<span>{
   
   {slot.data.join('*')}}</span><!--这里要求这个数组以*号分隔,而不是以列表形式呈现-->
        </template>
    </cpn>
</div>
<template id="cpn">
	<div>
        <slot :data="Languages">
            <ul>
                <li v-for="item in Languages">{
   
   {item}}</li>
            </ul>
        </slot>
    </div>
</template>
<script src="..."></script>
<script>
	const app = new Vue({
     
     
        el:'#app',
        data:{
     
     
            message:'hello',
        },
        components:{
     
     
            cpn:{
     
     
                template:'#cpn',
                data(){
     
     
                    return{
     
     
                        Languages:['js','c++','python']
                    }
                }
            }
        }
    })
</script>

14. 模块化

14.1 闭包

编程语言中,比如 Java,是支持将方法声明为私有的,即它们只能被同一个类中的其它方法所调用。

而 JavaScript 没有这种原生支持,但我们可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。

下面的示例展现了如何使用闭包来定义公共函数,并令其可以访问私有函数和变量。这个方式也称为模块模式(module pattern):

var Counter = (function() {
    
    
  var privateCounter = 0;
  function changeBy(val) {
    
    
    privateCounter += val;
  }
  return {
    
    
    increment: function() {
    
    
      changeBy(1);
    },
    decrement: function() {
    
    
      changeBy(-1);
    },
    value: function() {
    
    
      return privateCounter;
    }
  }   
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

该共享环境创建于一个立即执行的匿名函数体内。这个环境中包含两个私有项:名为 privateCounter 的变量和名为 changeBy 的函数。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。

这三个公共函数是共享同一个环境的闭包。多亏 JavaScript 的词法作用域,它们都可以访问 privateCounter 变量和 changeBy 函数。

我们可以把它看成类似于Java的语法。Counter是一个类,而三个公共函数是它的private方法,privateCounter是它的private属性。

14.2 模块化原始雏形

由上面一小节可知,我们可以使用模块化来避免变量的冲突,具体操作如下:

var moduleA = (function(){
    
    
    var age = 20
    var flag = true
    function sum(num1,num2){
    
    
		return num1 + num2
    }
    var obj = {
    
    }//1.创建一个对象
    obj.age=age//2.把这个模块里的属性和方法(函数)都赋值给这个对象
    obj.flag=flag
    obj.sum=sum
    
    return obj//3.将这个对象返回,赋值给一个var变量moduleA,此时moduleA就是obj的一个引用,属性完全相同
})()

console.log(moduleA.age)//20
console.log(moduleA.sum(10,20))//30

14.3 模块化规范

幸运的是,前端模块化开发已经有了许多的规范以及对应的实现方案。比如commonJS、AMD、CMD、ES6的Modules

14.4 ES6的模块化实现

ES6中,最简单的导入导出这样实现:

假设同一个html文件有多个js文件需要引入:

html文件:

<script src="a.js" type="module"></script><!--type="module"说明把js文件当成模块化-->
<script src="b.js" type="module"></script>

js文件:

//a.js
var flag = true
function sum(num1,num2){
    
    
    return num1 + num2
}
//第一种导出方式
export{
    
    	//export用以导出
	flag,sum
}
//第二种导出方式,定义时直接导出
export var name = 'james';
export function mul(num1,num2){
    
    
    return num1 * num2
}
export class Person(){
    
    
    run(){
    
    
        ...
    }
}
//b.js
import {
    
    flag} from "./a.js"		//import用以导入
import {
    
    Person} from "./a.js"

import * as aa from "./a.js"//当需要导入的东西很多时,可以使用*,并为它取个名字(aa),这里其实实质上是把这些属性统合成了一个对象,aa就是它的名字,当需要用到属性的时候,用aa.+属性名就可以调用了
console.log(aa.name)

if(flag){
    
    
    ...
}
const p = new Person()
p.run()

15. Webpack

15.1 Webpack简介

Webpack简单来说,就是把我们项目里一些浏览器无法识别的文件(比如commonJS)打包,转换成浏览器可以识别的文件。

在这里插入图片描述

15.2 Webpack基本使用

我们项目的文件夹下有两个子文件夹:src(源码)和dist(发布),我们把使用模块化开发的js文件写好后,不能直接发布,因为浏览器无法解析我们的模块化(比如commonJS)。

所以,要通过

webpack ./src/main.js ./dist/bundle.js

这条命令,把src下的js文件打包成为dist下的bundle.js文件,然后再在html文件里引入bundle.js,即可实现模块化开发。

要注意的是,webpack会自动识别js文件之间的依赖,所以我们不必把每一个js文件都打包。(比如上面的这行命令,main.js里引入了a.js的导出,但我们不必在命令里写上a.js)。

15.3 其他准备工作

在创建完src、dist、index.html后,再要写一个webpack.config.js文件,最后通过npm init 命令创建package.json

webpack.config.js文件内容如下:

const path = require('path')//commonJS的语法
module.exports={
    
    
    entry:'./src/main.js'
    output:{
    
    
    	path:path.resolve(__dirname,'dist')//dirname是node里的一个全局变量
    	filename:'bundle.js'
	}
}

这样,我们就不用每次都在命令里指定输出的文件,只要用webpack这一个简单的命令或者”npm run build“(项目中更常用,在package.json里的script里加一个build:webpack)就可以实现打包。

16. Vue CLI(脚手架)

如果要开发大型项目,那么必须要用到Vue CLI
在这里插入图片描述

使用脚手架,要先安装node、npm、webpack

16.1 Vue CLI3创建项目和目录结构

创建项目:vue create 项目名称

目录结构:

node modules:存放node的一些包(通过npm安装的)

public:类似于VueCLI2里的static,最后会原封不动地放入dist文件夹

src:源代码

跑项目:npm run serve

16.2 路由 vue-router

在这里插入图片描述

16.2.1 前端路由

在这里插入图片描述

前端路由中,整个项目只有一个index.html,通过router来决定选择哪些组件渲染,就不用写多个html文件,直接在index后加上/…即可。

前端路由的核心就是:改变url,但是页面不进行整体的刷新。

16.2.2 vue-router的安装配置

用以访问设定路径,将路径和组件映射起来。在vue-router的单页面应用中,页面路径的改变就是组件的切换。

安装vue-router: npm install vue-router --save

我们在脚手架中可以勾选router,这样在创建完项目后自动会生成一个router文件夹(里面有一个index.js)

为了后续方便自己加入组件,我们要自己学会配置router(在index.js里):

import VueRouter from 'vue-router'
import Vue from 'vue'
//配置路由相关信息

Vue.use(VueRouter)//1.通过Vue.use(插件)来安装插件,这里的VueRouter是一个插件
//2.创建VueRouter对象
const routes=[
	{
    
    
	},
]
const router = new VueRouter({
    
    
	routes
})//配置路由和组件之间的关系

16.2.3 vue-router路由映射配置

首先要知道,写路由对应的组件,都是通过.vue文件来写的:

import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../components/Home'
import Home from '../components/About'

Vue.use(VueRouter)//1.通过Vue.use(插件)来安装插件,这里的VueRouter是一个插件

const routes=[
	{
    
    
		path:'/home',
		component:Home
	},
	{
    
    
		path:'/about',
		component:About
	}
]//2.建立路径和组件联系
const router = new VueRouter({
    
    
	routes
})//3.创建router实例

自己创建.vue文件(模板参见Helloworld.vue),比如这个Home.vue:

<template>
<div>
    <h2>
        homepage
    </h2>
    </div>
</template>
<script>
	export default{
        name:"Home"
    }
</script>
<style scoped></style>

这样,我们就把router和对应的组件联系起来了。那么怎么跳转到对应的url,从而显示不同的组件呢?我们需要在App.vue里写上以下内容:

<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<!--router-link类似于a标签-->
<router-view></router-view>
<!--router-view用于占位,表示切换过去的组件在页面的哪里显示出来-->

总结来说,使用vue-router的步骤有三步:

第一步:创建路由组件(.vue)

第二步:配置路由映射(组件和路径)(index.js)

第三步:使用路由:通过和

16.2.4 vue-router路由的默认值

默认情况下,我们刚进入一个网站时,总是希望显示网站首页,但是按照之前的写法,还得再点击一个a标签才行,默认没有显示首页组件。所以,我们需要重定向:

import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from '../components/Home'
import About from '../components/About'

Vue.use(VueRouter)

const routes=[
    {
    
    
        path:'/',//path配置的是根路径: /
        redirect:'/home'
    },//redirect就是指:当进入这个网页时,默认重定向至某某组件
	{
    
    
		path:'/home',
		component:Home
	},
	{
    
    
		path:'/about',
		component:About
	}
]
const router = new VueRouter({
    
    
	routes
})

17. axios

前端需要发送网络请求到服务器请求后端数据,这时就需要用到axios框架。

17.1 axios基本使用

首先,在vue项目里安装axios:npm install axios --save

然后,在main.js中,引入axios:

import axios from 'axios'
new Vue({
    
    ...})
         

引入axios后,就可以直接引用它。只要往axios里传入相关的网络配置即可,而config一般是对象类型:

axios(config)
axios({
    
    
    url:'http://39.97.183.73:8000/home/data'//默认get方式,也可以用methods来换其他方式(下图)
}).then(res => {
    
    
    console.log(res)
})//单一参数函数的简便写法,相当于function(res){console.log(res)}
axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.post(url[,data[,config]])

17.2 axios发送并发请求

使用axios.all,可以放入多个请求的数组。axios.all([])返回的是一个数组,使用axios.spread可以把[res1,res2]展开为res1,res2

axios.all([axios({
    
    
    url:''
}),axios({
    
    
    url:''
})]).then(results => {
    
    
    console.log(results[0]);
   	console.log(results[1])
})//第一种写法

axios.all([axios({
    
    
    url:''
}),axios({
    
    
    url:''
})]).then(axios.spread((res1,res2) =>{
    
    
    console.log(res1);
    console.log(res2)
}))//第一种写法

17.3 axios实例

在工作中,一个项目的不同页面往往被放在不同的服务器上。所以,使用全局的axios会有所不便。这时就需要用到axios的实例:

const instance1 = axios.create({
    
    
    baseURL:'',
    timeout:5000
})
const instance2 = axios.create({
    
    
    baseURL:'',
    timeout:1000
})//不同的实例可以设置独立的配置

instance1({
    
    
    url:'/home'
}).then(res=>{
    
    
    console.log(res)
})

instance({
    
    
    url:''
}).then(res=>{
    
    
    console.log(res)
})

17.4 模块封装

当我们的组件(.vue)文件多起来后,我们最好不要在每个.vue文件里都import axios,这样对第三方框架的依赖太大了——当这个框架不维护后,我们要一个个文件改动,很麻烦。

为了解决这个可能出现的问题,我们不妨在项目的src文件夹里新创建一个network文件夹,并在里面新建一个request.js,用来当作框架和我们项目的一个“中介”,内容如下:

import axios from 'axios'
export function request(config){
    
    
    const instance = axios.create({
    
    //1.创建axios实例
    baseURL:'',
    timeout:1000
    })
    
    //2.发送网络请求
    return instance(config)
}

在.vue文件里这样写:

import {
    
    request} from './network/request'
request({
    
    
	url:'/home',
	
}).then(res=>{
    
    
	...
})//把res返回到要处理的地方

17.5 拦截器

用于每次发送请求或得到响应后,做出相应处理。

instance.interceptors.request.use(config=>{
    
    //请求拦截
    console.log()
    return config//必须返回,不然就彻底拦下来了传不下去了,我们只是为了处理
},err=>{
    
    
    
})
instance.interceptors.reponse.use(config=>{
    
    //响应拦截
    console.log()
},err=>{
    
    
    
})


new Vue({
    
    ...})
         

引入axios后,就可以直接引用它。只要往axios里传入相关的网络配置即可,而config一般是对象类型:

axios(config)
axios({
    
    
    url:'http://39.97.183.73:8000/home/data'//默认get方式,也可以用methods来换其他方式(下图)
}).then(res => {
    
    
    console.log(res)
})//单一参数函数的简便写法,相当于function(res){console.log(res)}
axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.post(url[,data[,config]])

猜你喜欢

转载自blog.csdn.net/weixin_44765402/article/details/109005610