【大白话】结合官方文档说Vue之组件化开发

712 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

前言

Vue和React属于当下主流框架,初学者(包括我)在选择的时候可能会犯难,我给个个人建议:初学选啥都一样,看个人爱好,想UI和JS一起写的推荐React,想数据渲染简单一点的选择React,两者在编码上存在 互相借鉴 ,都推荐使用Hooks编码,原理也大差不差,会其中一个,另一个在花个一周时间可以很快上手,相当于买一送一,很实惠了。

本系列主要介绍以下模块:

  • 1.基础模版语法指令
  • 2.比较常用的特性
  • 3.组件化开发
  • 4.前后端的一些交互功能
  • 5.一些路由知识

组件化的开发

全局注册和局部注册

组件基础-官方文档 ,作为Vue最强大的功能之一,学习它着实用了我不少时间。我理解的组件为:类似于页面中导航栏,回到顶部的模块功能,它把HTML内容结构,CSS外观样式,JS动画特效,封装在一起成为的一个功能,就叫组件。这样在开发项目时,哪需要直接引入即可。

下面看个 全局组件 的基本样例模版:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>
</head>
<body>
<div id="app">
<!-- 2. 调用组件:【注意】这里的组件可任意次复用,也就是说每个组件相互独立互不影响的 -->
    <mynav></mynav>
    <mynav></mynav>
    <mynav></mynav>
</div>

<script>
    // 1. 通过vue类注册组件,“mynav”为自定义标签名
    Vue.component("mynav", {
     // 模版必须是单个根元素
        template:`<div>
                    <button @click="sub">-</button>
                    <input type="text" v-model="num">
                    <button @click="add">+</button>
                   </div>`,
        data(){  // 组件里面的变量数据,【注意】data返回的必须是一个函数,正因为这条规则,上述的组件才相互独立
            return {
                num: 0
            }
        },
        methods:{ // 组件里面的方法
            sub(){
                this.num--;
            },
            add(){
                this.num++;
            }
        }
    });
     // 创建根实例
    var vm = new Vue({
        el:'#app',
        data:{},
    })
</script>
</body>
</html>

为了能让Vue识别和在模版中使用组件,所以组件分为全局和局部,全局通过Vue.component注册,局部通过new Vue注册,这里面有很多小细节需要注意,比如上述的全局组件(data返回值,组件模版要求,甚至组件名命名使用的限制),下面再说 局部注册

  <div id="app">
      <my-component></my-component>
  </div>

<script>
    // 定义组件的模板
    var Child = {
      template: '<div>局部注册</div>'
    }
    new Vue({
      //局部注册组件  
      components: {
        // <my-component> 将只在父模板可用  一定要在实例上注册了才能在html文件中使用
        'my-component': Child
      }
    })
 </script>

局部注册组件只能在当前注册它的vue实例中才能使用,不过值得注意的是,局部注册的组件在其子组件中不可用 ,怎么理解这句话,就是说如果你定义了好几个局部组件,组件间是不通的,像局部函数间不能直接访问一样,要通的话得换个写法,如

var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
    'component-a': ComponentA
  },
  // ...
}

各不相同的组件中有着父子,兄弟之类关系,这使得在数据交互方面得分门别类,下面将说道父组件,子组件,兄弟组件俩俩之间的传值。

vue2 组件传值

1.父子传值 props

父组件引入子组件后,在data中定义所传参数,在子组件标签直接传递,子组件通过props接收即可

 <!-- 父组件 -->
<div class="hello">
   <h1>父组件</h1>
   <sonVue :msg="msg"></sonVue>
</div>

import sonVue from "./son.vue";
export default {
  data () {
    return {
      msg: 11
    };
  },
  components: {
    sonVue
  }
};

 <!-- 子组件 -->
{{ msg }}

export default {
   // 1.以数组方式接受
  // props: ["msg"],
    
   // 2.以对象形式接收
  props: {
    msg: {
      typeof: Number,
      default: 3
    }
  }
}

2.子父传值:$emit('事件名','数据')

