0%

CSS 层叠样式表作为前端三剑客之一,通过各类选择器来解耦 HTML 结构与表现,让开发者拥有专注控制样式的能力,实现了关注点分离。通过层叠机制,为规则赋予不同的重要程度,让我们的样式代码能够灵活地继承与覆盖。

它就像精灵宝可梦里的百变怪 👾,拥有强大而奇妙的变化与适应能力,前端技术也因 CSS 的加入而变得漂亮与有趣 🤩

本文主要分享 CSS 技术中的一个切面概念:渐进增强,希望能够从原理+实例出发,给大家在设计、编写网页样式时带来一点启发 💡

渐进增强

随着 HTML 与 CSS 的发展,许多新特性陆续得到浏览器厂商们的支持,比如 HTML5 中的新增标签,CSS 中的 Flexbox,Grid、calc 等,给网页带来了很多新鲜而强大的能力。
但是由于时代推移、浏览器的厂商各自对规范实现的不一致,导致不同的浏览器以及版本存在新特性不兼容或者其他 Bug 问题,让开发者在新特性与兼容性之间放弃了尝试新特性的想法 🤪

但是!时代与技术一定是在进步的,而在新老交替的过渡期,我们可以采用渐进增强的策略,来平衡向后兼容性与最新的 HTML/CSS 特性。

什么是渐进增强 🧐

⛳️ 所谓渐进增强,即我们首先为最小公分母准备可用的内容,然后再为支持新特性的浏览器添加更多交互优化。

要实现渐进增强,意味着代码要分层,每一层增强代码都只会在相应特性被支持或被认为适当的情况下使用。听起来有点复杂,然而实际上 HTML 与 CSS 已经部分内置了这一策略。

HTML 的渐进增强策略

⛳️ 对于 HTML 而言,浏览器遇到未知元素或属性时并不会报错,也不会对页面产生什么影响。

假设表单中有输入邮件的控件:

1
<input type="text" name="field-email" />

我们就可以使用 HTML5 新增的 email 类型,可以拥有邮件格式检验以及移动设备中的特定格式软键盘的强化能力:

1
<input type="email" name="field-email" />

尚未实现这个新类型的浏览器就会想:”这是甚么玩意儿?不明白“🧐,然后给他退化成 type=”text”类型,就当无事发生。
这样,我们既渐进增强了页面,也不会对浏览器产生什么不好的影响。
类似的还有 HTML5 的文档声明,这样的语法也是向后兼容的。

CSS 的渐进增强策略

⛳️ 对于 CSS 来说,无法识别的属性/值都会被浏览器丢弃,所以只要提供合理的后备声明,使用新属性就不会带来不良后果。

例如,现代浏览器支持的颜色值 rgba 函数,我们可以这样定义红色:

1
2
3
4
.overlay {
background: #000;
background-color: rgba(0, 0, 0, 0.8);
}

对于旧的浏览器,它会丢 rgba 的声明,应用第一条规则;对于现代浏览器,第二条规则就会覆盖第一条,显示带透明度的红色。
那么即使不是所有浏览器都支持 rgba 函数,由于提供了合理后备,我们仍然可以大胆地使用它 😎

如何实现渐进增强 🧐

1. 厂商前缀

🛠 浏览器厂商也基于同样的原理为自家浏览器引入实验特性,并加上一串特殊前缀,这样其自家浏览器就能识别而其他浏览器忽略。
例如 transform 属性:

1
2
3
4
5
6
.thing {
-webkit-transform: translate(0, 10px);
-moz-transform: translate(0, 10px);
-ms-transform: translate(0, 10px);
transform: translate(0, 10px);
}

下面列举前缀对应的浏览器厂商:

-webkit-: 适用于 Webkit 内核的浏览器,包括 Blink 引擎的(基于 Webkit)

  • Safari
  • Chrome
  • Opera

-moz-: 基于 Mozilla 浏览器

  • Firefox

-ms-: 微软家

  • Internet Explorer

2. 条件规则与检测脚本

@supports
⚙️ 如果希望通过检测浏览器是否支持某个 CSS 特性来提供样式,可通过条件规则检测:

1
2
3
@supports (display: grid) {
/* 编写网格布局的规则 */
}

3. JavaScript 库

⚙️ 通过 Modernizr 这个 JS 库可以检测各种特性的支持情况。
原理是 modernizr 会根据检测情况给 html 根元素添加相应特性的类名,如 flexbox,然后在编写 CSS 时带上前缀,即可安全的增强样式。

更多关于新特性的浏览器支持情况,可以到 http://caniuse.com 查阅 🔎

🌰 例子 1:在浮动之上应用 Flex

这里有一个使用 float 布局实现的卡片布局,我们来看看如何在不破坏原有代码的情况下,用 Flexbox 特性增强这个页面。

这个页面的使用了 float 来实现网格布局效果,主要原理是

  • 💡 使用 col 类名定义浮动组件,row 类名定义清除浮动的父组件
  • 💡 加上 row-quartet 定义子项列宽,实现网格布局效果
  • 核心代码如下:
1
2
3
4
5
6
<div class="row row-quartet">
<div class="col"><article></article></div>
<div class="col"><article></article></div>
<div class="col"><article></article></div>
<div class="col"><article></article></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
// index.css/* 行组件 */
.row:after {
content: "";
display: block;
clear: both;
height: 0;
}
.row-quartet > * {
width: 25%;
} /* 列组件 */
.col {
float: left;
}

通过图片可以看到上述代码并未实现列等高效果,然而要实现浮动元素的列等高效果比较麻烦,详情方案参考:https://juejin.cn/post/6844904048278290440#heading-22

而使用 Flexbox 可以轻松实现,原理是 Flex 的等高机制:父容器定义为 Flexbox 容器之后,控制辅轴的属性 align-items 默认为 strech,即自动拉伸子项的高度以填满空间。

那么此时我们想通过 Flexbox 来实现列等高效果,又不破坏已实现的部分,该怎么做呢 🧐
⛳️ 答案是:引入检测机制,在检测块内编写新特性的规则。

上面也提到有两种方式:

  1. 使用 @supports 块检测,但由于此检测语法本身就比较新,所以对于旧浏览器不友好。
1
2
3
@supports (display: flex) {
/* 编写 flexbox 布局的规则 */
}
  1. 使用 Modernizr 库检测,然后使用带.flexbox 前缀的类名来编写增强的规则。

首先我们将 Modernizr 的 script 写在所有 link 的前面(保证检测脚本先加载)。

添加后,Modernizr 检测完毕后,我们的标签上就将支持的特性名字生成 classname,测试浏览器是当前最新的 Chrome 89, 所以可以看到 flexbox 赫然在列。

接下来就基于此类名前缀,编写增强代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// index.css​/_ flexbox 增强代码 _/
.flexbox .row {
display: flex;
}
.flexbox .col {
display: flex;
flex-direction: column;
}
.flexbox .col > _ {
flex: 1;
}
​/_ float 实现相关代码 _/.row:after {
content: "";
display: block;
clear: both;
height: 0;
}
.row-quartet > _ {
width: 25%;
}
.col {
float: left;
}

