七. v-model 的使用
- Vue中使用v-model指令来实现表单元素和数据的双向绑定
1.v-model的基本使用及原理
<!-- 改变输入框内值或message的值,另一值都会相应改变 -->
<body>
<div id="app">
<!-- <input type="text" v-model="message"> -->
<!-- <input type="text" :value="message" @input="valueChange"> -->
<input type="text" :value="message" @input="message = $event.target.value">
<h2>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
methods: {
valueChange(event) {
this.message = event.target.value;
}
},
})
</script>
</body>
v-model的原理
- 通过v-bind动态绑定输入框内的value属性,实现改变message的值的同时输入框内值也随之改变(单向绑定),再通过v-on监听input事件event绑定valueChange方法来实现改变输入框内的值的同时message值也随之改变(简便方法:引号内写表达式,通过$来直接获取当前事件)
2.v-model与其它类型的结合使用
1.结合radio类型(单选按钮)
<body>
<div id="app">
<label for="male">
<input type="radio" name="sex" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" name="sex" 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: {
message : '你好啊',
sex : '男',
}
})
</script>
</body>
- 注意:
- 当两标签用v-model绑定的是同一变量时,name属性(保证标签互斥)可省略不写
2.结合checkbox类型(复选框)
<body>
<div id="app">
<!-- 1.checkbox单选框 -->
<!-- <label for="license">
<input type="checkbox" id="license" v-model="isAgree">同意协议
</label>
<h2>您选择的是:{{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button> -->
<!-- 2.checkbox多选框 -->
<input type="checkbox" value="御姐" v-model="favorites">御姐
<input type="checkbox" value="萝莉" v-model="favorites">萝莉
<input type="checkbox" value="青春" v-model="favorites">青春
<input type="checkbox" value="成熟" v-model="favorites">成熟
<h2>您希望您的另一半是:{{favorites}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
isAgree : false, //单选框,布尔值
favorites : [], //多选框, 数组
}
})
</script>
</body>
3.结合select类型(下拉选项)
<body>
<div id="app">
<!-- 1.只能选择一个 -->
<select name="aaa" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="葡萄">葡萄</option>
<option value="榴莲">榴莲</option>
</select>
<h2>您选择的水果是:{{fruit}}</h2>
<!-- 2.可选择多个 -->
<!-- 按Ctrl键多选 -->
<select name="aaa" 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: {
message : '你好啊',
fruit : '榴莲',
fruits : [],
}
})
</script>
</body>
3.值绑定(动态的给value赋值)
- 之前value中的值都是在定义input时直接给定的,但是在真实开发中,这些input的值可能是从网络获取或定义在data中的,所以我们可以通过v-bind:value动态的给value绑定值
<body>
<div id="app">
<label v-for="item in originFavorites" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="favorites">{{item}}
</label>
<h2>您希望您的另一半是:{{favorites}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
favorites : [],
originFavorites : ["御姐","萝莉","青春","成熟"]
}
})
</script>
</body>
4.修饰符
<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>{{age}}-{{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 : 18,
name : ''
}
})
</script>
1.lazy修饰符(v-model.lazy)
- 默认情况下,v-model 默认是在 input 事件中同步输入框中的数据的,一旦有数据发生改变对应的data中的数据也会自动发生改变,而lazy修饰符可以让数据在失去焦点或者回车时才会更新。
2.number修饰符(v-model.number)
- 默认情况下,在输入框中输入的数字或字母都会被当做字符串处理,但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理,而number修饰符可以让输入框输入的内容自动转成数字类型。
3.trim修饰符(v-model.trim)
- 如果输入内容首尾有多个空格,通常我们希望将其去除,trim修饰符可以过滤内容左右两边的空格。
八.组件化开发
1.组件化思想及基本步骤
- 组件化是Vue.js中的重要思想,提供一种抽象,让我们可以开发出一个个独立可复用的小组件来构建我们的应用,任何应用都会被抽象成一颗组件树。
- 基本步骤:
创建组件构造器(调用Vue.extend()方法)
注册组件(调用Vue.component()方法)
使用组件(在Vue实例的作用范围内使用)
2.全局组件和局部组件
- 调用Vue.component()方法注册的组件为全局组件,可以在多个Vue的实例下使用。
- 若想要注册的组件为局部组件,需在对应的Vue实例中增加components属性(将注册组件挂载在某个实例中)
<body>
<div id="app">
<!-- 3.组件的基本使用(必须在Vue实例的作用范围内使用) -->
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
<cpn></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>Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita, harum! Corrupti inventore voluptatem eius optio in excepturi quidem numquam illum, non illo sint cum ab soluta praesentium animi, nihil vel.</p>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Doloremque laboriosam voluptate eum temporibus praesentium quo nesciunt eveniet rerum odio modi eius dolor atque sint ratione necessitatibus est, placeat veritatis amet.</p>
</div>`
})
//2.注册组件(全局组件)
//Vue.component('cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
//局部组件
components: {
//形式: "使用组件时的标签名: 组件构造器名"
cpn: cpnC
}
})
const app2 = new Vue({
el: '#app2',
})
</script>
</body>
3.父组件和子组件
<!-- Vue实例 -->
<div id="app">
<cpn2></cpn2>
<!-- <cpn1></cpn1> 会报错-->
</div>
<script src="../js/vue.js"></script>
<script>
//创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template:`
<div>
<h2>我是标题一</h2>
<p>hahahaha</p>
</div>`
})
//创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template:`
<div>
<h2>我是标题二</h2>
<p>emememem</p>
<cpn1></cpn1>
</div>`,
components: {
cpn1: cpnC1
}
})
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
components: {
cpn2: cpnC2,
// cpn1: cpnC1 解决办法
}
})
</script>
- 注意:
- 错误用法:以子标签的形式在Vue实例中使用。如:在Vue实例中添加该行时,会报错。这是因为当子组件注册到父组件的components时,Vue会编译好父组件的模块,相当于父组件中已经有子组件中的内容了,子标签是只能在父组件中被识别的。
- 解决办法: 想要子标签也生效,需在Vue实例的组件中注册该标签 cpn1: cpnC1
4.注册组件的语法糖
- 在上面注册组件的方式,可能会有些繁琐。Vue为了简化这个过程,提供了注册的语法糖。
- 主要是省去了调用Vue.extend()的步骤,而是直接使用一个对象来代替。
<body>
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
//1.注册全局组件的语法糖
Vue.component('cpn1', {
template:`
<div>
<h2>我是标题一</h2>
<p>hahahaha</p>
</div>`
})
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
//2.注册局部组件的语法糖
components:{
'cpn2': {
template:`
<div>
<h2>我是标题二</h2>
<p>emememem</p>
</div>`
}
}
})
</script>
</body>
5.template模板的分离写法
- 将其中的html代码分离出来,然后用 id 挂载到对应组件上,使结构更加清晰。
- Vue提供了两种方案:
①使用
<body>
<div id="app">
<cpn></cpn>
</div>
<!-- 模板分离写法1.script标签,注意:类型必须为text/x-template -->
<!--
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题一</h2>
<p>hahahaha</p>
</div>
</script> -->
<!-- 模板分离写法2.template标签 -->
<template id="cpn">
<div>
<h2>我是标题二</h2>
<p>emememem</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//1.注册一个全局组件
Vue.component('cpn',{
template: '#cpn'
})
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
}
})
</script>
</body>
6.组件中的数据存放问题
- 组件不可以访问Vue实例数据。
- 组件是一个有单独功能模板的封装,这个模块有属于自己的HTML模板和数据data。
- 组件对象也有一个data属性,但该属性必须是一个函数,并且该函数返回一个内部保存着数据的对象,函数每次返回一个新对象,保证各组件互不影响。
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>emememem</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn',{
template: '#cpn',
data() {
return {
title: '我是标题'
}
}
})
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
}
})
</script>
</body>
7.父子组件的通信
-
之前提过,子组件不能引用父组件或Vue实例的数据,但是在开发中,往往一些数据需要从上层传递到下层,例如在一个页面中,我们从服务器请求到的很多数据中有一部分,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这时并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
-
如何进行父子间通信?
1.通过props向子组件传递数据;
2.通过自定义事件$emit向父组件发送消息
7.1 父组件向子组件传递数据(props)
*props基本用法 *
- 在组件中,使用props来声明需要从父级接收到的数据。其属性名不能使用驼峰标识(小大写),中间使用“-”隔开
- props的值有两种方式:
1.字符串数组,数组中的字符串就是传递时的名称。
2.对象,对象可以设置传递时的类型,也可以设置默认值等。
<div id="app">
<cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//父传子:props
const cpn = {
template:'#cpn',
// 1.字符串数组
// props: ['cmovies','cmessage'],
// 2.对象
props: {
//1.可以进行类型限制
// c-movies: Array,
//2.也可以提供默认值,或设置其是否为必须传入的值
cmessage:{
type:String,
default:'aaaaaa',
required: true
},
// 类型是对象或者数组时,默认值必须是一个函数
cmovies: {
type: Array,
default(){
return []
}
}
},
data() {
return {}
}
}
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
movies :['海贼王','海王','海尔兄弟']
},
components:{
// 'cpn' : cpn
// 增强写法
cpn
}
})
</script>
7.2 子组件向父组件传递数据(自定义事件)
- 之前学过的v-on不仅仅可以用来监听DOM事件,也可以用于组件间的自定义事件。
- 自定义事件流程:在子组件中,通过**$emit()来触发事件;在父组件中,通过v-on**来监听子组件事件。
<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 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)
}
},
}
// 父组件
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
components:{
cpn
},
methods: {
cpnClick(item) {
console.log("cpnClick",item);
}
}
})
</script>
</body>
7.3 父子组件的访问方式
- 父组件访问子组件:使用 $children 或 ** $refs** (reference引用)
- 子组件访问父组件:使用 $parent
7.3.1 $children 的访问(父访问子)
- this.$children是一个数组类型,包括所有子组件对象。
- 这里我们通过一个遍历,取出所有子组件的message状态。
<div id="app">
<cpn></cpn>
<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();
// }
//2.$refs(常用) => 对象模型,默认为一个空对象 ref='aaa'
console.log(this.$refs.aaa)
}
},
components: {
cpn: {
template: '#cpn',
data() {
return{
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('showMessage');
}
},
}
}
})
</script>
7.3.2 $parent 的访问(子访问父)
- 为保证组件的可复用,开发中不建议使用。
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是子组件</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.访问父组件$parent
console.log(this.$parent);
console.log(this.$parent.name);
//2.访问根组件$root
console.log(this.$root);
console.log(this.$root.message);
}
}
}
}
}
}
})
</script>