在传值组件定义个自定义事件名称,接收组件通过@接收在触发即可

//传值组件
<button @click="sayLove">emit子向父传值</button>
  
methods: {
  sayLove () {
    this.$emit('sayLove', "生日快乐")
  }
}

//接收组件
<sonVue :msg="msg" @sayLove="getLove"></sonVue>
    
getLove (e) {
  console.log(e);
}

3.兄弟传值:在Vue原型上再定义个新实例,通过emit和on传递

在这里插入图片描述

//1.新定义个实例(好几种方法)
// 方法一
// 抽离成一个单独的 js 文件 Bus.js ,然后在需要的地方引入
import Vue from "vue"
export default new Vue()

// 方法二 在main.js直接挂载到全局
Vue.prototype.$bus = new Vue()

// 方法三 在main.js注入到 Vue 根对象上
import Vue from "vue"
new Vue({
    el:"#app",
    data:{
        Bus: new Vue()
    }
}).$mount('#app')

//2.在需要传值的组件上通过$emit事件传参
this.$bus.$emit('sayEat', "吃饭")

//3.接收组件,通过on方法接收自定义事件
created () {
    this.$bus.$on('sayEat', this.getSonLove)
  },
  methods: {
    getSonLove (ee) {
      console.log(ee);
    }
  }

4.爷孙传值provide / inject

provide:可以让我们指定想要提供给后代组件的数据或方法

inject:在任何后代组件中接收想要添加在这个组件上的数据或方法

但是!!!

在传值组件中,provide:{ }这样使用不能获取 methods 中的方法,provide(){ return{} }使用不能获取data中的属性。

// 1.在传值组件中
export default{
    // 方法一 不能获取 methods 中的方法
    provide:{
        name:"nanmo",
        age: this.data中的属性
    },
    // 方法二 不能获取 data 中的属性
    provide(){
        return {
            name:"nanmo",
            someMethod:this.someMethod
            //也可以将爷组件整个传入,但不推荐:father:this
        }
    },
    methods:{
        someMethod(){
            console.log("传个方法")
        }
    }
}

// 后代接收数据组件
export default{
    inject:["name","someMethod"],
    mounted(){
        console.log(this.name)
        this.someMethod()
    }
}

4.slot插槽传值了,hhhh

引入插槽后直接点出插槽数据即可

// Child.vue
<template>
    <div>
        <slot :user="user"></slot>
    </div>
</template>
export default{
    data(){
        return {
            user:{ name:"nanmo" }
        }
    }
}

// Parent.vue
<template>
    <div>
        <child v-slot="user">
            {{ user.name }}
        </child>
    </div>
</template>

方法肯定不止上述这些,笔者只是写了日常中比较常用的方案,更多请移步掘金。

vue3.2 组件传值

1.父子传值,props

很重要的一个API,贴个超链,官方介绍

跟vue2差不多,只是语法变了,在 <script setup> 中必须使用 defineProps API 来声明 props,不要要额外引入。

//父组件
<template>
  <Child :msg="message" />
</template>

<script setup>
import Child from './components/Child.vue'
let message = 'nanmo'
</script>

// 子组件
<template>
    {{ msg }}
</template>

<script setup>
const props = defineProps({
  msg: {
    type: String,
    default: ''
  }
})
console.log(props.msg) // 在 js 里需要使用 props.xxx 的方式使用。在 html 中使用不需要 props
</script>

2.子父传值,emits

还是跟vue2差不多,以上述为例子

<div>父组件:{{ message }}</div>
<Child @changeMsg="changeMessage" />
    
let message = ref('nanmo')
function changeMessage(data) {
  message.value = data
}

子组件:<button @click="handleClick">子组件的按钮</button>

const emit = defineEmits(['changeMsg'])
function handleClick() {
  emit('changeMsg', '南漠')
}

这里可以用 v-model 语法糖实现相同效果,我觉得其本质还是使用了emits传值

<Child v-model="message" />

//子组件
<div @click="handleClick">{{modelValue}}</div>

