前言
前面除了创建新专案之外也了解了 Astro CLI 与設定檔的大致样貌,本章节会从创建基本 Astro 组件开始,组件可以被放置在先前提到的 src 资料夹当中,建议创建一个 components 资料夹存放在内以方便管理。
创建组件
可以藉由创建一个副档名为 .astro 的档案来撰写 Astro 组件,组件中分为脚本与模板两个区块,这两个区块由栅栏(---)所区隔。
---// 组件脚本---<!-- 组件模板 -->组件脚本代表该组件在服务器端中会被如何执行,像是你可以在这里撰写 JavaScript 或 TypeScript 用于:
- 引入其他 Astro 组件
- 引入不同框架的组件
- 引入与索取资料
- 接收组件接收到的资料(props)
- 创建变数并在组件模板中存取
---// 1. 导入 Astro 组件import SomeAstroComponent from './components/SomeAstroComponent.astro';
// 2. 引入不同框架的组件import SomeReactComponent from './components/SomeReactComponent.jsx';
// 3. 引入资料import pokemons from '../data/pokemon.json';
// 3. 引入资料const user = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());
// 4. 透过解构获得该组件所接收到的 propsconst { title } = Astro.props;const message = 'Astro 很棒!'---
<!-- 5. 创建变量并在组件模板中存取 -->我认为:{ message }{ title }关于第 5 点,可以在服务器端 JavaScript 中创建变数使用 {} 单括弧插入到模板中,不管是属性还是作为 Props 传入:
---import Modal from './components/Modal.astro'const message = '你好,Astro'---<Modal class={message} message={`${message} Here Is My Props`} />或者是撰写 JavaScript 表达式来产生 HTML,就像是 JSX 一样:
---const fruits = ['苹果', '香蕉', '樱桃']---
<ul> {fruits.map(fruit => <li>{fruit}<li>)}</ul>或是选择性地显示模板内容:
---const isOnSale = true---
{isOnSale && <p>正在打折中!</p>}{isOnSale ? <p>正在打折中!</p> : <p>尚无打折</p>}或是动态的决定标签种类(须留意动态标签不支援 Hydration、并且标签命名必须大写,才能分辨出是原生标签还是客制化组件):
---import MyComponent from "./MyComponent.astro";const Element = 'div'const Component = MyComponent;---
<Element>Hello!</Element> <!-- 渲染出 <div>Hello!</div> --><Component /> <!-- 渲染出 <MyComponent /> -->组件属性 Props
可以设定让组件接受外部传入的属性,只需要在组件内的服务器端脚本中从 Astro.props 中来获得,像以下的例子 greeting 与 name 两个变数被使用解构的方式创建出来:
---// 组件外: <GreetingHeadline greeting="Hi" name="Joe" />const { greeting, name } = Astro.props;---<h2>{greeting}, {name}!</h2>甚至可以为 Props 添加类型,让文字编辑器可以知道「该组件应该要传入什么类型的东西」:
interface Props { greeting: string; name: string;}组件插槽 Slot
除了组件属性也可以透过插槽的方式将外部的 HTML 内容传入到组件之中,举例来说:由于目前网站大多数页面都包含了导航列与页脚组件,于是你可以创建一个名为 Base 的组件作为网站的通用组件。
---import Navbar from '../components/Navbar.astro'import Hero from '../components/Hero.astro'import Footer from '../components/Footer.astro'---<Navbar /> <Hero /> <slot /><Footer />并且在每个页面中引入该组件,不但可以统一管理所有页面的架构,也不用在每一页反覆的引入基本需要的组件。
---import Base from '../layouts/Base.astro---<Base> <!-- 此区间的模板将注入到 slot 中 --></Base>具名插槽
可以拥有一个以上的插槽,这时候使用具名插槽来指定「要注入内容的插槽」。举例来说根据先前的范例:
<Navbar /> <slot name="before-hero" /> <Hero /> <slot /><Footer />就可以使用 slot 属性来指定想注入的插槽名称。
<Base> <!-- 以下图片将会注入到 before-hero 插槽中 --> <img src ="example.jpg" slot="before-hero"> <!-- 以下没有特别注明 slot 将注入到预设 slot 中 --> <h1>你好世界</h1></Base>插槽后备方案
当在定义插槽的位址时,可以为其添加内容,这些内容就会成为当没有任何模板传递进来时所采用的预设内容。
<slot> <p>这是预设后备方案,当没有模板传递到 slot 时就会被采用</p></slot>插槽转移
插槽可以被转移到其他的组件之中,举例来说有个组件: BaseLayout.astro
------<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <slot name="head"/> </head> <body> <slot /> </body></html>而它被 HomeLayout.astro 所引用:
---import BaseLayout from "./BaseLayout.astro";---
<BaseLayout> <slot name="head" slot="head"/> <slot /></BaseLayout>这时候再对 HomeLayout.astro 传入 head 插槽将会转移到 BaseLayout 的 head 插槽之中。
---import HomeLayout from "../layouts/HomeLayout.astro";---<HomeLayout> <title slot="head">Astro</title> <h1>Astro</h1></HomeLayout>总结
如果你先前有接触过其他框架或 JSX 就会发现这些「组件」间的观念都很雷同好上手,并且由于 .astro 设计主要用于服务器渲染,因此不用担心状态反应性的問題,极大地删减了复杂度!
最后会建议实际动手练习,如果过程中有问题可以参考看看我的范例:
- 在
components资料夹内撰写像是Navbar、Footer之类常见的组件。 - 在
pages资料夹内的页面中导入并显示你新制作好的组件。 - 在
layouts资料夹内创建一个名为Base的组件,并且透过<slot />让整个网站的页面都使用该组件,甚至更进一步接受 Props 让该组件提供更多弹性可被修改的空间(像是接受修改<head>中的meta标签们)。 - 适当的撰写 TypeScript 进行类型定义。
延伸阅读
- Components - Astro DOCS
- Component Props - Astro DOCS
- Day5 - 基礎元件 - 相同文章同步發布於 iThome 鐵人賽中