unocss

unocss

uno

UnoCSS 介绍

1. 概述

UnoCSS 是一个高性能的原子化 CSS 引擎,由 Vite 团队成员 Anthony Fu 开发。它受到 Tailwind CSS 的启发,但更强调按需生成样式和高性能。UnoCSS 的核心理念是通过即时生成(Just-In-Time)的方式,仅根据实际使用的类名动态生成 CSS,从而实现极小的文件体积和高效的加载性能。

2. 核心特性

  • 按需生成:UnoCSS 仅生成实际使用的样式,不会预先生成大量未使用的类,从而显著减少 CSS 文件体积。
  • 高性能:采用即时生成模式,开发和生产环境都能快速生成所需的 CSS。
  • 高度可定制:支持自定义规则、快捷方式、变体和主题配置,能够满足复杂的设计系统需求。
  • 属性化模式:支持在 HTML 属性中直接书写 CSS,例如 bg="blue-400",使代码更加简洁。
  • 响应式设计:内置媒体查询支持,可轻松实现响应式布局。
  • 图标支持:通过预设支持纯 CSS 图标。

3. 使用场景

  • 现代前端项目:特别适合使用 Vite 构建的项目,如 Vue 3、React、Solid.js 和 Svelte。
  • 高性能需求:适用于对构建速度和加载性能有较高要求的大型应用。
  • 高度定制化:适合需要自定义设计系统或特殊样式需求的项目。

4. 配置与使用

UnoCSS 提供了灵活的配置方式,可以通过预设系统(如 presetUnopresetAttributifypresetIcons)快速启用核心功能。开发者还可以通过自定义规则、快捷方式和变体来扩展功能。
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// vite.config.js
import { defineConfig } from 'vite';
import UnoCSS from 'unocss/vite';
import { presetUno, presetAttributify, presetIcons } from 'unocss';
export default defineConfig({
plugins: [
UnoCSS({
presets: [
presetUno(),
presetAttributify(),
presetIcons()
]
})
]
});

5. 与 Tailwind CSS 的对比

特性 UnoCSS Tailwind CSS
生成方式 按需生成,即时生成 预生成,支持 JIT 模式
文件体积 非常小,仅包含实际使用的样式 默认较大,但可通过配置优化
定制性 高度灵活,支持动态规则 提供丰富的预定义类,易于扩展
开发体验 更适合熟悉原子类概念的开发者 类名直观,易于上手

6. 优势与最佳实践

  • 优势:UnoCSS 的按需生成特性使其在大规模项目中表现优异,文件体积小,加载速度快。
  • 最佳实践:合理使用预设,避免引入不必要的功能;利用浏览器缓存优化性能。

7. 总结

UnoCSS 是一个高性能、高度可定制的原子化 CSS 引擎,特别适合现代前端开发。它通过按需生成的方式,提供了极小的文件体积和高效的加载性能,同时支持灵活的扩展和定制。

响应式

响应式

前端判断不同设备

在前端开发中,判断用户使用的设备类型是一个常见的需求,这可以帮助你为不同的设备提供不同的用户体验。以下是一些常用的方法来判断用户使用的设备类型:

  1. User-Agent 检测
    通过检查浏览器的 User-Agent 字符串,可以识别出用户使用的设备类型。例如,iPhone、iPad、Android 设备等都会有特定的 User-Agent 标识。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function detectDevice() {
    var userAgent = navigator.userAgent || navigator.vendor || window.opera;
    if (/windows phone/i.test(userAgent)) {
    return "Windows Phone";
    }
    if (/android/i.test(userAgent)) {
    return "Android";
    }
    if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
    return "iOS";
    }
    return "unknown";
    }
    console.log(detectDevice());
  2. 屏幕分辨率和像素密度
    通过检查屏幕的分辨率和像素密度,可以推断出用户可能使用的设备类型。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function detectScreen() {
    var width = screen.width;
    var height = screen.height;
    var pixelRatio = window.devicePixelRatio;
    if (width <= 480) {
    return "Mobile";
    } else if (width <= 768) {
    return "Tablet";
    } else {
    return "Desktop";
    }
    }
    console.log(detectScreen());
  3. 触摸事件支持
    通过检查浏览器是否支持触摸事件,可以判断用户是否在使用触摸屏设备。
    1
    2
    3
    4
    function detectTouchSupport() {
    return 'ontouchstart' in window || navigator.maxTouchPoints;
    }
    console.log(detectTouchSupport() ? "Touch device" : "Non-touch device");
  4. CSS Media Queries
    使用 CSS 的媒体查询也可以检测设备特性,例如屏幕宽度、分辨率等。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @media (max-width: 600px) {
    /* 针对手机的样式 */
    }
    @media (min-width: 601px) and (max-width: 1024px) {
    /* 针对平板的样式 */
    }
    @media (min-width: 1025px) {
    /* 针对桌面的样式 */
    }
  5. JavaScript 库和框架
    有些 JavaScript 库和框架提供了设备检测的功能,例如 Modernizr、jQuery Mobile 等。
  6. 浏览器特性检测
    使用现代框架如 React、Vue 或 Angular,你可以使用相应的特性检测库来帮助识别设备。
    每种方法都有其局限性,例如 User-Agent 字符串可以被用户修改,而 CSS 媒体查询主要依赖于样式而不是设备的功能。在实际应用中,可能需要结合多种方法来提高设备检测的准确性。

CSS Media Queries 能替代 JavaScript 设备检测吗?

CSS Media Queries 和 JavaScript 设备检测各有优势和适用场景,它们可以互补,但通常不能完全替代彼此。以下是它们各自的一些特点:

