Vue学习笔记(三) – 组件的基本用法

  • 2018-08-04
  • 0
  • 0

Vue学习笔记

作者:黄志成

博客:地址

组件

组件是Vue最核心的功能,也是整个框架设计最精彩的地方。

组件就是将相同部分复用起来.

组件用法:注册组件 -> 挂载组件

注册有全局注册和局部注册两种方式.

// 全局注册
Vue.component('my-component', {
    // option
})

// 局部注册
var child = {
    // option
}

var app = new Vue({
    el: '#app',
    components: {
        'my-component': child
    }
})

下面来个demo 看看 什么是组件

image

这是一个计数器.

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
    <script src="./vue.js"></script>
</head>
<body>
<div id="app">
    <my-count></my-count>
    <my-count></my-count>
    <my-count></my-count>
</div>

<script type="text/javascript">
    Vue.component('my-count', {
        template: '<div><h1>{{count}}</h1><button @click="addCount">点击我+1</button></div>',
        data: function () {
            return {
                count: 0
            }
        },
        methods: {
            addCount: function () {
                this.count++;
            }
        }
    });
    var app = new Vue({
        el: "#app",
    })
</script>
</body>
</html>

组件不仅要把模板内容进行复用,更重要的是组件之间要通信.父组件正确的向子组件传递数据,子组件根据传递的数据显示不同内容或者是操作.

在Vue组件中,用props接收父级的数据。下面看看如何使用props

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
    <script src="./vue.js"></script>
</head>
<body>
<div id="app">
    <my-count init_num="1"></my-count>
    <my-count init_num="2"></my-count>
    <my-count init_num="1"></my-count>
</div>

<script type="text/javascript">
    Vue.component('my-count', {
        props: ['init_num'],
        template: '<div><h1>{{count}}</h1><button @click="addCount">点击我+1</button></div>',
        data: function () {
            return {
                count: this.init_num
            }
        },
        methods: {
            addCount: function () {
                this.count++;
            }
        }
    });
    var app = new Vue({
        el: "#app",
    })
</script>
</body>
</html>

现在通过传入 init_num 来设置每个计数器的初始值。 当然你也可以动态的传递这些初始值 通过 v-bind:init_num 的方式

在Vue2中取消了双向数据流.采用单向数据流.只有父组件给子组件传递.之所以这样设计是尽可能的将父子组件解耦.

下面通过一个Demo 来看看

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-component :msg="nihao"></my-component>
        <my-component :msg="buhao"></my-component>
    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">
    Vue.component('my-component', {
        props : ['msg'],
        template: '<div> <h1>{{ msg }}</h1> <button @click="btnClick">点击</button> <br /> <input type="text" v-model="msg"> </div>',
        data: function () {
            return {
                msgContent: this.msg
            }
        },
        methods: {
            btnClick: function () {
                alert(this.msgContent);
            }
        }

    });
    var app = new Vue({
        el: "#app",
        data: {
            nihao: '这是父组件的内容123',
            buhao: '这是父组件2'
        }
    });
</script>
</html>

如果改变了子组件的数据会报一个错误.

image

解决方法就是子组件用data接收props传递过来的数据.在自己的作用域下可以随意修改.

Vue.component('my-component', {
    props : ['msg'],
    template: '<div> <h1>{{ msgContent }}</h1> <button @click="btnClick">点击</button> <br /> <input type="text" v-model="msgContent"> </div>',
    data: function () {
        return {
            msgContent: this.msg
        }
    },
    methods: {
        btnClick: function () {
            alert(this.msgContent);
        }
    }

});

改成这样.问题就解决了。

上面说完了父组件向子组件传递数据的方式,接着我们继续看看子组件向父组件传递数据的方式吧

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-con @a="getA"></my-con>
    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        data :function () {
            return {
                count: 0,
                aaa: 12344
            }
        },
        template: '<div><h1>{{ count }}</h1><button @click="handerInc">+1</button></div>',
        methods: {
            handerInc: function () {
                this.count++;
                this.$emit('a', this.count,this.aaa);
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            count: 0
        },
        methods: {
            getA: function (val,a) {
                console.log("val is :" + val);
                console.log("a is :" + a);
            }
        }
    });
</script>
</html>

我们可以通过this.$emit向父组件传递数据

我们也可以使用 v-model 。

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-con v-model="getA"></my-con>
        <h1>{{ getA }}</h1>
    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        data :function () {
            return {
                count: 0,
            }
        },
        template: '<div><h1>{{ count }}</h1><button @click="handerInc">+1</button></div>',
        methods: {
            handerInc: function () {
                this.count++;
                this.$emit('input', this.count);
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            getA: 0
        },
    });
</script>
</html>

使用V-model 的前提就是 this.$emit('input', this.count); 事件名必须是input

v-model 还可以用来创建自定义表单输入组件,进行数据双向绑定

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-con v-model="total"></my-con>
        <button @click="inc">点击+1</button>
    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        props: ['value'],
        template: '<div><input type="number" :value="value" @input="updateValue" /></div>',
        methods: {
            updateValue: function (event) {
                console.log(event)
                this.$emit('input',event.target.value);
            }
        }

    });
    var app = new Vue({
        el: "#app",
        data: {
            total: 0,
        },
        methods: {
            inc: function () {
                this.total++;
            }
        }
    });
</script>
</html>

