zoukankan      html  css  js  c++  java
  • 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>
    
  • 相关阅读:
    自定义UITableViewCell
    使用NSOperation实现异步下载
    iOS中判断一个文件夹是否存在
    iphone下载进度条,显示下载字节数与百分比
    NSURLConnection下载文件并显示进度(HEAD)
    iphone 模拟器 显示 隐藏的资源库
    iOS使用NSURLConnection发送同步和异步HTTP Request
    NSFileManager和NSFileHandle(附:获取文件大小 )
    xcode4.2设立Created by &MyCompanyName_
    iphone开发之轻松搞定原生socket 编程,阻塞与非阻塞,收发自如
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/12401669.html
Copyright © 2011-2022 走看看