14、Web 组件与 UI 元素开发指南

Web 组件与 UI 元素开发指南

1. 反馈组件开发

当用户选择反馈选项时,可以监听自定义反馈事件以获得通知。以下是监听反馈事件的示例代码:

document.querySelector('feedback-rating').addEventListener('feedback', event => {
  // Get the value of the feedback component's "helpful" property and send it to an
  // endpoint with a POST request.
  fetch('/api/analytics/feedback', {
    method: 'POST',
    body: JSON.stringify({ helpful: event.target.helpful }),
    headers: {
      'Content-Type': 'application/json'
    }
  });
});

反馈评分组件会显示一个提示和两个按钮,用户根据认为网站内容是否有用来点击其中一个按钮。点击事件监听器使用了事件委托,只需添加一个监听器来响应反馈提示内的任何点击。若点击的元素没有 data-helpful 属性,则用户未点击反馈按钮,不做处理;否则,将字符串值转换为布尔值并设置为自定义元素的属性,还会触发一个可在其他地方监听的事件。

为使事件能从阴影 DOM 传递到常规 DOM,必须设置 composed: true 选项,否则添加到自定义元素的事件监听器不会触发。事件触发后,可通过 event.target 属性检查反馈元素的 helpful 属性,以确定用户点击了哪个反馈按钮。

由于样式和标记包含在阴影 DOM 中,CSS 规则不会影响阴影 DOM 之外的任何元素。不过,传递给组件插槽的内容可由全局 CSS 规则设置样式,因为插槽内容留在标准或轻量级 DOM 中。

2. 个人资料卡片组件开发

若想创建一个可重复使用的卡片组件来展示用户资料,可使用 Web 组件中的插槽将内容传递到特定区域。
- 定义模板

const template = document.createElement('template');
template.innerHTML = `
  <style>
    :host {
      display: grid;
      border: 1px solid #ccc;
      border-radius: 5px;
      padding: 8px;
      grid-template-columns: auto 1fr;
      column-gap: 16px;
      align-items: center;
      margin: 1rem;
    }
    .photo {
      border-radius: 50%;
      grid-row: 1 / span 3;
    }
    .name {
      font-size: 2rem;
      font-weight: bold;
    }
    .title {
      font-weight: bold;
    }
  </style>
  <div class="photo"><slot name="photo"></slot></div>
  <div class="name"><slot name="name"></slot></div>
  <div class="title"><slot name="title"></slot></div>
  <div class="bio"><slot></slot></div>
`;

此模板有三个命名插槽( photo name title )和一个用于传记的默认插槽。
- 组件实现

class ProfileCard extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}
customElements.define('profile-card', ProfileCard);
  • 使用组件
<profile-card>
  <img slot="photo" src="/api/portraits/chavez.jpg" />
  <div slot="name">Phillip Chavez</div>
  <div slot="title">CEO</div>
  <p>Philip is a great CEO.</p>
</profile-card>
<profile-card>
  <img slot="photo" src="/api/portraits/lynch.jpg" />
  <div slot="name">Jamie Lynch</div>
  <div slot="title">Vice President</div>
  <p>Jamie is a great vice president.</p>
</profile-card>

在 CSS 样式中, :host 选择器代表应用于自定义元素阴影宿主的样式,即阴影 DOM 所附着的元素。通过这个例子,可看到 Web 组件能创建可重复使用的内容和布局,插槽是插入内容的强大工具。

3. 懒加载图像组件开发

若需要一个包含图像的可重复使用组件,且该图像在滚动到视口时才加载,可使用 IntersectionObserver 等待元素滚动到视图中,然后设置包含图像的 src 属性。
- 组件实现

class LazyImage extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    this.image = document.createElement('img');
    shadowRoot.appendChild(this.image);
  }
  connectedCallback() {
    const observer = new IntersectionObserver(entries => {
      if (entries[0].isIntersecting) {
        console.log('Loading image');
        this.image.src = this.getAttribute('src');
        observer.disconnect();
      }
    });
    observer.observe(this);
  }
}
customElements.define('lazy-image', LazyImage);
  • 使用组件
<lazy-image src="https://placekitten.com/200/138"></lazy-image>

元素滚动到视图中后, IntersectionObserver 回调会获取 src 属性并设置为图像的 src 属性,触发图像加载。不过,较新的浏览器支持 img 标签的 loading="lazy" 属性,也能实现相同效果。

4. 展开/折叠组件开发

若想通过点击按钮显示或隐藏某些内容,可构建一个展开/折叠 Web 组件。该组件有两部分:切换内容的按钮和内容本身,每部分都有一个插槽,默认插槽用于内容,命名插槽用于按钮,还可通过更改 open 属性的值以编程方式展开或折叠组件。
- 定义模板

