加载中...

掘金是用什么写的?Nuxt就可以实现一模一样的效果

掘金是用什么写的?Nuxt就可以实现一模一样的效果

为了提高自己的全栈能力,学完Vue的我,又开始学习了Nuxt,以下就是我用Nuxt创建了一个简单的论坛。

1.Nuxt是什么?

Nuxt.js 是一个基于 Vue.js 的框架,主要用于构建服务端渲染(SSR)或静态网站生成(SSG)的应用。它提供了一些开箱即用的功能,简化了 Vue 应用的开发过程。

主要特点:

  1. 服务端渲染:Nuxt.js 可以让你的 Vue 应用在服务器上渲染,提升页面加载速度和 SEO 优化。
  2. 静态网站生成:可以将应用生成静态文件,便于部署和快速加载。
  3. 路由自动生成:Nuxt 会根据 pages 目录中的文件自动生成路由,不需要手动配置。
  4. 模块化架构:内置许多模块,支持状态管理(Vuex)、API 请求、PWA 等功能,方便扩展。
  5. 插件支持:可以在应用中轻松引入各种插件,增强功能。

生活中的例子:

想象一下,你想做一个电商网站。使用 Nuxt.js,你可以:

  • 提升 SEO:通过 SSR,让搜索引擎更容易抓取页面内容,增加曝光率。
  • 快速加载:生成静态页面,使用户在访问时能更快看到商品。
  • 简单路由:只需在 pages 文件夹中创建对应的文件,自动生成商品详情、购物车等页面。

帮助你更高效地开发现代化的 Vue 应用,特别是在 SEO 和性能方面表现优异。

2.ssr是什么?spa又是什么?

SSR(Server-Side Rendering)和 SPA(Single Page Application)是两种不同的前端渲染方式,各有优缺点。

1. SSR(服务端渲染)

服务端渲染指的是网页的内容在服务器端生成后,发送到客户端,浏览器直接显示完整的 HTML 页面。

工作流程:

  • 用户请求一个网页时,服务器先把页面的 HTML 生成好,带着数据一起返回给用户。
  • 浏览器拿到这个完整的页面后直接渲染出来,用户立即看到页面内容。
  • 当页面加载完成后,再通过 JavaScript 使页面变得“互动”。

优点:

  • SEO 友好:因为搜索引擎能抓取到完整的 HTML 内容。
  • 首屏加载快:用户可以快速看到页面内容,因为不需要先加载 JavaScript 再渲染页面。

缺点:

  • 服务器压力大:每次请求都需要服务器生成完整的页面。
  • 交互延迟:页面初始加载后,直到 JavaScript 完全加载,页面交互才能生效。

2. SPA(单页应用)

单页应用指的是整个应用只有一个 HTML 页面,所有页面内容和交互都是通过 JavaScript 动态加载和切换的。

工作流程:

  • 首次加载时,服务器只返回一个空的 HTML 壳和 JavaScript 文件。
  • JavaScript 运行后,通过 API 请求数据并渲染页面内容。
  • 后续页面切换时,只会加载部分内容,不会刷新整个页面。

优点:

  • 流畅的用户体验:页面切换无需刷新,用户体验更好。
  • 减少服务器负载:页面的大部分逻辑都在客户端处理,减少了服务器压力。

缺点:

  • 首屏加载慢:用户必须先下载 JavaScript 文件才能渲染页面内容。
  • SEO 不友好:因为初始 HTML 为空,搜索引擎可能抓取不到有价值的内容(可以通过 SSR 或其他方式解决)。

ssr和spa的对比:

ssr:就像去餐馆点菜,服务员会把所有菜肴准备好,一起送到你的桌上,你马上就能开始吃。

spa:就像吃自助餐,开始的时候拿一个空盘子,自己去拿食物,后续想吃什么就直接去取,不用每次都回到桌子上等服务员端上菜。

总结:

  • SSR 更适合需要快速加载和注重 SEO 的场景,如新闻网站、博客等。
  • SPA 适合需要高频交互和流畅体验的场景,如社交网络、后台管理系统等。

用Nuxt开发论坛

服务端渲染语言Nuxt + 强类型约束TS + 模拟后端接口json-server 实现一个简易的论坛。

如何使用 Nuxt.js

1. 安装 Nuxt 项目

首先,通过命令行工具创建 Nuxt.js 项目:

swift
代码解读
复制代码
npx nuxi init <project-name> cd <project-name> npm install

