feat: migrate and organize documentation and blog posts
refactor(blog): restructure blog post layouts and metadata docs: add markdown migration guide and project rules update style(global.css): update typography theme variables chore: move temp_docs to appropriate blog post locations
This commit is contained in:
@@ -50,6 +50,17 @@ const finalReadingTime = readTime ? parseInt(readTime.replace(/\D/g, '')) : unde
|
||||
|
||||
<!-- Blog post header -->
|
||||
<header class="mb-10">
|
||||
<PostMeta
|
||||
lang={lang}
|
||||
publishDate={finalPublishDate}
|
||||
readingTime={finalReadingTime}
|
||||
tags={tags}
|
||||
tagId={tagId}
|
||||
category={category}
|
||||
categoryId={categoryId}
|
||||
className="justify-start mb-4"
|
||||
/>
|
||||
|
||||
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold text-foreground mb-6 leading-tight tracking-tight">
|
||||
{title}
|
||||
</h1>
|
||||
@@ -59,21 +70,11 @@ const finalReadingTime = readTime ? parseInt(readTime.replace(/\D/g, '')) : unde
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<PostMeta
|
||||
lang={lang}
|
||||
publishDate={finalPublishDate}
|
||||
readingTime={finalReadingTime}
|
||||
tags={tags}
|
||||
tagId={tagId}
|
||||
category={category}
|
||||
categoryId={categoryId}
|
||||
className="justify-start"
|
||||
/>
|
||||
</header>
|
||||
|
||||
<!-- Blog post content with typography styles -->
|
||||
<article class="prose prose-lg dark:prose-invert max-w-none prose-headings:scroll-mt-24 prose-headings:font-bold prose-headings:tracking-tight prose-h1:text-2xl sm:prose-h1:text-3xl prose-h2:text-xl sm:prose-h2:text-2xl prose-h3:text-lg sm:prose-h3:text-xl prose-h4:text-base sm:prose-h4:text-lg prose-p:text-base sm:prose-p:text-lg prose-p:leading-relaxed prose-p:mb-6 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:text-foreground prose-code:text-sm prose-code:px-2 prose-code:py-1 prose-code:rounded prose-pre:bg-muted prose-pre:border prose-pre:text-sm prose-blockquote:border-l-primary prose-blockquote:bg-muted/30 prose-blockquote:text-base prose-li:text-base sm:prose-li:text-lg prose-li:leading-relaxed">
|
||||
<article class="prose prose-lg dark:prose-invert max-w-none prose-headings:scroll-mt-24 prose-headings:font-bold prose-headings:tracking-tight prose-h1:text-2xl sm:prose-h1:text-3xl prose-h2:text-xl sm:prose-h2:text-2xl prose-h3:text-lg sm:prose-h3:text-xl prose-h4:text-base sm:prose-h4:text-lg prose-p:text-base sm:prose-p:text-lg prose-p:leading-relaxed prose-p:mb-6 prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-strong:text-foreground prose-code:text-sm prose-code:px-2 prose-code:py-1 prose-code:rounded prose-pre:bg-muted prose-pre:border prose-pre:text-sm prose-blockquote:border-l-primary prose-blockquote:bg-muted/30 prose-blockquote:text-base prose-li:text-base sm:prose-li:text-lg prose-li:leading-relaxed
|
||||
prose-code:before:content-none prose-code:after:content-none">
|
||||
<slot />
|
||||
</article>
|
||||
|
||||
|
||||
100
src/pages/zh/blog/posts/browser-rendering-principles.md
Normal file
100
src/pages/zh/blog/posts/browser-rendering-principles.md
Normal file
@@ -0,0 +1,100 @@
|
||||
---
|
||||
layout: "@/layouts/BlogPostLayout.astro"
|
||||
title: "浏览器的运行原理"
|
||||
description: "本文详细介绍了浏览器渲染页面的七个阶段(HTML解析、样式计算、布局、分层、绘制、分块、光栅化)以及reflow(回流)、repaint(重绘)的概念和transform效率高的原因。"
|
||||
date: "2023-08-10"
|
||||
image: "https://images.unsplash.com/photo-1510511459019-5dda7724fd87?q=80&w=1470&auto=format&fit=crop"
|
||||
tags: ["浏览器", "前端", "渲染原理"]
|
||||
tagId: ["browser", "frontend", "rendering"]
|
||||
category: "前端开发"
|
||||
categoryId: "frontend"
|
||||
readTime: "7 min read"
|
||||
---
|
||||
|
||||
浏览器渲染页面的过程可以分为七个阶段:
|
||||
|
||||
### 1. HTML解析
|
||||
|
||||
浏览器接收到服务器响应的HTML文档后,会首先解析HTML文档,构建DOM树。DOM树是由DOM元素及属性节点组成的,树的根是document对象。
|
||||
|
||||
### 2. 样式计算
|
||||
|
||||
浏览器会解析CSS文件和元素上的inline样式,计算出DOM节点的样式。这个过程包括:
|
||||
|
||||
- 把CSS转换为浏览器能够理解的结构
|
||||
- 计算出DOM节点的具体样式
|
||||
|
||||
### 3. 布局
|
||||
|
||||
布局阶段会计算出DOM树中可见元素的几何位置,这个过程叫做布局或重排。
|
||||
|
||||
### 4. 分层
|
||||
|
||||
浏览器会根据布局后的DOM树,将其分为多个图层。这个过程主要是为了处理页面中的复杂效果,如3D变换、页面滚动等。
|
||||
|
||||
### 5. 绘制
|
||||
|
||||
在分层的基础上,浏览器会为每个图层生成绘制列表,并将其提交到合成线程。
|
||||
|
||||
### 6. 分块
|
||||
|
||||
合成线程会将图层划分为图块,这些图块的大小通常是256x256或者512x512。
|
||||
|
||||
### 7. 光栅化
|
||||
|
||||
合成线程会按照视口附近的图块来优先生成位图,这个过程叫做光栅化。光栅化过程中,会将图块转换为位图。
|
||||
|
||||
## 回流(Reflow)和重绘(Repaint)
|
||||
|
||||
### 回流(Reflow)
|
||||
|
||||
当我们对DOM的修改引发了DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。
|
||||
|
||||
引起回流的操作包括:
|
||||
|
||||
- 页面首次渲染
|
||||
- 浏览器窗口大小发生改变
|
||||
- 元素尺寸或位置发生改变
|
||||
- 元素内容变化(文字数量或图片大小等等)
|
||||
- 元素字体大小变化
|
||||
- 添加或者删除可见的DOM元素
|
||||
- 激活CSS伪类(例如::hover)
|
||||
- 查询某些属性或调用某些方法
|
||||
|
||||
一些常用且会导致回流的属性和方法:
|
||||
|
||||
- clientWidth、clientHeight、clientTop、clientLeft
|
||||
- offsetWidth、offsetHeight、offsetTop、offsetLeft
|
||||
- scrollWidth、scrollHeight、scrollTop、scrollLeft
|
||||
- scrollIntoView()、scrollIntoViewIfNeeded()
|
||||
- getComputedStyle()
|
||||
- getBoundingClientRect()
|
||||
- scrollTo()
|
||||
|
||||
### 重绘(Repaint)
|
||||
|
||||
当我们对DOM的修改导致了样式的变化、但未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需要重新计算元素的几何属性、直接为该元素绘制新的样式。这个过程叫做重绘。
|
||||
|
||||
相比回流,重绘的性能开销较小,因为重绘只是重新绘制元素的外观,而不需要重新计算元素的位置。
|
||||
|
||||
## 为什么transform效率高
|
||||
|
||||
transform是通过创建一个新的复合图层来实现的,这个复合图层会单独进行绘制,然后再与其他图层进行合成。这样,当我们使用transform进行变换时,只需要重新绘制这个复合图层,而不需要重新绘制整个页面。
|
||||
|
||||
使用transform可以避免回流和重绘,因为它不会影响到DOM的布局,只会影响到最终的渲染结果。这就是为什么使用transform进行动画会比直接修改元素的top、left等属性效率高的原因。
|
||||
|
||||
## 优化策略
|
||||
|
||||
为了减少回流和重绘,我们可以采取以下策略:
|
||||
|
||||
1. **批量修改DOM**:使用DocumentFragment或者先将元素设为display: none,然后进行多次修改,最后再显示。
|
||||
|
||||
2. **避免频繁读取会引发回流/重绘的属性**:如果必须多次读取,就将值缓存起来。
|
||||
|
||||
3. **使用transform和opacity来实现动画效果**:这两个属性不会触发回流。
|
||||
|
||||
4. **使用will-change属性**:这个属性可以提前告知浏览器元素将要发生的变化,浏览器可以提前做好优化工作。
|
||||
|
||||
5. **使用绝对定位或固定定位**:绝对定位和固定定位的元素发生变化时,只会影响自身,不会影响其他元素。
|
||||
|
||||
通过理解浏览器的渲染原理和优化策略,我们可以编写出更高效的前端代码,提升用户体验。
|
||||
129
src/pages/zh/blog/posts/docusaurus-v3-with-tailwindcss.md
Normal file
129
src/pages/zh/blog/posts/docusaurus-v3-with-tailwindcss.md
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
layout: "@/layouts/BlogPostLayout.astro"
|
||||
title: "Docusaurus v3中使用Tailwind CSS的样式"
|
||||
description: "本文详细介绍了如何在Docusaurus v3中集成和配置Tailwind CSS,包括安装依赖、配置tailwind.config.js、创建tailwind.css和postcss.config.js等步骤。"
|
||||
date: "2023-11-05"
|
||||
image: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?q=80&w=1470&auto=format&fit=crop"
|
||||
tags: ["Docusaurus", "TailwindCSS", "前端框架"]
|
||||
tagId: ["docusaurus", "tailwindcss", "frontend-framework"]
|
||||
category: "前端开发"
|
||||
categoryId: "frontend"
|
||||
readTime: "4 min read"
|
||||
---
|
||||
|
||||
## 安装相关依赖
|
||||
|
||||
```bash
|
||||
npm install tailwindcss
|
||||
```
|
||||
## 配置tailwind.config.js
|
||||
|
||||
```js
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{js,jsx,ts,tsx,md,mdx}",
|
||||
"./docs/**/*.{md,mdx}",
|
||||
],
|
||||
theme: {
|
||||
screens: {
|
||||
xs: '480px',
|
||||
sm: '576px',
|
||||
md: '768px',
|
||||
lg: '998px',
|
||||
xl: '1200px',
|
||||
'2xl': '1400px',
|
||||
},
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: 'var(--ifm-font-family-base)',
|
||||
firacode: 'var(--font-family-firacode)',
|
||||
kaiti: 'var(--font-family-kaiti)',
|
||||
},
|
||||
colors: {
|
||||
'font-color': 'var(--ifm-font-color-base)',
|
||||
'link-color': 'var(--ifm-link-color)',
|
||||
'link-hover-color': 'var(--ifm-link-hover-color)',
|
||||
primary: 'var(--ifm-color-primary)',
|
||||
'primary-light': 'var(--ifm-color-primary-light)',
|
||||
'primary-lighter': 'var(--ifm-color-primary-lighter)',
|
||||
'primary-lightest': 'var(--ifm-color-primary-lightest)',
|
||||
'primary-dark': 'var(--ifm-color-primary-dark)',
|
||||
'primary-darker': 'var(--ifm-color-primary-darker)',
|
||||
'primary-darkest': 'var(--ifm-color-primary-darkest)',
|
||||
},
|
||||
boxShadow: {
|
||||
nysm: '0 0 2px 0 rgb(0 0 0 / 0.05)',
|
||||
ny: '0 0 3px 0 rgb(0 0 0 / 0.1), 0 0 2px - 1px rgb(0 0 0 / 0.1)',
|
||||
nymd: '0 0 6px -1px rgb(0 0 0 / 0.1), 0 0 4px -2px rgb(0 0 0 / 0.1)',
|
||||
nylg: '0 0 15px -3px rgb(0 0 0 / 0.1), 0 0 6px -4px rgb(0 0 0 / 0.1)',
|
||||
spread: '0 5px 40px rgb(0 0 0 / 0.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
darkMode: ["class", '[data-theme="dark"]'],
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
blocklist: ["container"],
|
||||
}
|
||||
```
|
||||
## 创建tailwind.css
|
||||
|
||||
在`src/css`目录下创建`tailwind.css`文件,并写入以下内容:
|
||||
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
```
|
||||
**注意:创建完成后是需要在custom.css中导入tailwind.css的,否则tailwind样式不会生效**
|
||||
|
||||
## 创建postcss.config.js
|
||||
|
||||
在项目根目录下创建`postcss.config.js`文件,并写入以下内容:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 在custom.css中导入tailwind.css
|
||||
|
||||
找到项目中的`src/css/custom.css`文件,在文件顶部添加以下内容:
|
||||
|
||||
```css
|
||||
@import './tailwind.css';
|
||||
```
|
||||
|
||||
## 使用Tailwind CSS
|
||||
|
||||
现在,你可以在项目中使用Tailwind CSS的样式了。例如:
|
||||
|
||||
```jsx
|
||||
<div className="flex flex-col items-center justify-center p-4 bg-gray-100 dark:bg-gray-800 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-bold text-primary mb-2">Hello Tailwind CSS</h2>
|
||||
<p className="text-gray-700 dark:text-gray-300">This is a paragraph styled with Tailwind CSS</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 由于Docusaurus已经有自己的CSS样式,所以我们在配置Tailwind CSS时禁用了`preflight`,这样可以避免样式冲突。
|
||||
|
||||
2. 我们使用了`darkMode: ["class", '[data-theme="dark"]']`配置,这样可以让Tailwind CSS的暗黑模式与Docusaurus的暗黑模式保持一致。
|
||||
|
||||
3. 我们在`theme.extend.colors`中添加了一些变量,这些变量使用了Docusaurus的CSS变量,这样可以让Tailwind CSS的颜色与Docusaurus的主题颜色保持一致。
|
||||
|
||||
4. 我们在`blocklist`中添加了`container`,这是因为Docusaurus已经有自己的容器样式,我们不希望Tailwind CSS的容器样式覆盖它。
|
||||
|
||||
## 总结
|
||||
|
||||
通过以上步骤,我们成功地在Docusaurus v3中集成了Tailwind CSS。现在,我们可以使用Tailwind CSS的所有功能,同时保持与Docusaurus主题的一致性。
|
||||
|
||||
希望这篇文章对你有所帮助!如果你有任何问题,欢迎在评论区留言。
|
||||
152
src/pages/zh/blog/posts/git-commit-convention.md
Normal file
152
src/pages/zh/blog/posts/git-commit-convention.md
Normal file
@@ -0,0 +1,152 @@
|
||||
---
|
||||
layout: "@/layouts/BlogPostLayout.astro"
|
||||
title: "Git提交规范"
|
||||
description: "本文详细介绍了Git提交规范,包括Header、Body和Footer三个部分的具体内容和使用规则,帮助团队成员更好地理解和遵循统一的提交标准。"
|
||||
date: "2023-10-20"
|
||||
image: "https://images.unsplash.com/photo-1618401471353-b98afee0b2eb?q=80&w=1476&auto=format&fit=crop"
|
||||
tags: ["Git", "规范", "版本控制"]
|
||||
tagId: ["git", "convention", "version-control"]
|
||||
category: "开发工具"
|
||||
categoryId: "dev-tools"
|
||||
readTime: "5 min read"
|
||||
---
|
||||
|
||||
### 为什么需要提交规范
|
||||
|
||||
在团队协作中,如果Git的提交说明精准,在后期协作以及Bug处理时会变得有据可查,项目的开发可以根据规范的提交说明快速生成开发日志,从而方便开发者或用户追踪项目的开发信息和功能特性。
|
||||
|
||||
### 提交说明的结构
|
||||
|
||||
提交说明包含三个部分:Header、Body 和 Footer。
|
||||
|
||||
```
|
||||
<Header>
|
||||
|
||||
<Body>
|
||||
|
||||
<Footer>
|
||||
```
|
||||
|
||||
#### Header
|
||||
|
||||
Header部分包含三个字段:type(必需)、scope(可选)和subject(必需)。
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
```
|
||||
|
||||
##### type
|
||||
|
||||
type用于说明commit的类别,只允许使用下面7个标识:
|
||||
|
||||
- **feat**:新功能(feature)
|
||||
- **fix**:修补bug
|
||||
- **docs**:文档(documentation)
|
||||
- **style**: 格式(不影响代码运行的变动)
|
||||
- **refactor**:重构(即不是新增功能,也不是修改bug的代码变动)
|
||||
- **test**:增加测试
|
||||
- **chore**:构建过程或辅助工具的变动
|
||||
|
||||
如果type为feat和fix,则该commit将肯定出现在Change log之中。其他情况(docs、chore、style、refactor、test)由你决定,要不要放入Change log,建议是不要。
|
||||
|
||||
##### scope
|
||||
|
||||
scope用于说明commit影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
|
||||
|
||||
##### subject
|
||||
|
||||
subject是commit目的的简短描述,不超过50个字符。
|
||||
|
||||
- 以动词开头,使用第一人称现在时,比如change,而不是changed或changes
|
||||
- 第一个字母小写
|
||||
- 结尾不加句号(.)
|
||||
|
||||
#### Body
|
||||
|
||||
Body部分是对本次commit的详细描述,可以分成多行。
|
||||
|
||||
有两个注意点:
|
||||
|
||||
- 使用第一人称现在时,比如使用change而不是changed或changes
|
||||
- 应该说明代码变动的动机,以及与以前行为的对比
|
||||
|
||||
#### Footer
|
||||
|
||||
Footer部分只用于两种情况:
|
||||
|
||||
##### 不兼容变动
|
||||
|
||||
如果当前代码与上一个版本不兼容,则Footer部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。
|
||||
|
||||
```
|
||||
BREAKING CHANGE: isolate scope bindings definition has changed.
|
||||
|
||||
To migrate the code follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
scope: {
|
||||
myAttr: 'attribute',
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
scope: {
|
||||
myAttr: '@',
|
||||
}
|
||||
|
||||
The removed `inject` wasn't generaly useful for directives so there should be no code using it.
|
||||
```
|
||||
|
||||
##### 关闭Issue
|
||||
|
||||
如果当前commit针对某个issue,那么可以在Footer部分关闭这个issue。
|
||||
|
||||
```
|
||||
Closes #234
|
||||
```
|
||||
|
||||
也可以一次关闭多个issue。
|
||||
|
||||
```
|
||||
Closes #123, #245, #992
|
||||
```
|
||||
|
||||
### 示例
|
||||
|
||||
```
|
||||
feat(browser): onUrlChange event (popstate/hashchange/polling)
|
||||
|
||||
Added new event to browser:
|
||||
- forward popstate event if available
|
||||
- forward hashchange event if popstate not available
|
||||
- do polling when neither popstate nor hashchange available
|
||||
|
||||
Closes #123, #305, #392
|
||||
```
|
||||
|
||||
```
|
||||
fix(release): need to depend on latest rxjs and zone.js
|
||||
|
||||
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||
```
|
||||
|
||||
```
|
||||
docs(changelog): update change log to beta.5
|
||||
```
|
||||
|
||||
```
|
||||
style(router): fix linting errors
|
||||
```
|
||||
|
||||
```
|
||||
refactor(core): rename onInit to ngOnInit
|
||||
```
|
||||
|
||||
```
|
||||
chore(deps): bump rxjs from 5.0.0-beta.2 to 5.0.0-beta.6
|
||||
```
|
||||
|
||||
### 总结
|
||||
|
||||
规范的Git提交说明可以帮助团队成员更好地理解和遵循统一的提交标准,提高团队协作效率,也为后期的项目维护提供了便利。
|
||||
104
src/pages/zh/blog/posts/gitea-actions-deployment-automation.md
Normal file
104
src/pages/zh/blog/posts/gitea-actions-deployment-automation.md
Normal file
@@ -0,0 +1,104 @@
|
||||
---
|
||||
layout: "@/layouts/BlogPostLayout.astro"
|
||||
title: "利用Gitea actions创建项目部署的自动化流程"
|
||||
description: "本文以部署一个前端项目为示例,手把手教你如何利用Gitea actions创建项目部署的自动化流程,对于不想使用Jenkins的小伙伴来说,Gitea actions是一个不错的CI/CD解决方案。"
|
||||
date: "2023-12-15"
|
||||
image: "https://images.unsplash.com/photo-1607799279861-4dd421887fb3?q=80&w=1470&auto=format&fit=crop"
|
||||
tags: ["Gitea", "Actions", "CI/CD", "自动化部署"]
|
||||
tagId: ["gitea", "actions", "cicd", "automation-deployment"]
|
||||
category: "DevOps"
|
||||
categoryId: "devops"
|
||||
readTime: "10 min read"
|
||||
---
|
||||
|
||||
本文以部署一个前端项目为示例,将手把手教你如何利用Gitea actions创建项目部署的自动化流程,对于不想使用Jenkins的小伙伴来说,Gitea actions是一个不错的`CI/CD`解决方案。
|
||||
|
||||
## 前言
|
||||
在我们开发完一个项目后,需要将项目部署到服务器上,以便于用户可以访问。但是,每次部署都需要手动操作,这会浪费我们的时间和精力。因此,我们可以利用Gitea actions创建一个自动化流程,以便于我们快速部署项目。
|
||||
|
||||
## 什么是Gitea actions
|
||||
Gitea actions是Gitea的一个功能,它允许我们创建自动化流程,以便于我们快速部署项目。Gitea actions可以执行各种任务,例如构建、测试、部署等。Gitea actions可以使用各种编程语言和工具,例如Python、Java、Docker等。并且Gitea actions在配置上尽量与GitHub actions的配置兼容,因此,如果你已经熟悉了GitHub actions,那么你也可以轻松地使用Gitea actions。
|
||||
详见官方文档:[Gitea actions](https://docs.gitea.com/zh-cn/usage/actions/overview)
|
||||
|
||||
## 准备工作
|
||||
在我们开始创建此流程前,首先你需要准备以下资料:
|
||||
1. 一台服务器,最好拥有root权限(会安装一些工具)
|
||||
2. 一个已部署的Gitea仓库,版本至少需要>=`v1.19`,官方强烈建议使用`v1.20`或更高版本
|
||||
3. docker环境(非必须, 用在部署runner的主机上)
|
||||
|
||||
> ps: 作者这里服务器准备的是:一台打包服务器 + 一台部署(目标)服务器, 你可以只用一台服务器,但注意环境的隔离
|
||||
|
||||
请在进行后续步骤前,确保已完成所有的准备工作,具体的安装配置,请参考官方文档,这里我不会再赘述。[安装](https://docs.gitea.com/zh-cn/category/installation)
|
||||
|
||||
如果你使用**1panel面板**,可直接在应用商店中一键完成安装,作者就就是使用此方式。
|
||||
|
||||
>注意:在版本 < `v1.21.0`之前的版本,需要在配置文件中,开启`actions`功能,具体请参考官方文档[配置](https://docs.gitea.com/zh-cn/usage/actions/quickstart),之后的版本则是默认开启。
|
||||
|
||||
本教程使用的Gitea版本为`v1.22.6` !
|
||||
|
||||
|
||||
## 配置Runner
|
||||
在Gitea actions中,运行Job,都是通过Runner来执行的,因此,我们需要先配置Runner,以便于我们后续的部署流程。有两种安装方式,可直接安装到主机上,也可以通过docker容器中,这里作者将通过docker安装,为避免过多的资源消耗,可以将其安装到一个单独的主机中(ps:对于国内用户,可以选择一台香港服务器,这样运行job的速度会更快😄), 作者选择的主机就是一台香港的VPS
|
||||
|
||||
### 下载act_Runner
|
||||
|
||||
首先我们需要下载此工具,用来注册或者生成配置[下载地址](https://gitea.com/gitea/act_runner/releases),请在此页面中选择与你的系统对应的版本进行下载,下载完成后,上传到服务器中,这里我是上传到了服务器中的`/gitea_runner2`目录下。也可以使用`curl`等工具直接下载, 可直接使用以下命令,但注意名称和下载地址,我的系统是`Debain12 x86_64`,因此下载的是`act_runner-0.2.11-linux-amd64`。
|
||||
|
||||
```bash
|
||||
curl -o act_runner2 https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64
|
||||
```
|
||||
完成后,设置下执行权限
|
||||
```bash
|
||||
chmod +x act_runner2 # 注意文件名,请根据实际情况修改
|
||||
|
||||
./act_runner --version # 看到版本信息就代表成功了
|
||||
```
|
||||
### 生成配置
|
||||
```
|
||||
./act_runner2 generate-config > config.yaml
|
||||
```
|
||||
一般情况下默认的配置就够了,如果你有其他的需求请自行修改`config.yaml`。
|
||||
|
||||
**由于作者选用docker安装的方式,所以如果你选用直接在主机安装的方式,可以跳过后续的安装步骤,参考官方文档进行安装[直接安装](https://docs.gitea.com/zh-cn/usage/actions/quickstart)**
|
||||
|
||||
### 注册runner
|
||||
|
||||
这里我选择通过`docker-compose.yml`配置的方式进行注册
|
||||
|
||||
**注意: 将配置中的参数结合你自己的情况修改,以下配置仅供参考!!!**
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
services:
|
||||
runner:
|
||||
image: gitea/act_runner:latest
|
||||
environment:
|
||||
CONFIG_FILE: /config.yaml
|
||||
GITEA_INSTANCE_URL: "<your_gitea.com>"
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN: "<your_registration_token_here>"
|
||||
GITEA_RUNNER_NAME: "act_vps_runner2"
|
||||
GITEA_RUNNER_LABELS: "act_vps_runner2"
|
||||
volumes:
|
||||
- /gitea_runner2/config.yaml:/config.yaml
|
||||
- /gitea_runner2/runner_data/data:/data
|
||||
- /gitea_runner2/runner_data/cache:/root/.cache
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
### 配置说明
|
||||
|
||||
**GITEA_RUNNER_REGISTRATION_TOKEN** 此参数为注册令牌,需要从你的gitea中进行获取,一共有三个级别,我这里直接选用组织级别的,在组织设置页中进行获取,如下图所示:
|
||||

|
||||
|
||||
其他的实例配置页面及说明请参考官方文档,有详细的说明
|
||||
|
||||
**如果您无法看到设置页面,请确保您具有正确的权限并且已启用 Actions, 检查Gitea版本 !!!**
|
||||
|
||||
### 运行并生成runner
|
||||
|
||||
由于作者使用1panel面板,因此直接在容器选项中编排即可,如果你没有使用此类的可视化工具,就需要手动执行``docker-compose up -d``
|
||||
|
||||
成功后,可查看容器的运行以及日志,日志中会显示注册成功的信息,如下图所示:
|
||||
|
||||

|
||||
|
||||
然后我们回到你获取runner令牌的页面,进行刷新就能看到你的runner了。
|
||||
86
src/pages/zh/blog/posts/javascript-map-parseint-trap.md
Normal file
86
src/pages/zh/blog/posts/javascript-map-parseint-trap.md
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
layout: "@/layouts/BlogPostLayout.astro"
|
||||
title: "JavaScript中map遍历数字的陷阱"
|
||||
description: "本文详细解析了JavaScript中[\"1\",\"2\",\"3\"].map(parseInt)返回[1, NaN, NaN]的原因,通过分析map和parseInt方法的工作原理,揭示了这个常见的JavaScript陷阱。"
|
||||
date: "2023-09-15"
|
||||
image: "https://images.unsplash.com/photo-1579468118864-1b9ea3c0db4a?q=80&w=1470&auto=format&fit=crop"
|
||||
tags: ["JavaScript", "函数式编程", "陷阱"]
|
||||
tagId: ["javascript", "functional-programming", "trap"]
|
||||
category: "前端开发"
|
||||
categoryId: "frontend"
|
||||
readTime: "3 min read"
|
||||
---
|
||||
|
||||
在JavaScript中,有一个经典的问题:
|
||||
|
||||
```javascript
|
||||
["1","2","3"].map(parseInt)
|
||||
```
|
||||
|
||||
|
||||
这段代码的返回值是什么?很多人可能会认为是`[1, 2, 3]`,但实际上是`[1, NaN, NaN]`。为什么会这样呢?让我们一起来分析一下。
|
||||
|
||||
### map方法的工作原理
|
||||
|
||||
首先,我们需要了解`map`方法的工作原理。`map`方法会为数组中的每个元素调用一次提供的函数,并使用函数的返回值创建一个新数组。
|
||||
|
||||
`map`方法的回调函数接受三个参数:
|
||||
|
||||
1. `currentValue`:当前正在处理的元素
|
||||
2. `index`:当前元素的索引
|
||||
3. `array`:调用`map`方法的数组
|
||||
|
||||
所以,当我们执行`["1","2","3"].map(parseInt)`时,实际上是在执行:
|
||||
|
||||
```javascript
|
||||
["1","2","3"].map((item, index, array) => parseInt(item, index, array))
|
||||
```
|
||||
|
||||
|
||||
### parseInt方法的工作原理
|
||||
|
||||
接下来,我们需要了解`parseInt`方法的工作原理。`parseInt`方法解析一个字符串参数,并返回一个指定基数的整数。
|
||||
|
||||
`parseInt`方法接受两个参数:
|
||||
|
||||
1. `string`:要被解析的值。如果参数不是一个字符串,则将其转换为字符串。字符串开头的空白符将会被忽略。
|
||||
2. `radix`:一个介于2和36之间的整数,表示上述字符串的基数。例如,指定10表示使用十进制数值系统。始终指定此参数可以消除阅读该代码时的困惑并且保证转换结果可预测。当未指定基数时,不同的实现会产生不同的结果,通常将值默认为10。
|
||||
|
||||
### 分析问题
|
||||
|
||||
现在,让我们分析一下`["1","2","3"].map(parseInt)`的执行过程:
|
||||
|
||||
1. 对于第一个元素"1",`map`方法调用`parseInt("1", 0, ["1","2","3"])`。由于`radix`为0,`parseInt`会根据字符串的前缀来决定基数。如果字符串不以"0x"或"0"开头,则基数为10。所以,`parseInt("1", 0)`返回1。
|
||||
|
||||
2. 对于第二个元素"2",`map`方法调用`parseInt("2", 1, ["1","2","3"])`。由于`radix`为1,而有效的`radix`范围是2-36,所以`parseInt("2", 1)`返回`NaN`。
|
||||
|
||||
3. 对于第三个元素"3",`map`方法调用`parseInt("3", 2, ["1","2","3"])`。由于`radix`为2,表示二进制,而在二进制中只有0和1,所以`parseInt("3", 2)`返回`NaN`。
|
||||
|
||||
因此,`["1","2","3"].map(parseInt)`的返回值是`[1, NaN, NaN]`。
|
||||
|
||||
### 如何正确使用
|
||||
|
||||
如果我们想要将字符串数组转换为数字数组,可以使用以下方法:
|
||||
|
||||
```javascript
|
||||
["1","2","3"].map(Number) // [1, 2, 3]
|
||||
```
|
||||
|
||||
或者使用箭头函数:
|
||||
|
||||
```javascript
|
||||
["1","2","3"].map(item => parseInt(item)) // [1, 2, 3]
|
||||
```
|
||||
|
||||
或者使用`parseInt`的绑定函数:
|
||||
|
||||
```javascript
|
||||
["1","2","3"].map(parseInt.bind(null)) // [1, 2, 3]
|
||||
```
|
||||
|
||||
|
||||
### 总结
|
||||
|
||||
这个问题揭示了JavaScript中函数式编程的一个常见陷阱。当我们使用高阶函数如`map`时,需要注意回调函数的参数和行为。在这个例子中,`parseInt`函数接受的参数与`map`方法提供的参数不完全匹配,导致了意外的结果。
|
||||
|
||||
理解这个问题有助于我们更好地理解JavaScript中的函数式编程和类型转换。
|
||||
@@ -2,6 +2,11 @@
|
||||
@import "tw-animate-css";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@theme {
|
||||
--typography-code-before-content: "";
|
||||
--typography-code-after-content: "";
|
||||
}
|
||||
|
||||
/* Blog List Component Styles */
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
|
||||
Reference in New Issue
Block a user