我们来看下效果,这里用到浏览器测试工具:browserstack ,选择在 Chrome 下打开页面,展示了等高效果,说明应用增强代码:

而在 IE 10 浏览器上就降级为无 flexbox 的效果:

查阅 CanIUse 网站,看到 IE10 只支持带 -ms- 前缀的 flexbox 属性,所以也解释了上述表现。

总结一下 在浮动之上应用 Flex 方案,有几个关键点:

  • 不理解 flex 关键字的浏览器会忽略它。
  • 给标签定义了 display: flex 之后,浏览器会忽略该标签定义的 float,clear 与 display:inline-block 等声明。

举一反三,其他场景我们也可以遵循首先写一个适合任何场景的布局,然后再通过 新特性检测 + 运用 Flexbox / Grid 等新特性来渐进增强我们的网页。

详情代码:CodePen:

🌰 例子 2:响应式图片

响应式 Web 设计出现以后,何时加载合适的图片是前端开发者要面对的问题。很多开发者不管设备、屏幕大小,一律使用相同的图片,这种做法导致小屏幕看大图浪费带宽与内存、大屏幕展示小图看不清楚等问题。而浏览器会对网页进行预处理,图片等资源会在浏览器构建完页面或运行 JavaScript 之前就开始下载,这意味着不可能仅凭脚本就完美解决图片响应式的问题。所以 HTML5 提出了响应式图片的解决方案。

响应式图片(Responsive Image)是指给 HTML5 规范给标签添加的 srcset、sizes 新属性,旨在解决不同的条件下告诉浏览器加载不同的图片。

  • srcset: 哪个是当前图片的可替换源文件,其宽度是多少像素?
  • sizes: 在各个断点中,图片的 CSS 宽度是多少?

Hi,img 标签的新属性

来看个例子 🌰:

1
2
3
4
5
6
7
8
9
<img
src="http://dummyimage.com/300x150"
srcset="
http://dummyimage.com/300x150 300w,
http://dummyimage.com/600x300 600w,
http://dummyimage.com/1200x600 1200w
"
sizes="(max-width: 600px) 300px, (max-width: 1200px) 600px, 1200px"
/>

分析一下:

  • srcset 的值是一组图片 URL+实际像素宽度(不是 CSS 像素),定义了一组资源后,还要告诉浏览器怎么使用这些图片。
  • sizes 通过开头可选的媒体查询条件 + 图片展示宽度值(CSS 单位,可为 px, em, vw)。

如果某条媒体查询条件为真,浏览器将取得条件后面定义的宽度,然后去匹配到最接近尺寸的图片,执行下载。最后,图片将按照定义的宽度进行展示。

在 Chrome 上运行的效果:



需要注意的点:

  • 在有图片缓存的情况下,浏览器可能会加载较大的图片。
  • 在网速慢或者电量低的情况下,浏览器会加载较小的图片。
  • 浏览器知道当前设备是不是高分辨率屏幕,从而决定是否自动加载大图。

通过 srcset,我们渐进增强了原有的图片组件,让不同宽度的屏幕加载更合适尺寸的图片。

进一步加强:picture 标签

MDN 介绍:picture 元素除了在多个不同分辨率的图片间切换,还有几个很重要的响应式图片的应用场景:

响应式图片在大小屏幕分别需要不同的裁切方式,根据浏览器的支持加载不同格式的图片。如 Google 的 Webp 格式会比 JPG 体积小 25%-34%,可以优化网页的性能。

这些问题的标准解决方案是:picture 元素,他作为 img 元素的容器,同时扩展了 srcset 和 sizes 的能力。举个例子:

1
2
3
4
5
6
<picture>
<source
type="image/webp"
srcset="https://www.gstatic.com/webp/gallery/4.sm.webp" />
<img src="https://www.gstatic.com/webp/gallery/4.sm.jpg" alt="tree"
/></picture>

picture 元素包含 source 标签与 img 标签,在支持 picture 与 webp 格式的浏览器下,就会加载 webp 格式的图片;否则忽略将 picture 与 source 元素,应用 img 标签中的 jpg 格式图片。以下是在 browerstack 的测试情况:图一在 IE11 上测试,IE11 不支持 webp 图片,所以加载 jpg。

Chrome85 支持 webp,加载 webp 图片

除此之外,picture 还可以添加 media 属性,结合媒体查询进一步控制浏览器选择图片的逻辑:

1
2
3
4
5
<picture>
<source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg" />
<source media="(min-width: 800px)" srcset="elva-800w.jpg" />
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva"
/></picture>

对于开发者来说,我们在不破坏 img 原有能力的基础上,大大增加了操控图片的可能性,也得感谢 HTML 的渐进增强策略。

总结一下

在前端新标准、新技术百花齐放的时代,我们应该积极关注、拥抱新技术,用渐进、可降级的方式去为旧项目赋能焕新 ✅ 其实渐进增强,在我看来是一种严谨、巧妙的思维方式,感受其中的分层、容错、可扩展等设计理念,并尝试运用在日后的编程中吧 🤓

辅助工具

这里就列举几个辅助工具,有助于我们写出有良好兼容性的样式代码:

  • PostCSS 预处理器插件 - Autoprefixer, 根据 CanIUse 网站与你项目所支持的浏览器,自动为你的 CSS 代码添加相应前缀。
  • 静态分析及 Linter- Stylelint,能检查语法错误,也能检查选择符/声明中的有问题的规则。
  • 浏览器测试工具:Browserstack,是一款基于 Web 的实时浏览器测试工具,支持虚拟机测试全平台&浏览器版本,收费 💰
  • HTML/CSS/JS 兼容性查询平台:CanIUse

相关资源

  • 《精通 CSS 高级 Web 标准解决方案》(第三版)——埃米尔·比约克隆德
  • MDN - Responsive images

定个小目标 ⛳️:实现用 VS Code 编辑默认 vue cli 创建的 vue2 项目,对.vue, .js 等文件有错误检查与代码风格检查(lint)、保存时自动修复(autofix)、按照 Prettier 风格进行格式化。

上网搜索 vscode 的 vue 项目 lint 和格式化,大多文章介绍的是其团队自己一揽子规范,很多规则不通用,还有很多过时、无效的配置也一并贴出来(当前为 2021 年),看得我头都大了 🤯

我只想要一个 vue、eslint、prettier 三者结合的官方默认配置,怎么这么麻烦??
于是我决定痛下决心,分别查询各个官网以寻求最简配置 ⚙️

✅Tips: 只关心配置的朋友,直接拉到最后。

问题的解决思路

经过一轮探索与思考,要想实现上述目标,我们其实要解决三个问题:

  1. 代码检查(Lint),包括错误检查与风格检查
  2. 在 Lint 之后做代码自动修复(autofix)
  3. 使用代码格式化工具(formatter)去做 autofix

