目录

  1. 组件定义
  2. 全局组件
  3. 局部组件
  4. DOM模板解析注意事项
  5. data必须是函数
  6. 通过props向子组件传递数据
  7. 单个根元素

1.组件的定义

组件(component)是vue.js 最强大的功能之一。组件可以扩展HTML元素,封装可重用代码。在较高层面上,组件是自定义元素,Vue.js的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用is特性进行了扩展的原生HTML元素。所有Vue组件同时也都是Vue的实例,所以可接受相同的选项对象(除了一些根级特有的选项)并提供相同的生命周期钩子。

2. 全局组件

  1. 第一步都是先创建一个 Vue() 实例
// 实例化
new Vue({
    el:"#app",
})
  1. 要注册一个全局组件,可以用 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>

练习

  1. 父组件向子组件传递值实现累加
  2. 父组件向子组件传递值实现select下拉列表展示
  3. 父组件发起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标签。