JS组件系列——又一款MVVM组件:Vue(二:构建本身的Vue组件)

前言:转眼距离上篇 JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查) 已有好几个月了,今天打算将它捡起来,发现很久不用,Vue相关技术点都生疏很多。通过这几个月的时间,Vue的发展也是异常迅猛,不过这好像和博主都没什么太大的关系,博主仍是老老实实研究本身的技术吧。技术之路还很长,且行且研究吧。javascript

本文原创地址:http://www.cnblogs.com/landeanfen/p/6518679.htmlcss

1、为何组件很重要

前两天,看到一篇关于 汇总vue开源项目 的文章,资源很是丰富,不得不感叹开源社区的强大。随便点进去看了几个UI组件,基本都不是原生的html用法,若是你不懂Vue的组件相关概念,看到一些“稀奇古怪”的标签写法,可能会使用,但确定没法理解为何能够这么写。好比咱们随便找了一个名叫IView的来看看:html

<i-input type="text" :value.sync="formInline.user" placeholder="Username">
     <Icon type="ios-person-outline" slot="prepend"></Icon>
</i-input>

这样一段代码就能获得以下效果:前端

博主好奇心重,打算一探究竟,今天就和你们一块儿来看一看这些“古怪”写法的出处。但愿经过本文,让你有一种“哦,原来是这样,不过如此嘛!”的感受!vue

2、Vue里面的组件基础知识

一、组件的概念

官方定义:组件(Component)是 Vue.js 最强大的功能之一。组件能够扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些状况下,组件也能够是原生 HTML 元素的形式,以 is 特性扩展。java

博主理解:Vue里面的组件能够理解为经过对普通html标签的封装,获得一套独立并且能够通用的html标签,咱们在页面里面使用这些标签传入相应的参数便可调用封装好的组件。经过下面这张图相信能够一目了然。jquery

由普通的html标签form、input、button、label组成了一个新的元素集合,咱们命名为i-form,这个i-form就是vue里面组件的概念。咱们在页面里面使用<i-form></i-form>时,经过vue的组件渲染机制,在浏览器里面最终就能够显示成为普通的html标签form、input、button、label。webpack

二、组件原理

 经过上图咱们知道,vue里面的组件实际上就是一些普通html元素的集合。那么,它是如何将这些自定义标签转换为普通html标签的呢?在介绍组件原理以前,仍是先来看一个最简单的组件实例。ios

  <div style="text-align:center;margin-top:200px;" id="app">
        <!-- 3. 在Vue实例里面使用组件-->
        <b-component></b-component>
    </div>

    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        // 1.建立组件构造器
        var myComponent = Vue.extend({
            template: '<div id="bComponent">我是自定义组件的内容</div>'
        });

        //2.注册组件到vue里面
        Vue.component('b-component', myComponent)

        new Vue({
            el: '#app',
        });
        
    </script>

获得效果:web

整个过程不难理解,主要分为三个大的步骤:

  1. 定义一个组件构造器,声明组件要渲染的html内容
  2. 将组件构造器注册到Vue的组件系统里面,使其成为Vue的一个组件,给组件取一个名称,好比b-component
  3. 在Vue的实例里面使用组件。由于上面两步定义了Vue的组件,既然是Vue的组件,那么要使用组件,首先得有一个Vue的实例,组件必需要在Vue的实例里面使用。

在网上找到一张图能够清晰地解释组件的整个渲染过程。

其实有时为了简便,咱们常将一、2步合并,代码以下:

  <div style="text-align:center;margin-top:200px;" id="app">
        <!-- 2. 在Vue实例里面使用组件-->
        <b-component></b-component>
    </div>

    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        //1.建立组件构造器,注册组件到vue里面
        Vue.component('b-component', {
            template: '<div id="bComponent">我是自定义组件的内容</div>'
        })

        new Vue({
            el: '#app',
        });
        
    </script>

获得的结果和上述相同。 

三、组件使用

上述解释了下组件的定义和原理,关于组件的简单实用,咱们主要介绍如下几个方面。

(1)组件的做用域