这里 nuxi 是 Nuxt 3 的 CLI 工具,用来初始化和管理 Nuxt 项目。

2. 运行 Nuxt 项目

安装完所有依赖后,可以启动开发服务器:

arduino
代码解读
复制代码
npm run dev

默认情况下,Nuxt 开发服务器运行在 http://localhost:3000

3. 目录结构

Nuxt 有特定的目录结构,它基于文件系统来定义路由、布局、页面等。一个标准的 Nuxt 项目通常包含以下目录:

  • pages/:页面文件夹,文件名决定了路由。例如 pages/index.vue 对应 / 路由,pages/about.vue 对应 /about
  • layouts/:用于定义布局,页面可以继承不同的布局。
  • components/:用于存放 Vue 组件。
  • store/:用于定义 Vuex store 文件,用于管理全局状态。
  • static/:存放静态文件,如图片、字体等。

4. 创建页面和路由

Nuxt 自动根据 pages 目录下的文件生成路由。例如,创建以下文件结构:

代码解读
复制代码
pages/ ├── index.vue ├── about.vue

index.vue 对应 / 路由,about.vue 对应 /about 路由。

5. 使用布局(layouts)

Nuxt 支持布局系统,可以为页面定义不同的布局。默认布局文件是 layouts/default.vue,每个页面会自动使用该布局。

例如,定义一个布局:

xml
代码解读
复制代码
<!-- layouts/default.vue --> <template> <div> <header> <h1>My Website Header</h1> </header> <nuxt /> <footer> <p>Footer</p> </footer> </div> </template>

页面的内容会被插入 <nuxt /> 的位置。

6. 使用 Vuex(store)

如果你需要全局状态管理,Nuxt 内置对 Vuex 的支持。创建 store/index.js 文件,Nuxt 会自动处理。

javascript
代码解读
复制代码
// store/index.js export const state = () => ({ counter: 0 }) export const mutations = { increment(state) { state.counter++ } }

然后可以在页面或组件中使用它:

xml
代码解读
复制代码
<template> <div> <p>Counter: {{ $store.state.counter }}</p> <button @click="$store.commit(''increment'')">增加计数</button> </div> </template>

7. API 请求

Nuxt 提供了 asyncData 方法,允许你在页面加载前获取数据。这个方法会在服务端或客户端执行,取决于页面的渲染模式。

xml
代码解读
复制代码
<template> <div> <h1>{{ post.title }}</h1> <p>{{ post.content }}</p> </div> </template> <script> export default { async asyncData({ $axios, params }) { const post = await $axios.$get(`/api/posts/${params.id}`) return { post } } } </script>

如何用json-server

1. 安装 json-server

首先,你需要在项目中安装 json-server。可以通过 npm 或 yarn 安装。

vbscript
代码解读
复制代码
npm install -g json-server

全局安装后,你可以在任何地方使用 json-server

2. 创建一个 JSON 文件

接下来,你需要创建一个模拟数据库文件,通常叫做 db.json,这个文件会作为 json-server 的数据源。

