vue组件化

189 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。详情

组件化

vue系统提供了一种抽象,让我们可以使用独立可复用的组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树。

组件化能提高开发效率,方便重复使用,简化调试步骤,提升项目可维护性,便于多人协同开发

1、Vue组件之间的通信

image-20220224165917242.png

组件通信常用方式

  • props

    示例 props (父传子)

    // parent组件
    <HelloWorld msg="Welcome to Your Vue.js App"/>
    // child组件
    props: { msg: String }
    
  • $emit/ $on(vue3废弃$on)

    示例:自定义事件 (子传父)

    // child
    this.$emit('add', good)
    // parent
    <Child @add="cartAdd($event)"></Cart>
    
  • $children/$parent(vue3废弃$children)

    兄弟组件之间通信可通过共同祖辈搭桥,parentparent或root。

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

    父组件可以通过$children访问子组件实现父子通信。

    // parent
    this.$children[0].xx = 'xxx'
    // $children可以拿到父组件中自定义组件的实例集合
    

    注意:$children不能保证子元素顺序。

  • $root

    同理$parent可以作为桥梁通信

  • ref

    获取子节点引用

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

    能够实现祖先和后代之间传值,隔代传参(一般3代以上使用)

    // 祖先组件中
    provide() {
        return {
          foo: "foo",  // 传入foo值
          handel: this.someMsg, // 传入handel方法
        };
    },
    methods: {
      someMsg(e) {
        console.log(e); // 此处可拿到孙辈组件传来的值 1
      },
    }
    ​
    // 子孙辈组件:
    inject: ["foo", "handel"],
    mounted() {
        console.log(this.foo);
        this.handel(1);  // 触发传进来的handel函数并传值1
    },
    
  • $attrs/ $listeners(vue3废弃$listeners)

    $attrs识别非属性特性(非props特性)。即识别没有声明在props中的属性

    示例:父子组件

    // parent父组件
    <HelloWorld foo="foo"/>
    ​
    // child子组件:并未在props中声明foo
    <p>{{$attrs.foo}}</p>
    

    主要使用场景:属性的透传,创建高阶组件时这个属性非常有用。

    比如有一个通用组件A,但是需要在A的基础上做一些扩展,那么我们就开发一个组件B将A包起来做扩展组件使用。然后用在组件C中时需要给B传一些属性,但是这些属性可能是实际上B中使用的也可能是A中使用了。这时的属性透传就可以用$attrs。c父组件 -> b子组件 -> a孙组件

    // A中使用B组件,给C传参
    <B msg="lalala" @some-event="onSomeEvent"></B>
    methods:{
      onSomeEvent(e) {
         console.log(e) // 此处的e是从C组件中传来的值
      }
    }
    ​
    // B组件做C组件的扩展组件
    <C v-bind="$attrs" v-on="$listeners"></C>
    // v-bind可以讲属性展开,类似react中传值时用...展开符。此处就将b组件拿到的$attrs的属性全部挂在了c组件
    // $listeners是监听A组件传进来的事件,通过v-on将所有的事件就监听上// C组件识别参数
    <div @click="click">
      {{$attrs.msg}}
    </div>
    methods: {
      click() {
        // 点击时触发透传过来的事件并传值
        this.$emit('some-event', 'C中的值')
      }
    }
    
  • eventbus

    事件总线:在任意两个组件之间传值常用事件总线或vuex

    // 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))
        }
      }
    }
    // main.js
    Vue.prototype.$bus = new Bus()
    ​
    // child1
    this.$bus.$on('foo', handle)
    // child2
    this.$bus.$emit('foo')
    

    实践中通常⽤Vue代替Bus,因为Vue已经实现了onon和emit

  • vuex

    创建唯⼀的全局数据管理者store,通过它管理数据并通知组件状态变更。

应用场景

父子传值:props / emit/emit / parent / ref / $attrs

兄弟组件:parent/parent / root / eventbus / vuex

跨层级通信:eventbus / vuex / provide + inject

vue3变化

