xyxsw
文章35
标签3
分类0
Slidev 用markdown写PPT!| 青训营笔记

Slidev 用markdown写PPT!| 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 14 天

前言

欢迎使用 Slidev!
为开发者打造的演示文稿工具

slidev是slidevjs开发的一款开源的使用markdown来编写ppt样式的前端应用的框架

只需要在单一 Markdown 文件中编写幻灯片就可以创建一个非常漂亮的前端ppt应用

这个项目自从0.20.0版本我就关注来着,当时就觉得很炫很好用

昨天看到了antfu更新说在最新版本0.39.0里支持了简单的动画切换效果

😋感觉动画切换是ppt最重要的东西吧,很好很圆满😎

来试试

img

特性

  • 支持 Markdown 语法:Slidev 使用一种扩展的 Markdown 格式,在一个纯文本文件中存储和组织你的幻灯片。这让你专注于制作内容。而且由于内容和样式是分开的,这也使得在不同的主题之间切换变得更加容易。

  • 可定制主题:Slidev 的主题可以通过 npm 包的形式来分享和安装。

  • 对开发者友好:Slidev 为开发者提供了一流的代码片段支持。它同时支持 PrismShiki 以获得像素级的完美语法高亮,并且能够随时修改代码。通过内置的 Monaco 编辑器,它还能让你在演示文稿中进行现场编码/演示,并支持自动补全、类型悬停、甚至是 TypeScript 类型检查。

  • 快速:Slidev 得益于 ViteVue 3Windi CSS,为你带来了最美妙的创作体验。你所做的每一个改变都会立即反映到你的幻灯片上。

  • 互动性 & 直观表达:你可以编写自定义的 Vue 组件并直接在你的 MarkDown 文件中使用它们。你也可以在演示文稿中与它们互动,以更深入和直观的方式表达你的想法。

  • 支持录制:Slidev 提供了内置的录音和摄像头视图。你可以将你的演示文稿与你的相机视图一起分享,或者为你的屏幕和相机分别录制并保存。所有这些都是内置的,不需要额外的工具。

  • 可移植性:用一个命令就可以将你的幻灯片导出为 PDF 或 PNG,甚至是可托管的单页应用程序(SPA),并在任何地方分享它们。

  • 可配置:由于 Slidev 基于 Web 技术,任何可以在 Web 应用中完成的事情,Slidev 也可以做到。例如,WebGLAPI请求iframes,甚至是实时共享。完全取决于你的想象力!

  • 演讲者模式: 可以使用另一个窗口,甚至是你的手机来控制幻灯片。

安装

npm init slidev

在package.json里配置

{
  "scripts": {
    "dev": "slidev", //  启动 dev server
    "build": "slidev build", // 构建生产环境的单页面应用
    "export": "slidev export" // 将幻灯片导出为 pdf 格式
  }
}

执行 npx slidev --help 命令获取更多选项的详细信息。

这样就安装完成了,你只需要在./slides.md里编写你想要的内容就行😎

推荐的目录结构

your-slidev/
  ├── components/       # 自定义组件
  ├── layouts/          # 自定义布局
  ├── public/           # 静态资源
  ├── setup/            # 自定义 setup / hooks
  ├── styles/           # 自定义样式
  ├── index.html        # 注入的 index.html
  ├── slides.md         # 幻灯片主入口
  └── vite.config.ts    # 扩展 vite 配置

Markdown 语法

分隔符

使用 --- 添加分隔符来分隔你的幻灯片。

注意 这个分隔符最好上下空两行

像这样

你可以安装一个VSCode插件 名叫slidev 来获得更好的体验,https://marketplace.visualstudio.com/items?itemName=antfu.slidev

这个#2是插件告诉你这下面是第几张幻灯片

# Slidev

Hello, World!

---

# Page 2

Directly use code blocks for highlighting

```ts
console.log('Hello, World!')
```

---

# Page 3

You can directly use Windi CSS and Vue components to style and enrich your slides.

<div class="p-3">
  <Tweet id="20" />
</div>

像这样就创建了三张幻灯片

扉页及布局

用两个分隔符来表示一个扉页块

---
layout: cover
---

两个分隔符里是yaml格式的对象

具体的语法如下

---
# 主题id 或 主题包名称
# 了解更多:https://sli.dev/themes/use.html
theme: 'default'
# 幻灯片的总标题,如果没有指定,那么将以第一张拥有标题的幻灯片的标题作为总标题
title: 'Slidev'
# titleTemplate for the webpage, `%s` will be replaced by the page's title
titleTemplate: '%s - Slidev'
# information for your slides, can be a markdown string
info: false

# 在单页(SPA)构建中启用 pdf 下载,也可以指定一个自定义 url
download: false
# 要导出文件的文件名称
exportFilename: 'slidev-exported.pdf'
# 语法高亮设置,可以使用 'prism' 或 'shiki' 方案
highlighter: 'prism'
# 在代码块中显示行号
lineNumbers: false
# 启用 monaco 编辑器,可以是 boolean,'dev' 或者 'build'
monaco: 'dev'
# 使用 vite-plugin-remote-assets 在本地下载远程资源,可以是 boolean,'dev' 或者 'build'
remoteAssets: false
# 控制幻灯片中的文本是否可以选择
selectable: true
# 启用幻灯片录制,可以是 boolean,'dev' 或者 'build'
record: 'dev'

# 幻灯片的配色方案,可以使用 'auto','light' 或者 'dark'
colorSchema: 'auto'
# vue-router 模式,可以使用 'history' 或 'hash' 模式
routerMode: 'history'
# 幻灯片的长宽比
aspectRatio: '16/9'
# canvas 的真实宽度,单位为 px
canvasWidth: 980
# 用于主题定制,会将属性 `x` 注入根样式 `--slidev-theme-x`
themeConfig:
  primary: '#5d8392'

# favicon 可以是本地文件路径,也可以是一个 URL
favicon: 'https://cdn.jsdelivr.net/gh/slidevjs/slidev/assets/favicon.png'
# 用于渲染图表的 PlantUML 服务器的 URL
plantUmlServer: 'https://www.plantuml.com/plantuml'
# 字体将从 Google 字体自动导入
# 了解更多:https://sli.dev/custom/fonts
fonts:
  sans: 'Roboto'
  serif: 'Roboto Slab'
  mono: 'Fira Code'

# 为所有幻灯片添加默认的 frontmatter
defaults:
  layout: 'default'
  # ...

# 绘制选项
# 了解更多:https://sli.dev/guide/drawing.html
drawings:
  enabled: true
  persist: false
  presenterOnly: false
  syncAll: true
---

代码块

可以通过```来创建一个代码块

```ts
console.log('Hello, World!')
```

可以通过在语言名后写大括号的形式来指定特定行高亮{}

注意:行号从 1 开始计算。

```ts {2-3|5|all}
function add(
  a: Ref<number> | number,
  b: Ref<number> | number
) {
  return computed(() => unref(a) + unref(b))
}
```

这段代码的意思是 点进来先是2到3行高亮,再点一下2到3不亮第5行亮,再点一下整个都亮

Monaco 编辑器

只需在语言 id 后添加 {monaco} 就可以启用monaco

什么是monaco呢,可以理解为网页版的vscode那样的,可以在线编辑的

```ts {monaco}
console.log('HelloWorld')
```

内联样式

你可以在 Markdown 中直接使用 <style> 标签来覆盖当前幻灯片的样式。

Slidev支持windicss的原子化写法和指令集 例如@apply

# Slidev

> Hello `world`

<style>
blockquote {
  code {
    @apply text-teal-500 dark:text-teal-400;
  }
}
</style>

静态资源

和编写 Markdown 的方式一样,你可以使用本地或远程的 URL 的图片。

远程资源会缓存

本地资源请放到public文件夹

备注

你也可以为每张幻灯片编写备注。它们将展示在 演讲者模式 中,供你在演示时参考。

在每一页幻灯片内容最后

添加注释即可

---
layout: cover
---

# 第 1 页

This is the cover page.

<!-- 这是一条备注 -->

---

# 第 2 页

<!-- 这不是一条备注,因为它在幻灯片内容前 -->

The second page

<!--
这是另一条备注
-->

icon图标

很喜欢的功能

Slidev 允许你在 Markdown 中直接访问几乎所有的开源的图标集。

你可以通过 Icônes 来浏览访问所有可用的图标。

更详细的请看https://cn.sli.dev/guide/syntax.html#icons

LaTeX

这个不是很熟啊

https://demo.sli.dev/starter/8

可以看下示例 和 文档

https://katex.org/

图标

这个东西也没怎么用过,只知道github什么的支持这种语法 基于https://github.com/mermaid-js/mermaid

被标记为 mermaid 的代码块将被转换为图形,例如:

```mermaid
sequenceDiagram
  Alice->John: Hello John, how are you?
  Note over Alice,John: A typical interaction
