Vue组件化开发(一)——组件的定义和使用

前言

组件化的思想可以说是可以渗透在前端开发中方方面面了,而组件化的思想也确实使我们的代码更加简洁、复用性更高之外,也让我们的项目更加的轻巧。需要注意的是,组件化的思想并不是Vue中所特有的,基本上当前三大前端框架的开发都秉持着这种理念。本篇文章将对Vue框架下的组件定义和使用进行讲解,希望对各位读者有所帮助。

一、认识组件化的思想

在定义和使用组件之前,我们不妨先来聊聊什么是组件化。我们知道,一个网站的页面往往是比较复杂的,如果我们把页面上的所有功能维护在一个文件中,那么随着功能的不断增多,内部的代码和样式可能会越来越复杂,也越来越不容易维护。
同时,我们可以发现一个网页上面的内容,其实很多都是可以划分为不同的大模块,大模块下可能还可以再细分出不同的小模块。组件化的思想也就由此而来,我们可以把讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就会容易许多。

以简书的首页为例,我们可以把简书的首页划分为不同的区域,分别拆成不同的组件,这样一旦我们需要改动某个功能的时候,我们只需要关注对应模块的改动即可。同时,我们还可以发现,其实部分组件内部的元素其实是大同小异的,比如简书内容区的文章,其实都是格式相近的内容,所以我们针对这种情况还可以进一步提取为一个组件。

简书的首页可以大致分为这几个区域

组件化是Vue.js中的重要思想,它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树。

组件化思想的应用:
  • 有了组件化的思想,我们在之后的开发中就要充分的利用它。
  • 尽可能的将页面拆分成一个个小的、可复用的组件。
  • 这样让我们的代码更加方便组织和管理,并且扩展性也更强。

二、注册组件的基本步骤

组件的使用可以分为三个步骤:创建组件构建器 -> 注册组件 -> 使用组件

(一)创建组件构建器

调用Vue.extend()创建的是一个组件构造器。通常在创建组件构造器时,传入template代表我们自定义组件的模板。
该模板就是在使用到组件的地方,要显示的HTML代码。事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

// 创建组件
const myComponent = Vue.extend({
            template: 
            `<div>
                <h2>我是组件的标题</h2>
                <h4>我是组件的内容</h4>
            </div>`
        })
(二)注册组件

调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。所以需要传递两个参数:1、注册组件的标签名 2、组件构造器

// 全局注册组件,第一个参数为组件的名称
Vue.component('my-component',myComponent);
(三)使用组件

使用组件的话十分简单,只需要在APP实例下使用我们在步骤二定义的标签名即可。从结果图中我们可以看到,我们只是使用了定义的组件标签,组件的内容就已经可以展示出来了。可能有人读者会认为,这种做法有些多此一举,明明两行代码的事情,还需要经过组件定义、注册、使用这三个步骤才能实现。
确实,这种想法不无道理,但当我们需要多个地方渲染同一内容,而内容又比较多时,组件的威力就显现了出来。我们只需要使用一下组件的标签就可以完成复用!

 <div id="app">
        <my-component></my-component>
</div>
组件使用结果

同时,我们刚刚提到了使用组件标签的前提是需要在APP实例下,一般来说全局注册的组件只有在app实例下使用才有效,如果是在<div id="app">标签外使用组件,那么是不能够被正确解析的。

组件需要在app实例下使用才有效

三、全局组件和局部组件

(一)全局组件

之前我们在注册组件时,使用了Vue.component()这种方式来实现,其实这种方式注册的组件也叫全局组件,而全局组件可以在任意Vue示例下使用。我们可以看一下下面这个例子:
我们一共定义了2个Vue实例,一个全局注册的自定义组件,我们分别在2个vue实例中和vue实例之外的地方使用全局组件。从最终的结果中可以发现,我们的全局组件可以正常地在两个vue实例中显示。

<body>
    <div id="app1">
        <my-component></my-component>
    </div>
    <div id="app2">
        <my-component></my-component>
    </div>
    <my-component></my-component>
    <script>
        const myComponent = Vue.extend({
            template: 
            `<div>
                <h2>我是组件的标题</h2>
                <h4>我是组件的内容</h4>
            </div>`
        })
        // 全局注册组件,第一个参数为组件的名称
        Vue.component('my-component',myComponent);
        let app1 = new Vue({
            el: '#app1',
        })
        let app2 = new Vue({
            el: '#app2',
        })
    </script>
</body>
image.png
(二)局部组件

如果我们注册的组件是挂载在某个实例中, 那么这个组件就是一个局部组件。如何将组件挂载到实例中呢?
我们可以看一下下面的做法:

<body>
    <div id="app1">
        <my-component></my-component>
    </div>
    <div id="app2">
        <my-component></my-component>
    </div>
    <my-component></my-component>
    <script>
        const myComponent = Vue.extend({
            template: 
            `<div>
                <h2>我是组件的标题</h2>
                <h4>我是组件的内容</h4>
            </div>`
        })
        // 注册局部组件
        let app1 = new Vue({
            el: '#app1',
            components:{
                'my-component': myComponent
            }
        })
        let app2 = new Vue({
            el: '#app2',
        })
    </script>
</body>
自定义组件演示

四、父组件和子组件

我们在第一节提到过,一个复杂的页面可以抽取为几个大的组件,而大的组件可能还可以抽取出几个小的组件。此时就涉及到组件中的一种很重要的关系,组件间的父子关系。常常表现为一个大的组件之中包含了几个小的组件,那么这个大的组件就是小组件的父组件。

