首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue.js-组件 原

Vue.js-组件 原

作者头像
tianyawhl
发布2019-04-04 11:39:37
5.3K0
发布2019-04-04 11:39:37
举报
文章被收录于专栏:前端之攻略前端之攻略

使用组件

1、注册

之前说过,我们可以通过以下方式创建一个Vue实例 new Vue({   el: '#some-element',   // 选项 })

(1)要注册一个全局组件,你可以使用Vue.component(tagName,options) 例如: Vue.component("my-component",{ //选项 })

对应自定义标签名,Vue.js不强制要求遵循W3C规则

组件在注册之后,便可以在父实例的模块中以自定义元素 <my-component></my-component>的形式使用。要确保在初始化根实例之前注册了组件

<body class="">
    <div id="example-1">
        <my-component></my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        template: `<div>a custom component</div>`
    })
    var app = new Vue({
        el: "#example-1",
    })
    </script>
</body>
渲染为
<div id="example">
  <div>A custom component!</div>
</div>

(2)局部注册 不必在全局注册每个组件,通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用

<body class="">
    <div id="example-1">
        <my-component></my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    var child = {
        template: `<div>aa custom component</div>`
    }
    var app = new Vue({
        el: "#example-1",
        components: {
            //<my-component> 将只在父模板可用
            "my-component": child
        }
    })
    </script>
</body>

这种封装也适用于其它可注册的Vue功能,如指令

2、is属性

当使用DOM作为模板时,你会受到HTML的一些限制,因为Vue只有在浏览器解析和标准化HTML后才能获取模板内容,尤其像一些元素<ul>,<ol>,<table>,<select>限制了能被它包裹的元素,而一些像option这样的元素只能出现在某些其它元素内部,在自定义组件中使用这些受限制的元素时会导致一些问题,例如 <table>   <my-row>...</my-row> </table> 在渲染的时候会导致错误。变通的方法是使用特殊的is属性,如下例子:

<body class="">
    <div id="example-1">
        <table>
            <tr is="my-row"></tr>
        </table>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-row", {
        template: `<tr><td>11</td></tr> `
    })
    var app = new Vue({
        el: "#example-1"
    })
    </script>
</body>

注意如果您使用来自以下来源之一的字符串模板,这些限制将不适用 1、<script type="text/x-template"> <!DOCTYPE html> <html>     <body>         <div id="app">             <my-component></my-component>         </div>

        <-- 注意:使用<script>标签时,type指定为text/x-template,意在告诉浏览器这不是一段js脚本,浏览器在解析HTML文档时会忽略<script>标签内定义的内容。-->

        <script type="text/x-template" id="myComponent">//注意 type 和id。             <div>This is a component!</div>         </script>     </body>     <script src="js/vue.js"></script>     <script>         //全局注册组件         Vue.component('my-component',{             template: '#myComponent'         })

        new Vue({             el: '#app'         })

    </script> </html>

2、JavaScript 内联模版字符串         <template id="myComponent">             <div>This is a component!</div>         </template>

3、.vue 组件,创建.vue后缀的文件,如组件Hello.vue,放到components文件夹中,在使用的页面进行引用

3、data必须是函数

通过Vue构造器传入的各种选项大多数可以在组件里用,data是个例外,它必须是函数

<body class="">
    <div id="example-1">
        <simple-counter></simple-counter>
        <simple-counter></simple-counter>
        <simple-counter></simple-counter>
    </div>
    <script src="js/vue.js"></script>
    <script>
    var data={counter:0}
    Vue.component("simple-counter",{
        template:`
        <button v-on:click="counter+=1">{{counter}}</button>
        `,
        data:function(){
            return data
        }
    })
    var app = new Vue({
        el: "#example-1"
    })
    </script>
</body>

由于这3个组件共享了同一个data,因此增加一个counter会影响所有组件,我们可以通过为每个组件返回全新的data对象来解决这个问题

<body class="">
    <div id="example-1">
        <simple-counter></simple-counter>
        <simple-counter></simple-counter>
        <simple-counter></simple-counter>
    </div>
    <script src="js/vue.js"></script>
    <script>
    
    Vue.component("simple-counter",{
        template:`
        <button v-on:click="counter+=1">{{counter}}</button>
        `,
        data:function(){
            return {
                counter:0
            }
        }
    })
    var app = new Vue({
        el: "#example-1"
    })
    </script>