const template = document.createElement('template');
template.innerHTML = `
  <div>
    <button type="button" class="toggle-button">
      <slot name="title"></slot>
    </button>
    <div class="content">
      <slot></slot>
    </div>
  </div>
`;
  • 组件实现
class Disclosure extends HTMLElement {
  // Watch the 'open' attribute to react to changes.
  static observedAttributes = ['open'];
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    this.content = this.shadowRoot.querySelector('.content');
  }
  connectedCallback() {
    this.content.hidden = !this.hasAttribute('open');
    this.shadowRoot.querySelector('.toggle-button')
      .addEventListener('click', () => {
        if (this.hasAttribute('open')) {
          // Content is currently showing; remove the 'open'
          // attribute and hide the content.
          this.removeAttribute('open');
          this.content.hidden = true;
        } else {
          // Content is currently hidden; add the 'open' attribute
          // and show the content.
          this.setAttribute('open', '');
          this.content.hidden = false;
        }
      });
  }
  attributeChangedCallback(name, oldValue, newValue) {
    // Update the content's hidden state based on the new attribute value.
    if (newValue !== null) {
      this.content.hidden = false;
    } else {
      this.content.hidden = true;
    }
  }
}
// The element name must be hyphenated.
customElements.define('x-disclosure', Disclosure);

为避免子内容闪烁,需添加一点 CSS:

x-disclosure:not(:defined) {
  display: none;
}
  • 使用组件
<x-disclosure>
  <div slot="title">Details</div>
  This is the detail child content that will be expanded or collapsed
  when clicking the title button.
</x-disclosure>

展开/折叠组件使用 open 属性来确定是否显示子内容。点击切换按钮时,根据当前状态添加或删除该属性,然后有条件地应用 hidden 属性到子内容。也可通过添加或删除 open 属性以编程方式切换子内容。

5. 样式按钮组件开发

若想创建一个具有不同样式选项的可重复使用按钮组件,有三种按钮变体:默认变体(灰色背景)、“主要”变体(蓝色背景)和“危险”变体(红色背景)。
- 创建模板

const template = document.createElement('template');
template.innerHTML = `
  <style>
    button {
      background: #333;
      padding: 0.5em 1.25em;
      font-size: 1rem;
      border: none;
      border-radius: 5px;
      color: white;
    }
    button.primary {
      background: #2563eb;
    }
    button.danger {
      background: #dc2626;
    }
  </style>
  <button>
    <slot></slot>
  </button>
`;
  • 组件实现
class StyledButton extends HTMLElement {
  static observedAttributes = ['variant', 'type'];
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    this.button = this.shadowRoot.querySelector('button');
  }
  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'variant') {
      this.button.className = newValue;
    } else if (name === 'type') {
      this.button.type = newValue;
    }
  }
}
customElements.define('styled-button', StyledButton);
  • 使用组件
<styled-button id="default-button" type="button">Default</styled-button>
<styled-button id="primary-button" type="button" variant="primary">
  Primary
</styled-button>
<styled-button id="danger-button" type="button" variant="danger">
  Danger
</styled-button>

通过将按钮元素的类名设置为变体名称来应用样式,相应的 CSS 规则会应用所需的背景颜色。添加点击监听器时,可直接添加到 styled-button 元素,得益于事件委托,点击底层按钮时会触发该监听器。

以下是创建不同组件的流程总结:

graph LR
    A[反馈组件] --> B[监听反馈事件]
    B --> C[处理点击事件]
    C --> D[传递事件到常规 DOM]
    E[个人资料卡片组件] --> F[定义模板]
    F --> G[实现组件]
    G --> H[使用组件]
    I[懒加载图像组件] --> J[使用 IntersectionObserver]
    J --> K[设置图像 src]
    L[展开/折叠组件] --> M[定义模板]
    M --> N[实现组件]
    N --> O[添加 CSS 避免闪烁]
    O --> P[使用组件]
    Q[样式按钮组件] --> R[创建模板]
    R --> S[实现组件]
    S --> T[使用组件]
6. 现代浏览器内置 UI 元素

现代浏览器有一些强大的内置 UI 元素,可用于应用程序。
| UI 元素 | 描述 |
| ---- | ---- |
| 对话框(Dialogs) | 弹出对话框是许多应用程序的主要组成部分,提供反馈并提示输入。现代浏览器的 <dialog> 元素自带背景覆盖页面其余部分,可通过 CSS 设置样式。需自行添加标题、按钮等内容,并处理按钮点击事件。关闭对话框可调用 close 方法,可传递可选参数作为“返回值”,通过 returnValue 属性检查。 |
| 详情(Details) | <details> 元素的内容可折叠,有摘要内容显示在交互式元素中,点击可显示或隐藏详细内容,可通过 CSS 设置样式并使用 JavaScript 切换可见性。 |
| 弹出框(Popovers) | 类似于对话框的弹出元素。点击弹出框外部会关闭它;弹出框可见时仍可与页面其余部分交互;可将任何 HTML 元素转换为弹出框。 |
| 通知(Notifications) | 现代浏览器有用于显示原生操作系统通知的 API,由 JavaScript 触发。用户必须授予权限才能发送通知,这些通知在应用程序运行时按需在 JavaScript 代码中创建。 |