我们还推荐使用一个空的Vue实例来作为中央事件总线(BUS),也就是一个中介

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-con></my-con>
        {{msg}}
    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">
    const bus = new Vue();

    Vue.component('my-con', {
        template: '<button @click="btn_click">点击传递信息</button>',
        methods: {
            btn_click: function () {
                bus.$emit('on-message','this is my-con');
            }
        }
    });

    var app = new Vue({
        el: "#app",
        data: {
            msg: '',
        },
        mounted (){
            bus.$on('on-message', (msg) => {
                this.msg = msg;
            });
        }
    });
</script>
</html>

除此之外还有父链和子组件索引.用来组件通信.

下面说说 使用slot分发内容

在子组件使用特殊的标签可以为这个子组件开启一个插槽,在父组件模板里,插入在子组件标签内的所有内容将替代子组件的

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-con>{{msg}}</my-con>

    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        template: '<div><slot>这是默认内容</slot></div>',

    });

    var app = new Vue({
        el: "#app",
        data: {
            msg: '这是传入的内容',
        }
    });
</script>
</html>

还可以存在多个solt标签

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-con>
            <div slot="header">
                111
            </div>
            <div slot="footer">
                {{msg}}
            </div>
        </my-con>

    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        template: '<div><slot name="header">头部</slot><slot name="footer">底部</slot></div>',

    });

    var app = new Vue({
        el: "#app",
        data: {
            msg: '这是传入的内容',
        }
    });
</script>
</html>

作用域插槽是一个特殊的slot,使用一个可复用的模板替换已渲染的元素。其实就是子组件与父组件的数据动态交互的一种常见案例

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
    <div id="app">
        <my-con>
            <template slot="a" scope="props">
                {{props.msg}}
            </template>
        </my-con>

    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        template: '<div><slot name="a" msg="这是信息内容"></slot></div>',

    });

    var app = new Vue({
        el: "#app",
        data: {
            msg: '这是传入的内容',
        }
    });
</script>
</html>

递归组件 - 顾名思义就是自己调用自己.需要注意的是给递归的组件设置name 名
,当然还需要限制递归数量,不然会溢出。

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
<div id="app">
    <my-con :count="1">
    </my-con>

</div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        name: 'my-con',
        props: {
            count: {
                type: Number
            }
        },
        data: function () {
            return {
                thisCount: this.count
            }
        },
        template: '<div class="child"><my-con :count="count + 1" v-if="count < 3"></my-con>{{thisCount}}</div>'

    });

    var app = new Vue({
        el: "#app"
    });
</script>
</html>

还有内联模板,就是给组件标签加一个 inline-template ,组件就会把它的内容当做模板,而不会把它内容分发

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
<div id="app">
    <my-con inline-template>
        <div>
            {{message}}
        </div>
    </my-con>

</div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', {
        data: function () {
            return {
                message: "这是子组件内容"
            }
        },
        template: '<div class="child"></div>'
    });

    var app = new Vue({
        el: "#app",
        data: {
            message: "这是父组件内容"
        }
    });
</script>
</html>

需要注意的就是它的作用域.

最后结果是 “这是子组件内容”

动态组件 Vue 提供了一个 标签 用来动态加载不同组件 使用is 特性来选择不同组件

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
<div id="app">
    <component :is="currentView"></component>
</div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    var app = new Vue({
        el: "#app",
        data: {
            currentView: "comA"
        },
        components: {
            comA: {
                template: '<div>这是 comA </div>'
            },
            comB: {
                template: '<div>这是 comB </div>'
            },
            comC: {
                template: '<div>这是 comC </div>'
            }
        }
    });
</script>
</html>
  • 异步组件
<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
<div id="app">
    <my-con></my-con>

</div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    Vue.component('my-con', function (resolve, reject) {
        window.setTimeout(function () {
            resolve({
                template: '<div>这是异步加载的内容</div>'
            })
        }, 2000)
    });

    var app = new Vue({
        el: "#app"
    });
</script>
</html>
  • $nextTick

Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM,API 文档中官方示例如下:

new Vue({
  // ...
  methods: {
    // ...
    example: function () {
      // modify data
      this.message = 'changed'
      // DOM is not updated yet
      this.$nextTick(function () {
        // DOM is now updated
        // `this` is bound to the current instance
        this.doSomethingElse()
      })
    }
  }
  • X-Templates

在没有使用webpack的时候,组件的template属性会很长,效率低下

Vue 提倡的一种方式是 在标签使用 text/x-template类型 并指定一个id,将这个id赋值给template。

  • 手动挂载实例

我们现在创建的实例都是通过 new Vue() 形式创建的.在一些非常特殊的情况下,要去动态创建vue实例,vue 提供了 Vue.extend 和 $mount 两个方法

<!DOCTYPE html>
<html>
<head>
    <title>Vue</title>
</head>
<body>
<div id="app">

</div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">

    var MyComponent = Vue.extend({
        template: '<div>这是动态加载的内容</div>'
    })

    new MyComponent().$mount("#app")
</script>
</html>

页面中会打印出 这是动态加载的内容

除了这种写法外,还有两种

new MyComponent({
    el: "#app"
})
var component = new MyComponent().$mount();
    document.getElementById('app').appendChild(component.$el);

手动挂载组件是一种高端的用法 在业务代码中几乎用不到.

上面就是组件所有的基础内容.

评论

还没有任何评论,你来说两句吧