CSS Media Queries

  1. 优点
    • 性能:CSS Media Queries 直接在浏览器层面处理,不需要执行JavaScript代码,因此性能更好。
    • 响应式设计:它们非常适合用于响应式设计,可以根据不同屏幕尺寸和特性应用不同的样式。
    • 无需JavaScript:在某些情况下,如果只需要根据屏幕尺寸调整样式,不需要JavaScript介入。
  2. 缺点
    • 功能有限:Media Queries 主要基于CSS属性(如屏幕宽度、分辨率等)进行判断,不能检测设备的具体型号或操作系统。
    • 不能动态交互:Media Queries 只能根据静态条件应用样式,不能根据用户的交互动态改变样式或执行逻辑。

JavaScript 设备检测

  1. 优点
    • 灵活性:JavaScript 可以执行更复杂的逻辑,包括读取和解析User-Agent字符串,检测浏览器支持的特定API等。
    • 动态交互:JavaScript 可以响应用户行为,动态改变页面内容或样式。
    • 更详细的设备信息:可以获取更多关于设备的信息,如操作系统、浏览器版本等。
  2. 缺点
    • 性能:执行JavaScript代码需要更多的计算资源。
    • User-Agent字符串不可靠:User-Agent字符串可以被用户或某些软件修改,导致检测结果不准确。

结合使用

在实际开发中,通常会结合使用CSS Media Queries和JavaScript设备检测:

  • CSS Media Queries 用于处理基于屏幕尺寸、分辨率等静态条件的样式调整。
  • JavaScript 设备检测 用于需要更复杂逻辑或动态交互的场景,比如根据设备型号提供特定的功能或优化。
    总的来说,CSS Media Queries 是实现响应式设计的重要工具,而JavaScript设备检测则提供了更灵活的设备信息处理能力。两者结合使用,可以提供更好的用户体验和更精确的设备适配。

rem和px的区别

rem(根em)和px(像素)是CSS中用于定义长度单位的两种不同方式,它们在网页设计和布局中有着不同的应用场景和特点:

  1. 定义
    • px(像素):是像素的缩写,是一种绝对长度单位。在CSS中,1px通常等同于一个物理像素点。像素值是相对于显示器分辨率的,因此在不同的设备和屏幕分辨率上,显示效果可能不同。
    • rem(根em):是相对于根元素(HTML元素)的字体大小的单位。1rem等于HTML元素的字体大小,即<html>标签的font-size属性值。如果<html>font-size没有被明确设置,那么默认值通常是16px。
  2. 可访问性和响应性
    • px:由于像素是绝对单位,它们在不同设备和屏幕尺寸上的表现是一致的,但这也意味着它们不利于响应式设计,因为它们不随视口大小变化而变化。
    • rem:由于rem单位是相对于根元素的字体大小,它们可以提供更好的响应性和可访问性。通过改变根元素的字体大小,可以统一地缩放页面上所有使用rem单位的元素,这对于响应式设计非常有用。
  3. 兼容性
    • px:几乎所有的浏览器都支持像素单位。
    • rem:现代浏览器都支持rem单位,但在一些非常老旧的浏览器中可能不被支持。
  4. 继承性
    • px:不具有继承性,每个元素的尺寸都是独立的。
    • rem:具有继承性,子元素的rem值是相对于父元素的字体大小计算的,但可以通过根元素的字体大小来统一控制。
  5. 使用场景
    • px:适合用于定义固定大小的元素,如图标、小部件等,或者在不需要响应式设计的场合。
    • rem:适合用于定义需要根据根元素字体大小变化的元素,特别是在响应式设计中,可以提供更好的灵活性和可维护性。
  6. 计算方式
    • px:直接定义元素的大小,不涉及其他元素的尺寸。
    • rem:计算方式涉及到根元素的字体大小,如果根元素的字体大小发生变化,使用rem单位的元素也会相应变化。
      总结来说,rem单位提供了一种基于根元素字体大小的相对长度单位,有助于实现响应式设计和更好的可访问性,而px单位则提供了一种固定的绝对长度单位,适合于不需要响应式设计的场合。

rem 和 em 有什么区别?

rem(根em)和em是CSS中两种相对长度单位,它们都用于定义元素的大小相对于某个参考值,但它们的参考值不同:

  1. 参考值
    • emem单位是相对于父元素的字体大小。如果用于字体大小,1em等于当前元素的字体大小;如果用于其他属性(如宽度、高度、边距等),1em等于父元素的字体大小。
    • remrem单位是相对于根元素(即HTML元素)的字体大小。无论在文档的哪个部分使用rem单位,它总是相对于HTML元素的字体大小。
  2. 继承性
    • em:由于em单位是相对于父元素的,所以它具有继承性。这意味着,如果父元素的字体大小发生变化,使用em单位的子元素也会受到影响。
    • remrem单位不受父元素的影响,它总是相对于根元素的字体大小。这使得rem在布局中更加稳定和可预测。
  3. 使用场景
    • em:适合用于定义相对于当前元素或父元素字体大小的尺寸,如文本缩放、按钮大小等。
    • rem:适合用于定义整个页面的布局尺寸,因为它提供了一种统一的参考值,使得整个页面的布局更加一致。
  4. 兼容性
    • em:几乎所有的浏览器都支持em单位。
    • rem:现代浏览器都支持rem单位,但在一些非常老旧的浏览器中可能不被支持。
  5. 计算方式
    • em:计算方式涉及到父元素的字体大小,如果父元素的字体大小发生变化,使用em单位的元素也会相应变化。
    • rem:计算方式只涉及到根元素的字体大小,不受其他元素的影响。
  6. 可访问性
    • em:由于em单位依赖于父元素的字体大小,它可能在某些情况下导致可访问性问题,比如用户调整浏览器的默认字体大小时,使用em单位的元素可能不会按预期缩放。
    • rem:由于rem单位基于根元素的字体大小,它提供了更好的可访问性,因为用户可以通过改变浏览器的默认字体大小来统一调整页面上所有使用rem单位的元素。
      总结来说,remem的主要区别在于它们的参考值不同,rem基于根元素的字体大小,而em基于父元素的字体大小。这使得rem在响应式设计和布局中更加稳定和一致,而em则在处理文本和相对尺寸时更加灵活。

