美文网首页
组件化开发

组件化开发

作者: zhchhhemmm | 来源:发表于2020-03-05 17:51 被阅读0次

    组件化开发思想

    • 标准
    • 分治
    • 重用
    • 组合
      组件开发规范:Web Components
      通过封装好功能的定制元素(自定义标签)来解决问题
      Vue部分实现来上述规范

    组件的注册方式

    Vue.component(组件名称,{
           data:组件数据,
           template:组件模板内容
      })
    
    一个具体组件:
          Vue.component('button-counter',{
                data:function(){
                    return{
                        count:0
                    }
                },
                template:'<button @click="count++" >按钮被点击{{count}}次</button>'
            })
    
    组件是可以重用的,重用的每个组件是相互独立的,他们之间的数据是相互不影响的
    

    注意事项:
    1 自定义组件的data必须是一个函数,而Vue实例的data可以是个对象
    2 组件内部的模板根元素只应该有一个
    3 组件模板内容可以是模板字符串
    eg:

    template:`
                    <button @click='count++'>
                        按钮被点击{{count}}次
                    </button>
                `
    

    4 组件的命名方式有短横线方式和驼峰命名方式
    如果组件要在别的组件中使用,可以直接按照命名的写法用
    如果要在html中直接使用,在命名时无论用的哪种方法,使用时都得时短横线方式

    • 局部组件
    //注册一个Vue实例中的局部组件
            var ComponentA = {
                data:function(){
                    return {
    
                    }
                },
                template:``
            }
            var vm = new Vue({
                el:'#box',
                components:{
                    'component-a':ComponentA
                }
            })
    

    组件之间数据的交互

    • 父组件向子组件传值
      1 组件内部通过props接收传递过来的值
      2 父组件将值通过v-bind绑定到属性上,用以传给子组件


      image.png

      props属性名规则:
      1 在props中使用驼峰形式,在模板中需要使用短横线模式
      2 字符串形式的模版中没有这个限制
      props属性值类型:
      - 字符串
      - 数值
      - 布尔
      - 数组
      - 对象
      在传递数字的时候,如果用了v-bind,传过去的是数值,否则就是string
      传递布尔型的值时,属性可以不加引号,但是推荐加上,而且也需要v-bind绑定

    • 子组件向父组件传值
      props传递数据的原则:单向数据流,只能父传子,不能子传父
      -- 子组件通过自定义事件的方式向父组件传值
      举个例子:
      业务场景:点击子组件的按钮,增大父组件中的值的大小(子组件向父组件传递信息,没有传值)
    <body>
        <div id="box">
            <div :style='{fontSize:fontSize+"px"}'>{{msg}}</div>
            <button-son @enlarge-text='handle'></button-son>
        </div>
            <script>
            Vue.component('button-son',{
                template:`
                    <button @click='$emit("enlarge-text")'>扩大父组件的字体大小</button>
                `
            })
            var vm = new Vue({
                el:'#box',
                data() {
                    return {
                        msg:'Hello world',
                        fontSize:13
                    }
                },
                methods: {
                    handle:function(){
                        //扩大字体大小
                        this.fontSize ++;
                        console.log(this.fontSize);
                        
                    }
                },
            })
            </script>
        
    </body>
    

    -- 传值:在emit时传递的第一个参数是自定义事件名,而第二个参数则可以用来传递值,在父组件中用$event来接收值

    子组件:
    <button @click='$emit("enlarge-text",2)'>扩大父组件的字体大小</button>
    父组件:
    <button-son @enlarge-text='handle($event)'></button-son>
    
    • 非父子组件之间传值:
    -- 单独的事件中心管理组件间的通信
    var eventBus = new Vue();
    -- 监听事件与销毁事件
    eventBus.$on('add-todo',addtodo);
    eventBus.$off('add-todo')
    -- 触发事件
    eventBus.$emit('add-todo',id)
    
    image.png

    实例:在一个父组件中有两个子组件,都是button和data,点击第一个子组件的button,第二个子组件的data改变,点击第二个子组件的button,第一个子组件的data改变。

    <body>
        <div id="box">
            <p>father</p>
            <button v-on:click='handle'>销毁事件</button>
            <button-a></button-a>
            <button-b></button-b>
        </div>
        <script>
            //事件中心
            var eventBus = new Vue()
    
            Vue.component('button-a',{
                data:function(){
                    return {
                        msg:0
                    }
                },
                template:`
                <div>   
                    <div>A:{{msg}}</div>
                    <button @click='handle'>给老弟加2</button>
                </div> 
                `,
                methods: {
                    handle:function(){
                        eventBus.$emit('b-event',2)
                    }
                },
                mounted() {
                    //监听事件
                    eventBus.$on('a-event',(val)=>{
                        this.msg += val
                    })
                },
            })
            Vue.component('button-b',{
                data:function(){
                    return {
                        msg:0
                    }
                },
                template:`
                <div>
                    <div>B:{{msg}}</div>
                    <button @click='handle'>给老哥加3</button>
                </div>
                `,
                methods: {
                    handle:function(){
                        //触发兄弟组件的事件
                        eventBus.$emit('a-event',3)
                    }
                },
                mounted() {
                    //监听事件
                    eventBus.$on('b-event',(val)=>{
                        this.msg += val
                    })
                },
            })
            var vm = new Vue({
                el:'#box',
                data:{
    
                },
                methods: {
                    handle:function(){
                        eventBus.$off('a-event')
                        eventBus.$off('b-event')
                    }
                },
            })
        </script>
    </body>
    

    组件插槽的用法

    在之前的自定义组件中,我们在使用的时候都是没有具体值的,比如:

    <a-button><a-button>
    

    可如果我们想要这样使用呢?:

    <a-button>click me</a-button>
    

    这时候,我们要使用插槽
    我们在模板字符串中定义组件的dom时,可以预留<slot></slot>,
    在使用组件时输入的值就在slot中
    eg:

            <my-com>I don't know...</my-com>
    ...
            Vue.component('my-com',{
                template:`
                <div>   
                    <strong>Error : </strong>
                    <slot></slot>
                </div> 
                `
            })
    

    -- 具名插槽
    定义方法:

    template:`
              <div>   
                  <div>
                  <slot name='header'></slot>
                  </div>
                  <strong>Error : </strong>
                  <div><slot></slot></div>
                  <div>
                  <slot name='footer'></slot>    
                  </div>
              </div> 
              `
    

    使用:

    <my-com>
                <div slot="header">标题信息</div>
                I don't know...
                <div slot="footer">底部信息</div>
     </my-com>
    

    给了name的插槽对号入座,没有给name的,放入默认插槽中

    -- 作用域插槽:
    应用场景:父组件可以对子组件的内容进行加工处理
    例子:子组件是个水果列表,但是高亮内容和文本内容应该是动态的

            <fruit-list v-bind:list='list'>
                <template slot-scope="slotProps">
                    <strong v-if='slotProps.info.id==2' class="orange">{{slotProps.info.name}}</strong>
                    <span v-else>{{slotProps.info.name}}</span>
                </template>
            </fruit-list>
    //组件定义
            Vue.component('fruit-list',{
                props:['list'],
                
                template:`
                    <div>
                        <li :key='item.id' v-for='item in list'>
                            <slot :info='item'>{{item.name}}</slot>    
                        </li>                        
                    </div>
                `
            })
    

    Vue组件调试工具的用法

    组件在浏览器中会被渲染成原始的DOM,我们在浏览器的检查工具上只能看到已经渲染好了的DOM,不便于调试
    官方调试工具,dev-tools,直接下载安装chrome插件即可
    https://github.com/vuejs/vue-devtools

    基于组件方式实现业务功能

    一个小例子:

    <body>
        <script>
            /*
            1 组件化划分「
                        1.标题组件(展示文本)
                        2.列表组件(列表展示、商品数量变更、商品删除)
                        3.结算组件(计算商品总额)
                        」
            
            */
        </script>
        <div id="box">
            <div id="containner">
                <my-cart></my-cart>
            </div>
        </div>
        <script>
            var cartTitle = {
                props:['uname'],
                template:`
                <div class='title'>{{uname}}的商品</div>
                `
            }
            var list = {
                props:['list'],
                template:`
                    <div>
                        <div :key=item.id  v-for='item in list'>
                            <img :src="item.img" alt="">
                            <div>{{item.name}}</div>
                            <div class='change'>
                                <a class='a' href="" @click.prevent='sub(item.id)'>-</a>    
                                <input type="text" class='num' :value='item.num' @blur='changeNum(item.id,$event)'/>    
                                <a class='a' href="" @click.prevent='add(item.id)'>+</a>    
                                <button class='delete' @click='del(item.id)'>✖️</button>
                            </div>
                            
                        </div>    
                    </div>
                `,
                methods: {
                    changeNum:function(id,event){
                        this.$emit('numChange',{id:id,num:parseInt( event.target.value),type:'change'})
                        //console.log(id,parseInt( event.target.value));
                        //新的值是event.target.value
                    },
                    del:function(id){
                        this.$emit('cart-del',id)
                    },
                    sub:function(id){
                        this.$emit('numChange',{
                            id:id,
                            type:'sub'
                        })
                    },
                    add:function(id){
                        this.$emit('numChange',{
                            id:id,
                            type:'add'
                        })
                    }
                },
            }
            var totol = {
                props:['list'],
                template:`
                <div class='totol'>
                    <span>{{result}}</span>    
                    <button>结算</button>
                </div>
                `,
                computed: {
                    result:function(){
                        var totolNum = 0;
                    for(var i = 0;i<this.list.length;i++){
                        totolNum += this.list[i].num * this.list[i].price;                   
                    }
                    // console.log(this.list.length);
                    // console.log(totolNum);
                    
                    return totolNum;
                    }
                }
            }
            Vue.component('my-cart',{
                data:function(){
                    return{
                        list:[
                        {id:1,name:'tcl',img:'',num:1,price:100},
                        {id:2,name:'tl',img:'',num:1,price:1100},
                        {id:3,name:'tfel',img:'',num:1,price:1200},
                        {id:4,name:'gdgmkll',img:'',num:1,price:1400},
                        {id:5,name:'fghl',img:'',num:2,price:1900}
                    ],
                    uname:'sam'
                    }
                },
                template:`
                    <div class='cart'>
                        <cart-title :uname='uname'></cart-title>    
                        <cart-list :list='list' @numChange='changeNum($event)' @cart-del='delCart($event)'></cart-list>
                        <cart-totol :list='list'></cart-totol>
                    </div>
                `,
                components:{
                    'cart-title':cartTitle,
                    'cart-list':list,
                    'cart-totol':totol
                },
                methods:{
                    delCart:function(id){
                        console.log(id);
                        //根据ID删除list中对应的数据
                        //1 根据id找到需要删除的数据的索引
                        var index = this.list.findIndex(item=>{
                            return item.id == id
                        })
                        //2 根据索引删除对应的数据
                        this.list.splice(index,1)
                    },
                    changeNum:function(obj){
                        //根据子组件传递来的数据,更新list的数据
                        // for(var i = 0; i< this.list.length;i++){
                        //     if(this.list[i].id == obj.id){
                        //         this.list[i].num = obj.num
                        //     }
                        // }
                        if(obj.type == 'change'){
                           this.list.some(item=>{
                            if(item.id == obj.id){
                             item.num = obj.num
                             return true
                            }
                        })
                        //some这个API用起来遍历,简洁了很多
                        }else if(obj.type == 'sub'){
                            this.list.some(item=>{
                                if(item.id == obj.id&&item.num!=1){
                                    item.num -= 1;
                                    return true
                                }
                            })
                        }
                        else{
                            this.list.some(item=>{
                                if(item.id == obj.id){
                                    item.num += 1
                                    return true
                                }
                            })
    
                        }
                        
                    }
                }
            })
            var vm = new Vue({
                el:'#box',
                data:{
                    
                },
    
            })
        </script>
    </body>
    

    相关文章

      网友评论

          本文标题:组件化开发

          本文链接:https://www.haomeiwen.com/subject/nvqfkhtx.html