我们可以看一下下面这个例子:父组件在app实例下注册和使用,子组件在父组件下注册和使用,在页面上也可以正常的使用。

<body>
    <div id="app">
        <parent-component></parent-component>
    </div>
    <script>
        const childComponent = Vue.extend({
            template:
            `<div>
                <h4>我是子组件的内容</h4>
            </div>`
        })
        const parentComponent = Vue.extend({
            template: 
            `<div>
                <h2>我是父组件的标题</h2>
                <h4>我是父组件的内容</h4>
                <child-component></child-component>
            </div>`,
            components: {
                'child-component': childComponent
            }
        })
        // 注册局部组件
        let app = new Vue({
            el: '#app',
            components:{
                'parent-component': parentComponent
            }
        })
    </script>
</body>
父子组件的正常使用

我们再看下面这个例子,子组件在父组件中注册,但是是在APP实例下使用,那么此时是无法正确解析出子组件的。因为当子组件注册到父组件的components时,Vue会编译好父组件的模块,该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了),所以<child-component></child-component>是只能在父组件中被识别的。

<body>
    <div id="app">
        <parent-component></parent-component>
        <child-component></child-component>
    </div>
    <script>
        const childComponent = Vue.extend({
            template:
            `<div>
                <h4>我是子组件的内容</h4>
            </div>`
        })
        const parentComponent = Vue.extend({
            template: 
            `<div>
                <h2>我是父组件的标题</h2>
                <h4>我是父组件的内容</h4>
            </div>`,
            components: {
                'child-component': childComponent
            }
        })
        // 注册局部组件
        let app = new Vue({
            el: '#app',
            components:{
                'parent-component': parentComponent
            }
        })
    </script>
</body>
子组件没有正常解析出来

PS:其实这一小节除了想要讲述组件间的父子关系之外,更重要的还是想要提醒各位读者在使用组件时,一定要注意好组件的作用域,避免出现组件无法渲染的情况发生。

五、组件的简写方式

(一)简写组件的注册方式

我们之前定义组件时,采用的方式是Vue.extend()来定义具体一个模板的内容,而后再进行全局或者局部的注册。但实际上,我们可以省去了调用Vue.extend()的步骤,直接使用一个对象来代替。

<body>
    <div id="app">
        <my-component></my-component>
    </div>
    <script>
        // 注册局部组件
        let app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    template:
                        `<div>
                            <h2>我是组件的标题</h2>
                        </div>`,
                }
            }
        })
    </script>
</body>
简写Vue.extend方法
(二)模板分离

相比于上面提到的简写注册组件的方式,模板分离也同样简化了我们定义组件的方式,模板分离将template中的HTML分离出来写,然后挂载到对应的组件上,让我们的代码结构变得非常清晰。
我们可以看一下下面的这个案例,我们在上一小节的基础上,使用模板分离的方式再次简化了我们的代码。(其实也不算特别简化,但是template的存在使得代码结果比以前要清晰很多!)

<body>
    <div id="app">
        <my-component></my-component>
    </div>
    <template id="myComponent">
        <div>
            <h2>我是组件的标题</h2>
        </div>
    </template>
    <script>
        // 注册局部组件
        let app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    template: "#myComponent"
                }
            }
        })
    </script>
</body>
除了用template标签之外,我们还可以使用script标签来抽取我们的html
<body>
    <div id="app">
        <my-component></my-component>
    </div>
    <script type="text/x-template" id="myComponent">
        <div>
            <h2>我是组件的标题</h2>
        </div>
    </script>
    <script>
        // 注册局部组件
        let app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    template: "#myComponent"
                }
            }
        })
    </script>
</body>

六、组件的data

实际上,每个组件内部都可以维护属于自身的data,用于组件内部用于展示或者计算。这点相比大家都知道,这里要说的是对于data的定义方式。组件中的data属性必须是一个函数,原因很简单,Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

这是什么意思呢?我们来看一下下面这个案例:
由于组件的data如果不是函数的话会报错,所以下面的案例我们使用了一个外部定义的obj来模拟data不为函数的结果。

<body>
    <div id="app">
        <my-component></my-component>
        <my-component></my-component>
        <my-component></my-component>
    </div>
    <template id="myComponent">
        <div>
            <button @click="add">+1</button>
            <span>当前数量为:{{count}}</span>
        </div>
    </template>
    <script>
        const obj = {
            count : 0
        }
        // 注册局部组件
        let app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    template: "#myComponent",
                    data(){
                        return obj
                    },
                    methods:{
                        add(){
                            this.count++;
                        }
                    }
                }
            }
        })
    </script>
</body>
模拟data不是一个函数的结果

正确的做法是让每个组件都有独立的data作用域

<body>
    <div id="app">
        <my-component></my-component>
        <my-component></my-component>
        <my-component></my-component>
    </div>
    <template id="myComponent">
        <div>
            <button @click="add">+1</button>
            <span>当前数量为:{{count}}</span>
        </div>
    </template>
    <script>
        let app = new Vue({
            el: '#app',
            components: {
                'my-component': {
                    template: "#myComponent",
                    data(){
                        return {
                            count : 0
                        }
                    },
                    methods:{
                        add(){
                            this.count++;
                        }
                    }
                }
            }
        })
    </script>
</body>
data为一个函数
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容