VUE学习笔记1
1. Vuejs的内容概述
-
Vue是一个渐进式的框架(渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来丰富的交互体验)
-
Vue有很多特点和Web开发中常见的高级功能
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
-
官方网站:https://vuejs.bootcss.com/guide/
-
安装vue的方法
1.1 Vuejs初体验
<body>
<div id="app">
<h2>{{message}}</h2>
<h1>{{name}}</h1>
</div>
<div>{{message}}</div>
<script src="../js/vue.js"></script>
<script>
// 定义变量let 定义常量const
let app=new Vue({
el:'#app', // 用于挂载要管理的元素
data:{ // 定义数据
message:'你好啊,栀虞!',
name:'coderwhy'
}
})
</script>
</body>
1.2 Vue列表展示
<body>
<div id="app">
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:'#app',
data:{
message:'你好啊',
movies:['星际穿越','大话西游','少年派','盗梦空间','天赋异禀']
}
})
</script>
</body>
案例:计数器
<body>
<div id="app">
<h2>当前计数: {{counter}}</h2>
<!--<button v-on:click="counter++">+</button>
<button v-on:click="counter--">-</button>-->
<button v-on:click="add">+</button>
<button v-on:click="sub">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
// 语法糖:简写
const app=new Vue({
// el:document.querySelector('#app'),
el:'#app',
data:{
counter:0
},
methods:{
add:function (){
console.log('add被执行');
this.counter++;
},
sub:function (){
// console.log('sub被执行');
this.counter--;
}
}
})
</script>
</body>
1.3 Vue的MVVM
Model View ViewModel
- View:视图层,在前端开发中通常是DOM层,给用户展示各种信息
- Model:数据层,可能是固定的死数据,更多的是来自服务器,从网络上请求下来的数据
- ViewModel:视图模型层,是View和Model沟通得桥梁,一方面绑定数据,另一方面实现DOM监听
1.4 Vue的options选项
options包含选项详细解析:https://cn.vuejs.org/v2/api/
目前需掌握:
- el
- 类型:string|HTMLElement
- 作用:决定之后Vue实例管理哪一个DOM
- data
- 类型:Object|Function
- 作用:Vue实例对应的数据对象
- methods
- 类型:{[key:string]:Function}
- 作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中调用
1.5 Vue的生命周期
声明周期:事物从诞生到消亡的整个过程。
Vue生命周期:https://segmentfault.com/a/1190000011381906
1.6 定义Vue的Template
-
先在HTML中写如下代码(WebStrom)
-
赋值红框中的代码,点击File->Setting->Editor->Live Templates->Vue
-
按照下列步骤进行,之后在html代码中输入vue然后点击tab键即可出现模板
2. 模板语法
2.1 插值操作
2.1.1 mustache语法
Mustache语法也就是双大括号
<body>
<div id="app">
<h2>{{message}}</h2>
<h2>{{message}},李银河!</h2>
<!-- mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式 -->
<h2>{{firstName+' '+lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{counter*4}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
firstName:'zhi',
lastName:'yu',
counter:100
}
})
</script>
</body>
2.1.2 其他指令使用
-
v-once
-
该指令后面不需要跟任何表达式
-
该指令表示元素和组件只渲染一次,不会随着数据的改变而改变
<h2>{{message}}</h2> <h2 v-once>{{message}}</h2>
-
-
v-html
-
该指令后面往往会跟上一个string类型
-
会将string的HTML解析出来并且进行渲染
<div id="app"> <h2>{{url}}</h2> <h2 v-html="url"></h2> </div> <script src="../js/vue.js"></script> <script> const app=new Vue({ el:"#app", data:{ url:'<a href="https://www.baidu.com">百度一下</a>' } }) </script>
-
-
v-text
-
与mustache相似,都是用于将数据显示在界面中
-
通常情况下,接收一个string类型
<h2>{{message}},栀虞!</h2> <!-- 一般不用,不够灵活 --> <h2 v-text="message">,栀虞!</h2>
-
-
v-pre
-
用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法
<h2>{{message}}</h2> // 你好啊 <h2 v-pre>{{message}}</h2> // {{message}} 把h2里面的内容原封不动显示,不需要解析
-
-
v-cloak
-
在某些情况下,浏览器可能会直接显示出未编译的Mustache标签
-
在vue解析之前,div有一个属性v-cloak,在vue解析之后,div中没有一个属性v-cloak
<style> [v-cloak]{ display: none; } </style> <div id="app" v-cloak> // 在解析之前,div标签不展示 <h2>{{message}}</h2> </div>
-
2.2 v-bind的使用
作用:动态绑定属性
预期:any(with argument) | Object(without argument)
参数:attOrProp(optional)
2.2.1 v-bind的基本使用
v-bind:用于绑定一个或多个属性值,或者想另一个组件传递props值
简写::
<div id="app">
<!-- <img v-bind:src="imgURL" alt="">
<a v-bind:href="aHref">百度一下</a> -->
<!-- 语法糖写法 -->
<img :src="imgURL" alt="">
<a :href="aHref">百度一下</a>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
imgURL:'https://p6.itc.cn/images01/20211219/90211482300a418b88376f41258850f2.jpeg',
aHref:'https://www.baidu.com'
}
})
</script>
2.2.2 v-bind的动态绑定class
- 对象语法
<!-- 用法一:直接通过{}绑定一个类 -->
<h2 v-bind:class="{active:isActive}">{{message}}</h2>
<!-- 用法二:也可以通过判断,传入多个值 -->
<h2 v-bind:class="{active:isActive,line:isLine}">{{message}}</h2>
<!-- 用法三:和普通的类同时存在,并不冲突 -->
<h2 class="title" v-bind:class="{active:isActive,line:isLine}">{{message}}</h2>
<!-- 方法四:如果过于复杂,可以放在一个methods或者computed中 classes是一个计算属性-->
<h2 class="title" :class="classes">{{message}}</h2>
<div id="app">
<!--<h2 class="active">{{message}}</h2>
<h2 :class="active">{{message}}</h2>-->
<!--<h2 v-bind:class="{类名1:boolean,类名2:boolean}"></h2>-->
<!-- title是固定的,不可删除,后面的active与line是可以改变的 -->
<h2 class="title" v-bind:class="{active:isActive,line:isLine}">{{message}}</h2>
<button v-on:click="btnClick">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
isActive:true,
isLine:true
},
methods:{
btnClick:function (){
this.isActive=!this.isActive;
}
}
})
</script>
- 数组语法
<div id="app">
<h2 class="title" :class="[active,line]">{{message}}</h2>
<h2 class="title" :class="getClasses()">{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
active:'aaaa',
line:'bbbb'
},
methods:{
getClasses:function (){
return [this.active,this.line];
}
}
})
</script>
2.2.3 v-bind的动态绑定style
-
对象语法
<div id="app"> <!--<h2 :style="{key(属性名):value(属性值)}">{{message}}</h2>--> <!-- value值必须加单引号,否则会当成一个变量去解析 --> <h2 :style="{fontSize:'50px',color:'red'}">{{message}}</h2> <!-- finalSize当成一个变量使用 --> <h2 :style="{fontSize:finalSize+'px',color:colorLine}">{{message}}</h2> <!-- methods使用 --> <h2 :style="getStyles()">{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app=new Vue({ el:"#app", data:{ message:'你好啊', finalSize:50, colorLine:'red', }, methods:{ getStyles:function (){ return {fontSize:this.finalSize+'px',color:this.colorLine} } } }) </script>
-
数组语法
<div id="app"> <h2 :style="[baseStyle,baseStyle1]">{{message}}</h2> </div> <script src="../js/vue.js"></script> <script> const app=new Vue({ el:"#app", data:{ message:'你好啊', baseStyle:{color:'red',fontSize:'50px'}, baseStyle1:{backgroundColor:'pink'} } }) </script>
2.3 计算属性的使用
2.3.1 计算属性的基本使用
<div id="app">
<h2>{{firstName+' '+lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<!-- 使用methods -->
<h2>{{getFullName()}}</h2>
<!-- 计算属性 -->
<h2>{{fullName}}</h2>
<h2></h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
firstName:'zhiyu',
lastName:'Jemas'
},
computed:{
// 计算属性(),定义函数,
fullName:function () {
return this.firstName+' '+this.lastName;
}
},
methods:{
getFullName:function (){
return this.firstName+' '+this.lastName;
}
}
})
</script>
2.3.2 计算属性的复杂操作
<div id="app">
<h2>总价格:{{totalPrice}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
books:[
{id:110,name:'计算机系统基础',price:120},
{id:111,name:'代码大全',price:110},
{id:112,name:'深入理解计算机原理',price:78}
]
},
computed:{
totalPrice:function (){
let result=0;
/*for(let i=0;i<this.books.length;i++){
result+=this.books[i].price;
}*/
for(let i in this.books){
result+=this.books[i].price;
}
// for(let book of this.books){}
return result;
}
}
})
</script>
2.3.3 计算属性的setter和getter
一般情况下,一般没有set方法,只读属性
<div id="app">
<h2>{{fullName}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
firstName:'Kobe',
lastName:'Bryant'
},
computed:{
/* 简介写法
fullName:function (){
return this.firstName+' '+this.lastName;
}*/
/* 原本写法 */
fullName:{
set:function (newValues){
const names=newValues.split(' ');
this.firstName=names[0];
this.lastName=names[1];
},
get:function (){
return this.firstName+' '+this.lastName;
}
}
}
})
</script>
2.3.4 计算属性和methods的对比
<div id="app">
<!-- 1.直接拼接:语法过于繁琐,一般不采用 -->
<h2>{{firstName}} {{lastName}}</h2>
<!-- 2. 通过定义methods:多次打印相同的,会多次调用函数 -->
<h2>{{getFullName()}}</h2>
<!-- 3. 通过定义computed:计算属性会进行缓存,如果多次使用,计算属性只会调用一次,性能更高 -->
<h2>{{fullName}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
firstName:'Kobe',
lastName:'Bryant'
},
computed:{
fullName:function (){
return this.firstName+' '+this.lastName;
}
},
methods:{
getFullName:function (){
return this.firstName+' '+this.lastName;
}
}
})
</script>
3. ES6补充语法
3.1 块级作用域-let和var
ES5之前,因为if和for都没有块级作用域的概念,所以在很多时候,我们必须借助于function的作用域来解决应用外面变量的问题。
ES6中,接入了let,let它是由if和for的作用域的。
-
变量作用域:变量在什么范围内是可用的
-
块级作用域:
- JS中使用var来声明一个变量时,变量的作用域主要是和函数的定义有关
- 针对于其他块定义来说是没有作用域的,比如if/for等
-
let和var的区别
使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象;
使用let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升;
https://blog.csdn.net/harborian/article/details/102521610
3.2 三种方案对比
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
// 1. 没有块级作用域引起的问题
// 为什么闭包可以解决问题:函数是一个作用域
// 2. 情况一:ES5中没有使用闭包(错误的方式)
var btns=document.querySelectorAll('button');
for(var i=0;i<btns.length;i++){
btns[i].addEventListener('click',function (){
console.log('第'+i+'个按钮被点击');
})
}
// 3. 情况二:ES5中使用闭包
var btns=document.querySelectorAll('button');
for(var i=0;i<btns.length;i++){
(function (i){
btns[i].addEventListener('click',function (){
console.log('第'+i+'个按钮被点击');
})
})(i)
}
// 4. 情况三:ES6中的let
const btns=document.querySelectorAll('button');
for(let i=0;i<btns.length;i++){
btns[i].addEventListener('click',function (){
console.log('第'+i+'个按钮被点击');
})
}
</script>
</body>
3.3 const的使用和注意点
当修饰的标识符不会被再次赋值时,就可以使用const来保证数据的安全性
建议:在开发中,优先使用const,只有在需要改变某一标识符的时候才使用let
注意:const值不可以再次赋值,const修饰的标识符必须赋值,常量的含义是指向的对象不能修改,但是可以改变对象内部的属性
<script>
const obj={
name:'zhiyu',
age:18,
height:165
}
console.log(obj);
obj.name='huahua';
console.log(obj);
</script>
3.4 对象字面量增强写法
- 属性的增强写法
- 函数的增强写法
<script>
// 1. 属性的增强写法
const name='zhiyu';
const age=18;
const height=1.88;
// ES5的写法
const obj={
name:name,
age:age,
height:height
};
// ES6写法
const obj1={
name,
age,
height
}
console.log(obj1);
// 2. 函数的增强写法
// ES5写法
const obj2={
run:function (){},
eat:function (){}
}
// ES6写法
const obj3={
run(){},
eat(){}
}
</script>
4. 事件监听v-on
4.1 v-on的基本使用和语法糖
- 作用:绑定事件监听器
- 缩写:@
- 预期:Function|Inline Statement|Object
- 参数:event
<div id="app">
<h2>{{counter}}</h2>
<!-- <button v-on:click="counter++">+</button>-->
<!-- <button v-on:click="counter--">-</button>-->
<!-- <button v-on:click="increment">+</button>-->
<!-- <button v-on:click="decrement">-</button>-->
<button @click="counter++">+</button>
<button @click="counter--">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
counter:0
},
methods:{
increment(){
this.counter++;
},
decrement(){
this.counter--;
}
}
})
</script>
4.2 v-on的参数传递问题
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
- 如果该方法不需要额外参数,那么方法后的()可以不添加,但是如果方法本身有一个参数,默认将原生事件event参数传递进去
- 如果需要同时将某个参数传递进去,同时需要event时,可以通过$event传入事件
<div id="app">
<!-- 事件调用的方法没有参数表-->
<button @click="btn1Click">按钮1</button>
<button @click="btn1Click()">按钮1</button>
<!-- 在定义事件时,写函数时省略了小括号,但是方法本身是需要一个参数的,这时,vue会默认将浏览器生成的event事件对象作为参数传入到方法-->
<!-- <button @click="btn2Click()">按钮2</button>-->
<!-- <button @click="btn2Click">按钮2</button>-->
<button @click="btn2Click(123)">按钮2</button>
<!-- 定义时,需要event对象,又需要其他参数 -->
<button @click="btn3Click(123,$event)">按钮3</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
},
methods:{
btn1Click(){
console.log("btn1Click");
},
btn2Click(event){
console.log("------",event);
},
btn3Click(abc,event){
console.log("+++++",abc,event);
}
}
})
</script>
4.3 v-on的修饰符使用
- .stop:调用event.stopPropagation()
- .prevent:调用event.preventDefault()
- .{keyCode|keyAlias}:只当事件是从特定键触发时才触发回调
- .native:监听组件根元素的原生事件
- .once:只触发一次回调
<div id="app">
<!-- 1. .stop修饰符的使用 调用event.stopPropagation()-->
<div @click="divClick">aaaa
<button @click.stop="btnClick">按钮</button>
</div>
<br>
<!-- 2. .prevent-->
<form action="baidu">
<input type="submit" value="提交" @click.prevent="submitClick">
</form>
<!-- 3. .{keyCode|keyAlias} 例子:监听enter按键 -->
<input type="text" @keyup.enter="KeyUp">
<!-- 4. .once-->
<button @click.once="btn2Click">按钮2</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
},
methods:{
divClick(){
console.log("divClick");
},
btnClick(){
console.log("btnClick");
},
submitClick(){
console.log("submitClick");
},
KeyUp(){
console.log("keyUp");
},
btn2Click(){
console.log("btn2Click");
}
}
})
</script>
5. 条件判断v-if
5.1 v-if v-else-if v-else的使用
<div id="app">
<h2 v-if="isShow">{{message}}</h2>
<h1 v-else>isShow为false时,显示我</h1>
<!--不建议这样使用-->
<p v-if="score>=90">优秀</p>
<p v-else-if="score>=80">良好</p>
<p v-else-if="score>=60">及格</p>
<p v-else>不及格</p>
<!--尽量这样使用-->
<p>{{result}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
isShow:true,
score:98
},
computed:{
result(){
let showMessage='';
if(this.score>=90){
showMessage='优秀';
}else if(this.score>=80){
showMessage='良好';
}else if(this.score>=60){
showMessage='及格';
}else{
showMessage='不及格';
}
return showMessage;
}
}
})
</script>
5.2 v-show的使用
<div id="app">
<!--
v-if;当条件为false时,包含v-if指令的元素,根本不会存在dom中
v-show:当条件为false时,v-show只是给我们的元素增加了一个行内样式:display:none
-->
<h2 v-if="isShow">{{message}}</h2>
<h2 v-show="isShow">{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
isShow:true
}
})
</script>
注意:当需要在显示与隐藏之间切换很频繁时,使用v-show,只有一次切换时,使用v-if
6. 循环遍历v-for
6.1 v-for遍历数组
<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>
<!-- 3.遍历对象 -->
<!-- 不建议使用的方法 -->
<ul>
<li>{{info.name}}</li>
<li>{{info.age}}</li>
<li>{{info.height}}</li>
</ul>
<!-- 在遍历对象的过程中,如果只是获取一个值,获取的是value -->
<ul>
<li v-for="item in info">{{item}}</li>
</ul>
<!-- 获取value和key (value,key,index) -->
<ul>
<li v-for="(value,key,index) in info">{{value}}-{{key}}-{{index+1}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
names:['why','kobe','james','curry'],
info:{
name:'why',
age:18,
height:1.88
}
}
})
</script>
6.2 v-for绑定和非绑定key的区别
官方推荐:在使用v-for时,给对应的元素或组件加上一个:key属性
key的作用:是为了高效的更新虚拟DOM
<div id="app">
<ul>
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
letters:['A','B','C','D','E']
},
methods:{
btnClick(){
// 1. push方法:在数组元素后面添加元素,可添加多个
// this.letters.push('aaa','bbb');
// 2. 通过索引值修改数组中的元素,不可响应
// this.letters[0]='F';
// 3. pop(): 删除数组中的最后一个元素
// this.letters.pop();
// 4. shift():删除数组中的第一个元素
// this.letters.shift();
// 5. unshift():在数组最前面添加元素,可添加多个
// this.letters.unshift("AAA",'BBB');
// 6. splice(start,value,item):start表示开始操作的位置,value表示需要插入(0)/删除/替换的个数,item表示添加的值
// this.letters.splice(2,0,"F","G"); 添加
// this.letters.splice(2,3,'m','n','l','x'); 替换
// this.letters.split(2,3); 删除
// 7.sort():
// this.letters.sort();
// 8.reverse():翻转数组
// this.letters.reverse();
// set(要修改的值,索引值,修改后的值)
Vue.set(this.letters,0,'bbbb');
}
}
})
</script>
7. 表单绑定v-model
7.1 v-model的使用和原理
<div id="app">
<!-- 双向绑定方法一:常用 <input type="text" v-model="message">-->
<!-- 双向绑定方法二: <input type="text" :value="message" @input="valueChage">-->
<!-- 双向绑定方法三: -->
<input type="text" :value="message" @input="message=$event.target.value">
<!-- 绑定textarea -->
<textarea v-model="message"></textarea>
<h2>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:'你好啊'
},
methods:{
valueChage(event){
// 使用event.target.value获取input当前最新的内容
this.message=event.target.value;
}
}
})
</script>
v-model其实是一个语法糖,它的本质是包含两个操作:
- v-bind绑定一个value属性
- v-on指令给当前元素绑定input事件
7.2 v-model结合radio类型使用
<div id="app">
<!-- 两个radio设置同样的name属性可以保持互斥,但是绑定同样的v-model,也有同样的效果-->
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
<h2>您选择的性别是: {{sex}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
sex:'男'
}
})
</script>
7.3 v-model结合checkbox类型使用
<div id="app">
<!-- checkbox单选框 -->
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h2>您选择的是: {{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button><br><br><br>
<!-- 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">羽毛球
<h2>您的爱好是: {{hobbies}}</h2>
<!-- input中的值绑定 -->
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
isAgree:false,
hobbies:[],
originHobbies:['篮球','足球','乒乓球','羽毛球','台球','高尔夫球']
}
})
</script>
7.4 v-model结合select类型使用
<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 src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
fruit:'香蕉',
fruits:[]
}
})
</script>
7.5 v-model修饰符
<div id="app">
<!-- 1.修饰符 lazy 可以让数据失去焦点或回车时才会更新-->
<input type="text" v-model.lazy="message">
<h2>{{message}}</h2>
<!-- 2.修饰符 number:可以让输入框中输入的内容转为数字类型 -->
<input type="number" v-model.number="age">
<h2>{{typeof age}}</h2>
<!-- 3.修饰符 trim:去除左右两边的空格 -->
<input type="text" v-model.trim="name">
<h2>您输入的名字: {{name}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
age:0,
name:''
}
})
</script>
8. 组件化开发
8.1 组件化的实现和使用步骤
Vue组件化思想:
- 他提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
- 任何的应用都会被抽象成一颗组件树
组件使用的步骤:
- 创建组件的构造器(调用Vue.extend()方法)
- 注册组件(调用Vue.component()方法)
- 使用组件(在Vue实例的作用范围内使用组件)
<div id="app">
<!-- 3. 使用组件 -->
<my-cpn></my-cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1. 创建组件构造器对象
const cpnC=Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,呵呵呵呵</p>
<p>我是内容,哈哈哈哈哈哈</p>
</div>`
});
// 2. 注册组件(全局组件,意味着可以在多个VUE实例中使用)
Vue.component('my-cpn',cpnC);
const app=new Vue({
el:"#app",
data:{
},
components:{ // 局部组件
// cpn:使用组件的标签名
cpn:cpnC
}
})
</script>
8.2 父组件和子组件
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1. 创建第一个组件构造器(子组件)
const cpnC1=Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈哈</p>
</div>`
})
// 2. 创建第二个组件构造器(父组件)
const cpnC2=Vue.extend({
template:`
<div>
<h2>我是标题2</h2>
<p>我是内容2,哈哈哈哈哈</p>
<cpn1></cpn1>
</div>
`,
components:{ // 注册组件
cpn1:cpnC1
}
})
// root组件
const app=new Vue({
el:"#app",
data:{
},
components:{
cpn2:cpnC2
}
})
</script>
8.3 注册组件的语法糖写法
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 注册全局组件的语法糖
Vue.component('cpn1',{
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈哈</p>
</div>`
})
const app=new Vue({
el:"#app",
data:{
message:'你好啊'
},
// 注册局部组件的语法糖
components:{
'cpn2':{
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈哈</p>
</div>`
}
}
})
</script>
8.4 组件模板抽离的写法
<div id="app">
<cpn></cpn>
</div>
<!--1.模板写法一:script标签 类型必须是text/x-template -->
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈哈</p>
</div>
</script>
<!-- 2.模板写法二:template标签 -->
<template id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,哈哈哈哈哈</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1,注册全局组件
Vue.component('cpn',{
template:`#cpn`
})
const app=new Vue({
el:"#app",
data:{
}
})
</script>
8.5 组件data必须是函数
- 组件不可以直接访问Vue实例中的data
- 组件模块有属于自己的HTMl模块,也应该有属于自己的数据data
- 组件data必须是函数,这个函数返回一个对象,对象内部保存着数据
<div id="app">
<cpn></cpn>
</div>
<script type="text/x-template" id="cpn">
<div>
<h2>{{title}}</h2>
<p>我是内容,哈哈哈哈哈</p>
</div>
</script>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn',{
template:`#cpn`,
data(){
return {
title:'abc'
}
}
})
const app=new Vue({
el:"#app",
data:{
}
})
</script>
8.6 父子组件的通信
- 通过props向子组件传递数据
- 字符串数组,数组中的字符串就是传递时的名称
- 对象,对象可以设置传递时的类型,也可以设置默认值等
- 通过事件向父组件发送信息
8.6.1 父传子props
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<p>{{cmovies}}</p>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 父组件向子组件传递信息:props
const cpn={
template:`#cpn`,
// props:['cmovies','cmessage'],
props:{
// 1. 类型的限制
// cmovies:Array,
// cmessage:String
// 2. 提供一些默认的值
cmessage:{
type:String,
default:'aaaa',
required:true // 当required为true时,这个属性必须传,否则会报错
},
// 类型是对象或者数组时,默认值必须是一个函数
cmovies:{
type:Array,
// default: [], // 2.5.3以下不会报错
default(){
return []
}
}
},
}
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
movies:['海王','海贼王','海尔兄弟']
},
components:{
cpn
}
})
</script>
8.6.2 props驼峰标识
例如:childMyMessage写成 child-my-message
<div id="app">
<!-- v-bind不支持驼峰 -->
<cpn :c-info="info" :child-my-message="message"></cpn>
</div>
<template id="cpn">
<div>
<h2>{{cInfo}}</h2>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn={
template:`#cpn`,
props:{
cInfo:{
type:Object,
default() {
return {};
}
},
childMyMessage:{
type:String,
default:''
}
}
}
const app=new Vue({
el:"#app",
data:{
message:'你好啊',
info:{
name:'why',
age:18,
height:1.88
}
},
components:{
cpn
}
})
</script>
8.6.3 子传父(自定义事件)
自定义事件的流程:
- 在子组件中,通过$emit()来触发事件
- 在父组件中,通过v-on来监听子组件事件
<!--父组件模板-->
<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 src="../js/vue.js"></script>
<script>
// 1. 子组件
const cpn={
template:`#cpn`,
data(){
return {
categories:[
{id:'aaa',name:'热门推荐'},
{id:'bbb',name:'手机数码'},
{id:'ccc',name:'家用家电'},
{id:'ddd',name:'电脑办公'}
]
}
},
methods:{
btnClick(item){
// 将item传入父组件 发射事件
this.$emit('item-click',item)
}
}
}
// 2.父组件
const app=new Vue({
el:"#app",
data:{
message:'你好啊'
},
components:{
cpn
},
methods:{
cpnClick(item){
console.log('cpnClick',item);
}
}
})
</script>
8.7 父访问子-children-refs
-
父组件访问子组件:使用$ c h i l d r e n 或 children或 children或refs
<div id="app"> <cpn></cpn> <cpn ref="aaa"></cpn> <button @click="btnClick">按钮</button> </div> <template id="cpn"> <div>我是子组件</div> </template> <script src="../js/vue.js"></script> <script> const app=new Vue({ el:"#app", data:{ message:'你好啊' }, methods: { btnClick(){ // 1.$children // console.log(this.$children); // for(let c of this.$children){ // console.log(c.name); // c.showMessage(); // } // console.log(this.$children[0].name) // 2.$refs => 对象类型 默认是一个空的对象,ref='aaa' console.log(this.$refs.aaa.name); } }, components:{ cpn:{ template:`#cpn`, data(){ return { name:'我是子组件的name' } }, methods:{ showMessage(){ console.log('showMessage'); } } } } }) </script>
-
子组件访问父组件:使用$parent
<div id="app"> <cpn></cpn> </div> <template id="cpn"> <div> <h2>我是cpn组件</h2> <ccpn></ccpn> </div> </template> <template id="ccpn"> <div> <h2>我是ccpn子组件</h2> <button @click="btnClick">按钮</button> </div> </template> <script src="../js/vue.js"></script> <script> const app=new Vue({ el:"#app", data:{ message:'你好啊' }, components:{ cpn: { template: `#cpn`, data(){ return { name:'我是cpn组件的name' } }, components:{ ccpn:{ template: `#ccpn`, methods: { btnClick(){ // 1.访问父组件 console.log(this.$parent); console.log(this.$parent.name); // 2.访问根组件 console.log(this.$root); console.log(this.$root.message); } } } } } } }) </script>