这个应该不难理解,组件分为全局组件和局部组件,也就是说,你能够在页面上面定义一个全局组件,页面上面的任何Vue实例均可使用;而对于局部组件,是和具体的Vue实例相关的,只能在当前Vue实例里面使用组件。还有一点须要说明:组件必须在Vue的实例里面使用,在Vue实例以外使用组件无效。经过下面一个例子便可清晰说明它们的区别。

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component></b-component>
        <b-component2></b-component2>
    </div>
    <div style="text-align:center;margin-top:50px;" id="app2">
        <b-component></b-component>
        <b-component2></b-component2>
    </div>

    <b-component></b-component>
    <b-component2></b-component2>
   
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">

        //定义组件
        Vue.component('b-component', {
            template: '<div id="bComponent">我是全局组件,任何Vue实例均可使用</div>'
        })

        new Vue({
            el: '#app',
            components: {
                'b-component2': {
                    template: '<div id="bComponent">我是局部组件,只能在app这个div里面使用</div>'
                }
            }
        });
        new Vue({
            el: '#app2',
        });
        
    </script>
</body>

获得结果:

(2)组件的传值

组件实例的做用域是孤立的。这意味着不能而且不该该在子组件的模板内直接引用父组件的数据。可使用 props 把数据传给子组件。这段话怎么理解呢?咱们先来看几个例子。

  • 静态Prop

咱们先来看看下面的一段简单的代码

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component componentmessage="你好"></b-component>
    </div>
   
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        Vue.component('b-component', {
            template: '<div>{{componentmessage}}</div>',
            props: ['componentmessage'],
        })

        new Vue({
            el: '#app'
        });
    </script>
</body>

经过在组件里面使用props属性,将外部的值传入组件模板。最终渲染到页面上面就获得“<div>你好</div>”这么一段html

  • 动态Prop

在多数状况下,咱们在使用Vue实例的时候,通常经过data属性传入模型,好比

    new Vue({
            el: '#app',
            data: {
                name: 'Jim',
                Age: '28'
            }
        });

这个时候,咱们的name和age如何传到组件实例里面呢?

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component v-bind:my-name="name" v-bind:my-age="Age"></b-component>
    </div>
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">
        Vue.component('b-component', {
            template: '<div>姓名:{{myName}},年龄:{{myAge}}</div>',
            props: ['myName', 'myAge'],
        })

        new Vue({
            el: '#app',
            data: {
                name: 'Jim',
                Age: '28'
            }
        });
    </script>
</body>

获得结果

须要说明几点:

  • 在使用标签<b-component>的时候,经过v-bind命令,将Vue实例里面的name、Age属性以别名my-name、my-age的形式传入组件实例。
  • 为何my-name、my-age传到组件里面就变成了['myName', 'myAge']呢?这是由于在子组件中定义prop时,使用了camelCase命名法。因为HTML特性不区分大小写,camelCase的prop用于特性时,须要转为 kebab-case(短横线隔开)。
  • 不少状况下,v-bind能够简写为冒号(:),因此上述代码也能够这么写: <b-component :my-name="name" :my-age="Age"></b-component> 。效果也是同样。
  • 这里很恶心的还有一点,在Props里面定义的必需要使用所谓“驼峰式”的方式来定义变量,不然会由于一个变量名大小写搞死你。好比props:["myName"]这样能够正确,可是若是props:["myname"]这样的话就错误,使用myname取值会是undefined。博主第一次玩这个玩意找了好半天,新手必定注意,大坑,大坑,大坑!慎入!

在封装组件里面,props属性使用很是多,更多props用法可参见文档https://vuefe.cn/v2/guide/components.html#Prop

(3)组件的插槽

在使用组件的时候,咱们常常须要在组件实例向组件模板传入html元素,这个时候咱们就须要在组件的模板标签里面留一些占位符(俗称“坑”),而后在具体的组件实例里面传入标签来填“坑”,在Vue里面这些“坑”也叫插槽,使用<slot>来解决。对于开发人员来讲,这个其实不陌生,从原来的母版页到如今的layout页面,基本都是使用的这种原理。

