【ElementPlus源码】Container 布局容器

文章目录

    • index.ts
    • Container
    • header
    • utils
      • withInstall
      • withNoopInstall
    • hooks
      • useNamespace
    • 单元测试

看源码时候做的笔记。如有错误请指出!
关于路径的省略,详见button:【ElementPlus源码】Button按钮-CSDN博客

index.ts

导入一堆组件,导出ElContainer。

import Container from './src/container.vue'
import Aside from './src/aside.vue'
import Footer from './src/footer.vue'
import Header from './src/header.vue'
import Main from './src/main.vue'

export const ElContainer = withInstall(Container, {
  Aside,
  Footer,
  Header,
  Main,
})
... // 不全

Container

判断是否垂直,先判断props中的direction属性。

在这里插入图片描述
若没有props.direction,判断插槽中是否有header和footer,有则返回true,代表垂直。

const isVertical = computed(() => {
  if (props.direction === 'vertical') {
    return true
  } else if (props.direction === 'horizontal') {
    return false
  }
  //   是否有ElHeader或ElFooter组件,有就为true
  if (slots && slots.default) {
    const vNodes: VNode[] = slots.default()
    return vNodes.some((vNode) => {
      const tag = (vNode.type as Component).name
      return tag === 'ElHeader' || tag === 'ElFooter'
    })
  } else {
    return false
  }
})

表示垂直的样式会绑定在style中:

<section :class="[ns.b(), ns.is('vertical', isVertical)]">
   <slot />
 </section>

useNamespace是一个hook,详情写在博客hooks/useNamespace下。它返回一个对象,可以生成规范的类名。

const ns = useNamespace('container')

ns.is('vertical', isVertical)生成的就是:isVertical

在这里插入图片描述

container只有direction属性。

header

header只有height属性,写在props中。style会计算height属性,最终将结果绑定到header上。

<template>
  <header :class="ns.b()" :style="style">
    <slot />
  </header>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { useNamespace } from '@element-plus/hooks'

import type { CSSProperties } from 'vue'

defineOptions({
  name: 'ElHeader',
})

const props = defineProps({
  /**
   * @description height of the header
   */
  height: {
    type: String,
    default: null,
  },
})

const ns = useNamespace('header')
const style = computed(() => {
  return props.height
    ? (ns.cssVarBlock({
        height: props.height,
      }) as CSSProperties)
    : {}
})
</script>

aside、footer、main完全相似,不赘述。

utils

withInstall

传入一个main组件和extra,返回一个添加了install方法的main组件。
这个方法将main和extra中的所有属性注册到app中。

export const withInstall = <T, E extends Record<string, any>>(
  main: T, // 主要的 Vue 组件
  extra?: E // 可选的对象,其属性值是其他 Vue 组件
) => {
  /* 给 `main` 组件添加一个 `install` 方法
     这个方法接受一个 Vue 应用作为参数
     并将 `main` 组件以及 `extra` 中的所有组件注册到这个应用中
     */
  ;(main as SFCWithInstall<T>).install = (app): void => {
    for (const comp of [main, ...Object.values(extra ?? {})]) {
      app.component(comp.name, comp)
    }
  }

  //   将extra的属性添加到main上
  if (extra) {
    for (const [key, comp] of Object.entries(extra)) {
      ;(main as any)[key] = comp
    }
  }
  // SFCWithInstall<T>表示带有 `install` 方法的 Vue 单文件组件
  // `E` 是 `extra` 的类型。
  return main as SFCWithInstall<T> & E
}

container/index.ts中调用:

返回了一个对象ElContainer ,有一个install方法,若调用install方法,则将Container、Aside、Footer、Header、Main五个组件注册到app上,并可以通过ElContainer.Container的方法访问Container。

export const ElContainer = withInstall(Container, {
  Aside,
  Footer,
  Header,
  Main,
})

withNoopInstall

创建一个带有空操作 install 方法的 Vue 组件对象

export const withNoopInstall = <T>(component: T) => {
  ;(component as SFCWithInstall<T>).install = NOOP

  return component as SFCWithInstall<T>
}

关于NOOP:

import { NOOP } from '@vue/shared'

NOOP 是一个常见的编程术语,代表 “No Operation”,即不执行任何操作。在许多编程语言和环境中,它通常被用作一个占位符函数,当你需要一个函数但又不希望它做任何事情时,可以使用NOOP。

调用withNoopInstall:

export const ElAside = withNoopInstall(Aside)

hooks

useNamespace

路径:hooks/use-namespace

用于生成 BEM(Block Element Modifier)命名规则的类名和 CSS 变量名.

