目录
- 组件定义
- 全局组件
- 局部组件
- DOM模板解析注意事项
- data必须是函数
- 通过props向子组件传递数据
- 单个根元素
1.组件的定义
组件(component)是vue.js 最强大的功能之一。组件可以扩展HTML元素,封装可重用代码。在较高层面上,组件是自定义元素,Vue.js的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用is特性进行了扩展的原生HTML元素。所有Vue组件同时也都是Vue的实例,所以可接受相同的选项对象(除了一些根级特有的选项)并提供相同的生命周期钩子。

2. 全局组件
- 第一步都是先创建一个 Vue() 实例
// 实例化
new Vue({
el:"#app",
})
- 要注册一个全局组件,可以用
Vue.component()
Vue.component('my-component',{
//选项
})
组件注册成功后,就可以在<div id="app"></div>里使用自定义的标签了。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>第一个组件</title>
</head>
<body>
<div id="app">
<!-- 模板引用 -->
<my-component></my-component>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
//注册声明组件
Vue.component("my-component",{
data(){
return {
msg:"全局组件"
}
},
template:"<h4>{{msg}}</h4>"
})
//创建Vue()实例
var vm = new Vue({
el:"#app"
})
</script>
</body>
</html>
3. 局部组件

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>局部组件</title>
</head>
<body>
<div id="app">
<!-- 模板引用 -->
<my-component></my-component>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
//模板
var myComponent = {
template:"<h4>我是局部组件</h4>"
}
//创建Vue()实例
new Vue({
el:"#app",
components:{ //注册声明组件
myComponent
}
})
</script>
</body>
</html>
练习1:利用vue中的组件完成一行两列基本布局

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>一行两列</title>
<style type="text/css">
.row{
display: flex;
}
.col{
flex: 1;
height: 500px;
border: 1px solid #000;
}
</style>
</head>
<body>
<div id="app">
<div class="row">
<!-- 模板引用 -->
<cols></cols>
<cols></cols>
</div>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
//注册组件
Vue.component("cols",{
data(){
return{
msg:"col"
}
},
template:'<div class="col">{{msg}}</div>'
})
//创建Vue()实例
new Vue({
el:"#app"
})
</script>
</body>
</html>
4. DOM模板解析注意事项
当使用 DOM 作为模板时 (例如,使用 el 选项来把 Vue 实例挂载到一个已有内容的元素上),你会受到 HTML 本身的一些限制,因为 Vue 只有在浏览器解析、规范化模板之后才能获取其内容。尤其要注意,像 ul、ol、table、select 这样的元素里允许包含的元素有限制,而另一些像 option 这样的元素只能出现在某些特定元素的内部。
在自定义组件中使用这些受限制的元素时会导致一些问题,例如:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>注意</title>
</head>
<body>
<div id="app">
<table>
<my-tr></my-tr>
</table>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
//注册组件
Vue.component("my-tr",{
template:`
<tr>
<td>模板组件</td>
</tr>
`
})
//创建Vue()实例
new Vue({
el:"#app"
})
</script>
</body>
</html>
当你查看DOM元素的时候,你会发现这个并不符合HTML5规范。所以,我们在这里必须借助Vue中的特殊属性,is属性

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>注意</title>
</head>
<body>
<div id="app">
<table>
<tr is="my-tr"></tr>
</table>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
//注册组件
Vue.component("my-tr",{
template:`
<tr>
<td>模板组件</td>
</tr>
`
})
//创建Vue()实例
new Vue({
el:"#app"
})
</script>
</body>
</html>
5.data必须是函数
我们在使用组件的时候,必须要注意到而且也要认识,组件中的data选项它不是一个对象,它是一个函数,这点尤为重要。因为它需要每个实例可以维护一份被返回对象的独立拷贝。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>data函数</title>
</head>
<body>
<div id="app">
<my-btn></my-btn>
<my-btn></my-btn>
<my-btn></my-btn>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
//注册组件
Vue.component("myBtn",{
data(){
return {
num:0
}
},
template:`
<button @click="add">{{num}}</button>
`,
methods:{
add(){
this.num++
}
}
})
//创建Vue()实例
new Vue({
el:"#app"
})
</script>
</body>
</html>
6. 通过prop向子组件传递数据
6.1 父组件通过 prop给子组件下发数据,子组件通过事件($emit)给父组件发送消息。

6.2 prop 是父组件用来传递数据的一个自定义属性, 父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”。组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>data函数</title>
</head>
<body>
<div id="app">
<blog-post
v-for="post in bloginfo"
:key="post.id"
:content="post.content"
></blog-post>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
//注册组件
Vue.component("blog-post",{
props:["content"],
template:`
<h4>{{content}}</h4>
`
})
//创建Vue()实例
new Vue({
el:"#app",
data:{
bloginfo:[
{id:0,content:"这是第一条"},
{id:1,content:"这是第二条"},
{id:2,content:"这是第三条"}
]
}
})
</script>
</body>
</html>
练习
- 父组件向子组件传递值实现累加
- 父组件向子组件传递值实现select下拉列表展示
- 父组件发起ajax请求实现商品列表(axios)
练习一:父组件向子组件传递值实现累加

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>父组件向子组件传递值实现累加</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
</head>
<body>
<div id="app">
<count :num="3" @addincre="handClick"></count>
<count :num="2" @addincre="handClick"></count>
{{total}}
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script>
Vue.component('count',{
props:["num"],
data(){
return {
numcount:this.num
}
},
template:'<div @click="add">{{numcount}}</div>',
methods:{
add(){
this.numcount++
this.$emit('addincre')
}
}
})
var vm = new Vue({
el:"#app",
data:{
total:5
},
methods:{
handClick(){
this.total++
}
}
})
</script>
</body>
</html>
练习2:父组件向子组件传递值实现select下拉列表展示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>父组件向子组件传递值实现select下拉列表展示</title>
<script type="text/javascript" src="js/vue.js" ></script>
</head>
<body>
<div id="app">
<select v-model="fruit">
<option is="isoption" v-for="item in list" :item="item"></option>
</select>
</div>
<script>
Vue.component("isoption",{
props:["item"],
template:`
<option :value="item.title">{{item.title}}</option>
`
})
new Vue({
el:"#app",
data:{
list:[
{"title":"苹果1"},
{"title":"苹果2"},
{"title":"苹果3"},
{"title":"苹果4"}
],
fruit:"苹果3"
}
})
</script>
</body>
</html>
练习3:父组件发起ajax请求实现商品列表(axios)

