vue——组件

1、组件概念

  vue的核心基础就是组件的使用,玩好了组件才能将前面学的基础更好的运用起来。组件的使用更使咱们的项目解耦合。更加符合vue的设计思想MVVM。javascript

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

一、组件是可复用的Vue实例

  组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 <button-counter>。咱们能够在一个经过 new Vue 建立的 Vue 根实例中,把这个组件做为自定义元素来使用:css

<div id="components-demo">
  <button-counter></button-counter>
</div>

new Vue({ el: '#components-demo' })

  由于组件是可复用的 Vue 实例,因此它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项html

二、组件可屡次复用

  能够将组件进行任意次数的复用:vue

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

  

  点击按钮时,每一个组件都会各自独立维护它的count。由于你每用一次组件,都会有它的新实例被建立。html5

三、组件中data必须是一个函数

  定义这个 <button-counter> 组件时,你可能会发现它的 data 并非像这样直接提供一个对象:java

data: {
  count: 0
} 

  取而代之的是,一个组件的 data 选项必须是一个函数,所以每一个实例能够维护一份被返回对象的独立的拷贝:node

data: function () {
  return {
    count: 0
  }
}

若是 Vue 没有这条规则,点击一个按钮就可能会影响到其它全部实例。app

2、组件的组织

  一般一个应用会以一棵嵌套的组件树的形式来组织:ide

  

  例如,你可能会有页头、侧边栏、内容区等组件,每一个组件又包含了其它的像导航连接、博文之类的组件。函数

  

  为了能在模板中使用,这些组件必须先注册以便 Vue 可以识别。这里有两种组件的注册类型:全局注册局部注册

一、vue中全局组件使用

  经过 Vue.component 全局注册:

Vue.component('my-component-name', {
  // ... options ...
})

  全局注册的组件能够用在其被注册以后的任何 (经过 new Vue) 新建立的 Vue 根实例,也包括其组件树中的全部子组件的模板中。

二、vue中局部组件的使用

(1)局部组件简单示例

<body>
    <div id="app"></div>
    <script type="text/javascript" src="./node_modules/vue/dist/vue.js"></script>
    <script type="text/javascript">
        // 声明头部组件
        var Vheader = {
            template: `
                <header class="head">
                    我是头部
                </header>
            `
        };

        // 1.声明入口组件
        /*
            1.头部组件
            2.侧边栏
            3.内容组件
            4.脚本组件
         */
        var Vmain = {
            template: `
                <div class="main">
                    我是入口
                    <Vheader></Vheader>
                </div>
            `,
            components:{
                // 挂载子组件
                Vheader,   // 等价于Vheader:Vheader
            }
        };

        new Vue({
            el: '#app',
            // 3.使用子组件
            template: `<Vmain/>`,   // 单闭合
            data: {

            },
            components: {
                // 2.声明变量,挂载子组件
                Vmain: Vmain
            }
        });
    </script>
</body>

  显示效果以下所示:

  

(2)局部组件复杂示例

  注意:先声明子组件、再挂载子组件、最后使用子组件。

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        * {
            padding: 0;
            margin: 0;
        }

        .main {
            width: 100%;
        }

        body {
            color: #fff;
        }

        .head {
            width: 100%;
            height: 70px;
            background-color: purple;
            text-align: center;
            font-size: 20px;
            line-height: 70px;
        }

        .wrap {
            width: 100%;
            height: 1200px;
        }

        .wrap .aside {
            width: 30%;
            height: 1200px;
            background-color:green;
            float: left;    /*侧边栏浮动*/
        }

        .wrap .content {
            width: 70%;
            height: 1200px;
            background-color: saddlebrown;
            float: left;    /*内容区浮动*/
        }
    </style>
</head>
<body>
    <div id="app">

    </div>