响应式开发

响应式开发是一种使网站能够兼容不同设备和屏幕尺寸的网页设计方法。以下是实现响应式开发的主要步骤和技巧:

  1. 使用流体布局
    • 使用相对单位(如%emrem)而不是固定单位(如px)来设置元素的宽度和高度。
    • 流体布局允许元素根据容器的大小动态调整尺寸。
  2. 媒体查询(Media Queries)
    • CSS媒体查询允许你根据不同的屏幕尺寸和设备特性应用不同的CSS样式规则。
    • 通过媒体查询,可以为不同的屏幕尺寸设置不同的样式,比如调整布局、字体大小等。
  3. 弹性盒子(Flexbox)
    • Flexbox提供了一种更加高效的方式来布局、对齐和分配容器内项目的空间,即使它们的大小是未知或者动态变化的。
  4. 网格布局(CSS Grid)
    • CSS Grid是一个二维布局系统,允许你在两个维度上创建复杂的布局结构。
    • 它非常适合创建响应式设计,因为它可以很容易地创建和调整网格结构。
  5. 视口元标签(Viewport Meta Tag)
    • 在HTML的<head>部分添加视口元标签,可以控制视口的大小和比例。
    • <meta name="viewport" content="width=device-width, initial-scale=1.0"> 这行代码确保页面的宽度等于设备屏幕的宽度,并在页面加载时设置初始缩放比例为1。
  6. 图片和媒体的响应式处理
    • 使用max-width: 100%height: auto确保图片和媒体元素不会超出其容器的宽度。
    • 使用srcsetsizes属性为不同设备提供不同大小的图片。
  7. 可折叠组件和导航
    • 对于小屏幕设备,使用汉堡菜单或其他可折叠组件来隐藏导航菜单,以节省空间。
    • 使用JavaScript或CSS来控制导航的显示和隐藏。
  8. 测试和优化
    • 使用开发者工具模拟不同设备和屏幕尺寸,测试网站的响应性。
    • 确保所有元素在不同设备上都能正确显示和功能正常。
  9. 性能优化
    • 优化图片和资源文件,确保它们在不同设备上快速加载。
    • 使用异步加载和延迟加载技术来提高页面加载速度。
  10. 渐进增强和优雅降级
    • 渐进增强是指在保证基本功能的前提下,为高级浏览器添加更多功能。
    • 优雅降级是指为低版本浏览器提供基本功能,同时确保在高版本浏览器中也能提供增强体验。
  11. 框架和工具
    • 使用Bootstrap、Foundation等响应式框架可以快速实现响应式布局。
    • 使用像Sass、Less这样的预处理器来编写更复杂的CSS代码。
      通过上述方法,可以创建一个在不同设备和屏幕尺寸上都能良好工作的响应式网站。记住,响应式设计不仅仅是技术的堆砌,更是一种设计思维,需要考虑到用户体验和内容的可访问性。

居中

居中的办法

居中的办法

在CSS中,有多种方法可以实现元素的水平和垂直居中。以下是一些常用的居中技巧:

水平居中

  1. 文本居中
    • 使用text-align: center;可以很容易地实现文本的水平居中。
      1
      2
      3
      .center-text {
      text-align: center;
      }
  2. 块级元素居中
    • 将元素的左右margin设置为auto,并且指定宽度,可以使块级元素水平居中。
      1
      2
      3
      4
      5
      .center-block {
      width: 50%;
      margin-left: auto;
      margin-right: auto;
      }
  3. 使用text-align属性的inline-block元素
    • 如果元素是inline-block,可以直接使用text-align属性来居中。
      1
      2
      3
      4
      .center-inline-block {
      display: inline-block;
      text-align: center;
      }

垂直居中

  1. 使用line-height
    • 对于单行文本,可以将line-height设置为与元素的高度相同来实现垂直居中。
      1
      2
      3
      4
      .center-vertical {
      height: 100px;
      line-height: 100px;
      }
  2. 使用vertical-align
    • 对于inline-blocktable-cell元素,可以使用vertical-align: middle;来垂直居中。
      1
      2
      3
      4
      .center-vertical-align {
      display: inline-block;
      vertical-align: middle;
      }

水平和垂直居中

  1. 使用Flexbox
    • Flexbox提供了一种简单有效的方式来实现元素的水平和垂直居中。