具体到 VS Code 这款编辑器的配置,我们需要搞定以下概念以及解决方案:

  • 概念
    • Linter 与 Formatter 的区别?
  • VS Code 相关
    • VS Code 如何识别 Vue SFC(.vue 单文件) 文件?
    • VS Code 如何识别项目的 eslint 配置,并在编辑器中提示错误?
    • VS Code 如何在保存时自动修复 eslint 错误?
    • VS Code 如何通过 Prettier 这款格式化工具,来自动修复 eslint 错误?
    • 如何解决 ESLint、Prettier 之间的规则冲突?
  • 项目配置相关
    • vue cli 创建的默认 eslint 配置到底包含了哪些规则?
    • 如何设置 Eslint 的代码错误规则?
    • 如何设置 Eslint 的代码风格 Prettier 的规则?

Linter 与 Formatter 的区别?

我们首先讲下什么是 Linter:
Linter 是语法检查/代码质量检查工具,不同语言都有自己的版本,比如 JavaScript 的 Linter 有 ESLint,JSLint,Python 有 pylint…而 JS 中目前最常用的就是 ESLint,通过配置规则+ESLint 的命令行工具(CLI),可以实现代码检错、风格检错、自动修复错误等能力。

然后是 Formatter:
Formatter 是指代码格式化工具,这类工具会帮你把代码按照规则进行统一的格式化,保证项目代码的美观、统一。前端有代表性的 Formatter 有 Prettier、Beautify 等,他们都可以规范 HTML、CSS、JS 等前端语言。

那么 Linter 与 Formatter 有什么区别呢?参考 Prettier 官网的解释:Prettier vs. Linters · Prettier,我们可以得知 Linter 主要有两类配置:

  • 代码格式化相关(Formatting rules) :
    • eg: max-len, no-mixed-spaces-and-tabs, keyword-spacing, comma-style…
  • 代码质量相关(Code-quality rules):
    • eg no-unused-vars, no-extra-bind, no-implicit-globals, prefer-promise-reject-errors…
      而 Formatter 如 Prettier,只关心代码格式化的规则,完全不关心代码质量的规则。

所以我们应该发挥各自所长,在项目中:

  • 使用 Linter 来做代码质量检查
  • 在 Linter 中配置 Formatter 的规则,使用 Linter 来做代码风格检查
  • 使用 Formatter 来做代码的格式化

💪 实战:从零开始配置

环境说明:

  • Mac OS 10.15.7
  • vs code 1.50.1
  • @vue/cli 4.5.12
  • 有 eslint 配置的 vue2 项目

初始化 vue2 项目

场景是这样的,通过 @vue/cli 创建一个默认的 vue2 项目,包括 eslint、babel 都是最基础的。
在这里插入图片描述创建之后,用没装什么插件的 vs code 打开项目:
在这里插入图片描述

安装必要 vs code 插件

看到.vue 后缀的文件默认是没有语法高亮的,我们需要安装 vetur 插件来支持

安装完之后,vscode 即支持 vue SFC 文件的语法高亮:
在这里插入图片描述
什么??居然没有错误提示,明明 vue/cli 默认帮我们创建了 eslint 配置的!

代码质量错误检测

其实这里要安装 vs code 的插件 ESLint,他能读取项目中的 eslint 配置,并告诉 vs code,vs code 才可以在编辑器中找到错误并提示出来!
在这里插入图片描述
安装完之后,回到App.vue,可以看到错误的代码有红色波浪线提示,鼠标悬浮还有具体错误原因,eslint(no-undef),这里说明 vscode 已经可读取项目的 eslint 配置,并按照 no-undef(不允许存在未定义的变量)这条规则定位错误!注意,这是属于代码质量类错误
在这里插入图片描述
对于 vue 的 template,代码质量类错误也是可以检测出来:
在这里插入图片描述
可以看到这里错误规则来源于eslint-plugin-vue,这是因为 vue cli 创建的 eslint 规则中,包含了两条继承的规则:

  • eslint:recommended: 负责检查 JS 错误(eslint),来自 eslint

  • plugin:vue/essential:负责检查 vue 的错误(eslint-plugin-vue),来自 eslint-plugin-vue
    在这里插入图片描述

代码风格类错误检测

接下来我们测试下 代码风格类错误 ,在 template 和 script 中都打上多余的空格:
在这里插入图片描述
好家伙,这么丑的代码都不报错,还有没有王法了?!为什么呢,我们不是有配置 eslint 规则吗?怀疑到了我们的两条规则:eslint:recommendedplugin:vue/essential,查阅资料发现:

List of available rules - ESLint - Pluggable JavaScript linter 告诉我们:eslint:recommended 只包含了必要的代码质量规则,对于风格规则,完全不在意。

User Guide | eslint-plugin-vue告诉我们:plugin:vue/essential只包含了 vue 在 ESLint 的解析、以及 vue 错误的检查,对于风格规则也是完全不 care。如果我们想加上 vue 的代码风格规范,应该使用 “plugin:vue/recommended” 规则集。
我们只需改一下 eslint 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
+ "plugin:vue/recommended"
- "plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},

再回到编辑器,可以发现 template 的代码风格校验告警了,而 script 中仍然没有提示。
在这里插入图片描述
其实也很容易想到,当前 eslint 的规则集里缺少 JS 的风格校验规则,是 Prettier 登场的时候了! 我们希望 eslint 里能写入 prettier 的规则,这里需要在项目里安装 eslint-plugin-prettier-vue NPM 依赖,根据其 NPM 文档指引,我们在项目里安装:

1
2
3
4
5
6
npm install --save-dev \
eslint-plugin-prettier-vue \
eslint-plugin-vue \
eslint-config-prettier \
eslint \
prettier
  • prettier: 是 prettier 的核心包
  • eslint: 是 eslint 的核心包
  • eslint-plugin-vue:是 vue 的 eslint 插件,包含多个规则集
  • eslint-plugin-prettier-vue:是 prettier fo vue 的 eslint 插件,包含 prettier 风格检测规则集
  • eslint-config-prettier:禁用 linter 与 prettier 之间会产生冲突的部分规则,保证 Prettier 与 ESLint 不冲突

安装完后我们再为 ESLint 添加风格校验规则集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/recommended",
+ "plugin:prettier-vue/recommended",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},

再回到编辑器,已经可以检测 template, script, style 所有的风格错误了!
在这里插入图片描述

开启 ESLint Autofix

检测出错误,我们该修复错误了,我们可以手动修复(太笨),也可以命令行执行 eslint --fix(太麻烦),有没有更舒服地方式,有,在保存文件时自动修复风格错误!

要开启此特性,我们在 vscode 中,打开 偏好设置preference,点击切换到 JSON 配置文件模式:
在这里插入图片描述
在配置中添加:

1
2
3
4
// 开启代码保存时,eslint执行fix动作
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},

保存后回到编辑器,按ctrl+s保存,可以发现代码根据prettier的规则,一口气格式化好了!
在这里插入图片描述
至此,ESLint 将怀揣 vue 与 js 的质量规则+prettier for vue 的风格规则,帮我们检测代码异常,并自动修复风格问题 🤩

正式宣布,我们完成了在 vscode 下,编辑有 eslint 配置 的 vue2 项目,拥有代码质量检测+代码风格检测以及自动根据 prettier 规则修复的目标了!

其他说明

autofix 的范围