<script type="text/javascript" src="./vue.js"></script>
<script type="text/javascript">
    // 打油诗:先声子再挂子再用子
    var Vheader = {     // 先声明头部组件
        template:`
            <header class="head">
                我是头部
            </header>
        `
    };

    var Vaside = {     // 声明侧边栏组件
        template:`
            <div class="aside">
                我是侧边栏
            </div>
        `
    };

    var Vcontent = {     // 声明内容区组件
        template:`
            <div class="content">
                我是内容区域
            </div>
        `
    };

    // 第一步.声明入口组件
    /*
        1.头部组件
        2.侧边栏
        3.内容组件
        4.脚步组件
        以上组件分别挂载到入口组件里面去。
     */

    var Vmain = {       // 局部组件
        template:`
            <div class='main'>
                <Vheader></Vheader>
                <div class="wrap">
                    <Vaside/>
                    <Vcontent/>
                </div>
            </div>
        `,
        components:{
            // 等价于Vheader:Vheader,当两个词如出一辙时能够这样简写:
            Vheader,     // 挂载子组件:头部组件
            Vaside,      // 挂载子组件:侧边栏组件
            Vcontent
        }
    };

    new Vue({
        el:"#app",   // 注意一个vue里面只有一个el
        //第三步.使用子组件
        template:"<Vmain></Vmain>",
        data: {

        },
        components:{
            //第二步.声明变量,挂载子组件  key表示组件名  value表示组件对象
            Vmain:Vmain
        }
    });
</script>
</body>

  组件是可复用的Vue实例,而且带有一个名字:在这个例子中是 <Vheader>。咱们能够在一个经过 new Vue 建立的 Vue 根实例中,把这个组件做为自定义元素来使用。

  显示效果以下:

  

三、组件使用的总结

  组件使用打油诗:1.声子   2.挂子   3.用子。

<!-- 1.声子 -->
var App = {
    template:`
        <div class='app'></div>
    `
};

<!-- 2.挂子 -->
new Vue({
    el:"#app",
    template:"<App/>"
    components:{
        App
    }
}) 

3、经过Prop向子组件传递数据

  Prop 是你能够在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。那么就能够像访问data中的值同样来访问。

  为了给博文组件传递一个标题,咱们能够用一个 props 选项将其包含在该组件可接受的 prop 列表中:

// 全局组件
Vue.component('blog-post', {
    props: ['title'],
    template: '<h3>{{ title }}</h3>'
})

 

  一个组件默承认以拥有任意数量的 prop,任何值均可以传递给任何 prop。在上述模板中,你会发现咱们可以在组件实例中访问这个值,就像访问 data 中的值同样。

  一个 prop 被注册以后,你就能够像这样把数据做为一个自定义特性传递进来:

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

一、向子组件传递数据示例

<script type="text/javascript">
    var Vheader = {     // 先声明头部组件
        template:`
            <header class="head">
                <h3>{{title}}</h3>    // 模板语法渲染title
                <span>{{count}}</span>
                <button @click = 'count+=1'>点击</button>
            </header>
        `,
        data(){
            return {
                count: 0
            }
        },
        props:['title'],     // props接收title
        methods:{

        }
    };

    var Vmain = {       // 局部组件
        template:`
            <div class='main'>
                <a href="#">{{title}}</a>
                <Vheader v-bind:title = 'title'></Vheader>
                <div class="wrap">
                    <Vaside/>
                    <Vcontent/>
                </div>
            </div>
        `,
        components:{
            // 等价于Vheader:Vheader,当两个词如出一辙时能够这样简写:
            Vheader,     // 挂载子组件:头部组件
            Vaside,      // 挂载子组件:侧边栏组件
            Vcontent
        },
        props:['title']   // 自定义的属性
    };

    new Vue({
        el:"#app",   // 注意一个vue里面只有一个el
        //第三步.使用子组件
        template:"<Vmain v-bind:title='text'/>",    // title是属性名,'text'是数据属性的名字
        data: {
            text:"alex是SB"   // 在data中设置text,这是数据
        },
        components:{
            //第二步.挂载子组件 key表示组件名  value表示组件对象
            Vmain:Vmain
        }
    });
</script>

  显示效果以下所示:

  

二、总结流程

(1)在子组件中自定义特性

  props:['自定义的属性']

  当一个值传递给一个prop特性的时候,它就变成了那个组件实例的一个属性,能够像访问data中的值同样。