1
2
3
4
5
.center-flex {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
  1. 使用Grid
    • CSS Grid也可以用来实现居中,特别是当你想要更复杂的布局时。
      1
      2
      3
      4
      .center-grid {
      display: grid;
      place-items: center;
      }
  2. 使用绝对定位和transform
    • 通过绝对定位将元素定位到一个父容器的中心,然后使用transform属性进行微调。
      1
      2
      3
      4
      5
      6
      .center-absolute {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      }
    • 确保父容器的position属性设置为relative
  3. 使用table-cell
    • 将父元素设置为display: table;,子元素设置为display: table-cell;,并使用vertical-align: middle;
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      .center-table {
      display: table;
      width: 100%;
      height: 100%;
      }
      .center-table-cell {
      display: table-cell;
      text-align: center;
      vertical-align: middle;
      }
  4. 使用margintransform
    • 对于已知尺寸的元素,可以使用margintransform属性来实现居中。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      .center-margin-transform {
      position: absolute;
      top: 50%;
      left: 50%;
      width: 100px;
      height: 100px;
      margin-top: -50px; /* 元素高度的一半 */
      margin-left: -50px; /* 元素宽度的一半 */
      }
      选择合适的方法取决于具体的布局需求和浏览器兼容性要求。Flexbox和Grid是现代CSS布局中最灵活和强大的工具,推荐在可能的情况下使用它们。

流行的动画库

流行的动画库

流行的动画库有很多,它们可以帮助开发者创建各种引人入胜的动画效果。以下是一些广受欢迎的动画库:

  1. Anime.js:这是一个轻量级的JavaScript动画库,具有简单而强大的API,可以为CSS属性、SVG、DOM属性和JavaScript对象制作动画。
  2. Lottie:由Airbnb开发的Lottie是一个库,可以解析使用Bodymovin插件以JSON格式导出的Adobe After Effects动画,并在移动和网络应用程序上进行原生渲染。
  3. GreenSock动画平台(GSAP):GSAP是一个强大的JavaScript库,用于制作高性能动画,支持在React、Vue、WebGL和HTML画布中对颜色、字符串、运动路径等进行动画处理。
  4. Velocity.js:Velocity是一个动画引擎,具有与jQuery的$.animate()相同的API,提供彩色动画、变换、循环、缓动、SVG支持和滚动。
  5. ScrollReveal:ScrollReveal库可以让您在DOM元素进入或离开浏览器视口时轻松为其设置动画,提供了各种类型的优雅效果。
  6. Three.js:Three.js是一个用于显示复杂3D对象和动画的轻量级库,利用WebGL、SVG和CSS3D渲染器来创建引人入胜的三维体验。
  7. Animate.css:一个开箱即用型的跨浏览器动画库,可供你在项目中使用。
  8. Magic Animations CSS3:一组简单的动画,可以包含在你的网页或应用项目中。
  9. Granim.js:使用这个小型JavaScript库创建流畅且交互式的渐变动画。
  10. Vanta.js:只需几行代码即可制作动画网站背景。

盒模型

盒模型

盒模型

CSS盒模型(Box Model)是CSS中用于描述元素在页面中如何布局的一个基本概念。每个HTML元素都可以被视为一个矩形的盒子,这个盒子由四个部分组成:内容区域(content)、内边距(padding)、边框(border)和外边距(margin)。以下是盒模型的详细解释:

一、盒模型的四个部分

  1. 内容区域(Content)
    • 内容区域是盒子的核心部分,用于显示元素的实际内容,如文本、图片等。内容区域的大小可以通过widthheight属性来设置。
    • 示例:
      1
      2
      3
      4
      .box {
      width: 300px;
      height: 200px;
      }
  2. 内边距(Padding)
    • 内边距是内容区域和边框之间的空间。内边距可以增加元素内容和边框之间的间距,使内容看起来更舒适。内边距可以通过padding属性来设置,可以分别设置上、右、下、左四个方向的内边距。
    • 示例:
      1
      2
      3
      4
      5
      .box {
      padding: 20px; /* 上右下左都是20px */
      padding: 10px 20px; /* 上下10px,左右20px */
      padding: 10px 20px 30px 40px; /* 上10px,右20px,下30px,左40px */
      }
  3. 边框(Border)
    • 边框是围绕内容区域和内边距的一层,用于定义元素的边界。边框可以通过border属性来设置,可以分别设置边框的宽度、样式和颜色。
    • 示例:
      1
      2
      3
      4
      5
      6
      .box {
      border: 1px solid black; /* 1px宽的黑色实线边框 */
      border-width: 2px; /* 边框宽度 */
      border-style: dashed; /* 边框样式为虚线 */
      border-color: red; /* 边框颜色为红色 */
      }
  4. 外边距(Margin)
    • 外边距是元素与相邻元素之间的空间。外边距可以通过margin属性来设置,可以分别设置上、右、下、左四个方向的外边距。外边距可以为负值,用于创建重叠效果。
    • 示例:
      1
      2
      3
      4
      5
      .box {
      margin: 20px; /* 上右下左都是20px */
      margin: 10px 20px; /* 上下10px,左右20px */
      margin: 10px 20px 30px 40px; /* 上10px,右20px,下30px,左40px */
      }

二、盒模型的计算方式

  1. 标准盒模型(W3C盒模型)
    • 在标准盒模型中,元素的总宽度和高度计算方式如下:
      • 总宽度 = width + padding-left + padding-right + border-left-width + border-right-width + margin-left + margin-right
      • 总高度 = height + padding-top + padding-bottom + border-top-width + border-bottom-width + margin-top + margin-bottom
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      .box {
      width: 300px;
      height: 200px;
      padding: 20px;
      border: 10px solid black;
      margin: 30px;
      }
      • 总宽度 = 300px + 20px + 20px + 10px + 10px + 30px + 30px = 420px
      • 总高度 = 200px + 20px + 20px + 10px + 10px + 30px + 30px = 330px
  2. IE盒模型(怪异盒模型)
    • 在IE盒模型中,widthheight属性包括了内容区域、内边距和边框的宽度,但不包括外边距。这种模型在早期的IE浏览器中使用,现在可以通过设置box-sizing属性来启用。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      .box {
      width: 300px;
      height: 200px;
      padding: 20px;
      border: 10px solid black;
      margin: 30px;
      box-sizing: border-box; /* 启用IE盒模型 */
      }
      • 总宽度 = 300px + 30px + 30px = 360px
      • 总高度 = 200px + 30px + 30px = 260px

三、box-sizing属性

  • box-sizing属性用于指定盒模型的计算方式,取值有三种:
    • content-box:默认值,使用标准盒模型。
    • border-box:使用IE盒模型,widthheight包括内容区域、内边距和边框的宽度。
    • padding-boxwidthheight包括内容区域和内边距的宽度,但不包括边框的宽度。

四、实际应用

  1. 标准盒模型
    • 适用于需要精确控制内容区域大小的场景。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      .box {
      width: 300px;
      height: 200px;
      padding: 20px;
      border: 10px solid black;
      margin: 30px;
      }
  2. IE盒模型
    • 适用于需要固定元素总宽度和高度的场景,特别是当内边距和边框宽度变化时,元素的总宽度和高度保持不变。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      .box {
      width: 300px;
      height: 200px;
      padding: 20px;
      border: 10px solid black;
      margin: 30px;
      box-sizing: border-box;
      }

通过理解和使用CSS盒模型,可以更灵活地控制元素的布局和尺寸,从而实现各种复杂的页面设计。

流布局

流布局(Flow Layout),也称为“文档流”或“正常文档流”,是CSS中最基础的布局方式。它基于HTML文档的结构顺序,按照从上到下、从左到右的自然顺序排列元素。流布局是网页布局的默认行为,不需要额外的CSS设置。

1. 流布局的基本特性

1.1 块级元素(Block-level Elements)

块级元素是流布局中的主要元素类型,具有以下特点:

  • 独占一行:每个块级元素默认会占据一整行,不会与其他块级元素共享行空间。
  • 宽度和高度:可以设置宽度(width)和高度(height)。默认情况下,块级元素的宽度会自动扩展到父元素的宽度。
  • 内边距和外边距:可以设置内边距(padding)和外边距(margin),这些属性会影响元素的总尺寸。
  • 常见块级元素<div><p><h1>-<h6><ul><ol><li><form>等。

1.2 行内元素(Inline Elements)

行内元素是流布局中的另一种元素类型,具有以下特点:

  • 在同一行显示:多个行内元素可以在同一行内显示,直到行空间不足时才会换行。
  • 宽度和高度:行内元素的宽度和高度由其内容决定,不能通过CSS直接设置。
  • 内边距和外边距:可以设置内边距(padding),但外边距(margin)只对水平方向生效。
  • 常见行内元素<span><a><img><strong><em>等。

1.3 行内块元素(Inline-block Elements)

行内块元素是行内元素和块级元素的结合体,具有以下特点:

  • 在同一行显示:多个行内块元素可以在同一行内显示,直到行空间不足时才会换行。
  • 宽度和高度:可以设置宽度(width)和高度(height),类似于块级元素。
  • 内边距和外边距:可以设置内边距(padding)和外边距(margin),这些属性会影响元素的总尺寸。
  • 应用场景:常用于需要在同一行显示多个元素,同时又需要精确控制尺寸的场景。

2. 流布局的布局规则

2.1 块级元素的排列

  • 块级元素会按照HTML文档的顺序垂直排列。
  • 每个块级元素默认占据一整行,即使其宽度小于父元素的宽度。
  • 块级元素的宽度默认为父元素的宽度,可以通过设置 width 属性来改变。
  • 块级元素的外边距(margin)会影响其与其他元素的间距。

2.2 行内元素的排列

  • 行内元素会按照HTML文档的顺序水平排列。
  • 行内元素的宽度和高度由其内容决定,不能通过CSS直接设置。
  • 行内元素的外边距(margin)只对水平方向生效,垂直方向的外边距会被忽略。
  • 行内元素的内边距(padding)会影响其内容与边框之间的间距。

2.3 行内块元素的排列

  • 行内块元素会按照HTML文档的顺序水平排列。
  • 行内块元素的宽度和高度可以由CSS设置。
  • 行内块元素的外边距(margin)和内边距(padding)都会生效,但外边距的垂直方向可能会出现合并(margin collapsing)。

3. 流布局的控制方法

3.1 使用 display 属性

通过设置 display 属性,可以改变元素的显示类型:

  • display: block;:将元素设置为块级元素。
  • display: inline;:将元素设置为行内元素。
  • display: inline-block;:将元素设置为行内块元素。
  • display: none;:隐藏元素,元素不会占据空间。

3.2 使用 float 属性

float 属性可以使元素脱离文档流,浮动到左侧或右侧,从而实现多列布局:

  • float: left;:元素浮动到左侧。
  • float: right;:元素浮动到右侧。
  • float: none;:元素不浮动(默认值)。
    注意:浮动元素会脱离文档流,可能导致父元素高度塌陷。需要通过清除浮动(clear: both;)来解决。

3.3 使用 clear 属性

clear 属性用于清除浮动元素的影响:

  • clear: left;:清除左侧浮动元素的影响。
  • clear: right;:清除右侧浮动元素的影响。
  • clear: both;:清除左右两侧浮动元素的影响。
  • clear: none;:不清除任何浮动元素的影响(默认值)。

4. 流布局的优缺点

优点

  • 简单易用:流布局是CSS的默认布局方式,不需要额外的设置。
  • 语义化:符合HTML文档的结构顺序,易于理解和维护。
  • 兼容性好:流布局在所有浏览器中都能正常工作,无需额外的兼容性处理。

缺点

  • 灵活性差:流布局的排列顺序固定,难以实现复杂的布局需求。
  • 依赖HTML结构:布局效果高度依赖HTML文档的结构顺序,调整布局可能需要修改HTML代码。
  • 浮动问题:使用 float 时,可能会导致父元素高度塌陷,需要额外的清除浮动处理。

5. 流布局的典型应用场景

5.1 文章页面

流布局非常适合用于文章页面,其中内容以段落(<p>)、标题(<h1>-<h6>)和列表(<ul><ol>)的形式呈现。这些元素默认为块级元素,自然地按照文档顺序垂直排列。

5.2 导航栏

导航栏通常由多个链接(<a>)组成,可以通过设置 display: inline-block; 将链接排列在同一行,实现水平导航栏。

5.3 侧边栏布局

通过使用 float 属性,可以将侧边栏(<aside>)浮动到页面的左侧或右侧,同时将主要内容(<main>)浮动到另一侧,从而实现两列布局。

6. 流布局的代码示例

示例 1:文章页面

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
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>流布局示例</title>
<style>
body {
font-family: Arial, sans-serif;
}
h1 {
color: #333;
}
p {
line-height: 1.5;
margin-bottom: 10px;
}
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 5px;
}
</style>
</head>
<body>
<article>
<h1>文章标题</h1>
<p>这是文章的第一段内容。</p>
<p>这是文章的第二段内容。</p>
<ul>
<li>列表项 1</li>
<li>列表项 2</li>
<li>列表项 3</li>
</ul>
</article>
</body>
</html>