能自动修复的只有代码风格类错误(缩进,换行),代码质量类是没法自动修复的(你当他 AI 吗?自动帮你修 bug)

在这里插入图片描述
如上图,输入 aaa 检测出一条质量问题(no-undef),一条风格问题(缺少;号),点击保存,只会帮你把分号加上 😥
在这里插入图片描述

关于 prettier 风格覆盖

在 eslint 配置中的 rules 字段配即可(更多字段查询https://eslint.org/docs/rules/),例如想覆盖 prettier 的双引号规则为单引号:

1
2
3
4
5
6
7
8
9
10
rules: {
'prettier-vue/prettier': [
'error', // 不符合规则的设为错误,好让eslint修复
{
// Override all options of `prettier` here
// @see https://prettier.io/docs/en/options.html
singleQuote: true,
},
],
},

疑问总结

  • VS Code 相关

    • VS Code 如何识别 Vue SFC(.vue 单文件) 文件?
      • 通过 vetur 插件
    • VS Code 如何识别项目的 eslint 配置,并在编辑器中提示错误?
      • 通过 Eslint 插件
    • VS Code 如何在保存时自动修复 eslint 错误?
      • 偏好配置中开启 :editor.codeActionsOnSave -> "source.fixAll.eslint": true
    • VS Code 如何通过 Prettier 这款格式化工具,来自动修复 eslint 错误?
      • 项目安装 prettier, eslint, eslint-plugin-prettier-vue, eslint-config-prettier
      • eslint 配置中继承 plugin:prettier-vue/recommended 规则
    • 如何解决 ESLint、Prettier 之间的规则冲突?
      • 安装 eslint-config-prettier 依赖,禁用冲突的规则,需要搭配其他包使用,如 eslint-plugin-prettier-vue
  • 项目配置相关

    • vue cli 创建的默认 eslint 配置到底包含了哪些规则?

      • eslint:recommended: 负责检查 JS 错误(eslint),不含风格检测,来自 eslint

      • plugin:vue/essential:负责检查 vue 的必要错误(eslint-plugin-vue),不含风格检测,来自 eslint-plugin-vue

    • 如何设置 Eslint 的代码错误规则?

      • 继承已有的规则集,或通过 rules 手动配置
    • 如何设置 Eslint 的代码风格 Prettier 的规则?

      • 通过plugin:prettier-vue/recommended继承 Prettier 的规则集

配置总结

VS Code 插件:

  • Vetur: 让 vscode 识别 vue SFC 文件,语法高亮、代码检测等
  • ESLint 让 vscode 读取项目 eslint 配置,并在编辑器中提示语法错误、应用 autofix 等

NPM 开发依赖:

  • prettier: 是 prettier 的核心包
  • eslint: 是 eslint 的核心包
  • eslint-plugin-vue:是 vue 的 eslint 插件,包含多个规则集
  • eslint-plugin-prettier-vue:是 prettier fo vue 的 eslint 插件,包含 prettier 风格检测规则集
  • eslint-config-prettier:禁用 linter 与 prettier 之间会产生冲突的部分规则,保证 Prettier 与 ESLint 不冲突

ESLint 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/recommended",
"plugin:prettier-vue/recommended",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {
"prettier-vue/prettier": [
"error",
{
"singleQuote": true
}
]
}
}

VS Code 配置

1
2
3
4
// 开启代码保存时,eslint执行fix动作
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}

以上就是 Vue 项目在 vscode 中 lint/autofix/format 的最简配置 🤓 装备好武器,开始撸代码吧 👨🏻‍💻

参考网站:
Prettier vs. Linters · Prettier: https://prettier.io/docs/en/comparison.html
ESLint rules: https://eslint.org/docs/rules/
eslint-plugin-vue: https://eslint.vuejs.org/user-guide/#usage
eslint-plugin-prettier-vue: https://www.npmjs.com/package/eslint-plugin-prettier-vue

读到博文Hexo常用插件介绍 hexo-symbols-count-time, 发现hexo-symbols-count-time这个好用的插件,能自动统计每篇博客的字数计算估计阅读时间 ,赶紧下载下来玩玩。

1
$ npm install hexo-word-counter

_config.yml 中配置

1
2
3
4
5
6
7
8
9
symbols_count_time:
symbols: true
time: true
total_symbols: true
total_time: true
exclude_codeblock: false
awl: 2
wpm: 300
suffix: "mins."

其中关键参数解释:

  • awl 平均字符长度
    • CN ≈ 2
    • EN ≈ 5
    • RU ≈ 6
  • wpm 每秒阅读的单词数
    • Slow ≈ 200
    • Normal ≈ 275
    • Fast ≈ 350
  • 更多参数

中文的话推荐配置 awl = 2, wpm = 300

在你的Next主题配置文件 _config.next.yml 中配置:

1
2
3
4
5
6
post_meta:
item_text: true

symbols_count_time:
separated_meta: true
item_text_total: false

最后启动博客前记得清除以前的构建数据:

1
2
$ hexo clean
$ npm run server

效果如图:

hexo-plugin-count-time

是不是很有用,继续鼓捣吧!

在网上搜索了一下文章标题,找到一篇博客:在 Next 主题中添加友情链接,这里记录一下步骤,也方便后来者~

找到你的 hexo next 配置文件,我的 next 配置文件是根目录下的 _config.next.yml,搜索 Blog rolls 注释,配置下方的 Links 即可:

1
2
3
4
5
6
7
8
9
10
11
12
# Blog rolls
links_settings:
icon: fa fa-user-friends
title: My friends
# Available values: block | inline
layout: block

# 这里添加友链~
links:
Airing的小屋: https://me.ursb.me/
Hedwig: https://hedwig.pub/
Wall-E Paradise: https://www.qirencloud.com/

效果如图:

hexo-add-friend-link.png

快去添加你的好友吧~

Ayy yo what’s up,这里是Brrruski aka. 搞程序的Gatsby👨🏻‍💻

