第七章 组件

编辑本文章

7.1  组件与复用

7.1.1 为什么使用组件

7.1.2 组件用法

组件注册,全局注册后,任何Vue实例都可以使用。my-component就是注册的组件自定义标签名称,推荐使用小写和减号分割形式命名。

Vue.component("my-component",{
    /*选项*/
})

 要在父实例中使用该组件时,需要在实例创建之前注册。选项中的template的结构必须被DOM元素包含,如这里的div

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.1.2 组件用法</title>
</head>
<body>

<div id="app">
    <my-component></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("my-component", {
        template:"<div>这里是组件的内容</div>"
    })
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

使用components注册局部组件,局部组件可以在该实例作用域下有效。组件也可以使用components来注册组件,使组件嵌套。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.1.2 组件用法</title>
</head>
<body>

<div id="app">
    <my-component></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">

    var Child = {
        template: "<div>这里是局部注册组件</div>"
    }

    var app = new Vue({
        el: "#app",
        componets: {
            //注册局部组件
            "my-component": Child
        }
    })
</script>
</body>
</html>
View Code

Vue组件在某些情况下受HTML的限制,如table内规定只允许tr和td、th等,所以在table内直接使用组件是无效的。

这种特殊情况可以用is属性来挂载

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.1.2.1 is属性挂载组件</title>
</head>
<body>