示例 2:导航栏

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>导航栏示例</title>
<style>
nav {
background-color: #333;
padding: 10px;
}
nav a {
display: inline-block;
color: white;
text-decoration: none;
margin-right: 10px;
padding: 5px 10px;
border-radius: 5px;
}
nav a:hover {
background-color: #555;
}
</style>
</head>
<body>
<nav>
<a href="#">首页</a>
<a href="#">关于我们</a>
<a href="#">服务</a>
<a href="#">联系我们</a>
</nav>
</body>
</html>

示例 3:两列布局

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>两列布局示例</title>
<style>
body {
font-family: Arial, sans-serif;
}

00 electron

01 electron 项目实战

环境搭建

1
2
npm init
npm install -D electron@30.0.0

初始化

package. json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "name": "eva",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "dev":"electron .",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "loyalvi",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "electron": "^30.0.0"
  }
}

index. js

1
2
3
4
5
6
7
8
9
10
11
const {app,BrowserWindow} = require('electron')
const minWindow = function(){
    let win = new BrowserWindow({
        width:800,
        height:600
    })
    win.loadFile('./page/index.html')
}
app.whenReady().then(() => {
    minWindow()
})

powershell

1
npm run dev

渲染进程和主进程的通信

ipcMain<->ipcRenderer

