06_表单绑定、组件化及 ES6 语法的补充
- 表单绑定 v-model
- 作用:实现表单元素和数据的双向绑定;
- 本质:
v-bind
指令和v-on
指令的结合,v-bind
绑定一个 value 属性,并且v-on
给当前元素绑定 input 事件;
<input type="text" v-bind:value='message' v-on:input='message=$event.target.value'>
- 与其它标签的结合使用(见代码): 1.
radio
2.checkbox
单选:一般传递一个布尔值;多选:传递数组 3.select
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 注:一般一个label会包一个input,这样点击文字的时候也会有反应 -->
<div id="container">
<!-- 1. 与 radio 的结合使用 -->
<input type="radio" name="sex" v-model='sex' value="男">男
<input type="radio" name="sex" v-model='sex' value="女">女
<h3>您选择的是:{{sex}}</h3>
<!-- 2. 与 checkbox 的结合使用 -->
<input type="checkbox" id="agree" v-model='isAgree'>同意协议
<button v-bind:disabled='!isAgree'>下一步</button>
<h3>您选择的是:{{isAgree}}</h3>
<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'>打排球
<h3>您选择的是:{{hobbies}}</h3>
<!-- 3. 与 select 的结合使用 -->
<select v-model='fruits'>
<option>苹果</option>
<option>西瓜</option>
<option>香蕉</option>
<option>草莓</option>
</select>
<h3>您选择的是:{{fruits}}</h3>
<select v-model='moreFruits' multiple>
<option>苹果</option>
<option>西瓜</option>
<option>香蕉</option>
<option>草莓</option>
</select>
<h3>您选择的是:{{moreFruits}}</h3>
<!-- 值绑定 -->
<label v-for='attr in originHobbies'>
<input type="checkbox" :value="attr" v-model='hobbies'>{{attr}}
</label>
<h3>您选择的是:{{hobbies}}</h3>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: '#container',
data: {
message: 'hello',
sex: '男',
isAgree: true,
hobbies: ['打篮球'],
originHobbies: ['打篮球', '打羽毛球', '打乒乓球', '打排球'],
fruits: '',
moreFruits: []
}
})
</script>
</body>
</html>
以上代码引出一个知识点就是关于值绑定,项目实际开发过程中,不会让以上的例如爱好等数据定死,而是从服务器端请求回来的,所以要使用值绑定的操作,其实还是v-bind
的使用。
- 修饰符
lazy
回车或失去焦点时触发,双向绑定中,由于前台页面不需要实时的与数据像绑定,只需要用户输入结束时再更新数据,故有此修饰符来减少刷新次数;number
保证数据的类型是 number 类型,若没有此修饰符,即使在 input 中指定类型为 number,在你改变这个值之后,浏览器都会自动变成 String 类型来处理;trim
去掉前面后面的空格,中间的空格是不会去的;
组件化
**什么是组件化?**
一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立功能,那么之后整个页面的管理和维护就会变得非常容易了。
**Vue的组件化思想** 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件,用来构造我们的应用,任何的应用都会被抽象成一棵组件树。 **注册组件的基本步骤**(代码如下) 1. 创建组件构造器(调用``Vue.extend()``方法创造组件构造器); 2. 注册组件(调用``Vue.component(使用的标签名,构造器实例名)``方法); 3. 使用组件(在**Vue实例的作用范围内**使用组件)。
<div id="app">
<my-cpn></my-cpn>
</div>
<script src="./vue.js"></script>
<script>
//1.创建组建构造器
const cpn = Vue.extend({
template: `
<div>
<p>这是模板嗷!</p>
</div>
`
});
//2.注册组件
Vue.component('my-cpn', cpn);
const app = new Vue({
el: '#app'
})
</script>
全局组件和局部组件的创建(代码如下)
全局组件可以在多个 Vue 的实例下面使用,而局部组件只有在特定的实例下面才能使用。
<body>
<div id="container">
<my-tpl></my-tpl>
<my-tpl></my-tpl>
<my-tpl></my-tpl>
<tpl></tpl>
</div>
<div id="box">
<!-- <tpl></tpl> -->
<tpl></tpl>
</div>
<script src="./vue.js"></script>
<script>
//全局组件 只有在Vue的实例对象中才能用
const cpn = Vue.extend({
template: `
<div>
<p>这是全局组件</p>
</div>
`
});
const cpns = Vue.extend({
template: `
<div>
<p>这是局部组件</p>
</div>
`
});
Vue.component('my-tpl', cpn);
const app = new Vue({
el: '#container'
});
const box = new Vue({
el: '#box',
//局部组件
components: {
// mytpl使用组件时的标签名
tpl: cpns
}
})
</script>
</body>
父组件和子组件的创建(代码如下)
<body>
<div id="container">
<cpn2></cpn2>
<cpn1></cpn1>
</div>
<script src="./vue.js"></script>
<script>
//子组件
const cpnC1 = Vue.extend({
template:`
<div>
<p>这是第一个</p>
</div>
`
});
//父组件
const cpnC2 = Vue.extend({
template:`
<div>
<p>这是第二个</p>
<cpn1></cpn1>
</div>
`,
components:{
cpn1:cpnC1
}
});
const app = new Vue({
el: '#container',
components:{
cpn2:cpnC2,
cpn1:cpnC1
}
});
</script>
</body>
这个父组件和子组件我口头表述也有点绕,还是看代码吧,具体就是在父组件被创建的时候,模板中使用到了子组件,就可以在父组件构造器中增添一个compontents
属性,此属性下面跟着的对象就是原本正常注册组件的两个参数,即使用到的标签名和模板的标签名。
注册组件的语法糖形式(代码如下) 不难发现,在注册组件时传递的第二个参数就代表着创建组件构造器的的实例对象,我们只需将实例对象即template
部分传递到注册组件中即可,这样一来就不用写Vue.extend()
方法了
<body>
<div id="container">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="./vue.js"></script>
<script>
Vue.component('cpn1', {
template: `
<div>
<p>语法糖形式全局组件</p>
</div>
`
});
const app = new Vue({
el: '#container',
components: {
'cpn2': {
template: `
<div>
<p>语法糖形式局部组件</p>
</div>
`
}
}
});
</script>
</body>
模板的分离写法(代码如下) 以上的写法中,我们又发现,如果在 script 代码中书写大量的 HTML 语法显得非常臃肿,所以我们又可以将 template 进行分离,主要有两种方式。
- 使用
<script type="text/x-template" >
标签 - 使用
<template>
标签
将 HTML 标签写在这两个其中一个标签中均可。
<body>
<div id="container">
<cpn></cpn>
<cpn3></cpn3>
</div>
<script type="text/x-template" id="cpn1">
<div>
<p>全局分离这是模板哦</p>
</div>
</script>
<template id="cpn2">
<div>
<p>局部分离这也是模板哦</p>
</div>
</template>
<script src="./vue.js"></script>
<script>
Vue.component('cpn',{
template:'#cpn1'
});
const app = new Vue({
el: '#container',
components:{
cpn3:{
template:'#cpn2'
}
}
})
</script>
</body>
数据的问题 我们又想,以往的 Vue 实例中,可以很轻松的从 data 属性中获取数据,那么我现在如果在模板中也需要用到数据怎么办呢?可以直接像以往的 Vue 实例中拿到数据吗?答案是否定的,在组件内部是不能够访问实例数据的,此时问题就来了,怎么获取数据呢?实际上,在注册组件时的第二个传递对象的参数中,是可以添加 data 数据的,并且组件中的 data 必须是一个函数,返回值必须是一个对象。 至于为什么是一个函数,其实很好理解,如果像以往的 Vue 实例访问数据一样,那么如果有多个组件同时在对同一个数据进行处理的话,它们之间就会相互影响,这就违背了组件化的一个开发思想,所以 Vue 要求我们的 data 必须是一个函数,因为每当调用一次函数的时候,其内部都会开辟一块内存空间,这个内存空间是相互独立的,互不影响的,所以说这个值不会被其修改,避免了牵一发而动全身这样的情况。以下案例可以很好的说明这一点,代码如下:
<body>
<div id="container">
<tpl></tpl>
<tpl></tpl>
<tpl></tpl>
</div>
<template id="tpl">
<div>
<h1>当前计数:{{counter}}</h1>
<button v-on:click = 'add'>+</button>
<button @click = 'down'>-</button>
</div>
</template>
<script src="./vue.js"></script>
<script>
// const counter={
// counter:0
// }
Vue.component('tpl',{
template:'#tpl',
data(){
return {
counter:0
}
},
methods:{
add(){
this.counter++;
},
down(){
this.counter--;
}
}
});
const app = new Vue({
el: '#container'
})
</script>
</body>
此时我们可以把我注释的那部分去掉注释,并将counter:0
修改为counter
,这时候可以模拟 data 不是函数的情况,此时我们调用了多次的组件,当你点击第一个组件的按钮时,其它组件的值全都会一起改变,这就很好的说明了以上那一点。
父子组件的通信问题
- 父组件向子组件传递数据 我们通过 props 属性向子组件传递数据
<body>
<div id="container">
<!-- 如果没有v-bind会把movie直接当字符串输出 不会当成变量 -->
<cpn :cmessage="message" :cmovies="movie"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for='attr in cmovies'>{{attr}}</li>
</ul>
<p>{{cmovies}}</p>
<p>{{cmessage}}</p>
</div>
</template>
<script src="./vue.js"></script>
<script>
const cpn = {
template: '#cpn',
//props:['cmovies'],
props:{ //1.类型限制
//cmovies:Array,
// cmessage:String
//2.提供一些默认值 上面没有传使用默认值
cmessage:{
type:String,
default:'aaaa',
required:false //此属性必传 不传报错
},
// 类型是对象或者数组时 默认值必须是一个函数
cmovies:{
type:Array,
// default:[] //2.5.x 版本一下这样不会错 否则报错
default(){
return []
}
}
},
data(){
return{
}
}
};
const app = new Vue({
el: '#container',
data:{
message: 'hello',
movie: ['海王','海绵宝宝','海尔兄弟']
},
components:{
cpn
}
})
</script>
</body>
另外在 props 中还可以对数据进行一些类型 默认值 是否必须传递此属性等进行控制,可以学习一下
- 子组件向父组件传递数据 我们通过事件向父组件发送消息($emit),父组件监听子组件的事件即可
<body>
<div id="container">
<tpl v-on:sendclick='sendClicks'></tpl>
</div>
<template id="tpl">
<div>
<button v-for='attr in some' @click='btnClick(attr)'>{{attr.name}}</button>
</div>
</template>
<script src="./vue.js"></script>
<script>
const tpl = {
template:'#tpl',
data(){
return {
some:[
{id:'aaa',name:'热门推荐'},
{id:'bbb',name:'手机数码'},
{id:'ccc',name:'家用电器'},
{id:'ddd',name:'酒水饮料'}
]
}
},
methods: {
btnClick(attr){
//发送一个事件
this.$emit('sendclick',attr);
}
},
}
const app = new Vue({
el: '#container',
components:{
tpl
},
methods: {
sendClicks(attr){
console.log(attr);
}
},
})
</script>
</body>
注意以上所有的 props 中的命名好像不能遵循驼峰原则,浏览器会报错,之后在脚手架开发中是不会出现的,这点注意一下即可,改成小写或是加 - 都行;还有一个小问题即 组件的模板里如果有多个标签,要有一个根标签包裹在它们外面。
有关 ES6 语法的补充我把它放到和之前的博客一起好了,这里不再赘述了。