【ElementPlus源码】Container 布局容器

news2024/11/26 11:27:38

文章目录

    • 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.coloradmin.cn/o/1875777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(南京观海微电子)——三极管原理及应用区别

PNP与NPN三极管的原理与使用方法 三极管主要的功能是电流放大和开关作用。 三极管按材料分有两种&#xff1a;锗管和硅管。而每一种又有NPN和PNP两种结构形式&#xff0c;但使用最多的是硅NPN和PNP两种三极管&#xff0c;两者除了电源极性不同外&#xff0c;其工作原理都是相同…

编程开发不得不懂的世界协调时UTC的由来

在各种时间标准出现之前&#xff0c;各地都是根据太阳来进行计时的。把太阳连续2次经过地球同一位置所经历的时间间隔称为真太阳日&#xff0c;然后再把这个太阳日划分为更小的时间单位&#xff0c;例如中国古代使用日晷记录时间&#xff0c;把一个太阳日分为12个时辰。因为地球…

排序(冒泡排序、选择排序、插入排序、希尔排序)-->深度剖析(一)

欢迎来到我的Blog&#xff0c;点击关注哦&#x1f495; 前言 排序是一种基本的数据处理操作&#xff0c;它涉及将一系列项目重新排列&#xff0c;以便按照指定的标准&#xff08;通常是数值大小&#xff09;进行排序。在C语言中&#xff0c;排序算法是用来对元素进行排序的一系…

竞赛选题 python的搜索引擎系统设计与实现

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python的搜索引擎系统设计与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;5分创新点&#xff1a;3分 该项目较为新颖&#xff…

昇思25天学习打卡营第04天|数据集 Dataset

数据是深度学习的基础&#xff0c;高质量的数据输入将在整个深度神经网络中起到积极作用。MindSpore提供基于Pipeline的数据引擎&#xff0c;通过数据集&#xff08;Dataset&#xff09;和数据变换&#xff08;Transforms&#xff09;实现高效的数据预处理。其中Dataset是Pipel…

【机器学习】基于层次的聚类方法:理论与实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 基于层次的聚类方法&#xff1a;理论与实践引言1. 层次聚类基础1.1 概述1.2 距离…

decode()方法——解码字符串

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 解码是将字节流转换成字符串&#xff08;文本&#xff09;&#xff0c;其他编码格式转成unicode。在Python中提供了decode()方法&#xff0…

GS NVMe全闪存储通过XFS文件系统助力太空科研AI处理

用户是名列全球TOP 5的太空研究机构&#xff0c;专为各种卫星任务和应用开发有效载荷、仪器及天基系统&#xff0c;在通信、广播、导航、灾害监测、气象学、海洋学、环境监测&#xff0c;以及自然资源测量等方面发挥重要的作用&#xff0c;为探索月球、火星等天体做出了重大的贡…

pafination官网自制

1.pafination.js 参考element ui 中 prev表示上一页&#xff0c;next为下一页 // const itemsPerPage 10; // const totalItems 30; var itemsPerPage ; var totalItems ; let currentPage 1; var pagerCount5 // 设置最大页码按钮数 var totalPages Math.ceil(totalItem…

Flutter循序渐进==>封装、继承、多态、抽象类以及属性修改

导言 新学一门编程语言&#xff0c;最难以理解的莫过于类了。如果类没用&#xff0c;也就算了&#xff0c;它偏偏很有用&#xff0c;我们必须得掌握&#xff0c;不然怎么好意思说自己会面向对象编程呢? 抽象类&#xff08;Abstract Class&#xff09;在面向对象编程中扮演着…

前后端分离项目面试总结

一&#xff1a;是否登录状态 服务端登录的时候&#xff0c;给分配一个session用于存储数据&#xff0c;同时将sessionID返回给浏览器&#xff0c;浏览器通过cookie把sessionID存储起来&#xff0c;下次访问时携带上&#xff0c;服务端就可以通过sessionID来确定用户是否登录。 …

uview文本框组件计数count报错u--textarea

报错内容&#xff1a; [Vue warn]: Error in render: “TypeError: Cannot read property ‘length’ of null” found in —> at uni_modules/uview-ui/components/u-textarea/u-textarea.vue at uni_modules/uview-ui/components/u–textarea/u–textarea.vue mp.runtime.…

Flutter循序渐进==>基金管理APP首页

目录 查看版本 组件 组件源码学习 做个基金APP首页源代码 效果 查看版本 组件 组件的本质就是个类。 import package:flutter/material.dart;void main() {runApp(const OurFirstApp(),); } OurFirstApp()实例化&#xff0c;就是给runApp用的&#xff0c;runApp就是运行实…

Java的NIO体系

目录 NIO1、操作系统级别下的IO模型有哪些&#xff1f;2、Java语言下的IO模型有哪些&#xff1f;3、Java的NIO应用场景&#xff1f;相比于IO的优势在哪&#xff1f;4、Java的IO、NIO、AIO 操作文件读写5、NIO的核心类 :Buffer&#xff08;缓冲区&#xff09;、Channel&#xff…

用GPT-4纠错GPT-4 OpenAI推出CriticGPT模型

根据OpenAI周四&#xff08;6月27日&#xff09;发布的新闻稿&#xff0c;该公司新推出了一个基于GPT-4的模型——CriticGPT&#xff0c;用于捕获ChatGPT代码输出中的错误。CriticGPT的作用相当于让人们用GPT-4来查找GPT-4的错误。该模型可以对ChatGPT响应结果做出批评评论&…

Echarts地图实现:山东省报考人数

Echarts地图实现&#xff1a;山东省报考人数 效果预览 设计思路 数据可视化&#xff1a;选择地图作为数据展示的方式&#xff0c;可以直观地展示山东省不同城市的报考人数分布。交互性&#xff1a;通过ECharts的交互功能&#xff0c;如提示框&#xff08;tooltip&#xff09;…

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…

二叉搜索数的最小绝对差-二叉树

需要用到中序遍历 中序遍历 94. 二叉树的中序遍历 - 力扣&#xff08;LeetCode&#xff09; 递归 class Solution { public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;inoder(root,res);return res;}void inoder(TreeNode* root , vector…

从零开始搭建spring boot多模块项目

一、搭建父级模块 1、打开idea,选择file–new–project 2、选择Spring Initializr,选择相关java版本,点击“Next” 3、填写父级模块信息 选择/填写group、artifact、type、language、packaging(后面需要修改)、java version(后面需要修改成和第2步中版本一致)。点击“…

计算机Java项目|基于SpringBoot的新闻稿件管理系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、Python项目、前端项目、人工智能与大数据、简…