• 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

  • 相关阅读:
    VysorPro助手
    Play 2D games on Pixel running Android Nougat (N7.1.2) with Daydream View VR headset
    Play 2D games on Nexus 6P running Android N7.1.1 with Daydream View VR headset
    Native SBS for Android
    ADB和Fastboot最新版的谷歌官方下载链接
    How do I install Daydream on my phone?
    Daydream Controller手柄数据的解析
    蓝牙BLE传输性能及延迟分析
    VR(虚拟现实)开发资源汇总
    Android(Java)控制GPIO的方法及耗时分析
  • 原文地址:https://www.cnblogs.com/feihusurfer/p/12250057.html
Copyright © 2020-2023  润新知