将 dev.to 帖子嵌入到您自己的网站
我最近在 dev.to - DEV 社区上发表了我的第一篇文章,其中提到了将我的 dev.to 文章整合到我自己的网站中。现在就开始吧!
如果你还没有看过,我的网站是用 SvelteKit 和 Tailwind CSS 构建的,它在这里完全开源:https://github.com/nunogois/nunogois-website
您可以在此处查看此功能的主要提交,但我会尝试分解下面的重要部分。
API
首先,我们需要获取帖子,使用API很容易做到。我使用了getLatestArticles端点,它返回“按发布日期排序的已发布文章”。
就我而言,它看起来像这样:
GET https://dev.to/api/articles/latest?username=nunogois
- 你可以在浏览器中访问此 URL 进行测试。
分页是我稍后需要考虑的事情。
无论如何,为了将其与我的网站集成,我利用了SvelteKit 的端点,这是我最喜欢的 SvelteKit 功能之一,您可以在其中看到src/routes/api.ts
:
// ...
export async function get(): Promise<EndpointOutput> {
return {
body: {
// ...
blog: await loadBlog()
}
}
}
export const loadBlog = async (): Promise<JSONString[]> =>
await fetch('https://dev.to/api/articles/latest?username=nunogois').then((res) => res.json())
然后,这个端点被提取到我的index.svelte
文件中,并将blog
数组作为 prop 传递给我的Blog
组件:
<script context="module">
export async function load({ fetch }) {
// ...
const res = await fetch('/api')
if (res.ok) {
return {
props: await res.json()
}
}
}
</script>
<script lang="ts">
//...
export let blog
</script>
<Blog {blog} />
博客
我的Blog
组件只不过是我的单页网站的一部分。这里相关的部分是为每篇博客文章迭代并渲染一些内容,你可以在以下代码中看到src/pages/blog.svelte
:
{#each filteredBlog as { slug, title, description, readable_publish_date, cover_image, tag_list, positive_reactions_count, comments_count, reading_time_minutes }}
<div class="border border-light-gray rounded-xl">
<a sveltekit:prefetch href={`/blog/${slug}`} class="flex flex-col h-full">
<img src={cover_image} alt={title} class="w-full rounded-t-xl object-cover" />
<h4 class="flex justify-center items-center text-lg font-medium p-2 border-light-gray">
{title}
</h4>
<span class="text-xs text-gray-300 mb-1"
>{readable_publish_date} - {reading_time_minutes} min read</span
>
<span class="text-xs text-gray-300">{tag_list.map((tag) => `#${tag}`).join(' ')}</span>
<div class="text-xs my-3 mx-5 text-justify">
{description}
</div>
<div class="flex-1 grid grid-cols-2 text-sm content-end">
<div class="flex justify-center items-center border-t border-light-gray border-r p-1">
<Icon icon="fa:heart" width="16px" class="inline-block mr-1" />
{positive_reactions_count}
</div>
<div class="flex justify-center items-center border-t border-light-gray border-r p-1">
<Icon icon="fa:comment" width="16px" class="inline-block mr-1" />
{comments_count}
</div>
</div>
</a>
</div>
{/each}
目前有点乱,充斥着各种 Tailwind CSS 类和一些小调整,但目前看起来已经完全符合我的要求了。可能很快会把它重构成一个独立的组件(BlogItem
或者类似的东西)。
现在我们已经显示了所有博客文章,我们需要一种打开和阅读它们的方法。注意上面的锚标签:
<a sveltekit:prefetch href={`/blog/${slug}`}...
这slug
是博客文章的唯一标识。
蛞蝓
利用 SvelteKit 的更多酷炫功能,我创建了一个新src/routes/blog/[slug].svelte
文件:
<script context="module" lang="ts">
// ...
import Icon from '@iconify/svelte'
export async function load({ page, fetch }) {
const url = `https://dev.to/api/articles/nunogois/${page.params.slug}`
const response = await fetch(url)
return {
status: response.status,
props: {
post: response.ok && (await response.json())
}
}
}
</script>
<script lang="ts">
export let post
</script>
<div class="flex justify-center">
<div class="flex flex-col w-full px-4 md:px-24 max-w-screen-lg text-justify pt-16">
<div class="border-b border-light-gray md:border md:rounded-xl">
<img src={post.cover_image} alt={post.title} class="w-full rounded-t-xl object-cover mb-4" />
<div class="md:px-4">
<div class="flex">
<h3 class="w-full text-left text-2xl md:text-3xl font-medium">
{post.title}
</h3>
<a href={post.url} class="w-8"
><Icon icon="fa-brands:dev" width="32px" class="inline-block" /></a
>
</div>
<div class="flex flex-col pt-2 pb-6 gap-1 text-xs text-gray-300">
<span>{post.readable_publish_date}</span>
<span>{post.tags.map((tag) => `#${tag}`).join(' ')}</span>
</div>
<div class="blog-post">
{@html post.body_html}
</div>
</div>
</div>
<a href={post.url} class="mt-5 text-center">React to this blog post on DEV Community 👩💻👨💻</a>
<a href="/" class="my-5 text-center text-sm">www.nunogois.com</a>
</div>
</div>
这段代码slug
从 URL 中获取了相应的文章端点,并将其传递给 props。之后,我们只需要按照自己的意愿渲染文章即可。
CSS
以下是我目前添加的具体 CSS,src/app.css
用于正确显示博客文章及其嵌入的内容:
.blog-post p {
margin-bottom: 20px;
}
.blog-post > .crayons-card {
border-width: 1px;
--tw-border-opacity: 1;
border-color: rgb(51 51 51 / var(--tw-border-opacity));
border-radius: 0.75rem;
margin-bottom: 20px;
}
.blog-post > .crayons-card > .c-embed__cover img {
object-fit: cover;
max-height: 200px;
border-top-left-radius: 0.75rem;
border-top-right-radius: 0.75rem;
}
.blog-post > .crayons-card > .c-embed__body {
padding: 20px;
}
.blog-post > .crayons-card > .c-embed__body > h2 {
margin-bottom: 8px;
color: #93ceff;
}
.blog-post > .crayons-card > .c-embed__body > .truncate-at-3 {
font-size: 0.875rem;
margin-bottom: 8px;
}
.blog-post > .crayons-card > .c-embed__body > .color-secondary {
font-size: 0.875rem;
}
.blog-post > .crayons-card .c-embed__favicon {
max-height: 18px;
width: auto;
margin-right: 14px;
}
您可以在此处看到它的样子:https://www.nunogois.com/blog/hello-world-4pdf
看起来非常漂亮,如果我这么说的话!
动态 sitemap.xml 和 rss.xml
为了获得奖励回合,让我们设置一个动态sitemap.xml
和rss.xml
。
注意:在这里我必须以某种方式在代码中引用它们的端点,以便它们在部署后显示出来,这就是我获取它们的原因index.svelte
:
fetch('/sitemap.xml')
fetch('/rss.xml')
源文件如下所示:
站点地图.xml
https://www.nunogois.com/sitemap.xml
这里是src/routes/sitemap.xml.ts
:
import { loadBlog } from './api'
const website = 'https://www.nunogois.com'
export async function get(): Promise<unknown> {
const posts = await loadBlog()
const body = sitemap(posts)
const headers = {
'Cache-Control': 'max-age=0, s-maxage=3600',
'Content-Type': 'application/xml'
}
return {
headers,
body
}
}
const sitemap = (posts) => `<?xml version="1.0" encoding="UTF-8" ?>
<urlset
xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
xmlns:xhtml="https://www.w3.org/1999/xhtml"
xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
>
<url>
<loc>${website}</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
${posts
.map(
(post) => `
<url>
<loc>${website}/blog/${post.slug}</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
`
)
.join('')}
</urlset>`
rss.xml
https://www.nunogois.com/rss.xml
下面是src/routes/rss.xml.ts
:
import { loadBlog } from './api'
const website = 'https://www.nunogois.com'
export async function get(): Promise<unknown> {
const posts = await loadBlog()
const body = xml(posts)
const headers = {
'Cache-Control': 'max-age=0, s-maxage=3600',
'Content-Type': 'application/xml'
}
return {
headers,
body
}
}
const xml = (
posts
) => `<rss xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:atom="https://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>Nuno Góis - Full-Stack Developer</title>
<link>${website}</link>
<description>Full-Stack Developer from Portugal. Experienced with every step of developing and delivering software projects using .NET C#, JavaScript, Go, Python, and more.</description>
${posts
.map(
(post) =>
`
<item>
<title>${post.title}</title>
<description>${post.description}</description>
<link>${website}/blog/${post.slug}/</link>
<pubDate>${new Date(post.published_timestamp)}</pubDate>
<content:encoded>${post.description}
<br />
<a href="${website}/blog/${post.slug}">
Read more
</a>
</content:encoded>
</item>
`
)
.join('')}
</channel>
</rss>`
结论
在此过程中,我还做了一些修正和优化,最终得到了这个 Lighthouse 分数:
这个集成肯定还没完成,我确信在发布这篇文章后我得再做一些额外的工作才能正确显示它。不过,这是一件相当有趣且轻松的事情。
我可能还应该花一些时间来重构和清理我的网站代码(并在各处使用适当的类型),所以请继续关注。
您可以根据我的设计随意搭建自己的网站,或者从中汲取一些灵感。如果您愿意,我建议您查看以下文档:
另外,请分享,我很想看看!
鏂囩珷鏉ユ簮锛�https://dev.to/nunogois/embed-devto-posts-into-your-own-website-36jj