webPreferences

主进程配置后,可以在渲染进程使用 node 代码,安全性差不推荐。

1
2
3
4
webPreferences:{
nodeIntegration:true,//渲染进程使用 node 模块(required)
contextlsolation:false//关闭js隔离,可以在渲染进程使用electron模块
}

不考虑安全性,可以直接在渲染进程使用 ipcRenderer 方法直接和主进程通信

预加载

ipcMain<->preload<->ipcRenderer

主进程

index. js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const {app,BrowserWindow,ipcMain} = require('electron')
const path = require('node:path')
const minWindow = function(){
    let win = new BrowserWindow({
        width:800,
        height:600,
        webPreferences:{
            preload:path.join(__dirname,'./preload.js'),
        }
    })
    win.loadFile('./page/index.html')
    ipcMain.on('open-dev-tools',()=>{
        win.webContents.openDevTools()
    })
    ipcMain.on('close-dev-tools',()=>{
        win.webContents.closeDevTools()
    });
    ipcMain.handle('get-version',()=>{
        return process.versions;
    })
}
app.whenReady().then(() => {
    minWindow()
})

preload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const {ipcRenderer,contextBridge} = require('electron')
contextBridge.exposeInMainWorld('tools',{
    ipcSend:function (mes,...args){
        ipcRenderer.send(mes,...args)
    },
    ipcInvoke:function (msg){
        return ipcRenderer.invoke(msg);
    }
})
contextBridge.exposeInMainWorld('versions',{
    chrome:function (){
        return process.versions.chrome;
    },
    electron:function (){
        return process.versions.electron;
    },
    node:function (){
        return process.versions.node;
    }
})

渲染进程

index. html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 预加载js把方法暴露到windows对象里
let openDevToolsEl = document.getElementById('open-dev-tools');
openDevToolsEl.addEventListener('click',()=>{
window.tools.ipcSend('open-dev-tools')
})
let closeDevToolsEl = document.getElementById('close-dev-tools');
closeDevToolsEl.addEventListener('click',()=>{
window.tools.ipcSend('close-dev-tools')
})
let getVersionInfoEl = document.getElementById('get-version-info');
getVersionInfoEl.addEventListener('click',()=>{
document.getElementById('chrome-version').innerText = window.versions.chrome();
document.getElementById('election-version').innerText = window.versions.electron();
document.getElementById('node-version').innerText = window.versions.node();
})

webview

仍然涉及渲染进程和主进程的通信

主进程

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
34
35
36
37
38
39
40
41
42
43
44
const {app,BrowserWindow,ipcMain,WebContentsView} = require('electron')
const path = require('node:path')
const minWindow = function(){
    let win = new BrowserWindow({
        width:800,
        height:600,
        webPreferences:{
            preload:path.join(__dirname,'./preload.js'),
        }
    })
    win.loadFile('./page/search.html')
    win.setMenu(null)
    let [width,height] = win.getContentSize();
    let webview = null;
    ipcMain.on('create-webview',()=>{
        if(webview === null){
            webview = new WebContentsView();
            webview.setBounds({
                x:0,
                y:40,
                width:width,
                height:height - 40,
            })
            win.contentView.addChildView(webview)
        }
    })
    ipcMain.on('search-loadurl',(event,url)=>{
        webview.webContents.loadURL(url);
    })
    win.on('resize',()=>{
        let [width,height] = win.getContentSize();
        if(webview !== null){
            webview.setBounds({
                x:0,
                y:40,
                width:width,
                height:height - 40,
            })
        }
    })
}
app.whenReady().then(() => {
    minWindow()
})

