3.组件基础
#3.1.什么是组件?
#3.1.1.理解组件
前端组件化开发是目前非常流行的方式,什么是前端组件化开发呢?就是将页面的某一部分独立出来,将这一部分的数据、视图、以及一些控制逻辑封装到一个组件内部,暴露一些开箱即用的函数或者属性供外部组件调用。这种组织代码的开发方式我们称为组件化开发。通俗的说,我们需要把一个页面拆分成若干的小单元,每个小单元就是一个小组件,例如,一个网页,我们可以做如下拆分
image组件开发的好处就是可以复用代码,下面是组件库举例
image#3.1.2.vue中的组件
在vue中,所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。Vue组件带有一个名字,在根实例中,组件被定义为元素使用,下面我们来定义一个button计数器组件
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('button-counter',{
data(){
return {
num: 0
}
},
methods:{
fn(){
this.num++
}
},
template:'<button @click="fn">点击我,自己加1:{{num}}</button>'
});
let vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
注意:组件中的data必须写成函数的形式,如果不写成函数的形式,组件间数据改变会相互影响
#3.1.3.组件定义实例
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<div id="app">
<header-component></header-component>
<main-component></main-component>
<footer-component></footer-component>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//头部组件
Vue.component('header-component', {
template: "<div :style='styles'>这里是头部区域</div>",
computed: {
styles() {
return {
width: '100%',
height: '100px',
backgroundColor: 'black',
color: 'white',
textAlign: 'center',
lineHeight: '100px',
fontSize: '30px'
}
}
}
});
Vue.component('main-component', {
template: `<div :style="styles">
<mainleft-component></mainleft-component>
<mainright-component></mainright-component>
</div>`,
computed: {
styles() {
return {
width: '100%',
height: '500px',
backgroundColor: 'orangered',
paddingTop: '50px'
}
}
}
});
Vue.component('mainleft-component', {
template: '<div :style="styles"></div>',
computed: {
styles() {
return {
width: '35%',
height: '400px',
backgroundColor: 'green',
float: 'left'
}
}
}
});
Vue.component('mainright-component', {
template: '<div :style="styles"></div>',
computed: {
styles() {
return {
width: '60%',
height: '400px',
backgroundColor: 'blue',
float: 'right'
}
}
}
});
Vue.component('footer-component', {
template: '<div :style="styles">这里是底部区域</div>',
computed: {
styles() {
return {
width: '100%',
height: '150px',
backgroundColor: 'black',
color: 'white',
textAlign: 'center',
lineHeight: '150px',
fontSize: '30px'
}
}
}
});
let vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
#3.1.4.组件的父子关系
image#3.2.父组件向子组件传数据
组件有自己的作用域,并且相互之间是相互独立的,这样就涉及到组件间的通信问题,在子组件中是不能直接使用父组件中的数据的,要在子组件中使用父组件的数据,可以在子组件注册的时候用props选项来声明一个自定义属性,然后在使用组件的时候,通过这个自定义属性来绑定数据
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="showLogin">登录</button>
<button @click="showRegister">注册</button>
<template v-if="flagLogin">
<dialog-component :title="loginTitle"></dialog-component>
</template>
<template v-if="flagRegister">
<dialog-component :title="registerTitle"></dialog-component>
</template>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('dialog-component', {
template: '<div :style="box"><div :style="boxTitle">{{title}}</div></div>',
computed: {
box() {
return {
width: '300px',
height: '300px',
border: '1px solid black'
}
},
boxTitle() {
return {
backgroundColor: 'green',
textAlign: 'center',
height: '30px',
lineHeight: '30px',
color: 'white'
}
}
},
props:['title']
});
let vm = new Vue({
el: '#app',
data: {
flagLogin: false,
flagRegister: false,
loginTitle: '登录',
registerTitle: '注册'
},
methods: {
showLogin() {
this.flagLogin = true
},
showRegister(){
this.flagRegister = true
}
}
});
</script>
</body>
</html>
实例2: 新闻列表渲染
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<news-component :newsdata="newsdata" :newstype="newstype"></news-component>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('news-component', {
template: `<div :style="wrap">
<div :style="wrapTitle">{{newstype}}</div>
<div :style="content">
<ul v-for="item in newsdata">
<li>{{item.title}}</li>
</ul>
</div>
</div>`,
computed: {
wrap() {
return {
width: '300px',
height: '400px',
border: '1px solid black',
}
},
wrapTitle() {
return {
padding: '15px',
width: '100%',
borderBottom: '1px solid black',
boxSizing: 'border-box'
}
},
content(){
return {
padding: '10px'
}
}
},
props:['newsdata', 'newstype']
});
let vm = new Vue({
el: '#app',
data: {
newstype: '国内新闻',
newsdata: [
{'title': '国家药监局:武汉生物百白破疫苗不合格属偶发'},
{'title': '下月起 这部分人群的抚恤补助标准将再次提高'},
{'title': '85岁老人40万买保健品 身无分文流落街头称继续买'}
]
}
});
</script>
</body>
</html>
进一步拆分组件
Vue.component('news-component', {
template: `<div :style="wrap">
<div :style="wrapTitle">{{newstype}}</div>
<div :style="content">
<news-list :news="newsdata"></news-list>
</div>
</div>`,
computed: {
wrap() {
return {
width: '300px',
height: '400px',
border: '1px solid black',
}
},
wrapTitle() {
return {
padding: '15px',
width: '100%',
borderBottom: '1px solid black',
boxSizing: 'border-box'
}
},
content() {
return {
padding: '10px'
}
}
},
props: ['newsdata', 'newstype']
});
Vue.component('news-list', {
template: `<ul>
<template v-for="item in news">
<li>{{item.title}}</li>
</template>
</ul>`,
props:['news']
});
let vm = new Vue({
el: '#app',
data: {
newstype: '国内新闻',
newsdata: [
{'title': '国家药监局:武汉生物百白破疫苗不合格属偶发'},
{'title': '下月起 这部分人群的抚恤补助标准将再次提高'},
{'title': '85岁老人40万买保健品 身无分文流落街头称继续买'}
]
}
});
#3.3.子组件向父组件传数据
#3.3.1.自定义事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"><button-component v-on:myevent="sayhi"></button-component></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('button-component', {
template: `<button @click="$emit('myevent', 'nodeing')">点击</button>`
});
new Vue({
el: '#app',
methods: {
sayhi(name){
alert('hi,' + name)
}
}
})
</script>
</body>
</html>
注意: v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),因此,在定义事件名字的时候就不要用大写了,例如: myEvent,如果定义的名字为myEvent,在绑定事件的时候,v-on:myEvent会转成 v-on:myevent,这样就不会触发事件了
#3.3.2.实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-model="inputValue">
<button @click="add">增加</button>
<ul >
<list-item
v-for="(todo,index) in todos"
:key="index"
:todo="todo"
v-on:del="del_todo"
></list-item>
</ul>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('list-item', {
template: `<li @click="$emit('del',todo)">{{todo}}</li>`,
props: ['todos', 'todo']
});
new Vue({
el: '#app',
data: {
inputValue: '',
todos: []
},
methods: {
add(){
this.todos.push(this.inputValue);
this.inputValue = ''
},
del_todo(todo){
this.todos = this.todos.filter((item)=>{
return item !== todo
})
}
}
})
</script>
</body>
</html>
网友评论