<div id="app">
    <table>
        <tbody is="my-component"></tbody>
    </table>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("my-component", {
        template: "<div>这里是组件内容,可用is属性挂载到table内</div>"
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

在组件中,除了有template选项外,还有data,computed,methods等方法,不过data必须是函数,将数据return出去。

JavaScript对象是引用关系,所以如果return出的对象引用了外部的一个对象,那么这个对象就是共享的,任何一方修改数据都会同步。所以我们给组件返回一个新的data对象来独立。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.1.2.2 组件的其他属性</title>
</head>
<body>

<div id="app">
    <my-component></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("my-component", {
        template: "<div>消息内容:{{message}}</div>",
        data: function () {
            return {
                message: '这里是return回的内容'
            }
        }
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

7.2  使用props传递数据

7.2.1  基本用法

组件不仅仅是要把模板的内容进行复用,更重要的是组件间要进行通信。父组件需要向子组件正向传递数据,子组件根据收到的数据进行渲染,正向传递就是通过props来实现;

props值可以是数组,也可以是对象。当使用DOM模板时,驼峰命名的props名称要转换为短横线分割命名。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.2.1 props正向传递参数</title>
</head>
<body>

<div id="app">
    <my-component message="来自父组件的数据"></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('my-component', {
        props: ['message'],
        template: "<div>{{message}}</div>"
    })
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

父组件的动态数据传递给子组件,当不使用v-bind时,传递的数字、布尔值、数组、对象等仅是字符串,非源对象类型。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.1.2.3 父组件动态数据传递给子组件</title>
</head>
<body>

<div id="app">
    <label for="">父组件中输入:</label>
    <input type="text" v-model="parentMessage">
    <my-component v-bind:message="parentMessage"></my-component>
</div>


<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('my-component', {
        template: "<div>子组件:{{message}}</div>",
        props: ["message"]
    });
    var app = new Vue({
        el: "#app",
        data: {
            parentMessage: "",
        }
    })
</script>
</body>
</html>
View Code

7.2.2  单向数据流

 props传递的数据是单向的,即子组件中的修改不会传递给父组件。但是在JavaScript中对象和数组是应用类型,都指向同一个内存空间,所以props是对象和数组时,在子组件中改变会影响父组件。

情况1:父组件传递初始值进来,子组件自己保存,并在自己作用域下使用和修改,这种情况可以在data内再声明一个数据,引用父组件的prop。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.2.2.1 单向数据流之子组件保存</title>
</head>
<body>

<div id="app">
    <my-component v-bind:init-count="1"></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("my-component", {
        template: "<div>{{count}}</div>",
        props: ["initCount"],
        data: function () {
            return {
                count: this.initCount
            }
        }
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

情况2:prop作为需要被转变的原始值传入,这时使用计算属性就行

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.2.2.2 单向数据流之子组件计算</title>
</head>
<body>

<div id="app">
    <my-component v-bind:width="100"></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("my-component", {
        template: "<div v-bind:style='style'>组件内容</div>",
        props: ["width"],
        computed: {
            style: function () {
                return {
                    width: this.width + 'px'
                }
            }
        }
    })
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

7.2.3  数据验证

当prop需要验证时,就需要传入对象。一般组件给别人用时,推荐进行数据验证,比如要求数字类型传入字符串就弹出警告。

<script type="application/javascript">
    Vue.component('my-component', {
        props: {
            //必须时数字类型
            propA: Number,
            //必须时字符串或数字类型
            propB: [String, Number],
            //布尔值,如果没有定义,默认为true
            propC: {
                type: Boolean,
                default: true
            },
            //数字,而且是比传
            propD: {
                type: Number,
                required: true
            },
            //如果是数组或对象,默认值必须是一个函数来返回
            propE: {
                type: Array,
                default: function () {
                    return [];
                }
            },
            //自定义一个验证函数
            propF: {
                validator: function (value) {
                    return value > 10;
                }
            }
        }
    })
</script>
View Code

验证的type类型可以是:

  1.  String
  2. Number
  3. Boolean
  4. Object
  5. Array
  6. Function

 当prop验证失败时,在开发版本下会在控制台抛出一条警告。

7.3  组件通信

 组件通信分父子组件通信、兄弟组件通信、跨级组件通信

  7.3.1 自定义事件

    当子组件需要向父组件传递数据时,就需要用到自定义事件。子组件用$emit()来触发事件,父组件用$on()来监听子组件的事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.3.1 自定义事件</title>
</head>
<body>

<div id="app">
    <p>总数:{{total}}</p>
    <my-component @increase="handleGetTotal" v-on:reduce="handleGetTotal"></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('my-component', {
        template: '<div><button @click="handleIncrease">+1</button><button v-on:click="handleReduce">-1</button></div>',
        data: function () {
            return {
                counter: 0
            }
        },
        methods: {
            handleIncrease: function () {
                this.counter++;
                this.$emit('increase', this.counter);
            },
            handleReduce: function () {
                this.counter--;
                this.$emit('reduce', this.counter)
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            total: 0
        },
        methods: {
            handleGetTotal: function (total) {
                this.total = total;
            }
        }
    })
</script>
</body>
</html>
View Code

7.3.2 使用v-model

这里使用的特殊事件名称input,如果不用v-model,也可以使用自定义事件@input=“”来实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.3.2.1 使用v-model</title>
</head>
<body>

<div id="app">
    <p>总数:{{total}}</p>
    <my-component v-model="total"></my-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("my-component", {
        template: '<div><button v-on:click="handleClick">+1</button></div>',
        data: function () {
            return {
                counter: 0
            }
        },
        methods: {
            handleClick: function () {
                this.counter++;
                this.$emit("input", this.counter)
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            total: 0
        }
    })
</script>
</body>
</html>
View Code

用v-model创建自定义的表单输入组件,进行数据双向绑定。

条件:

  1. 接收一个value属性
  2. 在有新的value时,出发input事件

这里props中的value就是v-model传入的参数

<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>7.3.2.1 使用v-model事件表单输入组件进行数据双向绑定</title>
</head>
<body>

<div id="app">
    <p>总数:{{ total }}</p>
    <my-component v-model="total"></my-component>
    <button v-on:click="handleReduce">-1</button>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("my-component", {
        template: '<input type="text" v-bind:value="value" @input="updateValue">',
        props: ["value"],
        methods: {
            updateValue: function (event) {
                this.$emit('input', event.target.value);
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            total: 0
        },
        methods: {
            handleReduce: function () {
                this.total--;
            }
        }
    })
</script>
</body>
</html>
View Code

7.3.3 非父子组件通信

    创建一个中间人bus(即一个Vue实例),在事件发布者内部发布事件,在接收者内部接收事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.3.3 非父子组件通信</title>
</head>
<body>
<div id="app">
    {{ message }}
    <component-a></component-a>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    var bus = new Vue();
    Vue.component('component-a', {
        template: '<button v-on:click="handleEvent">传递事件</button>',
        methods: {
            handleEvent: function () {
                bus.$emit('on-message', "来自component-a的内容")
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            message: ""
        },
        mounted: function () {
            var _this = this;
            bus.$on('on-message', function (msg) {
                _this.message = msg;
            })
        }
    })
</script>
</body>
</html>
View Code

    

父链

在子组件中,使用this.$parent可以直接访问该组件的父实例或组件,父组件也可以通过this.$children访问他所有的子组件,而且可以无限访问,知道根实例或最内层组件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.3.3.1 父链</title>
</head>
<body>

<div id="app">
    {{ message }}
    <component-a></component-a>
</div>


<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('component-a', {
        template: '<button v-on:click="handleEvent">通过父链直接修改数据</button>',
        methods: {
            handleEvent: function () {
                //访问到父链后,可以做任何操作,比如直接修改数据
                this.$parent.message = '子组件component-a修改了数据'
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            message: '根组件原始数据'
        }
    })
</script>
</body>
</html>
View Code

    子组件索引

    当子组件较多时,通过this.$children来遍历找出一个组件比较困难,Vue提供了子组件索引的方法,用特殊的属性ref来为子组件指定索引名称

<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>7.3.3.2 子组件索引</title>
</head>
<body>

<div id="app">
    <button v-on:click="handleRef">通过ref获取子组件</button>
    <p ref="p">P内容</p>
    <component-a ref="child"></component-a>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('component-a', {
        template: '<div>子组件</div>',
        data: function () {
            return {
                message: '子组件内容'
            }
        }
    });
    var app = new Vue({
        el: "#app",
        methods: {
            handleRef: function () {
                var msg = this.$refs.child.message;
                console.log(msg)
                console.log(this.$refs.p)//类似v1版本中的v-el,在2.0中,Vue会自动判断时普通标签还是组件
            }
        }
    })
</script>
</body>
</html>
View Code

7.4  使用slot分发内容

  7.4.1  什么时slot

    当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,该过程叫做内容分发。

    特点:

      <app>组件不知道它的挂载点会有什么内容。挂载点的内容是由<app>的父组件决定的。

      <app>组件很可能有自己的模板。

    props传递数据、events触发事件、slot分发内容构成了Vue的组件的3个API来源。

  7.4.2 作用域

    编译作用域。父组件模板内容是在父组件作用域内编译,子组件模板的内容是在子组件作用域内编译。

  7.4.3 slot用法

    单个slot:在子组件内使用<slot>元素就可以在这个组件中开启一个slot,在父组件模板里,插入在子组件标签内的所有内容将替代子组件的<slot>标签已经它的内容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.4.3.1 单slot用法</title>
</head>
<body>

<div id="app">
    <child-component>
        <p>分发的内容</p>
        <p>更多分发的内容</p>
    </child-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("child-component", {
        template: '\
        <div>\
            <slot>\
                <p>如果父组件没有插入内容,我将作为默认出现</p>\
            </slot>\
        </div>'
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

具名slot:给<slot>元素指定一个name后可以分发多个内容,具名slot可以与单个slot共存

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.4.3.2 具名slot用法</title>
</head>
<body>

<div id="app">
    <child-component>
        <h2 slot="header">标题</h2>
        <p>分发的内容</p>
        <p>更多分发的内容</p>
        <div slot="footer">底部信息</div>
    </child-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('child-component', {
        template: '\
            <div class="container">\
                <div class="header"><slot name="header"></slot></div>\
                <div class="main"><slot></slot></div>\
                <div class="footer"><slot name="footer"></slot></div>\
            </div>'
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

7.4.4 作用域插槽

    作用域插槽是一种特殊的slot,使用一个可以复用的模板替换已渲染的元素。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.4.4.1 作用域插槽</title>
</head>
<body>

<div id="app">
    <child-component>
        <template scope="prols"><!--临时变量来访问子组件插槽的数据msg-->
            <p>来自父组件的内容</p>
            <p>{{ prols.msg }}</p>
        </template>
    </child-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('child-component', {
        template: '\
        <div class="container">\
            <slot msg="来自子组件的内容"></slot>\
        </div>'
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

使用作用域插槽渲染列表组件,驼峰命名的props名称要转换为短横线分割命名

子组件my-list接收一个来自父级的prop数组books,并且将他在name为book的slot上使用v-for循环指令,暴露一个变量nookName(注意在接收时是减号分割)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.4.4.2 作用域插槽列表组件</title>
</head>
<body>

<div id="app">
    <my-list v-bind:books="books">
        <template slot="book" scope="props">
            <li>{{ props.bookName }}</li>
        </template>
    </my-list>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('my-list', {
        template: '\
        <ul>\
            <slot name="book" v-for="book in books" v-bind:book-name="book.name"></slot>\
        </ul>',
        props: {
            books: {
                type: Array,
                default: function () {
                    return [];
                }
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            books: [
                {name: "《Vue.js实战》"},
                {name: "《JavaScript实战》"},
                {name: "《Html5实战》"}
            ]
        }

    })
</script>
</body>
</html>
View Code

7.4.5 访问slot

通过$slots访问slot,default是默认的slot,即没有name属性的slot

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.4.5.1 访问slot</title>
</head>
<body>

<div id="app">
    <child-component>
        <h2 slot="header">标题</h2>
        <p>正文内容</p>
        <p>更多正文内容</p>
        <div slot="footer">底部信息</div>
    </child-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('child-component', {
        template: '\
        <div class="container">\
            <div class="header">\
                <slot name="header"></slot>\
            </div>\
            <div class="main">\
                <slot></slot>\
            </div>\
            <div class="footer">\
                <slot name="footer"></slot>\
            </div>\
        </div>',
        mounted: function () {
            var header = this.$slots.header;
            var main = this.$slots.default;
            var footer = this.$slots.footer;
            console.log(footer);
            console.log(footer[0].elm.innerHTML);
        }
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

7.5  组件高级用法

  7.5.1 递归组件

    组件在它的模板内可以递归调用自己,只要给组件设置name的选项即可。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.5.1 递归组件</title>
</head>
<body>

<div id="app">
    <child-component v-bind:count="1"></child-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("child-component", {
        name: "child-component",
        props: {
            count: {
                type: Number,
                default: 1
            }
        },
        template: '\
            <div class="child">\
            子组件第{{count}}调用\
                <child-component v-bind:count="count+1" v-if="count < 3"></child-component>\
            </div>\
        '
    });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

7.5.2 内联模板

    在使用组件时,给标签使用inline-template特性,组件就会把它的内容当作模板,而不是把它当作内容分发。

    在父组件和子组件中都声明的数据,两个都可以渲染,当同名时,优先使用子组件数据。作用域比较难理解,如非特殊不使用内敛模板。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.5.2 内联模板</title>
</head>
<body>

<div id="app">
    <child-component inline-template>
        <div>
            <h2>在父组件中定义子组件模板</h2>
            <p>{{ message }}</p>
            <p>{{ msg }}</p>
        </div>
    </child-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("child-component", {
        data: function () {
            return {
                msg: "在子组件声明的数据"
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            message: "在父组件中声明的数据"
        }
    })
</script>
</body>
</html>
View Code

7.5.3 动态组件

    Vue提供一个特殊的元素<component>用来动态挂载不同的组件,使用is特性来选择要挂载的组件。

<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>7.5.3 动态组件</title>
</head>
<body>

<div id="app">
    <component v-bind:is="currentView"></component>
    <button v-on:click="handleChangeView('A')">切换到A</button>
    <button v-on:click="handleChangeView('B')">切换到B</button>
    <button v-on:click="handleChangeView('C')">切换到C</button>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    var app=new Vue({
        el:"#app",
        components:{
            conA:{
                template:'<div>组件A</div>'
            },
            conB:{
                template:'<div>组件B</div>'
            },
            conC:{
                template:'<div>组件C</div>'
            },
        },
        data:{
            currentView:"conA"
        },
        methods:{
            handleChangeView:function (component) {
                this.currentView='con'+component;
            }
        }
    })
</script>
</body>
</html>
View Code

7.5.4 异步组件

    Vue动态解析组件,只有组件需要渲染时才触发工厂函数,并把结果进行缓存。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.5.4 异步组件</title>
</head>
<body>

<div id="app">
    <child-component></child-component>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component("child-component",
        function (resolve, reject) {
            window.setTimeout(function () {
                resolve({
                    template: "<div>我是异步渲染的</div>"
                });
            }, 2000)
        });
    var app = new Vue({
        el: "#app"
    })
</script>
</body>
</html>
View Code

7.6  其他

  7.6.1  $nextTick

Vue在观察到数据变化时,并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,在缓冲时会对数据去重,从而避免不必要的计算和DOM操作。

在下一个事件循环tick中,刷新队列并执行实际工作。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.6.1 $nextTick</title>
</head>
<body>
<div id="app">
    <div id="div" v-if="showDiv">这是一段文本</div>
    <button @click="getText">获取div内容</button>
</div>


<script src="../vue.min.js"></script>

<script type="application/javascript">
    var app=new Vue({
        el:"#app",
        data:{
            showDiv:false
        },
        methods:{
            getText:function () {
                this.showDiv=true;
                var text=document.getElementById("div").innerHTML;
                console.log(text);
            }
        }
    })
</script>
</body>
</html>
View Code

这里报错没有获取到div元素,因为在执行this.showDiv=true;时,div任然没有被创建出来,直到下一个Vue事件循环才开始创建。$nextTick就是用来知道什么时候DOM更新完成的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.6.1 $nextTick</title>
</head>
<body>
<div id="app">
    <div id="div" v-if="showDiv">这是一段文本</div>
    <button @click="getText">获取div内容</button>
</div>


<script src="../vue.min.js"></script>

<script type="application/javascript">
    var app=new Vue({
        el:"#app",
        data:{
            showDiv:false
        },
        methods:{
            getText:function () {
                this.showDiv=true;
                this.$nextTick(function () {
                    var text=document.getElementById("div").innerHTML;
                    console.log(text);
                })
            }
        }
    })
</script>
</body>
</html>
View Code

7.6.2  X-Templates

    另一种定义模板的方式。避免在JavaScript里面拼接字符串。在script标签里使用text/x-template类型,并且指定一个id,将这个id赋给template。

在这个script标签里写html代码,不用考虑换行问题。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>X-Templates</title>
</head>
<body>

<div id="app">
    <my-component></my-component>
    
    <script type="text/x-template" id="my-component">
        <div>这是组件内容</div>
    </script>
</div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    Vue.component('my-component',{
        template:"#my-component"//这个id是一个script标签的id
    });
    var app=new Vue({
        el:"#app"
    })
</script>
</body>
</html>
View Code

    

7.6.3  手动挂载实例

    Vue提供Vue.extent和$mount两个方法来手动挂载一个实例。Vue.extend是基础Vue构造器,创建一个“子类”,参数是一个包含组件选项的对象。

    没有el元素就处于未挂载状态,可以用$mount()来手动挂载到一个DOM对象上。可以链式调用实例其他方法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>7.6.3 手动挂载实例</title>
</head>
<body>

<div id="mount-div"></div>

<script src="../vue.min.js"></script>

<script type="application/javascript">
    var MyComponent = Vue.extend({
        template: '<div>Hello:{{ name }}</div>',
        data: function () {
            return {
                name: "VueJs"
            }
        }
    });
    new MyComponent().$mount("#mount-div");
</script>
</body>
</html>
View Code

7.7 实战:两个常用组件开发

  7.7.1 开发一个数字输入框组件

input-number.js 数字输入框组件

function isValueNumber (value) {
    return (/(^-?[0-9]+\.{1}\d+$)|(^-?[1-9][0-9]*$)|(^-?(){1}$)/).test(value + '');
}
Vue.component('input-number',{
    template:'\
    <div class="input-number">\
    <input type="text" v-bind:value="currentValue" v-on:change="handleChange">\
    <button @click="handleDown" :disabled="currentValue <= min">-</button>\
    <button @click="handleUp" :disabled="currentValue >= max">+</button>\
    </div>\
    ',
    //props向子组件传递数据
    props:{
        max:{
            type:Number,
            defaule:Infinity
        },
        min:{
            type: Number,
            default:-Infinity
        },
        value:{
            type:Number,
            default:0
        }
    },
    data:function () {
        return {
            currentValue:this.value
        }
    },
    watch:{
        currentValue:function (val) {
            this.$emit("input",val);
            this.$emit("on-change",val);
        },
        value:function (val) {
            this.updateValue(val);
        }
    },
    methods:{
        updateValue:function (val) {
            if (val>this.max) val=this.max;
            if(val<this.min) val=this.min;
            this.currentValue=val;
        },
        handleDown:function () {
            if (this.currentValue<=this.min) return;
            this.currentValue-=1;
        },
        handleUp:function () {
            if (this.currentValue>=this.max) return;
            this.currentValue+=1;
        },
        handleChange:function (event) {
            var val=event.target.value.trim();
            var max=this.max;
            var min=this.min;

            if (isValueNumber(val)){
                val=Number(val);
                this.currentValue=val;
                if(val>max){
                    this.currentValue=max;
                }else if(val<min){
                    this.currentValue=min;
                }
            }else {
                event.target.value=this.currentValue;
            }
        }
    },
    mounted:function () {
        this.updateValue(this.value);
    }
});
View Code

index.js 根实例

var app=new Vue({
    el:"#app",
    data:{
        value:5
    }
});
View Code

index.html 入口页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>

<div id="app">
    <input-number v-model="value" v-bind:max="10" v-bind:min="5"></input-number>
</div>

<script src="vue.min.js"></script>

<script src="input-number.js"></script>
<script src="index.js"></script>
<script type="application/javascript">

</script>
</body>
</html>
View Code

  7.7.1 开发一个数字输入框组件

 

posted @ 2019-05-01 09:54  丫丫625202  阅读(180)  评论(0编辑  收藏  举报