zxpnet网站 zxpnet网站
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

zxpnet

一个爱学习的java开发攻城狮
首页
前端
后端服务器
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 大前端课程视频归档
  • html

  • js

  • 前端框架

  • 自动化构建

  • typescript

  • es6

  • bootstrap

  • layer

  • vue

  • vue3

    • 邂逅Vue3和Vue3开发体验
    • Vue3基础语法
    • Vue3组件化开发
      • Vue打包后不同版本解析
      • 全局组件和局部组件
      • 父子组件之间通信
        • 1、子组件props属性
        • Props有两种常见的用法
        • 非Prop的Attribute
        • 2、子组件传递给父组件
        • 开发流程
        • 自定义事件的参数和验证
      • 非父子组件间通住
        • Provide/Inject
        • Mitt全局事件总线
      • 插槽slot
        • 具名插槽
        • 渲染作用域
        • 作用域插槽
        • 独占默认插槽的缩写
      • 动态组件
        • keep-alive
        • 缓存组件的生命周期
      • v-model
      • 异步组件
        • defineAsyncComponent
        • 与 Suspense 一起使用
      • 组件生命周期
      • 引用元素和组件
        • $refs
        • $parent和$root
    • Vue3过渡&动画实现
    • Babel和devServer
    • Composition API
    • vue3高级语法
    • vue3源码
    • vue-router路由
    • vuex状态管理
    • vue开源项目
    • vue3-cms项目笔记
    • pinia状态管理
  • vuepress

  • hexo博客

  • 文档

  • biz业务

  • frontend
  • vue3
shollin
2022-01-04
目录

Vue3组件化开发

  • Vue打包后不同版本解析
  • 全局组件和局部组件
  • 父子组件之间通信
    • 1、子组件props属性
    • 2、子组件传递给父组件
  • 非父子组件间通住
    • Provide/Inject
    • Mitt全局事件总线
  • 插槽slot
    • 具名插槽
    • 渲染作用域
    • 作用域插槽
    • 独占默认插槽的缩写
  • 动态组件
    • keep-alive
    • 缓存组件的生命周期
  • v-model
  • 异步组件
    • defineAsyncComponent
    • 与 Suspense 一起使用
  • 组件生命周期
  • 引用元素和组件
    • $refs
    • $parent和$root

# Vue打包后不同版本解析

vue(.runtime).global(.prod).js:

  • 通过浏览器中的<script src="..."> 直接使用;

  • 我们之前通过CDN引入和下载的Vue版本就是这个版本;

  • 会暴露一个全局的Vue来使用;

vue(.runtime).esm-browser(.prod).js:

  • 用于通过原生 ES 模块导入使用 (在浏览器中通过 <script type="module"> 来使用)。

vue(.runtime).esm-bundler.js:

  • 用于 webpack,rollup 和 parcel 等构建工具;

  • 构建工具中默认是vue.runtime.esm-bundler.js;

  • 如果我们需要解析模板template,那么需要手动指定vue.esm-bundler.js;

vue.cjs(.prod).js:

  • 服务器端渲染使用;

  • 通过require()在Node.js中使用;

# 全局组件和局部组件

全局组件所有的组件里面都可以使用

app.component("component-b",{}); // 注册全局组件
1
const App = {
      template: '#my-app',
      components: {
        // key: 组件名称
        // value: 组件对象
        ComponentA: ComponentA
      }, ...
}
1
2
3
4
5
6
7
8

# 父子组件之间通信

父子组件之间如何进行通信呢?

  • 父组件传递给子组件:通过props属性;

  • 子组件传递给父组件:通过$emit触发事件;

image-20220127174520882

# 1、子组件props属性

什么是Props呢?

  • Props是你可以在组件上注册一些自定义的attribute;

  • 父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值;

# Props有两种常见的用法

  • 方式一:字符串数组,数组中的字符串就是attribute的名称;

  • 方式二:对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型、是否是必须的、默认值等等

数组用法中我们只能说明传入的attribute的名称,并不能对其进行任何形式的限制,接下来我们来看一下对象的写法是如何让我们的props变得更加完善的。