vue3中废弃了$children$listeners$on

  • $children

    $childreninstance 属性已从 Vue 3.0 中删除。如果需要访问子组件实例,建议使用refs

  • $listeners

    $listeners对象已在 Vue 3 中删除。事件监听器现在是用$attrs

    在 Vue 3 的虚拟 DOM 中,事件监听器现在只是属性,前缀on为 ,因此是对象的一部分$attrs,因此$listeners已被删除。

  • $on

    $on,$off$once在vue3中已被删除。

    注意:通过const Bus = new Vue()创建的eventBus的$on,$off$once的方法已经不能再用。但是我们自己封装的事件总线可以自行定义这些属性方法使用的。

    在vue3中我们可以自行定义事件总线使用这些方法,也可以使用例如mitttiny-emitter外部插件来实现。

具体详解:

v3-migration.vuejs.org/breaking-ch…

v3-migration.vuejs.org/breaking-ch…

v3-migration.vuejs.org/breaking-ch…

2、插槽

插槽主要就是内容分发。

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

匿名插槽

// comp1子组件在需要父组件自定义的地方使用slot标签
<div>
 <slot></slot>
</div>
​
// parent父组件中使用子组件,但是子组件的某些内容在父组件中自行定义
<comp>hello</comp>

具名插槽

将多个内容分发到子组件指定位置

// comp2
<div>
 <slot></slot>
 <slot name="content"></slot>
</div>
// parent
<Comp2>
 <!-- 默认插槽⽤default做参数 -->
 <template v-slot:default>具名插槽</template>
 <!-- 具名插槽⽤插槽名做参数 -->
 <template v-slot:content>内容...</template>
</Comp2>

作用域插槽

分发内容时,用到子组件中的数据

// comp3
<div>
 <slot :foo="foo"></slot>
</div>
// parent
<Comp3>
 <!-- 把v-slot的值指定为作⽤域上下⽂对象 -->
 <template v-slot:default="slotProps">
 来⾃⼦组件数据:{{slotProps.foo}}
 </template>
</Comp3>

3、vue3中的组件化

vue3快速开始

vue3新特性

  • composition-api组合式API

    可以提高更好的逻辑复用和代码组织。可以替换mixins、生命周期、计算属性、各种方法等。使用setup可以讲这些逻辑用很好的函数的方式去实现复用。

    <template>
      <h1 @click="msg = 'xx'">{{ msg }}</h1>
      <p>{{ data.num }}</p>
      <p>{{ data.doubleNum }}</p>
      
    </template>
    <script>
    import { reactive, computed, onMounted, onUnmounted, ref } from "vue";
    export default {
      setup() {
        // num相关逻辑
        const data = reactive({  // reactive创建一个响应式对象
          num: 1,
          doubleNum: computed(() => data.num * 2),  // 计算属性
        });
        let timer;
        onMounted(() => {  // 生命周期挂载事件
          timer = setInterval(() => {
            data.num++;
          }, 1000);
        });
        onUnmounted(() => {  // 生命周期卸载事件
          clearTimeout(timer);
        });
          
       // 使用dom元素
        const desc = ref(null); // desc在下面return导出,会在html中找到同名的ref元素赋值
        watch(
          () => data.num,
          (val, oldVal) => {
            // console.log(desc.value);
            const p = desc.value;
            p.textContent = "当前值" + val + ",之前值" + oldVal;
          }
        );
          
        // msg相关逻辑
        const msg = useMsg();
        return { data, msg, desc };
      },
    };
    // 还可以讲相关的数据操作这样封装后在setup中使用
    function useMsg() {
      let msg = ref("msg");  // ref生成一个响应式的单值
      // 其他处理...
      return msg
    }
    </script>
    
  • 全局API

    vue3中创建vue的方法改为createApp

    // vue3 - main.js
    import { createApp } from 'vue'
    import App from './App.vue'createApp(App).mount('#app')
    ​
    // vue2 - main.js
    import Vue from 'vue'
    import App from './App.vue'
    Vue.config.productionTip = falsenew Vue({
      render: h => h(App),
    }).$mount('#app')
    ​
    

其他新特性

\