• 03 vue组件技术


    系列文章导航

    01 Vue语法基础

    02 vue数据绑定与指令

    03 vue组件技术

    04 vue单文件组件定义与使用


    1 组件介绍

    组件是Vue.js最强大的功能之一。组件是可复用的Vue实例,且带有一个名字,通过组件封装可重用的代码,在较高层面上,组件是自定义元素。

    2 组件注册

    1.1 组件注册方式1

    组件名对应标签名,标签名不允许出现大写字母,因此组件名包含多个单词时需要采用kebab-case (短横线分隔命名) 命名。

    组件名称中如果用了大写字母,使用组件的时候需要转成kebab-case方法,例如组件名为:uniStatusBar,使用组件的时候<uni-status-bar></uni-status-bar>

    组件模板代码必须是单个跟元素。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <button-counter>组件1</button-counter>
                <button-counter>组件2</button-counter>
            </div>
            <script type="text/javascript">
                // 1、创建组件
                var myComponent = Vue.extend({
              // 组件内部数据 data:function() {
    // data是一个函数 return { count:0 } },
    // 组件模板 template:'<button @click="count++">You clicked me {{ count }} times.</button>' }); // 2、注册组件 Vue.component('button-counter',myComponent);
    var app = new Vue({ // 挂载元素 - 作用范围 el: '#app', // vue数据 data: { } }); </script> </body> </html>

    1.2 组件注册方式2

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <button-counter>组件1</button-counter>
                <button-counter>组件2</button-counter>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('button-counter', {
    // 组件内部数据 data: function() {
    // data是一个函数 return { count: 0 } },
              // 组件模板 template: '<button @click="count++">You clicked me {{ count }} times.</button>' });
    var app = new Vue({ // 挂载元素 - 作用范围 el: '#app', // vue数据 data: {} }); </script> </body> </html>

    1.3 组件嵌套注册

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <parent></parent>
            </div>
            <script type="text/javascript">
                //定义子组件
                var child = Vue.extend({
                    template: '<button @click.stop="subCount++">我是子组件{{subCount}}</button>',
                    data: function() {
                        return {
                            subCount: 1
                        }
                    }
                });
                // 定义父组件
                Vue.component('parent', {
                    data: function() {
                        // 一定要返回数据
                        return {
                            count: 0
                        }
                    },
                    //定义组件内所用到的子组件
                    components: {
                        'child':child
                    },
                    template: '<button @click="count++">我是父组件{{count}} - <child></child></button>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    // vue数据
                    data: {
    
                    }
                });
            </script>
        </body>
    </html>

    两个按钮可以单独点击:子组件事件加了.stop防止冒泡。

    运行效果:

    3 Props的使用

    3.1 通过Props公开组件属性

    属性名对应标签属性,标签的属性名不允许出现大写字母,因此属性名包含多个单词时需要采用kebab-case (短横线分隔命名) 命名。

    Props公开的属性是父组件传递给子组件的,当父组件数据发生变更的时候子组件一起变更,因此在子组件内不要对传入的数据变量进行修改。

    当父组件需要向子组件传递数据时,需要在子组件内通过props向外公开接收的属性列表。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 生成组件 -->
                <button-counter title="组件1">组件1</button-counter>
                <button-counter title="组件2">组件2</button-counter>
                <!-- 通过v-for生成多个组件 -->
                <button-counter v-for="post in posts" :start="post.id" :title="post.title">组件2</button-counter>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('button-counter', {
                    data: function() {
                        // 一定要返回数据
                        return {
                            count: 0
                        }
                    },
                    // 对外公开的属性,也可以用简化方式 props: ['title','start']
                    props: {
                        title: {
                            type:String,
                            required: true,
                            default: "默认title"
                        },
                        start:{
                            type:Number
                        }
                    },
                    template: '<div><button @click="count++">You clicked {{title}} {{start}} {{ count }} times.</button></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    // vue数据
                    data: {
                        posts: [
                            {id: 1,title: 'My journey with Vue'},
                            {id: 2,title: 'Blogging with Vue'},
                            {id: 3,title: 'Why Vue is so fun'}
                        ]
                    }
                });
            </script>
        </body>
    </html>

    3.2 Props的方方面面

    1) 普通方式

    props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

    2) 类型限定方式

    props: {
      title: String,
      likes: Number,
      isPublished: Boolean,
      commentIds: Array,
      author: Object,
      callback: Function,
      contactsPromise: Promise // or any other constructor
    }

    3) 复杂限定方式

    Vue.component('my-component', {
      props: {
        // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
        propA: Number,
        // 多个可能的类型
        propB: [String, Number],
        // 必填的字符串
        propC: {
          type: String,
          required: true
        },
        // 带有默认值的数字
        propD: {
          type: Number,
          default: 100
        },
        // 带有默认值的对象
        propE: {
          type: Object,
          // 对象或数组默认值必须从一个工厂函数获取
          default: function () {
            return { message: 'hello' }
          }
        },
        // 自定义验证函数
        propF: {
          validator: function (value) {
            // 这个值必须匹配下列字符串中的一个
            return ['success', 'warning', 'danger'].indexOf(value) !== -1
          }
        }
      }
    })

    4) 单向数据流

    所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

    额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

    4 子组件事件

    4.1 通过$emit公开组件事件

    $emit参数值中不允许出现大写字母,因此事件名需要采用kebab-case (短横线分隔命名) 命名。

    以下两种情况要用到$emit:

    1) 当子组件某事件触发时,要执行父组件的某个事件处理方法时

    2) 当要把子组件的数据传递给父组件时

    采用$emit方式,代码实例如下:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 使用组件时 绑定组件对外公开的事件的事件处理方法-->
                <button-counter @outer-event="receiveSub1">组件1</button-counter>
                <div>父组件的值:{{pValue}}</div>
                <button-counter @outer-event="receiveSub2">组件2</button-counter>
                <div>父组件的值:{{pValue}}</div>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('button-counter', {
                    data: function() {
                        return {
                            count: 0
                        }
                    },
                    methods:{
                        // 组件内的事件处理方法
                        onClick:function(){
                            this.count++;
                            // 通过$emit('组件对外公开的事件名'),调用公开事件绑定的事件处理函数,并向外传递数据
                            this.$emit("outer-event",this.count);
                        }
                    },
                    template: '<div><button @click="onClick">You clicked {{ count }} times.</button></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    // vue数据
                    data: {
                        pValue:0
                    },
                    methods:{
                        // 组件外的事件处理方法,该方法通过组件内的事件调用
                        receiveSub1:function(c){
                            console.info("父组件1的方法被子组件调用了"+c);
                            // 变更父组件的值
                            this.pValue = c;
                        },
                        receiveSub2:function(c){
                            console.info("父组件2的方法被子组件调用了"+c);
                            this.pValue = c;
                        }
                    }
                });
            </script>
        </body>
    </html>

    两个子组件共享父组件的值,当单击第一个组组件4次后,运行效果如下:

    核心代码

    子组件方法内:this.$emit('自定义事件', data.ids)
    
    组件使用时:<组件名  @自定义事件='父组件的方法'/>

    4.2 在组件上使用v-model

    v-model是Props和事件的结合,用来实现父组件和子组件的双向通信。

    一个组件上的 v-model 默认会利用名为 value 的 props和名为 input 的事件,为了防止冲突,也可以通过组件的model选项来自己定义。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 使用组件时 绑定组件对外公开的事件的事件处理方法-->
                <button-counter v-model="pValue">组件1</button-counter>
                <div>父组件的值:{{pValue}}</div>
                <button-counter>组件2</button-counter>
                <div>父组件的值:{{pValue}}</div>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('button-counter', {
                    data: function() {
                        return {
                            count: 0
                        }
                    },
                    props:{
                        value_me:{
                            default:""
                        }
                    },
                    // 通过model声明
                    // v-model所绑定变量的值会传递给子组件的value_me变量
                    // 子组件对开公开的事件input_me,通过input_me事件可以实现向外层v-model绑定变量传值
                    model:{
                        prop:'value_me',
                        event:'input_me'
                    },
                    methods:{
                        onChange:function(e){
                            console.info(e.target.value);
                            // 外部不需要提供方法绑定子组件的事件,$emit的第二个参数值会传递给v-model所绑定的变量 
                            this.$emit('input_me',e.target.value);
                        }
                    },
                    template: '<div><input type="text" :value="value_me" @change="onChange($event)" /><br/><span>子组件内的值:{{value_me}}</span></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    // vue数据
                    data: {
                        pValue:""
                    }
                });
            </script>
        </body>
    </html>

    4.3 将原生组件绑定到组件上

    在组件使用的时候,直接在组件标签上绑定原生事件是不生效的,如果要把该原生事件绑定到组件内的某个表单上,需要在表单上增加 v-on="$listeners" 属性值。

    直接在组件内的input上绑定focus也可以的,不清楚该语法使用场景是啥。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 使用组件时 绑定组件对外公开的事件的事件处理方法-->
                <button-counter v-model="pValue" @focus="onFocus">组件1</button-counter>
                <div>父组件的值:{{pValue}}</div>
                <button-counter>组件2</button-counter>
                <div>父组件的值:{{pValue}}</div>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('button-counter', {
                    data: function() {
                        return {
                            count: 0
                        }
                    },
                    props: {
                        value_me: {
                            default: ""
                        }
                    },
                    // 通过model声明
                    // v-model所绑定变量的值会传递给子组件的value_me变量
                    // 子组件对开公开的事件input_me,通过input_me事件可以实现向外层v-model绑定变量传值
                    model: {
                        prop: 'value_me',
                        event: 'input_me'
                    },
                    methods: {
                        onChange: function(e) {
                            console.info(e.target.value);
                            // 外部不需要提供方法绑定子组件的事件,$emit的第二个参数值会传递给v-model所绑定的变量 
                            this.$emit('input_me', e.target.value);
                        }
                    },
                    template: '<div><input type="text" :value="value_me" @change="onChange($event)" v-on="$listeners" /><br/><span>子组件内的值:{{value_me}}</span></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    // vue数据
                    data: {
                        pValue: ""
                    },
                    methods: {
                        onFocus: function() {
                            console.info("触发onFocus");
                            this.pValue="获取光标了"
                        }
                    }
                });
            </script>
        </body>
    </html>

    5 插槽分发内容

    在组件模板代码中可以创建插糟(占位符),在组件使用的时候,可以向组件内部的插槽填充内容,实现组件展示内容的个性化定制。

    5.1 不具名slot插槽

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 使用组件时 绑定组件对外公开的事件的事件处理方法-->
                <my-component></my-component>
                <my-component>父容器填充内容</my-component>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('my-component', {
                    template: '<div><slot>第一个插槽默认内容</slot><button>子组件<slot>第二个插槽默认内容</slot></button><slot>第三个插槽默认内容</slot></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app'
                });
            </script>
        </body>
    </html>

    使用组件是,组件内的内容自动填充到所有不具名的插槽中,可以有多个不具名插糟,运行效果:

    5.2 具名slot插槽

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 使用组件时 绑定组件对外公开的事件的事件处理方法-->
                <my-component><span slot="begin">父容器给begin的内容{{pValue}}</span>默认填充内容</my-component>
                <my-component><span slot="end">父容器给end的内容</span>父容器填充内容</my-component>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('my-component', {
                    data:function() {
                        return {
                            cValue:"4567"
                        }
                    },
                    template: '<div><slot name="begin">begin插槽默认内容</slot><button>子组件<slot>center插槽默认内容</slot></button><slot name="end">end插槽默认内容</slot></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    data:{
                        // 外部组件的变量
                        pValue:"1234"
                    }
                });
            </script>
        </body>
    </html>

    运行效果:

    填充内容里边可以使用外层组件的数据,不能使用组件内的数据。

    父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

     v2.6.0之后,用v-slot替代了slot和slot-scope这两个指令。

    通过v-slot设定插槽名称,v-slot必须应用在template标记上,修改后代码如下:

                <my-component><template v-slot="begin">父容器给begin的内容{{pValue}}</template>默认填充内容</my-component>
                <my-component><template v-slot="end">父容器给end的内容</template>父容器填充内容</my-component>

    5.3 让父级代码中使用组件内的数据

    有时让插槽内容能够访问子组件中才有的数据是很有用的。

    实例代码如下:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 使用组件时 绑定组件对外公开的事件的事件处理方法-->
                <my-component><template v-slot="begin">父给begin的内容{{pValue}}}</template>默认填充内容</my-component>
                <!-- 通过v-slot:插糟名="引用分享数据的变量名",名称可以随便起 -->
                <my-component><template v-slot:begin="slotProps">{{slotProps.cValue}}-{{slotProps.user.firstName}}</template></my-component>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('my-component', {
                    data: function() {
                        return {
                            cValue: "内部变量",
                            user:{
                                firstName:'wang',
                                lastName:'zhirui'
                            }
                        }
                    },
                    // 对应插糟上一定要通过v-bind:对外属性名,或者简写方式 :对外属性名,公开要对外分享得内部数据
                    template: '<div><slot name="begin" v-bind:cValue="cValue" :user="user">begin默认内容</slot><button>子组件<slot>center默认内容</slot></button><slot name="end">end默认内容</slot></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    data: {
                        // 外部组件的变量
                        pValue:"外部变量"
                    },
                    created:function(){
                        console.info("12121")
                    }
                    
                });
            </script>
        </body>
    </html>

    那个插槽分享出去的,那个插槽才可以使用

    运行效果:

    5.4 v-slot的相关情况

    1)独占默认插槽的缩写语法:废弃template标签直接加载组件上

    <current-user v-slot:default="slotProps">
      {{ slotProps.user.firstName }}
    </current-user>
    <current-user v-slot="slotProps">
      {{ slotProps.user.firstName }}
    </current-user>

    2)名称直接带表达式

    <current-user v-slot="{ user }">
      {{ user.firstName }}
    </current-user>

    3)名称重命名

    <current-user v-slot="{ user: person }">
      {{ person.firstName }}
    </current-user>

    4)变更属性内容

    <current-user v-slot="{ user = { firstName: 'Guest' } }">
      {{ user.firstName }}
    </current-user>

    5.5 动态插槽名

    变量名不能有大写字母。

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="vue.js" type="text/javascript" charset="utf-8"></script>
        </head>
        <body>
            <div id="app">
                <!-- 使用组件时 绑定组件对外公开的事件的事件处理方法-->
                <my-component><template v-slot="begin">父给begin的内容{{pValue}}}</template>默认填充内容</my-component>
                <!-- 通过v-slot:插糟名="公开分享数据的引用变量名" -->
                <my-component><template v-slot:[slotname]="slotProps">{{slotProps.cValue}}-{{slotProps.user.firstName}}</template></my-component>
            </div>
            <script type="text/javascript">
                // 1、定义和注册合并
                Vue.component('my-component', {
                    data: function() {
                        return {
                            cValue: "内部变量",
                            user:{
                                firstName:'wang',
                                lastName:'zhirui'
                            }
                        }
                    },
                    // 对应插糟上一定要通过v-bind:对外属性名,或者简写方式:对外属性名,公开要对外分享得内部数据
                    template: '<div><slot name="begin" v-bind:cValue="cValue" :user="user">begin默认内容</slot><button>子组件<slot>center默认内容</slot></button><slot name="end">end默认内容</slot></div>'
                });
                var app = new Vue({
                    // 挂载元素 - 作用范围
                    el: '#app',
                    data: {
                        // 外部组件的变量
                        pValue:"外部变量",
                        slotname:"begin"
                    },
                    created:function(){
                        console.info("12121")
                    }
                    
                });
            </script>
        </body>
    </html>

    5.5 具名插槽的缩写

    v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

  • 相关阅读:
    m_Orchestrate learning system---三十一、模板和需求的关系
    你如何破解后安装PS cs6
    STL 源代码分析 算法 stl_algo.h -- pre_permutation
    霸气侧漏HTML5--之--canvas(1) api + 弹球例子
    hello nodejs
    整洁的正则表达式教程,这很容易理解
    基于Gsoap 的ONVIF C++ 库
    一个常见的错误时编写代码
    IT只忍者龟Photoshop简单人像的头发抠图过程
    彩色图像--色彩空间 HSI(HSL)、HSV(HSB)
  • 原文地址:https://www.cnblogs.com/feihusurfer/p/12250057.html
Copyright © 2020-2023  润新知