zoukankan      html  css  js  c++  java
  • Vue.js 2.x笔记:组件(5)

    1. 组件简介

      组件(Component)是 Vue.js 最强大的功能之一,组件可以扩展 HTML 元素,封装可重用的代码。

      组件:为了拆分Vue实例的代码量,以不同的组件来划分不同的功能模块,需要什么样的功能,可以去调用对应的组件。

      模块化和组件化的区别:

      ◊ 模块化:是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一。

      ◊ 组件化:是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用。  

    2. 注册组件

      Vue.js提供两种组件注册方式:全局注册和局部注册。

    2.1 全局组件

      全局注册需要在根实例初始化之前注册,这样组件才能在任意实例中被使用。

      注册全局组件语法格式:

    Vue.component(tagName, options)

      其中,tagName 为组件名,options 为配置选项。

      这条语句需要写在var vm = new Vue({ options })之前。

      注册组件后调用方式:

    <tagName></tagName>

      所有实例都能用全局组件。

      组件名定义方式:PascalCase和kebab-case。在组件命名时可以采用PascalCase或kebab-case,但在DOM中只能使用kebab-case。

      PascalCase示例:

    <div id="app">
        <my-component></my-component>
    </div>
    <script>
        Vue.component('MyComponent', {
            template: '<div>标题</div>'
        });
    
        var vm = new Vue({
            el: "#app"
        });
    </script>

      kebab-case示例:

    <div id="app">
        <my-component></my-component>
    </div>
    <script>
        Vue.component('my-component', {
            template: '<div>标题</div>'
        });
    
        var vm = new Vue({
            el: "#app"
        });
    </script>
    <div id="app">
        <home></home>
    </div>
    <script>
        Vue.component("home", {
            template: "<div>{{text}}</div>",
            data: function () {
                return {
                    text: "主页"
                };
            }
        });
    
        new Vue({
            el: "#app"
        });
    </script>
    <div id="app">
        <home></home>
    </div>
    <script>
        var homeTpl = Vue.extend({ 
            template: "<div>{{text}}</div>",
            data: function () {
                return {
                    text: "主页"
                };
            }
        });
    
        Vue.component('home', homeTpl);
    
        new Vue({
            el: "#app"
        });
    </script>

      使用template标签:

    <div id="app">
        <home></home>
    </div>
    <template id="tpl">
        <div>{{text}}</div>
    </template>
    <script>
        Vue.component("home", {
            template: "#tpl",
            data: function () {
                return {
                    text: "主页"
                };
            }
        });
    
        new Vue({
            el: "#app"
        });
    </script>

    2.2 局部组件

      局部组件只能在被注册的组件中使用,不能在其他组件中使用。

    <div id="app">
        <home></home>
    </div>
    <script>
        new Vue({
            el: "#app",
            components: {
                "home": {
                    template: "<div>{{text}}</div>",
                    data: function () {
                        return {
                            text: "主页"
                        };
                    }
                }
            }
        });
    </script>

    2.3 Vue.extend

    2.3.1 基本使用

    <div id="app">
        <home></home>
    </div>
    <script>
        var home = Vue.extend({
            template: "<div>标题</div>"
        });
    
        Vue.component("home", home);
    
        new Vue({
            el: "#app"
        });
    </script>

    2.3.2 参数data

      data:在 Vue.extend() 中必须是函数。

    <body>
        <task></task>
    
        <script>
            var task = Vue.extend({
                template:"<div>{{ taskName }}</div>",
                data:function(){
                    return {
                        taskName:"任务名称"
                    }
                }
            });
    
            new task().$mount("task");
        </script>
    </body>

    2.3.3 使用$mount

      在实例中没有el选项时,可通过mount挂载。

      mount:挂载,将vue实例挂靠在某个dom元素上的一个过程。

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <title>libing.vue</title>
        <script src="node_modules/vue/dist/vue.min.js"></script>
    </head>
    
    <body>
        <div id="app"></div>
        <script>
            var home = Vue.extend({
                template: "<div>标题</div>"
            });
    
            new home().$mount("#app");
        </script>
    </body>
    
    </html>

    3. 组件通信

    3.1 props:父组件向子组件传递数据

      prop 是组件用来传递数据的自定义特性,在组件上注册自定义属性。

      prop特性注册成为组件实例的属性。

       props :父组件向子组件传递数据。

      一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。

    3.1.1 静态props

      示例:

    <div id="app">
        <home text="主页"></home>
    </div>
    <script>
        var homeTpl = Vue.extend({
            props:["text"],
            template: "<div>{{text}}</div>"
        });
    
        Vue.component('home', homeTpl);
    
        new Vue({
            el: "#app"
        });
    </script>

    3.1.2 动态props

      使用 v-bind 动态绑定 props 的值到父组件的数据中。每当父组件的数据变化时,该变化也会传导给子组件。

    <div id="app">
        <home v-bind:text="text"></home>
    </div>
    <script>
        var homeTpl = Vue.extend({
            props: ["text"],
            template: "<div>{{text}}</div>"
        });
    
        Vue.component('home', homeTpl);
    
        new Vue({
            el: "#app",
            data: {
                text: "主页"
            }
        });
    </script>

      由于HTML Attribute不区分大小写,当使用DOM模板时,camelCase的props名称要转为kebab-case。

    <div id="app">
        <home warning-text="提示信息"></home>
    </div>
    <script>
        Vue.component('home', {
            props: ['warningText'],
            template: '<div>{{ warningText }}</div>'
        });
    
        var vm = new Vue({
            el: "#app"
        });
    </script>

      传递的数据可以是来自父级的动态数据,使用指令v-bind来动态绑定props的值,当父组件的数据变化时,也会传递给子组件。

    <div id="app">
        <home v-bind:warning-text="warningText"></home>
    </div>
    <script>
        Vue.component('home', {
            props: ['warningText'],
            template: '<div>{{ warningText }}</div>'
        });
    
        var vm = new Vue({
            el: "#app",
            data: {
                warningText: '提示信息'
            }
        });
    </script>

    注:prop 是单向传递,当父组件的属性变化时,将传递给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态。

      示例:

    <template>
      <li>{{ id }}-{{ text }}</li>
    </template>
    <script>
    export default {
      name: "TodoItem",
      props: ["id", "text"]
    };
    </script>
    TodoItem.vue
    <template>
      <ul>
        <TodoItem
          v-for="item in list"
          :key="item.id"
          :id="item.id"
          :text="item.text"
        ></TodoItem>
      </ul>
    </template>
    <script>
    import TodoItem from "./TodoItem";
    
    export default {
      name: "TodoList",
      components: {
        TodoItem
      },
      data: function() {
        return {
          list: [
            {
              id: 1,
              text: "To Do"
            },
            {
              id: 2,
              text: "In progress"
            },
            {
              id: 3,
              text: "Done"
            }
          ]
        };
      }
    };
    </script>
    TodoList.vue
    <template>
      <div id="app">
        <TodoList />
      </div>
    </template>
    
    <script>
    import TodoList from './views/TodoList'
    
    export default {
      name: 'App',
      components: {
        TodoList
      }
    }
    </script>
    App.vue

    3.1.3 props验证

      为组件的 prop 指定验证要求,如果有一个需求没有被满足,则 Vue 会在控制台中警告。

    Vue.component('my-component', {
        props: {
            // 基础的类型检查 (`null` 匹配任何类型)
            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
                }
            }
        }
    });

      类型检查:type可以是下列原生构造函数中的一个:String、Number、Boolean、Array、Object、Date、Function、Symbol,也可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。

      示例:

    <div id="app">
        <parent-component></parent-component>
    </div>
    
    <template id="child-component1">
        <h2>{{ message }}</h2>
    </template>
    <template id="child-component2">
        <h2>{{ message }}</h2>
    </template>
    <template id="parent-component">
        <div>
            <child-component1></child-component1>
            <child-component2></child-component2>
        </div>
    </template>
    
    <script>
        Vue.component('parent-component', {
            template: '#parent-component',
            components: {
                'child-component1': {
                    template: '#child-component1',
                    data() {
                        return {
                            message: '子组件1'
                        };
                    }
                },
                'child-component2': {
                    template: '#child-component2',
                    data() {
                        return {
                            message: '子组件2'
                        };
                    }
                }
            }
        });
    
        var vm = new Vue({
            el: "#app"
        });
    </script>

      示例:

    <div id="app">
        <todo :todo-data="taskList"></todo>
    </div>
    
    <template id="tpl-todo-item">
        <li>{{ id }} - {{ text }}</li>
    </template>
    
    <template id="tpl-todo-list">
        <ul>
            <todo-item v-for="item in todoData" :id="item.id" :text="item.text"></todo-item>
        </ul>
    </template>
    
    <script>
        // 构建一个子组件
        var todoItem = Vue.extend({
            template: "#tpl-todo-item",
            props: {
                id: {
                    type: Number,
                    required: true
                },
                text: {
                    type: String,
                    default: ''
                }
            }
        })
    
        // 构建一个父组件
        var todoList = Vue.extend({
            template: "#tpl-todo-list",
            props: {
                todoData: {
                    type: Array,
                    default: []
                }
            },
            // 局部注册子组件
            components: {
                todoItem: todoItem
            }
        })
    
        // 注册到全局
        Vue.component('todo', todoList)
    
        new Vue({
            el: "#app",
            data: {
                taskList: [{
                        id: 1,
                        text: 'New'
                    },
                    {
                        id: 2,
                        text: 'InProcedure'
                    },
                    {
                        id: 3,
                        text: 'Done'
                    }
                ]
            }
        });
    </script>

    3.2 自定义事件:子组件向父组件传递数据

      每一个Vue实例都实现事件接口:

      $on(eventName):监听事件

      $emit(eventName) :触发事件,自定义事件。推荐始终使用 kebab-case 的事件名。

      子组件需要向父组件传递数据时,子组件用$emit(eventName)来触发事件,父组件用$on(eventName)来监听子组件的事件。

      示例1:

    <template>
      <div>
        <button @click="onparent">子组件触发父组件</button>
      </div>
    </template>
    <script>
    export default {
      methods: {
        onparent() {
          this.$emit("onchild");
        }
      }
    };
    </script>
    Child.vue
    <template>
      <div>
        <Child @onchild="inparent"></Child>
      </div>
    </template>
    <script>
    import Child from "./Child";
    export default {
      components: {
        Child
      },
      methods: {
        inparent() {
          console.log("父组件响应了");
        }
      }
    };
    </script>
    Parent.vue
    <template>
      <div id="app">
        <Parent />
      </div>
    </template>
    
    <script>
    import Parent from './views/Parent'
    
    export default {
      name: 'App',
      components: {
        Parent
      }
    }
    </script>
    App.vue

      示例2:

    <div id="app">
        <searchbar></searchbar>
    </div>
    
    <template id="tpl-search-form">
        <div class="input-group form-group" style=" 500px;">
            <input type="text" class="form-control" placeholder="请输入查询关键字" v-model="keyword" />
            <span class="input-group-btn">
                <input type="button" class="btn btn-primary" value="查询" @click="search">
            </span>
        </div>
    </template>
    <template id="tpl-search-bar">
        <searchform @onsearch="search"></searchform>
    </template>
    
    <script>
        // 构建一个子组件
        var searchform = Vue.extend({
            template: "#tpl-search-form",
            data: function () {
                return {
                    keyword: 'libing'
                };
            },
            methods: {
                search: function () {
                    this.$emit('onsearch', this.keyword);
                }
            }
        });
    
        // 构建一个父组件
        var searchbar = Vue.extend({
            template: "#tpl-search-bar",
            components: {
                searchform: searchform
            },
            methods: {
                search(keyword) {
                    console.log(keyword);
                }
            }
        })
    
        // 注册到全局
        Vue.component('searchbar', searchbar);
    
        new Vue({
            el: "#app"
        });
    </script>

      购物车示例:

    <div id="app">
        <shoppingcart :shopppingcarts="products" @calc="getTotal"></shoppingcart>
        <div>总计:{{ totalPrice }}</div>
    </div>
    <template id="shoppingcart">
        <table>
            <tr>
                <th>商品ID</th>
                <th>商品名称</th>
                <th>单价</th>
                <th>数量</th>
            </tr>
            <tr v-for="item in shopppingcarts">
                <td>{{ item.ID }}</td>
                <td>{{ item.ProductName }}</td>
                <td>{{ item.UnitPrice }}</td>
                <td><input type="text" v-model="item.Quantity" @change="calcTotal" /></td>
            </tr>
        </table>
    </template>
    <script>
        var shoppingcart = Vue.extend({
            template: "#shoppingcart",
            props: ["shopppingcarts"],
            methods: {
                calcTotal: function () {
                    this.$emit("calc");
                }
            }
        });
    
        new Vue({
            el: "#app",
            components: {
                shoppingcart: shoppingcart
            },
            data: {
                totalPrice: 100,
                products: [{
                    ID: 1,
                    ProductName: "手机",
                    UnitPrice: 1000,
                    Quantity: 2
                }, {
                    ID: 2,
                    ProductName: "电脑",
                    UnitPrice: 5000,
                    Quantity: 5
                }]
            },
            methods: {
                getTotal() {
                    console.log(new Date());
                    this.totalPrice = 0;
                    this.products.forEach(product => {
                        this.totalPrice += product.UnitPrice * product.Quantity;
                    });
                }
            },
            mounted() {
                //当vue执行完毕之后,去执行函数
                this.getTotal();
            }
        });
    </script>

    3.3 EventBus:非父子组件通信

      非父子组件包括:兄弟组件、跨级组件。

      通过实例化一个Vue对象 (如:const bus = new Vue() ) 作为总线,在组件中通过事件传递参数( bus.$emit(event, [...args]) ),再在其他组件中通过bus来监听此事件并接受参数( bus.$on(event, callback) ),从而实现通信。

      示例:

      bus.js

    import Vue from 'vue'
    
    const bus = new Vue();
    
    export default bus;

      Send.vue

    <template>
      <div class="send">
        <h1>发送参数:{{msg}}</h1>
        <button @click="send">发送</button>
      </div>
    </template>
    <script>
    import bus from "../utils/bus.js";
    
    export default {
      data() {
        return {
          msg: "Hello World"
        };
      },
      methods: {
        send() {
          bus.$emit("receive", this.msg);
        }
      }
    };
    </script>

      Receive.vue

    <template>
      <div class="receive">
        <h1>接收参数:{{msg}}</h1>
      </div>
    </template>
    <script>
    import bus from "../utils/bus.js";
    
    export default {
      data() {
        return {
          msg: "Hello"
        };
      },
      created() {
        bus.$on("receive", param => {
          this.msg = param;
        });
      },
      beforeDestroy() {
        bus.$off("receive");
      }
    };
    </script>

      App.vue

    <template>
      <div id="app">
        <Send></Send>
        <Receive></Receive>
      </div>
    </template>
    
    <script>
    import Send from './views/Send'
    import Receive from './views/Receive'
    
    export default {
      name: 'App',
      components: {
        Send,
        Receive
      }
    }
    </script>
  • 相关阅读:
    仿美团pc,koa+ssr(四)
    基本的数据库操作脚本
    jQuery选择器总结
    经常学习参考网站
    WebAPI
    Sqlserver 基本面试题
    truncate和delete的区别
    Webservice,WCF,WebAPI 之间的区别
    WCF 学习
    学习angularJs(1)--引用文件
  • 原文地址:https://www.cnblogs.com/libingql/p/9171544.html
Copyright © 2011-2022 走看看