preload

1
2
3
4
5
6
7
8
9
const {ipcRenderer,contextBridge} = require('electron')
contextBridge.exposeInMainWorld('tools',{
    ipcSend:function (mes,...args){
        ipcRenderer.send(mes,...args)
    },
    ipcInvoke:function (msg){
        return ipcRenderer.invoke(msg);
    }
})

渲染进程

1
2
3
4
5
6
7
8
let submitEl = document.getElementById('submit');
submitEl.addEventListener('click',()=>{
window.tools.ipcSend('create-webview')
let type = document.getElementById('type').value;
let info = document.getElementById('info').value;
let url = type + info;
window.tools.ipcSend('search-loadurl',url)
})

01 electron+vue3

01 electron+vue3

创建项目

1
2
3
4
npm create vue@latest
npm install
npm install electron@30.0.0
npm install vite-plugin-electron -D

实例

一个简单的 vue3+electron 融合项目,只和这几个文件相关,ASCII 树如下

1
2
3
4
5
6
├── dist
│ └── index.js
├── electron
│ └── index.js
├── package.json
└── vite.config.js

electron/index. js 是 electron 的入口文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import {
    app,
    BrowserWindow
} from 'electron'
const createMainWindow = function () {
    const win = new BrowserWindow({
        title:"title666",
        width:800,
        height:600,
        webPreferences: {
        }
    })
    win.loadURL(process.env['VITE_DEV_SERVER_URL'])
}
app.whenReady().then(res=>{
    createMainWindow()
})

package.json 配置文件

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
{
  "name": "eva",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  //配置了electron项目的入口文件,
  "main": "dist/index.js",//这里选择的vite打包好的目录
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "electron": "^30.0.0",
    "pinia": "^2.3.1",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.2.1",
    "vite": "^6.0.11",
    "vite-plugin-electron": "^0.29.0",
    "vite-plugin-vue-devtools": "^7.7.0"
  }
}

vite.config.js vite 的配置文件

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
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import electron from 'vite-plugin-electron'
// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
    //使用vite-electron插件,在dev的同时启动electron
    electron({
    //入口
      entry: 'electron/index.js',
      vite: {
        build: {
        //vite打包的路径默认为dist-electron
            outDir: './dist'
        }
      }
    })
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
})

02 electron打包

02 electron打包

相关文件

1
2
3
4
5
6
7
├── dist                   vite&vite-electron打包的vue3&electron项目
├── electron electron代码
│ └── index.js
├── release electron-builder打包的dist
├── electron-builder.json5 electron-builder的配置文件
├── package.json
└── vite.config.js

安装electron-builder

1
npm install electron-builder -D

electron-builder.json5

1
2
3
4
5
6
7
8
9
10
{
appId:"com.lovi.electron",
productName:"eva", //项目名称
directories:{
output:"release/${version}" //package.json的version
},
files:[
"dist"
]
}

package.json

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
{
  "name": "eva",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "main": "dist/index.js",
  "scripts": {
    "dev": "vite",
    "build": "vite build && electron-builder -mwl",
    "build:linux": "vite build && electron-builder --linux tar.xz",
    "build:win32": "vite build && electron-builder --windows nsis:ia32",
    "build:win64": "vite build && electron-builder --windows nsis:x64",
    "build:mac": "vite build && electron-builder --mac",
    "preview": "vite preview"
  },
  "dependencies": {
    "pinia": "^2.3.1",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "electron": "^30.0.0",
    "@vitejs/plugin-vue": "^5.2.1",
    "electron-builder": "^25.1.8",
    "vite": "^6.0.11",
    "vite-plugin-electron": "^0.29.0",
    "vite-plugin-vue-devtools": "^7.7.0"
  }
}

在package.json 文件中添加打包脚本,不同客户端打包脚本不一样,同时需要在对应环境打包,运行脚本时需要注意网络环境以及管理员权限。

electron/index. js

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
import {
    app,
    BrowserWindow
} from 'electron'
import path from 'node:path'
import {fileURLToPath} from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const createMainWindow = function () {
    const win = new BrowserWindow({
        title:"title666",
        width:800,
        height:600,
        webPreferences: {
        }
    })
    //开发环境和真实环境路径区别
    if(process.env.NODE_ENV === 'development'){
        win.loadURL(process.env['VITE_DEV_SERVER_URL'])
    }else{
        win.loadFile(path.join(__dirname,"./index.html"))
    }
}
app.whenReady().then(res=>{
    createMainWindow()
})

asar

静态资源,前端代码通过 asar 加密,真实环境文件操作需要注意

不同路径

03 electron更新

03 electron更新

一、用户如何获取到实际软件,这应出于实际项目出发,考虑如何分发软件安装包给用户,
如,用户从官网下载,或者各类途径分享可下载此软件安装包的链接,可以参考常见的应用如:QQ、微信、百度网盘等。
二、项目更新,可有多种实现手段,如请求某个接口,对比用户本地版本和服务器端最新版本,
如有不同,那么提示用户升级,同样应处于实际项目出发,如拥有云服务器,可将新版本放置服务器指定路径,
根据指定规则进行升级。或使用现成的electron官方的 update-electron-app 工具
如没有自己的服务器,可以考虑 update-electron-app + github 仓库的解决方案,
但是需要注意的是,如果使用 update-electron-app + github的方案,
要考虑到部分地区、部分运营商、部分场景下的用户会有无法访问 github的情况。
由于 update-electron-app 实现 应用程序的更新需要较多流程和技术手段,同学可参考electron官方提供的文档
https://www.electronjs.org/zh/docs/latest/tutorial/更新

