Vue组件

1. 组件

a. 什么是组件

组件化开发

我们可以很直观的把一个复杂的页面分割成若干个独立的组件,每个组件包含自已的逻辑和样式,再将这些独立的组件组合成一个复杂的页面。这样就降低了逻辑复杂度,又实现了代码复用。

组件开发的好处

提高开发效率
方便重复使用
便一协同开发
更容易被管理和维护

组件分类

页面级组件:
1、一个页面是一个组件
2、将可复用的部分抽离出来-基础组件
根据作用划分:全局组件和局部组件
全局组件:可以声明一次在任何地方使用。
局部组件:必须告诉这个组件属于谁。
什么时侯用:
一版写插件时全局组件使用的多些

2. 全局组件

要注册一个全局组件,你可以使用 Vue.component(tagName, options)
全局组件在注册之后,便可以在父实例的模块中以自定义元素 <my-component></my-component>的形式使用。
要确保在初始化根实例 之前 注册了组件:
组件中的数据必须是函数类型,返回一个实例做为组件的数据。
步骤:

<body>
  <div id="app">      
    <!-- 自定义元素 <my-component></my-component> 的形式-->
    <my-div></my-div>
  </div>
<body>
<script src="js/vue.js"></script>
<script>
// 定义全局组件
Vue.component('myDiv', {
<!--template中只能识别一个元素,所以要将所写的页面元素都要放在一个盒子元素里-->
  template: '<h1>my first component</h1>'
});
// 创建根实例
new Vue({
  el: '#app'
})
</script>

注意:
html中的自定义标签名不能用驼峰命名法

3. 局部组件

不必在全局注册每个组件。通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用:
步骤:1,创建一个组件、2,注册个组件 、3引用这个组件

<body>
 <div id="app">
   <my-div></my-div>
 </div>
</body>
<script src="js/vue.js"></script>
<script>
// 定义子组件child
 var child = {
   template: '<h1>this is a child component</h1>'
 };
 var vm = new Vue({
   el: '#app',
/* 声明组件child*/
   components:{'myDiv': child}
})
</script>

另外一种写法

<div id="app">
    <!--1,创建一个组件、2,注册个组件 、3引用这个组件-->        {{msg}}
    <child></child>
    <children></children>
</div>
 <script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
 <script type="text/javascript">
    new Vue({
        el:'#app',
        data:{
           msg:'我是父级'
        },
        components:{
//                  child:childs
            child:{
                template:'<div><h1>我是子组件<span>我是一人</span></h1>14323232</div>'
            },
            children:{
                template:'<h1>我是子组件们</h1>'
            }
        }
    })
 </script>

4. 组件注意点

DOM模板解析说明

当使用 DOM 作为模版时(例如,将 el 选项挂载到一个已存在的元素上), 你会受到 HTML的一些限制,因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容。尤其像这些元素 <ul>,<ol>,<table> ,<select> 限制了能被它包裹的元素, 而一些像 <option>这样的元素只能出现在某些其它元素内部。

  • 定义自定义组件时,应当尽量符合W3C的规定,
  • 在VUE2.0中,VUE官方做了一些容错处理,但是在一些浏览器中,可能会出错
  • 那么VUE建议大家使用is属性来完成书写
    eg:
<body>
        <div id="app">
            <table>
               //用is属性完成书写时,my-div在table内
                <tr is="my-div"></tr>
              //如果不用则table和my-div是同一级的              
                <my-div></my-div>
            </table>
        </div>
        <script type="text/javascript" src="js/vue.js"></script>
        <script type="text/javascript">
            Vue.component('myDiv', {
                template: '<td>my first component</td>'
            });
            new Vue({
                el: '#app',
                data: {}
            })
        </script>
    </body>

5. data

组件里的data必须是函数
通过Vue构造器传入的各种选项大多数都可以在组件里用。 data 是一个例外,它必须是函数。实际上,如果你这么做:
Vue.component('my-component', {
template: '<span>{{ message }}</span>',
data: {
message: 'hello'
}
})
那么 Vue 会停止,并在控制台发出警告,告诉你在组件中 data
必须是一个函数。理解这种规则的存在意义很有帮助,让我们假设用如下方式来绕开Vue的警告:
<div id="example-2">
<simple-counter></simple-counter>
<simple-counter></simple-counter>
<simple-counter></simple-counter>
</div>
var data = { counter: 0 }
Vue.component('simple-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
// 技术上 data 的确是一个函数了,因此 Vue 不会警告,
// 但是我们返回给每个组件的实例的却引用了同一个data对象
data: function () {
return data
}
})
new Vue({
el: '#example-2'
})
由于这三个组件共享了同一个 data , 因此增加一个 counter会影响所有组件!这不对。我们可以通过为每个组件返回全新的 data 对象来解决这个问题:
data: function () {
return {
counter: 0
}
}