(2)要在父组件中导入子组件内部

  须要绑定自定义的属性<Vheader :title = '父组件中data声明的数据属性'/>

(3)注意

  一个组件默承认以拥有任意数量的prop,任何值均可以传递任何prop。

  在上述模板中,会发现咱们可以在组件实例中访问这个值,就像访问data中的值同样。

三、父组件向子组件传递博客信息示例

<script type="text/javascript">
    var Vcontent = {     // 声明内容区组件
        template:`
            <div class="content">
                <ul>
                    <li v-for="post in posts" :key="post.id">   // 对应id
                        <h3>个人博客标题:{{post.title}}</h3>
                        <p>个人博客内容:{{post.content}}</p>
                    </li>
                </ul>
            </div>
        `,
        props:['posts']
    };

    var Vmain = {       // 局部组件
        template:`
            <div class='main'>
                <a href="#">{{title}}</a>
                <Vheader v-bind:title = 'title'></Vheader>
                <div class="wrap">
                    <Vaside/>
                    <Vcontent v-bind:posts = 'appPosts' />
                </div>
            </div>
        `,
        components:{
            // 等价于Vheader:Vheader,当两个词如出一辙时能够这样简写:
            Vheader,     // 挂载子组件:头部组件
            Vaside,      // 挂载子组件:侧边栏组件
            Vcontent
        },
        props:['title', 'appPosts']
    };

    new Vue({
        el:"#app",   // 注意一个vue里面只有一个el
        //第三步.使用子组件
        template:"<Vmain v-bind:title='text' :appPosts = 'posts'/>",    // title对应属性名
        data: {
            text:"alex是SB",
            posts:[
                {id:1, title:"组件中传值1", content:"经过prop传递数据1"},
                {id:2, title:"组件中传值2", content:"经过prop传递数据22"},
                {id:3, title:"组件中传值3", content:"经过prop传递数据333"}
            ]
        },
        components:{
            //第二步.挂载子组件 key表示组件名  value表示组件对象
            Vmain:Vmain

        }
    });
</script>

  显示效果:

  

4、子组件经过事件向父级组件发送消息

  开发 <blog-post> 组件时,它的一些功能可能要求咱们和父级组件进行沟通。例如咱们可能会引入一个可访问性的功能来放大博文的字号,同时让页面的其它部分保持默认的字号。

  在其父组件中,咱们能够经过添加一个 postFontSize 数据属性来支持这个功能:

new Vue({
  el: '#blog-posts-events-demo',
  data: {
    posts: [/* ... */],
    postFontSize: 1
  }
})

  它能够在模板中用来控制全部博文的字号:

<div id="blog-posts-events-demo">
  <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
    ></blog-post>
  </div>
</div>

  在每篇文章正文前添加一个按钮来放大字号。

  当点击这个按钮时,咱们须要告诉父级组件放大全部博文的文本。幸亏 Vue 实例提供了一个自定义事件的系统来解决这个问题。咱们能够调用内建的 $emit 方法并传入事件的名字,来向父级组件触发一个事件:

Vue.component('blog-post', {
    props: ['post'],
    template: `
        <div class="blog-post">
            <h3>{{ post.title }}</h3>
            /*<button>
                Enlarge text
            </button>*/
            <button v-on:click="$emit('enlarge-text')">
                Enlarge text
            </button>
            <div v-html="post.content"></div>
        </div>
    `
})

一、经过自定义事件向父级组件发送消息示例