json
代码解读
复制代码
{ "posts": [ { "id": 1, "title": "如何吸引野生画眉鸟到你的花园?", "content": "画眉鸟以其悦耳的鸣叫闻名。要吸引这些美丽的鸟儿到你的花园,可以在庭院中种植它们喜欢的植物,如灌木和果树,或者放置鸟食器,提供新鲜的水果和昆虫饲料。", "user": { "id": 2, "name": "鸟鸣之声" } }, { "id": 2, "title": "识别常见的城市鸟类:麻雀与鸽子", "content": "在城市环境中,麻雀和鸽子是最常见的鸟类。麻雀通常小巧,身体棕色,喜欢成群结队,而鸽子体型较大,多见于公园和广场附近。通过观察它们的体型和行为,很容易区分这两种鸟类。", "user": { "id": 3, "name": "飞鸟观察家" } }, { "id": 3, "title": "夏季如何为鸟类提供合适的饮水?", "content": "夏天鸟类需要充足的水分,尤其是在干旱地区。你可以在庭院中放置浅水盘,每天更换新鲜的水,并确保水盆安全、无害,让鸟类能安心饮水。", "user": { "id": 4, "name": "自然守护者" } }, { "id": 4, "title": "鸟类迁徙的秘密:如何跟踪候鸟的迁徙路径?", "content": "通过安装GPS设备,科学家们能够跟踪候鸟的迁徙路径,了解它们的迁徙习惯和栖息地变化。这些数据对保护濒危鸟类具有重要意义。", "user": { "id": 5, "name": "迁徙研究员" } }, { "id": 5, "title": "鹦鹉的饲养技巧:从食物到互动", "content": "饲养鹦鹉不仅仅是提供食物,还要注重与它们的互动。鹦鹉喜欢玩具和挑战,给它们提供丰富的精神刺激对健康有好处。食物方面,可以提供多种水果、种子和蔬菜。", "user": { "id": 6, "name": "鹦鹉达人" } }, { "id": 6, "title": "夜莺:夜晚的歌手", "content": "夜莺以其夜晚优美的鸣唱而闻名。夜莺通常在春季开始鸣唱,主要为了吸引伴侣和划定领地。它们的鸣叫节奏复杂,频率多变,非常动听。", "user": { "id": 7, "name": "夜鸟迷" } }, { "id": 7, "title": "如何搭建合适的鸟巢箱?", "content": "自制鸟巢箱可以吸引鸟类在你家附近筑巢。选择合适的材料如天然木材,确保鸟巢的通风和排水,并根据目标鸟类调整入口大小。放置在高处且不易被捕食者攻击的位置是关键。", "user": { "id": 8, "name": "DIY爱好者" } }, { "id": 8, "title": "冬季如何帮助鸟类过冬?", "content": "冬天食物匮乏,鸟类需要额外的能量来抵御寒冷。你可以为它们提供高能量的种子和坚果,或者安装鸟食器,并在寒冷的日子定期补充食物。", "user": { "id": 9, "name": "冬日守护者" } }, { "id": 9, "title": "金翅雀的饲养与繁殖注意事项", "content": "金翅雀是一种美丽的鸟类,饲养它们需要特别注意饮食搭配,多提供新鲜的蔬果和种子。繁殖期间要确保它们有安静的环境,避免过度打扰。", "user": { "id": 10, "name": "金翅爱好者" } }, { "id": 10, "title": "发现稀有鸟类的乐趣:我在森林中的一次探险", "content": "上周,我在森林探险时意外发现了一只罕见的蓝翡翠!它的羽毛色彩斑斓,十分惊艳。这是一次令人难忘的经历,让我更加热爱观鸟。", "user": { "id": 11, "name": "森林探险家" } } ] }

3. 启动 json-server

在包含 db.json 文件的目录下启动 json-server

css
代码解读
复制代码
json-server --watch db.json --port

默认情况下,它会在 http://localhost:3000 上运行

4. 自定义端口

如果你想在自定义端口上运行 json-server,可以使用 --port 选项。例如,运行在 5000 端口:

css
代码解读
复制代码
json-server --watch db.json --port 5000

项目

New Image

内容列表像掘金一样,点进去显示详细

New Image

用Nuxt能有很好SEO,利于网站被访问,浏览器能读取到数据,根据数据推荐给用户,手机端应用一般不会用Nuxt去写。

核心项目片段:

ts
代码解读
复制代码
<template> <div> <h1>内容列表</h1> <div class="post-list" v-if="posts !== null"> <div v-for="post in posts" :key="post.id"> <div> <div> <NuxtLink :to="`/posts/${post.id}`">{{ post.title }}</NuxtLink> </div> <div>{{ post.content}}</div> <div> - <small>{{ post.user.name }}</small> </div> </div> </div> </div> </div> </template> <script setup lang="ts"> const { data:posts, pending, refresh, error } = await useApiFetch(()=> `posts`) </script>

这里自定义了hooks函数,在useApiFetch中去写了useFetch()

ts
代码解读
复制代码
<template> <div class="app"> <header> <div> <span></span> <NuxtLink to="/">{{ name }}</NuxtLink> </div> <nav> <div> <NuxtLink to="/about">关于</NuxtLink> </div> <div> <NuxtLink to="/posts">内容</NuxtLink> </div> <div> <NuxtLink to="/point">特点</NuxtLink> </div> <div> <NuxtLink to="/culture">文化</NuxtLink> </div> </nav> <div> <div v-if="currentUser"> <NuxtLink to="/create"> <img src="/icons/add.svg" alt="添加内容"> </NuxtLink> </div> <div v-if="!currentUser"> <NuxtLink to="/login"> <img src="../static/icons/account.svg" alt="登录"> </NuxtLink> </div> </div> </header> <main> <slot></slot> </main> <footer class="footer"> <div class="footer-container"> <div class="footer-links"> <a href="#">联系我们</a> <a href="#">隐私政策</a> <a href="#">使用条款</a> </div> <div class="footer-copyright"> &copy; 2024 小鸟论坛 - 热爱自然,守护鸟类 </div> </div> </footer> </div> </template> <script lang="ts" setup> const name = ''小鸟论坛'' const currentUser = false </script> <style> @import ''../assets/styles/default.css'' </style>