</body>

现在每个counter都有它自己内部的状态了

4、构成组件

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

使用Prop传递数据 组件实例的作用域是孤立的,这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据,要让子组件使用父组件的数据,需要通过子组件的props选项,子组件要显式的用props选项声明它期待获得的数据

<body class="">
    <div id="example-1">
        <message v-bind:message="data"></message>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("message", {
        props: ["message"],
        template: `
        <span>{{message}}</span>
        `,
    })
    var app = new Vue({
        el: "#example-1",
        data: {
            data: "hello props"
        }
    })
    </script>
</body>

camelCase vs.kebab-case HTML特性是不区分大小写的,所以,当使用的不是字符串模板,camelCased(驼峰式)命名的prop需要父组件属性名需要转换为相应的kebab-case(短横线隔开式)命名

<body class="">
    <div id="example-1">
        <message v-bind:my-message="data"></message>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("message", {
        props: ["myMessage"],
        template: `
        <span>{{myMessage}}</span>
        `,
    })
    var app = new Vue({
        el: "#example-1",
        data: {
            data: "hello props MY MESSAGE"
        }
    })
    </script>
</body>

如果你使用字符串模板,则没有这些限制 字面量语法与动态语法 初学者常犯的一个错误是使用字面量语法传递数值 //传递了一个字符串“1” <comp some-prop="1"></comp> 因为它是一个字面prop,它的值是字符串“1”,而不是number,如果想传递一个实际的number,需要使用v-bind从而让它的值被当作JavaScript表达式计算 <!-- 传递实际的 number --> <comp v-bind:some-prop="1"></comp>

单向数据流 prop是单向绑定的,当父组件的属性变化时,将传导给子组件,但是不会反过来,另外每次父组件更新时,子组件的所有prop都会更新为最新值,这意味着你不应该在子组件内部改变prop.如果你这么做了,Vue会在控制台给出警告 为什么我们会有修改prop中的数据的冲动呢,通常有2种原因 1、prop作为初始值传入后,子组件想把它当做局部数据来用 2、prop作为初始值传入,由子组件处理成其它数据输出 对于这2种原因,正确的应对方式是: (1)定义一个局部变量,并用prop的值初始化它

<body class="">
    <div id="example-1">
        <message v-bind:message="initialCounter"></message>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("message", {
        props: ["message"],
        data: function() {
            return { counter: this.message }
        },
        template: `
        <span>{{counter}}</span>
        `,
    })
    var app = new Vue({
        el: "#example-1",
        data: {
            initialCounter: 1
        }
    })
    </script>
</body>

(2)定义一个计算属性,处理prop的值并返回

<body class="">
    <div id="example-1">
        <message v-bind:message="initialCounter"></message>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("message", {
        props: ["message"],
        computed:{
            newmessage:function(){
                return this.message.trim().toLowerCase()
            }
        },
        template: `
        <span>{{newmessage}}</span>
        `,
    })
    var app = new Vue({
        el: "#example-1",
        data: {
            initialCounter: "THIS IS THE SINGLE DATA SAMPLE"
        }
    })
    </script>
</body>

注意在JavaScript中对象和数组是引用类型,指向同一个内存空间,如果prop是一个对象或数组,在子组件内部改变它会影响父组件的状态

自定义事件 我们知道,父组件是使用props传递数据给子组件,但子组件怎么跟父组件通信呢,这个时候Vue的自定义事件系统就派上用场了,使用v-on绑定自定义事件 每个Vue实例都实现了事件接口(Events interface)即使用$on(eventName)监听事件 使用$emit(eventName)触发事件 Vue的事件系统分离自浏览器的EventTarget API尽管它们的运行类似,但是$on ,$emit不是addEventListener和dispatchEvent的别名 另外,父组件可以在使用子组件的地方直接用v-on来监听子组件触发的事件 不能用$on侦听子组件释放的事件,而必须在模板里直接使用v-on绑定,就像以下的例子

<body class="">
    <div id="example">
        <div>{{total}}</div>
        <my-component v-on:crement="crementTotal"></my-component>
        <my-component v-on:crement="crementTotal"></my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        template: `
           <button v-on:click="incrementCounter">{{counter}}</button>
        `,
        data: function() {
            return {
                counter: 0
            }
        },
        methods: {
            incrementCounter: function() {
                this.counter += 1
                this.$emit("crement")

            }
        }
    })
    var app = new Vue({
        el: "#example",
        data: {
            total: 0
        },
        methods: {
            crementTotal: function() {
                this.total += 1
            }
        }
    })
    </script>