<script type="text/javascript">
    var Vcontent = {     // 声明内容区组件
        template:`
            <div class="content">
                <ul>
                    <li v-for="post in posts" :key="post.id">   // 对应id
                        <h3>个人博客标题:{{post.title}}</h3>
                        <p>个人博客内容:{{post.content}}</p>
                    </li>
                </ul>
                <button @click="changeSize">改变字体大小</button>
            </div>
        `,
        props:['posts'],
        methods:{   // 声明方法
            changeSize(){
                // 经过$emit()方法传入事件名字,来触发自定义的事件
                this.$emit('postChangeSize')
            }
        }
    };

    var Vmain = {       // 局部组件
        template:`
            <div class='main' :style="{fontSize:fontsize+'px'}">
                <a href="#">{{title}}</a>
                <Vheader v-bind:title = 'title'></Vheader>
                <div class="wrap">
                    <Vaside/>
                    <Vcontent v-bind:posts = 'appPosts' @postChangeSize="fontsize+=1" />    // 绑定自定义属性和自定义事件
                </div>
            </div>
        `,
        data(){
            return {
                fontsize:14   // 默认字体大小为14
            }
        },
        components:{
            // 等价于Vheader:Vheader,当两个词如出一辙时能够这样简写:
            Vheader,     // 挂载子组件:头部组件
            Vaside,      // 挂载子组件:侧边栏组件
            Vcontent
        },
        props:['title', 'appPosts']
    };
    
    new Vue({...)};
</script>

  经过点击按钮能够不断修改字体大小,具体驱动流程以下所示:

  

  页面显示效果以下所示:

  

