模板基础 #
一、模板结构 #
1.1 Astro 组件结构 #
Astro 组件由两个主要部分组成:
astro
---
// Frontmatter(脚本区域)
// JavaScript/TypeScript 代码
const title = "Hello Astro";
---
<!-- 模板区域 -->
<!-- HTML 模板 -->
<h1>{title}</h1>
1.2 Frontmatter 区域 #
Frontmatter 是组件顶部的代码围栏区域:
astro
---
// 导入模块
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
// 定义变量
const siteName = "My Site";
const year = new Date().getFullYear();
// 定义函数
function formatDate(date) {
return new Date(date).toLocaleDateString('zh-CN');
}
// 获取数据
const posts = await fetch('/api/posts').then(r => r.json());
---
<Layout title={siteName}>
<!-- 使用定义的变量和函数 -->
<p>© {year} {siteName}</p>
<p>日期: {formatDate(new Date())}</p>
</Layout>
二、文本插值 #
2.1 基本插值 #
使用 {变量} 语法插入动态值:
astro
---
const name = "张三";
const age = 25;
---
<p>姓名: {name}</p>
<p>年龄: {age}</p>
2.2 表达式插值 #
可以在大括号中使用 JavaScript 表达式:
astro
---
const a = 10;
const b = 20;
const items = ['苹果', '香蕉', '橙子'];
---
<p>计算结果: {a + b}</p>
<p>数组长度: {items.length}</p>
<p>当前时间: {new Date().toLocaleString()}</p>
<p>大写: {'hello'.toUpperCase()}</p>
2.3 模板字符串 #
astro
---
const name = "世界";
const count = 42;
---
<p>{`你好,${name}!`}</p>
<p>{`总数: ${count} 个`}</p>
三、属性绑定 #
3.1 基本属性 #
astro
---
const imageUrl = "/images/photo.jpg";
const linkUrl = "https://example.com";
const inputId = "username";
---
<img src={imageUrl} alt="照片" />
<a href={linkUrl}>访问链接</a>
<input id={inputId} type="text" />
3.2 动态属性名 #
astro
---
const attrName = "data-custom";
const attrValue = "value";
---
<div {attrName}={attrValue}></div>
<!-- 等同于 <div data-custom="value"></div> -->
3.3 展开属性 #
astro
---
const attrs = {
class: "btn primary",
disabled: true,
"data-action": "submit"
};
---
<button {...attrs}>按钮</button>
<!-- 等同于 <button class="btn primary" disabled data-action="submit">按钮</button> -->
3.4 布尔属性 #
astro
---
const isDisabled = true;
const isRequired = false;
---
<button disabled={isDisabled}>禁用按钮</button>
<input required={isRequired} />
<input disabled /> <!-- 简写形式 -->
3.5 class 属性 #
astro
---
const isActive = true;
const isPrimary = false;
---
<!-- 使用 class:list 指令 -->
<div class:list={['btn', { active: isActive, primary: isPrimary }]}>
按钮
</div>
<!-- 输出: <div class="btn active">按钮</div> -->
<!-- 也可以使用普通 class -->
<div class={`btn ${isActive ? 'active' : ''}`}>
按钮
</div>
四、HTML 注释 #
4.1 模板注释 #
astro
---
const name = "张三";
---
<!-- 这是 HTML 注释,会出现在输出中 -->
<p>{name}</p>
{/* 这是 Astro 注释,不会出现在输出中 */}
<p>{name}</p>
4.2 条件注释 #
astro
---
const showComment = true;
---
{showComment && <!-- 条件显示的注释 -->}
五、Fragment #
5.1 使用 Fragment #
当需要返回多个元素而不添加额外 DOM 节点时:
astro
---
const Fragment = Astro.fragment;
---
<Fragment>
<h1>标题</h1>
<p>段落</p>
</Fragment>
<!-- 简写形式 -->
<>
<h1>标题</h1>
<p>段落</p>
</>
5.2 条件渲染中使用 #
astro
---
const showContent = true;
---
{showContent && (
<>
<h2>标题</h2>
<p>内容</p>
</>
)}
六、转义 #
6.1 转义 HTML #
默认情况下,变量内容会被转义:
astro
---
const htmlContent = "<strong>加粗</strong>";
---
<p>{htmlContent}</p>
<!-- 输出: <p><strong>加粗</strong></p> -->
6.2 插入原始 HTML #
使用 set:html 指令插入原始 HTML:
astro
---
const htmlContent = "<strong>加粗</strong>";
---
<p set:html={htmlContent} />
<!-- 输出: <p><strong>加粗</strong></p> -->
6.3 安全警告 #
astro
---
// ⚠️ 危险:不要直接插入用户输入的 HTML
const userInput = "<script>alert('XSS')</script>";
---
<!-- 不安全 -->
<p set:html={userInput} />
<!-- 安全:先清理 HTML -->
import { sanitize } from 'sanitize-html';
const safeHtml = sanitize(userInput);
<p set:html={safeHtml} />
七、模板指令 #
7.1 set:html #
设置元素的 innerHTML:
astro
---
const content = "<em>斜体文字</em>";
---
<div set:html={content} />
7.2 set:text #
设置元素的 textContent:
astro
---
const content = "<em>斜体文字</em>";
---
<div set:text={content} />
<!-- 输出: <div><em>斜体文字</em></div> -->
7.3 is:raw #
将子内容作为原始字符串处理:
astro
---
const code = "<div>Not parsed</div>";
---
<code is:raw>
{code}
</code>
7.4 is:inline #
保持样式/脚本内联:
astro
<style is:inline>
/* 这个样式会内联到 HTML 中 */
.inline-style { color: red; }
</style>
<script is:inline>
// 这个脚本会内联到 HTML 中
console.log('inline script');
</script>
八、动态标签 #
8.1 动态元素类型 #
astro
---
const Tag = 'h1';
---
<Tag>这是标题</Tag>
<!-- 输出: <h1>这是标题</h1> -->
8.2 条件标签 #
astro
---
const isLarge = true;
const Heading = isLarge ? 'h1' : 'h2';
---
<Heading>标题</Heading>
九、模板最佳实践 #
9.1 保持 Frontmatter 简洁 #
astro
---
// ✅ 好的做法:将复杂逻辑抽取到函数
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
const items = await getItems();
const total = calculateTotal(items);
---
<p>总价: {total}</p>
---
// ❌ 不好的做法:在模板中写复杂逻辑
const items = await getItems();
---
<p>总价: {items.reduce((sum, item) => sum + item.price, 0)}</p>
9.2 使用类型定义 #
astro
---
interface Props {
title: string;
items: string[];
}
const { title, items }: Props = Astro.props;
---
<h1>{title}</h1>
<ul>
{items.map(item => <li>{item}</li>)}
</ul>
9.3 避免深层嵌套 #
astro
---
// ✅ 好的做法:使用组件拆分
import Card from './Card.astro';
const posts = await getPosts();
---
{posts.map(post => <Card post={post} />)}
---
// ❌ 不好的做法:深层嵌套
const posts = await getPosts();
---
{posts.map(post => (
<article>
<header>
<h2>{post.title}</h2>
<p>{post.date}</p>
</header>
<div>
<p>{post.excerpt}</p>
<a href={`/blog/${post.slug}`}>阅读更多</a>
</div>
</article>
))}
十、总结 #
模板基础核心要点:
text
┌─────────────────────────────────────────────────────┐
│ 模板基础要点 │
├─────────────────────────────────────────────────────┤
│ │
│ 📝 Frontmatter 脚本区域,定义变量和逻辑 │
│ │
│ 🔤 文本插值 {变量} 插入动态值 │
│ │
│ 📎 属性绑定 动态绑定 HTML 属性 │
│ │
│ 📦 Fragment 无额外节点的元素包装 │
│ │
│ 🔧 指令 set:html、set:text 等 │
│ │
└─────────────────────────────────────────────────────┘
下一步,让我们学习 表达式,深入了解模板中的表达式使用!
最后更新:2026-03-28