</body>

自定义事件不能用驼峰来命名 本例中,子组件已经和它的外部完全解耦,链接桥梁是自定义事件crement

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

在一些情况下,我们可能需要对一个prop进行双向绑定,事实上这正是Vue1.x中的,由于破坏了单向数据流的假设,我们在2.0版本中移除.sync 但在实际应用中.sync还是有其适用之处,从2.3.0起我们重新引用了.sync 修饰符 完整实例代码如下:

<body class="">
    <div id="example">
        <div>{{bar}}</div>
        <comp v-bind:foo.sync="bar"></comp>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("comp", {
        props: ["foo"],
        template: `
        <button v-on:click="changeMessage">{{foo}}</button>
        `,
        methods: {
            changeMessage: function() {
                this.$emit('update:foo', "changedM")
            }
        }
    })
    var app = new Vue({
        el: "#example",
        data: {
            bar: "initial message"
        }
    })
    </script>
</body>

非父子组件通信 如果2个组件不是父子组件,也需要通信,在简单的场景下,可以使用一个空的Vue实例作为中央事件总线

<body class="">
    <div id="app">
        <c1></c1>
        <c2></c2>
    </div>
    <script src="js/vue.js"></script>
    <script>
    var Bus = new Vue();
    Vue.component('c1', {
        template: '<div>{{msg}}</div>',
        data: function() {
            return { msg: 'Hello World!' }
        },
        created() {
            var self = this
            Bus.$on('setMsg', function(content) {
                self.msg = content;
            });
        },
    });
    Vue.component('c2', {
        template: '<button @click="sendEvent">Say Hi</button>',
        methods: {
            sendEvent() {
                Bus.$emit('setMsg', 'Hi Vue!!');
            }
        }
    });
    var app = new Vue({
        el: '#app'
    })
    </script>
</body>

在复杂的情况下,我们应该考虑使用专门的状态管理模式

5、使用Slot分发内容

在使用组件时,我们常常要像这样组合它们 <app>   <app-header></app-header>   <app-footer></app-footer> </app>

1、<app>组件不知道它会收到什么内容。这是由<app>的父组件决定的 2、<app>组件很可能有它自己的模板 为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板,这个过程被称为内容分发 Vue.js实现了一个内容分发API,使用特殊的<slot>元素作为原始内容的插槽

编译作用域 在深入内容分发API之前,我们先明确内容在哪个作用域里编译,假定模板为: <child-component>{{message}}</child-component> message应该绑定到父组件的数据 组件作用域简单的说是: 父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译

(1)单个slot 除非子组件的模板包含至少一个<slot>插口,否则父组件的内容将会被丢弃。当子组件的模板只有一个没有属性的slot时,父组件整个内容片段将插入到slot所在的DOM位置,并替换掉slot标签本身 最初在<slot>标签中的任何内容都被视为备用内容,备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容才显示备用内容

<body class="">
    <div id="example">
        <h2>父组件标题</h2>
        <my-component>
            <p>这是一些初始内容</p>
            <p>这是另一些初始内容</p>
        </my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        template: `
          <div>
          <h2>子组件标题</h2>
          <slot>没有分发内容时显示</slot>
          </div>
        `
    })
    var app = new Vue({
        el: "#example",
    })
    //子模板要用div包裹
    </script>
</body>

最后渲染为

<div>
  <h1>我是父组件的标题</h1>
  <div>
    <h2>我是子组件的标题</h2>
    <p>这是一些初始内容</p>
    <p>这是更多的初始内容</p>
  </div>
</div>

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

<body class="">
    <div id="example">
        <my-component>
            <h2 slot="header">这是一个标题</h2>
            <h2 slot="footer">这是底部</h2>
            <h3>其它内容</h3>
        </my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component", {
        template: `
         <div class="container">
          <header>
          <slot name="header"></slot>
          </header>
          <main>
            <slot></slot>
          </main>
          <footer>
          <slot name="footer"></slot>
          </footer>
         </div>
        `
    })
    var app = new Vue({
        el: "#example",
    })
    //子模板要用div包裹,子组件的slot 的name属性与父组件slot属性对应
    </script>