BEM 是一种 CSS 命名方法,全称是 Block Element Modifier,即块(Block)、元素(Element)、修饰符(Modifier)。

下面代码接受两个参数:块名和可选的命名空间覆盖。

返回一个对象,包含属性如下:

  • namespace:命名空间。
  • b、e、m、be、em、bm 和 bem:用于生成不同类型的 BEM 类名的函数。
  • is:用于生成状态类名的函数。
  • cssVar、cssVarName、cssVarBlock 和 cssVarBlockName:用于生成 CSS 变量名的函数。
const statePrefix = 'is-'

const _bem = (
  namespace: string,
  block: string,
  blockSuffix: string,
  element: string,
  modifier: string
) => {
  let cls = `${namespace}-${block}`
  if (blockSuffix) {
    cls += `-${blockSuffix}`
  }
  if (element) {
    cls += `__${element}`
  }
  if (modifier) {
    cls += `--${modifier}`
  }
  return cls
}

export const useNamespace = (
  block: string,
  namespaceOverrides?: Ref<string | undefined>
) => {
  const namespace = useGetDerivedNamespace(namespaceOverrides)
  const b = (blockSuffix = '') =>
    _bem(namespace.value, block, blockSuffix, '', '')
  const e = (element?: string) =>
    element ? _bem(namespace.value, block, '', element, '') : ''
  const m = (modifier?: string) =>
    modifier ? _bem(namespace.value, block, '', '', modifier) : ''
  const be = (blockSuffix?: string, element?: string) =>
    blockSuffix && element
      ? _bem(namespace.value, block, blockSuffix, element, '')
      : ''
  const em = (element?: string, modifier?: string) =>
    element && modifier
      ? _bem(namespace.value, block, '', element, modifier)
      : ''
  const bm = (blockSuffix?: string, modifier?: string) =>
    blockSuffix && modifier
      ? _bem(namespace.value, block, blockSuffix, '', modifier)
      : ''
  const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
    blockSuffix && element && modifier
      ? _bem(namespace.value, block, blockSuffix, element, modifier)
      : ''
  const is: {
    (name: string, state: boolean | undefined): string
    (name: string): string
  } = (name: string, ...args: [boolean | undefined] | []) => {
    const state = args.length >= 1 ? args[0]! : true
    return name && state ? `${statePrefix}${name}` : ''
  }

  // for css var
  // --el-xxx: value;
  const cssVar = (object: Record<string, string>) => {
    const styles: Record<string, string> = {}
    for (const key in object) {
      if (object[key]) {
        styles[`--${namespace.value}-${key}`] = object[key]
      }
    }
    return styles
  }
  // with block
  const cssVarBlock = (object: Record<string, string>) => {
    const styles: Record<string, string> = {}
    for (const key in object) {
      if (object[key]) {
        styles[`--${namespace.value}-${block}-${key}`] = object[key]
      }
    }
    return styles
  }

  const cssVarName = (name: string) => `--${namespace.value}-${name}`
  const cssVarBlockName = (name: string) =>
    `--${namespace.value}-${block}-${name}`

  return {
    namespace,
    b,
    e,
    m,
    be,
    em,
    bm,
    bem,
    is,
    // css
    cssVar,
    cssVarName,
    cssVarBlock,
    cssVarBlockName,
  }
}

省流版:返回一个对象,可以生成规范的类名。

单元测试

单元测试写的很好啊,可以做学习单元测试的例子:

container.test.tsx:

const AXIOM = 'Rem is the best girl'

describe('Container.vue', () => {
  test('container render test', async () => {
    const wrapper = mount(() => <Container>{AXIOM}</Container>)
    expect(wrapper.text()).toEqual(AXIOM)
  })

  test('vertical', () => {
    const wrapper = mount(() => (
      <Container>
        <Header />
        <Main />
      </Container>
    ))
    expect(wrapper.classes('is-vertical')).toBe(true)
  })

  test('direction', () => {
    const wrapper = mount({
      data: () => ({ direction: 'horizontal' }),
      render() {
        return (
          <Container direction={this.direction}>
            <Header />
            <Main />
          </Container>
        )
      },
    })

    expect(wrapper.vm.$el.classList.contains('is-vertical')).toBe(false)
    wrapper.vm.direction = 'vertical'
    wrapper.vm.$nextTick(() => {
      expect(wrapper.vm.$el.classList.contains('is-vertical')).toBe(true)
    })
  })
})

describe('Header', () => {
  test('create header', () => {
    const wrapper = mount(() => <Header />)
    expect(wrapper.classes()).toContain('el-header')
  })

  test('header height', () => {
    const wrapper = mount(() => <Header height="100px" />)
    const vm = wrapper.vm
    expect(getCssVariable(vm.$el, '--el-header-height')).toEqual('100px')
  })
})