//1.接收父组件使用 v-model 传进来的值,必须用 modelValue 这个名字来接收
const props = defineProps([
  'modelValue' 
])

//2.必须用 update:modelValue 这个名字来通知父组件修改值
const emit = defineEmits(['update:modelValue']) 

function handleClick() {
  emit('update:modelValue', '南漠')
}

//更简单的还能这样,传值过程直接卸载标签内
<div @click="$emit('update:modelValue', '南漠')">{{modelValue}}</div>

const props = defineProps([
  'modelValue' 
])

3.子父传值,expose / ref

这个比较简单,一个把数据暴露出去,一个ref拿就好

子组件可以通过 expose 暴露自身的方法和数据,父组件通过 ref 获取到子组件并调用其方法或访问数据。

是不是有点像vue2的 provide / inject , 哈哈

<div>父组件:拿到子组件的message数据:{{ msg }}</div>
<button @click="callChildFn">调用子组件的方法</button>
<Child ref="com" />

const com = ref(null) // 通过ref拿到子组件实力,后续.出数据来就行
const msg = ref('')
onMounted(() => {
  msg.value = com.value.message
})

//子组件使用 defineExpose 向外暴露指定的数据和方法,一样defineExpose 不需要额外引入
defineExpose({
  message,
  changeMessage
})

4.Vue3当然可以使用插槽,用法和Vue2一样

5.provide / inject,用法和Vue2一样

6.总线Bus,用法和Vue2一样

3.5、 组件插槽

插槽-官方文档 ,缩写 "#" , 简而言之就是父组件向子组件传递内容(模版的内容):子组件会有一个插槽 <slot></slot> ,用来接收父组件标签的内容。基本用法大致为:

  1. 插槽在 template 下装入 <slot>默认内容</slot> 插槽;如果有默认内容的话会直接输出到插槽上
  2. 要插入的标签的内容: <alert-box>要插槽的内容</alert-box>

基本使用: 写个插槽文件,在需要的地方import 插槽名字 from 路径 导入,slot的内容会被更新。 v-slot: 可以使用#替换

<!-- MySlotCpn组件 -->
<template>
  <div class="my-slot-cpn">
    <h1>组件开始</h1>
    <slot>默认内容</slot>
    <h1>组件结束</h1>
  </div>
</template>

<!-- App组件 -->
<template>
  <div class="app">
    <MySlotCpn>
      <i>Hello Vue</i>
      <div>Hello Vue</div>
    </MySlotCpn>
  </div>
</template>

具名插槽: 在插槽文件设定name,引用时v-slot:对应名字即可。 一个不带 name 的 slot,会带有隐含的名字 default

<!-- NavBar组件 -->
<template>
  <div class="nav-bar">
    <div class="left">
      <slot name="left"></slot>
    </div>
    <div class="center">
      <slot name="center"></slot>
    </div>
    <div class="right">
      <slot name="right"></slot>
    </div>
  </div>
</template>

<!-- App组件 -->
<template>
  <div class="app">
    <NavBar>
      <template v-slot:left>
        <button>button</button>
      </template>
      <template v-slot:center>
        <span>span</span>
      </template>
      <template v-slot:right>
        <i>i</i>
      </template>
    </NavBar>
  </div>
</template>

在这里插入图片描述

动态的插槽名: 给name绑定:,引用时在通过 [] 解构

<!-- 组件里的name是通过props动态绑定的 -->
<slot :name="name"></slot>

<!-- 通过[]解构 -->
<template v-slot:[name]></template>

还有个作用域插槽 大体就是,父组件想用插槽组件的数据,可以在调用的时候拿到

//插槽组件,将数据传递给调用方
<slot :user = "use.name">

//调用组件,可以解构,可以自定义接受名称,在.出来
//v-slot="{scope}" 获取子组件传数据,:list="list" 把list传给子组件
<template>
	<Child v-slot="{scope}" :list="list">
  	{{ user.name }}
  </Child>
</template> 

组件化对Vue的学习来说可谓十分重要,这块还是要多磨多练。