<body>
    <div style="text-align:center;margin-top:50px;" id="app">
        <b-component>
            <h1 slot="header">这里多是一个页面标题</h1>
            <h2 slot="content">姓名:{{name}},年龄:{{Age}}</h2>
            <h1 slot="footer">尾部</h1>
        </b-component>
    </div>
    <template id="slottest">
        <div class="container">
            <header>
                <slot name="header"></slot>
            </header>
            <main>
                <slot name="content"></slot>
            </main>
            <footer>
                <slot name="footer"></slot>
            </footer>
        </div>
    </template>
   
    <script src="Content/vue/dist/vue.js"></script>
    <script type="text/javascript">

        Vue.component('b-component', {
            template: '#slottest',
        })

        new Vue({
            el: '#app',
            data: {
                name: 'Jim',
                Age: '28'
            }
        });
    </script>
</body>

获得结果

上述代码应该不难理解,就是一个“挖坑”和“填坑”的过程。顺便要提一笔的是,Vue的组件支持使用<templete>的模式来定义标签模板,使用更加灵活和方便。

3、封装本身的Component

以上讲了这么多,都是关于Vue里面Component组件的一部分主要知识点,其余还有不少都没有展开说,由于这方面的文档也是至关丰富,园子里面keepfool的博文关于Vue组件的部分就介绍得很是详细,再者,Vue中文文档也是有很详细的用法说明。接下来,博主打算经过几个实例来讲明使用组件给咱们前端开发带来的好处。

一、使用Component封装bootstrapTable

对于项目里面的表格展现,能够基于Vue能够本身开发一套,可是说实话,这个工程量仍是蛮大的,而且若是要作好,要兼容不少表格的功能,从零开始去重复造轮子实在是有点太耗时。博主项目里面大部分的表格用的bootstrapTable组件,因而博主一直在想能不能封装一套基于Vue的bootstrapTable的用法。网上也找不到相似的封装示例,大部分使用vue的框架都会本身去实现一套本身的表格样式。因而打算本身动手试试,正好也能够熟悉下component的用法。

首先新建一个js文件命名为vue.bootstrapTable.js。博主直接将代码贴出来,若是有不完善的地方,但愿你们斧正。

(function ($) {
    //表格初始化的默认参数
    var defaults = {
        method: 'get',                      
        toolbar: '#toolbar',                
        striped: true,                      
        cache: false,                       
        pagination: true,                   
    };
    //注册bootstrapTable组件
    Vue.component('bootstrap-table', {
        template: '<table></table>',
        props: {
            'tableParam': { type: Object }
        },
        //组件渲染以前
        created: function () {
            //debugger;

        },
        //组件渲染以后
        mounted: function () {
            debugger;
            var params = $.extend({}, defaults, this.tableParam || {});
            this.bootstraptable = $(this.$el).bootstrapTable(params);
        }
    });

})(jQuery);

而后再界面上面

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <link href="Content/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <link href="Content/bootstrap-table/bootstrap-table.css" rel="stylesheet" />
</head>
<body>
    <div id="app">
        <bootstrap-table :table-param="tableParam"></bootstrap-table>
    </div>

    <script src="Content/jquery-1.9.1.min.js"></script>
    <script src="Content/bootstrap/js/bootstrap.js"></script>
    <script src="Content/bootstrap-table/bootstrap-table.js"></script>
    <script src="Content/vue/dist/vue.js"></script>
    <script src="Content/vue-component/vue.bootstrapTable.js"></script>
    <script type="text/javascript">
        var testData = [
      { Name: 'Jim', Age: 30, Remark: '鸡母格林' },
      { Name: 'Kate', Age: 28, Remark: '凯特' },
      { Name: 'Lucy', Age: 20, Remark: '露西' },
      { Name: 'Uncle Wang', Age: 45, Remark: '严厉的王老师' }
        ];

        new Vue({
            el: '#app',
            data: {
                tableParam: {
                    data: testData,
                    columns: [
                        {
                            field: 'Name',
                            title:'姓名'
                        }, {
                            field: 'Age',
                            title: '年龄'
                        }, {
                            field: 'Remark',
                            title: '备注'
                        }]
                },
            }
        });
        
    </script>
</body>

最后测试结果:

纵观这数十行代码,基本原来其实很简单,经过组件的props功能将<bootstrap-table>实例中的初始化参数传到组件模板里面,而后再组件加载完成以后初始化bootstrapTable,最后将bootstrapTable的实例给到组件,这样在就能够经过Vue的实例经过子组件调用到当前初始化的bootstrapTable对象。

二、封装select

关于select的封装,仍是打算基于第三方组件来作。一样的,咱们新建一个js文件,命名为vue.bootstrapSelect.js,其代码以下:

(function ($) {
    $("body").append('<template id="bootstrapSelect">' +
        '<select class="selectpicker" v-if="myMultiple" v-bind:data-live-search="mySearch" multiple>' +
            '<option v-for="item in myDatasource" v-bind:value="item.value">{{item.text}}</option>'
        +'</select>' +
        '<select class="selectpicker" v-else v-bind:data-live-search="mySearch">' +
            '<option v-for="item in myDatasource" v-bind:value="item.value">{{item.text}}</option>'
        +'</select>' +
    '</template>');

    Vue.component('bootstrap-select', {
        template: '#bootstrapSelect',
        props: ['myDatasource', 'myMultiple', 'mySearch'],
        //组件渲染以前
        created: function () {
        },
        //组件渲染以后
        mounted: function () {           
        }
    });

})(jQuery);

页面使用

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <link href="Content/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <link href="Content/bootstrap-table/bootstrap-table.css" rel="stylesheet" />
    <link href="Content/bootstrap-select/css/bootstrap-select.css" rel="stylesheet" />
</head>
<body>
    <div id="app">
        <bootstrap-select :my-datasource="selectOptions.data"
                          :my-multiple="selectOptions.multiple" 
                          :my-search="selectOptions.search">
        </bootstrap-select>
    </div>
    <script src="Content/jquery-1.9.1.min.js"></script>
    <script src="Content/bootstrap/js/bootstrap.js"></script>
    <script src="Content/bootstrap-table/bootstrap-table.js"></script>
    <script src="Content/bootstrap-select/js/bootstrap-select.js"></script>
    <script src="Content/bootstrap-select/js/i18n/defaults-zh_CN.js"></script>
    <script src="Content/vue/dist/vue.js"></script>
    <script src="Content/vue-component/vue.bootstrapSelect.js"></script>
    <script type="text/javascript">
        $(function () {
            var vm = new Vue({
                el: '#app',
                data: {
                    selectOptions:{
                        multiple: false,//多选
                        search: true,//搜索
                        data: [
                            { text: "北京市", value: 1 },
                            { text: "上海市", value: 2 },
                            { text: "重庆市", value: 3 },
                        ]
                    }
                },
            });
        });
    </script>
</body>
</html>

获得效果:

而后可配置多选,将初始化参数multiple设置为true便可。

为何模板里面会有两个select标签?缘由就在于那个multiple,由于只要标签里面出现了multiple,select就自动多选,把multiple的值设置为任何属性都很差使,这不作了一个if判断,若是哪位有更好的方法,欢迎指出,不胜感激!

三、查看其余Vue框架源码

如今再来看文章的开头那段html

<i-input type="text" :value.sync="formInline.user" placeholder="Username">
     <Icon type="ios-person-outline" slot="prepend"></Icon>
</i-input>

结合Vue组件的文档,其实上述就是一个对input标签作的封装。

固然,以上只是component的基础,组件的封装还得结合不少其余的东西,要读懂那些框架的源码还须要学习一些其余知识,但至少经过本文但愿可以让你了解这些东西的由来。

4、总结

本篇到此结束,经过本文,相信你对Vue的component有了一个大概的了解。接下来若是有时间将结合webpack介绍Vue的一些高级用法。

最近打算作点本身的东西出来,将博客里面的一些好的技术(包括vue)融合进去。有项目合做的小伙伴赶快联系博主吧!

本文原创出处:http://www.cnblogs.com/landeanfen/

欢迎各位转载,可是未经做者本人赞成,转载文章以后必须在文章页面明显位置给出做者和原文链接,不然保留追究法律责任的权利

相关文章
相关标签/搜索