Loading

Vue.js笔记(二) 组件

组件基础

示例

<body>
    <div id="app">
        <button-counter></button-counter>
        <button-counter></button-counter>
    </div>
    <script type="text/javascript">
        Vue.component('button-counter',{
            data() {
                return {
                    count:0
                }
            },
            template:'<button v-on:click="count++">You clicked me {{ count }} times.</button>',
        })
        var vm = new Vue({
            el: '#app',

        });
    </script>
</body>

从这个简单的例子看来

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

1581593575875

复用

你可以将组件进行任意次数的复用。

注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

因此,data必须是一个函数

当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
  return {
    count: 0
  }
}

props属性

props属性可以用来向组件传递数据

向刚刚的组件中加入props属性

        Vue.component('button-counter',{
            props:['title'],
            data() {
                return {
                    count:0
                }
            },
            template:'<button v-on:click="count++">{{title}}: You clicked me {{ count }} times.</button>',
        })

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:

        <button-counter title="button1"></button-counter>
        <button-counter title="button2"></button-counter>

1581593979441

看起来当组件变得越来越复杂的时候,例如博文不只需要标题和内容,还需要发布日期、评论等等。为每个相关的信息定义一个 prop 会变得很麻烦:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
  v-bind:content="post.content"
  v-bind:publishedAt="post.publishedAt"
  v-bind:comments="post.comments"
></blog-post>

所以是时候重构一下这个 <blog-post> 组件了,让它变成接受一个单独的 post prop:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>


Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})

单个根元素

every component must have a single root element (每个组件必须只有一个根元素)

模板的内容包裹在一个父元素内

$emit

https://cn.vuejs.org/v2/api/#vm-emit

<body>
    <div id="app">
        <button-counter title="button1" @click-now="clickNow"></button-counter>
        <button-counter title="button2" @click-now="clickNow"></button-counter>
    </div>
    <script type="text/javascript">
        Vue.component('button-counter',{
            props:['title'],
            data() {
                return {
                    count:0
                }
            },
            template:'<button v-on:click="clickFun">{{title}}: You clicked me {{ count }} times.</button>',
            methods: {
                clickFun:function(){
                    this.count++;
                    this.$emit('click-now',this.title,this.count);      
                }
            },
        })
        var vm = new Vue({
            el: '#app',
            methods: {
                clickNow:function(title,count){
                    console.log(title+' has been clicked '+count+' times.');
                }
            },
        });
    </script>
</body>

1581595131460

逻辑为:

模板内的v-on:click="clickFun"绑定到模板method clickFun

clickFun内通过$emit函数调用了click-now:,并且传入两个参数

在外部使用模板时候,通过@click-now="clickNow"click-nowclickNow绑定,进而调用vm中的方法

插槽

在模板内用<slot></slot>包裹

<alert-box>
  Something bad happened.
</alert-box>

可能会渲染出这样的东西:

1581595426311

幸好,Vue 自定义的 <slot> 元素让这变得非常简单:

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

注意事项总结

  • 自定义组件需要有一个root element
  • 父子组件的data是无法共享
  • 组件可以有data,methods,computed....,但是data 必须是一个函数
  • new Vue其实就是注册一个root组件

组件通信

父传子

(1) 示例

父传子通过props属性

<body>
    <div id="app">
        <blog-post title="journey" author="Tony" :likes="likes"></blog-post>
    </div>
    <script type="text/javascript">

        Vue.component('blog-post', {
            props: {
                title: String,
                likes: Number,
                isPublished: {
                    type: Boolean,
                    default: true,
                },
                author: {
                    type: String,
                    required: true
                }
            },
            template: `
            <div>
                <p>{{title}} by {{author}} ispublish:{{isPublished}} likes{{likes}}</p>
            </div>`,
        })

        var vm = new Vue({
            el: '#app',
            data() {
                return {
                    likes:10
                }
            },
            methods: {
            },
        });
    </script>
</body>

</html>

注意,在父组件root里面有个data为likes,如果要简单的传字符串用

title=xxx, author=xxx就可以了

但是如果要传类似数据、布尔值,要用动态绑定传法

:likes="likes"

1582536191812

(2) 属性验证

有时候写组件和用组件的人不一样 可能会出错

比如 我们在用上面组件的时候

<blog-post title="journey" author="Tony" :likes="likes" is-published="false"></blog-post>

这是错误的,但是很难发现

好在,我们在定义组件的时候作了属性验证

isPublished: {
    type: Boolean,
    default: true,
},

1582536488795

浏览器会报错,因此我们需要修改为

<blog-post title="journey" author="Tony" :likes="likes" :is-published="false"></blog-post>
        <!-- 动态绑定is-published -->

