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

发布时间:2024-11-08 10:01

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:'xxx'
		}
	})
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:'xxx'
		}
	})
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……}">
        {{message}}
    h2>
    <h2 v-bind:class="{active:isActive, line:isLine}">
        {{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'}">
        {{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(){

	}
}

const obj = {
	run(){
	
	}
}

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 

7.2 v-on的参数传递

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

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

<div id="app">
    <button v-on:click="Click1">
        按钮1
    button>
    <button v-on:click="Click2(123,$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>
        <li v-for="item in info">{{item}}li>
    ul>
    <ul>
        <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>
    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')

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

以下是一个购物车的案例,综合运用了v-if,v-for,v-bind,v-on等知识,同时用到了过滤器。目标是这样的:
vue学习笔记(b站王红元老师网课笔记)_第1张图片

<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">
                        -
                    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)
a.reduce(function(preValue,n){
	return 1
},0)



let total = newNums2.reduce(function(preValue,n){
	return preValue+n
},0)


let total = this.books.reduce(function(preValue,book){
	return preValue+book.price*book.quantity
},0)

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

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">
    {{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">
    	<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">appleoption>
        <option value="orange">orangeoption>
        <option value="banana">bananaoption>
    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>
        <option value="apple">appleoption>
        <option value="orange">orangeoption>
        <option value="banana">bananaoption>
    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:`
			

`
})
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>
div>
<script src="../vue.js">script>
<script>
    const cpnC1 = Vue.extends({
        template:`
			

headline

`
}) const cpnC2 = Vue.extend({ template:`

headline

`
, 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:`
			

headline

`
}) Vue.components('cpn1',cpnC1)<!--普通写法--> Vue.components('cpn1',{ template:`

headline

`
})<!--全局组件语法糖,省去extend语句,把template直接写到第二个参数的位置,即直接把构造器的内容写到构造器的位置处--> const app = new Vue({ el:'#app', components:{ cpn2: { template:`

headline

`
} } })<!--局部组件的语法糖-->
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">
    <cpn :childrenmovies="movies">cpn>
div>
<template id="cpn">
	<div>
        {{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>
    <cpn>cpn>
div>
<template id="cpn">
	<div>
        <h2>
        h2>
    div>
    <slot>slot>
    <slot><span>默认值span>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>
div>
<template id="cpn">
	<p>
        abc
    p>
    <span v-show="isShow">defspan>
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>
<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)打包,转换成浏览器可以识别的文件。

vue学习笔记(b站王红元老师网课笔记)_第2张图片

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

vue学习笔记(b站王红元老师网课笔记)_第3张图片

16.2.1 前端路由

vue学习笔记(b站王红元老师网课笔记)_第4张图片

前端路由中,整个项目只有一个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:




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

首页
关于



总结来说,使用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]])

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号