二、利用$emit第二个参数传值修改示例以下

  $emit第一个参数是自定义的事件名字,第二个参数就是传递的值

    var Vcontent = {     // 声明内容区组件
        template:`
            <div class="content">
                <ul>
                    <li v-for="post in posts" :key="post.id">   // 对应id
                        <h3>个人博客标题:{{post.title}}</h3>
                        <p>个人博客内容:{{post.content}}</p>
                    </li>
                </ul>
                <button @click="changeSize">改变字体大小</button>
            </div>
        `,
        props:['posts'],
        methods:{   // 声明方法
            changeSize(){
                // 经过$emit()方法来触发自定义的事件
                // 第一个参数是自定义的事件名字;第二个参数就是传递的值。
          // this指的是vue实例化对象的子类 this.$emit('postChangeSize', 1) } } }; var Vmain = { // 局部组件 template:` <div class='main' :style="{fontSize:fontsize+'px'}"> <a href="#">{{title}}</a> <Vheader v-bind:title = 'title'></Vheader> <div class="wrap"> <Vaside/> <Vcontent v-bind:posts = 'appPosts' @postChangeSize="clickHandler" /> // 绑定自定义属性和自定义事件 </div> </div> `, methods:{ clickHandler(value){ this.fontsize += this.fontsize+1; } }, data(){ return { fontsize:14 // 默认字体大小为14 } }, components:{ // 等价于Vheader:Vheader,当两个词如出一辙时能够这样简写: Vheader, // 挂载子组件:头部组件 Vaside, // 挂载子组件:侧边栏组件 Vcontent }, props:['title', 'appPosts'] };

  组件传值是vue中最重要的知识点。

三、从子组件传递数据到父组件总结

  (1)给子组件中的某个按钮绑定原生事件,能够调用内建方法this.$emit('自定义事件名','传递的数据'),来向父级组件触发一个自定义的事件。

  (2)在父组件中的子组件标签中要绑定自定义的事件

5、全局组件(公共组件)的建立和使用

  全局注册的组件能够用在其被注册以后的任何(经过 new Vue)新建立的 Vue 根实例,也包括其组件树中的全部子组件的模板中。

一、公共组件示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        * {
            padding:0;
            margin:0;
        }
        #head {
            width: 100%;
            height: 80px;
            background-color: purple;
        }
    </style>
</head>
<body>
    <div id="app"></div>
    <script type="text/javascript" src="./node_modules/vue/dist/vue.min.js"></script>
    <script type="text/javascript">
        // 建立公共组件
        // 第一个参数是公共组件的名字;第二个参数options(与局部组件相同)
        Vue.component('Vbtn', {
            template:`<button>登陆</button>`
        });

        var Vheader = {   // 这里使用Vheader的缘故是,html5中有header标签
           template:`<div id="header">
                <Vbtn></Vbtn>
                <Vbtn></Vbtn>
                <Vbtn></Vbtn>
           </div>`
        };

        // 局部组件的使用
        var App = {
            template:`<div>
                <Vheader></Vheader>
            </div>`,
            components: {
                Vheader
            }
        };

        new Vue({
            el: '#app',
            data(){   // 组件中必定是函数

            },
            template:'<App/>',   // 注意必定是闭合标签
            components: {
                App    // App组件
            }
        })
    </script>
</body>
</html>

  页面显示效果以下:

  

二、公共组件使用总结

全局组件的使用:
Vue.component('全局组件的名称', {
    跟new.Vue()实例化对象中的options是同样的,可是要注意是:
    不论是公共组件仍是局部组件,data必须是一个函数,函数必定要有返回值(哪怕是一个空对象{})
})

6、内置组件slot(插槽)的用法

  Vue 实现了一套内容分发的 API,这套 API 基于当前的 Web Components 规范草案,将 <slot> 元素做为承载分发内容的出口。 

一、修改上面公共组件实例,实现内容分发

<head>代码省略</head>
<body>
    <div id="app"></div>
    <script type="text/javascript" src="./vue.js"></script>
    <script type="text/javascript">
        // 建立公共组件
        // 第一个参数是公共组件的名字;第二个参数options
        Vue.component('Vbtn', {
            template:`<button>
                <slot></slot>
            </button>`
        });

        var Vheader = {   // 这里使用Vheader的缘故是,html5中有header标签
           template:`<div id="header">
                <Vbtn>登陆</Vbtn>
                <Vbtn>注册</Vbtn>
                <Vbtn>提交</Vbtn>
           </div>`
        };
    
    """代码省略"""
    </script>
</body>
</html>

  若是不使用slot,像上面这么在模板中添加不一样的信息是不显示的。使用vue内置组件slot后显示效果以下:

  

二、封装按钮添加样式

<script type="text/javascript">
    // 建立公共组件
    // 第一个参数是公共组件的名字;第二个参数options
    Vue.component('Vbtn', {
        template: `<button class="default" :class="type">
                <slot></slot>
            </button>`,
        props: ['type']
    });


    var Vheader = {   // 这里使用Vheader的缘故是,html5中有header标签
        template: `<div id="header">
                <Vbtn>登陆</Vbtn>
                <Vbtn>注册</Vbtn>
                <Vbtn>提交</Vbtn>
                <Vbtn>默认按钮</Vbtn>
                <Vbtn type="primary">主要按钮</Vbtn>
                <Vbtn type="success">成功按钮</Vbtn>
           </div>`
    };

    """代码省略"""
</script>

(1)给按钮绑定class  

  这里应用了vue的v-bind:class来给每一个标签元素添加class。 

  props:一个 prop 被注册以后,能够把数据做为一个自定义特性传递进来。

  所以在“<Vbtn type="success">成功按钮</Vbtn> ”,中给type定义了一个属性,经过props组件传值传递到公共组件中,由此就能够对网页中全部的按钮进行一个修饰。

(2)使用elementUI,调整按钮样式

  Element - 网站快速成型工具:http://element-cn.eleme.io/#/zh-CN

  

  添加css代码以下所示:

    <style>
        * {
            padding: 0;
            margin: 0;
        }

        #head {
            width: 100%;
            height: 80px;
            background-color: purple;
        }

        button {
            display: inline-block;
            line-height: 1;
            white-space: nowrap;
            cursor: pointer;
            background: #fff;
            border: 1px solid #dcdfe6;
            border-top-color: rgb(220, 223, 230);
            border-right-color: rgb(220, 223, 230);
            border-bottom-color: rgb(220, 223, 230);
            border-left-color: rgb(220, 223, 230);
            border-color: #dcdfe6;
            color: #606266;
            text-align: center;
            box-sizing: border-box;
            outline: none;
            margin: 0;
            transition: .1s;
            font-weight: 500;
            padding: 12px 20px;
            font-size: 14px;
            border-radius: 4px;
        }

        .primary {
            color: #fff;
            background-color: #409eff;
            border-color: #409eff;
        }

        .success {
            color: #fff;
            background-color: #67c23a;
            border-color: #67c23a;
        }
    </style>

(3)显示效果以下所示

  

相关文章
相关标签/搜索