7. 创建警告对话框

若想显示一个带有简单消息和“确定”按钮的对话框,可使用 <dialog> 元素和“确定”按钮。
- 定义 HTML

<dialog id="alert">
  <h2>Alert</h2>
  <p>This is an alert dialog.</p>
  <button type="button" id="ok-button">OK</button>
</dialog>
<button type="button" id="show-dialog">Show Dialog</button>
  • 添加 JavaScript
// Select the dialog, its OK button, and the trigger button elements.
const dialog = document.querySelector('#alert');
const okButton = document.querySelector('#ok-button');
const trigger = document.querySelector('#show-dialog');
// Close the dialog when the OK button is clicked.
okButton.addEventListener('click', () => {
  dialog.close();
});
// Show the dialog when the trigger button is clicked.
trigger.addEventListener('click', () => {
  dialog.showModal();
});

showModal 方法显示模态对话框,会阻止页面其余部分,焦点“被困”在对话框内;也可调用 show 方法显示无模态对话框,可在对话框打开时与页面其余部分交互。点击“确定”按钮或按 Escape 键可关闭对话框,可监听 cancel 事件捕获按 Escape 键的操作,关闭对话框会触发 close 事件。

对话框元素还有一些键盘可访问性功能,打开对话框时,第一个可聚焦元素自动获得焦点,可通过添加 autofocus 属性更改此行为。关闭对话框时,键盘焦点会返回触发按钮。可使用 CSS 设置对话框及其背景的样式,如使用 ::backdrop 伪元素设置背景颜色。

8. 创建确认对话框

若想提示用户确认操作,提示应显示问题并带有“确认”和“取消”按钮。具体实现需根据需求进一步处理按钮点击事件和逻辑。

通过以上介绍,我们了解了多种 Web 组件和现代浏览器内置 UI 元素的开发和使用方法,这些技术能帮助我们创建更丰富、交互性更强的 Web 应用程序。

Web 组件与 UI 元素开发指南

9. 不同组件开发的对比分析

为了更清晰地了解各个组件的特点和适用场景,我们对前面介绍的组件进行对比分析。
| 组件类型 | 主要功能 | 关键技术 | 适用场景 |
| ---- | ---- | ---- | ---- |
| 反馈组件 | 监听用户反馈选项,可将反馈数据发送到分析端点 | 自定义反馈事件、Fetch API | 需要收集用户对内容反馈的页面,如文章、产品介绍页等 |
| 个人资料卡片组件 | 展示用户资料,可重复使用 | Web 组件插槽 | 社交平台、员工介绍页面等需要展示用户信息的地方 |
| 懒加载图像组件 | 图像在滚动到视口时才加载,节省资源 | IntersectionObserver | 图片较多的页面,如相册、电商商品列表页 |
| 展开/折叠组件 | 通过点击按钮显示或隐藏内容 | 自定义元素属性、事件监听 | 表单中的高级选项、文章的详细内容等 |
| 样式按钮组件 | 创建具有不同样式的可重复使用按钮 | 自定义元素属性、CSS 类名 | 各种需要按钮交互的页面,如登录页、操作提示页等 |

10. 组件开发的注意事项

在进行组件开发时,有一些注意事项需要我们关注:
- 阴影 DOM 的使用 :阴影 DOM 可以封装组件的样式和结构,避免对外部元素产生影响。但在传递事件和样式时,需要注意设置相关选项,如 composed: true 让事件跨越阴影 DOM。
- 兼容性问题 :不同浏览器对 Web 组件和内置 UI 元素的支持程度不同。在使用新特性时,要查看兼容性数据,如使用 <dialog> 元素时可参考 CanIUse 网站。
- 性能优化 :对于懒加载图像组件等涉及资源加载的组件,要合理使用技术,避免不必要的资源浪费。

11. 组件开发的扩展思路

在实际应用中,我们可以对这些组件进行扩展和组合,以满足更多的需求。以下是一些扩展思路:
- 组合组件 :将个人资料卡片组件和反馈组件组合,在展示用户资料的同时收集用户对该资料的反馈。

<profile-card>
  <img slot="photo" src="/api/portraits/chavez.jpg" />
  <div slot="name">Phillip Chavez</div>
  <div slot="title">CEO</div>
  <p>Philip is a great CEO.</p>
  <feedback-rating></feedback-rating>
