在这个实用的Astro指南中,我将指导你完成设置过程,并告诉你如何构造你的文件。你将学习如何添加页面、交互式组件,甚至是markdown文章。我还会告诉你如何从服务器上获取数据,创建布局,并使用vanilla JavaScript和其他框架添加互动性。准备好享受一些动手的乐趣,因为我们将一起创建一个小型的网站实例。我们将构建一个多页面的网站,包括一个博客。
在这篇文章末尾,你会很好地理解Astro是如何工作的,以及你如何使用它来更快地创建高效的网站。开始吧!
作为开发人员,我们知道,在建设网站时,创造一个良好的用户体验是关键。而用户最喜欢的是什么?快速的网站,不浪费他们的时间。有了Astro,我们可以通过向浏览器发送更少的代码来实现这一目标。
我们都有自己喜欢的UI框架,使我们的生活更轻松,但它们可能会以沉重的网站为代价。但是有了Astro,我们就可以拥有两个世界中最好的东西。它允许我们用我们最喜欢的框架甚至多个框架同时构建我们的网站,但它在构建时将它们渲染成静态HTML。因此,我们可以为我们的用户创建一个快速的网站,而不牺牲现代开发者的体验。
但Astro并没有止步不前。它还允许我们在需要时加入动态的客户端JavaScript,这意味着我们可以在网站上拥有可交互的组件,但只在必要时进行。换句话说,Astro允许你从简单的开始,在需要时增加复杂性。
简而言之,Astro是一个强大的框架,它同时支持静态网站生成(SSG)和服务端渲染(SSR),帮助我们建立快速的、基于内容的网站,同时考虑到开发人员的体验。它轻量、高效、灵活,使它成为创建内容丰富的网站的合适选择,如博客、投资组合、文档和一些电子商务网站。如果你想创建一个具有大量交互的复杂应用程序,Astro可能不是你的正确选择。相反,你可以考虑其他工具比如Next.js。
很好,现在我们对Astro是什么以及它能做什么有了很好的了解。接下来,让我们继续研究,看看我们能一起构造些什么。
首先,让我们安装Astro并创建项目的模板。确保你的电脑上安装了Node.js v16.12.0或者更高版本。
npm create astro@latest
或者:
yarn create astro@latest
CLI会询问你的项目名称和你是否要使用Typescript。此外,它还会给你几个选项,告诉你如何设置你的项目。在本教程中,我选择了"一个空项目 "选项。
以下是我与Astro CLI的小型对话:
一旦你在编辑器中打开你的项目,你可以安装Astro的扩展。由于Astro有自己的文件扩展和语法,你可能想为你的编辑器安装其扩展。这里是VSCode的Astro扩展的链接,它不仅仅为你高亮语法,还能做得更多。
现在你可以用以下命令来运行你的项目。不需要安装任何依赖。
npm run dev
打开浏览器,访问http://localhost:3000/ ,一切就绪。
Astro的文件结构相当直截了当。
src
:你的源码(组件、页面、样式等等)public
:你的静态资源(字体、图标等等)在src/pages
目录下,Astro创建了一个index.astro
,你可以认为这是index.html
。
值得注意的是,src/content/
是Astro的一个保留目录。Astro v2.0引入了Collections API
,用于将你的Markdown和MDX文件组织成内容集合。这个API保留了src/content/
作为一个特殊的文件夹。
好了,让我们来谈谈Astro的页面。Astro页面处理路由、数据加载以及网站上每个页面的整体布局。它们是具有不同扩展名的文件,存在于src/pages/
子目录中。
在Astro中,我们有不同类型的页面,包括.astro
、.md
、.mdx
、.html
甚至是.js/.ts
。每种文件类型都有不同的用途,可以用不同的方式来创建你的页面。
Astro使用一种称为基于文件路由的路由策略,这意味着你的src/pages/
目录中的每个文件都会根据其文件路径成为你网站上的一个端点。这使得你的页面具有灵活性,并易于组织。
在本教程中,我们主要使用.astro
和.md
文件来创建页面。注意,如果你使用.html
页面,一些关键的Astro特性在HTML组件中不被支持。
现在让我们来创建第二个页面,看看它是如何工作的。你所需要做的就是在src/pages
目录下创建一个紧挨着index.astro
的文件。
我打算把它命名为about.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" />
<title>About</title>
</head>
<body>
<h1>About</h1>
<p>Jamstack development agency.</p>
</body>
</html>
现在你可以访问localhost:3000/about
,在你的浏览器中看到这个页面。正如你所看到的,只要你把文件添加到pages
文件夹中,/about
路由就能立即发挥作用。
你可以更新内容,甚至添加你自己的内部样式,Astro将为你实时预览。所以不需要安装任何实时重载的NPM包或此类东西。
为了能够在页面之间轻松导航,我打算在我的index.astro
和about.astro
文件中添加导航:
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<h1>About</h1>
<p>Jamstack development agency.</p>
</body>
但你不希望每次在导航中增加一个链接时都要更新所有的页面,对吗?
这就是组件发挥作用的地方,让你不要重复你自己(DRY)。
Astro组件是任何Astro项目的基本构成模块。它们有两个主要部分:
Astro组件长这样:
---
// Component Script (JavaScript)
---
<!-- Component Template (HTML + JS Expressions) -->
让我们为项目添加第一个组件。
我打算创建一个src/components
目录,并在里面添加一个header.astro
文件。接着,我会移动导航标记到Header组件中。目前为止,我们的组件的脚本部分是空白的。
---
---
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
下一步是将该组件添加到我们的页面中。为了做到这一点,我们需要导入该组件。因此,打开你的about.astro
文件,在文件的顶部添加以下导入内容:
---
import Header from '../components/Header.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" />
<title>About</title>
</head>
<body>
<h1>About</h1>
<p>Jamstack development agency.</p>
</body>
</html>
现在,header
组件已经导入了,可以这么使用:
<body>
<Header />
<h1>About</h1>
</body>
对主页做同样的事情,也就是pages
目录中的index.astro
文件。
现在,如果你在浏览器中查看这些页面,你应该看到header
展现良好。
最后,我将把我们的logo
和一些语义标记,与一个容器一起添加到我们的header
中,这样我稍后可以添加一些样式:
<header>
<div class="container">
<a class="logo" href="/">
<svg><!-- SVG Logo goes here --></svg>
</a>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</div>
</header>
这就是Astro的厉害之处。到目前为止,我们已经制作了页面,并向其添加了组件,而几乎不需要写任何HTML以外的东西。
代码栅栏是你的Astro组件的脚本部分。你可以用栅栏来写任何你需要的JavaScript来渲染你的模板。你甚至可以写TypeScript!
---
// The code fence area
---
例如,在上一节中,我在我的代码栅栏中添加了一个导入行,将Header
组件添加到我的页面。我们将继续讨论我们在代码栅栏中还能做什么。
它被称为代码栅栏的原因是,无论你在其中写了什么JavaScript代码,都会被"围起来",不能逃到浏览器中去,也不能到你的用户手中。你在这里写的一切只是为了帮助你的组件模板。
让我们回到我们的页面,在页面的代码栅栏中添加一个变量,用于存储页面标题:
---
import Header from '../components/Header.astro';
const pageTitle = 'Bejamas';
---
<html lang="en">
<head>
<title>{pageTitle}</title>
</head>
<body>
<Header />
<h1>{pageTitle}</h1>
</body>
</html>
正如你在上面的片段中所看到的,你可以在代码栅栏内定义本地JavaScript变量,然后使用类似JSX的表达式将变量注入HTML模板中。
现在在你的其他页面比如about.astro
等,可以做同样的事情。
当你查看不同页面时,你可能又开始注意到一些恼人的东西。是的,当你在不同的页面中写同样的东西时,你会重复自己。除了标题之外,你的页面中所有的代码都是完全一样的。
我想这是谈论Astro布局的一个好时机。
Astro布局只是具有不同名称的Astro组件,用于创建一个UI结构或布局,比如一个页面模板。因此,任何你能在组件中做到的事情,都有可能在布局中实现。
你可以把你的布局文件放在你项目的任何地方,但把它们添加到src/layouts
目录中是很好的做法。
在我们的项目中,有一些跨页面的共享标记可以作为模板使用,以避免在不同的文件中重复它们。为了做到这一点,让我们在 src/layouts
目录中创建一个 BaseLayout.astro
文件。
我们把index.astro
的内容复制粘贴到该文件中:
你刚刚完成了你的第一个Astro布局,现在你需要在你的Astro页面中使用它。让我们看看你如何能做到这一点。
像组件一样,第一步是将布局导入到代码栅栏,然后通过在模板部分放置其标签来使用它。下面就是index.astro
的样子:
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout></BaseLayout>
如果你试图为你的About
页面做同样的事情,你会注意到about.astro
里面有不同的内容。你需要保留它,或者以某种方式将它传递给你的BaseLayout
文件。这就是<slot />
的用武之地。
<slot />
元素是一个外部HTML内容的占位符。它指定了其他文件中的子元素应该被注入你的组件模板的位置。
你可以把这个元素添加到你的BaseLayout.astro
文件中,就像下面的代码:
---
import Header from '../components/Header.astro';
const pageTitle = 'Bejamas';
---
<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" />
<title>{pageTitle}</title>
</head>
<body>
<Header />
<h1>{pageTitle}</h1>
<slot /> <!-- your content is injected here -->
</body>
</html>
现在,About
页面的内容可以像这样放在BaseLayout
标签之间:
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout>
<p>Jamstack development agency.</p>
</BaseLayout>
如果你查看你的浏览器,你会看到这两个页面是如何使用相同的模板但内容不同的。
只有一个部分被我们弄乱了,就是页面的标题。在我的例子中,两个标题都是 "Bejamas"。
这里就是组件props
的用武之地!
除了插槽,Astro组件的另一个特性是props
,它在布局中可能非常有用。任何Astro组件都可以定义和接收props
。要定义模板部分以外的props
,你可以通过Astro.props
全局设置。
在我们的例子中,我们可以定义一个pageTitle
参数,并把它传递给我们的BaseLayout
组件,以便能够在不同的页面上有不同的页面标题。
下面是你如何在BaseLayout
组件中定义一个prop
的例子:
---
import Header from '../components/Header.astro';
const { pageTitle } = Astro.props;
---
<html lang="en">
<head>
<title>{pageTitle}</title>
</head>
<body>
<Header />
<h1>{pageTitle}</h1>
<slot /> <!-- your content is injected here -->
</body>
</html>
注意我们是如何使用结构化语法从全局的Astro.props
对象中取出props
的。如果你有更多的props
,你可以使用逗号分隔,就像下面这样:
const { pageTitle, pageDescription } = Astro.props;
现在,让我们看看你如何将一个prop
传递到组件或者布局中。
index.astro
页面可以将pageTitle
作为属性进行传递:
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout pageTitle="Bejamas"></BaseLayout>
此外,你可以在脚本中定义变量更易于管理。在About
页面中这样做:
---
import BaseLayout from '../layouts/BaseLayout.astro';
const pageTitle = 'About';
---
<BaseLayout pageTitle={pageTitle}>
<p>Jamstack development agency.</p>
</BaseLayout>
目前为止,我还没给你展示任何结果。原因是我们还缺少样式。那么我们就加一些CSS吧。
Astro项目可以用很多方法进行样式声明,我们在这里无法涵盖所有。然而你应该知道,Astro支持任何你能想到的方法。你可以编写纯CSS、Sass和CSS模块,甚至可以导入你喜欢的CSS库,比如Tailwind。
你可以直接在你的组件或页面模板上添加一个<style>
标签。
---
---
<style>
.container { /* */ }
nav {
display: flex;
gap: 1rem;
}
nav a { /* */ }
</style>
<header>
<div class="container">
<a class="logo" href="/">
<svg><!-- SVG Logo goes here --></svg></a>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</div>
</header>
Astro <style>
CSS规则默认是在组件内起作用和封装的。这意味着写在这个组件中的样式不会泄漏,也不会影响你网站的其他部分。
除了Header
组件外,我将把其余的样式添加到一个外部的CSS文件中,并在项目中作为全局样式导入。
下面是你如何将外部的CSS文件导入到BaseLayout.astro
文件中的例子:
---
import Header from '../components/Header.astro';
import '../styles/global.css';
const { pageTitle } = Astro.props;
---
<html lang="en">
<head>
<title>{pageTitle}</title>
</head>
<body>
<Header />
<h1>{pageTitle}</h1>
<slot />
</body>
</html>
在我跳到下一节之前,我将在这里分享一个工作demo。我给我的Home
和About
页面添加了一些静态内容,并写了一些样式。
下面是我添加到我的主页的内容:
---
import BaseLayout from '../layouts/BaseLayout.astro';
const pageTitle = 'Bejamas';
---
<BaseLayout pageTitle={pageTitle}>
<section class="section">
<div class="container">
<div class="banner">
<div class="">
<h3 class="pretitle">Jamstack Developers for hire</h3>
<h1 class="title">We build<br><mark>fast sites & apps.</mark></h1>
<p class="text">Maximize your business potential...</p>
<a class="button" href="">Start project</a>
</div>
<img src="jam.svg" alt="" />
</div>
</div>
</section>
</BaseLayout>
主页看起来长这样:
点击这里查看demo。
我们教程的这一部分,我们将使用Bejamas API从服务器获取一些数据,并在我们的主页上创建这些案例研究卡。
在Astro中获取数据是非常容易的。你可以在你的代码栅栏中使用全局fetch()
函数,在你的所有组件中向API发出HTTP请求。
在index.astro
中,我将添加这段代码,使用fetch()
函数从服务器上获取案例研究的数据:
---
import BaseLayout from '../layouts/BaseLayout.astro';
const pageTitle = 'Bejamas';
const response = await fetch('bejamas.io/api/blog?category=case-studies');
const data = await response.json();
const posts = data.posts;
---
现在我有了posts
变量中的数据,我可以用它来生成动态HTML,在我的主页上显示案例研究卡片。
下面是posts
数组的样子:
"posts": [
{
"excerpt": "",
"mainImage": {
"asset": {
"url": ""
}
},
"slug": "",
"title": ""
}
]
我会使用map
函数遍历posts
,然后动态创建每张卡片:
<section class="section" id="casestudies-section">
<div class="container">
<h2 class="section-title">Case studies</h2>
<ul class="case-studies">
{posts.map((post) => (
<li>
<article class="casestudy">
<img src={post.mainImage.asset.url} alt="" />
<div>
<h3>{post.title}</h3>
<p>{post.excerpt}</p>
<a href={`http://bejamas.io/blog/${post.slug}`} class="">Read more</a>
</div>
</article>
</li>
))}
</ul>
</div>
</section>
最酷的是,所有这些都是在构建时发生的,我们将只向浏览器发送静态HTML。
这是向全局CSS添加样式后的结果:
点击这里查看demo。
当涉及到创建和管理你的内容时,Astro给你两个选择:
MDX类似于标准的Markdown,但有额外的功能。它允许你在你的Markdown内容中使用变量、JSX表达式和组件。
Astro内置了对Markdown的支持,但为了处理.mdx
文件,你需要安装@astrojs/mdx进行集成。在本教程中,我们坚持使用标准的Markdown内容。
在我们进一步讨论之前,有必要提到Astro v2引入了内容集合,这是一种在Astro项目中组织内容的绝佳方式。我们将在未来的教程中写更多关于这个主题的内容。
正如我们前面所说的,由于静态路由在Astro中的工作方式,src/pages/
目录中的任何页面都会得到一个路由。这意味着你可以在你的浏览器中打开它,或者在你的项目中的任何地方链接到它。另外,我们知道Markdown文件是Astro的一种页面类型,我们可以把它放在这个目录里面。牢记这些信息,让我们在 src/pages
目录中创建我们的第一个内容。
为了更有条理,我创建了一个blog
文件夹,把我的.md
文件放在那里,并把我的第一个Markdown内容添加到其中:
由于src/pages
目录下的Astro组件(.astro)和Markdown文件(.md)会自动成为你网站上的页面,你可以简单地导航到以下URL,访问你的第一篇博文:
localhost:3000/blog/jamstack
你可以在你的Markdown的前言中添加更多关于你内容的信息,比如它的标题,发布日期,主要图片等等,就像下面这样:
---
title: 'Jamstack Explained'
pubDate: 2020-12-14
description: 'Jamstack is not about a specific technology…'
author: 'Denis Kostrzewa'
image:
url: '<http://bejamas.io/_next/image/?.>..'
alt: 'Jamstack Explained Photo'
---
# Jamstack Explained
Jamstack is no longer a buzzword over which dev keyword warriors brawl. It has grown to become a booming ecosystem of tools helping developers ship performant websites, progressive web apps, and other projects with benefits too good to ignore.
你当然不希望你的文章看起来像这样,肯定希望把你的网站设计融入其中,对吗?让我们为我们的博客内容创建一个布局。
在src/layouts
目录下添加一个名为BlogLayout.astro
的文件,并将以下代码复制并粘贴到其中:
---
import Header from '../components/Header.astro';
import '../styles/global.css';
const { frontmatter } = Astro.props;
---
<html lang="en">
<head>
</head>
<body>
<Header />
<div class="container">
<div class="meta">
<div>
<time class="date">{frontmatter.pubDate.slice(0, 10)}</time>
<h1 class="title">{frontmatter.title}</h1>
<p class="description">{frontmatter.description}</p>
<span class="author">Author: <span class="author-name">{frontmatter.author}</span></span>
</div>
<img class="poster" src={frontmatter.image.url} alt={frontmatter.image.alt} />
</div>
<div class="body">
<slot />
</div>
</div>
</body>
</html>
请注意我们是如何使用Astro.props将frontmatter
属性传递给这个布局的。现在你可以以任何方式将这些属性添加到你的模板中。
另外,注意插槽元素。这是内容出现在最终HTML页面上的地方。
还有一步。我们需要把这个布局添加到我们的内容中,所以我们回到我们的第一个Markdown文件,像以下代码那样做:
---
layout: ../../layouts/BlogLayout.astro
title: 'Jamstack Explained'
pubDate: 2020-12-14
description: 'Jamstack is not about a specific technology…'
author: 'Denis Kostrzewa'
image:
url: '<http://bejamas.io/_next/image/?.>..'
alt: 'Jamstack Explained Photo'
---
# Jamstack Explained
Jamstack is no longer a buzzword over which dev keyword warriors brawl. It has grown to become a booming ecosystem of tools helping developers ship performant websites, progressive web apps, and other projects with benefits too good to ignore.
正如你在第一行代码中所看到的,Astro有一个特殊的layout
属性用于Markdown页面。这个属性定义了一个Astro布局组件的相对路径。
在localhost:3000/blog/jamstack
再次预览,看看布局给你的页面添加了什么。
你是否注意到,我们不得不在我们的BlogLayout.astro
布局中添加<head>
和<body>
标签?难道我们不能直接使用我们的BaseLayout
来做这个吗?
因为Astor布局基本上是组件,那么我们可以嵌套它们。所以我们需要做的就是把我们的BaseLayout
添加到新的BlogLayout.astro
文件中:
---
import BaseLayout from "./BaseLayout.astro";
const { frontmatter } = Astro.props;
---
<BaseLayout pageTitle={frontmatter.title}>
<div class="container">
<div class="meta">
<div>
<time class="date">{frontmatter.pubDate.slice(0, 10)}</time>
<h1 class="title">{frontmatter.title}</h1>
<p class="description">{frontmatter.description}</p>
<span class="author">Author: <span class="author-name">{frontmatter.author}</span></span>
</div>
<img class="poster" src={frontmatter.image.url} alt={frontmatter.image.alt} />
</div>
<div class="body">
<slot />
</div>
</div>
</BaseLayout>
多么整洁,不是吗?
另外,注意到我们是如何将我们页面的标题传递给BaseLayout
中的页面标题的:
<BaseLayout pageTitle={frontmatter.title}>
让我们给BlogLayout
添加一些样式,我将在BlogLayout.astro
文件中添加作用域内的样式:
---
import BaseLayout from "./BaseLayout.astro";
const { frontmatter } = Astro.props;
---
<style>
.container {
padding: 3rem 0;
max-width: 1152px;
}
.meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
}
/* And more... */
</style>
<BaseLayout pageTitle={frontmatter.title}>
<!-- ... -->
</BaseLayout>
现在页面看起来长这样:
我们需要做的最后一件事是在我们的主页上显示这些文章。
在你的blog
目录中添加更多的博客文章,这样我们就可以在我们的主页上创建一个列表。
Astro.glob()
允许你将本地文件加载到你的静态页面上。它将返回一个对象数组,每个博客文章都有一个,包含所有关于你的Markdown文件的信息。
下面是你如何在你的index.astro
文件中使用它:
---
const blogs = await Astro.glob('../pages/blog/*.md');
---
为了动态地生成整个文章列表,我们对Astro.glob()
返回的博客数组进行map
遍历,如以下代码:
<section class="section blog-section" id="blog-section">
<div class="container">
<h2 class="section-title">Our Blog</h2>
<ul class="blogs">
{blogs.map((blog) =>
<li>
<article class="blog">
<img src={blog.frontmatter.image.url} alt="blog.frontmatter.image.alt" />
<h3>{blog.frontmatter.title}</h3>
<p>{blog.frontmatter.description}</p>
<a href={blog.url} class="">Read more</a>
</article>
</li>
)}
</ul>
</div>
</section>
我们在index.astro
文件中的案例研究部分下面添加了这个文件。
在浏览器中重新审视你的主页,享受你在页面底部添加的博客文章列表。在向global.css
添加一些样式后,页面看起来是这样的:
如果你点击”Read more”链接,你会看到每篇文章的内容。
这里是目前为止项目的demo。
你可以使用标准的HTML <script>
标签向你的Astro组件添加交互。这允许你发送JavaScript到浏览器中运行,并为你的Astro组件添加功能。例如,对于切换明亮模式到暗黑模式,你不需要一个JavaScript框架,你可以用普通的JavaScript来处理它。
在.astro
文件中,你可以用与HTML文件相同的标准方式添加客户端JavaScript:
<script>
console.log('Print this to the browser console');
</script>
<!-- relative path to script at `src/scripts/local.js` -->
<script src="../scripts/local.js"></script>
<!-- also works for local TypeScript files -->
<script src="./script-with-types.ts"></script>
<script is:inline src="<http://my-extenrnal-script.com/script.js>"></script>
你添加的所有脚本将被处理并捆绑在Astro中,然后用type="module"
注入到页面的<head>
中。
如果你想避免捆绑脚本,你可以使用is:inline
指令导入一个外部文件,就像例子中最后一个那样。
Astro最引人注目的特点之一是它可以灵活地与你喜欢的JS框架集成。而且你不必只使用一个框架,你可以使用多个。
在我们的项目中,我想在主页的底部添加一个FAQ部分。我没有太多的时间,只想使用别人的作品,以便能够尽快创建我的页面。我搜索了一下FAQ的React组件,出现了一些链接。
那么问题来了,如何将React组件添加到你的项目中。
首先,你需要将React添加到你的项目中。在你的终端运行以下命令:
npx astro add react
你可以简单地在你的项目中编写自己的React组件,在src/components
目录下添加一个.jsx
文件。
由于我想导入一个React组件而不是自己写,所以我需要先把它添加到我的项目中。
所以我将用我的终端来安装这个包:
npm install react-faq-component
我将把FAQ.jsx
和FAQ.css
文件添加到components
目录中,并对我所导入的组件进行自定义:
import React, { useEffect, useState } from 'react';
import Faq from 'react-faq-component';
import './FAQ.css';
const data = {
rows: [],
};
const styles = {};
const config = {};
export default function FAQ() {
return <div>
<Faq
data={data}
styles={styles}
config={config}
/>
</div>;
}
你的组件一旦准备就绪,你就需要将其导入到页面中:
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Faq from '../components/FAQ.jsx';
const pageTitle = "Bejamas";
---
接着在模板中随心所欲地使用:
<section class="section" id="faq-section">
<h2 class="section-title">Questions you probably want to ask</h2>
<div class="container faq-container">
<Faq />
</div>
</section>
下面是最终效果:
如果你在浏览器中进行查看,你会注意到该组件已经在你的页面上呈现了。然而它没有任何可交互性。由于动作不生效,当你点击按钮时,你无法展开子项。
我将在下一节向你展示我们需要的东西。
正如你在上一个例子中看到的,这个组件被添加到了页面上,但它没有生效。发生这种情况是因为,默认情况下,你的框架组件只会在服务端渲染为静态HTML,所以,你的点击处理器或状态钩子就不会生效。这对于添加非交互式的组件非常有用,可以避免向客户端发送任何不必要的JavaScript。
你可以通过添加Astro指令使一个框架组件具有交互性(hydrated)。这些组件属性指明组件的JavaScript何时应该被发送到浏览器。
我在这里罗列出一些Astro的指令:
<Faq client:load />
在页面加载时渲染该组件。<Faq client:idle />
一旦浏览器有空闲时间,就会渲染该组件。<Faq client:visible />
只有当该组件被滚动到视口范围中时才会渲染。<Faq client:media="{media query here}" />
只有在媒体查询生效的情况下才会渲染该组件。<Faq client:only="react" />
只在客户端渲染该组件,而不会在服务端渲染成静态HTML。如果你不像上一个例子那样给你的组件添加任何这些东西,该组件的纯HTML版本将在浏览器中渲染,所以任何点击处理器或状态都不会生效。
我想在页面加载时加载我的FAQ组件,所以这就是我需要的:
<Faq client:load />
请注意,所有这些都来自于Astro中的Island Architecture。
最后,这是我们的最终效果。
Astro网站是开箱即用型的静态网站。这意味着所有的页面在最后都是静态HTML。因此,如果你拥有一台服务器,你部署网站的第一个选择是仅将你的最终HTML文件上传到你的服务器。
在你部署你的网站之前,你需要构建它。要做到这一点,你需要从你的Astro项目的根目录中运行构建命令。你可以通过在你的终端运行以下命令来做到这一点:
npm run build
项目的最终构建将被默认存储在dist
文件夹中。所以,你需要做的就是把你的dist
目录上传到你的服务器。 手动部署你的网站不是现在的首选方法。有大量的工具和服务可以为你自动完成整个工作流程。你所需要做的就是推送你的变化,他们将为你构建和部署项目。
你可以按照Astro网站上的指南,看看你如何在不同的部署服务上部署你的项目,如Netlify、Vercel、Deno等。
总之,作为网络开发者,JavaScript框架的兴起赋予了我们诸多力量和丰富的体验。我们喜欢使用这些框架,因为它们使创建组件、共享和重用它们变得很容易。围绕使用Vue、React和Svelte等框架进行构建的工具是一流的。然而,使用这些框架的代价往往是在我们的页面上发送大量的JavaScript,即使是简单的静态内容。
有了Astro,我们就能得到两者的好处。我们仍然可以使用JSX和JavaScript编写动态内容,但Astro将其全部渲染成静态HTML,所以我们只加载我们真正需要的JavaScript。这样一来,我们的用户就有了快速、无缝的体验,而我们仍然可以享受到使用动态框架工作的所有好处。这对用户和开发者来说是一个双赢的局面!
以上就是本文的全部内容,如果对你有所帮助,欢迎点赞、收藏、转发~
大家好,又见面了,我是你们的朋友全栈君。6.springmvc的工作流程是什么?SpringMVC流程1、用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。2、DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)3、DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller4、HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet5、DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。6、DispatcherServlet将模型数据填充到视图中7、DispatcherServlet将结果响应给用户7.mybatis的工作原理是什么?mybatis的缓存的理
本文实例讲述了PHP模拟一般面向对象语言中的方法重载(overload)。分享给大家供大家参考,具体如下:在一般的面向对象设计语言(如C++,Java)中的方法重载就是定义相同的方法名,通过“参数的个数”不同或“参数的类型”不同,来访问我们的相同方法名的不同方法。但是PHP中,方法是不能重载的,因为PHP是弱类型的语言,所以在方法的参数中本身就可以接收不同类型的数据,又因为PHP的方法可以接收不定个数的参数,所以通过传递不同个数的参数调用不相同方法名的不同方法也是不成立的。所以在PHP里面没有传统上的重载(overload)方法,但由于php其灵活性,可以模拟一般面向对象语言中的重载方法。在PHP中模拟一般面向对象语言中的方法重载(overload)首先来看一个例子:<?php /* 重写/覆盖override指:子类重写了父类的同名方法 重载:overload指:存在多个同名方法,但参数类型/个数不同.传不同的参数,调用不同的方法。 但是在PHP中,不允许存在多个同名方法。因此,不能够完成java,c++中的这种重载。 但是,PHP的灵活,能达到类似的效果 */ //在PHP中
流量拷贝工具,你用过那几个?在进行灰度又或者进行压测(或者放大倍率)的时候,我们可能会选择流量拷贝的方案来佐证我们架构设计的可行性和可用性,关于流量拷贝这块,可能大家听到的额最多的是老牌工具tcpcopy,除此之外还有goreplay,sharingan,下面我们挨个简单介绍下(只是介绍),具体的使用还是要参考官方文档。流量拷贝的工具•tcpcopy•goreplay•sharingantcpcopyAnonlinerequestreplicationtool,alsoatcpstreamreplaytool,fitforrealtesting,performancetesting,stabilitytesting,stresstesting,loadtesting,smoketesting,etc支持实时数据流复制,同时也支持TCP流量复制,适用于性能测试,压力测试,冒烟测试场景的开源工具。tcpcopy[1]•3.6kstar•基于c语言goreplayGoReplayisanopen-sourcetoolforcapturingandreplayingliveHTTPtraffi
“磨刀不误砍柴工”。一款好的工具对提高工作效率有着巨大帮助,安全工程师同样需要一款优秀的安全软件来提高自己的工作效率。在具体的工作场景中,会有不同的选择,这里有10款开源免费的安全工具推荐给大家,不仅可以提高工作效率,还可以降低企业成本。NmapNmap(NetworkMapper)是一款免费开源的安全扫描工具,主要用于端口扫描、网络探测等。Nmap也是系统和网络管理员的最爱,常被用于监控主机或服务正常运行时间,管理服务升级计划和网络库存等任务。Nmap通常利用原始IP数据包来探测网络上可用的主机,并能为我们获取目标系统的版本及服务等信息。作为一款专业而强大的安全扫描工具,企业需要掌握更多的技术知识才能充分利用它。该工具专为较大的网络而设计,并可在所有主流的操作系统上运行。下载地址:https://nmap.org/SecurityOnionSecurityOnion(安全洋葱)基于Ubuntu,包含了入侵检测、网络安全监控、日志管理所需的Snort、Suricata、Bro、OSSEC、Sguil、Squert、ELSA、Xplico、NetworkMiner等众多工具。Securi
简单工厂模式:含义:简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(StaticFactoryMethod)模式,它属于类创建型模式。问题何时使用简单工厂模式,我的理解是:某类产品的成产线。通过设置不同的参数,生产出同一类别下不同形态的产品。 举个例子: 手机生产线上,通过设置不同的参数既可以生成小米手机,也可以生成IPhone手机。优点工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。缺点由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。 当系统中的具体产品类不断增多时候,可能会出现
在Python中有着三类特殊方法:静态方法、类方法以及抽象方法。今天我们来谈谈其中的这三类特殊方法。在此之前你可能已经写过许许多多的方法但从未深入的思考。静态方法静态方法是属于类的方法,但实际上并非在类的实例上运行。示例1@staticmethod的用法也可以像非静态方法那样写fun_1,它会接收self作为参数但是不会真地使用它。装饰器@staticmethod提供了下面几点特性: ①Python不必为我们创建的每个Pizza对象实例化来绑定一个方法。绑定方法也是创建对象,创建对象则需要开辟新的空间,消耗资源。而静态方法则规避了这类开销:②提高了代码的可读性。当看到staticmethod时,我们可以立即知道这个方法不需要创建对象就可以直接使用。③可以在子类中覆盖静态方法。类方法类方法是直接绑定到类的而非它的实例的方法:如果我们选择访问这个方法,它会被绑定在附属的类上面,它的第一个参数是类本身cls。记住,类也是对象。抽象方法抽象方法是定义在基类中可能有或者没有任何实现的方法。Python中一个最简单的抽象方法类似下面的func_4:任何继承了这个含有抽象方法都必须重写这个抽象方法,
作者介绍:玉伯,支付宝前端,seajs作者,国内开源支持者,中文github排行第二。本文首发于蚂蚁金服体验科技-知乎专栏,大家可以通过点击文章下方的阅读原文来访问原文地址。视频在文章下方。什么是前端技术第一次接触前端开发是2002年大学期间,转眼15年多。这些年一直在思考一个问题:究竟什么是前端技术?很长很长一段时间,前端技术的定义非常清晰,就是浏览器端的HTML、CSS、JS技术。我们用这些技术做出各种各样的页面,我们是离用户最近的程序员。记得2009年开始接触Node,很快前端技术开始爆炸性增长。最开始的变化,是前端压缩工具从基于Java的YUICompressor开始切换到基于Node实现的UglifyJS等工具。除了前端工具上的一路狂奔,在服务端领域也出现了Express等框架,前端开始通过Node完成服务端模板甚至整个MVC层的开发。在蚂蚁金服,服务端层我们更多把Node定位为BFF层实现,BFF是BackendForFrontend的缩写,翻译成用户体验适配层。 图片来源TheBack-endforFront-endPattern(BFF)BFF模式下,整体分工很清晰,后
前言做任何事情都不是想象中的那么简单。好久没有更新技术博客了,跟最近瞎忙有很大关系,虽说是瞎忙也抽空研究了些技术。主要是前端渲染,像原生的WebGL和Cesium。WebGL写了几篇博客,自我感觉还可以。Cesium是一个封装好的WEB端3DEarth框架,有了WebGL的基础之后切换到Cesium按理说一切应该是顺理成章,简单的测试了几个功能之后发现确实非常好,简单的几行代码就可以实现GoogleEarth的功能,当然GoogleEarth重要的绝对不是他的渲染框架。前期做了很多Geotrellis的工作,那么我就想着能不能把Geotrellis发布的TMS加载到Cesium中来,本来这是很简单的嘛,以前是在leaft-let中显示,现在就是换一个地方显示而已,并且Cesium已经调通。说干就干,结果怎么着,前天晚上整到四点,昨天折腾了几个小时居然一直不出图,所以我说任何看似简单的事情其实都不简单,下面就让我娓娓道来。一、Cesium1.1简介介绍之前还是来简单介绍一下Cesium,当然如果后面继续对此框架进行研究的话可能也会多写几篇关于此框架的博客。官网地址:https://ces
XXXX大学C语言课程设计报告题目图书管理系统设计专业班级XXXX级计算机科学与技术本科X班组别计科第29组学生姓名XXX、XXX、XXX院(系)信息工程系指导教师(职称)XXX(教授)完成时间xxxx年x月xx日XXX大学课程设计任务书题目图书管理系统设计班级xxxx级计算机科学与技术本科x班学号xxxxxxxxxxxx姓名xxx学号xxxxxxxxxxxx姓名xxx学号xxxxxxxxxxxx姓名xxx一、主要内容:本课程设计结合本学期所学C语言知识,数组、函数、结构体、指针、链表、文件读取操作等等,准备设计开发一个简单的图书管理系统。设计开发这个系统需要用到链表、文件读取操作、结构体、函数、指针、等C语言知识。本课程设计将会实现对图书信息的账号登录、注册账号、密码修改、密码查找、查找、输出、排序、备份、恢复、图书借阅和归还功能。本着简单、易用的设计原则,本课程设计在尽量优化界面在保证输入输出美观的同时又不失友好的交互界面。本次设计主要学习内容包括:(一)进一步学习并熟练掌握C语言语法和编程思想。(二)学习C语言提供的库函数,熟悉CodeBlocks的开发工具。(三)学习C语言函数
1.接口描述接口请求域名:tsf.tencentcloudapi.com。 创建容器部署组 默认接口请求频率限制:20次/秒。 APIExplorer提供了在线调用、签名验证、SDK代码生成和快速检索接口等能力。您可查看每次调用的请求内容和返回结果以及自动生成SDK调用示例。 2.输入参数以下请求参数列表仅列出了接口请求参数和部分公共参数,完整公共参数列表见公共请求参数。 参数名称 必选 类型 描述 Action 是 String 公共参数,本接口取值:CreateContainGroup。 Version 是 String 公共参数,本接口取值:2018-03-26。 Region 是 String 公共参数,详见产品支持的地域列表。 ApplicationId 是 String 分组所属应用ID NamespaceId 是 String 分组所属命名空间ID GroupName 是 String 分组名称字段,长度1~60,字母或下划线开头,可包含字母数字下划线 InstanceNum 是 Integer 实例数量 Acc
spring依赖注入的方式有4种 构造方法注入 属性注入 工厂注入 注解注入 下面通过一个实例统一讲解: User.java复制 packagecom.bjsxt.model; publicclassUser{ privateStringusername; privateStringpassword; publicUser(){} publicUser(Stringusername,Stringpassword){ super(); this.username=username; this.password=password; } publicStringgetUsername(){ returnusername; } publicvoidsetUsername(Stringusername){ this.username=username; } publicStringgetPassword(){ returnpassword; } publicvoidsetPassword(Stringpassword){ this.password=password; } @Overri
上篇文章讲了卷积神经网络的基本知识,本来这篇文章准备继续深入讲CNN的相关知识和手写CNN,但是有很多同学跟我发邮件或私信问我关于PaddlePaddle如何读取数据、做数据预处理相关的内容。网上看的很多教程都是几个常见的例子,数据集不需要自己准备,所以不需要关心,但是实际做项目的时候做数据预处理感觉一头雾水,所以我就写一篇文章汇总一下,讲讲如何用PaddlePaddle做数据预处理。 PaddlePaddle的基本数据格式 根据官网的资料,总结出PaddlePaddle支持多种不同的数据格式,包括四种数据类型和三种序列格式: 四种数据类型: dense_vector:稠密的浮点数向量。 sparse_binary_vector:稀疏的二值向量,即大部分值为0,但有值的地方必须为1。 sparse_float_vector:稀疏的向量,即大部分值为0,但有值的部分可以是任何浮点数。 integer:整型格式 api如下: paddle.v2.data_type.dense_vector(dim, seq_type=0) 说明:稠密向量,
概率无向图模型 又称马尔可夫随机场(Markovrandomfield)或马尔可夫网络,是一个由无向图表示的联合概率分布。 图是由结点和边组成,无向图中的边没有方向。概率无向图中结点表示随机变量,边表示结点之间的概率依赖关系。 成对马尔可夫性: 设u和v是无向图G中任意两个没有连接边的结点,对应随机变量分别为Yu和Yv,图中其他所有结点为O,对应随机变量组YO,那么给定随机变量组YO的条件下,Yu和Yv是条件独立的,此为成对马尔可夫性,即 (1) 局部马尔可夫性: 设v是无向图G中的任意一个节点,W是与v有边连接的所有结点,O是除v和W之外的其他所有结点(即,与v没有边连接的所有结点),给定随机变量组YW的条件下,Yv与YO是条件独立的,此为局部马尔可夫性,即, (2) 如果P(YO|YW)>0,那么(2)式两边同时除以P(YO|YW), (3) 下图表示局部马尔可夫性, 上图中,假设某个黑点表示v,那么与其有边连接的有一个或大于一个白点为W,剩余的其他点(包括白点和黑点
1WebService基础 1.1作用 1, WebService是两个系统的远程调用,使两个系统进行数据交互,如应用: 天气预报服务、银行ATM取款、使用邮箱账号登录各网站等。 2, WebService之间的调用是跨语言的调用。Java、.Net、php,发送Http请求,使用的数据格式是XML格式。 3, webxml.com.cn上面有一些免费的WebService服务,可以进去看看。 1.2应用基础 4, 基础概念: (1),理解服务: 现在的应用程序变得越来越复杂,甚至只靠单一的应用程序无法完成全部的工作。更别说只使用一种语言了。因此需要访问别人写的服务,以获得感兴趣的数据。 在写应用程序查询数据库时,并没有考虑过为什么可以将查询结果返回给上层的应用程序,甚至认为,这就是数据库应该做的,其实不然,这
假设有这样的场景,开发者用的是Windows系统,且系统的存储资源和内存有限,在运行VMware虚拟机中做一些测试时,通常会碍于电脑的VMWare客户端图形界面的响应速度太慢。而在Xshell中对虚拟机进行操作,可以避免客户端中出现的响应慢的问题。 这只是一个假设,一般开发者的电脑都是性能比较好的。而在工作环境中的电脑更不用说了。而本文的重点不在这,只是通过这个场景可以了解到Xshell在连接Windows和Linux系统上的优势。在实际工作中,Xshell都是一个经常被使用到的工具。 下面是摘自百度百科上的描述:Xshell是一款Windows下非常优秀的安全终端模拟软件,它支持SSH1,SSH2,以及MicrosoftWindows平台的TELNET协议。Xshell通过互联网到远程主机的安全连接以及它创新性的设计和特色,帮助用户在复杂的网络环境中享受他们的工作。而本人觉得其内置的ftp文件传输功能更是windows系统和linux系统之间文件传输的强大的接口。 本文先介绍怎么利用Xshell连接Linux虚拟机,后面的文章继续介绍Xshell的使用技巧
使用者-->Ansible工具集-->作用对象 使用者调用工具集,作用于对象之上。 使用者: 1、CMDB系统 2、 3、手工命令 4、将手工命令编写为playbook 工具集: 1、INVENTORY 2、API 3、MODULES 4、PLUGINS 作用对象: 1、主机 2、网络 在执行ansible命令时,其内部的调用关系为: 系统下所有的操作可从运维操作角度划分为两类。 1、文件传输 2、命令执行 系统下所有的操作可从自动化工作类型角度归为三类。 1、应用部署 2、配置管理 3、任务流编排 安装方式: 1、通过yum或apt安装 2、通过pip或easy_install安装 验证安装结果: ansible--version
Day0 没有直达焦作的飞机,所以选择了先到新郑机场,再转乘城际列车。城际列车猜是专门给学生开通的吧,每天只有来和回一共两趟(所以机票选择的余地也不多)。买的时候只有无座票了,本来以为会一直站着,但上车才发现是只在卖无座票,而新郑机场是首发站,所以随便坐就行了,不用担心要给后上车的人让座位的,买了无座票还全程坐着的感觉真爽_(:з」∠)_ Day1 领衣服(羽绒马甲!纯蓝色没有任何广告!),领餐券(5*3+2*2+1=20元/顿),去了食堂一看,主食居然都是6元8元一份?!尤其是面食,价格低的不敢相信,20元都够吃三四顿了啊,于是不小心点太多,吃撑了QAQ。然后是开幕式和热身赛,因为吃得太多在校园里四处乱逛找不到路,所以错过了开幕式orz,热身赛打的也很水,测了一下各种RE的情况,都有详细的报错,甚至把警告也写出来了,真的很贴心;Java测试了一下,没看懂提示信息,感觉没有成功运行。 晚饭再次吃撑——我真的没想到10元的饺子能有四五十个,只吃了十几个就吃不下了,对不起我不是故意浪费粮食的QAQ。 Day2 酒店早上的炒蛋很好吃所以吃了两盘(?) 比赛情况: A题,8分钟完成签到; I
【题目描述】 19.删除链表的倒数第N个节点给定一个链表,删除链表的倒数第?n?个节点,并且返回链表的头结点。 示例:给定一个链表:1->2->3->4->5,和n=2.当删除了倒数第二个节点后,链表变为1->2->3->5. 说明:给定的n?保证是有效的。 进阶:你能尝试使用一趟扫描实现吗? 来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list 【提交代码】 1/** 2*Definitionforsingly-linkedlist. 3*structListNode{ 4*intval; 5*structListNode*next; 6*}; 7*/ 8 9structListNode*removeNthFromEnd(structListNode*head,intn){ 10inti; 11structListNode*first; 12structListNode*second; 13structListNode*dumm
Java重难点: 1、Map类型的对象中的键值支持转义字符 importjava.io.*; importjava.util.*; classtest { publicstaticvoidmain(String[]args)throwsjava.lang.Exception { Map<String,String>map=newHashMap<String,String>(); map.put("aa","good\n\n\n\n\n"); System.out.println(map.get("aa")); System.out.println("test"); } }复制 输出结果如下所示: 2、JSON字符串中Boolean类型值的存取 importjava.io.*; importjava.util.*; classtest { publicstaticvoidmain(String[]args)throwsjava.lang.Exception { Mapmap=newHashMap(); map.put("aa