现代化HTML

原生折叠面板

<details>

原生折叠面板

<details>+<summary>

做一个“点击展开/收起”的 FAQ 列表,以前要写 JS 监听 click 事件,切换 class,现在只需要

html
<details>
  <summary>点击查看更多详情</summary>
  <p>这里是隐藏的内容!可以是图片、文字或任何东西。</p>
  <p>只要给 details 加一个 open 属性,默认就是展开的。</p>
</details>

CSS 配合: 你可以用 details[open] 选择器来做展开动画!

1. 基本语法

html
<details>
  <summary>点击展开/收起</summary>
  <p>这里是展开后显示的内容。</p>
</details>

浏览器会自动渲染一个可点击的箭头图标,点击 <summary> 就能切换内容的显示/隐藏。

image-20250921201727082

2. 重要特性

元素 / 属性作用
<details>容器元素,包裹要展开/折叠的内容
<summary>可点击标题,控制展开/收起
open 属性控制默认是否展开,等同于 <details open>
toggle 事件当展开状态变化时触发,可用 JS 监听

3. 示例:默认展开 + JS 监听

html
<details open>
  <summary>展开的详情</summary>
  <p>默认就是展开的,因为加了 open 属性。</p>
</details>

<script>
  const details = document.querySelector('details');
  details.addEventListener('toggle', () => {
    console.log('当前状态:', details.open ? '展开' : '收起');
  });
</script>

4. 样式自定义

默认浏览器会加一个小箭头,可以通过 CSS 自定义样式:

css
details summary {
  cursor: pointer;
  font-weight: bold;
  list-style: none; /* 去掉默认小箭头 */
}
details[open] summary {
  color: #0070f3;
}

想要完全自定义箭头,可以隐藏默认标记:

css
summary::-webkit-details-marker {
  display: none; /* 去掉默认 marker */
}

然后自己加个图标或伪元素模拟展开/收起效果。


5. 特点和优势

零 JS:自带交互逻辑 ✅ 无障碍支持:自动处理键盘导航和 ARIA 属性 ✅ 可嵌套:可以多个 <details> 组成折叠列表 ✅ 状态可控:用 JS 改 open 属性就能展开/收起

来试着美化成这样吧:

image-20260213182618344

<dialog>

<dialog>是原生模态弹窗,以前做一个 Modal(弹窗),要处理 z-index、遮罩层、点击空白关闭、ESC 关闭……太累了。

原生 HTML5 <dialog> 元素 是浏览器内置的对话框组件,用来显示模态窗口或非模态弹出层,无需额外的 JS UI 库就能实现基本弹窗功能。

基本用法

html
<dialog id="myDialog">
  <p>这是一个原生对话框</p>
  <button id="closeBtn">关闭</button>
</dialog>

<button id="openBtn">打开对话框</button>

<script>
  const dialog = document.getElementById('myDialog');
  document.getElementById('openBtn').addEventListener('click', () => dialog.showModal());
  document.getElementById('closeBtn').addEventListener('click', () => dialog.close());
</script>
image-20250921195635475

关键特性

特性/方法作用
<dialog>定义一个对话框元素,默认是 display: none
open 属性表示对话框是否打开,手动加上 open 就能显示
show()打开对话框(非模态,页面其他元素仍可交互)
showModal()打开对话框(模态,阻止背景交互,并自动添加遮罩层)
close()关闭对话框
::backdrop伪元素,可自定义遮罩层样式
returnValue可存储关闭时返回的值 按下esc就是返回值为空

样式自定义

你可以用 CSS 美化 <dialog> 和遮罩层:

css
dialog {
  border: none;
  border-radius: 10px;
  padding: 20px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
dialog::backdrop {
  background: rgba(0, 0, 0, 0.5); /* 自定义遮罩 */
}

注意事项

兼容性:现代浏览器都支持(IE 不支持)。

<dialog> 默认是脱离文档流的,不会占空间。

如果要在表单里用,可以直接用 <form method="dialog">,关闭时表单值会被传给 returnValue

html
<dialog id="formDialog" open>
  <form method="dialog">
    <p><label>名字:<input name="username"></label></p>
    <menu>
      <button value="cancel">取消</button>
      <button value="confirm">确定</button>
    </menu>
  </form>
</dialog>

<script>
const dlg = document.getElementById("formDialog");
dlg.addEventListener("close", () => {
  console.log("用户操作:", dlg.returnValue);
});
</script>

image-20250921200011542

<datalist>

<datalist>就是原生自动补全,之前你想给输入框加一个“推荐选项”,但用户也能自己输别的。以前要用 Select2 这种重型插件。现在只需要给 Input 挂一个数据列表。

html
<label>选择你的浏览器:</label>
<input list="browsers" name="browser">

<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
</datalist>

效果: 用户点击输入框,会弹出下拉列表;用户输入 “Ch”,会自动筛选出 “Chrome”。零 JS!

<fieldset>

<fieldset> 是 HTML 原生的表单分组容器,用来把一组表单控件(input、select、textarea…)组织在一起,通常搭配 <legend> 元素作为分组标题。

它的主要作用是:

  • 语义化:告诉浏览器和无障碍工具“这是一组相关的表单控件”
  • 视觉样式:默认会画一个边框把这组控件框起来
  • 可禁用:可以一次性禁用整组表单控件

1. 基本用法

html
<form>
  <fieldset>
    <legend>个人信息</legend>
    <label>
      姓名:<input type="text" name="name">
    </label>
    <label>
      邮箱:<input type="email" name="email">
    </label>
  </fieldset>
</form>

image-20250921202824377

浏览器会自动渲染:

  • 外层一个边框框住整个 fieldset
  • legend 会显示在边框上方作为标题

2. 主要属性

属性作用
disabled禁用整个 fieldset 内的控件(不可编辑、不可提交)
form指定此 fieldset 属于哪个表单(即便不在 <form> 标签里)
name给 fieldset 命名,便于在 JS 或表单数据里识别

示例:禁用整个分组

html
<fieldset disabled>
  <legend>支付方式</legend>
  <label><input type="radio" name="pay" value="card">信用卡</label>
  <label><input type="radio" name="pay" value="cash">现金</label>
</fieldset>

所有单选按钮都会被禁用。


3. 样式自定义

你可以完全自定义外观,不一定要用默认边框:

css
fieldset {
  border: 2px solid #ccc;
  border-radius: 8px;
  padding: 1rem;
  margin-bottom: 1rem;
}

legend {
  font-weight: bold;
  padding: 0 0.5rem;
}

也可以用 border: none; 去掉默认边框,把它当普通分组容器使用。

image-20250921203835448


4. 特点和优势

无障碍友好:屏幕阅读器会把它作为一组控件一起朗读 ✅ 批量控制:可以一次禁用/启用整组表单控件 ✅ 语义清晰:代码可读性更高,特别是表单很复杂时


5. 对比常见替代方案

  • 如果只是做视觉分组,可以用 <div> + CSS
  • 如果需要无障碍支持和批量禁用功能,最好用 <fieldset>

Popover

你之前学了 <dialog>(模态框,强制遮罩)。但如果你只是想做一个 “点击按钮,旁边弹出一个小气泡/菜单” 呢?

以前要做一个 TooltipDropdown,你得算坐标 (top: 100px; left: 200px),还得处理 z-index,还得监听“点击空白处关闭”。现在只需要使用原生 popover 属性。

popoverHTML 新增的一个属性/特性,用来实现原生的轻量弹出层(popover),类似 tooltip、dropdown、menu 这类浮层 UI,完全不需要自己写 JS 计算定位。它是继 <dialog> 之后,浏览器在原生层面提供的另一个“UI 基础能力”。

1. 基本概念

popover 不是一个新标签,而是一个属性,可以加在任意元素上,使该元素变成可显示/隐藏的浮层。

基本用法

html
<button popovertarget="my-popover">打开 Popover</button>

<div popover id="my-popover">
  <p>这是一个原生 popover!</p>
</div>

点击按钮后,浏览器会自动切换 #my-popover 的可见状态。

image-20250921200422248

点击空白就关闭了这个popover

2. 核心属性

属性/方法作用
popover让元素成为 popover 浮层,默认是隐藏的
popovertarget绑定触发器,指定要控制的 popover 元素的 id
popovertargetaction控制行为,可选值:toggle(默认)、showhide
:popover-openCSS 伪类,当 popover 打开时匹配,可用来写动画
.showPopover() / .hidePopover()JS API,手动打开/关闭 popover
.togglePopover()JS API,手动切换 popover 状态

3. 示例:不同动作

html
<button popovertarget="p1" popovertargetaction="show">显示</button>
<button popovertarget="p1" popovertargetaction="hide">隐藏</button>
<button popovertarget="p1" popovertargetaction="toggle">切换</button>

<div popover id="p1" class="p-3 border rounded">
  我是 popover
</div>

4. 样式与动画

popover 本质上是一个元素,默认样式由浏览器管理,你可以自己写 CSS:

css
[popover] {
  background: white;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 8px 12px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  opacity: 0;
  transition: opacity 0.2s ease;
}

[popover]:popover-open {
  opacity: 1; /* 打开时渐显 */
}

5. 特点与优势

原生定位:自动计算位置,避免自己写 position: absolute; left/top原生状态管理:不需要 JS 维护开关逻辑,浏览器帮你处理 ✅ 无障碍支持好:自动管理焦点和可访问性 ✅ <dialog> 更轻量:适合 tooltip、菜单、气泡提示 ✅ 可嵌套:一个页面可以有多个 popover,相互独立

6. 注意事项

  • 浏览器支持还在推进中,2025 年主流 Chrome、Edge、Safari 都支持,Firefox 也在跟进。
  • 不像 <dialog> 自带遮罩层,popover 不会阻止背景点击,更适合非模态场景。
  • 可以和 <form>menu<button> 配合做原生 dropdown / context menu。

图片与多媒体

<img src="..."> 已经不够用了。现代 HTML 要求我们根据设备环境提供不同的资源。

<picture>

电脑上:显示一张宽屏的风景图。

手机上:如果只是把宽图缩小,根本看不清细节。你想换成一张正方形的特写图

html
<picture>
  <source media="(max-width: 600px)" srcset="portrait.jpg">
  <source srcset="img.avif" type="image/avif">
  <img src="landscape.jpg" alt="风景图">
</picture>

底蕴价值: 这叫 Art Direction (艺术指导)。浏览器只下载其中一张图片,不会浪费流量。

<figure> & <figcaption>

这俩是语义化图注:

图片下面加一行小字说明,以前是 <div class="img-wrap"><img...><span class="desc">...</span></div>。浏览器不知道这行字是属于这张图的。

html
<figure>
  <img src="logo.png" alt="Logo">
  <figcaption>图1:公司 2024 年新版 Logo 设计</figcaption>
</figure>

懒加载

原生懒加载

<img loading="lazy">

页面有 100 张图片。用户刚打开页面,浏览器就傻乎乎地把 100 张全下载了,导致首屏加载巨慢。

以前的做法:引入 lozad.js 或写 IntersectionObserver 监听滚动。

现在只需要一个属性就解决了

html
<img src="heavy-image.jpg" loading="lazy" alt="...">
<img src="..." decoding="async">

资源预加载:

<link rel="preload">

你的网页用了一个特殊的字体文件。通常浏览器解析到 CSS 里的 @font-face 才会去下载字体,这时候字会闪一下。

告诉浏览器:“这个东西很重要,别等了,先去下载!”

html
<head>
  <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
</head>

原生输入控件

还在用 jQuery UI Datepicker 吗?还在用 JS 写颜色选择器吗?快停下!

原生拾色器与日历

<input type="...">

HTML5 的 input 类型已经进化得非常强大,尤其在手机端,它们会调用系统原生的滚轮选择器,体验比 JS 插件好一万倍。

html
<input type="color" value="#ff0000">
<input type="date">
<input type="datetime-local">
<input type="range" min="0" max="100" step="10">

底蕴价值: 这些控件最大的优势是 Accessibility (无障碍)Mobile Friendly。在手机上,原生的日期滚轮比任何网页模拟的日历都好用。

网页变成编辑器

你想做一个像 Notion 或在线文档那样的编辑器吗?

contenteditable

你想让用户直接在 <div> 里打字,而不是在丑陋的 <textarea> 里。

html
<div contenteditable="true" class="editor">
  这里的内容可以直接修改...
  <b>甚至可以加粗</b>
</div>

spellcheck

浏览器会认为代码里的变量名(如 myVar)是拼写错误的单词,并在下面画红色波浪线。这很烦人。

html
<textarea spellcheck="false">const myVar = 1;</t

键盘模式

当你让用户输入“验证码(数字)”时,手机弹出的却是“全键盘(带字母)”。用户得手动切换到数字键盘,体验极差。

现在可以使用inputmode强制弹起指定键盘。

html
<input type="text" inputmode="numeric" pattern="[0-9]*">
<input type="tel" inputmode="tel">
<input type="email" inputmode="email">
<input type="url" inputmode="url">

enterkeyhint

在聊天框里,键盘右下角是“换行”。

在搜索框里,键盘右下角是“前往”。

如果不控制,用户经常因为不知道点哪个而困惑。

现代解法: 自定义那个蓝色按钮的文案/图标。

html
<input type="text" enterkeyhint="send">
<input type="search" enterkeyhint="search">
<input type="text" enterkeyhint="done">

短信验证码的“自动填充”

autocomplete="one-time-code"

场景: 用户用手机号登录,收到了短信验证码 123456

新体验:

  1. 短信来了。
  2. 键盘上方自动出现“123456 来自短信”。
  3. 用户点一下,自动填入。
html
<label>请输入验证码:</label>
<input 
  type="text" 
  inputmode="numeric" 
  autocomplete="one-time-code" 
  pattern="\d{6}"
>

底蕴价值: 这行代码直接打通了 操作系统(iOS/Android)浏览器。它让 Web 的登录流程丝滑程度追平了原生 App。

调用摄像头

想做“拍照上传”功能?千万别只写 type="file"

capture

痛点: 在手机上点 <input type="file">,默认是弹出一个菜单:“相册、文件、拍照”。用户想自拍还要多点一次。

现代解法: 直接进相机。

html
<input type="file" accept="image/*" capture="user">
<input type="file" accept="image/*" capture="environment">
<input type="file" accept="video/*" capture="environment">

体验提升: 这在做 实名认证(拍身份证)头像上传 时非常关键。少一步操作,转化率就高一分。

语义化搜索

你还在用 <div> 包裹 <input> 来做搜索框吗?HTML5 终于给了它名分。

html
<search>
  <form action="/search">
    <input type="search" placeholder="搜索...">
    <button type="submit">Go</button>
  </form>
</search>

底蕴价值: 除了语义清晰,type="search" 在手机键盘上会自动把“回车”变成“搜索”,而且那个自带的 “一键清除按钮” (X) 是浏览器原生提供的,既美观又好用。

体验优化

这是一个极其前沿但超级实用的属性(Chrome 已支持)。

自动展开搜索:hidden="until-found"

你用 <details> 做了一个很长的 FAQ 页面,内容都是折叠的。用户按 Ctrl + F (页面内查找) 搜关键词。结果: 搜不到!因为内容被折叠隐藏了。用户必须手动一个个点开才能搜。

html
<details>
  <summary>如何退款?</summary>
  <p hidden="until-found">
    退款流程是:点击订单,选择售后...
  </p>
</details>

神奇效果: 当用户按 Ctrl + F 搜索“售后”时,浏览器会自动强制展开这个 <details> 面板,并高亮“售后”两个字!

沉浸式体验

你有没有发现,有些网站在手机上打开时,**顶部的系统状态栏(电量、时间那一栏)**会自动变成和网页一样的颜色?而不是默认的白色或灰色。

<meta name="theme-color">

  • 痛点: 网页做得再漂亮,顶着一个白色的浏览器状态栏,瞬间就有种“我在刷网页”的出戏感。
  • 现代解法: 把浏览器 UI 染成你的品牌色。
html
<meta name="theme-color" content="#4285f4">
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#4285f4" media="(prefers-color-scheme: light)">

底蕴价值: 这叫 Web App Manifest 的一部分。它消除了“网页”和“原生应用”在视觉上的边界,是 PWA(渐进式 Web 应用)的第一步。

强制下载

<a download>

你放了一个 PDF 或图片链接 <a href="report.pdf">下载报告</a>。用户点击后,浏览器直接在当前页打开了这个 PDF。用户想下载,还得右键 -> 另存为… 体验很差。

html
<a href="report.pdf" download>下载报告</a>
<a href="/files/x8s7f.pdf" download="2024年度财报.pdf">
  下载并重命名
</a>

让链接变“卡片”

为什么把淘宝或 B 站的链接发到微信/Twitter/Slack 里,会变成一张漂亮的卡片(有图、有标题、有简介),而你的链接发出去只是一行蓝色的字?

Open Graph 协议<meta property="og:...">

  • 痛点: 链接裸奔,没人想点。
  • 现代解法: 告诉社交软件,怎么渲染你的网页。
html
<head>
  <meta property="og:title" content="惊爆!HTML 还能这么写?">
  <meta property="og:description" content="5 个你绝对不知道的 HTML 标签...">
  <meta property="og:image" content="https://example.com/cover.jpg">
  <meta property="og:type" content="article">
</head>

体验提升: 这直接决定了你的网页在社交网络上的点击率 (CTR)。一张好图胜过千言万语。

交互冻结inert

场景: 侧边栏打开时,你希望背后的主页面完全不可点、不可选、不可聚焦

html
<main id="content" inert>
  ...这里的所有按钮都点不了,文字选不中,Tab 键也会跳过...
</main>

<aside id="sidebar">
  ...现在用户只能在这里操作...
</aside>

视频播放

你有没有试过在手机网页上放一个 <video> 作为背景? 结果: 视频全是黑的,或者点一下才能播,或者一播就自动全屏盖住了网页。

手机浏览器为了省流量,默认禁止自动播放视频。 必须集齐 3 个属性,才能召唤神龙。

html
<video autoplay muted playsinline src="bg.mp4"></video>
  • autoplay: 自动播放。
  • muted: 关键! 只有静音的视频,浏览器才允许自动播放。
  • playsinline: 关键! 在 iOS 上,视频默认会弹出来全屏播放。加上这个属性,视频就会乖乖地“内嵌”在网页里播放(像 GIF 一样)。

原生正则校验:pattern

场景: 输入用户名,要求:只能是字母或数字,且 6-12 位。

html
<form>
  <label>用户名:</label>
  <input 
    type="text" 
    pattern="[a-zA-Z0-9]{6,12}" 
    title="请输入6-12位字母或数字" 
    required
  >
  <button>提交</button>
</form>

<style>
  /* 配合 CSS:输入不对时变红 */
  input:invalid {
    border-color: red;
  }
  input:valid {
    border-color: green;
  }
</style>

体验提升: 当用户输入不符合规则并点击提交时,浏览器会自动弹出一个气泡提示(内容就是你写的 title),并阻止表单提交。零 JS!

user-invalid这个CSS伪元素可以增强体验

Web Components

核心概念:Web Components 的“三驾马车”

Web Components 不是一个单一的技术,而是三项技术的组合:

  1. Custom Elements(自定义元素): 允许你定义 <my-button> 这样的新 HTML 标签。
  2. Shadow DOM(影子 DOM): 提供样式和结构的隔离。你的组件内部样式不会影响外面,外面的 CSS 也搞不坏你的组件。(这是 Web Components 最强的地方)。
  3. HTML Templates(模板): 使用 <template><slot> 来定义组件的结构,这点和 Vue 非常像。

实战演练:写一个 <user-card> 组件

我们要实现一个用户卡片组件,功能如下:

  1. 支持传入 nameavatar 属性。
  2. 支持插槽(Slot)放入自定义内容。
  3. 样式隔离,不被全局 CSS 污染。

第一步:HTML 结构(怎么用它)

我们希望最终能这样使用它:

html
<user-card 
  name="Gemini" 
  avatar="https://api.dicebear.com/7.x/avataaars/svg?seed=Gemini"
>
  <p slot="bio">我是 Google 的 AI 模型。</p>
  <button>关注我</button>
</user-card>

第二步:JavaScript 实现(怎么造它)

不需要任何构建工具(Webpack/Vite),新建一个 .html 文件就能跑。

js
// 1. 定义一个类,继承自 HTMLElement
class UserCard extends HTMLElement {
  
  // 构造函数:初始化组件
  constructor() {
    super(); // 必须第一行调用

    // 🌟 核心:开启 Shadow DOM
    // mode: 'open' 意味着你可以通过 JS (this.shadowRoot) 访问内部
    this.attachShadow({ mode: 'open' });
    
    // 渲染组件结构
    this.render();
  }

  // 2. 监听属性变化:告诉浏览器我们要监听 'name' and 'avatar'
  static get observedAttributes() {
    return ['name', 'avatar'];
  }

  // 3. 生命周期:当属性变化时触发(类似 Vue 的 watch)
  attributeChangedCallback(name, oldValue, newValue) {
    if (oldValue === newValue) return;
    this.render(); // 重新渲染
  }

  // 4. 渲染函数
  render() {
    // 获取属性值
    const name = this.getAttribute('name') || '匿名用户';
    const avatar = this.getAttribute('avatar') || 'default.png';

    // 🌟 写入 Shadow DOM
    // 注意:这里的 <style> 只在这个组件内部生效!
    this.shadowRoot.innerHTML = `
      <style>
        :host {
          display: inline-block; /* 默认是 inline,改一下 */
          border: 1px solid #e0e0e0;
          border-radius: 8px;
          padding: 16px;
          width: 300px;
          font-family: sans-serif;
          background: #fff;
          box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        .header { display: flex; align-items: center; gap: 10px; }
        img { width: 50px; height: 50px; border-radius: 50%; }
        h3 { margin: 0; color: #333; }
        .content { margin-top: 10px; color: #666; font-size: 0.9em; }
        
        /* ::slotted 可以给插进来的内容设置样式 */
        ::slotted(button) {
          background: #007bff;
          color: white;
          border: none;
          padding: 5px 10px;
          border-radius: 4px;
          cursor: pointer;
        }
      </style>

      <div class="header">
        <img src="${avatar}" alt="Avatar">
        <h3>${name}</h3>
      </div>
      
      <div class="content">
        <slot name="bio">暂无简介</slot>
        <hr/>
        <slot></slot> 
      </div>
    `;
  }
}

// 5. 注册组件:把类和标签名绑定
// ⚠️ 标签名必须包含连字符 (-),不能叫 <card>,必须叫 <user-card>
customElements.define('user-card', UserCard);

进阶:生命周期(Lifecycle)

Web Components 的生命周期非常简单,一共只有 4 个回调:

  1. constructor(): 组件被创建(内存中)时执行。初始化数据。
  2. connectedCallback(): 重要! 组件被插入到页面 DOM 时执行。
    • Vue 对应: onMounted
    • 用途: 发起网络请求、绑定事件监听、启动定时器。
  3. disconnectedCallback(): 组件从页面被移除时执行。
    • Vue 对应: onUnmounted
    • 用途: 清理定时器、解绑事件(防止内存泄漏)。
  4. attributeChangedCallback(name, oldVal, newVal): 属性变化时执行。
    • Vue 对应: watch
    • 用途: 响应数据变化更新视图。

示例:给组件加个点击事件

js
connectedCallback() {
  console.log('组件已挂载!');
  // 给内部的头像绑定点击事件
  this.shadowRoot.querySelector('img').addEventListener('click', () => {
    alert('你点了头像!');
    
    // 🌟 发送自定义事件给外部(父组件)
    // detail 里面可以放任何数据
    this.dispatchEvent(new CustomEvent('avatar-click', {
      detail: { userId: 123 },
      bubbles: true, // 允许冒泡
      composed: true // 允许穿透 Shadow DOM 边界传出去
    }));
  });
}

痛点与现代解决方案:Lit

写原生 Web Components 有个问题:太啰嗦了! 你看上面的 render() 函数,直接用字符串拼接 HTML,这非常容易出错,而且性能不好(每次都重写 innerHTML)。

所以在真实开发中(比如 Google 的 Material Design 团队),大家会用一个微型库:Lit

Lit 是 Google 推出的 Web Components 封装库,它让写法变得非常像 React/Vue,但产出的依然是标准的 Web Components。

看一下 Lit 的写法(对比上面,清爽了多少):

js
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('user-card')
export class UserCard extends LitElement {
  
  // 自动处理属性监听和类型转换
  @property({ type: String }) name = '匿名';
  @property({ type: String }) avatar = '';

  // 样式(Scoped CSS)
  static styles = css`
    :host { display: block; padding: 16px; border: 1px solid #ccc; }
    h3 { color: blue; }
  `;

  // 渲染(使用 html 模板字面量,类似 React JSX)
  render() {
    return html`
      <div>
        <img src="${this.avatar}">
        <h3>${this.name}</h3>
        <slot></slot>
      </div>
    `;
  }
}

评论

评论加载中……