</profile-card>
  • 扩展功能 :为展开/折叠组件添加动画效果,让内容的显示和隐藏更加平滑。可以使用 CSS 动画或 JavaScript 动画库来实现。
.content {
  transition: height 0.3s ease-in-out;
  overflow: hidden;
}
12. 内置 UI 元素的使用技巧

对于现代浏览器的内置 UI 元素,有一些使用技巧可以提升用户体验:
- 对话框的返回值处理 :在使用对话框时,合理利用 close 方法的可选参数和 returnValue 属性,将用户的选择传递回页面。

const dialog = document.querySelector('#confirmation');
const confirmButton = document.querySelector('#confirm');
const cancelButton = document.querySelector('#cancel');

confirmButton.addEventListener('click', () => {
  dialog.close('confirmed');
});

cancelButton.addEventListener('click', () => {
  dialog.close('cancelled');
});

// 在打开对话框的地方检查返回值
dialog.addEventListener('close', () => {
  if (dialog.returnValue === 'confirmed') {
    // 执行确认操作
  } else if (dialog.returnValue === 'cancelled') {
    // 执行取消操作
  }
});
  • 详情元素的样式定制 :通过 CSS 对 <details> 元素进行样式定制,让其更加美观和符合页面风格。
details {
  border: 1px solid #ccc;
  border-radius: 5px;
  padding: 10px;
}

details summary {
  cursor: pointer;
  font-weight: bold;
}
13. 总结与展望

通过本文的介绍,我们详细了解了多种 Web 组件和现代浏览器内置 UI 元素的开发和使用方法。这些技术为我们创建丰富、交互性强的 Web 应用程序提供了有力支持。

在未来的 Web 开发中,Web 组件和内置 UI 元素的应用将会更加广泛。随着浏览器技术的不断发展,我们可以期待更多强大的功能和更便捷的开发方式。同时,我们也可以进一步探索组件之间的组合和扩展,创造出更加独特和实用的用户界面。

以下是一个总结不同组件开发步骤的 mermaid 流程图:

graph LR
    A[组件开发] --> B[选择组件类型]
    B --> C{反馈组件}
    B --> D{个人资料卡片组件}
    B --> E{懒加载图像组件}
    B --> F{展开/折叠组件}
    B --> G{样式按钮组件}
    C --> H[定义反馈事件监听]
    C --> I[处理反馈数据]
    D --> J[定义模板]
    D --> K[实现组件逻辑]
    E --> L[使用 IntersectionObserver]
    E --> M[设置图像加载]
    F --> N[定义模板和属性]
    F --> O[处理展开/折叠逻辑]
    G --> P[创建模板和样式]
    G --> Q[处理按钮属性]
    H --> R[完成开发]
    I --> R
    J --> R
    K --> R
    L --> R
    M --> R
    N --> R
    O --> R
    P --> R
    Q --> R

通过以上的总结和流程图,我们可以更清晰地看到组件开发的整体流程和不同组件的关键步骤。希望这些内容能帮助你在 Web 开发中更加熟练地运用这些技术。

内容概要:本文详细介绍了“秒杀商城”微服务架构的设计实战全过程,涵盖系统从需求分析、服务拆分、技术选型到核心功能开发、分布式事务处理、容器化部署及监控链路追踪的完整流程。重点解决了高并发场景下的超卖问题,采用Redis预减库存、消息队列削峰、数据库乐观锁等手段保障数据一致性,并通过Nacos实现服务注册发现配置管理,利用Seata处理跨服务分布式事务,结合RabbitMQ实现异步下单,提升系统吞吐能力。同时,项目支持Docker Compose快速部署和Kubernetes生产级编排,集成Sleuth+Zipkin链路追踪Prometheus+Grafana监控体系,构建可观测性强的微服务系统。; 适合人群:具备Java基础和Spring Boot开发经验,熟悉微服务基本概念的中高级研发人员,尤其是希望深入理解高并发系统设计、分布式事务、服务治理等核心技术的开发者;适合工作2-5年、有志于转型微服务或提升架构能力的工程师; 使用场景及目标:①学习如何基于Spring Cloud Alibaba构建完整的微服务项目;②掌握秒杀场景下高并发、超卖控制、异步化、削峰填谷等关键技术方案;③实践分布式事务(Seata)、服务熔断降级、链路追踪、统一配置中心等企业级中间件的应用;④完成从本地开发到容器化部署的全流程落地; 阅读建议:建议按照文档提供的七个阶段循序渐进地动手实践,重点关注秒杀流程设计、服务间通信机制、分布式事务实现和系统性能优化部分,结合代码调试监控工具深入理解各组件协作原理,真正掌握高并发微服务系统的构建能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值