Electron

要实现 Electron 应用的打包和发布更新,可以按照以下步骤进行:

1. 打包应用

使用 Electron 打包工具(如 electron-builderelectron-forge)将应用打包为可安装的格式。确保在打包配置中正确设置应用的版本号和其他必要信息。

2. 选择更新服务器

根据你的需求选择更新服务器:

  • 使用公开的 GitHub 仓库:如果你的应用是开源的,可以使用 update.electronjs.org 提供的免费更新服务。
  • 私有更新服务器:如果你的应用是私有的,或者需要更复杂的更新管理,可以部署自己的更新服务器,例如使用 Hazelelectron-release-server 或其他自定义解决方案。

3. 发布更新

使用 GitHub Releases

  1. 配置 GitHub Publisher
    • 安装 @electron-forge/publisher-github 插件。
    • forge.config.js 中配置发布目标。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    module.exports = {
    publishers: [
    {
    name: '@electron-forge/publisher-github',
    config: {
    repository: {
    owner: 'github-user-name',
    name: 'github-repo-name'
    },
    prerelease: false,
    draft: true
    }
    }
    ]
    }
  2. 生成 GitHub 访问令牌:创建一个个人访问令牌(PAT),并将其存储在环境变量 GITHUB_TOKEN 中。
  3. 运行发布命令
    JSON复制
    1
    2
    3
    "scripts": {
    "publish": "electron-forge publish"
    }
    运行 npm run publish 将打包后的应用发布到 GitHub Releases。

使用自定义更新服务器

  1. 上传更新文件:将打包后的安装包和更新元数据(如 latest.ymlRELEASES 文件)上传到你的服务器。
  2. 配置更新路径:在应用的主进程中设置 autoUpdater 的更新源。
    1
    2
    3
    4
    const { app, autoUpdater } = require('electron');
    const server = 'https://your-update-server.com';
    const url = `${server}/update/${process.platform}/${app.getVersion()}`;
    autoUpdater.setFeedURL({ url });

4. 配置自动更新

在应用的主进程中使用 autoUpdater 模块来检查和应用更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
autoUpdater.on('checking-for-update', () => console.log('Checking for update...'));
autoUpdater.on('update-available', () => console.log('Update available.'));
autoUpdater.on('update-not-available', () => console.log('No update available.'));
autoUpdater.on('update-downloaded', () => {
// 提示用户重启应用以安装更新
dialog.showMessageBox({
type: 'info',
buttons: ['Restart', 'Later'],
message: 'Update downloaded. Restart to apply.'
}).then((response) => {
if (response.response === 0) autoUpdater.quitAndInstall();
});
});
// 定期检查更新
setInterval(() => autoUpdater.checkForUpdates(), 60000);

5. 测试更新流程

在非开发环境下运行应用,模拟更新过程以确保一切正常。这包括检查更新、下载更新和安装更新等步骤。

注意事项

  • 安全性:确保使用 HTTPS 进行通信,并对更新包进行签名验证。
  • 用户体验:尽量使更新过程对用户透明且无缝。
  • 签名:在 macOS 上,应用必须签名后才能使用自动更新功能。
    通过以上步骤,你可以实现 Electron 应用的打包、发布和自动更新功能。

WebGis

WebGis

# 五个前端WebGis地图框架
leaflet

不同

特性 Leaflet OpenLayers Mapbox Cesium ArcGIS
功能特点 轻量级、用户友好,适合基本地图显示和交互 全面的 GIS 支持,适合复杂地图操作和数据处理 自定义样式、数据可视化,适合高级地图功能 3D 地图渲染,高性能,适合三维可视化 二三维一体化,适合复杂 GIS 应用
开源情况 开源(BSD 许可证) 开源(MIT 许可证) 非开源(部分功能需付费) 开源(Apache 2.0 许可证,部分高级功能需付费) 非开源(ESRI 提供)
包体积 约 40 KB 约 300 KB 约 1 MB 约 1.5 MB -
市场占有率 开源项目广泛,适合轻量级应用 在 GIS 领域应用广泛 市场占有率较高,适合企业级应用 在 3D 可视化领域有较高占有率 在 WebGIS 领域有较高占有率
适用人群 初学者、快速开发 GIS 开发者、需要高级地图功能的开发者 企业开发者、对地图样式要求高的开发者 3D 开发者、高端可视化需求 专业 GIS 开发者
学习曲线 简单易用,适合新手 学习曲线较平缓,适合有一定基础的开发者 中等难度,需熟悉其生态系统 上手难度较大,需掌握 WebGL 上手难度较大,需熟悉 ESRI 体系
优势 轻量级、移动设备支持好 功能全面、支持多种数据格式 可视化效果好、支持 2D 和 2.5D 3D 渲染能力强、支持大规模数据 二三维一体化,适合复杂 GIS
缺点 功能有限,复杂应用支持不足 地图样式定制较难 需付费,依赖网络 结构复杂,性能要求高 非开源,学习成本高
根据项目需求选择合适的地图库:
  • 如果需要快速开发轻量级地图应用,Leaflet 是不错的选择。
  • 如果项目涉及复杂的 GIS 功能,OpenLayers 更适合。
  • 如果需要高度定制化的地图样式和强大的可视化功能,Mapbox 是较好的选择。
  • 如果项目需要 3D 地图渲染和高性能可视化,Cesium 是最佳选择。
  • 如果项目需要与 ArcGIS Server 集成,ArcGIS JavaScript API 是首选。