v-model的使用
v-model可以实现双向绑定:原理
<input type="text" v-model="message"> //input的value值为message,同时修改input的值时,message也会跟着改变,通过h2中显示message的内容可以看出来
<h2>{{message}}</h2>
//如果不用v-model
<input type="text" :value="message" @input="valueChange">
<h2>{{message}}</h2>
//也可以不用函数,直接监听输入事件
<input type="text" :value="message" @input="message=$event.target.value">
data:{
message:"你好啊",
},
methods:{
valueChange(event){
this.message=event.target.value;
}
}
v-model与radio使用
radio标签加上相同的name属性就可以实现单选(往服务器提交时key是name)
如果两个radio绑定的了一个v-model,那么即使没有name,两个选项也是互斥的
<label for="male">
<input type="radio" id="male" name="sex" value="男" v-model="sex">男
</label>
<label for="famale">
<input type="radio" id="famale" name="sex" value="女" v-model="sex">女
</label>
<h2>你选择的性别是:{{sex}}</h2>
data:{
sex:"" //一般都是有默认选中的,只要在sex这里填男就可以了
}
v-model与checkbox使用
label的好处:点击文字也可以选中,一般情况下,一个label与一个input一起使用
//单选框
<label for="agree">
<input type="checkbox" id="agree" name="sex" v-model="isAgree">女
</label>
<h2>你选择的是:{{isAgree}}</h2>
<button :disabled="!isAgree"> 下一步</button>
//多选框
<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>你的爱好是:</h2>
data:{
isAgree:false, //单选框,对应的bollean
hobbies:[] //多选框,对应的数组
}
//单选
<select name="favoriteFruit" id="" v-model="fruit">
<option value="香蕉">香蕉</option>
<option value="苹果">苹果</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select>
<h2>你的选择的水果是:{{fruit}}</h2>
//多选(id不能重复所以这个多选的id删掉)
<select name="favoriteFruit" v-model="fruits" multiple> //加上multiple变成多选
<option value="香蕉">香蕉</option>
<option value="苹果">苹果</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select>
<h2>你的选择的水果是:{{fruit}}</h2>
data:{
fruit:'香蕉', //字符串类型
fruits:[], //数组类型
}
值绑定
v-model修饰符的使用
1、lazy修饰符:数据在输入回车或者失去焦点时才会更新
2、number修饰符:默认情况下,v-model给变量赋值时都是string类型,使用该修饰符可以将数据类型转换为number
//修饰符:number
<input type="number" v-model="age"> //给input的type设置number,就只能输入数字。,但是输入的数字类型其实是字符串
<h2>{{typeof(age)}}</h2> //输出string
3、trim修饰符:去除空格
组件化开发
在ES6中,使用点(·)tab上面的那个键,也可以代表字符串,和单引号、双引号(’,”)功能相似,并且可以换行
const str=~abc
def~
1、创建组件构造器对象,Vue.extend({})
2、注册组件,需要传入两个参数。Vue.component(‘组件的标签名’,组件构造器)
3、在Vue实例里使用组件标签,并且一定要记住创建和注册组件要放在创建Vue前面,不然对于自己创建的组件标签会报错
//3、使用组件
<my-cpn></my-cpn>
//1、创建组件构造器对象
const cpnC = Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是第一个内容</p>
<p>我是第二个内容</p>
</div>`
})
//2、注册组件,以这种方法注册的组件是全局组件
Vue.component('my-cpn',cpnC)
全局组件和局部组件
全局组件:可以在多个Vue实例下面使用
局部组件:在某个Vue的实例中注册的组件是局部组件
//局部组件的注册
const app=new Vue({
components:{
//标签名:组件构造器
cpn:cpnC
})
父组件和子组件
下例中,组件构造器1是在组件构造器2中被注册的,组件构造器2是在Vue实例中注册的
cpn2是父组件,cpn1是子组件
也可以将Vue看作一个父组件,最顶层的组件我们称之为root组件
想要在某个作用域里面使用某个组件,需要在全局注册组件,或者在该作用域组件的component中注册
代码解析过程:看到cpn2标签在vue实例中找对应组件,找到后解析opn2组件,解析opn2组件时,看到cpn1标签在cpn2组件中找对应组件,找到后解析opn1组件,然后将html。整个流程vue是不知道opn1组件的存在的。所以如果在vue实例中使用opn1组件,现在vue中查找opn1组件,没找到后在全局查找,没找到就报错
<div id="app">
<cpn2></cpn2>
<cpn1></cpn1> //并不显示cpn1中的内容,而且会报错。需要在Vue中再注册一次
</div>
const cpnC1=Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>
`
})
const cpnC2=Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,xixixi</p>
<cpn1></cpn1>
</div>
`
components:{
cpn1:cpn1 //在一个组件中注册另一个组件
}
})
const app=new Vue({
components:{
'cpn2':cpnC2
}
})
//显示内容
我是标题
我是内容
我是标题
我是内容,xixixi
//一般写法
const cpn1=Vue.extend({
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,xixixi</p>
</div>
`
})
Vue.component('cpn1',cpn1)
//全局组件的语法糖
Vue.component('cpn1',{ //本质还是调用了extend
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,xixixi</p>
</div>
`
}
//局部组件的语法糖
const app=new Vue({
components:{
'cpn1':{
template:`
<div>
<h2>我是标题</h2>
<p>我是内容,xixixi</p>
</div>
`
}
}
})
Vue.component('cpn1',{ //本质还是调用了extend
template:`
我是标题
我是内容,xixixi
`
}
//组件模块分离(1),通过script标签
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<p>我是内容,xixixi</p>
</div>
</script>
Vue.component('cpn',{
template:'#cpn'
})
//(2)template标签
<template id='cpn'>
<div>
<h2>我是标题</h2>
<p>我是内容,xixixi</p>
</div>
<template>
Vue.component('cpn',{
template:'#cpn'
})
<template id='cpn'>
<div>
<h2>{{title}}</h2>
<p>我是内容,xixixi</p>
</div>
<template>
Vue.component('cpn',{
template:'#cpn',
data(){
return {
title:'我是标题呀呀呀'
}
})
组件中的data是函数
如果组件中的data是函数,那么多次使用组件时,如果修改了data的值,会影响其他地方使用组件。
如果想在别的地方使用组件的时候同时修改数据值,可以在全局定义变量
//调用函数,如果返回值是在函数中声明定义的,此时的obj1和obj2接收的其实是一个地址,改变自己地址中的属性值不会影响别的调用相同函数的变量的属性值
function abc(){
return {
name:'why',
age:18
}
let obj1=abc();
let obj2=abc();
obj1.name='kobe';
console.log(obj2.name) //输出why
//此时调用函数,函数的返回值是obj的地址,将函数的返回值赋给obj3和obj4,此时它们指向的是同一个地址,修改obj3的属性值也会影响到obj4
const obj= {
name:'why',
age:18
}
function abc(){
return obj
}
let obj3=abc();
let obj4=abc();
obj1.name='kobe';
console.log(obj2.name) //输出kobe
多次使用计数组件
<template id="cpn">
<div>
<button @click="decrement">-</button>
<h2>当前计数:{{counter}}</h2>
<button @click="increment">+</button>
</div>
</template>
Vue.component('cpn',{
template:'#cpn',
data(){
return { counter:0}
},
methods:{
increment(){
this.counter++;
},
decrement(){
this.counter--;
}
}
})
父子组件的通信
1、父组件向子组件中传递数据
在Vue实例中使用组件时才传递数据,如果不使用v-bind,那么movies和message会作为字符串赋值给cmovies和cmessage,因为cpn组件中没有对应的变量
props是一个对象时
可以给变量限制类型(也可以是限制为多个类型,用逗号隔开,null
可以匹配任何类型,)、提供默认值、确定是否为必传值
类型是对象或者数组时,默认值必须是一个函数,返回值对应着对象和数组
<div id="app"> //在Vue实例中使用组件时才传递数据
<cpn :cmovies="movies" :cmessage="message"></cpn> //如果不使用v-bind,那么movies和message会作为字符串赋值给cmovies和cmessage,因为cpn组件中没有对应的变量
</div>
<template id="cpn">
<div>
<ul>
<li v-for="(item,index) in cmovies">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
const cpn={
template:"#cpn",
data(){
return{};
},
props:['cmovies','cmessage'] //数组形式
props:{ //对象形式
cmovies:Array, //可以给变量指定类型
cmessage:String
cmessage:{
type:String,
default:'aaaaaaaa', //提供默认值
required:true //表示使用组件时必须给cmessage传值
cmovies:{
type:Array,
//default:[] //会报错,必须提供一个函数
default:(){
return []
}
}
}
const app=new Vue({
el:"#app",
data:{
movies:['海王','海贼王','海尔兄弟'],
message:'你好啊'
}
components:{
/*'cpn': {
cpn中的内容
}*/
//利用对象字面量增强写法的属性增强写法
cpn
})
下图右边代码示例需要多看看
props中的驼峰标识
使用v-bind不支持驼峰命名,所以可以使用横杠(-)
创建和注册组件时使用大小写,html文件中使用(-),结合使用才不会报错
<div id="app">
<cpn :c-movies="movies" :c-message="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="(item,index) in cMovies">{{item}}</li>
</ul>
<h2>{{cMessage}}</h2>
</div>
</template>
<script src="js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {};
},
props: {
cMovies: {},
cMessage: {}
}
}
const app = new Vue({
el: "#app",
data: {
movies: ['海王', '海贼王', '海尔兄弟'],
message: '你好啊'
},
components: {
/*'cpn': {
cpn中的内容
}*/
//利用对象字面量增强写法的属性增强写法
cpn
}
})
2、子组件向父组件传递(自定义事件 this.$emit(‘自定义函数的名字’,参数))
如子组件中发生一些事件需要传递给父组件
需要注意的是:html标签中的需要绑定函数名称一定需要用(-),而绑定函数的名字,也就是在引号中的函数可以用驼峰式,但是如果实在methods中定义的函数,就只能用驼峰,不能用(-)
<div id="app">
<cpn @item-click="cpnClick"></cpn> //父组件接收事件
</div>
<template id="cpn">
<div>
<button v-for="(item,index) in categories" @click="btnClick(item)">item.name</button>
</div>
</template>
<script src="js/vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {categories[
{id:'aaa',name:'热门推荐'},
{id:'bbb',name:'手机数码'},
{id:'ccc',name:'家用家电'},
{id:'ddd',name:'电脑办公'}
]};
},
methods:{
btnClick(item){ //发射事件
this.$emit('item-click',item) //会默认把item传递给接收函数
}
}
}
const app = new Vue({
el: "#app",
data: {
movies: ['海王', '海贼王', '海尔兄弟'],
message: '你好啊'
},
components: {
cpn
}
methods:{
cpnClick(item){
console.log(item);
}
}
})
可以试试写一下下面的这个案例(复习时!!)
知识点总结
一个综合案例:E:\web\basic\vue\4.day1da2test
主要是结合了父组件向子组件传递的同时子组件向父组件传递,基本是按照老师讲的来写的,但是还有一个问题没有解决,当第一个输入框中的内容清零后,h2中的pnum1也消失
直接用watch监听数据的改变
组件访问
父组件访问子组件
$children的返回值是vueComponent的数组,可以通过该属性可以调用组件的数据和函数
$children属性是根据子组件的索引值来获取子组件的数据的,子组件对应的索引值并不是一成不变的,所以通过该属性获取数据不常用
$refs 是一个对象类型,默认refs属性是空的对象,需要自己在组件上添加ref属性 ref=" "
//$children
<cpn></cpn>
<button @click="btnClick" >按钮</button>
//$refs
<cpn ref="aaa"></cpn>
<button @click="btnClick" >按钮</button>
<template id="cpn">
<div>我是子组件</div>
</template>
const app= new Vue({
el:"#app",
methods:{
btnclick(){
//$children
console.log(this.$children) //输出:Vuecomponent数组
console.log(this.$children[0].name) //输出"我是子组件中的函数"
this.$children[0].showMessage() //输出"我是子组件的数据"
//$refs
console.log(this.$refs); //输出包含组件的对象{aaa:vueComponent}
console.log(this.$refs.aaa); //输出aaa这个组件
console.log(this.$refs.aaa。name); //输出"我是子组件中的函数"
}
},
components:{
cpn:{
template:"#cpn",
methods:{
showMessage(){
console.log("我是子组件中的函数");
}
},
data(){
return{ name:"我是子组件的数据"};
}
}
}
})
子组件访问父组件
$parent
尽量不用使用,组件的目的在于复用,而访问了父元素的组件不够独立,于父组件耦合度太高,且复用性不强
$root
访问根组件
<cpn></cpn>
<template id="#cpn">
<ccpn></ccpn>
</template>
<template id="ccpn">
<div>我是cpn的子组件</div>
<button @click="ccpnBtn" >ccpn的按钮</button>
</template>
const app= new Vue({
el:"app",
components:{
cpn:{
template:"#cpn",
methods:{
btnclick(){
console.log(this.$parent); //输出Vue实例
}
},
data(){
return{ name:"我是cpn组件的数据"};
}
components:{
ccpn:{
template:"#ccpn",
methods:{
ccpnBtn(){
console.log(this.$parent);
console.log(this.$parent.name);
console.log(this.$root);
}
}
}
}
}
}
})
插槽
1、插槽的基本使用<\slot>
2、插槽的默认值<\slot> button
3、如果有多个值,同时放到组件中进行替换时,会一起作为替换元素
<cpn> <span>哈哈哈</span> </cpn> //替换默认插槽
<cpn></cpn>
<cpn></cpn>
<template id="cpn">
<h2>我是组件</h2>
<slot> <button>按钮</button> </slot> //预留插槽,默认插槽内容
</template>
const app=new Vue({
el:"#app",
components:{
cpn:{
template:"#cpn",
}
}
})
<cpn> <span slot="center">标题</span> </cpn>
<template id="cpn">
<div>
<slot name="left"> <span>左边</span> </slot>
<slot name="center"> <span>中间</span> </slot>
<slot name="right"> <span>右边</span> </slot>
</div>
</template>
const app=new Vue({
el:"#app",
components:{
cpn:{
template:"#cpn",
}
}
})
编译作用域
有问题一般在这个网站找答案
查找变量时,都是先在模板中查找。但是如果是组件template则会在自己组件的内部查找
<cpn v-show="isShow"> </cpn> //cpn的内容显示
<template id="cpn">
<div>
<h2>我是组件</h2>
</div>
</template>
const app=new Vue({
el:"#app",
data:{
isShow:true
components:{
cpn:{
template:"#cpn",
}
data(){
return false;
}
}
})
有一点不懂,为什么template添加的属性名为slot,slot.data就能获取到data的值,data并不是slot的属性。
试了一下添加多个slot-scope属性,会报错,只能有一个。而且即使在组件中绑定的数据名字不相同,在执行组件时还是会只执行最后一个数据显示,像是第二个template覆盖了第一个。
在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中
。。。插槽这一章在复习时结合文档学习。现在大概了解了。
插槽的使用的文档
<cpn></cpn> //cpn的内容显示
//目的是获取子组件中的pLanguage
<cpn>
<template slot-scope="slot">
<h2 v-for="item in slot.data"></h2>
</template>。
</cpn>
<template id="cpn">
<div>
<slot :data="pLanguage"></slot>
<ul>
<li v-for="item in pLanguage">{{item}}</li>
</ul>
</div>
</template>
const app=new Vue({
el:"#app",
components:{
cpn:{
template:"#cpn",
}
data(){
return {
pLanguage:['JavaScript','C++','Java','C#']
}
}
}
})