describe('Aside', () => {
  test('aside create', () => {
    const wrapper = mount(() => <Aside />)
    expect(wrapper.classes()).toContain('el-aside')
  })

  test('aside width', () => {
    const wrapper = mount(() => <Aside width="200px" />)
    const vm = wrapper.vm
    expect(getCssVariable(vm.$el, '--el-aside-width')).toEqual('200px')
  })
})

describe('Main', () => {
  test('main create', () => {
    const wrapper = mount(() => <Main />)
    expect(wrapper.classes()).toContain('el-main')
  })
})

describe('Footer', () => {
  test('footer create', () => {
    const wrapper = mount(() => <Footer />)
    expect(wrapper.classes()).toContain('el-footer')
  })

  test('footer height', () => {
    const wrapper = mount(() => <Footer height="100px" />)
    const vm = wrapper.vm
    expect(getCssVariable(vm.$el, '--el-footer-height')).toEqual('100px')
  })
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/754765.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Centos7配置支持ftp文件传输功能

报错信息 适用于不支持ftp协议的centos7的系统。 报错信息&#xff1a;An error occurred while executing the action Connect. Details: No connection could be made because the target machine actively refused it. 解决办法 安装及启动等命令 # 安装vsftpd sudo yum…

Spark SQL 的总体工作流程

Spark SQL 是 Apache Spark 的一个模块,它提供了处理结构化和半结构化数据的能力。通过 Spark SQL,用户可以使用 SQL 语言或 DataFrame API 来执行数据查询和分析。这个模块允许开发者将 SQL 查询与 Spark 的数据处理能力结合起来,实现高效、优化的数据处理。下面是 Spark S…

高级运维工程师讲述银河麒麟V10SP1服务器加固收回权限/tmp命令引起生产mysql数据库事故实战

高级运维工程师讲述银河麒麟V10SP1服务器加固收回权限/tmp命令引起生产MySql数据库事故实战 一、前言 作为运维工程师经常会对生产服务器进行安全漏洞加固&#xff0c;一般服务厂商、或者甲方信息安全中心提供一些安全的shell脚本&#xff0c;一般这种shell脚本都是收回权限&…

C++实现简化版Qt信号槽机制(2):增加内存安全保障

在上一篇文章中《C实现一个简单的Qt信号槽机制》&#xff0c;我们基于前面的反射代码实现了信号槽的功能。 但是上一篇的代码中没有对象生命周期管理的机制&#xff0c;如果在对象的生命周期结束后还存在未断开的信号和槽连接&#xff0c;那么信号触发时可能会尝试访问已经被析…

Redis 7.x 系列【11】数据类型之位图(Bitmap)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 基本命令2.1 SETBIT2.2 GETBIT2.3 BITCOUNT2.4 BITPOS2.5 BITFIELD2.6 BITF…

【产品经理】订单处理10-分配快递策略

本次主要讲解下在订单处理过程中分配快递的策略以及分配快递中需要用到的设置。 一、建立快递档案 在ERP系统中&#xff0c;需要建立快递档案&#xff0c;设置所属快递、快递的服务类型、支持的打印模版以及快递在各个平台的电子面单支持情况。 二、仓库绑定快递 仓库需要设…

【动态规划】279.完全平方数

279. 完全平方数 难度&#xff1a;中等 力扣地址&#xff1a;https://leetcode.cn/problems/perfect-squares/ 没有刷过的小伙伴们请一定要先去刷一次&#xff0c;然后如果感兴趣的话再阅读以下内容&#xff0c;便于交流 ~ 多谢支持 ~ 问题描述 给你一个整数 n &#xff0c;返…

C# 入门—基本语法

一、数据类型 C# 语言中内置了一些基本的数据类型&#xff0c;数据类型用来指定程序中变量可以存储的数据的类型&#xff0c;C# 中的数据类型可以大致分为三类&#xff1a; 值类型&#xff08;Value types&#xff09;&#xff1b;引用类型&#xff08;References types&…

[推荐]有安全一点的网贷大数据信用查询网站吗?

在互联网金融日益发展的今天&#xff0c;网贷大数据查询网站成为了许多人申贷前的必备工具。随着使用这些网站的人群越来越多&#xff0c;安全问题也逐渐浮出水面。最近&#xff0c;就有许多用户反馈自己的个人信息在网贷大数据查询网站上被泄露。为了解决这一问题&#xff0c;…

Spring Cloud Gateway3.x自定义Spring Cloud Loadbalancer负载均衡策略以及实现动态负载均衡策略的方案

目录 前言 1.原理分析 1.1 ReactiveLoadBalancerClientFilter源码分析 1.2 LoadBalancerClientFactory源码分析 2.代码实现 2.1 扩展原生RoundRobinLoadBalancer轮询策略 2.1.1 自定义实现RoundRobinLoadBalancer 2.1.2 配置自定义的RoundRobinLoadBalan…

idea2024使用springboot3.x系列新建java项目,使用jdk17,启动项目报错

身为一名开发人员&#xff0c;敲代码无数&#xff0c;竟被一个小小启动给我卡了大半天&#xff0c;太丢脸了 报错一&#xff1a;Field infoSysRepository in com.erectile.Impl.PersonalInfoServiceImpl required a bean of type ‘com.erectile.jpa.repository.InfoSysReposit…

前端vue使用onlyoffice控件实现word在线编辑、预览(仅列出前端部分需要做的工作,不包含后端部分)

简介 ONLYOFFICE 文档 是一个开源办公套件&#xff0c;包括文本文档、电子表格、演示文稿和可填写表单的编辑器。 它提供以下功能&#xff1a; 创建、编辑和查看文本文档、电子表格、演示文稿和可填写表单&#xff1b; 与其他队友实时协作处理文件。 基于这个控件&#xff0c;…

微信小程序根据蓝牙RSSI信号强度测试设备距离

背景 在做小程序连接蓝牙设备的时候&#xff0c;有需求表明在搜索到0.5米之内的设备时自动连接 问题&#xff1a; 蓝牙模组只提供了RSSI信号强度&#xff0c;那又该如何计算蓝牙设备距离小程序的距离呢&#xff1f; 解决方案 通过以下公式做大量测试&#xff1a;求 A、n 的平均…

【深度学习】卷积神经网络CNN

李宏毅深度学习笔记 图像分类 图像可以描述为三维张量&#xff08;张量可以想成维度大于 2 的矩阵&#xff09;。一张图像是一个三维的张量&#xff0c;其中一维代表图像的宽&#xff0c;另外一维代表图像的高&#xff0c;还有一维代表图像的通道&#xff08;channel&#xff…

【LeetCode】四、栈相关:有效的括号 + 下一个更大的元素

文章目录 1、栈结构2、Java中的栈3、leetcode20&#xff1a;有效的括号4、leetcode496&#xff1a;下一个更大元素 1、栈结构 和队列相反&#xff0c;栈先进后出 时间复杂度&#xff1a;访问、插入、删除都在栈顶进行操作&#xff0c;时间复杂度为O(1)&#xff0c;搜索需要遍…

技术分享:分布式数据库DNS服务器的架构思路

DNS是企业数字化转型的基石。伴随微服务或单元化部署的推广&#xff0c;许多用户也开始采用分布式数据库将原来的单体数据库集群服务架构拆分为大量分布式子服务集群&#xff0c;对应不同的微服务或服务单元。本文将从分布式数据库DNS服务器的架构需求、架构分析两方面入手&…

2.用BGP对等体发送路由

2.用BGP对等体发送路由 实验拓扑&#xff1a; 实验要求&#xff1a;用BGP对等体发送路由信息 实验步骤&#xff1a; 1.完成基本配置&#xff08;略&#xff09; 2.建立BGP对等体&#xff08;略&#xff09; 3.创建路由信息&#xff08;用创建一个loop back接口就能产生一个直连…

【java】【控制台】【javaSE】 初级java家教管理系统控制台命令行程序项目

更多项目点击&#x1f446;&#x1f446;&#x1f446;完整项目成品专栏 【java】【控制台】【javaSE】 初级java家教管理系统控制台命令行程序项目 获取源码方式项目说明&#xff1a;功能点数据库涉及到&#xff1a; 项目文件包含&#xff1a;项目运行环境 &#xff1a;截图其…

HarmonyOS Next开发学习手册——弹性布局 (Flex)

概述 弹性布局&#xff08; Flex &#xff09;提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。常用于页面头部导航栏的均匀分布、页面框架的搭建、多行数据的排列等。 容器默认存在主轴与交叉轴&#xff0c;子元素默认沿主轴排列&#xff0c;子元素在主轴…

网络流-EK算法(保姆级教学)

本文引用董晓算法的部分图片。 一些不能带入纸质资料的竞赛&#xff0c;网络流纳入考纲。 因为需要默写&#xff0c;想来也不会考默写dinic这种算法难倒大家&#xff0c;只需要快速敲对EK算法就行了。 EK算法能在O(n*m^2)的复杂度内解决最大流问题&#xff0c;其中最大流就是…