b站的最全最新Vue、Vuejs教程,从入门到精通
2021.10.28
1.vue的初体验
<div id="app">
<div>{{message}}</div>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el:'#app',//用于挂载要管理的元素
data:{//定义数据
message:''
},
methods:{
}
})
</script>
el:
类型:string | HTMLElement
决定之后vue实例会管理哪一个DOM
data:
类型:Object | Function(组件中data必须是一个函数)
Vue实例对应的数据对象
methods:
类型:{[key:string]:Function}
定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
计数器案例
<div id="app">
<h2>当前计数{{counter}}</h2>
<!-- @click是v-on:click的语法糖(缩写),可以写变量,也可以写函数 -->
<button @click="counter++">+</button>
<button @click="counter--">-</button>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
// add(){}是add:function(){}的语法糖
add() {
this.counter++;
},
sub() {
this.counter--;
}
}
})
</script>
2.vue中的MVVM
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。
View层
视图层,通常是DOM层,给用户展示各种信息
Model层
数据层,可能是自定义的数据,也有可能是从服务器,网络请求下来的数据
ViewModel层
视图模型层,是view和model沟通的桥梁
它实现了Data Binding,数据绑定,将model中的改变实时反应到view中
它实现了DOM Listener,就是DOM监听,当DOM发生一些事件时,可以监听改变对应的data
3.生命周期
Vue生命周期(11个钩子函数)_宅男呀-CSDN博客_vue生命周期11个钩子函数
帮助理解vue的生命周期
生命周期的钩子函数的this会默认指向vue实例
1.在beforCreate钩子函数执行之前初始化事件以及生命周期
2.beforeCreate(){
不能获取data中的数据,也不能操作DOM
}
3.给Vue的实例注入数据,进行数据监听
4.created(){
可以在created中发送请求,可以获取data中的数据,不可以操作DOM
}
5.beforeMount(){}
6.mounted(){
挂载:
把VUE实例生成的虚拟的DOM转成真实的DOM,放在了页面中,编译出的DOM把原有的DOM替换
可以获取最终的DOM
}
7.当数据更新时,会调用beforeUpdate 和updated钩子函数,上面的不再运行
8.beforeUpdate(){}
9.updated(){
数据更新,虚拟的DOM更新,然后更新真实的DOM;最后触发这个函数
}
10.beforeDestroy(){}
11.destroyed(){销毁}
用得比较多的是created和mounted,一般在created发送请求
4.指令语法
4.1.Mustache(双大括号)
mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式
<div id="app">
<h2>{{message}}</h2>
<h2>{{firstName + lastName}}</h2>
<h2>{{firstName +' ' +lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{counter*2}}</h2>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
firstName: 'first',
lastName: 'last',
counter: 100
}
})
</script>
4.2.v-once、v-html、v-pre、v-cloak插值指令的使用
v-once后面不需要跟任何表达式,元素和组件只渲染一次
<h2 v-once>只渲染一次</h2>
v-html能够解析指令,把需要解析的指令放到v-html=""里面
<div id="app">
<h2>{{url}}</h2>
<h2 v-html="url"></h2>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
url: '<a href="http://www.baidu.com">百度一下</a>'
}
})
</script>
不想要用mustache语法的大括号,可以使用v-pre
<h2 v-pre>{{data}}<h2>
防止插值闪烁,v-cloak
5.v-bind动态绑定
如果需要动态绑定属性,使用v-bind,经常用在a元素的href和img的src属性上
v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值
v-bind的语法糖 :
<div id="app">
<a v-bind:href="ahref">百度一下</a>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
ahref: 'http://www.baidu.com'
})
</script>
5.1.v-bind可以动态绑定class属性(对象语法)
<div id="app">
<h2 class="title" :class="{active: isActive, line: isLine}">{{message}}</h2>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message:'msg',
isActive: true,
isLine: true
}
})
</script>
通过isActive和isLine的布尔值(true false)来决定是否要将active和line加入class中
<h2 class="title" :class="{active: isActive, line: isLine}">{{message}}</h2>
title的class名是一定有的,而active和line是可以根据布尔值决定的
5.2.v-bind可以动态绑定class属性(数组语法)
<h2 class="title" :class="['active', 'line']">{{message}}</h2>
用的比较少
5.3.v-bind动态绑定style(对象语法)
在写css属性名的时候,可以使用驼峰式(camelCase)或者短横线分隔(kebab-case,记得要用单引号括起来)
<div id="app">
<!-- <h2 :style="{key(属性名): value(属性值)}">{{message}}</h2> -->
<h2 :style="{fontSize: '50px'}">{{message}}</h2>
<!-- 50px必须要加单引号,不然就当做一个变量去解析 -->
<h2 :style="{fontSize: finalSize +'px' }">{{message}}</h2>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!',
finalSize: 50
}
})
</script>
6.计算属性
有的时候需要对数据进行一些转化后再展示,或者需要将多个数据结合起来进行显示
计算属性computed在多次调用的时候只会调用一次,而方法methods调用几次就用几次,性能computed更高一点
6.1.计算属性的简单应用
<div id="app">
<h2>{{fullName}}</h2>
<!-- 可以直接使用,不需要加() -->
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!',
firstName: 'first',
lastName: 'last'
},
//computed计算属性
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
})
</script>
6.2.计算属性的复杂应用
<div id="app">
<h2>总价格{{totalPrice}}</h2>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [{
id: 110,
name: 'a',
price: 110
}, {
id: 111,
name: 'b',
price: 77
}, {
id: 112,
name: 'c',
price: 98
}, {
id: 113,
name: 'd',
price: 200
}]
},
computed: {
totalPrice() {
let result = 0
for (let i = 0; i < this.books.length; i++) {
result += this.books[i].price
}
return result
}
}
})
</script>
2021.10.29
6.3.计算属性的setter和getter
computed: {
// fullName() {
// return this.firstName + ' ' + this.lastName
// }
//因为fullName是当作属性使用的,所以不是函数,而是一个对象
fullName:{
get:function(){
return this.firstName + ' ' + this.lastName
},
set:function(){
}
}
}
计算属性中其实是有getter和setter的,但是极少使用,于是只使用get就足够了
默认是使用get方法,所以索性将get化简掉
所以计算属性在使用的时候不需要加()
7.ES6语法
7.1.let/var定义变量
var可以看成是JavaScript语言设计上的错误,为了考虑到兼容性,Brendan Eich添加了let关键字
let有块级作用域,而var没有块级作用域
7.2.块级作用域
没有块级作用域引起的问题:if的块级
var func;
if (true) {
var name = 'sss';
func = function() {
console.log(name);
}
}
name = 'aaa'
func()
打印的结果为aaa,因为var定义的name没有块级作用域,所以在外面也可以修改name的值
没有块级作用域引起的问题:for的块级
想要写一个按钮点击事件,点击哪个按钮就显示第几个按钮被点击了
var btns = document.getElementByTagName('button');
for(var i = 0;i<btns.length;i++){
btns[i].addEventListener('click',function(){
console.log('第'+ i + '个按钮被点击')
})
}
但是无论点击哪一个,最后的结果都是点击了最后一个按钮
以前的解决方案,闭包 (函数有作用域,所以闭包可以解决)
var btns = document.getElementByTagName('button');
for(var i = 0;i<btns.length;i++){
(function(i){
btns[i].addEventListener('click',function(){
console.log('第'+ i + '个按钮被点击')
})
})(i)
}
JavaScript之闭包,给自己的Js一场重生(系列七)_余光的博客-CSDN博客
let解决了if和for没有作用域的问题
7.3.const的使用
当我们的修饰的标识符不会被再次赋值,可以用const保证数据的安全性
在ES6开发中,优先使用const,只有需要改变某个标识符的时候才使用let
一旦给const修饰的标识符赋值之后,不能修改
在使用const定义标识符,必须进行赋值
常量的含义是指向的对象不能修改,但是可以修改对象内部的属性,如下
const obj = {
name:'aa',
age:18
}
console.log(obj);
//{name:"aa", age:18}
obj.name = 'bb';
obj.age = 17;
console.log(obj);
//{name:"bb", age:17}
const只是保存的内存地址不能修改
7.4.对象字面量的增强写法
<script>
const name = 'aa';
const age = 18;
const obj ={
name:name,
age:age,
}
console.log(obj);
//ES6的写法
const obj ={
name,
age,
}
console.log(obj);
</script>
8.v-on事件监听的使用
使用v-on监听用户触发的事件,如点击、拖拽、键盘事件等
语法糖 @
8.1.v-on参数传递问题
如果该方法不需要额外参数,方法后面的()可以不添加
如果方法有一个参数,默认将原生事件event参数传递进去
如果既需要传入某个参数,又需要原生事件event,可以通过$event传入
<div id="app">
<h2>点击次数{{counter}}</h2>
<button @click="add">+1</button>
<button @click="addTen(10,$event)">+10</button>
//传入了10参数,所以默认的event事件要加$
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
add(e) {
console.log(e);//会打印出点击事件
this.counter++;
},
addTen(ten, event) {
console.log(event);
this.counter += ten;
}
}
})
</script>
8.2.v-on修饰符
.stop阻止事件冒泡
<div @click="divClick">aaa
<button @click.stop="btnClick">按钮</button>
</div>
.prevent阻止默认行为
<form action="a">
<input type="submit" value="提交" @click.prevent="submitClick">
</form>
.native 监听组件根元素的原生事件
.once 只触发一次回调
9.v-if和v-else的使用
<div id="app">
<p v-if="score>=90">优秀</p>
<p v-else-if="score>=80">良好</p>
<p v-else-if="score>=60">及格</p>
<p v-else>不及格</p>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!',
score: 89
}
})
</script>
9.1.登陆切换案例
<div id="app">
<span v-if="isUser">
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="用户账号" key="key1">
</span>
<span v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="用户邮箱" key="key2">
</span>
<button @click="isUser = !isUser">切换类型</button>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isUser: true
}
})
</script>
在没有加上key的时候会出现一个问题,在用户账号输入框内输入完切换类型后,用户邮箱里面的内容还是用户账号输入框里面的内容
这是因为vue在DOM渲染的时候,出于性能的考虑,会尽可能复用已经存在的元素
可以添加key属性
10.v-show的使用
v-show和v-if用法类似
<h2 v-show="isShow">{{msg}}</h2>
v-if:当条件为false时,包含v-if指令的元素,根本就不会存在在DOM中
v-show:当条件为false时,v-show只是给我们的元素添加一个行内样式,display:none
显示和隐藏切换频繁时,使用v-show
只有一次切换时,通过v-if切换
11.v-for循环遍历使用
11.1.v-for遍历数组和对象
<div id="app">
<!-- 1.在遍历数组过程中不使用索引值 -->
<ul>
<li v-for="item in name">{{item}}</li>
</ul>
<!-- 2.在遍历数组过程中使用索引值 -->
<ul>
<li v-for="(item, index) in name">{{index+1}}--{{item}}</li>
</ul>
<!-- 3.遍历对象value -->
<ul>
<li v-for="(item, key, index) in info">{{index}}--{{key}}--{{item}}</li>
<!-- 里面的内容最重要放在前面item -->
</ul>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: ['a', 'b', 'c', 'd'],
info: {
name: 'a',
height: 188,
age: 18
}
}
})
</script>
11.2.v-for的key
官方推荐在使用v-for的时候添加一个key属性,key的作用主要是为了高效地更新虚拟DOM
<li v-for="(item, index) in array" :key="index">{{item}}</li>
尽量绑定具有唯一性的值
12.响应式数组方法
通过索引值修改数组的元素不是响应式的
push() 在最后面添加
pop()在最后面删除
shift()在最前面删除
unshift()在最前面添加
splice()可以删除元素/插入元素/替换元素
splice(从第几个开始,几个元素,要替换的元素)
sort()排序
reverse()反转
2021.10.30
13.JavaScript高阶函数的使用
13.1.javascript循环高阶写法
正常写法
for (let i = 0; i < this.books.length; i++) {
totalPrice += this.books[i].price * this.books[i].count
}
for循环简便写法 in
for (let i in this.books) {
totalPrice += this.books[i].price * this.books[i].count
}
for循环直接拿到数组里面的内容 of
for (let item of this.books) {
totalPrice += item.price * item.count
}
13.2.高阶函数filter过滤
const nums = [10, 20, 111, 222, 444, 40, 50]
需求:取出所有小于100的数字
可以使用filter函数来实现
filter必须传入一个回调函数,必须返回一个Boolean值
当返回的Boolean值为true时,函数内部会自动将这次回调的n加入到新的数组中
false时,函数内部会自动过滤掉这次的n
//let newNums = nums.filter( function(n){ return n<100 })
let newNums = nums.filter((n) => n < 100)
//可以使用箭头函数
console.log(newNums);
//10 20 40 50
13.3.高阶函数map遍历
const nums = [10, 20, 40, 50]
需求:将所有的数字乘2并且输出
可以使用map函数来实现,map函数会遍历数组
//let new2Nums = nums.map( function(n){ return n * 2})
let new2Nums = nums.map((n)=>n*2)
console.log(new2Nums)
//20 40 80 100
13.4.高阶函数reduce汇总
reduce作用对数组中所有的内容进行汇总
const nums = [10, 20, 40, 50]
reduce有两个参数
nums.reduce(回调函数(),汇总之前的初始值)
new3Nums = nums.reduce((preValue, n) => preValue + n , 0)
console.log(new3Nums);//120
14.v-model双向绑定
14.1.v-model的基本使用
<div id="app">
<input type="text" v-model="message">{{message}}
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '我实现了双向绑定'
}
})
</script>
14.2.v-model通过v-on和v-bind实现
<div id="app">
<input type="text" :value="message" @input="valueChange">{{message}}
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '我实现了双向绑定'
},
methods: {
valueChange(event) {
this.message = event.target.value;
}
}
})
</script>
先v-bind绑定data里面的message数据,注意是input的value属性
接着想要让监听到value数据的变化,就使用v-on的input写一个函数监听,注意时input而不是click,取到event原生对象,event.target.value可以取到变化的值
2021.11.1
14.3.v-model.lazy修饰符的使用
在input的时候不想要实时绑定,想要在敲回车或者失去焦点的时候绑定,可以使用.lazy修饰符
<input type="text" v-model.lazy="message">
<h2>{{message}}</h2>
14.4.v-model.number修饰符的使用
在输入框,默认情况下,无论我们输入的是字母还是数字,都会被当成字符串来处理
number修饰符可以让在输入框中输入的内容自动转成数字类型
<input type="text" v-model.number="number">
<h2>{{number}}--{{typeof number}}</h2>
14.5.v-model.trim修饰符的使用
去除掉两边的空格
15.组件化的实现
组件化思想
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展
但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于子级这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了
组件的使用分成三个步骤
(1)创建组件构造器 调用Vue.extend()方法创建组件构造器
(2)注册组件 调用Vue.component()方法注册组件
(3)使用组件 在Vue实例的作用范围内使用组件
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>>
</div>
<script src="../vue.js"></script>
<script>
// 1.创建组件构造器
const component = Vue.extend({
template:`
<div>
<p>我是一个组件</p>
</div>
`
})
// 2.注册组件,并且定义组件标签的名称
Vue.component('my-cpn',component)
const app = new Vue({
el: '#app',
})
</script>
15.1.全局组件和局部组件
//全局组件
Vue.component('my-cpn', component)
//局部组件
const app = new Vue({
el: '#app',
components: {
'my-cpn': component
//前面是要使用时名字,后面是组件实例
}
})
如果是全局组件,就代表在哪里都可以使用,局部组件仅可以在相应的vue实例中使用
15.2.组件模板分离写法
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是模板</h2>
</div>
</template>
<script src="../vue.js"></script>
<script>
Vue.component('cpn', {
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!'
}
})
</script>
15.3.为什么组件data必须是函数
想要使用多个组件,但是如果组件的data不是函数的话,数据就会使用同一个
也就是说在使用一个组件的时候,会影响到另外一个组件
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是模板{{count}}</h2>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
</template>
<script src="../vue.js"></script>
<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
count: 0
}
},
methods: {
add() {
this.count++
},
sub() {
this.count--
}
}
})
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!'
}
})
</script>
data使用了函数写法,就不会出现数据公用的问题了
15.4.父传子props
<div id="app">
<cpn :cmessage="message"></cpn>
</div>
<template id="tcpn">
<div>
111 {{cmessage}}
</div>
</template>
<script src="../vue.js"></script>
<script>
const cpn = {
template: '#tcpn',
props: {
cmessage: {
type:String,
default:'aaa',
required:true,
}
}
}
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!'
},
components: {
cpn
}
})
</script>
1.在app父组件中注册组件cpn
2.在<template>中写组件元素和id
3.cpn对象中的template中写上对应的id
4.data取到数据info
5.在cpn对象中props属性写需要传入的信息类型等
6.在<template>编写需要用到的信息cmessage
7.在正式使用的时候用v-bind绑定cpn对象中props里的数据=app对象中data的数据
props有三种方式
props:['a','b']//直接写变量的名字
//类型限制
props:{
message:Array,
moive:String,
}
props:{
//对象的形式,传进默认值
cmessage:{
type:String,
default:'aaaa',
required:true,//是否必写
}
//如果类型是对象或者数组时,默认值必须是一个函数
cmoives:{
type:Array,
default(){
return []
}
}
}
2021.11.3
15.5.子传父自定义事件
<div id="app">
<cpn @itemclick="cpnclick"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnclick(item)">
{{item.name}}
</button>
</div>
</template>
<script src="../vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data() {
return {
categories: [{
id: 'aaa',
name: '热门推荐'
}, {
id: 'bbb',
name: '冷门推荐'
}, {
id: 'ccc',
name: '家用推荐'
}, ]
}
},
methods: {
btnclick(item) {
this.$emit('itemclick', item)
}
}
}
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!'
},
components: {
cpn
},
methods: {
cpnclick(item) {
console.log(item)
}
}
})
</script>
1.cpn对象写categories数据
2.在template模板中引用categories,再@click,在cpn的methods监听事件,并且将自定义事件的名称发射出去this.$emit('itemclick',item)
3.在父组件的cpn组件中使用自定义事件itemclick,监听这个自定义事件用cpnclick的名字,在app的methods处理cpnclick
15.6.父访问子-children-refs
一般不会使用$children去访问子组件里面的东西
refs的使用
<div id="app">
<cpn ref="aaa"></cpn>
<button @click="btnclick">按钮</button>
</div>
<template id="cpn">
<div>
我是子组件
</div>
</template>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello,vue!'
},
methods: {
btnclick() {
console.log(this.$refs.aaa.name)
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name'
}
}
}
}
})
</script>
16.插槽slot的基本使用
插槽的目的是 让我们原来的设备具备更多的扩展性
如何去封装这类组件?
提取共性,保留不同
最好的封装方式是将共性抽取到组件中,将不同暴露为插槽
一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容
搜索框,文字还是菜单,由调用者自己来决定
<div id="app">
<cpn><i>呵呵呵</i></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>哈哈哈哈</p>
<slot><button>按钮</button></slot>
</div>
</template>
slot里面可以编写html标签表示默认,如果父组件在使用的时候添加了其他的标签,就会替代掉
16.1.具名插槽的使用
<div id="app">
<cpn><button slot="left">牛牛</button></cpn>
</div>
<template id="cpn">
<div>
<slot name="left">
<span>左边</span>
</slot>
<slot name="center">
<span>边</span>
</slot>
<slot name="right">
<span>右边</span>
</slot>
</div>
</template>
给slot标签添加上name,就可以准确地替换掉slot="left"的插槽内容