定 义组 件私有 变 量数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue</title>
</head>
<body>
<div id="app">
<my-div></my-div>
</div>
<script src="js/vue.js"></script>
<script>
Vue.component('myDiv', {
template: '<h1>my first component {{ str }}</h1>',
/*定义组件私有变量,data必须为函数*/
data: function() {
return {
str: 'it`s my data'
}
}
});
// 创建根实例
new Vue({
el: '#app'
})
</script>
</body>
</html>

6. 传值

Vue 组件的数据传递
组件意味着协同工作,通常父子组件会是这样的关系:组件 A 在它的模版中使用了组件 B。它们之间必然需要相互通信:父组件要给子组件传递数据,子组件需要将它内部发生的事情告知给父组件。
然而,在一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。
在 Vue 中,父子组件的关系可以总结为 props down, events up 。父组件通过 向下传递数据给子组件,子组件通过 events 给父组件发送消息。


image.png

a. props 的作用

组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用props 把数据传给子组件。
prop 是父组件用来传递数据的一个自定义属性。子组件需要显式地用 props 选项 声明 “prop”:

<body>
        <div id="app">
            父亲{{qian}}
            <son v-bind:hand='qian'></son>
        </div>
        <script src="js/vue.js"></script>
        <script type="text/javascript">
            new Vue({
                el:'#app',
                data:{
                    qian:'300'
                },
                components:{
                    son:{
                        template:'<div>儿子收到{{hand}}</div>',
                        props:['hand'],
                        
                }
            })
        </script>
    </body>

使用v-bind绑定数据,当父组件的数据放生变化时,子组件的数据也会放生改变父组件把数据传到子组件当中,用props 接收。

b. 子给父传值

Vue是单向数据流的,允许props 传递数据给子组件,如果子组件想传值个父组件,如果子组件想传值个父组件,就得通过事件的方式
过程:给父亲绑定一些事件,儿子触发这个事件,将参数传递过去
通过自定义事件来调用父级的方法,把给父元素传的值作为参数
使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件

<body>
        <div id="app">
            父亲{{qian}}
            <!--songsay自定义事件它的函数变成了 thing函数-->
            <son v-bind:hand='qian' v-on:sonsay='thing'></son>
        </div>
        <script src="js/vue.js"></script>
        <script type="text/javascript">
            new Vue({
                el:'#app',
                data:{
                    qian:'300'
                },
                methods:{
                    thing:function(val){
                        this.qian=val
                    }
                },
                components:{
                    son:{
                        template:"<div>儿子收到{{hand}}<button @click='say'>不够要900</button></div>",
                        props:['hand'],
                        methods:{
                            say:function(){
                                this.$emit('sonsay',900)
                            }
                        }
                    }
                }
            })
        </script>
    </body>

有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如:
<my-div v-on:click.native="doTheThing"></my-div>
自定 义 事件,把子 组 件的 值传给 父 组 件的方法

7. 使用slot分发内容

有时候,组件里面也有自己的内容,如:
<my-component>
<p>这是一些内容</p>
</my-component>
最终结果p标签是没办法显示出来的
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发 ,使用特殊的 <slot> 元素作为原始内容的插槽。

单个slot

<body>
        <div id="demo">
            <div>
                <h1>我是父组件的标题</h1>
                <my-component>
                    <p>这是一些初始内容</p>
                    <p>这是一些初始内容</p>
                </my-component>
            </div>
        </div>
        <script src="js/vue.js"></script>
        <script>
            /*定义全局组件*/
            Vue.component('myComponent', {
                template: '<div><h2>这是子组件标题</h2><slot>默认值</slot></div>'
            })
            new Vue({
                el: '#demo',
                data: {}
            })
        </script>
    </body>

具名slot

<slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。slot 将匹配内容片段中有对应 slot 特性的元素。
仍然可以有一个匿名 slot ,它是默认 slot,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。

<body>
        <div id="demo">
            <my-component>
                <h1 slot="header">这里可能是一个页面标题</h1>
                <p>主要内容的一个段落。</p>
                <p>另一个主要段落。</p>
                <p slot="footer">这里有一些联系信息</p>
            </my-component>
        </div>
        <script src="js/vue.js"></script>
        <script>
            /*定义全局组件*/
            Vue.component('myComponent', {
                template: '<div  class="container"><header><slot name = "header" > < /slot></header > < main > < slot > < /slot></main > < footer > < slot name = "footer" > < /slot></footer > < /div>'
            })
            new Vue({
                el: '#demo',
                data: {}
            })
        </script>
    </body>

8. 动态组件

通过使用保留的 <component> 元素,动态地绑定到它的 is
特性,我们让多个组件可以使用同一个挂载点,并动态切换:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="demo">
<input type="text" v-model="currentView" />
<my-component v-bind:is="currentView"></my-component>
</div>
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#demo',
data: {
currentView:'home'
},
components:{
home:{
template:'<h1>这是h1标签</h1>'
},
about:{
template:'<h2>这是h2标签</h2>'
},
contact:{
template:'<h3>这是h3标签</h3>'
}
}
})
</script>
</body>
</html>

如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-
alive 指令参数
<keep-alive>
<my-component v-bind:is="currentView"></my-component>
</keep-alive>

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

推荐阅读更多精彩内容