</body>

(3)作用域插槽 2.1.0新增 作用域插槽是一种特殊类型的插槽,用作使用一个(能够传递数据到)可重用模板替换已渲染元素 在子组件中,只需将数据传递到插槽,就像你将props传递给组件一样

<body class="">
    <div id="example">
        <div class="parent">
            <child>
                <template scope="props">
                    <span>this is from parent</span>
                    <br>
                    <span>{{props.text}}</span>
                </template>
            </child>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("child", {
        template: `
         <div class="child">
            <div>child div</div>
            <slot text="hello from child"></slot>
         </div>
        `
    })
    var app = new Vue({
        el: "#example",
    })
    //可以通过模板中的scope属性获得子组件里面的text的值,父模板与子组件的正常HTML内容都会保留
    </script>
</body>

最后解析成下面 

div id="example">
        <div class="parent">
            <div class="child">
                <div>child div</div>
                <span>this is from parent</span>
                <br> 
                <span>hello from child</span></div>
        </div>
</div>

作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表的每一项

<body class="">
    <div id="example">
        <div class="parent">
            <child>
                <template slot="item" scope="props">
                    <li>{{props.tex}}</li>
                </template>
            </child>
        </div>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("child", {
        template: `
         <ul>
         <slot name="item" v-for="item in items" v-bind:tex="item.text"></slot>
         </ul>
        `,
        data: function() {
            return {
                items: [
                    { text: "text1" },
                    { text: "text2" },
                    { text: "text3" },
                ]
            }
        }
    })
    var app = new Vue({
        el: "#example",
    })
    
    </script>
</body>

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

var vm = new Vue({   el: '#example',   data: {     currentView: 'home'   },   components: {     home: { /* ... */ },     posts: { /* ... */ },     archive: { /* ... */ }   } })

<component v-bind:is="currentView">   <!-- 组件在 vm.currentview 变化时改变! --> </component>

也可以直接绑定到组件对象上: var Home = {   template: '<p>Welcome home!</p>' } var vm = new Vue({   el: '#example',   data: {     currentView: Home   } })

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

编写可复用的组件 Vue组件的API来自三部分-props,events 和slots; Props允许外部环境传递数据给组件 Events允许从外部环境在组件内触发自定义事件 Slots允许外部环境将额外的内容组合在组件中

内联模板 如果子组件有inline-template特性,组件将把它的内容当做它的模板,而不是把它当作分发内容,这让模板更灵活

<body class="">
    <div id="example">
        <my-component inline-template>
            <div>
                <p>These are compiled as the component's own template.</p>
                <p>Not parent's transclusion content.</p>
            </div>
        </my-component>
    </div>
    <script src="js/vue.js"></script>
    <script>
    Vue.component("my-component",{
        
    })
    var app = new Vue({
        el: "#example",
    })
    </script>
</body>

渲染成
<div id="example">
        <div>
            <p>These are compiled as the component's own template.</p>
            <p>Not parent's transclusion content.</p>
        </div>
</div>

X-Templates 另一种定义模板的方式是在JavaScript标签里使用text/x-template类型,并且指定一个ID.例如 <script type="text/x-template" id="hello-world-template">   <p>hello </p> </script> Vue.component("hello-world",{ template:"#hello-world-template" }) 这在有很多模板或者小的应用中有用,否则应该避免使用,因为它将模板和组件的其他定义隔离了

对低开销的静态组件使用v-once 尽管在Vue中渲染HTML很快,不过当组件中包含大量静态内容时,可以考虑使用v-once将渲染结果缓存起来,就像这样 Vue.component('terms-of-service', {   template: '\     <div v-once>\       <h1>Terms of Service</h1>\       ... a lot of static content ...\     </div>\   ' })

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017/09/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、注册
  • 2、is属性
  • 3、data必须是函数
  • 4、构成组件
  • 5、使用Slot分发内容
相关产品与服务
事件总线
腾讯云事件总线(EventBridge)是一款安全,稳定,高效的云上事件连接器,作为流数据和事件的自动收集、处理、分发管道,通过可视化的配置,实现事件源(例如:Kafka,审计,数据库等)和目标对象(例如:CLS,SCF等)的快速连接,当前 EventBridge 已接入 100+ 云上服务,助力分布式事件驱动架构的快速构建。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档