mock数据:goods.json
{
"code": 200,
"msg": "",
"goodslist": [
{
"goodsId": 0,
"goodsImg": "//h2.appsimg.com/a.appsimg.com/upload/merchandise/pdcvis/607153/2018/0528/120/5ed543e8-b6dc-4fe3-bcf1-e393816b8d4b_5t.jpg",
"desc": "熙世界V领撞色花边针织女款短袖连衣裙",
"price": 99,
"discount": 809
},
{
"goodsId": 1,
"goodsImg": "//h2.appsimg.com/a.appsimg.com/upload/merchandise/pdcvis/2018/07/17/113/e4c755e3-1663-437e-9dde-a4979363a469_5t.jpg",
"desc": "娜尔思 | NAERSI/娜尔思秋季新品连衣裙",
"price": 929,
"discount": 939
},
{
"goodsId": 2,
"goodsImg": "//h2.appsimg.com/a.appsimg.com/upload/merchandise/pdcvop/00603727/10000646/2094163692-438051563145240605-438051563145240902-5.jpg",
"desc": "欧时力 | ochirly欧时力新女装拼接荷叶边刺绣网纱无袖连衣裙1JH2084210",
"price": 1119,
"discount": 8199
},
{
"goodsId": 3,
"goodsImg": "//h2.appsimg.com/a.appsimg.com/upload/merchandise/pdcvis/2018/07/17/113/e4c755e3-1663-437e-9dde-a4979363a469_5t.jpg",
"desc": "娜尔思 | NAERSI/娜尔思秋季新品连衣裙",
"price": 929,
"discount": 499
},
{
"goodsId": 4,
"goodsImg": "//h2.appsimg.com/a.appsimg.com/upload/merchandise/pdcvis/2018/08/02/34/b2d13309-0796-4408-beff-ce01f6295388_5t.jpg",
"desc": "哥弟 | 气质时尚短袖连衣裙",
"price": 929,
"discount": 8189
},
{
"goodsId": 5,
"goodsImg": "//h2.appsimg.com/a.appsimg.com/upload/merchandise/pdcvis/2018/06/07/147/fd624450-a586-4b7f-99aa-dbb8e272c26d_5t.jpg",
"desc": "裂帛 | 立领刺绣七分袖雪纺连衣裙女",
"price": 329,
"discount": 2399
}
]
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>商品列表</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link href="../css/mui.css" rel="stylesheet" />
<style>
*{
margin: 0;
padding: 0;
}
header{
height: 40px;
box-sizing: border-box;
padding: 8px 12px;
background: #fff;
border-bottom: 1px solid #ddd;
text-align: center;
}
header a{
color: #666;
}
.mui-content a img{
width: 100%;
}
.main{
}
.main .row{
}
.main .row div{
background: #fff;
text-overflow:ellipsis;
float: left;
margin: 5px;
box-sizing: border-box;
padding: 5px;
}
.mui-col-sm-6{
width: 47%;
}
.price{
font-size: 18px;
font-weight: bold;
color: #d90000;
}
.oldPrice{
font-size: 14px;
text-decoration: line-through;
}
.shopTitle{
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
</style>
</head>
<body>
<div id="app">
<header>
<a href="javascript:;" class="back mui-pull-left"><i class="mui-icon mui-icon-back"></i></a>
<a href="javascript:;" class="title">风衣</a>
<a href="javascript:;" class="home mui-pull-right"><i class="mui-icon mui-icon-home"></i></a>
</header>
<div class="main">
<div class="mui-content">
<div class="row">
<!-- 加载商品列表 -->
<goodsitem
v-for="(item, index) in goodsInfo"
:key = "item.id"
:item="item"
></goodsitem>
</div>
</div>
</div>
</div>
<script src="https://www.tanshangbiao.cn/pack/vue/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
//商品组件
Vue.component('goodsitem',{
data(){
return{
}
},
props:["item"],
template:`
<div class="mui-col-sm-6 mui-col-xs-12 ">
<a href="javascript:;" data-goodsid="item.goodsId">
<img :src="item.goodsImg" alt="">
<p class="shopTitle">{{item.desc}}</p>
<p>
<span class="price">¥{{item.price}}</span>
<span class="oldPrice">¥{{item.discount}}</span>
</p>
</a>
</div>
`
})
var vm = new Vue({
el:"#app",
data:{
goodsInfo:[]
},
created(){
var _this = this
axios.get('../data/goods.json')
.then(function(res){
_this.goodsInfo = res.data.goodslist
console.log(res)
})
.catch(function(error){
console.log(error)
})
}
})
</script>
<script src="../js/mui.js"></script>
<script type="text/javascript">
mui.init()
</script>
</body>
</html>
7.单个根元素
当你构建一个组件的时候,你很有可能会出现 every component must have a single root element (每个组件必须只有一个根元素 ),在编写组件的过程中,切记所有的元素都必须要有一个根元素包裹起来,一般选择使用div标签。