假如我们简单的定义属性

props=['isPublished'],那就不会检测出来

子传父

子传父通过事件向上传

<body>
    <div id="app">
        <child @myevent="handleEvent"></child>
    </div>
    <script type="text/javascript">

        Vue.component('child', {
            template: `
            <div>
                <p>子组件</p>
                <button @click="payMoney">点击</button>
            </div>`,
            methods:{
                payMoney(){
                    this.$emit("myevent",100)
                }
            }
        })

        var vm = new Vue({
            el: '#app',
            data() {
                return {
                    likes:10
                }
            },
            methods: {
                handleEvent(a){
                    console.log("父组件收到"+a+"元");
                    
                }
            },
        });
    </script>
</body>

</html>
  • 在子组件的模板里通过@绑定事件给子组件定义的方法

  • 在子组件定义的方法中用this.$emit("funtion",...)进行事件调用

    以上述为例

    this.$emit("myevent",100)就是调用myevent方法,传参数100

  • 在调用子组件的时候对myevent进行绑定

    <child @myevent="handleEvent"></child>

    这里是动态绑定父组件的handleEvenet方法

1582537252148

注意:

<child @myevent="handleEvent"></child>默认传参数

<child @myevent="handleEvent()"></child>默认参数为空,如果要加括号,可以用

<child @myevent="handleEvent($event)"></child>来传参数

ref

我们分布给原生组件和自定义组件加上ref属性,并通过父组件的this.$refs进行调用

<body>
    <div id="app">
        <input type="text" ref="mytext"/>
        <child ref="mychild"></child>
        <button @click="handleClick">点击</button>
    </div>
    <script type="text/javascript">

        Vue.component("child",{
            template:`
            <div>
                我是一个用户
            </div>`,
            data() {
                return {
                    name:"子组件信息"
                }
            },
        })

        var vm = new Vue({
            el: '#app',
            methods: {
                handleClick(){
                    console.log(this.$refs.mytext);
                    console.log(this.$refs.mychild);
                }
            },
        });
    </script>
</body>

1582719862326

点击按钮发现原生组件获取到的是dom节点自定义组件获取到的是组件的详细信息

1582720002998

所以我们修改一下log的信息为

                handleClick(){
                    console.log(this.$refs.mytext.value);
                    console.log(this.$refs.mychild.name);
                }

1582720039925

发现这是一种降维打击的方法,可以获取到所有的信息!

事件总线/非父子通信

<body>
    <div id="app">
        <author></author>
        <user></user>
    </div>
    <script type="text/javascript">

        var bus = new Vue();

        Vue.component("author",{
            template:`
            <div>
                <input type="text" ref="mytext"/>
                <button @click="handleClick">发布消息</button>
            </div>`,
            methods:{
                handleClick(){
                    bus.$emit("message",this.$refs.mytext.value)
                }
            }
        })

        Vue.component("user",{
            template:`
            <div>
                我是一个用户
            </div>`,
            mounted() {
                console.log("mounted钩子");
                bus.$on("message",(data)=>{
                    console.log('收到消息',data);
                })
            },
        })

        var vm = new Vue({
            el: '#app',
            data() {
                return {
                    likes:10
                }
            },
        });
    </script>
</body>

两个组件之间的通信可以通过事件总线进行

事件总线就是上述代码中的var bus = new Vue();

发送方通过.$emit函数进行调用

接收方通过.$on可以进行监听

bus.$emit(标识符,参数...)

bus.$on(标识符,函数)

动态组件

  • <component> 元素,动态地绑定多个组件到它的 is 属性
  • <keep-alive> 保留状态,避免重新渲
<body>
    <div id="app">
        <component :is="who"></component>
        <footer>
            <ul>
                <li><a @click="who='home'">首页</a></li>
                <li><a @click="who='list'">列表</a></li>
                <li><a @click="who='shopcar'">购物车</a></li>
            </ul>
        </footer>
    </div>
    <script type="text/javascript">

        var vm = new Vue({
            el: '#app',
            data: {
                who: 'home'
            },
            components: {
                "home": {
                    template: `<div>home<input type="text"/></div>`
                },
                "list": {
                    template: `<div>list</div>`
                },
                "shopcar": {
                    template: `<div>shopcar</div>`
                }
            }
        });
    </script>
</body>

但是,还有一点问题,在home界面的输入框输入后,切换页面回来发现没了,怎么保留?

1582720622938

只要用keep-alive包裹就可以

        <keep-alive>
            <component :is="who"></component>
        </keep-alive>
posted @ 2020-03-03 12:56  cpaulyz  阅读(554)  评论(0编辑  收藏  举报