当使用对象语法的时候,我们可以对传入的内容限制更多:

  • 比如指定传入的attribute的类型;

  • 比如指定传入的attribute是否是必传的;

  • 比如指定没有传入时,attribute的默认值;

type的类型都可以是哪些

String、Number、Boolean、Array、Object、Date、Function、Symbol

image-20220208164333159

Prop 的大小写命名(camelCase vs kebab-case)

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符;

这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名

export default {
    // props: ['title', 'content']
    inheritAttrs: false, // 不继承父元素传来的非prop属性,可以使用$attrs获得
    props: {
      title: String,
      content: {
        type: String,
        required: true,
        default: "123"
      },
      counter: {
        type: Number
      },
      info: {
        type: Object, // 引用类型,需要传值
        default() {
          return {name: "why"}
        }
      },
      messageInfo: {
        type: String
      }
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 非Prop的Attribute

当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的Attribute**;

常见的包括class、style、id属性等;

Attribute继承

当组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中:

如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false:

  • 禁用attribute继承的常见情况是需要将attribute应用于根元素之外的其他元素;

我们可以通过 $attrs来访问所有的 非props的attribute;

多个根节点的attribute

多个根节点的attribute如果没有显示的绑定,那么会报警告,我们必须手动的指定要绑定到哪一个属性上:

# 2、子组件传递给父组件

# 开发流程

首先,我们需要在子组件中定义好在某些情况下触发的事件名称;

其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中;

最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件;

image-20220208180655544

# 自定义事件的参数和验证

export default {
    // emits: ["add", "sub", "addN"],
    // 对象写法的目的是为了进行参数的验证
    emits: {
      add: null,
      sub: null,
      addN: (num, name, age) => {
        console.log(num, name, age);
        if (num > 10) {
          return true
        }
        return false;
      }
    },
    data() {
      return {
        num: 0
      }
    },
    methods: {
      increment() {
        console.log("+1");
        this.$emit("add");
      },
      decrement() {
        console.log("-1");
        this.$emit("sub");
      },
      incrementN() {
        this.$emit('addN', this.num, "why", 18);
      }
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 非父子组件间通住

# Provide/Inject

image-20220209141115602

  • 无论层级结构有多深,父组件都可以作为其所有子组件的依赖提供者;

  • 父组件有一个 provide 选项来提供数据,建议写成函数,可以通过this拿data里面的数据;

  • 子组件有一个 inject 选项来开始使用这些数据;

处理响应式数据

computed返回的是一个ref对象,需要取出其中的value来使用

import { computed } from 'vue';

  export default {
    components: {
      Home
    },
    provide() { // 建议写成函数,通过this拿到data里面的数据
      return {
        name: "why",
        age: 18,
        length: computed(() => this.names.length) // ref对象 .value
      }
    },
1
2
3
4
5
6
7
8
9
10
11
12
13

Provide / Inject | Vue.js (vuejs.org) (opens new window)

# Mitt全局事件总线

Vue3从实例中移除了 $on、$off 和 $once 方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库:

  • Vue3官方有推荐一些库,例如 mitt 或 tiny-emitter
npm install mitt
1

以封装一个工具eventbus.js

import mitt from 'mitt';
const emitter = mitt(); // 可以生成多个
export default emitter;
1
2
3

image-20220209155810759

# 插槽slot

# 具名插槽

具名插槽顾名思义就是给插槽起一个名字,<slot> 元素有一个特殊的 attribute:name;

  • 一个不带 name 的slot,会带有隐含的名字 default

  • 我们可以通过 v-slot:[dynamicSlotName]方式动态绑定一个名称

  • v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #;

<nav-bar :name="name">
      <template v-slot:left>
        <button>左边的按钮</button>
      </template>
      <template #center>
        <h2>我是标题</h2>
      </template>
      <template #right>
        <i>右边的i元素</i>
      </template>
      <template v-slot:[name]>
        <i>why内容</i>
      </template>
    </nav-bar>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 渲染作用域

父级模板里的所有内容都是在父级作用域中编译的;

子模板里的所有内容都是在子作用域中编译的;

# 作用域插槽

子组件通过绑定slot的属性将值传给父组件,父组件通过v-slot:default="slotProps"的方式获取到slot的props

image-20220209165232194

# 独占默认插槽的缩写

如果我们的插槽是默认插槽default,那么在使用的时候 v-slot:default="slotProps"可以简写为v

slot="slotProps":

<!-- 独占默认插槽 -->
    <show-names :names="names" v-slot="coderwhy">
      <button>{{coderwhy.item}}-{{coderwhy.index}}</button>
    </show-names>

    <!-- 注意: 如果还有其他的具名插槽, 那么默认插槽也必须使用template来编写 -->
    <show-names :names="names">
      <template v-slot="coderwhy">
        <button>{{coderwhy.item}}-{{coderwhy.index}}</button>
      </template>

      <template v-slot:why>
        <h2>我是why的插入内容</h2>
      </template>
    </show-names>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template v-for="(item, index) in names" :key="item">
      <slot :item="item" :index="index"></slot>
      <slot name="why"></slot>
    </template>
1
2
3
4

# 动态组件

动态组件是使用 component 组件,通过一个特殊的attributeis 来实现

<component :is="currentTab"
                 name="coderwhy"
                 :age="18"
                 @pageClick="pageClick">
</component>

<!-- 1.v-if的判断实现 -->
    <!-- <template v-if="currentTab === 'home'">
      <home></home>
    </template>
    <template v-else-if="currentTab === 'about'">
      <about></about>
    </template>
    <template v-else>
      <category></category>
    </template> -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# keep-alive

默认情况下,我们在切换组件后,about组件会被销毁掉,再次回来时会重新创建组件; 但是,在开发中某些情况我们希望继续保持组件的状态,而不是销毁掉,这个时候我们就可以使用一个内置组件:keep-alive

keep-alive有一些属性:

  • include - string | RegExp | Array。只有名称匹配的组件会被缓存;

  • exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存;

  • max - number | string。最多可以缓存多少组件实例,一旦达到这个数字,那么缓存组件中最近没有被访问的实例会被销毁;

include 和 exclude prop 允许组件有条件地缓存:

  • 二者都可以用逗号分隔字符串、正则表达式或一个数组来表示;

  • 匹配首先检查组件自身的 name 选项

# 缓存组件的生命周期

对于缓存的组件来说,再次进入时,我们是不会执行created或者mounted等生命周期函数的,但是有时候我们确实希望监听到何时重新进入到了组件,何时离开了组件;这个时候我们可以使用activated 和 deactivated 这两个生命周期钩子函数来监听

# v-model

前面我们在input中可以使用v-model来完成双向绑定:

p这个时候往往会非常方便,因为v-model默认帮助我们完成了两件事;

  • v-bind:value的数据绑定和@input的事件监听;

如果我们现在封装了一个组件,其他地方在使用这个组件时,是否也可以使用v-model来同时完成这两个功能呢?

也是可以的,vue也支持在组件上使用v-model;

当我们在组件上使用的时候,等价于如下的操作:

  • 我们会发现和input元素不同的只是属性的名称modelValue和事件触发的名称update:modelValue而已;
 <!-- <input v-model="message">
    <input :value="message" @input="message = $event.target.value"> -->

    <!-- 组件上使用v-model -->
    <!-- <hy-input v-model="message"></hy-input> -->
    <!-- <hy-input :modelValue="message" @update:model-value="message = $event"></hy-input> -->

    <!-- 绑定两个v-model v-model:XXX -->
    <hy-input v-model="message" v-model:title="title"></hy-input>
1
2
3
4
5
6
7
8
9
<!-- 1.默认绑定和事件处理 -->
    <!-- <button @click="btnClick">hyinput按钮</button>
    <h2>HyInput的message: {{modelValue}}</h2> -->

    <!-- 2.通过input -->
    <!-- <input :value="modelValue" @input="btnClick"> -->

    <!-- 3.绑定到props中是不对的 -->
    <!-- <input v-model="modelValue"> -->

    <!-- 4. -->
    <input v-model="value">

export default {
    props: {
      modelValue: String,
      title: String 
    },
    emits: ["update:modelValue", "update:title"],
    computed: {
      value: {
        set(value) {
          this.$emit("update:modelValue", value);
        },
        get() {
          return this.modelValue;
        }
      },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

自定义事件 | Vue.js (vuejs.org) (opens new window)

# 异步组件

# defineAsyncComponent

如果我们的项目过大了,对于某些组件我们希望通过异步的方式来进行加载(目的是可以对其进行分包处理,在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了实现这个效果,Vue 有一个 defineAsyncComponent 方法

defineAsyncComponent接受两种类型的参数:

类型一:工厂函数,该工厂函数需要返回一个Promise对象;

类型二:接受一个对象类型,对异步函数进行配置;

<suspense>
      <template #default>
        <async-category></async-category>
      </template>
      <template #fallback>
        <loading></loading>
      </template>
    </suspense>

import { defineAsyncComponent } from 'vue';

import Home from './Home.vue';
import Loading from './Loading.vue';

  // import AsyncCategory from './AsyncCategory.vue';
  const AsyncCategory = defineAsyncComponent(() => import("./AsyncCategory.vue"))

  // const AsyncCategory = defineAsyncComponent({
  //   loader: () => import("./AsyncCategory.vue"),
  //   loadingComponent: Loading,
  //   // errorComponent,
  //   // 在显示loadingComponent组件之前, 等待多长时间
  //   delay: 2000,
  //   /**
  //    * err: 错误信息,
  //    * retry: 函数, 调用retry尝试重新加载
  //    * attempts: 记录尝试的次数
  //    */
  //   onError: function(err, retry, attempts) {

  //   }
  // })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 与 Suspense 一起使用

异步组件在默认情况下是可挂起的。这意味着如果它在父链中有一个 <Suspense>,它将被视为该 <Suspense> 的异步依赖。在这种情况下,加载状态将由 <Suspense> 控制,组件自身的加载、错误、延迟和超时选项都将被忽略。

通过在其选项中指定 suspensible: false,异步组件可以退出 Suspense 控制,并始终控制自己的加载状态。

你可以在 API 参考 (opens new window)查看更多可用的选项

Suspense是一个内置的全局组件,该组件有两个插槽:

  • default:如果default可以显示,那么显示default的内容;

  • fallback:如果default无法显示,那么会显示fallback插槽的内容;

参考:

动态组件 & 异步组件 | Vue.js (vuejs.org) (opens new window)

# 组件生命周期

什么是生命周期呢?

  • 每个组件都可能会经历从创建、挂载、更新、卸载等一系列的过程;

  • 在这个过程中的某一个阶段,用于可能会想要添加一些属于自己的代码逻辑(比如组件创建完后就请求一些服务器数据);

  • 但是我们如何可以知道目前组件正在哪一个过程呢?Vue给我们提供了组件的生命周期函数;

生命周期函数:

  • 生命周期函数是一些钩子函数,在某个时间会被Vue源码内部进行回调;

  • 通过对生命周期函数的回调,我们可以知道目前组件正在经历什么阶段;

  • 那么我们就可以在该生命周期中编写属于自己的逻辑代码了;

    img

img

# 引用元素和组件

# $refs

某些情况下,我们在组件中想要直接获取到元素对象或者子组件实例:

在Vue开发中我们是不推荐进行DOM操作的;

这个时候,我们可以给元素或者组件绑定一个ref的attribute属性;

组件实例有一个$refs属性:

p它一个对象Object,持有注册过 ref attribute 的所有 DOM 元素和组件实例

<!-- 绑定到一个元素上 -->
    <h2 ref="title">哈哈哈</h2>

    <!-- 绑定到一个组件实例上 -->
    <nav-bar ref="navBar"></nav-bar>  
1
2
3
4
5

this.$ref.navBar可以拿到组件的data数据和methods方法

# $parent和$root

在Vue3中已经移除了$children的属性,所以不可以使用了。

Vue3基础语法
Vue3过渡&动画实现

← Vue3基础语法 Vue3过渡&动画实现→

最近更新
01
国际象棋
09-15
02
成语
09-15
03
自然拼读
09-15
更多文章>
Theme by Vdoing | Copyright © 2019-2023 zxpnet | 粤ICP备14079330号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式