内置好的NuxtLink,其实和router-link一样的用法。

ts
代码解读
复制代码
export const useApiFetch = ( api : string | (() => string) ) => { return useFetch(api,{ baseURL:''http://localhost:1234'', onRequest:async(context) =>{ } }) }

自定义的hooks函数,useApiFetch

js
代码解读
复制代码
body { margin: 0; padding: 0; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } main { font-size: 18px; line-height: 1.8; } h1 { font-size: 32px; } a { color: black; text-decoration: none; } button { border: none; outline: none; background: #ededed; padding: 6px 16px; font-size: 14px; border-radius: 5px; color: #666; cursor: pointer; border: 1px solid transparent; margin-bottom: 16px; margin-top: 8px; } button:hover { color: #000; } button ~ button { margin-left: 8px; } button.primary { background: #000; color: #ededed; } button.primary:hover { color: #fff; } button.bordered { background: none; border: 1px solid #eaeaea; } input, textarea { margin-bottom: 16px; margin-right: 16px; } pre { font-size: 16px; padding: 8px; } label { margin-right: 16px; } input[type=''submit''] { border: none; outline: none; background: #ededed; padding: 6px 16px; font-size: 14px; border-radius: 5px; color: #666; cursor: pointer; border: 1px solid transparent; } input[type=''submit'']:active { color: #000; } input[type=''text''], input[type=''password''], textarea { min-width: 360px; } input[type=''text''], input[type=''password''], textarea, select { border: 1px solid #eaeaea; padding: 8px; border-radius: 5px; font-size: 14px; } input[type=''checkbox''] { transform: scale(1.3); } /* App */ .app { display: flex; flex-direction: column; min-height: 100vh; } /* Header */ .app > header { display: flex; align-items: center; padding: 8px 16px; border-bottom: 1px solid #eaeaea; margin-bottom: 24px; } .app > header > div:nth-child(1) { display: flex; align-items: center; font-size: 22px; font-weight: bold; margin-right: 56px; } .app > header > div:nth-child(1) > span { font-size: 32px; padding-right: 8px; } .app > header > nav { display: flex; flex-grow: 1; color: #666; } .app > header > nav > div { margin: 0 16px; } .app > header > nav ~ div { display: flex; } .app > header > nav ~ div > div { margin-left: 8px; } /* Main */ .app > main { flex-grow: 1; padding: 8px 16px; display: flex; flex-direction: column; } .post-list > div { display: flex; margin-bottom: 24px; } .post-list > div > div { margin-right: 16px; } .post-list > div > div > img { width: 64px; height: 64px; object-fit: cover; border-radius: 50%; } .post-list > div > div > div:nth-child(1) { font-weight: bold; } .post-list > div > div > div:nth-child(3) { font-size: 14px; } .post-list > div > div > div:last-child { display: flex; margin-top: 8px; } .post-list > div > div > div:last-child img { width: 20px; } .post-list > div > div > div:last-child > div { margin-right: 8px; } .router-link-active { font-weight: bold; color: black; } .footer { background-color: #2c3e50; /* 深色背景 */ color: #ecf0f1; /* 浅色字体 */ padding: 20px 0; /* 上下内边距 */ text-align: center; /* 文字居中 */ } .footer-container { max-width: 1200px; /* 限制内容最大宽度 */ margin: 0 auto; /* 居中对齐 */ display: flex; flex-direction: column; align-items: center; /* 垂直居中 */ } .footer-links { margin-bottom: 10px; } .footer-links a { color: #ecf0f1; /* 链接颜色 */ margin: 0 15px; /* 链接之间的间距 */ text-decoration: none; /* 去除下划线 */ font-size: 14px; }

全局样式,layouts->defaults.css

总结

当你学完Vue的生态,再学Nuxt的时候,会发现Nuxt会方便很多,很多东西都是直接用的,相关的好处也是显而易见的。服务端渲染(SSR)与静态生成支持,提升 SEO,文件系统路由,开发更高效,灵活的布局系统,统一设计风格,自动化的 SEO 优化。