作为第一篇正式对外的文章,想了很久要分享什么主题才会比较有意思,还要易上手,还要接地气🧐 那最近也是看到朋友的博客(基于Hexo搭建的),眼馋里面的markdown代码块、时间线timeline整理以及自动分类与标签词云呀🤩 (天知道我作为程序员是怎么忍受wordpress / ghost默认的markdown支持的🐶

于是我兴致勃勃地鼓捣了一番Hexo博客,在本地已经装饰的漂漂亮亮了✨ 。到了该部署的环节,我一拍脑袋💡,不如摒弃我的小水管server🚰,玩一次地道的云原生部署玩法吧?

经过一早上的踩坑,终于在云上建好属于自己的一亩三分地了,简直比在深圳买了房子装修完还开心呢(醒醒,你哪来的房子

所以我决定,不如就分享一下我是怎么把我的Hexo博客拎到云上去的吧☁️

网站托管(Serving)

让自己的网站能被大家访问到,我们需要进行名为网站托管的一系列操作。我们先简单回答一下关于上网冲浪🏄‍♂️ 的两个灵魂发问:

  1. 🧐 网页的本质是什么?
  2. 👨🏻‍💻 我们为什么能在浏览器上搜到并看到网页?

1. 网页的本质是什么?

网页的本质其实就是一堆按格式书写的字符,即我们常说的HTML(超文本标记语言),文本的内容大概长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!doctype html>
<html>
<head>
<title>Bruski's Website</title>
</head>
<body>
<h1>My Intro</h1>
<p>Yo whatsup, this is Brrruski aka Coding-Gatsby</p>
<img src="/image/handsome-selfie.jpg" />
<audio src="/audio/handome-bgm.jpg" autoplay />
<video src="/video/me-skating.mp4" controls />
</body>
</html>

看到里面的标签了吗,我们通过书写这类浏览器能识别的标签,来创建内容、引用其他资源,经过浏览器处理后渲染到屏幕上,就变成我们看到的色彩缤纷、能够交互的网页啦。

2. 我们为什么能在浏览器上搜索并看到网页?

设想我们在网上买衣服,我们先按名字搜到某个牌子的衣服,如果找到了提供该衣服的商铺,购买下单,商家处理好之后发货,不久后你能穿上心仪的衣服啦。

同理,当我们在浏览器的地址栏中输入某个网址的时候,浏览器会发出寻找该网址对应服务的请求,如果找到了,提供该网站服务的服务器会把相应的网页内容返回给浏览器,浏览器解析后,网页内容就呈现在我们眼前了🤩

所以一个网页要想能被别人访问到,需要具备满足以下条件:

  1. 一个能找到你网页的地址(IP+端口或者域名)
  2. 一个能处理浏览器的请求,把资源返回去的服务(HTTP Web服务)

所以网站托管做的事情就是:

  1. 📦 把网页等资源上传到某个地方。
  2. 🤖 启动一个能对外提供服务的HTTP Web服务,把我们的网页内容发送给请求方。
  3. ⛳️ 设置这个服务的访问地址,可以是IP+端口,也可以起别名(域名)。

网站托管的方式

通过提供HTTP Web服务进行网站托管的方式大致可以分为:

  1. 借助能处理静态资源的Web Server,如Apache,Nginx。
  2. 由Web Server动态生成HTML内容,如JSP。

由于我们今天的主题是博客托管,我们只讨论第一种,只提供静态资源的方式。

云原生(Cloud Native)

cloud native

首先我们简单快速了解一下云原生这个概念,这里引用了掘金的文章《什么是云原生?这回终于有人讲明白了》——华为云开发者社区

云原生是一种基于云计算技术的构建和运行应用程序的方法,是一套技术体系和方法论。

云原生(CloudNative)是一个组合词,Cloud+Native。

  • Cloud表示应用程序位于云中,而不是传统的数据中心;
  • Native表示应用程序从设计之初即考虑到云的环境;

Pivotal公司的Matt Stine于2013年首次提出云原生(CloudNative)的概念,对云原生的定义总结为4个要点 DevOps + 持续交付 + 微服务 + 容器。一句话概括:原生为云而设计,在云上以最佳姿势运行,充分利用和发挥云平台的弹性+分布式优势。

图片来自掘金-华为云开发者社区的文章

其实结合实际理解,云原生已经具象化地存在于各大云服务厂商的官网中:云服务器、云存储、云容器、DevOps流水线等等,他就是一个船新的软件开发和部署的新生态,帮助开发者们更简单、更快地打造、发布与维护产品🔫

传统网站托管 VS 云原生网站托管

传统网站托管 VS 云原生网站托管

传统模式托管和云原生托管最大的不同在于:资源部署的维度不同

传统模式按硬件资源为单位部署,云托管按功能服务为单位部署,两者带来的服务架构设计、实际操作与效果都有着很大的差别。

传统网站托管: 我们需要自己维护服务器,把文件上传到服务器的具体路径,接着设置Web Server啊,安装证书 ¥&!# ,一顿操作之后才能完成网站托管。

云原生托管:文件打包后,上传到对象存储服务,设置一下存储桶为静态网站托管模式,嗯就可以了,什么域名啊、证书啊全部自动生成。什么,你想让你的网站在全国各地的访问速度都更快一点?那再到网页上点击配置一下CDN加速服务,让它将你的网页分发到全国各个边缘节点中,通过统一的加速域名来访问,用户访问速度杠杠的。

由此可见云原生托管不仅简单便捷、灵活按需、省心省钱,而且服务的效果和质量都比传统模式强😎 既然云托管这么香,那我们赶紧进入实操环节体验一把🤨

实战: 把这只Hexo博客拎到云上吧

⛳️ 明确我们的目标:将Hexo博客项目快速地部署,可以通过HTTPS域名访问。

注意:实战的云服务商选择腾讯云,其他云服务商操作同理

前置准备

  • 安装好gitNodeJS与Npm的环境
  • 一个hexo博客工程,并设置好github仓库关联(其他代码托管服务同理)
  • 一个腾讯云账户

Hexo博客工程可以下载示例工程:

1
2
3
4
# Github
git clone https://github.com/bruceeewong/hexo-demo.git
# Gitee码云
git clone https://gitee.com/bruceeewong/hexo-demo.git

安装npm依赖

1
npm install

预览页面效果

1
npm run server  

通过浏览器访问的本机地址 http://localhost:4000,查看效果:

最后测试打包命令是否正常:

1
npm run build

如果在当前目录下新增public目录,说明打包命令可用,就进入下一步。

开通网站托管相关的云资源

我们需要开通以下资源:

开通对象存储服务&上传静态资源

根据指引,为了存储我们的静态资源,我们需要创建一个存储桶资源。

注意存储桶的访问权限我们先设置为 公有读私有写,方便在没有接入CDN服务前直接访问网页。

接下里来两步直接按默认的来,点击创建。


存储桶创建好之后,我们找到文件列表>上传图片按钮,挨个把本地构建好的public下的文件夹&文件上传(好累,这里只是让你体会一下没有自动化工具的辛苦😂


到这里,我们已经把静态资源都传到存储桶中了,接下来就是设置其访问方式。

开启静态网站托管模式

到这里,网站的托管就完成了,是不是不敢相信?我们把访问节点的URL复制到浏览器试试:

怎么样,我们是不是已经完成了定下的目标:将Hexo博客项目快速地部署,并可以通过HTTPS域名访问。

你能做的,岂止如此

复盘一下刚才的操作,最费时的就是手动上传静态文件了(可能还不如 scp 传到服务器快呢)如何摆脱手动上传文件?解决的办法无疑就是将这部分操作自动化,让我们接入Coding CI DevOps服务,创建一条CI/CD流水线,来拉开跟手工部署的差距。

搜索Coding CI服务:

创建项目,这里只勾选 构建流水线 即可:

选择流水线模板 React + COS(我们要的只是对接COS上传的部分)

代码仓库选择 Github 或 码云(需完成授权),选中我们的仓库;关掉的单元测试选项(我们的hexo项目没有此命令)

接下来的上传COS Bucket配置部分参考下图。注意不勾选“创建后触发构建”,还有一些要配置的地方。


修改一下环境变量>产物的路径名(hexo的产物路径叫public

如果选择的是github,触发的分支注意有可能需要设置为 main(不知道微软为啥要改掉master)

最后点击构建,短短23秒流水线就执行完成了。

这意味着我们以后只需编辑与提交代码,构建和部署上传的工作交给流水线去做就好了😆

🚀 最后一步,配置CDN加速服务

CDN内容分发网络的工作方式大致如下,通过CDN服务的接入,把源站的文件分发至各个边缘节点。

为了能让用户能从最近的CDN节点获取资源,我们应该只对外开放CDN域名,隐藏存储桶的访问路径(可以设置为私有读写)

落到腾讯云这,有两种方案:

  • 使用COS提供的默认CDN加速域名
    • 优点:简单快捷,一键生成带ssl证书的域名
    • 缺点:域名太长
  • 配置自定义加速域名
    • 优点:可定制域名
    • 缺点:配置稍微复杂一些,需要前往CDN控制台配置

限于篇幅有限,就只介绍默认CDN加速域名的配置:

鉴于我们的hexo博客是将markdown生成html文件,为了防止因为缓存而导致用户不能看到最新更新的文章,我们还需要设置CDN的缓存配置。

配好默认CDN域名后,把html文件的CDN节点与浏览器缓存都设为 不缓存



最后,我们通过CDN加速域名 https://hexo-demo-1257249827.file.myqcloud.com/ 来访问一下我们的页面:

快速地打开了,没有任何问题 🚀

总结

至此,我们的Hexo博客就已经正式在云上托管了☁️,开罐啤酒庆祝吧,Hooray🍺

我们不仅完成了基础目标:快速地部署,并可以通过HTTPS域名访问,还通过添加devops服务与CDN服务,让我们的开发与访问速度都提升了🚀

其实能折腾的东西还有很多,像我还做了Hexo网站主题定制、自定义加速域名、强制HTTPS转换、SEO优化、ICP+公安备案等,这些操作留给你们去探索吧,我只是那个把你们带上云端的男人😆

今天就写到这了,还有更多有趣的云原生玩法,一边实践再一遍分享吧🔥

作为一个程序员,要是因为网络原因,不能享受全世界程序员的发展成果,那就真的太亏了=。=

比如 github 看到一个很棒的项目,激动地打下git clone,结果…

1
2
Cloning into 'awesome-project'...
fatal: unable to access 'https://github.com/god/awesome-project.git/': LibreSSL SSL_read: SSL_ERROR_SYSCALL, errno 60

由于网络连不通,又一位少年/少女失去了编程梦想,可惜!可惜!

本文就以作者实际探索,介绍 mac 设置命令行代理的 2 种方案:手动设置变量 与 使用proxychains工具 ,希望能帮到正在阅读本文的你,一起起飞 🚀

✅ 前置准备

首先,你的 mac 得有一个 socks 代理工具,以及可用的 socks 代理服务

本文重点不是这里,but sadly,这里又是全流程的重点,得靠你自己探索了,少年。

作者的 mac 有 socks 代理工具,它自动运行着两种协议的代理端口:socks5 代理端口与配套的 http 代理端口,都可用作请求的代理转发:

1
2
socks5 127.0.0.1 1086
http 127.0.0.1 1087

如果你的 mac 也满足条件,那么继续往下看吧 🤓

🚀 方案一:直接设置终端代理环境变量

这是最简便的方案,无需安装任何工具

每个终端会话都可以设置 $http_proxy$https_proxy环境变量,来控制终端的 http 请求代理。

首先保证当前终端会话没有设置代理:

image.png

我们来试下拉取一个别人分享的 mysql 命令行 gist(即文本笔记 📒),保存为 mysql_gist 文件。

链接:https://gist.github.com/hofmannsven/9164408

1
curl -o mysql_gist https://gist.github.com/hofmannsven/9164408

image.png

嗯,Connection refused,早已被拒绝习惯了 😢

擦干眼泪,我们接下来在命令行输入代理配置(=号左右不要有空格):

1
export http_proxy=http://127.0.0.1:1087; export https_proxy=http://127.0.0.1:1087;

我们再次检查 http 环境变量:

image.png

Yes,生效了,那再下载一遍试试看?

image.png

芜湖!成功把 github gist 下的配置下载下来了!不再是绝望的 Connection refused 或者 3kb/s 了 🚀

别急,还没完

上边的设置只是针对当前会话,如果关掉当前终端再开一个,悲剧依旧会重演 💔

我们可以把配置命令作为别名(alias),这里就叫 proxy 吧,写到.bash_profile里吧,方便以后取用:

1
alias proxy="export http_proxy=http://127.0.0.1:1087;export https_proxy=http://127.0.0.1:1087;"

那么以后在命令行,如果遇到下载不通的情况,可以在原始命令前加上 proxy 即可:

1
proxy curl -o mysql_gist https://gist.github.com/hofmannsven/9164408

image.png

Nicely done!

不建议在 bash_profile 中写死 export http_proxy=xxx,一是显示声明总比隐式声明好,二是节省你代理服务的流量,我们大多数场景使用直连就行了。

取消代理

当 export 了之后,当前终端会话的 http 请求都会走代理,如果要取消,需要手动输入:

1
unset http_proxy https_proxy

这样的方式可行,但还不够方便,不能按需使用,比如我只想某一条命令的请求走代理,而不是全局都走,我们把这个问题留在下面的方案解决吧。

方案总结

优点:

  1. 简单、无依赖:只需设置环境变量http_proxyhttps_proxy,即可为当前终端会话的 http 请求设置代理啦!

缺点:

  1. 无法做到按需使用:设置了代理后,当前会话之后的所有 http 请求都会走代理,如要取消需要手动 unset http_proxyhttps_proxy

🚀 方案二:使用 proxychains 工具

参考文章:

📚 Mac OSX 使用 proxychains-ng

📚 故事:试图不关闭 SIP 在 macOS Sierra 上使用 proxychains-ng

proxychains 是一个 UNIX 程序,可以帮助我们代理终端发出的请求,支持 http, https, socks4, socks5 的代理类型。

ProxyChains is a UNIX program, that hooks network-related libc functions in dynamically linked programs via a preloaded DLL and redirects the connections through SOCKS4a/5 or HTTP proxies.

📦 安装

macOS 可以通过 brew 来安装,不过巨慢(一个怪圈:慢是因为没配好终端代理,而配置终端代理又要到外网下载工具 🤪

1
brew install proxychains-ng

还可以尝试源码安装,这里参考 Mac OSX 使用 proxychains-ng 的安装步骤,我没试验过.

1
2
3
4
5
6
7
$ git clone https://github.com/rofl0r/proxychains-ng

$ cd proxychains-ng
$ ./configure --prefix=/usr --sysconfdir=/etc
$ make
$ make install
$ sudo make install-config # 安装proxychains.conf配置文件

注:mac 上 make install 会报错。因为 Mac 下用 Homebrew 安装的默认为/usr/local/etc/proxychains.conf

解决方法:

1
2
3
4
5
6
7
8
cd configure
vi config.mak
将:
bindir = /usr/bin
libdir = /usr/lib
修改为:
bindir=/usr/local/bin
libdir=/usr/local/lib

🔖 配置 proxychains

vim 打开 /usr/local/etc/proxychains.conf 配置文件:

在[ProxyList]下添加 socks5 代理 (115 行,vim 快捷跳转 :115回车 )

1
2
3
4
5
# 代理端口一定要和shadowsocks中的保持一致
# 如果有不明白的可以查看93110
[ProxyList]
socks5 127.0.0.1 1086 # 配socks5
# http 127.0.0.1 1087 # 也可以配为http

具体代理类型配了 socks5 和 http 有什么性能上的不同,我不清楚,有知道的大神可以在评论科普一下

🎼 使用前的小插曲

如果不是 macOS 10.11 或更新,则跳过本节 🤪

Mac OSX 使用 proxychains-ng 提到:

macOS 10.11 后下由于开启了 SIP(System Integrity Protection) 会导致命令行下 proxychains-ng 代理的模式失效,如果使用 proxychains-ng 这种简单的方法,就需要先关闭 SIP。

想想苹果设置这个策略肯定是有安全考虑,有没有更优雅地解决方案,网上冲浪找到这篇文章 故事:试图不关闭 SIP 在 macOS Sierra 上使用 proxychains-ng ,他提到 SIP 策略的细节:

根据苹果的 官方说明,以下路径受到保护:

  • /System
  • /usr (不包含 /usr/local)
  • /bin
  • /sbin
  • Apps that are pre-installed with OS X

于是解决方案是,将工具如 curlssh 的二进制文件拷贝到非受保护路径:

我们在当前用户目录下创建自定义目录:

1
mkdir -p ~/local/bin

~/.bash_profile中加入 PATH:

1
export PATH=/Users/xxx/local/bin:$PATH

接着拷贝要用的工具即可

1
2
cp $(which ssh) ~/local/bin/ssh  # 拷贝 ssh
cp $(which curl) ~/local/bin/curl # 拷贝 curl

🚀 使用 proxychains

使用方法巨简单,在原始命令前加上 proxychains4 即可:

1
proxychains4 curl -o mysql_gist https://gist.github.com/hofmannsven/9164408

image.png

成功,并且不污染全局代理配置,舒服。

同样我们为 proxychains 配置 alias,就可以简便地使用了!

1
2
3
# ~/.bash_profile

alias pc='proxychains4'

方案总结

优点:

  1. 使用简便,直接在原始命令前加 proxychains4 前缀即可
  2. 支持多种代理的类型
  3. 按需使用,不污染全局代理配置

缺点:

  1. 安装可能受阻,配置相对麻烦
  2. macOS 10.11 之后的版本需要用小聪明绕考 SIP 策略。

🍳 总结

两种方案都是作者亲测可行,如果有疑问可以下方留言,或者 📧 邮件我 bruskiwang@outlook.com

已经没有什么能阻碍你了,享受全世界的开源代码吧 🚀

明天就是儿童节咯,写篇五月总结作为自己的儿童节礼物吧~如果要给五月设一个关键词的话,我想大概是:浮躁地成长着。
2021 年还剩 1 个月就要过去一半了,虽然没有虚度光阴,但总觉冲劲不足,好多事说到没做到。怎么说呢,就感觉浮躁的心态又卷土重来,不太愿意慢下来做一些事,比如写作,比如反思。在这五月的尾巴,稍微给点时间自己。

五月开头是假期,过得相当开心,先是和 KTV 四少去长沙探店、美食、听说唱音乐节,再是前往武汉——度过我最灿烂的青春的城市,回母校和 KS 的老炮儿们再聚首,跳舞、跑卡丁车、校园电驴瞎逛,借用结扎的话:“好久没感觉这样放肆的开心了”。

去了趟武汉结果得了感冒,回来蓝瘦的雅痞,手头拍摄的长沙武汉旅游的素材一直没剪成 VLOG,过了感冒后,又不愿意花时间在 VLOG 上了…一面羡慕着国外的人该工作工作,该玩玩,多会享受生活;一面自己莫名地不愿把时间花一点在这些看起来浪费时间无意义的生活美好上,多奇怪哦。

五月中又是广州之旅,和弯弯、脑子&他朋友一起听 M_DSK 音乐节,同时打卡融创雪世界,继神农架后第二次滑雪。开着脑子的宝马顶配 530,一路吹 B 一路吃,尝试了单板滑雪,也算是好好陪弯弯玩了一次(话说跟我在一起真的会无聊吧?毕竟我是连自己都不愿意多给享受生活的时间的人)

其实这个月成长上倒是收获蛮多的,比如第一次完成了直播组的活动页+挂件需求,终于能说自己是“直播组”的前端了哈哈哈;比如理财上把微淼前十周的课学完了,初步建立了投资的框架,也把资金都聚拢,真正有计划地去在股票、基金、REITs、债券领域建仓;项目上也是独立完成了许多有意思的尝试:状态机在前端的应用、对 React Hook 的使用和封装有了更深的认识、尝试用数据结构去完成前端的组件能力;滑板上 Ollie 高度突破两立,并顺带练会了 50-50……其实成长还是可喜的。

困扰我的点在于,月初指定的目标是什么,现在已经忘了…并且每个周末的大好时光,我都有点迷茫该做什么,等到定好计划,又效率很低导致只能完成 10-20%…明明觉得时间紧,却把整块时间花在看视频、打游戏上…想努力又觉困乏,说搞个人 IP、小程序,也只是雷声大雨点小,最后不了了之。好讨厌的感觉。

究其原因,我觉得有几点吧:休息不好导致精神不好;心态变得浮躁,什么都想做,什么都做不好;注意力的弦不懂松弛有度,学不好、玩不好;计划不甚清晰,不好落地,甚至忘了自己最初的目标;最后就是,执行力太差了。

与其说一直绷着,什么都想做,不如在六月做减法,只专注不超过三个目标,给自己留时间思考、写作。像明天要上线的摩尔庄园的宣传片:“小时候,快乐很简单;长大后,简单很快乐”,或许我的不快乐,就是因为我把心态弄得太复杂了吧。

晚安,明天起,做一个简单的小孩,儿童节快乐。

今天玩了期待已久的本子《古木吟》,这个本由于出版时太火爆,以至于现在要找到拼车的只能是萌新了,经历几次换本的尴尬后,今天终于排到了,弯弯同学也算是了结一桩心愿 😄

剧透什么的就不搞了,就说说感受,一句话:感人,温润。看完剧本听完语音后,剧情基本已经摊明了,故事串联起来后,情感就如一股暖流,从紧张疲惫的大脑开始温润全身。每个人都会在结尾做评述,当分享完内心感受时,觉得意犹未尽,此本立意的确不错,悬疑设计也是合情合理,没有太跳脱。

我玩的是文弱男,可惜没有亲哥哥的我,没有收到太强的共鸣。弯弯还有其他两个妹子都被触动到,流泪了。

弯弯跟我说,她看到剧中情侣情投而意不能合,联想起生离,联想到我,就有一股触动,当然还有许多她的回忆。是呀,离别于我们,迟早要到来….既然无法避免,那不如享受当下的每一秒相处,不要太多期望也不要留下遗憾,当一对潇洒的都市情侣吧。

另外,游戏结束之时,cxy 给我发消息说,现任介意,于是要删掉我的联系方式。我愣了一下,笑了笑回复,祝好。时间可以把一切冲得很淡,以至于这般离别也变得风轻云淡。那就把这段回忆尘封起来吧。

anyway,这是一篇日记,也不能剧透,那用剧中的话来结尾吧:

“愿你心意坚韧,不被昔时梦魇所扰。愿你未来之路,不受追忆所拌。”

平淡的下午写着代码,突然系统弹窗:”/目录空间不足!!!”,一点 “检查”一看,原来还是自己刚学 linux 那会儿分配的 20G 可怜空间,而如今已经不想打开 windows 了,感叹到,该给 linux 涨涨地位了. 于是买来一张 250GB nvme SSD, 又不想重装系统, 想把系统盘一字节不落的 完美迁移 ,于是乎开始了迁移的折腾之旅~~~

环境说明

本人的环境信息如下:

  • 原 250GB nvme SSD 装了双系统: 200G 给到 win10, 20G 给到 Ubuntu20
  • 8Gx2 = 16G 内存
  • Ubuntu 分区情况
    • /: 根目录 17G 主分区
    • /boot: 引导分区 1G 逻辑分区
    • swap: 交换分区 2G 逻辑分区
    • /home: 存储于机械硬盘,单独拆分出来: 100G

先结论后细节

整趟下来,踩了不少坑,终于把流程和思路理顺了! 期间查阅了很多以前不懂的 linux 知识,也算饶有收获~
在这里插入图片描述

折腾记录

购买 SSD,安装 SSD

此处知识点:

  1. ssd 固态硬盘 sata 的好还是 m.2 好呢?
  2. 一篇文章讲清什么是 NVMe

如今 SSD 价格不算很贵,250GB 的西部数据 nvme 卡在某东上买花了 308+, 不得不说如今的硬件做的都很精致呀, m.2 接口的 SSD 小巧精致, 即插即用. 当然需要一颗专用螺丝固定在主板上 (螺丝是不随卡附赠的噢!) 一般主板支持几个 m.2 插槽就会配几个, 我的是 2 个插槽.

在这里插入图片描述

很快安装完毕,盖上机箱玻璃,嗯,有那种 强化+1 的满足感,哈哈哈.

制作 Ubuntu 启动 U 盘, 进入 live 环境

制作启动盘的原因是,我们需要一个能够操作硬盘的沙箱环境,相比 grub secure 那样纯命令行的环境,ubuntu 的 live 环境对于不那么熟练的人来说,还是很友好滴.

我是在 windows 环境下用 UltraISO 把下载的 ubuntu20 镜像文件写入 u 盘,这里直接看已有的教程吧:Ubuntu 20.04 LTS 桌面版详细安装指南

格式化新硬盘的分区

这里有很多选择:

  • fdisk
  • parted
  • Ubuntu 系统自带的 Disks 工具

在 linux 下给硬盘分区没搞过,于是了解一下 fdisk 和 parted,他们都是 linux 的磁盘管理命令,fdisk 只能操作 2TB 以下的分区,parted 是更新更强大的分区工具,支持 GPT 分区格式.

这里踩坑预警!! 如果想完美迁移系统盘,原来是什么分区类型,就按什么类型分.比如我原来是msdos分区,尝鲜使用gpt分区,在修复 grub 引导时就出错了...

第二个坑是手动设置分区时还会提示 4k 对齐警告(知乎:4K 对齐,让你的 SSD 飞上天! ),看起来硬盘存储不是从我们想象中位置 0 开始呢- .-

好吧,我不装了,老实用自带的 Disks 工具分区吧

在这里插入图片描述

分区方案参考文章
Ubuntu20.04 操作系统安装及重中之重:系统分区 > Linux 主分区,扩展分区,逻辑分区的联系和区别 > Linux / boot 分区的建议大小是多少?

经过思考,整张 250GB 卡都给到 ubuntu, 我的分区方案如下:

在这里插入图片描述

硬盘根目录数据完整拷贝

参考文章:迁移 linux 系统到新硬盘

首先要知道: Linux 一切皆文件, 所以拷贝系统其实就是拷贝文件!

那这里使用dd 命令来进行字节级别的迁移,我的根目录所在的分区是/dev/nvme0n1p5,新硬盘划分的是/dev/nvme1n1p2。

1
dd if=/dev/nvme0n1p5 of=/dev/nvme1n1p2

由于 dd 命令没有展示中间过程,因此使用另一条命令来让他输出中间过程:

1
watch -n 5 killall -USR1 dd

17G 的数据在 nvme ssd 之间传输没有什么压力,很快传完

在这里插入图片描述

这里要注意,dd 命令也会拷贝 uuid 过去,意味着,新分区与旧分区的 uuid 是一样的。后面修改挂载信息会再提到.

拷贝完之后,还要更新一下分区信息,否则挂载后还是会显示原来的分区大小和使用情况:

1
2
3
umount /dev/nvme1n1p2
e2fsck -f /dev/nvme1n1p2
resize2fs /dev/nvme1n1p2

BOOT 引导盘数据拷贝

同样 /boot 如果之前单独拆分出来,现在也要拷贝至新硬盘,但这里要避免 uuid 的重复,两个办法:

  1. 使用 dd 复制,然后修改 UUID
  2. 分区后自动产生新的 uuid,使用cp等文件拷贝方式拷贝引导盘数据

这里我选了后者.当然在 live 环境下,要学会自己用mount命令挂载盘符来操作噢.

更新新的根路径分区 uuid

参考文章:Linux 修改分区 UUID

由于不确定是否能成功,我想保留原有的盘符不动(方便回退),这样就会导致根目录的两个分区 uuid 相同,可能在引导时会错误,所以查阅资料,尝试改变新分区的 uuid.

其实也挺简单,使用系统自带的uuidgen命令产生 id, 然后作为参数更新新的分区,再写入新的根路径下/etc/fstab文件

1
uuidgen | xargs tune2fs /dev/nvme1n1p2 -U

执行命令查看最新盘符的 uuid

1
sudo blkid

在这里插入图片描述
复制最新根目录的 uuid,写入新根目录下的/etc/fstab,这里还是要先挂载盘符噢
在这里插入图片描述
到这里,数据复制就完成了

修改 Grub 引导

直接推荐使用 boot-repair 工具,安装方式如下:

1
2
sudo add-apt-repository ppa:yannubuntu/boot-repair
sudo apt install -y boot-repair

安装完之后,运行命令会调起交互界面

1
boot-repair

这里注意要选择高级选项,手动指定新的引导盘,然后按提示修复引导.

在这里插入图片描述
修复完毕,重启再进,系统完全没变,但是容量已经变为舒服的 184G 了!
在这里插入图片描述

后记

一趟踩坑下来又花费了我美好周日的一下午,写文章花了一晚上,但是谁叫我们是 coder 呢,学了好多硬盘相关的硬件和 linux 知识,最后升级成功,就是爱折腾~~

最后,装备强化完毕,又可以愉快地写代码了,冲!

在这里插入图片描述