```
sequenceDiagram
  Alice->John: Hello John, how are you?
  Note over Alice,John: A typical interaction

我在typora也可以创建这个图表,感觉很好用

导航

导航栏默认在左下角,鼠标移动上去显示

可以用right / space来切换下一张幻灯片

g跳出一个栏,输入页码可以跳转

更多快捷键详见https://cn.sli.dev/guide/navigation.html#navigation-bar

动画

重头戏

这个版本就更新了这个

点击动画

v-click 可以用<v-click></v-click>标签或在标签内写<div v-click class="text-xl p-2">这样的指令

以下指令写法同理

v-afterv-click绑定,这俩会一起显示

v-click-hide点击后不可见

v-clicks 只能使用标签写法 可以方便的展示一个列表

<v-clicks>

- Item 1
- Item 2
- Item 3
- Item 4

</v-clicks>

你还可以手动指定过渡动画 覆写两个类的动画

// the default

.slidev-vclick-target {
  transition: opacity 100ms ease;
}

.slidev-vclick-hidden {
  opacity: 0;
  pointer-events: none;
}

运动

https://vueuse.org/functions.html#category=%40Motion

内置了vueuse的motion功能

你可以对任何元素应用 v-motion 指令,以对它们施加运动效果。例如:

<div
  v-motion
  :initial="{ x: -80 }"
  :enter="{ x: 0 }">
  Slidev
</div>

文本 Slidev 将从其初始化位置 -80px 移至其原始位置。

页面过渡

🤩新功能!

可以在扉页中填写

---
transition: slide-left
---

这将给你一个很好的滑动效果。将其设置在首页,将适用于所有的幻灯片。您还可以为每张幻灯片设置不同的过渡。

自带的过渡效果

fade - Crossfade in/out
fade-out - Fade out and then fade in
slide-left - Slides to the left, slide to right when going back
slide-right - Slides to the right, slide to left when going back
slide-top - Slides to the top, slide to bottom when going back
slide-bottom - Slides to the bottom, slide to top when going back

还可以自定义过渡效果

---
transition: my-transition
---
.my-transition-enter-active,
.my-transition-leave-active {
  transition: opacity 0.5s ease;
}

.my-transition-enter-from,
.my-transition-leave-to {
  opacity: 0;
}

导出

导出为pdf 依赖于微软开源的playwright框架

playwright-chromium

需要先安装

npm i -D playwright-chromium

使用

slidev export

可导出pdf格式

slidev export --format png

可导出png格式

静态部署

slidev build

编译当前幻灯片生成在dist/目录下,可以部署到静态托管平台

提供可下载的 PDF

在扉页中设置

---
download: true
---

会在单页应用中展示下载按钮

也可以自定义url

---
download: 'https://myside.com/my-talk.pdf'
---

部署到netlify

创建netlify.toml文件 配置

[build.environment]
  NODE_VERSION = "14"

[build]
  publish = "dist"
  command = "npm run build"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

部署到vercel

创建vercel.json文件 配置

{
  "rewrites": [
    { "source": "/(.*)", "destination": "/index.html" }
  ]
}

演讲录制

摄像头

导航面板的小人按钮可以开启摄像头

你可以拖动它,并使用右下角的把手来调整大小。尺寸和位置将持久化存储在 localStorage 中,因此,可以保证多次刷新后的展示一致,无需担心位置和大小丢失的问题。

录制

导航面板的摄像机按钮

可以录制麦克风和摄像头

演讲者模式

点击小人带麦克风的按钮

可以进入演讲者模式

需要双端在同一网络下

其他页面的实例会自动和演讲者的页面同步

绘图

可以在页面上画画

可以在演讲者模式中启用,会实时自动同步

支持触控笔压感

编辑器整合

你可以在运行中在网页上更改你的源码,他会自动同步到slide.md里

主题

在这里浏览主题并应用到你的实例中

https://cn.sli.dev/themes/gallery.html

布局

https://cn.sli.dev/builtin/layouts.html

总结

这篇写了不少,详细的写了我比较喜欢的ppt框架

我有一个部署的版本,地址在 https://botppt.netlify.app/

是很久之前写的辣,版本还是很老的,有时间会更新一下

😎

引用

https://botppt.netlify.app/1

https://cn.sli.dev/showcases.html

https://cn.sli.dev/

https://github.com/slidevjs/slidev

Nuxt3与服务端渲染SSR(高级篇) | 青训营笔记

Nuxt3与服务端渲染SSR(高级篇) | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 13 天

前言

前两篇写了一些nuxt3和SSR的基础

这一篇讲讲nuxt3比较进阶的部分

自动引入

nuxt3自带自动引入

包括一些nuxt3的api都是不用导入的直接就可以用

例如

  • 数据获取的函数useFetch useAsyncData

  • 状态管理函数useState

  • 运行变量获取函数 useRuntimeConfig

  • 运行时上下文获取函数 useNuxtApp

还有一些Vue的api

例如

  • 响应式的函数ref

  • 计算属性 computed

文件夹下的内容自动引入

例如

  • components/文件夹下的组件
  • composables/文件夹下的响应式函数
  • utils/文件夹下的工具函数

可以使用#imports来使导入明确

<script setup>
  import { ref, computed } from '#imports'

  const count = ref(1)
  const double = computed(() => count.value * 2)
</script>

关闭自动引入

nuxtconfig里加入

export default defineNuxtConfig({
  imports: {
    autoImport: false
  }
})

(😡谁会这么做

自动引入的组件

假设目录如下

| components/
--| base/
----| foo/
------| Button.vue

此时可以使用<BaseFooButton />标签引入这个button组件

另外,可以使用一个Lazy前缀来懒加载组件

例如

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show: false
    }
  }
}
</script>

这样可以减少打包大小,增加性能

路由

写在pages/里的组件会自动写入路由

pages/index.vue会匹配路由/

只需要在app.vue里添加

<NuxtPage />标签

可以通过插槽写法例如写[id].vue来匹配路由内的参数

例如如下文件树

-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue

[id].vue里写下

<template>
  <p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>

读取group参数和id参数

访问路由/users-admins/123会渲染成

<p>admins - 123</p>

渲染模式

传统的Vue项目渲染方式为 客户端渲染

浏览器会先下载到空的html文件→下载js→渲染出来

这种方式的优点是:

  • 开发速度快:当完全在客户端上工作时,我们不必担心代码的服务器兼容性,例如,使用像windows这样的浏览器api。

  • 更便宜的服务器成本:运行一个服务器会增加一个基础设施的成本,因为您需要在一个支持JavaScript的平台上运行。我们可以在任何具有HTML、CSS和JavaScript文件的静态服务器上托管仅限客户端的应用程序。

  • 下载好后离线:因为代码完全在浏览器中运行,所以它可以很好地在互联网不可用时很好地保持工作。

缺点为:

  • 性能不好:用户必须等待浏览器下载、解析和运行JavaScript文件。根据下载部分的网络和用户的解析和执行设备,这可能需要一些时间,并影响用户的体验。
  • 不友好的搜索引擎优化:索引和更新通过客户端呈现交付的内容比使用服务器呈现的HTML文档需要更多的时间。

nuxt3的服务端渲染

有内容的html文件会下载到浏览器→用户在此时可以点击和浏览页面上的内容(部分功能)→需要的组件渲染完成、此时的页面有全部功能

这种方式的优点是:

  • 性能好:用户可以立即访问页面的内容,因为浏览器显示静态内容的速度比javascript生成的静态内容要快得多。
  • 搜索引擎优化:通用呈现将页面的整个HTML内容作为一个经典的服务器应用程序传递到浏览器。Web爬虫可以直接索引页面的内容。

缺点是:

  • 开发约束:服务器和浏览器环境不提供相同的api,而且编写可以在两边无缝运行的代码可能会很棘手。幸运的是,Nuxt提供了特定的变量来同步这些。
  • 成本高:服务器需要运行,才能动态地呈现页面。这就像任何传统服务器一样,增加了每月的成本。但是,由于浏览器接管了客户端导航的通用渲染,服务器调用大大减少了。

类型检查

使用 TypeScript 后可以使用nuxi带的语法检查

yarn nuxi typecheck

nuxt也会自动生成类型

.nuxt/nuxt.d.ts目录下

文件目录

一个完整的nuxt3项目文件目录应该包含以下文件夹和文件

.nuxt                      nuxt生成的文件目录里面有 运行的类型等
.output                    build产物文件夹
assets                     css文件、字体文件、图片文件
components                 组件目录
composables                响应式函数的文件夹
content                    基于文件的cms的文件夹
layouts                    布局组件的文件夹
middleware                 中间件文件夹
node_modules               依赖文件夹
pages                      路由的页面
plugins                    nuxt插件
public                     公共文件文件夹,可以放ico图标,图片
server                     服务端api文件夹
utils                      工具函数文件夹
.gitignore                 git忽略文件
.nuxtignore                不加入nuxt编译的忽略文件
app.config.ts              config文件 可以使用useAppConfig读取
app.vue                    vue入口 
nuxt.config.ts             nuxt配置文件
package.json               依赖描述文件
tsconfig.json              ts检查配置文件

https://nuxt.com/docs/guide/directory-structure/nuxt

总结

本篇文章介绍了nuxt3的高级用法

我也是第一次使用nuxt3来开发项目

踩到的坑还是很多的,预计可以写一篇踩坑记录

😎🥰

参考

https://nuxt.com/docs/guide/concepts/auto-imports

https://nuxt.com/docs/guide/directory-structure/composables

https://nuxt.com/docs/guide/directory-structure/components

https://github.com/unjs/nitro

Nuxt3与服务端渲染SSR(下) | 青训营笔记

Nuxt3与服务端渲染SSR(下) | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 12 天

前言

上一篇文章介绍了大概一半的nuxt3特性,这一篇会介绍另外一半

我也是第一次使用nuxt3来开发项目

踩到的坑还是很多的,预计可以写一篇踩坑记录

过渡

Nuxt利用Vue的 <Transition>组件来应用页面和布局之间的转换。

只需要在nuxt config里设置

export default defineNuxtConfig({
  app: {
    pageTransition: { name: 'page', mode: 'out-in' }
  },
})

然后在app.vue里设置你想要的过渡动画

<template>
  <NuxtPage />
</template>

<style>
.page-enter-active,
.page-leave-active {
  transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
  opacity: 0;
  filter: blur(1rem);
}
</style>

类名必须是这几个

就可以实现一个切换页面有虚化效果的动画

更高级的用法详见官方文档

https://nuxt.com/docs/getting-started/transitions

使用Api获取数据

nuxt3自带一个vueuse里的 useFetch api

使用方法为

<script setup>
const { data: count } = await useFetch('/api/count')
</script>

<template>
  Page visits: {{ count }}
</template>

可以使用useAsyncData来异步获取数据

<script setup>
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
</script>

<template>
  Page visits: {{ data }}
</template>

更高级的用法详见官方文档

https://nuxt.com/docs/getting-started/data-fetching

状态管理

nuxt3提供了vueuse的一个api叫useState来实现全局状态管理

无需创建state文件

直接使用

<script setup>
const counter = useState('counter', () => Math.round(Math.random() * 1000))
</script>

<template>
  <div>
    Counter: {{ counter }}
    <button @click="counter++">
      +
    </button>
    <button @click="counter--">
      -
    </button>
  </div>
</template>

这个例子创建了一个state叫counter 里面默认记录了一个随机数

点击按钮会让随机数进行加减

useState默认为响应式 很方便

nuxt3的auto import特性可以使state简写为

const counter = useCounter() // Same as useState('counter')

错误处理

nuxt提供了很多高级的错误处理方式

一个最简单的api是useError

const error = useError()

这句代码可以快速的抛出一条错误

也可以使用createError方法

<script setup>
const route = useRoute()
const { data } = await useFetch(`/api/movies/${route.params.slug}`)
if (!data.value) {
  throw createError({ statusCode: 404, statusMessage: 'Page Not Found' })
}
</script>

部署

正常的使用nuxt build命令会生成一个在node环境中运行的运行时

.mjs文件

node .output/server/index.mjs

即可运行

$ node .output/server/index.mjs
Listening on http://localhost:3000

可以设置环境变量例如

PORT=3001 node .output/server/index.mjs

在3001端口运行

nuxt.config 里添加

export default {
  nitro: {
    preset: 'node-server'
  }
}

或者NITRO_PRESET=node-server nuxt build

preset字段可以更改生成的代码的依赖平台

支持的平台有

测试

可以添加vitest库来进行测试

yarn add --dev @nuxt/test-utils vitest

方法如下

新建test目录 创建文件 demo.test.ts

import { describe, test } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils'

describe('My test', async () => {
  await setup({
    // test context options
  })

  test('my test', () => {
    // ...
  })
})

运行vitest即可获得通过或不通过提示

结尾

到这里nuxt3基础就几乎结束了

还有一些更高级的用法没有讲,例如生命周期啊,nuxt-kit啊,自动导入啊

有机会可以写一篇高级教程

😋😎

参考

https://nuxt.com/docs/getting-started/state-management

https://nuxt.com/docs/api/composables/use-state

https://juejin.cn/post/7193305766692913189

https://vueuse.org/

Nuxt3与服务端渲染SSR(上) | 青训营笔记

Nuxt3与服务端渲染SSR(上) | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 11 天

前言

Nuxt同时提供了前端和后端功能,因此您可以专注于重要的事情:创建web应用程序。

Nuxt3 是基于 ViteVue3NitroNuxt 框架的重构,具有一流的 Typescript 支持,且这次更新对内核进行了精简,使之速度更快,体验更好。

了解 SSR

Nuxt 是一个基于 Vue.js 的服务端渲染应用框架

什么是 SSR

服务器端渲染(Server-Side Rendering)是指由服务端完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程。

简单理解就是html是由服务端写出,可以动态改变页面内容,即所谓的动态页面。早年的 php 、 asp 、 jsp 这些 Server page 都是 SSR 的。

为什么使用 SSR

  • 网页内容在服务器端渲染完成,一次性传输到浏览器,所以 首屏加载速度非常快
  • 有利于SEO,因为服务器返回的是一个完整的 html,在浏览器可以看到完整的 dom,对于爬虫、百度搜索等引擎就比较友好;

Nuxt 3

Nuxt 是一个基于 Vue.js 的服务端渲染应用框架,

刚去查了一下 nuxt3在1月23日变成了默认版本

在1月25日发布了3.1.0版本

  • 实验组件群岛 准备就绪

  • 新的api

  • Nitro v2 ,vite 4 ,rollup3

  • 性能改进

什么是Nuxt?

要理解Nuxt是什么,我们需要了解创建一个现代应用程序需要什么:

  • JavaScript框架: 一个带来JavaScript组件的JavaScript框架,支持Vue.js。
  • 打包工具:支持开发中的热模块替换和生产代码,支持webpack 5和Vite。
  • 最新JavaScript语法:在支持遗留浏览器的同时编写最新的JavaScript语法的转换器,支持esbuild。
  • 服务器端:Nuxt是一个在开发中服务于应用程序的服务器,同时也支持服务器端呈现或API路由,它使用h3进行部署的多功能性,如serverless, workers, Node.js和无与伦比的性能。
  • 路由:一个处理客户端导航的路由库,支持 vue-router.。

初始化项目

https://nuxt.com/docs/getting-started/installation#prerequisites

pnpm dlx nuxi init <project-name>
code <project-name>
# Make sure you have `shamefully-hoist=true` in `.npmrc` before running pnpm install
pnpm install

注意: 如果你用 pnpm 安装依赖,请创建一个 .npmrc 文件,且设置:

shamefully-hoist=true

安装完成😋

启动

pnpm dev -o

看一眼文件树

抛开编译结果.nuxt文件夹不谈 就只有一个app.vuenuxt.config.ts

很简陋 怎么会这样呢?我们一般写的vue框架也不是这样的啊

创建目录

创建 components/ pages/

具体操作见官方文档

https://nuxt.com/docs/getting-started/views

也可以新建src目录 把 components/ pages/目录放到src

在nuxt里组件是自动导入的,不用配置

路由跳转

路由是自动创建的

<NuxtLink to="/about">About</NuxtLink>

以上的代码点击会自动跳到page/about.vue

也可以使用路由

<script setup>
const route = useRoute()

// When accessing /posts/1, route.params.id will be 1
console.log(route.params.id)
</script>

/posts/1是创建的/posts/[id].vue这样id会被当做props传入

SEO优化

nuxt服务端渲染最重要的就是搜索引擎优化

可以被拉取到带内容的html

而不是一般vue项目的空html

export default defineNuxtConfig({
  app: {
    head: {
      charset: 'utf-16',
      viewport: 'width=500, initial-scale=1',
      title: 'My App',
      meta: [
        // <meta name="description" content="My amazing site">
        { name: 'description', content: 'My amazing site.' }
      ],
    }
  }
})

nuxt.config.ts配置如上内容

或者在app.vue里添加

<script setup lang="ts">
useHead({
  title: 'My App',
  meta: [
    { name: 'description', content: 'My amazing site.' }
  ],
  bodyAttrs: {
    class: 'test'
  },
  script: [ { children: 'console.log(\'Hello world\')' } ]
})
useServerSeoMeta({
  title: 'My Amazing Site',
  ogTitle: 'My Amazing Site',
  description: 'This is my amazing site, let me tell you all about it.',
  ogDescription: 'This is my amazing site, let me tell you all about it.',
  ogImage: 'https://example.com/image.png',
  twitterCard: 'summary_large_image',
})
</script>

这几个函数可以添加meta头

还有一些设置可以看官方文档 https://nuxt.com/docs/getting-started/seo-meta

也可以使用 https://nuxt.com/modules/seo-kit

这个模块来配置SEO

很方便😎

结尾

本篇文章介绍了大概一半的nuxt3特性,下一篇会介绍另外一半

我也是第一次使用nuxt3来开发项目

踩到的坑还是很多的,预计可以写一篇踩坑记录

😎

参考

https://nuxt.com/modules/seo-kit

https://nuxt.com/docs/getting-started/seo-meta

https://nuxt.com/docs/getting-started/views

https://nuxt.com/docs

https://juejin.cn/post/7170746000112353293

https://juejin.cn/post/7037336504418435103

UnoCSS实战 | 青训营笔记

UnoCSS实战 | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 10 天

前言

原子化css体验

重新构想原子化 CSS (antfu.me)

本篇介绍一下如何配置UnoCSS、UnoCSS怎么使用

安装

先创建一个vite模板 https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project

这里使用pnpm

pnpm create vite

选择vue、typescript

cd vite-project
pnpm install
pnpm run dev

运行一下

初始化成功了,接下来安装unocss https://github.com/unocss/unocss/tree/main/packages/vite

使用文档给的vite安装方式

pnpm i -D unocss

在vite config里写上

// vite.config.ts
import UnoCSS from 'unocss/vite'

export default {
  plugins: [
    UnoCSS({ /* options */ }),
  ],
}

配置完全的vite config是这样的

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import UnoCSS from 'unocss/vite'
import { presetUno } from 'unocss'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    UnoCSS({
      presets: [
        presetUno(),
      ],
    }),
  ],
})

然后在main.ts里加入

// main.ts
import 'uno.css'

这里没有eslint还是很不舒服的,推荐一个antfu的eslint配置,来安装一下eslint

https://github.com/antfu/eslint-config

pnpm add -D eslint @antfu/eslint-config

创建.eslintrc写入

{
  "extends": "@antfu"
}

在package json里加入

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix"
  }
}

如果你使用的是VSCode 下载 VS Code ESLint extension 然后编辑 .vscode/settings.json

{
  "prettier.enable": false,
  "editor.formatOnSave": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}

之后重启vscode

安装完成😎

使用

我们来写一个小demo

就把原本的默认界面改成unocss好了

先看app.vue

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo">
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo">
    </a>
  </div>
  <HelloWorld msg="Vite + Vue" />
</template>

<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

改成原子化css的形式

这里最好先配置一下vite config 多加一点预设

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import UnoCSS from 'unocss/vite'
import { presetAttributify, presetIcons, presetTypography, presetUno, presetWebFonts, transformerDirectives, transformerVariantGroup } from 'unocss'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    UnoCSS(
      {
        presets: [
          presetUno(),
          presetAttributify(),
          presetIcons({
            scale: 1.2,
          }),
          presetTypography(),
          presetWebFonts({
            fonts: {
              sans: 'DM Sans',
              serif: 'DM Serif Display',
              mono: 'DM Mono',
            },
          }),
        ],
        transformers: [
          transformerDirectives(),
          transformerVariantGroup(),
        ],
      },
    ),
  ],
})

我们先用@apply将css改一下 <style scoped>

.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}

这里不会的语法可以看tailwindcss文档

改成

.logo {
  @apply h-6em p-1.5em
}
.logo {
  @apply hover:drop-shadow-[0_0_2em_#646cffaa]
}
.logo.vue {
  @apply hover:drop-shadow-[0_0_2em_#42b883aa]
}

怎么样?是不是感觉很清晰明了

这是在tailwindcss里很受欢迎的@apply命令 通过预设导入了unocss

但是这样体现不出原子化css的方便,虽然非常好看

我们把css写入html

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="h-6em p-1.5em hover:drop-shadow-[0_0_2em_#646cffaa]" alt="Vite logo">
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="hover:drop-shadow-[0_0_2em_#42b883aa]" alt="Vue logo">
    </a>
  </div>
  <HelloWorld msg="Vite + Vue" />
</template>

<style scoped>
</style>

这样写就很方便了

再看HelloWorld.vue

<script setup lang="ts">
import { ref } from 'vue'

defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>

  <p>
    Check out
    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
      >create-vue</a
    >, the official Vue + Vite starter
  </p>
  <p>
    Install
    <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
    in your IDE for a better DX
  </p>
  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>

<style scoped>
.read-the-docs {
  color: #888;
}
</style>

这个改的比较少

<script setup lang="ts">
import { ref } from 'vue'

defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">
      count is {{ count }}
    </button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>

  <p>
    Check out
    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank">create-vue</a>, the official Vue + Vite starter
  </p>
  <p>
    Install
    <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
    in your IDE for a better DX
  </p>
  <p class="text-[#888]">
    Click on the Vite and Vue logos to learn more
  </p>
</template>

<style>
</style>

改成这样就行

再看style.css文件 内容比较多

:root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

h1 {
  font-size: 3.2em;
  line-height: 1.1;
}

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #1a1a1a;
  cursor: pointer;
  transition: border-color 0.25s;
}
button:hover {
  border-color: #646cff;
}
button:focus,
button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;
}

.card {
  padding: 2em;
}

#app {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #747bff;
  }
  button {
    background-color: #f9f9f9;
  }
}

一点点儿改

最后改成了这样 还挺还原的是不是

有一些媒体查询不会改

根元素没改

代码如下

// style.css
:root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}


@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #747bff;
  }
  button {
    background-color: #f9f9f9;
  }
}
// HelloWorld.vue
<script setup lang="ts">
import { ref } from 'vue'

defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1 class="text-3.2em leading-tight">
    {{ msg }}
  </h1>

  <div class="p-2em flex flex-col place-items-center">
    <button
      type="button" class="
      rounded-8px border-1px border-solid border-transparent
      py-0.6em px-1.2em text-1em font-medium
      bg-[#1a1a1a] cursor-pointer transition duration-250
      hover:border-color-[#646cff]
      focus:outline-4 focus:outline-[-webkit-focus-ring-color]
      focus-visible:outline-4 focus-visible:outline-[-webkit-focus-ring-color]
    " @click="count++"
    >
      count is {{ count }}
    </button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>

  <p>
    Check out
    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank" class="font-medium color-[#646cff] hover:color-[#535bf2]">create-vue</a>, the official Vue + Vite starter
  </p>
  <p>
    Install
    <a href="https://github.com/johnsoncodehk/volar" target="_blank" class="font-medium color-[#646cff] hover:color-[#535bf2]">Volar</a>
    in your IDE for a better DX
  </p>
  <p class="text-[#888]">
    Click on the Vite and Vue logos to learn more
  </p>
</template>

<style>
</style>
// App.vue
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <div class="m-0 flex flex-col place-items-center min-w-320px min-h-100vh">
    <div class="max-w-1280px my-0 mx-auto p-2rem text-center">
      <a href="https://vitejs.dev" target="_blank" class="font-medium color-[#646cff] hover:color-[#535bf2]">
        <img src="/vite.svg" class="h-6em p-1.5em hover:drop-shadow-[0_0_2em_#646cffaa]" alt="Vite logo">
      </a>
      <a href="https://vuejs.org/" target="_blank" class="font-medium color-[#646cff] hover:color-[#535bf2]">
        <img src="./assets/vue.svg" class="h-6em p-1.5em hover:drop-shadow-[0_0_2em_#42b883aa]" alt="Vue logo">
      </a>
    </div>
    <HelloWorld msg="Vite + Vue" />
  </div>
</template>

<style>
</style>

可以看出来虽然写的简单,但是还是比较乱

这样写的好处是,不用想类名,

如果加了@apply会更美观,但是还是要写类名,和普通css的区别就是简单了一些

同时还有一些自带的样式预设

结尾

本篇我们介绍了UnoCSS的特点,UnoCSS的使用,还是改了挺长时间的,主要是不会写语法,要查tailwind文档 https://tailwindcss.com/docs

还有一些好几个值的css 不知道什么意思

outline: green solid 3px;
/* mdn:颜色 | 样式 | 宽度 */
outline: 4px auto -webkit-focus-ring-color;
/* style.css */

关于这种就不知道怎么写

最后只能掰成

focus:outline-4 focus:outline-[-webkit-focus-ring-color]

题外话:

Anthony Fu大佬真的是我目前的偶像,狂热的开源爱好者,人长得又帅,做的东西又好

🤣这是我在掘金搜unocss搜到的一篇文章中的第一句话 文章在这 https://juejin.cn/post/7028841960752283656

antfu大佬确实也是我的偶像

22年秋季 antfu大佬在b站办过几场直播写代码,我基本上是每期必看,这个人非常有意思,有很多新鲜的点子,狠活也多 比如说

https://github.com/antfu/1990-script 这个仓库教你怎么把GitHub历史穿越回1990年🤭

他的每个star比较多的仓库我都体验过,感觉设计灵感真的让人眼前一亮,启发基于社区,创造更快的轮子,更好玩的轮子

https://github.com/antfu/retypewriter 这个是直播写的雏形 让写的代码在vsc里回放,便于理清思路

https://github.com/antfu/vue-minesweeper 这个是首次直播写的扫雷,很清晰的vue代码,看他写真的开窍

https://github.com/antfu/vue-starport 这个也是直播写的雏形,为了使组件有更好的过渡

就说这么多,人也长得帅

🥰😘

参考

https://developer.mozilla.org/zh-CN/docs/Web/CSS

https://tailwindcss.com/docs

https://www.tailwindcss.cn/docs/functions-and-directives#apply

https://github.com/unocss/unocss

https://cn.vitejs.dev/

原子化CSS| 青训营笔记

原子化CSS| 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 9 天

前言

原子化 CSS是一个很新颖的定义 出自https://CSS-tricks.com/lets-define-exactly-atomic-CSS/

意思是原子化 CSS是一种 CSS的架构方式,它倾向于小巧且用途单一的 class,并且会以视觉效果进行命名。

什么是原子化 CSS?

与一般的我们写正常CSS不同,原子化CSS会把元素本身的属性提取出来,加以视觉效果命名,例如:

如下的html

<button class="btn">Basic</button>

正常的CSS

.btn {
    padding: 1rem, 2rem;
    font-family: 'semi';
    font-weight: bold; 
}

使用原子化CSS后,将不用写CSS文件,改为写class属性

<button class="py-2 px-4 font-semibold rounded-lg shadow-md text-white bg-green-500 hover:bg-green-700">
  Click me
</button>

市面上有不少实用至上的 CSS框架,如 Tailwind CSSWindi CSS 以及 Tachyons 等。

同时有些 UI 库也会附带一些 CSS工具类作为框架的补充,如 BootstrapChakra UI

写之前,我们谈谈优势

  • 类名自由:你绞尽脑汁想这个元素的类名怎么写。在你面前的是一点击就会变颜色的按钮,你想给他起个class或者id,你想了半天,最后取了class名为click-it-and-turn-it-from-blue-to-green😰。
  • html嵌入:正常情况我们要引入CSS文件去写这个样式,切来切去不太方便。
  • 预设的原子化CSS:可以引入插件、其他基于原子化CSS写的组件库。
  • IDE支持:插件支持类名提示,很好写。
  • 写的快:比如说hover属性,原子化CSS可以直接写在class里,很快很方便,比如说tailwind自带的响应式布局,不用自己写媒体查询就可以适配移动端。

常用的原子化CSS框架

本文的重点:UnoCSS

讲这么多,我们终于回到主题UnoCSS上

antfu重新构想原子化CSS 这篇文章讲了unocss是怎么诞生的

Windi CSS已经足够优秀,但antfu大佬还是不够满意,对于框架预设外的自定义工具的额外配置上,还是比较繁琐,而且配置的方式也不够简便

由于 Windi 需要与 Tailwind 兼容,它还必须使用与 Tailwind 完全相同的配置项。尽管数字推断的问题得到了解决,但如果你想添加一些自定义的工具,这将是一场噩梦。

所以经过重新构想原子化CSS,UnoCSS出现了

UnoCSS - 具有高性能且极具灵活性的即时原子化 CSS引擎。

它不是像TailWind CSS和Windi CSS属于框架,而是一个引擎,它没有提供预设的原子化CSS工具类

UnoCSS通过编写规则来定制工具类

静态规则

rules: [
    ['m-1', { margin: '0.25rem' }]
]

使用正则表达式来做动态规则

rules: [
  [/^m-(\d)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
  [/^p-(\d)$/, (match) => ({ padding: `${match[1] / 4}rem` })],
]

当然这些比较常见的规则unocss已经提供了预设在@unocss/preset-uno

<div class="m-100">
  <button class="m-3">
    <icon class="p-5" />
    My Button
  </button>
</div>

生成出的css为

.m-100 { margin: 25rem; }
.m-3 { margin: 0.75rem; }
.p-5 { padding: 1.25rem; }

unocss提供了一个叫shortcuts的功能,可以批量生成css

shortcuts: [
  // you could still have object style
  {
    btn: 'py-2 px-4 font-semibold rounded-lg shadow-md',
  },
  // dynamic shortcuts
  [/^btn-(.*)$/, ([, c]) => `bg-${c}-400 text-${c}-100 py-2 px-4 rounded-lg`],
]

只需要配置这个字段

在使用中可以使用 btn-greenbtn-red

生成的css如下

.btn-green {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  padding-left: 1rem;
  padding-right: 1rem;
  --un-bg-opacity: 1;
  background-color: rgba(74, 222, 128, var(--un-bg-opacity));
  border-radius: 0.5rem;
  --un-text-opacity: 1;
  color: rgba(220, 252, 231, var(--un-text-opacity));
}
.btn-red {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  padding-left: 1rem;
  padding-right: 1rem;
  --un-bg-opacity: 1;
  background-color: rgba(248, 113, 113, var(--un-bg-opacity));
  border-radius: 0.5rem;
  --un-text-opacity: 1;
  color: rgba(254, 226, 226, var(--un-text-opacity));
}

unocss提供了很多预设

Official Presets
Community Presets

UnoCSS的优点

对比其他原子化框架来说

  • 完全可定制:作为一款引擎没有核心实用程序,所有的功能都是通过预设提供的。

  • 扩展性强:通过各种预设组合达到windi或者tailwind的效果。

  • 快:没有解析,没有AST,没有扫描,它是即时的。

  • 小:运行库打包好只有6kb,无痛

  • Shortcuts:可以写Shortcuts来预设一些常用样式

  • Attributify mode :将class写成组,避免无法阅读的问题

  • Pure CSSIcons :纯 CSSicon 可以在 https://icones.js.org/ 找到并导入成千上万的icon 安装只需要找到你喜欢的 iconfy 库 @iconify-json/[the-collection-you-want] 这样安装就行

  • Variant Groups :和 Attributify mode 看起来几乎是一样的功能,写成组

  • CSSDirectives :可以在style标签里写 @apply 把一系列样式打包成一个样式,在windi里是个很受欢迎的功能

  • Compilation mode :打包优化,生成随机class名

  • Inspector :自带的debug工具

  • CSS-in-JS Runtime build :可以CDN导入

  • VS Code extension:自带VSCode提示扩展,很方便

结尾

本篇我们介绍了UnoCSS的特点,原子化CSS的好处,下一篇讲讲unocss实战,安装使用之类的

题外话:

Anthony Fu大佬真的是我目前的偶像,狂热的开源爱好者,人长得又帅,做的东西又好

🤣这是我在掘金搜unocss搜到的一篇文章中的第一句话 文章在这 https://juejin.cn/post/7028841960752283656

antfu大佬确实也是我的偶像

22年秋季 antfu大佬在b站办过几场直播写代码,我基本上是每期必看,这个人非常有意思,有很多新鲜的点子,狠活也多 比如说

https://github.com/antfu/1990-script 这个仓库教你怎么把GitHub历史穿越回1990年🤭

他的每个star比较多的仓库我都体验过,感觉设计灵感真的让人眼前一亮,启发基于社区,创造更快的轮子,更好玩的轮子

https://github.com/antfu/retypewriter 这个是直播写的雏形 让写的代码在vsc里回放,便于理清思路

https://github.com/antfu/vue-minesweeper 这个是首次直播写的扫雷,很清晰的vue代码,看他写真的开窍

https://github.com/antfu/vue-starport 这个也是直播写的雏形,为了使组件有更好的过渡

就说这么多,人也长得帅

🥰😘

参考

https://juejin.cn/post/7028841960752283656

https://antfu.me/posts/reimagine-atomic-css-zh

https://juejin.cn/post/7027414082378530852

https://juejin.cn/post/7161211941652791304

https://www.tailwindcss.cn/docs/

MonoRepo&Nuxt框架初始化实战 | 青训营笔记

MonoRepo&Nuxt框架初始化实战 | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 8 天

前言

上一篇笔记介绍了MonoRepo的优点和基于go语言的MonoRepo框架TurboRepo,这一篇我们详细介绍一下如何配置出一个团队能用的TurboRepo库,包括一些actions和自动部署预览等。

技术栈

  • ⚡️ Nuxt3 - SSR框架
  • Vue3.2 - 最新Vue更多语法糖
  • 💨 Turbo - MonoRepo框架
  • 💎 Strapi - Headless CMS 框架
  • 😎 @Nuxt/Image - images 服务端渲染
  • 😁 Umami - Better Analytics 访问源分析
  • 🃏 Commit Lint - commit规范检查
  • 💖 Husky & Lint Staged — precommit运行库
  • 📏 Eslint - 规范化TypeScript、JavaScript代码
  • 🐶 UnoCss - 原子化 CSS 引擎、兼容 tailwindcss、windicss
  • 🤖 preset-icons - unocss 自带icons
  • ⏰ The <script setup> syntax setup语法糖 来自Vue3.2
  • 🍍 Pinia - 全局状态管理库 更好用
  • 🎨 APIs auto importing - 库自动引入
  • 🦾 TypeScript - 更规范的语法
  • 👷 Github Actions - actions自动运行、ci/cd云端检查语法
  • 👀 Automatic Branch and Issue Autolink - Issue AutoLink actions 创建issue会创建一个分支
  • 🗺 Nuxt SEO Kit - SEO优化 SSR必备
  • 📦 Vercel - 自动部署、预览的平台
  • 🔥 Netlify + Cloudflare - 部署平台

前端模板

前端模板选择了https://github.com/antfu/vitesse-nuxt3

是antfu的vitesse系列模板的nuxt3款

这套模板用了好多个 很好用的

配置很全、自带的unocss也是几乎兼容windicss和tailwind 很适应

想用icons可以去antfu的另一个项目里找

https://icones.js.org/ 这里二次封装了几万个icons用个爽

配置turbo

turbo官网有讲怎么配置到一个已有的MonoRepo里https://turbo.build/repo/docs/getting-started/existing-monorepo

这里只需要在根目录的package.json里建立workspaces字段 和packageManager字段

//package.json
{
"packageManager": "yarn@1.22.19",
  "workspaces": [
    "frontend",
    "backend"
  ],
}

安装TurboRepo

yarn global add turbo

创建任务管道

package.jsonturbo中,将想要”turbo”的命令添加到管道中 管道定义了 npm 包中 scripts 的依赖关系,并且为这些命令开启了缓存。这些命令的依赖关系和缓存设置会应用到 MonoRepo 中的各个包中

//turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      // A package's `build` script depends on that package's
      // dependencies and devDependencies
      // `build` tasks  being completed first
      // (the `^` symbol signifies `upstream`).
      "dependsOn": ["^build"],
      // note: output globs are relative to each package's `package.json`
      // (and not the monorepo root)
      "outputs": [".next/**"]
    },
    "test": {
      // A package's `test` script depends on that package's
      // own `build` script being completed first.
      "dependsOn": ["build"],
      // A package's `test` script should only be rerun when
      // either a `.tsx` or `.ts` file has changed in `src` or `test` folders.
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
    },
    // A package's `lint` script has no dependencies and
    // can be run whenever. It also has no filesystem outputs.
    "lint": {},
    "deploy": {
      // A package's `deploy` script depends on the `build`,
      // `test`, and `lint` scripts of the same package
      // being completed. It also has no filesystem outputs.
      "dependsOn": ["build", "test", "lint"]
    }
  }
}

上面的示例中,buildtest这两个任务具有依赖性,必须要等他们的依赖项对应的任务完成后才能执行,所以这里用^来表示。 对于每个包中 package.json 中的 script 命令,如果没有配置覆盖项,那么TurboRepo将缓存默认输出到 dist/** build/**文件夹中。

运行

这样只需要运行

npx turbo run build test lint deploy

就可以运行全部参数

配置.gitignore

根目录 创建.gitignore文件

+ .turbo
+ build/**
+ dist/**
+ .next/**

Remote cache

turbo login
turbo link

然后删除你的./node_modules/.cache/turbo文件夹

显示 full turbo 则证明匹配到了云端的缓存,直接拉下来不再构建一遍

配置husky

在根目录安装husky

先运行husky install

然后创建.husky文件夹

文件树如上

配置文件

//.gitignore
*
# husky.sh
#!/usr/bin/env sh
if [ -z "$husky_skip_init" ]; then
  debug () {
    if [ "$HUSKY_DEBUG" = "1" ]; then
      echo "husky (debug) - $1"
    fi
  }

  readonly hook_name="$(basename -- "$0")"
  debug "starting $hook_name..."

  if [ "$HUSKY" = "0" ]; then
    debug "HUSKY env variable is set to 0, skipping hook"
    exit 0
  fi

  if [ -f ~/.huskyrc ]; then
    debug "sourcing ~/.huskyrc"
    . ~/.huskyrc
  fi

  readonly husky_skip_init=1
  export husky_skip_init
  sh -e "$0" "$@"
  exitCode="$?"

  if [ $exitCode != 0 ]; then
    echo "husky - $hook_name hook exited with code $exitCode (error)"
  fi

  if [ $exitCode = 127 ]; then
    echo "husky - command not found in PATH=$PATH"
  fi

  exit $exitCode
fi
//commit-msg
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install commitlint --edit "$1"
//post-merge
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn install
//pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn lint-staged

安装commit lint

yarn install -D @commitlint/config-conventional 	@commitlint/cli

这个可以在MonoRepo内安装不在根目录装

在前端的package.json里配置

//./frontend/package.json
{
  "name": "frontend",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "build": "nuxi build",
    "dev": "nuxi dev",
    "start": "node .output/server/index.mjs",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "generate": "nuxi generate",
    "typecheck": "tsc --noEmit --incremental false",
    "lint:strict": "eslint --max-warnings=0 ."
  },
  "lint-staged": {
    "**/*.{js,ts,vue,html}": [
      "eslint --max-warnings=0"
    ]
  }
}

最后在根目录创建commitlint.config.js

里面写上commit lint 规则

//commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'docs',
        'chore',
        'style',
        'refactor',
        'ci',
        'test',
        'revert',
        'perf',
        'build',
        'vercel',
      ],
    ],
  },
}

配置.gitignore

node_modules
*.log
dist
.output
.nuxt
.env
.turbo

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port
.vercel
frontend/.env

配置vercel

创建vercel.json

由于是nuxt3项目 vercel有预设 只需要配置一个字段

{
  "framework": "nuxtjs"
}

然后是登录vercel把项目绑定上去

配置好团队是这样的

配置netlify

创建netlify.toml

[build.environment]
  NODE_VERSION = "16"

[build]
  publish = "dist"
  command = "yarn build"
  functions = "netlify/functions"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

默认配置为如上

然后是登录netlify把项目绑定上去

配好了是这样的

配置workflows

配置一个actions用来在线lint 有错会xx

# lint.yaml
# https://github.com/kentcdodds/kentcdodds.com/blob/main/.github/workflows/deployment.yml
name: Code Check
on:
  push:
    branches:
      - main
  pull_request: {}

jobs:
  lint:
    name: ⬣ ESLint
    runs-on: ubuntu-latest
    steps:
      - name: 🛑 Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.9.1

      - name: ⬇️ Checkout repo
        uses: actions/checkout@v2

      - name: ⎔ Setup node
        uses: actions/setup-node@v2
        with:
          node-version: 16

      - name: 📥 Download deps
        uses: bahmutov/npm-install@v1

      - name: 🔬 Lint
        run: npx turbo lint:strict

配置一个release bot 用来生成打包文件 区分版本号

# release.yaml
name: release-please
on:
  # workflow_dispatch:
  push:
    branches:
      - main
jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - uses: google-github-actions/release-please-action@v3
        with:
          release-type: node
          package-name: release-please-action

配置 issue bot 用来自动用issue创建分支

# issue.yaml
name: "Issue Autolink"
on:
  pull_request:
    types: [opened]

jobs:
  issue-links:
    runs-on: ubuntu-latest
    steps:
      - uses: tkt-actions/add-issue-links@v1.6.0
        with:
          repo-token: "${{ secrets.AUTO_LINK }}"
          branch-prefix: "i"
          resolve: "true"

结尾

到此应该初始化的差不多了

这个从创建 debug 到上线的时间大概花了两天 问题出在 vercel 和 netlify 上

vercel 对 nuxt 的兼容性不好,不如他的亲儿子 next

刚去查了一下 nuxt3在1月23日变成了默认版本

nuxt3用了一个叫nitro的编译框架 https://github.com/unjs/nitro

https://nitro.unjs.io/

构建和部署通用JavaScript服务器

Nitro提供了一个强大的工具链和一个来自unjs生态系统的运行时框架,可以随时随地构建和部署任何JavaScript服务器!

unjs 我感觉是写js魔法的组织 里面全是魔法库

这位是主谋

这个框架会自动检测你当前的环境 给你分配一个preset 比如说你在当前node环境build nuxt的话会给你生成一个 .mjs文件 你运行文件就可以启动一个SSR服务器

你在vercel上运行的话就给你 preset:vercel 生成vercel认的文件格式

netlify同理

问题出在 使用MonoRepo 后 nitro不认我这是在vercel里了 他会生成默认的node的mjs文件 然后构建失败

很烦

参考

https://turbo.build/repo/docs/getting-started/existing-monorepo

https://github.com/antfu/vitesse-nuxt3

https://github.com/antfu?tab=repositories&q=vitesse&type=&language=&sort=

https://nitro.unjs.io/

MonoRepo设置与部署 | 青训营笔记

MonoRepo设置与部署 | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 7 天

前言

举个例子,你现在有一个全栈的项目,团队里写了前端、后端、使用文档和一个工具库。

一般情况的话,我们会创建四个仓库,放不同的内容,其中前端如果使用工具库的话,我们会在前端里引入工具库的包(可能是直接,可能是在npm上publish过的)

你希望运行前端项目的时候,同时运行三个命令(例如使用yarn)你需要运行 yarn lintyarn buildyarn serve 你需要敲三个命令,很麻烦。

MonoRepo

什么是MonoRepo

在开发场景中,我们希望各个项目之间能够足够的独立,各自的开发和发布不会产生太多的耦合,现在很多的项目也是出于这种考虑去拆成一个一个独立的子项目,在单独的代码仓库中进行管理,这就是我们常见的单代码仓库的开发模式。

例如我们前言中举的例子,你想要前端项目中使用工具库中的包,你需要到前端,或者使用npm publish后再安装,当你工具库的版本更新的时候,你需要把前端项目里的工具库版本也更新掉。

如果把所有有依赖关系的代码都放到一个仓库中进行统一维护,当一个库变动时,其它的代码能自动的进行依赖升级,那么就能精简开发流程、提高开发效率。这种多包的代码仓库管,就是 MonoRepo。

其实TurboRepo在前端中非常常见,Babel、React、Vue等开源项目都是使用这种方式在管理代码,其中 Babel 官方开源的多包管理工具 Lerna 也被广泛的使用。

这次我介绍的是TurboRepo。

TurboRepo

TurboRepo是一个适用于 JavaScript 和 Typescript TurboRepo的高性能构建工具,使用go、rust语言编写,性能很好。

优势

  • 增量构建:缓存构建内容,并跳过已经计算过的内容,通过增量构建来提高构建速度

  • 内容hash:通过文件内容计算出来的hash来判断文件是否需要进行构建,缓存在云端,登录即可享受

  • 云缓存:可以和团队成员共享CI/CD的云构建缓存,来实现更快的构建

  • 多任务并行执行:在不浪费空闲 CPU 的情况下,以最大并行数量来进行构建

  • 任务管道:通过定义任务之间的关系,让 TurboRepo 优化构建的内容和时间

  • 约定式配置:通过约定来降低配置的复杂度,只需要几行简单的 JSON 就能完成配置(配置turbo.json)

开始使用

对于一个新的项目,可以运行下面的命令来生成全新的代码仓库

npx create-turbo@latest

对于一个已经存在的 monorepo 项目,可以通过下面的步骤来接入 turborepo

安装Turborepo

将 Turborepo 添加到项目最外层的devDependecies

npm install turbo -D
or
yarn add turbo --dev

创建任务管道

package.jsonturbo中,将想要”turbo”的命令添加到管道中 管道定义了 npm 包中 scripts 的依赖关系,并且为这些命令开启了缓存。这些命令的依赖关系和缓存设置会应用到 monorepo 中的各个包中

{
    "turbo": {
        "pipeline": {
            "build": {
                "dependsOn": ["^build"],        
                "outputs": [".next/**"]            
            },
            "test": {
                "dependsOn": ["^build"],
                "outputs": []                            
            },
            "lint": {
                "outputs": []
            },
            "dev": {
                "cache": false            
            } 
        }    
    }
}

上面的示例中,buildtest这两个任务具有依赖性,必须要等他们的依赖项对应的任务完成后才能执行,所以这里用^来表示。 对于每个包中 package.json 中的 script 命令,如果没有配置覆盖项,那么Turborepo将缓存默认输出到 dist/** build/**文件夹中。

pipeline

从上面的 turbo 的配置中可以看出来,管道(pipeline)是一个核心的概念,Turborepo也是通过管道来处理各个任务和他们的依赖关系的。

Turborepo提供了一种声明式的方法来指定各个任务之间的关系,这种方式能够更容易理解各个任务之间的关系,并且Turborepo也能通过这种显式的声明来优化任务的执行并充分调度CPU的多核心性能。

配置pipeline

pipeline中每个键名都可以通过运行turbo run来执行,并且可以使用dependsOn来执行当前管道的依赖项。

上图的执行流程,可以配置成如下的格式

{
    "turbo": {
        "pipeline": {
            "build": {
                "dependsOn": ["^build"],           
            },
            "test": {
                "dependsOn": ["build"],
                "outputs": []                            
            },
            "lint": {
                "outputs": []
            },
            "deploy": {
                "dependsOn": ["build", "test", "lint"]           
            } 
        }    
    }
}

通过dependsOn的配置,可以看出各个命令的执行顺序:

  • 因为A和C依赖于B,所以包的构建存在依赖关系,根据build的dependson配置,会先执行依赖项的build命令,依赖项执行完后才会执行自己的build命令。从上面的瀑布流中也可以看出,B的build先执行,执行完以后A和C的build会并行执行
  • 对于test,只依赖自己的build命令,只要自己的build命令完成了,就立即执行test
  • lint没有任何依赖,在任何时间都可以执行
  • 自己完成build、test、lint后,再执行deploy命令

可以通过下面的命令执行:

npx turbo run test build lint deploy

常规依赖

如果一个任务的执行,只依赖自己包其他的任务,那么可以把依赖的任务放在dependsOn数组里

{
    "turbo": {
        "pipeline": {
            "deploy": {
                "dependsOn": ["build", "test", "lint"]           
            } 
        }    
    }
}

特定依赖

在一些场景下,一个任务可能会依赖某个包的特定的任务,这时候我们需要去手动指定依赖关系。

{
    "turbo": {
        "pipeline": {
            "build": {
                "dependsOn": ["^build"],           
            },
            "test": {
                "dependsOn": ["build"],
                "outputs": []                            
            },
            "lint": {
                "outputs": []
            },
            "deploy": {
                "dependsOn": ["build", "test", "lint"]           
            },
            "frontend#deploy": {
                "dependsOn": ["ui#test", "backend#deploy"]            
            }
        }    
    }
}

Remote cache

当多人开个一个项目的时候,团队的成员可以共享构建的缓存,从而加快项目的构建速度。

当一个成员把某个分支构建的缓存文件推送到远程的git仓库是,另一个成员如果在同一个分支上进行开发,那么Turborepo 可以支持你去选择某个成员的构建缓存,并在运行相关的构建任务时,从远端拉去缓存文件到本地,加快构建的速度

运行 npx turbo link,进行登录后,就可以选择要使用的缓存

显示 full turbo 则证明匹配到了云端的缓存,直接拉下来不再构建一遍

结尾

我相信 Turborepo 的出现在不久的将来一定会成为 Monorepo 工具链中重要的一环,无论是构建缓存功能还是基于 pipeline 的智能任务调度系统,都非常优秀的解决了传统 Monorepo 存在“慢”的问题。

为了更好的性能,大部分人将不再局限于使用 JavaScript 开发 JavaScript 工具,而是更愿意选择其他高门槛语言。

我感觉使用turbo有很好的体验,但他好像对nuxt兼容性不好,只兼容自己的亲儿子next🤣。

参考

https://juejin.cn/post/7051929587852247077
https://juejin.cn/post/7048234698048274469
https://juejin.cn/post/7129267782515949575

https://github.com/vercel/turbo

https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks

HTTP协议 | 青训营笔记

HTTP协议 | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 6 天

本堂课重点内容

  1. HTTP 协议的简单介绍
  2. HTTP 协议的报文结构

详细知识点介绍

HTTP 全称超文本传输协议(Hyper Text Transfer Protocol),是一个基于TCP协议的无状态应用层协议。

HTTP 发展历史

  • HTTP/0.9

    • 只有GET类型的请求

    • 只能响应HTML文档

  • HTTP/1.0

    • 增加了 Header

    • 增加了状态码

    • 支持了多种文档类型

  • HTTP/1.1(常见)

    • 连接复用

    • 提供了缓存支持

    • 支持内容协商

  • HTTP/2

    • 二进制协议

    • 支持Header压缩

    • 增加了服务器推送(Server Push)

  • HTTP/3

    • 基于QUIC(udp)协议

常见的 HTTP Methods

请求类型 说明
GET 请求一个指定的资源。使用GET的请求一般用于获取数据
POST 将实体提交到指定资源,通常导致服务器上的状态变化或副作用
PUT 用于请求有效载荷替换目标资源
DELETE 用于删除指定的资源
HEAD 请求一个与GET请求的响应相同的响应,但没有响应体
OPTIONS 预检请求,用于描述目标资源的通信选项

其中最常见的是 GET 请求和 POST 请求,PUT DELETE 常见于各类 RESTful API 中。而 OPTIONS 请求被称为预检请求,倘若我们尝试为前面的几个请求类型增加自定义Header,浏览器会默认向服务器发出一个OPTIONS请求,用于判断服务器能否接收/处理该header。

常见 HTTP 状态码

1xx - 指示信息

2xx - 请求成功

3xx - 重定向操作

4xx - 客户端错误

5xx - 服务端错误

  • 200 正常响应
  • 301 永久重定向
  • 302 临时重定向
  • 401 未授权
  • 403 请求被拒绝
  • 404 请求资源不存在
  • 500 服务器错误
  • 504 网关错误

RESTful API

他是一种API设计风格。

  1. 每一个URL代表一种资源
  2. 客户端和服务端之间,传递这种资源的某种表现层。
  3. 客户端通过HTTP method,对服务端资源进行操作,实现”表现层状态转化”

常见请求头

  • Accept:接受类型
  • Content-Type:客户端发送出去实体内容的类型
  • Cache-Control:指定请求和响应遵循的缓存机制。
  • Cookie:有cookie会自动带上

常用响应头

  • Set-Cookie:设置和页面关联的Cookie
  • Content-Type:服务端

HTTP2

帧:http2最小通信单位,每个帧都包含帧头

消息:与逻辑请求或响应消息对应的完整的一系列帧。

数据流:已建立的连接内的双向字节流,可以承载一条或多条消息。

交错发送,接收方重组织。

HTTP2连接是永久的,而且仅需要每个来源一个连接。

流控制:阻止发送方向接收方发送大量数据的机制。

服务器有主动推送能力,可以提前推送静态资源。

HTTPS

HTTPS = HTTP + SSL

对称加密:加密和解密都是用同一个密钥

非对称加密:加密和解密都需要使用两个不同的密钥:公钥和私钥。

鉴权

  • Session+Cookie
  • JWT

WebSocket

  • 浏览器与服务器进行全双工通讯的网络技术
  • 实时性高

其他

QUIC:HTTP3的新特性

实践练习例子

https://www.w3schools.cn/html/exercise.asp

课后个人总结

本次课程主要介绍了 HTTP 协议的基本知识,以及 HTTP 的发展历史,以及常见的请求方法和状态码,以及 RESTful API。另外还介绍了 HTTPS 和 WebSocket 以及 QUIC 等新特性。学完本节课,我对 HTTP 协议有了更深入的理解,掌握了其中的常见请求头和响应头,以及常用的状态码,并且了解了 HTTP2,HTTPS,WebSocket,以及 QUIC 等新特性。

引用参考

https://developer.mozilla.org/zh-CN/docs/Web/HTTP

https://jwt.io/

TypeScript | 青训营笔记

TypeScript | 青训营笔记

这是我参与「第五届青训营」伴学笔记创作活动的第 4 天

本堂课重点内容

  1. TypeScript 定义解析
  2. TypeScript 基础语法

详细知识点介绍

  1. 语言特性
  2. 基本数据类型

语言特性

JavaScript 是动态类型的语言,而TypeScript顾名思义是静态类型的语言。与js相比,静态类型的特性为ts带来了许多优势

  • 更强的可读性
  • 更强的可维护性
  • 大型项目中提升稳定性
  • 开发效率

而作为js的超集,ts兼容所有js特性,且支持与js共存,能够渐进式地引入和升级。

基础数据类型

TypeScript能够在变量名称后面添加冒号和期望的类型来为变量赋予具体的类型,例如下面的代码,常见的类型有 string number boolean null undefined

let a: string = "string"
let b: number = 12
let c: boolean = true

如果在之后尝试给变量赋值不同类型的值,将会产生报错

对象类型

ts使用interface关键字来定义新类型

interface ICustomObject {
    name: string
    age: number
    hobby?: string
    readonly id: number
}

const obj: ICustonObject {
    name: "hello world",
    age: 12,
    hobby: 'coding',
    id: 10
}

对于只读属性,可以在类型定义时增加readonly修饰符,在之后倘若修改只读属性,将会报错。在属性名后面紧跟问号表示该属性可选(可能不存在),后续倘若直接读取该属性,将导致报错(需要先判断该属性是否存在)

一般情况下,约定自定义类型以大写字母I开头,例如上面的 ICustomObject

特殊情况

在某些情况下,对象的键名(key)可能并不固定,比如我希望某个对象键名是任意string,键值是布尔型,那么可以向下面这样定义

interface IObj {
    [key: string]: boolean
}

或者使用 type 关键字

type IObj = Record<string, boolean>

函数类型

假设有一个js函数 add

function add(x, y) {
    return x + y
}

为其添加类型声明后

function add(x: number, y: number):number {
    return x + y
}

对于匿名的箭头函数,我们也可以为其添加类型

// js
const add = (x, y) => x + y

// ts
const add: (x: number, y: number) => number = (x, y) => x + y

或许在某些情况下,将类型和函数写在一起会稍显凌乱,那么我们也可以将函数类型单独定义,例如下面这样

interface IAdd {
    (x: number, y: number): number
}

const add: IAdd = (x, y) => x + y

数组类型

数组的类型定义有很多方法,最常见的是像c语言那样的类型定义

type IArray = number[]

const array: IArray = [1, 2, 3, 4, 5]

同样也可以使用ts提供的Array泛型,是一样的效果

type IArray = Array<number>

const array: IArray = [1, 2, 3, 4, 5]

因为数组实际上就是键名特殊的对象,所以也可以用表示对象的方法来表示

interface IArray {
    [key: number]: number
}

const array: IArray = [1, 2, 3, 4, 5]

TS 新增的类型

为了实现一些特殊需求,ts也新增了许多类型方便使用

空类型

function test(): void {
    alert("hello")
}

顾名思义,表示无赋值,例如函数没有返回值

任意类型

type IArray = Array<any>

const array: IArray = [1, "string", true, {a: 1}]

枚举类型

该类型能够通过枚举名查枚举值,同时能够使用枚举值查枚举名

enum EnumTest {
    man = 'male',
    woman = 'female',
}

EnumTest['man'] === 'male'
EnumTest['male'] === 'man'

在不提供枚举名的时候,将默认为由0开始的索引值

enum EnumWeekDay { Mon, Tue, Wed, Thu, Fri }

EnumTest['Mon'] === 0
EnumTest['Tue'] === 1

高级类型

联合类型与交叉类型

联合类型: IA | IB;

交叉类型: IA & IB;

类型保护

可以直接通过.type然后拿到某个变量的类型,可用于后续变量类型的判断,达到类型保护的作用。

补充类型

  • 空类型,表示无赋值
  • 任意类型,是所有类型的子类型
  • 枚举类型:支持枚举值到枚举名的正、反向映射

课后个人总结

TypeScript是一个非常有用的语言,给JavaScript开发带来了严格的检查

引用参考

www.typescriptlang.org/docs/

https://bytedance.feishu.cn/file/boxcnIjrYQpF7pL6nWGtTl5W8Jd