最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue组件化最佳实践

    正文概述 掘金(捡代码的小女孩)   2021-03-06   562

    写在前面

    何为组件化?

    通常一个应用会以一棵嵌套的组件树的形式来组织:例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。如下图所示: vue组件化最佳实践

    1.组件常用通信方式

    1.1 props

    // 父组件
    <Child1 msg="some message from parent"></Child1>
    
    // Child1组件
    <template>
        <div class="border">
            我是子组件1
            <div>{{msg}}</div>
        </div>
    </template>
    
    <script>
    export default {
      props: {
        msg: {
          type: String,
          default: ''
        }
      },
      data () {
        return {}
      }
    }
    </script>
    

    结果:vue组件化最佳实践

    1.2 event

    // 父组件
    <template>
        <div>
            <h2>组件通信</h2>
            <!-- props 父传子 -->
            <Child1 msg="some message from parent"></Child1>
            <!-- 自定义事件 event 子传父 -->
            <Child2 @clickSon="getMsg"></Child2>
            <p>Child2传过来的值是: {{child2Info}}</p>
        </div>
    </template>
    
    <script>
    import Child1 from './Child1'
    import Child2 from './Child2'
    
    export default {
      components: {Child1, Child2},
      data () {
        return {
          child2Info: ''
        }
      },
      methods: {
        getMsg (info) {
          this.child2Info = info
        }
      }
    }
    </script>
    
    
    // Child2组件
    <template>
        <div @click="$emit('clickSon', 'msg from child2')" class="border">
            我是子组件2
        </div>
    </template>
    
    <script>
    export default {
      data () {
        return {}
      },
      mounted () {
        this.$bus.$on('send', (msg) => {
          console.log('Child1 点击了, 信息是:' + msg)
        })
      }
    }
    </script>
    

    结果: vue组件化最佳实践

    1.3 Bus

    任意两个组件之间传值可以用事件总线的方式。事件总线其实利用的就是设计模式中观察者模式。其核心实现方式如下:

    class Bus {
      constructor () {
        this.callbacks = {}
      }
    
      $on (name, fn) {
        this.callbacks[name] = this.callbacks[name] || []
        this.callbacks[name].push(fn)
      }
    
      $emit (name, args) {
        if (this.callbacks[name]) {
          this.callbacks[name].forEach(cb => cb(args))
        }
      }
    }
    
    export default Bus
    

    使用方式:

    1.先在main.js中注册$bus

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import Bus from './bus'
    Vue.prototype.$bus = new Bus()
    // 实际上Vue已经实现了事件总线,我们使用的时候不用自定义Bus,直接使用如下方式引入即可
    // Vue.prototype.$bus = new Vue()
    
    Vue.config.productionTip = false
    
    new Vue({
      el: '#app',
      router,
      components: { App },
      template: '<App/>'
    })
    

    2.在需要接受值的地方注册监听事件,这里已 Child1 与 Child2 之间的传值为例:

    Child2

    <template>
        <div @click="$emit('clickSon', 'msg from child2')" class="border">
            我是子组件2
        </div>
    </template>
    
    <script>
    export default {
      data () {
        return {}
      },
      mounted () {
      	// 组件挂载时即对send事件进行监听,监听到后打印信息
        this.$bus.$on('send', (msg) => {
          console.log('Child1 点击了, 信息是:' + msg)
        })
      }
    }
    </script>
    
    

    Child1

    <template>
        <div class="border">
            我是子组件1
            <button @click="$bus.$emit('send', '子组件1通过bus传来的值')">触发事件总线</button>
        </div>
    </template>
    

    结果: 点击Child1的button后,Child2打印:'Child1 点击了, 信息是: 子组件1通过bus传来的值'

    1.4 vuex

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。这里就不做举例啦~下期做个完成剖析给大家。 想学习的移步官网:vuex中文官方网站

    2.边界情况

    大多数情况下,并不推荐使用边界情况,而是使用更优的替代方案去代替边界情况。但是了解边界情况可以在理解源码封装组件化仓库中得到很大的便利。正如官网所说:在绝大多数情况下,我们最好不要触达另一个组件实例内部或手动操作 DOM 元素。不过也确实在一些情况下做这些事情是合适的。

    2.1 $root——访问根实例

    在每个 new Vue 实例的子组件中,其根实例可以通过 $root property 进行访问。

    // Vue 根实例
    new Vue({
      data: {
        foo: 1
      },
      computed: {
        bar: function () { /* ... */ }
      },
      methods: {
        baz: function () { /* ... */ }
      }
    })
    
    // 所有的子组件都可以将这个实例作为一个全局 store 来访问或使用:
    // 获取根组件的数据
    this.$root.foo
    // 写入根组件的数据
    this.$root.foo = 2
    // 访问根组件的计算属性
    this.$root.bar
    // 调用根组件的方法
    this.$root.baz()
    

    2.2 $parent——访问父级组件实例

    兄弟组件之间通信可通过共同祖辈搭桥

    // brother1
    this.$parent.$on('foo', handle) 
    // brother2
    this.$parent.$emit('foo')
    

    2.3 $children——访问所有子组件实例或子元素

    父组件可以通过children访问子组件实现父子通信。注意:children访问子组件实现父子通信。注意:children访问子组件实现父子通信。注意:children不能保证子元素顺序。

      // parent 
      this.$children[0].xx = 'xxx'
    

    2.4 $refs——访问子组件实例或子元素

    获取子节点引用

    // parent
    <HelloWorld ref="hw"/>
    
    mounted() { 
    	this.$refs.hw.xx = 'xxx'
    }
    

    2.5 provide/inject——依赖注入

    能够实现祖先和后代之间传值

    // 祖先元素
    provide() {
    	return {foo: 'foo'}
    }
    
    // 后代
    <template>
        <div class="border">
            <p>{{foo}}</p>
        </div>
    </template>
    
    <script>
    export default {
      inject: ['foo']
    }
    </script>
    // 也可以在后代元素中设置别名,以防命名冲突
     inject: {
        bar: {
          from: 'foo'
        }
     }
    访问的时候: <p>{{bar}}</p>
    

    3.非prop特性

    用于隔代透传,只能传一代。

    3.1 $attrs

    包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。当一个组件没有 声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外),并且可以通过 v- bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

    // 祖先组件
     <Child2 msg="some message from parent"></Child2>
     
    // Child2组件
    <template>
        <div>
          <Grandson v-bind="$attrs"/>
        </div>
    </template>
    
    <script>
    import Grandson from './Grandson'
    export default {
      components: {Grandson},
      data () {
        return {}
      }
    }
    </script>
    
    // 孙子组件
    <template>
        <div class="border">
            <div>孙子组件</div>
            <div>父辈通过$attr透传过来的信息:{{msg}}</div>
        </div>
    </template>
    
    <script>
    export default {
      props: {
        msg: {
          type: String
        }
      }
    }
    </script>
    
    

    结果:vue组件化最佳实践

    3.2 $listeners

    透传事件

    // 祖先组件
     <Child2 @clickSon="getMsg"></Child2>
      methods: {
       getMsg (info) {
         console.log(info)
       }
     }
     
    // Child2组件
    <template>
        <div>
          <Grandson v-on="$listeners"/>
        </div>
    </template>
    
    <script>
    import Grandson from './Grandson'
    export default {
      components: {Grandson},
      data () {
        return {}
      }
    }
    </script>
    
    // 孙子组件
    <template>
        <div class="border" @click="$emit('clickSon', 'msg from grandSon')">
            <div>孙子组件</div>
        </div>
    </template>
    

    4.插槽

    插槽语法是Vue实现的内容分发 API,用于复合组件开发。在通用组件库开发中有大量应用。

    4.1 匿名插槽

    // Layout
    <div class="body">
      <slot></slot>
    </div>
    
    // parent
    <Layout>
    	<!-- 匿名插槽 -->
    	<template>种一棵树最好是时间是十年前,而后是现在!</template>
    </Layout>
    

    4.2 具名插槽

    // Layout
    <div class="header">
      <slot name="header"></slot>
    </div>
    
    // parent
    <Layout>
    	<template v-slot:header>欢迎来到捡代码的小女孩的vue世界</template>
    </Layout>
    

    4.3 作用域插槽

    // Layout
    <div class="footer">
      <slot name="footer" :childValue="footerContent"></slot>
    </div>
    
    // parent
    <Layout>
    	<template v-slot:footer="value">{{value.childValue}}</template>
    	<!-- 对象解构 -->
        <!-- <template v-slot:footer="{childValue}">{{childValue}}</template> -->
    </Layout>
    

    完整代码如下:

    父组件

    <template>
      <div>
        <h2>插槽</h2>
        <!-- 插槽 -->
        <Layout>
          <!-- 具名插槽 -->
          <template v-slot:header>欢迎来到捡代码的小女孩的vue世界</template>
          <!-- 匿名插槽 -->
          <template>种一棵树最好是时间是十年前,而后是现在!</template>
          <!-- 作用域插槽 -->
          <template v-slot:footer="value">{{value.childValue}}</template>
           <!-- 对象解构 -->
           <!-- <template v-slot:footer="{childValue}">{{childValue}}</template> -->
        </Layout>
      </div>
    </template>
    
    <script>
    import Layout from './Layout.vue'
    export default {
      name: 'Slot',
      components: {
        Layout
      }
    }
    </script>
    

    Layout组件

    <template>
      <div>
        <div class="header">
          <slot name="header"></slot>
        </div>
        <div class="body">
          <slot></slot>
        </div>
        <div class="footer">
          <slot name="footer" :childValue="footerContent"></slot>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          footerContent: '学习的敌人是自己的满足,要认真学习一点东西,必须从不自满开始'
        }
      }
    }
    </script>
    
    <style scoped>
    .header {
      background-color: rgb(252, 175, 175);
    }
    .body {
      display: flex;
      background-color: rgb(144, 250, 134);
      min-height: 100px;
      align-items: center;
      justify-content: center;
    }
    .footer {
      background-color: rgb(114, 116, 255);
    }
    </style>
    
    

    效果如下: vue组件化最佳实践

    这些就是组件化开发的必备知识啦 (〃'▽'〃) get之后和我一起手写小组件吖~


    The end~


    起源地下载网 » vue组件化最佳实践

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元