vue学习笔记
学习视频:https://www.bilibili.com/video/BV17j411f74d
鸣谢:感谢coderwhy老师
1.vue的options选项
options: 创建vue对象时传入的东西
var options = {
el:'#app',
data:{},
methods:function(){
}
}
var vue = new Vue(options)
或:
var vue = new Vue({
//选项
el:'#app',//sting|
data:{},
methods:{
}
})
el:
- 类型:string|HTMLElement(docuennt.queryElement())
- 作用:决定之后的vue实例会管理哪一个DOM
data:
- 类型:Object|Function(组件当中的data必须是一个Function)
- 作用:Vue实例对象的数据对象
methods:
- 类型:{[key:string]:Function}
- 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
方法和函数的区别
函数(function):是一段代码,使用时通过名字来调用.
//求和函数
function sum(a,b){
return a+b;
}
sum(1,2);
方法(method):特殊的函数,属于对象,通过对象.来调用
//定义一个对象,对象中定义的函数,叫做方法
var obj = {
sum:function(a,b){
return a+b;
}
}
obj.sum(1,2);
2.vue的生命周期函数
beforeCreate:
- vue实例初始化(init)之后,vue实例对象被创建之前调用的方法
created:
- vue实例对象创建完成,调用的方法
beforeMount:
- vue实例对象和DOM挂载之前调用的方法
mounted:
- vue实例对象已经和DOM挂载,调用的方法
beforeupdate:
updated:
beforeDestroy:
- vue实例对象销毁之前调用的方法
destroyed:
- vue实例对象已销毁之后调用的方法
3.vue的插值操作
mustache(双大括号)语法:{{}}
<h2>{{key}}</h2>
4.vue的指令
v-once: 不带任何参数,带有该指令的标签内的值不会被重新渲染,不再响应式变化
v-html: 将值中的标签进行解析,格式标签会被编译并展示,例<a>:表示链接格式
v-text: 相当于{{}}语法,接受一个string类型的key,但是会将标签内的值全部覆盖,一般不用
v-pre: 带有该指令的标签将不会被编译,显示原标签内容
v-cloak: 在浏览器未编译出mustache标签时,不展示出来,编译结束时再展示
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app' v-cloak>
<div>插值:{{message}}</div>
<div v-once>v-once指令:{{message}}</div>
<div>不使用v-html指令时:{{url}}</div>
<div v-html="url">v-html指令:{{url}}</div>
<div v-text="message">v-text:</div>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
message:"hello world",
url:'<a href="http://www.baidu.com">www.baidu.com<a>'
}
})
//app.message = "打开测试v-once";
</script>
</html>
v-bind:
- **作用: **动态绑定属性
- 缩写: :
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<div>不使用v-bind:<a href="http://www.baidu.com">百度一下</a></div>
<div>使用v-bind:<a v-bind:href="url">百度一下</a></div>
<div>v-bind简写:<a :href="url">百度一下</a></div>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
message:"hello world",
url:'http://www.baidu.com'
}
})
</script>
</html>
v-bind:class
- 作用: 动态绑定class
- 简写: :class
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.active{
color:red;
}
</style>
</head>
<body>
<div id='app'>
<div class="active">不使用v-bind:class</div>
<div v-bind:class="active">使用v-bind:class</div>
<div :class="active">v-bind:class简写:</div>
<div :class="{active:true,line:false}">通过对象语法方式控制class</div>
<div>
<div :class="{active:isActive,line:isLine}">通过对象语法加变量的方式控制class</div>
<button v-on:click="btnClick">点击按钮修改变量,控制class</button>
</div>
<div>
<div :class="getClasses()">将对象语法写到方法中调用</div>
<button v-on:click="btnClick">点击按钮修改变量,控制class</button>
</div>
<div>
<div :class="[active,line]">通过数组语法加变量的方式控制class</div>
</div>
<div>
<div :class="getClasses2()">将数组语法写到方法中调用</div>
</div>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
message:"hello world",
active:'active',
line:'aaaaa',
isActive:true,
isLine:false
},
methods:{
btnClick:function(){
this.isActive = !this.isActive;
},
getClasses:function(){
return {active:this.isActive,line:this.isLine};
},
getClasses2:function(){
return [this.active,this.line]
}
}
})
</script>
</html>
v-bind:style
- 作用: 动态绑定style
- 简写: :style
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.text{
font-size:50px;
color:red;
}
</style>
</head>
<body>
<div id='app'>
<div style="font-size: 50px">不使用v-bind:style</div>
<div v-bind:style="{fontSize:'50px'}">使用v-bind:style</div>
<!-- 值必须要带'',否则会被认为是变量 -->
<div :style="{fontSize:'50px'}">v-bind:style简写:</div>
<!-- 对象语法绑定,数组方法绑定 -->
<div :style="{fontSize:finalSize}">v-bind:style:对象语法,变量控制</div>
<div :style="getStyles()">v-bind:style:调用方法动态绑定style</div>
<div :style="[baseStyle,baseStyle1]">v-bind:style:动态绑定style数组语法</div>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
finalSize:'100px',
finalColor:'red',
baseStyle:{backgroundColor:'green'},
baseStyle1:{fontSize:'50px'}
},
methods:{
getStyles:function(){
return {fontSize:this.finalSize,backgroundColor:this.finalColor};
},
getClasses2:function(){
return [this.active,this.line]
}
}
})
</script>
</html>
v-on:
- 作用: 绑定事件监听器
- 简写: @
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<h2>{{counter}}</h2>
<button v-on:click="increment">+</button>
<button v-on:click="decrement">-</button>
<button @click="increment">简写+</button>
<button @click="decrement">简写-</button>
<br>
<!-- 当调用的函数不需要参数的时候小阔号可以省略 -->
<button @click="btnClick()">按钮1</button>
<button @click="btnClick">按钮1</button>
<br>
<!-- 如果需要参数在绑定的时候加上小括号和入参即可,
如果省略了小括号,则vue会默认将浏览器产生的event事件对象当作参数传入到方法中 -->
<button @click="btn2Click(123)">按钮2</button>
<button @click="btn2Click">按钮2</button>
<br>
<!-- 方法定义时,我们需要event对象,同时有需要其他参数 -->
<!-- 在调用方法时,如何手动的获取到浏览器参数的event对象:$event -->
<button @click="btn3Click(abc,$event)">按钮3</button>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
counter:0,
abc:123
},
methods:{
increment(){
this.counter++
},
decrement(){
this.counter--
},
btnClick(){
console.log("btn1Click")
},
btn2Click(abc){
console.log('-----------',abc);
},
btn3Click(abc,event){
console.log(abc,event);
}
},
})
</script>
</html>
v-on常用的修饰符
- .stop-调用event.stopPropagation():阻止事件冒泡
- .prevent-调用event.preventDefault():阻止默认事件
- .{keyCode|keyAlias}-只当事件是从特定键触发时才触发回调
- .capture-把默认的冒泡变为捕获
- .native-监听组件根元素的原生事件
- .once-只触发一次回调
5.计算属性
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<h2>{{getFullName()}}</h2>
<h2>{{fullName}}</h2>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
firstName:'zhangsan' ,
lastName : "lisi"
},
computed:{
fullName:function(){
return this.firstName + this.lastName;
}
},
methods:{
getFullName(){
return this.firstName + this.lastName;
}
},
})
</script>
</html>
计算属性和methods的对比
- 当变量没有变化时,多次调用计算属性时,计算属性取的是缓存的值
- methods每次调用都会调用,消耗更多的性能
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{getFullName()}}</h2>
<br>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
<h2>{{fullName}}</h2>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
firstName:'zhangsan' ,
lastName : "lisi"
},
computed:{
fullName:function(){
console.log("计算属性");
return this.firstName + this.lastName;
}
},
methods:{
getFullName(){
console.log("methods方法");
return this.firstName + this.lastName;
}
},
})
</script>
</html>
6.条件渲染
v-if:
作用: 根据表达式的值决定对应的元素以及子元素会不会渲染
通常和 v-else-if 和 v-else 连用
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<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>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
score:90
}
})
</script>
</html>
v-if 和 v-show的区别
- v-if 当条件为false时,包含v-if指令的元素,根本就不会存在于dom中
- v-show 当条件为false时,v-show只是给元素加了一个display:none 的行内样式
- 开发中如果元素需要频繁的切换展示或隐藏,使用v-show,如果只用于一次判断就展示或隐藏则使用v-if
- 注意,由于v-if会将元素从dom中删除和重新添加,所以频繁调用时如果使用v-if会消耗性能
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<h2 v-if="isShow" id='aaa'>{{message}}</h2>
<h2 v-show="isShow" id='bbb'>{{message}}</h2>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
message:'你好啊',
isShow:false
}
})
</script>
</html>
7.列表渲染(循环遍历)
v-for:
作用: 循环遍历数组或者对象数据注: 在遍历过程中,给对应的元素或者组件添加一个:key属性,用来做唯一标识
目的: 为了更高效的更新虚拟DOM
相关拓展: 当某一层有很多相同的节点时,也就是列表节点时,如果我们希望在节点中插入一个新的节点,vue通过Diff算法默认的执行方式是循环递归遍,如果每个节点都有一个唯一的标识,则可以通过对比key找到正确的位置区插入新的节点
- 遍历数组数据
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<!-- 1.在遍历过程中,没有使用索引值(下标值)-->
<ul>
<li v-for="item in names">{{item}}</li>
</ul>
<!-- 2.在遍历的过程中,获取索引值-->
<ul>
<li v-for="(item,index) in names">
{{index+1}}.{{item}}
</li>
</ul>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
names:['why','kobe','james','curry']
}
})
</script>
</html>
- 遍历对象数据
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<!--1.在遍历对象的过程中,如果只是获取一个值,那么获取到的是value-->
<ul><li v-for="item in info">{{item}}</li></ul>
<!--2.获取key和value,格式: (value,key),value在前,key在后-->
<ul><li v-for="(value,key) in info">{{value}}--{{key}}</li></ul>
<!--3.获取key和value和index,格式:(value,key,index) -->
<ul><li v-for="(value,key,index) in info">{{value}}--{{key}}--{{index}}</li></ul>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
info:{
name:'why',
age: 18,
height:1.81
}
}
})
</script>
</html>
数组中哪些方法是响应式的
vue是响应式的,修改数据的时候页面也会同步刷新,但当data是数组数据时,通过索引值修改数组中的元素的时候,页面不会刷新
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="btnClick">按钮</button>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
letters:['B','A','C','E','D']
},
methods:{
btnClick(){
<!--数组中push,pop,shift,unshift,splice,sort,reverse响应式的 -->
//1.push()方法:在数组最后添加一个或多个元素
this.letters.push('aaa')
//2.pop()方法:删除数组中的最后一个元素
this.letters.pop()
//3.shift()方法:删除数组中的第一个元素
this.letters.shift()
//4.unshift()方法:在数组最前面添加一个或多个元素
this.letters.unshift()
//5.splice(start)方法
//splice作用:删除元素/插入元素/替换元素
//删除元素:第二个参数传入你要删除几个元素,如果没有传就删除后面所有元素
this.letters.splice(start)
//替换元素:第二个参数,表示我们要替换几个元素,后面是用于替换前面的元素
this.lttters.splice(1,3,'m','n','l')
//插入元素,第二个参数,出入0,并且后面跟上要插入的元素
this.letters.splice(1,0,'x','y','z')
//6.sort()方法:排序
this.letters.sort()
//7.reverse()方法:反转
this.letters.reverse()
//通过索引值修改数组中的元素(非响应式的)
this.letters[0]='bbbb'
//解决方案一:通过splice方法
this.letters.splice(0,1,'bbbb')
//解决方案二:通过vue内部实现的函数
//set(要修改的对象,索引值,修改后的值)
Vue.set(this.letters,0,'bbbb')
}
}
})
</script>
</html>
8.表单绑定
v-model: 实现表单元素和数据的双向绑定
v-model的实现原理,通过v-bind绑定响应式数据,v-on:input监听响应式数据的变化
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<input type="text" v-model="message">{{message}}</input>
<!--v-model的实现原理-->
<input type="text" :value="msg" @input="msg=$event.target.value">{{msg}}</input>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
message:'你好啊',
msg:'你好啊'
}
})
</script>
</html>
- v-model 结合radio类型使用
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<label for="male">
<input type="radio" id="male" name="sex" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" name="sex" value="女" v-model="sex">女
</label>
<h2>您选择的性别是:{{sex}}</h2>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
sex:'男'
}
})
</script>
</html>
- v-model结合checkbox类型使用
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<!--1.checkbox单选框-->
<label for="agree">
<input type="checkbox" id="agree" v-model='isAgree'>同意协议
</label>
<h2>您选择的是{{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
<br>
<!--2.checkbox多选框-->
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="排球" v-model="hobbies">排球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
<h2>您的爱好是:{{hobbies}}</h2>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
isAgree:false,
hobbies:[]
}
})
</script>
</html>
- v-model结合select使用
- 单选:只能选中一个值
- v-model绑定的是一个值
- 选中一个option中的选项时,将对应的value赋值到绑定的值里
- 多选:可以选中多个值
- v-model绑定的是一个数组
- 当选中多个option选项时,将选中的option对应的value添加到数组中
- 单选:只能选中一个值
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<!--1.选择一个-->
<select name="abc" v-model="fruit">
<option value="苹果" >苹果</option>
<option value="香蕉" >香蕉</option>
<option value="榴莲" >榴莲</option>
<option value="葡萄" >葡萄</option>
</select>
<h2>您的选择的水果是:{{fruit}}</h2>
<!--2.选择多个-->
<select name="abc" v-model="fruits" multiple>
<option value="苹果" >苹果</option>
<option value="香蕉" >香蕉</option>
<option value="榴莲" >榴莲</option>
<option value="葡萄" >葡萄</option>
</select>
<h2>您的选择的水果是:{{fruits}}</h2>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
fruit:"香蕉",
fruits:[]
}
})
</script>
</body>
</html>
vue的值绑定: 动态的给value赋值
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<!--非值绑定,value的值是写死的-->
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="排球" v-model="hobbies">排球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
<h2>您的爱好是:{{hobbies}}</h2>
<!--值绑定,数据动态绑定的-->
<label v-for="item in originHobbies">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</laber>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
isAgree:false,
hobbies:[],
originHobbies:['篮球','足球','羽毛球','乒乓球','橄榄球']
}
})
</script>
</html>
v-model 修饰符的使用
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id='app'>
<!--1.修饰符:lazy,让原本响应式的数据在失去焦点或者回车的时候才更新-->
<input type="text" v-model.lazy="message">{{message}}
<!--2.修饰符:number,v-model没有使用number修饰符时,绑定的值一旦发生变化,在赋值时总是string类型的-->
<input type="number" v-model="age1">{{age1}}
<h2>{{age1}}-{{typeof age1}}</h2>
<!--使用了number修饰符时,值的类型一定为number类型-->
<input type="number" v-model.number="age2">{{age2}}
<h2>{{age2}}-{{typeof age2}}</h2>
<!--3.修饰符:trim-->
<input type="text" v-model="name">
<h2>您输入的名字是:{{name}}</h2>
</div>
</body>
<script>
const app = new Vue({
el:'#app',
data:{
message:"你好啊",
age1:0,
age2:0,
name:""
}
})
</script>
</html>
9.vue中的组件
1.组件的使用
- 创建组件构造器:Vue.extend()创建组件构造器
- 注册组件:Vue.component()方法注册组件
- 使用组件
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--3.使用组件-->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template:
`<div>
<h2>我是标题</h2>
<p>我是内容1</p>
<p>我是内容2</p>
</div>`
})
//2.注册组件
Vue.component('my-cpn',cpnC)
const app = new Vue({
el:"#app"
})
</script>
</body>
</html>
- 全局组件和局部组件
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--3.使用组件-->
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template:
`<div>
<h2>我是标题</h2>
<p>我是内容1</p>
<p>我是内容2</p>
</div>`
})
//2.注册组件(全局组件,意味着可以在多个Vue的实例下面使用)
//Vue.component('my-cpn',cpnC)
const app = new Vue({
el:"#app",
//局部组件(在vue实例内部挂载)
components:{
//cpn使用组件时的标签名
cpn:cpnC
}
})
const app2 = new Vue({
el:"#app2"
})
</script>
</body>
</html>
- 父组件和子组件
当组件与组件之间存在层级关系,而其中一种非常重要的关系就是父子组件的关系
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--3.使用组件-->
<cpn2></cpn2>
</div>
<script>
//1.创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template:
`<div>
<h2>我是标题1</h2>
<p>我是内容</p>
</div>`
})
//2.创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template:
`<div>
<h2>我是标题2</h2>
<p>我是内容</p>
<cpn1></cpn1>
</div>`,
components:{
cpn1:cpnC1
}
})
//root组件
const app = new Vue({
el:"#app",
//局部组件(在vue实例内部注册)
components:{
//cpn使用组件时的标签名
cpn2:cpnC2
}
})
</script>
</body>
</html>
- 组件的语法糖注册方式
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--3.使用组件-->
<my-cpn></my-cpn>
<cpn1></cpn1>
</div>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template:
`<div>
<h2>我是标题</h2>
<p>我是内容1</p>
<p>我是内容2</p>
</div>`
})
//2.注册组件
Vue.component('my-cpn',cpnC)
//2.全局组件注册的语法糖(效果等同于分开写)
Vue.component("cpn1",{
template:
`<div>
<h2>我是标题</h2>
<p>我是内容1</p>
<p>我是内容2</p>
</div>`
})
//root组件
const app = new Vue({
el:"#app",
})
</script>
</body>
</html>
- 组件模版的分离写法
- 方法一: 使用script标签,注意:类型必须是text/x-template
- 方法二: 使用template标签
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--3.使用组件-->
<cpn></cpn>
<cpn1></cpn1>
</div>
<!--1.script标签,注意:类型必须是text/x-template-->
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题1</h2>
<p>我是内容</p>
</div>
</script>
<!--2.template标签-->
<template id="cpn1">
<div>
<h2>我是标题2</h2>
<p>我是内容</p>
</div>
</template>
<script>
//1.注册一个全局组件
Vue.component('cpn',{
template:"#cpn"
})
Vue.component('cpn1',{
template:"#cpn1"
})
const app = new Vue({
el:"#app",
})
</script>
</body>
</html>
-
组件中的数据存放问题
- 组件是一个单独功能模块的封装,这个模块儿有属于自己的HTML模板,也有属于自己的数据data
- 组件不能直接访问Vue实例中的data,组件中有保存自己数据的地方
- 组件对象也有data属性,只是data属性的类型必须是一个function,而且这个函数返回一个对象,对象内部保存着数据
组件中的data属性为什么必须是function类型?
简单来讲就是js的作用域,js中作用域分为,全局作用域,局部作用域.全局作用域中的变量是全局可访问并修改的,而局部作用域中的变量,外部是无法随意访问修改的,js里函数内的变量的作用域是局部的,这样才不会导致多次使用相同的组件时候,数据之间产生影响
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>我是内容</p>
</div>
</template>
<script>
Vue.component('cpn',{
template:"#cpn",
//组件对象也有data属性,只是data属性的类型必须是一个function,而且这个函数返回一个对象,对象内部保存着数据
data(){
return {
title:"abc"
}
}
})
const app = new Vue({
el:"#app",
})
</script>
</body>
</html>
-
父子组件的通信
- 在组件中,使用选项props来声明需要从父级接收到的数据
- props的值有两种方式:
- 方式一:字符串数据,数组中的字符串就是传递时的名称
- 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等
父传子
<!doctype html> <html> <head> <meta chartset="UTF-8"> <title>Vue</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <cpn :cmovies="movies" :cmessage="message" :child-my-message="msg"></cpn> </div> <template id="cpn"> <div> <h2>{{cmessage}}</h2> <p>{{cmovies}}</p> <ul v-for="item in cmovies"><li>{{item}}</li></ul> </div> </template> <script> Vue.component('cpn',{ template:"#cpn", //方式一:字符串数据, //props:['cmovies',"cmessage"], //方式二:对象 props:{ //1.类型限制 //cmovies:Array, //cmessage:String //2.提供一些默认值,以及必传值 cmessage:{ type:String, default:'我很好', required:true }, //类型是对象或者数组时,默认值必须是一个函数 cmovies:{ type:Array, //default:[] vue2.5.17以下正确,之后必须是一个函数,否则报错 default(){ return [] } }, //子组件中定义的props的值如果是驼峰标识的,在使用的时候,需要用短横线分隔命名 //childMyMessage在使用时应该写为:child-my-message childMyMessage:{ type:String, default:'' } } }) const app = new Vue({ el:"#app", data:{ message:"你好啊", movies:["海王","海贼王","航海王"], msg:"aaaaa" } }) </script> </body> </html>
-
在组件中,通过自定义事件来进行子组件传递数据或者事件到父组件中
什么时候需要自定义事件
- 当子组件需要向父组件传递数据时,就需要用到自定义事件了
- v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件
自定义事件的流程:
- 在子组件中,通过$emit()来触发事件
- 在父组件中,通过v-on来监听子组件事件
子传父
<!doctype html> <html> <head> <meta chartset="UTF-8"> <title>Vue</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <cpn @item-click="cpnClick"></cpn> </div> <template id="cpn"> <div> <button v-for="item in categories" @click="btnClick(item)"> {{item.name}} </button> </div> </template> <script> const cpn = { template:"#cpn", data(){ return { categories:[ {id:'aaa',name:'热门推荐'}, {id:'bbb',name:'手机数码'}, {id:'ccc',name:'家用家电'}, {id:'ddd',name:'电脑办公'}, ] } }, methods:{ btnClick(item){ console.log("子组件触发自定义事件"); //自定义组件名必须全小写,因为v-on会将DOM中监听的事件自动转为全小写,此处定义为大写,无法生效 this.$emit("item-click",item); } } } const app = new Vue({ el:"#app", data:{ }, components:{ cpn }, methods:{ cpnClick(item){ console.log("父组件:"+item.name) } } }) </script> </body> </html>
-
父子组件的访问方式
- 父组件访问子组件:使用 c h i l d r e n 或 者 children或者 children或者refs
- 子组件访问父组件:使用$parent
父访问子( c h i l d r e n 或 者 children或者 children或者refs)
<!doctype html> <html> <head> <meta chartset="UTF-8"> <title>Vue</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <!doctype html> <html> <head> <meta chartset="UTF-8"> <title>Vue</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <cpn></cpn> <cpn ref="aaa"></cpn> <cpn></cpn> <button @click="btnClick">按钮</button> </div> <template id="cpn"> <div>我是子组件</div> </template> <script> const cpn = { template:"#cpn", data(){ return { name:'我是子组件的name' } }, methods:{ showMessage(){ console.log('showMessage'); } } } const app = new Vue({ el:"#app", data:{ }, components:{ cpn }, methods:{ btnClick(){ //1.通过$children获取子组件的对象 //组件变化,组件的下标改变,易取错,取所有组件时可用 <!-- console.log(this.$children); --> <!-- this.$children[0].showMessage(); --> this.$children[0].name; for(let c of this.$children){ console.log(c.name); c.showMessage(); } //2.通过refs获取子组件对象 //在需要获取数据的子组件上定义一个ref属性,通过属性取子组件对象 console.log('通过refs获取子组件对象:'+this.$refs.aaa.name) } } }) </script> </body> </html>
子访问父($parent)
<!doctype html> <html> <head> <meta chartset="UTF-8"> <title>Vue</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <cpn></cpn> </div> <template id="cpn"> <ccpn></ccpn> </template> <template id="ccpn"> <div> <div>我是子组件</div> <button @click="btnClick">按钮</button> </div> </template> <script> const ccpn = { template:"#ccpn", methods:{ btnClick(){ //1.子访问父组件$parent console.log(this.$parent); console.log(this.$parent.name); //2.访问根组件 console.log(this.$root); console.log(this.$root.message); } } } const cpn = { template:"#cpn", data(){ return{ name:'我是cpn组件的name' } }, components:{ ccpn } } const app = new Vue({ el:"#app", data:{ message:'我是根组件的message' }, components:{ cpn }, methods:{ } }) </script> </body> </html>
10.插槽slot
插槽:父组件在使用子组件时,想传递一些特殊的内容,则在定义子组件的时候可以定义一个slot标签(插槽),就可用于接收父组件传递的内容了
组件的插槽使封装的组件更加具有扩展性
(2.6.0之前的用法)
- 基本使用,插槽位置的内容可以被替换
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<!-- 1.插槽的基本使用<slot></slot> -->
<!-- 2.插槽的默认值: <slot>button</slot> -->
<!-- 3.如果有多个值,同时放到组件进行替换时,一起作为替换元素 -->
<div id="app">
<cpn></cpn>
<cpn><button>按钮</button></cpn>
<cpn><p>我是外部内容</p><button>按钮</button></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件内容</p>
<slot><button>默认按钮</button></slot>
</div>
</template>
<script>
const cpn = {
template:"#cpn",
}
const app = new Vue({
el:"#app",
components:{
cpn
},
})
</script>
</body>
</html>
- 具名插槽
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<cpn><span slot="center">标题</span></cpn>
<cpn><button slot="left">返回</button></cpn>
<cpn><button slot="right">搜索</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>
<script>
const cpn = {
template:"#cpn",
}
const app = new Vue({
el:"#app",
components:{
cpn
},
})
</script>
</body>
</html>
- 作用域插槽(父组件替换插槽的标签,但是内容由子组件提供的)
什么是编译作用域: 父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 这里的isShow的变量取的是app模版里的isShow,子组件会被加载出来 -->
<cpn v-Show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<p>哈哈哈</p>
<!-- 这里的isShow的变量取的是组件模版里的isShow,所以下面的按钮无法被渲染出来 -->
<button v-show="isShow">按钮</button>
</div>
</template>
<script>
const cpn = {
template:"#cpn",
data(){
return {
isShow:false
}
}
}
const app = new Vue({
el:"#app",
data:{
message:'你好啊!',
isShow:true
},
components:{
cpn
},
})
</script>
</body>
</html>
作用域插槽实例
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<cpn></cpn>
<!-- 错误示范,编译作用域,此处无法取到子组件内的pLanguage -->
<!-- <cpn><span v-for="item in pLanguages"></span></cpn> -->
<cpn>
<!-- 定义了一个slot-scope,slot是一个引用,目的是获取子组件中的pLanguages -->
<template slot-scope="slot">
<!-- 父组件替换插槽的标签,但是内容由子组件提供的 -->
<span v-for="item in slot.data">{{item}} - </span>
<br>
<span>{{slot.data.join(" - ")}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script>
const cpn = {
template:"#cpn",
data(){
return {
pLanguages:['JavaScript','C++','Java','C#','Python','Go','Swift']
}
}
}
const app = new Vue({
el:"#app",
data:{
message:'你好啊!',
isShow:true
},
components:{
cpn
},
})
</script>
</body>
</html>
2.6.0之后的插槽与2.6.0之前的插槽的区别
-
2.6.0之前使用的是slot 和slot-scope 两个属性,slot用来选择使用的插槽是哪一个,slot-scope定义一个引用,用来接收子组件传过来的值
-
2.6.0后,使用v-slot指令替代了slot和slot-scope,写法为:v-slot:插槽名=引用名
<!doctype html>
<html>
<head>
<meta chartset="UTF-8">
<title>Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 例1和例2 为2.6.0之前使用普通插槽的两种用法,slot属性可写,可不写,带有slot属性则其值必为:default -->
<cpn >
<template slot-scope="slotProps">
例1:{{slotProps.data}}
</template>
</cpn>
<cpn >
<template slot="default" slot-scope="slotProps">
例2:{{slotProps.data}}
</template>
</cpn>
<!-- 2.6.0后slot和slot-scope属性被v-slot指令替代了 -->
<!-- 例3和例4 为2.6.0后使用普通插槽的两种用法,v-slot之后default可写可不写 -->
<cpn >
<template v-slot="slotProps">
例3:{{slotProps.data}}
</template>
</cpn>
<cpn >
<template v-slot:default="slotProps">
例4:{{slotProps.data}}
</template>
</cpn>
<!-- 例5和例6 为2.6.0后使用具名插槽的两种用法,v-slot之后写要使用的插槽的名字,=之后是插槽所有属性的引用 -->
<cpn >
<template slot="title" slot-scope="slotProps">
例5:{{slotProps.data}},{{slotProps.info}},{{slotProps.msg}}
</template>
</cpn>
<cpn >
<template v-slot:title="slotProps">
例6:{{slotProps.data}},{{slotProps.info}}
</template>
</cpn>
</div>
<template id="cpn">
<div>
<!-- 第一种没有定义name属性的,就是一个普通的插槽 -->
<slot :data="msg1"></slot>
<!-- 第二种定义了name属性的,这是一个具名插槽 -->
<slot name="title" :data="msg2" :info="msg3" msg="1111"></slot>
</div>
</template>
<script>
const cpn = {
template:"#cpn",
data(){
return {
msg1:'我是msg1',
msg2:'我是msg2',
msg3:'info数据'
}
}
}
const app = new Vue({
el:"#app",
data:{
message:'你好啊!',
},
components:{
cpn
},
})
</script>
</body>
</html>