更新任务

This commit is contained in:
jimmy committed 2025-01-18 00:48:37 +08:00
1 parent 120a57ad57
commit 6118a80ac4
279 files changed
+9682 -2362

No files matched your search

@@ -0,0 +1,90 @@
---
layout: doc
title: 如何在Vue3的Store中使用i18n
date: 2022-06-27
tags:
- 编程
- 工作
- 学习
head:
- - meta
- name: description
content: 使用的`element-plus`框架时,i18n 的前置初始化
- - meta
- name: keywords
content: vue vue3 i18n js javascript
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章讨论了在 Vue3 环境下使用 vue-i18n 和 element-plus 处理多语言提示时遇到的问题及解决方案。具体来说,文章描述了在使用
setup 函数的路由中,因 useI18n() 在加载时未准备好,导致无法直接使用 t、d 等翻译方法的问题。为了绕过这个问题,文章提供了解决方案,通过手动引入已配置的
i18n 方法,而不是依赖 useI18n()。
总结来说,Vue3 中的 setup 和 ts 环境在路由中使用多语言时会遇到时序性问题,解决方法是手动导入已配置的 i18n
实例,直接调用翻译方法,避免了在路由中使用 useI18n() 时的顺序问题。这种方法可以确保在路由和组件中都能正确地显示多语言提示信息。
<!-- DESC SEP -->
## 使用环境
> `Vue`的版本为:`3.2.36`,
>
> `vue-router`的版本为:`4.0.16`
>
> `vue-i18n`的版本为:`9.2.0-beta.40`
几乎都最新版本了
![在这里插入图片描述](/posts/2022/06/img-1.png)
## 问题复现
使用的`element-plus`框架进行项目的搭建,在登陆的时候在全局路由中进行密码的校验,如果密码错误或正确使用`element-plus`
中的提示组件登录的提示,但是提示的内容要根据选择的语言进行分别的提示
这个似乎很正常的需求,但是因为使用的`vue3`中的`setup`,并且在路由中是使用的`ts`,所以不能使用this,并且像`vue`文件中使用的引入方式:
```js
const { t, d, n, locale } = useI18n()
```
这种方式也是不行的,因为执行顺序的前后问题,在路由加载时`useI18n()`还没有引入执行,所以这种方式的引入会直接报错的
## 问题解决
因为在`.vue`文件中会使用对应的方法将对应的内容引入,但是当执行路由时这个内容并没有加载,但是这个代码一定是存在并且可以直接运行的,所以可以通过其他方式直接手动将对应需要的代码引入,而不使用提供的方法
当在创建对应的i18n时,会先将对应的内容进行创建和导出:
![在这里插入图片描述](/posts/2022/06/img-2.png)
在这个文件中创建了i18n,并且对特定的配置项进行了指定,但是导出的这个i18n中有什么?
![在这里插入图片描述](/posts/2022/06/img-3.png)
在配置的文件中已经存在了要使用的方法,也就是导出的:`t`,`d`,`n`等,要使用的方法也在这里面
![在这里插入图片描述](/posts/2022/06/img-4.png)
在这个`global`中存在了对应的全局的方法,不需要通过i18n指定的方法进行引入执行,可以直接使用,所以这个就是要手动引入的内容
## 解决
![在这里插入图片描述](/posts/2022/06/img-5.png)
使用这种方法问题得到解决,不过在尝试过程中,这种两种方式使用的i18n也是有区别的
在引入的过程中,因为方式不同,所以引入的方法其实也不是同一个
![在这里插入图片描述](/posts/2022/06/img-6.png)
不过在对应的注释中也能略微看出二者的区别
![在这里插入图片描述](/posts/2022/06/img-7.png)
使用方法引入的‘t’应当也有特有的内容,这里只进行列举,不继续深入探讨。
@@ -0,0 +1,518 @@
---
layout: doc
title: JavaScript和Vue中实现表格(table)固定表头和首列
date: 2022-08-26
tags:
- 编程
- 学习
head:
- - meta
- name: description
content: 本文主要介绍关于`JS`或`Vue`中如何进行表头,列固定,可以根据实际应用场景应用于`原生`,`Vue`,`移动端`,`小程序`中
- - meta
- name: keywords
content: vue vue3 js javascript
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章介绍了如何在 JS 或 Vue 中实现表格的列和表头固定功能,适用于不同的开发环境(如原生
JavaScript、Vue、React、小程序等)。文章详细展示了实现这一功能的思路和具体步骤,包括使用 Vue3 和 TypeScript 结合 Less
来完成样式和功能的实现。
<!-- DESC SEP -->
## 写在前面
本文主要介绍关于`JS`或`Vue`中如何进行表头,列固定,可以根据实际应用场景应用于`原生`,`Vue`,`移动端`,`小程序`中
实际效果展示:
![在这里插入图片描述](/posts/2022/08/img-1.gif)
## 思路概述
`表头,列头,表格本身,这三个内容被分为了三块元素,将这三个内容进行抽离,并且通过定位和大盒子的包裹进行样式管理。`
【当要进行滚动时,只要对滚动事件进行绑定,让`表头和列头显示对应的内容就完成了滚动的操作`】
下面有`Vue`组件的和`JavaScript`原生的代码,可以直接运行查看逻辑和效果
## 具体实现
整体代码,`可以直接创建组件,将代码复制到组件中使用`,在`App`中引用运行【基于`vue3+ts+less`实现,其他版本会在后面给出修改思路】
`【最下面也提供了原生写法】`
```html
<!--
~ Time:2022/8/9 13:06 29
~ Name:fixmeTable.vue
~ Path:src/components/table
~ ProjectName:element-plus
~ Author:charlatan
~
~ Il n'ya qu'un héroïsme au monde :
~ c'est de voir le monde tel qu'il est et de l'aimer.
-->
<script lang="ts" setup>
import { ref } from 'vue'
const row = 20
const column = 30
let top = ref('0px')
let left = ref('0px')
const Scroll = (event: Event) => {
top.value = -event.target.scrollTop + 'px'
left.value = -event.target.scrollLeft + 'px'
}
</script>
<template>
<h3>
列头表头固定table
</h3>
<hr>
<!-- 表格-->
<div class="table">
<!-- 表头-->
<div class="rowHeader">
<table class="rowHeader-table">
<tr>
<td class="td" v-for="(item,index) in row" :key="index">
{{ item }}
</td>
</tr>
</table>
</div>
<!-- 列头-->
<div class="columnHeader">
<table class="columnHeader-table">
<tr v-for="(RootItem,index) in column" :key="index">
<td class="td">
{{ String.fromCharCode(RootItem + 64) }}
</td>
</tr>
</table>
</div>
<!-- 表格-->
<div class="tableBody" @scroll="Scroll($event)">
<table class="tableBody-table">
<tr v-for="(RootItem,index) in column" :key="index">
<td
class="td" v-for="(item,index) in row" :key="index">
{{ item + String.fromCharCode(RootItem + 64) }}
</td>
</tr>
</table>
</div>
</div>
</template>
<style scoped lang="less">
.table {
border: 1px solid red;
padding: 1rem;
border-radius: 1rem;
position: absolute;
left: 200px;
top: 100px;
width: 300px;
height: 300px;
/ / 表头 . rowHeader {
position: relative;
width: 260px;
max-width: 300px;
overflow: hidden;
left: 30px;
.rowHeader-table {
position: relative;
left: v-bind(left);
width: fit-content;
}
} / / 列头 . columnHeader {
position: relative;
max-height: 300px;
overflow: hidden;
width: 30px;
top: 3px;
.columnHeader-table {
position: relative;
top: v-bind(top)
}
} / / 表格本体 . tableBody {
width: 265px;
height: 300px;
position: absolute;
top: 55px;
left: 50px;
overflow: auto;
} / / 表格元素 tr {
width: fit-content;
white-space: normal;
display: flex;
.td {
display: block;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
}
}
}
</style>
```
1. `html`部分:主要进行框架的设计,这里表头,列头,表格本体分为三份
```html
<template>
<h3>
列头表头固定table
</h3>
<hr>
<!-- 表格-->
<div class="table">
<!-- 表头-->
<div class="rowHeader">
<table class="rowHeader-table">
<tr>
<td class="td" v-for="(item,index) in row" :key="index">
{{ item }}
</td>
</tr>
</table>
</div>
<!-- 列头-->
<div class="columnHeader">
<table class="columnHeader-table">
<tr v-for="(RootItem,index) in column" :key="index">
<td class="td">
{{ String.fromCharCode(RootItem + 64) }}
</td>
</tr>
</table>
</div>
<!-- 表格-->
<div class="tableBody" @scroll="Scroll($event)">
<table class="tableBody-table">
<tr v-for="(RootItem,index) in column" :key="index">
<td
class="td" v-for="(item,index) in row" :key="index">
{{ item + String.fromCharCode(RootItem + 64) }}
</td>
</tr>
</table>
</div>
</div>
</template>
```
内容分为了三部分,这三部分将相互独立,通过样式的调节将三部分内容进行统一的管理。
这里使用的是`table`进行了内容的填充,因为表头和列头是单独的元素,所以可以使用其他内容如`div`等,通过样式上的和交互上的进行协调统一,这里不在赘述
2. 样式上的内容,因为主要是功能,这里样式使用`less`进行简单绘制
```css
<style scoped lang="less">
.table {
border: 1px solid red;
padding: 1rem;
border-radius: 1rem;
position: absolute;
left: 200px;
top: 100px;
width: 300px;
height: 300px;
// 表头
.rowHeader {
position: relative;
width: 260px;
max-width: 300px;
overflow: hidden;
left: 30px;
.rowHeader-table {
position: relative;
left: v-bind(left);
width: fit-content;
}
}
// 列头
.columnHeader {
position: relative;
max-height: 300px;
overflow: hidden;
width: 30px;
top: 3px;
.columnHeader-table {
position: relative;
top: v-bind(top)
}
}
// 表格本体
.tableBody {
width: 265px;
height: 300px;
position: absolute;
top: 55px;
left: 50px;
overflow: auto;
}
// 表格元素
tr {
width: fit-content;
white-space: normal;
display: flex;
.td {
display: block;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
}
}
}
</style>
```
内容上和定义的结构进行统一调节,这里需要主要的是:<span style='color:red;font-weight:900;'>
因为表格的宽度和表头的宽度对应,表格的高度和列头的高度固定,所以建议在定义时考虑好对应的宽高对应关系,方便调节</span>
在定义好表头和列头时,对与展示的部分通过外部的`div`进行定位的调节进行展示,内部的表格部分通过宽度的固定来达到样式统一,使用相对定位来完成展示内容的切换
内部使用了Vue3提供的`v-bind`,如果你使用的时vue2或其他内容请更换为内联样式或其他方式
`v-bind`
为Vue3中提供,主要是将绑定的内容转换为自定义标签使用,具体介绍可以看我的另一篇文章:[Vue3新属性 — v-bind in css](https://blog.csdn.net/weixin_52235488/article/details/126290046?spm=1001.2014.3001.5502)
,里面有用法和原理的详细介绍
3. 逻辑部分:主要为内部的真正的表格绑定了滚动的监听,
```html
<script lang="ts" setup>
import { ref } from 'vue'
// 定义行和列
const row = 20
const column = 30
// 将内容进行响应式定义
let top = ref('0px')
let left = ref('0px')
// 绑定对应的监听事件
const Scroll = (event: Event) => {
top.value = -event.target.scrollTop + 'px'
left.value = -event.target.scrollLeft + 'px'
}
</script>
```
这里使用的动态数据在`v-bind`中进行,这里直接修改即可
## 其他环境中要修改的内容
这里对常见问题进行解决,如果没有匹配,请评论或找其他方式解决
1. `原生JavaScript中使用`:如果你完全没有使用过`Vue`,上面的代码我也改成了原生的版本,逻辑一样,这里就只放代码不做介绍了
`直接创建文件复制使用`
```html
<!--
~ Time:2022/8/12 9:02 36
~ Name:test1.html
~ Path:
~ ProjectName:element-plus
~ Author:charlatan
~
~ Il n'ya qu'un héroïsme au monde :
~ c'est de voir le monde tel qu'il est et de l'aimer.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
font: 500 15px YouYuan;
text-decoration: none;
}
li {
cursor: pointer;
position: relative;
}
/*-------以上为默认设置(The above is the default setting)--------*/
.table {
border: 1px solid red;
padding: 1rem;
border-radius: 1rem;
position: absolute;
left: 200px;
top: 100px;
width: 300px;
height: 300px;
}
.rowHeader {
position: relative;
width: 260px;
max-width: 300px;
overflow: hidden;
left: 30px;
}
.rowHeader-table {
position: relative;
left: 0;
width: fit-content;
}
.columnHeader {
position: relative;
max-height: 270px;
overflow: hidden;
width: 30px;
top: 3px;
}
.columnHeader-table {
position: relative;
top: 0;
}
.tableBody {
width: 265px;
height: 270px;
position: absolute;
top: 55px;
left: 50px;
overflow: auto;
}
tr {
width: fit-content;
white-space: normal;
display: flex;
}
.td {
display: block;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
}
</style>
</head>
<body>
<div class="table">
<!-- 表头-->
<div class="rowHeader">
<table class="rowHeader-table">
<tr id="rowHeader-table-tr"></tr>
</table>
</div>
<!-- 列头-->
<div class="columnHeader">
<table class="columnHeader-table" id="columnHeader-table-tr"></table>
</div>
<!-- 表格-->
<div class="tableBody" id="scroll">
<table class="tableBody-table" id="tableBody-table-tr"></table>
</div>
</div>
</body>
<script>
// 定义行和列
const row = 30
const column = 30
// 获取表头
let rowHeard = document.getElementById('rowHeader-table-tr')
// 创建表头元素
for (let i = 0, len = row; i < len; i++) {
// 获取元素
let td = document.createElement('td')
// 添加样式
td.classList.add('td')
// 添加内容
let testNode = document.createTextNode(i)
td.appendChild(testNode)
// 将表头放置到指定位置
rowHeard.appendChild(td)
}
// 创建表头元素
let columnHeard = document.getElementById('columnHeader-table-tr')
// 创建元素
for (let i = 0, len = column; i < len; i++) {
let tr = document.createElement('tr')
let td = document.createElement('td')
td.classList.add('td')
td.appendChild(document.createTextNode(String.fromCharCode(i + 64)))
tr.appendChild(td)
columnHeard.appendChild(tr)
}
// 创建表格元素
let table = document.getElementById('tableBody-table-tr')
for (let i = 0, len = column; i < len; i++) {
let tr = document.createElement('tr')
for (let j = 0, len = row; j < len; j++) {
let td = document.createElement('td')
td.classList.add('td')
td.appendChild(document.createTextNode(i + String.fromCharCode(j + 64)))
tr.appendChild(td)
}
table.appendChild(tr)
}
// 获取表头和列头滚动元素
let row_heard = document.getElementsByClassName('rowHeader-table')[0]
let column_heard = document.getElementsByClassName('columnHeader-table')[0]
// 绑定滚动时间
let scroll = document.getElementById('scroll')
scroll.onscroll = function (event) {
row_heard.style.left = -event.target.scrollLeft + 'px'
column_heard.style.top = -event.target.scrollTop + 'px'
}
</script>
</html>
```
主要通过的是JS的元素创建然后将元素添加至对应的内容中,然后进行事件的绑定,从而完成整体逻辑
2. `React`和`小程序中`,二者都是可以进行数据的绑定,元素的循环处理更加方法,小程序中没有鼠标,可能要更换为手指滑动操作,对于这个逻辑相同,
`绑定元素的移动距离`,从而让表头和列头跟随移动从而达到同步的效果和目的
@@ -0,0 +1,119 @@
---
layout: doc
title: Vue3中的ref为何要用.value进行值的调用?
date: 2022-08-25
tags:
- 编程
- 学习
head:
- - meta
- name: description
content: 在`Vue2`中,所有的数据都通过一个Data进行统一的返回,并且在data中对某个组件要用的数据进行统一的管理,常见的使用形式是这样的
- - meta
- name: keywords
content: vue vue3 js javascript
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章对比了 Vue2 和 Vue3 中的数据响应式处理方式,特别是如何定义和代理数据。在 Vue2 中,所有数据都通过一个统一的 data 对象进行管理,使用 Object.defineProperty 来实现响应式绑定。而在 Vue3 中,数据定义变得更加灵活,允许直接使用单一的数据类型,通过 ref 和 reactive 来创建响应式数据。虽然 ref 看似代理了普通数据,但实际上 Vue3 使用了 Proxy 技术对数据进行处理,这使得响应式机制变得更加高效和灵活。
总结来说,Vue3 引入了更强大的数据代理机制,通过 Proxy 改善了响应式数据的管理,允许更复杂的数据类型和更加灵活的数据操作,而 Vue2 依赖于传统的对象代理方式。这些变化使得 Vue3 在处理响应式数据时更具优势,尤其是在开发大型项目时。
如果你对 Vue3 中的响应式机制和数据代理的工作原理感兴趣,这篇文章会带你深入了解背后的技术细节。
<!-- DESC SEP -->
在`Vue2`中,所有的数据都通过一个Data进行统一的返回,并且在data中对某个组件要用的数据进行统一的管理,常见的使用形式是这样的:
```html
<template>
<div class="div">
<todos :Obj="tos" :removeObj="removeObj"></todos>
</div>
</template>
<script>
import search from '@/components/search'
import todos from '@/components/todos'
import all from '@/components/all'
export default {
name: 'App',
data () {
return {
tos: [
{ id: '001', value: '第一个', done: true },
{ id: '002', value: '第二个', done: true },
{ id: '003', value: '第三个', done: false },
{ id: '004', value: '第四个', done: true },
],
}
},
computed: {},
components: {
search, todos, all,
},
methods: {
removeObj (obj) {
console.log(obj.id)
this.tos = this.tos.filter(item => item.id !== obj.id)
console.log(this.tos)
},
},
}
</script>
<style scoped>
/
/
样式
</style>
```
可以看出来这里定义的内容都在一个数组中进行,或者是一个函数,将要使用的数据返回出来,这里无论怎么进行操作处理,最终进行数据代理的时候得到的都是一个对象,Vue2中直接通过
`defineProperty`进行处理,并绑定对应的监听事件进行响应式的处理。
而Vue3中,数据的定义可以是单独的,Vue可以让我们随时需要随时定义,这也就带来了另一个问题,我需要的一个数据可能不是对象
```js
<script lang="ts" setup>
let str = ref('响应式字符串')
let obj = reactive({
name: '张三',
age: 10,
})
</script>
```
如果要定义的数据不是对象,还需要代理会怎么样?
在Vue2中数据的定义都在对象中统一进行,也就不会出现这种情况,如果一定要代理一个单独的数据呢?
Vue2中的数据代理通过`defineProperty`进行实现,也就是说我们要让`defineProperty`代理一个普通的数据,而不是一个对象,在
`defineProperty`的MDN的文档中是这样定义的:
> **`Object.defineProperty()`** 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
通过这个可以明确看出,只能进行对象的代理,不能进行普通数据的代理,如果使用普通数据类型会直接报以下错误[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p1Lm0OSJ-1661407967498)(Vue3中的ref为何要用.value进行值的调用?.assets/image-20220825094409254.png)]
在Vue3中数据代理可以使用单一数据了,并且也改进了数据代理的方式,使用的是`Peoxy`完成了数据代理,而MDN中对`Proxy`也进行了定义:
> **Proxy** 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
可以看出,`Proxy`依然是为对象服务,而不是普通的内容,这样问题似乎就解决了
本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
可以看出,`Proxy`依然是为对象服务,而不是普通的内容,这样问题似乎就解决了
> 即使是Vue3中使用的`Proxy`的代理方式也不能进行普通数据的代理,所以当调用`Ref`的时候其实仍然创建了一个`Proxy`
> 对象,并且Vue帮你给这个对象了一个`value`属性,属性值就是你定义的内容,改变的时候监视的改变依然是通过`Proxy`
> 的数据劫持来进行响应式的处理,而模板中使用的时候`Vue`会默认调用对应的`value`属性,从而完成模板中的内容的直接调用
@@ -0,0 +1,405 @@
---
layout: doc
title: Vue3新属性 — css中使用v-bind(v-bind in css)
date: 2022-08-11
tags:
- 编程
- 学习
head:
- - meta
- name: description
content: 本文主要介绍Vue3中的新增的`v-bind()`的常用使用方式,主要包括在`css`,`less`,`scss`中的使用,本文将主要通过一个demo中的使用来展示
- - meta
- name: keywords
content: vue vue3 js javascript v-bind
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章介绍了在 Vue3 中如何使用 v-bind() 来动态绑定 CSS 样式,尤其是在 CSS、LESS 和 SCSS 文件中的应用。
关键内容总结:
背景:文章解释了 Vue3 中 v-bind() 在样式绑定中的新用法,展示了在不同样式表语言(CSS、LESS、SCSS)中的常见应用场景。
实际示例:
CSS:展示了通过 v-bind() 绑定直接使用的值、拼接值、对象调用和组合使用四种方式。
LESS:与 CSS 类似,使用变量和 v-bind() 绑定数据,但在 LESS 中出现了拼接使用的问题。
问题分析:
拼接使用问题:在 LESS 中,某些拼接方式无法正常工作,导致变量无法正确转换为带单位的值,甚至会导致错误。
错误提示:解释了因 LESS 在拼接操作时报错,导致绑定失败的问题。
<!-- DESC SEP -->
# Vue3新属性:CSS 中的 `v-bind()`
## 写在前面:
本文主要介绍Vue3中的新增的`v-bind()`的常用使用方式,主要包括在`css`,`less`,`scss`中的使用,可以参考官方文档查看:[Vue3官方文档](https://cn.vuejs.org/api/sfc-css-features.html#v-bind-in-css),本文将主要通过一个demo中的使用来展示
## 实际使用:
先创建一个通过的页面结构,因为是结构是通用内容,所以这里在直接进行简单的定义
```html
<div class="div">
<div class="span">
<div class="span_title">
这是测试文字
</div>
</div>
</div>
```
- 在`css`中:
在`css`中主要通过直接进行绑定,主要包括几种使用方式(我自己编的名词):`直接使用`、`拼接使用`、`对象调用`、`组合使用`【这是我想到的几种常见方式,如果有特殊需求,可以评论区沟通】
在JS中先对需要用的数据进行定义:
```js
// 进行拼接调用测试的数据
let width = 400
// 直接调用的数据
let div_height = '400px'
let div_color = '#e89393'
// 对象调用的数据
let span = {
width: '200px',
height: '200px',
color: 'green',
}
// 组合调用的数据
let transition = 'cubic-bezier(0, 1.5, .6, 1)'
```
然后这些数据直接在`css`中进行使用,使用`v-bind()`进行绑定
```css
<style scoped lang="css">
.div {
/* 拼接使用 */
width: v-bind(width + 'px');
/* 直接使用 */
height: v-bind(div_height);
background: v-bind(div_color);
}
.span {
/* 对象调用 */
width: v-bind('span.width');
height: v-bind('span.height');
background: v-bind('span.color');
display: flex;
justify-content: center;
align-items: center;
}
.span_title {
width: 100px;
height: 100px;
background: #000;
color: white;
/* 组合使用 */
transition: all .9s v-bind(transition);
}
.span:hover .span_title {
border-radius: 50%;
background: #a5f5b8;
color: #ff0000;
}
</style>
```
在对应的四个方式的调用中:
`直接使用`:完全没有问题
`拼接使用`:这个在`css`中没有问题,不过在`scss`中会出现错误,这个在下面的`scss`中会有详细介绍
`对象调用`:对象的调用和直接使用类似,不过不同的是,需要使用引号的包裹才能正常使用,如果直接书写会报错
`组合使用`:完全没问题
- 在`less`中:
使用的数据同样是上面给出的数据,这里直接改样式的内容了
```css
<style scoped lang="less">
// 使用变量承接
@height: v-bind(div_height);
.div {
width: 400px;
/* 直接使用 */
height: @height;
background: v-bind(div_color);
.span {
@width: v-bind('span.width');
/* 对象调用 */
width: @width;
height: v-bind('span.height');
background: v-bind('span.color');
display: flex;
justify-content: center;
align-items: center;
.span_title {
@transition: v-bind(transition);
width: 100px;
height: 100px;
background: #000;
color: white;
/* 组合使用 */
transition: all .9s @transition;
}
&:hover .span_title {
border-radius: 50%;
background: #a5f5b8;
color: #ff0000;
}
}
}
</style>
```
`直接使用`:没有问题
`拼接使用`:**存在问题**,在下面尝试进行了解决
`对象调用`:没问题
`组合使用`:没问题
<h3>问题:</h3>
> 在拼接使用有几种方式:
>
> ```js
> // 定义使用的数据
> let width = 400
> ```
>
> ```css
> // 直接拼接
> @width: v-bind( width + 'px');
> width: @width;
>
> // 拿到内容后拼接
> @width: v-bind(width) + 'px';
> width: @width;
>
> // 使用时拼接
> @width: v-bind(width);
> width: @width + 'px';
>
> ```
>
> 常用的拼接方法一般为这几种(如果有欠缺还请补充),这几种方式都进行了尝试,其中第二种第三种会没有效果,第一种会直接报错
<h3>问题分析(尝试解决):【这部分会很长,如果不想看,可以直接跳到解决方法】</h3>
首先是第一种,如果使用的话会直接报错,报错内容为:
![在这里插入图片描述](/posts/2022/08/img-1.png)
因为`less`的直接报错,导致定义的变量压根就没有被编译出来,从而报错
第二种和第三种错误一样:
![在这里插入图片描述](/posts/2022/08/img-2.png)
在这种使用方式中,变量可以正确承接,但是不能完成拼接的操作,导致只有变量而没有单位,并且可以看出最终都被翻译为`var(--变量名)`来执行,所以顺便也对`var()`进行了拼接测试
![在这里插入图片描述](/posts/2022/08/img-3.png)
这里进行调用内容的定义,并且模仿编译后的内容,进行使用,在这种情况下尝试使用拼接
> 尝试的拼接方式:
>
> `直接拼接`:
>
> ```css
> :root{
> --test:400 + 'px';
> }
> /* 使用 */
> width: var(--test);
> ```
>
> `接受变量后拼接`:
>
> ```css
> :root{
> --test:400 + 'px';
> }
> /* 使用 */
> width: var(--test+ 'px');
> ```
>
> `继承后拼接`:
>
> ```css
> :root{
> --test:400 + 'px';
> }
> /* 使用 */
> width: var(--test) + 'px';
> ```
结果也非常让不出意外的都不行,并且在第二种方式也是直接报错的,在寻找问题的过程中,发现了更加`amazing`的事情:
![在这里插入图片描述](/posts/2022/08/img-4.png)
这个变量压根就没定义,或者说没被识别到,所以重新模仿`Vue`直接定义行内样式,并且使用拼接尝试:
![在这里插入图片描述](/posts/2022/08/img-5.png)
这种直接的拼接方式直接将所有内容都进行了拼接,再换一种思路:使用`V-bind`这个V-bind指的是数据的而不是样式:
![在这里插入图片描述](/posts/2022/08/img-6.png)
这次问题得到了解决,并且样式数据也是响应式的单个数据,并且也是在`less`中正确使用,应该也算解决问题吧
<h3>解决方式(思路):</h3>
将变量的内容在`行内样式`使用拼接的方式进行定义,然后在定义的`less或scss`中进行使用,因为定义的样式是行内样式,所以优先级和变量出现的位置,都是在使用之前,所以可以正常使用
<h3>完整代码:</h3>
> 使用数据的定义:
>
> ```js
> // 大部分数据和预先提供的数据一样(这里只写了新增数据)
> let test = 400
> ```
>
> 页面结构:
>
> ```html
> <!-- 改变的内容为下面这行(其他内容和原来内容保持一致)-->
> <!-- 【在这里对要使用的数据进行单位的绑定】-->
> <div class="div" :style="{'--test' :test + 'px'}">
> <div class="span">
> <div class="span_title">
> 这是测试文字
> </div>
> </div>
> </div>
> ```
>
> 样式的使用:
>
> ```css
> <style scoped lang="less">
> // 承接使用
> @width: var(--test);
>
> .div {
> /* 使用 */
> width: @width;
> /* 或者直接使用 */
> width: var(--test);
> height: 400px;
> background: red;
> }
> </style>
> ```
>
在`css`的测试中几种方式也都能完美运行,并且在`less`的特有的数据绑定中也都没有问题,对于`拼接使用`是可以正常使用的。
我用的工具是`webstorm`less也是唯一一个没有报错的提示的(工具内的报错提示,可能不认识新内容把)
- 在`scss`中:
```css
<style scoped lang="scss">
// 使用变量承接
$width: v-bind(width + 'px');
$height: v-bind(div_height);
.div {
/* 拼接使用 */
width: $width;
/* 直接使用 */
height: $height;
background: v-bind(div_color);
.span {
$width: v-bind('span.width');
/* 对象调用 */
width: $width;
height: v-bind('span.height');
background: v-bind('span.color');
display: flex;
justify-content: center;
align-items: center;
.span_title {
$transition: v-bind(transition);
width: 100px;
height: 100px;
background: #000;
color: white;
/* 组合使用 */
transition: all .9s $transition;
}
&:hover .span_title {
border-radius: 50%;
background: #a5f5b8;
color: #ff0000;
}
}
}
</style>
```
`scss`相对`less`一些方法更加灵活,在`v-bind()`的使用中,拼接使用会出现错误,这个暂时按下不表,在原理中会详细阐述
- 执行原理:
`v-bind() in css`并没有进行劫持或者其他操作,他主要完成的任务是将`v-bind()`中的内容通过自定义属性的方式进行了重新定义,在编译后代码中可以看到
![在这里插入图片描述](/posts/2022/08/img-7.png)
所有的数据直接在自定义属性中进行,并且直接对应引用,自定义属性的内容的改变影响视图的效果,从而达到数据影响视图的效果,这个在使用中`less`,`css`中完全没有问题
但是,在`scss`中,因为编译之后`名字无法进行匹配`从而造成样式的丢失
![在这里插入图片描述](/posts/2022/08/img-8.png)
可以看到,使用的名字和编译的名字并不匹配,从而出错,这个使用时需要注意
@@ -0,0 +1,135 @@
---
layout: doc
title: 在JavaScript获取Promise的状态
date: 2022-08-09
tags:
- 编程
- 学习
head:
- - meta
- name: keywords
content: js javascript
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章讨论了如何同步地获取 JavaScript Promise 对象的状态。通过一个 Stack Overflow 上的解决方案,文章介绍了如何使用
Promise.race() 方法判断 Promise 的状态。
关键内容总结:
问题:如何在执行过程中同步地检查一个 Promise 的状态?
解决方案:文章通过 Promise.race() 方法对 Promise 和一个额外的 target 对象进行竞赛(race)。根据返回的对象,判断 Promise 是否处于
pending(待定)、fulfilled(已完成)或 rejected(已拒绝)状态。
代码分析:
通过创建一个 target 对象,Promise.race() 在 Promise 和 target 中最先完成的会决定返回值。
如果 Promise 未完成,则返回 pending;如果已完成且无错误,则返回 fulfilled;如果发生错误,则返回 rejected。
注意事项:Promise.race() 中的顺序很重要,确保 promise 在前,否则可能会影响结果。
这个方法利用了 Promise.race() 的特性,巧妙地判断了 Promise 的状态。
<!-- DESC SEP -->
# 写在前面
本文属于对`stackoverflow`中写法的解析,原文链接:[**Stack Overflow
** ](https://stackoverflow.com/questions/30564053/how-can-i-synchronously-determine-a-javascript-promises-state)
如果你在其他环境下遇到同样问题,这个[文章](https://blog.csdn.net/Lee_01/article/details/124892590)可能也会有帮助
# 问题复现
如何在代码执行过程中获取到`promise`的状态?
如果你想寻找或尝试通过原生的属性,或者指定API进行访问,基本可以放弃了,我也是看了很多博客的确没找到,如果您找了其他方式,还望指教
# 解决方式
本文中提到的解决方式主要是继续`stackoverflow`中提供的解决方式【完整代码】
```js
/**
* 传入需要测试的 Promise 对象,将对应结果返回
* @param promise 需要测试的 Promise 对象
* @return {Promise<string>}
*/
const promiseState = promise => {
const target = {}
return Promise.race([promise, target]).then(
value => (value === target) ? 'pending' : 'fulfilled',
() => 'rejected',
)
}
// 测试代码
let a = Promise.resolve();
let b = Promise.reject();
let c = new Promise(() => {});
promiseState(a).then(state => console.log(state)); // fulfilled
promiseState(b).then(state => console.log(state)); // rejected
promiseState(c).then(state => console.log(state)); // pending
```
这种方式通过`promise`中的`race()`方法来进行了状态的判断,非常奇妙的一种方式
# 代码分析
1. 在方法中会接收一个`promise`对象,开始了整个方法
2. 进入方法后会先创建一个`target`对象,这个对象只用于后续的匹配,没有其他含义
```js
const target = {}
```
3. 然后开始最核心的部分,因为`stackoverflow`中对代码进行简写,可以先进行代码的还原
```js
return Promise.race([promise, target]).then(
// 成功回调
value => {
return (value === target) ? 'pending' : 'fulfilled'
},
// 失败回调
() => {
return 'rejected'
},
)
```
可以复现出这种结构,`promise`的`race`开始后,接收两个对象,一个为刚刚创建的`target`对象,还有一个是接收到的`promise`
对象,当两个其中一个状态结束时,整个`Promise.race()`结束,这时分为三种情况
1. 当传入的`promise`为`pending`状态时,`Promise.race`会找到最先有确定状态的元素,并将其返回,因为`promise`
没有结束,那么返回值对象会接收到定义的`target`,因为`target`的状态是确定的,这时`value`接收到的值就是已经确定状态的
`target`,如果进行对比,那么他们两个一定是相等的,这时也就是返回`pending`
2. 当传入的`promise`为`fulfilled`状态时,相当于`promise`已经有确定的值了,`Promise.race`
匹配到第一个直接结束,并将匹配到的内容返回,返回的内容因为是一个全新的对象,对象的内存地址不可能相等,所以判断结果是
`false`,也就返回`fulfilled`
3. 当传入的`promise`为`rejected`状态时,`promise`状态确定,那么因为状态报错,直接进入`catch`中,走失败回调,这个回调一旦执行,就可以判定
`promsie`结束,并且内容出现报错,直接返回`rejected`即可
4. 在调用的时候,因为返回的时会将对应的状态通过`promise`返回,所以直接通过调用方法之后的`then`中指定回调来完成状态的获取
- **值得注意的是**:`Promise.race()`在匹配的过程中,一定保证要判断的`promise`在前,因为只有当`promise`
状态不确定时才会判断第二个内容的状态,如果将状态已经确定的`target`放在第一个,那么将不会再进行`promise`的判断,返回结果永远为
`fulfilled`
- 并且因为`catch`有两种形式,可以进行变形,这种形式可能更常见(根据个人习惯了)
```js
const promiseState = promise => {
const target = {}
return Promise.race([promise, target]).then(
value => {
return (value === target) ? 'pending' : 'fulfilled'
},
).catch(() => {
return 'rejected'
})
}
```
@@ -0,0 +1,141 @@
---
layout: doc
title: TypeScript 应该被放弃吗
date: 2024-08-04
tags:
- 编程
- 学习
- 思考
head:
- - meta
- name: keywords
content: vue vue3 js javascript react ts typescript
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
文章讨论了关于是否放弃 TypeScript 的问题,尤其是在一些大牛和社区项目的选择上。重点聚焦于两篇文章的观点:
Turbo 8 放弃 TypeScript:作者认为 TypeScript 的类型检查反而增加了开发中的负担,并且声明类型复杂化代码,减少了开发效率。这个观点在 Reddit 和知乎引发了激烈讨论,部分评论指出 Turbo 8 并不具备广泛影响力,且作者习惯发表反向意见。
TypeScript 的额外成本:第二篇文章提到,在某些场景下,TypeScript 的类型体操带来不必要的复杂度,尤其是对于小型项目,编写精确的类型定义可能比核心逻辑代码更为冗长。然而,TypeScript 相比于 JavaScript 在维护性和可扩展性上具有优势,尤其是对于框架和类库的开发者而言,尽管带来一些额外的维护成本。
总体来看,放弃 TypeScript 的选择多见于小型或开源项目的维护者,而对于大多数开发者来说,TypeScript 提供的类型系统和可维护性仍然是更受欢迎的选择。
<!-- DESC SEP -->
### TypeScript 应该放弃吗
--------
抛出这个问题主要基于22年和23年发生的一些事件:
![image-20240627143223214](/posts/2024/08/10003/up-58c4155f83bbd826f4bb0ec1ab8d1e32ccd-1719469395020-15-1719469397190-17.png)
![image-20240627143223214](/posts/2024/08/10003/eed5a2dd0f71b8b4790a1b20c708416c-1719469475131-21.png)
并且Svelte 不是第一个放弃 TypeScript 的前端框架。早在 2020 年,Deno 就迁移了一部分內部 TypeScript 代码到 JavaScript,以减少构建时间。
并且这些大牛放弃 TypeScript 似乎还加快了工作进度,但这个选择是否应该被普通开发者所选择?
搜了很多文章,但是大部分都在将一些很正确的废话,目前来看只有下面两片有参考价值。
##### 第一篇:[Turbo 8 正在放弃 TypeScript (hey.com)](https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01)
>
> 这是原文,正式这篇文章,引来了众多讨论
文章的主要观点如下:
> TypeScript 只是妨碍了我。不仅因为它需要显式的编译步骤,还因为它用类型体操污染了代码
—— 多余类型判断只会降低效率
文章没有多长,几乎没有太多参考价值,但毕竟是原文,还是搬过来比较好。
并且对于这篇文章,Reddit的评论区也是活力十足
![image-20240627144850640](/posts/2024/08/10003/image-20240627144850640.png)
并且在知乎对于这个问题的评价更加激进:
![image-20240627143223214](/posts/2024/08/10003/image-20240627143223214.png)
以下是文字版本:
> 前端届盛产大牛,整点别人看不懂的代码,硬说函数式然后猛劲宣传就能当大牛。
>
> 搞计算机其实有一点不好,就是你以为有些应该不在世的大师他都活着呢。
>
> 算法分析之父高德纳还在呢。
>
> WindowsNT内核之父David Cuttler也还在呢。
>
> Linux内核之父Linus还到处喷人呢。
>
> GUI和面向对象之父Allen Kay也还在呢。
>
> Pascal总架构师,Delphi和C#之父,编程语言届的传奇**Anders Hejlsberg**也还在努力工作呢。
>
> 我不明白某些在社区当网红的不入流货色是怎么敢被封为大牛还大放厥词的。
>
> 对了,TypeScript就是Anders搞的。
无论是知乎还是Reddit的讨论都是相对激进,并拳拳到肉那种,只顾着输出情绪了。不过总结起来大致为以下两个观点:
1. Turbo 8 并不是什么知名项目,没人在意他是否放弃 TypeScript
2. Turbo 8 的作者(也是文章作者),经常写类似的文章来唱反调
3. 文章除了说明了类型让他更加麻烦了,并且有提出其他有力依据,所以被Reddit评论区狂怼
4. 这个大牛是虚假宣传的,为了博眼球臆造出来的,而那些真正的大牛创造了TypeScript
当然**无意义的情绪输出不能解决任何问题**,所以也有下面这个文章通过多个方面来讨论为什么那些社区项目会放弃TypeScript
##### 第二篇:[“根本不需要 TypeScript,JS + JSDoc 够了”,大佬说我想多了](https://my.oschina.net/u/6852546/blog/10114672)
文章的主要观点如下:
> 某些场景下,写 TypeScript 会带来一些额外成本。一些开源库的源码,核心逻辑可能就几十行,但为了实现准确的类型提示,写出来的类型体操反而远远多于核心源码,孰是孰非对于不同的开发者有不同的准绳,需要找到其中的平衡点。
—— 对于特定场景,TypeScript 确实更麻烦了
> 使用了 TypeScript 的项目改回使用 JavaScript 是很少见的,更多的项目是从 JavaScript 升级到 TypeScript。TypeScript 完善了 JavaScript 的类型系统,使得代码的可维护性更高了.
—— 相对来说,TypeScript 相对于 JavaScript 是更加受欢迎的,并且被更多项目所接受
> **框架和类库的开发者,往往需要考虑到很多 edge case**,在这种情况下,编写完善的类型是一件很费心力的事,代码量会多了不少,从而会导致维护成本的增加。
—— 放弃 TypeScript 的更多是那些公共仓库的维护者,而他们需要考虑所有有可能出现的情况,会有非常多的边缘判断来减少异常,这也造成了如果使用 TypeScript反而会更麻烦
> TypeScript 的成功无需质疑,已经有无数的项目证明了它的成功。开发者并没有 “嫌弃” TypeScript,只是认为并不是所有项目都适合使用 TypeScript。
> **有人认为,** **TypeScript** **的出现是因为一般人驾驭不了** **JavaScript** **,有人则觉得 “水平越差的人越喜欢自由”,你怎么看?这两个语言的选择跟程序员的水平有关吗?**
>
> - 平时可没少见有同学吐槽,**好好的 TypeScript 项目,被人提交了一堆 Any**。也见过很多吐槽接手了一个 TypeScript 仓库,要硬着头皮看一大堆类型定义,搞清楚这些奇奇怪怪的类型是如何工作的。我觉得语言的选择主要看团队的工程化和规范化程度,过犹不及。如果一个 TypeScript 类库写了一大堆类型,但却连一个单测都没有,那我觉得它是不合格的。
> - JavaScript 和 TypeScript 不能用来衡量程序员的水平。对于简单的项目或者个人项目而言,JavaScript 可能更加轻量和灵活,但对于需要大团队协作,复杂的项目而言,TypeScript 的类型系统就可以带来更好的代码维护性和可靠性了。
该文章相对来说理性了很多,通过一些采访来尽量还原更加客观的事实,并且对于 TypeScript 的选择相对来说没有那么难决断,那些仓库的作者因为繁琐的 **edge case** 放弃了 TS,但这并不代表他没有一点自己的用武之地,在常规的项目中,尤其是大型的多人合作项目中,TS也会是优先的选择。
并且无论是TS还是JS都能做出很好的产品,二者没有高低贵贱之分,如果一个TS项目写满了 `Any` 那不如直接切换为 JS 反而在某种程度还更能节约代码量了
+225
View File
@@ -0,0 +1,225 @@
---
layout: doc
title: Vue还是React?
date: 2024-08-04
tags:
- 编程
- 学习
- 思考
head:
- - meta
- name: description
content: Vue和React、Nextjs如何选择
- - meta
- name: keywords
content: vue vue3 js javascript react ts typescript
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章探讨了 Vue、React 和 Next.js 之间的选择,并深入分析了 React 的一些优缺点。作者指出,虽然 Vue 在国内市场占据主导地位,React 在国外仍然保持强大的活力。但与此同时,React 面临不少批评,特别是其逐渐显现的技术债务和性能问题。文章引用了一些反对 React 的观点,强调 React 已显老化,且与其他框架相比,存在着难以兼容和过度依赖自定义模块的问题。同时,文章还提到其他框架如 Svelte 的优越性,认为它能提供类似 React 的功能,但更加简洁和高效。
如果你对前端框架的选择和 React 未来的方向感兴趣,这篇文章将为你提供一些新的视角,帮助你做出更明智的决策。
<!-- DESC SEP -->
### Vue和React、Nextjs如何选择
--------
`Vue` 在大前端时代,在国内市场可谓是大杀特杀,几乎以压倒性的优势占领着国内市场,并且随着 2020 年`Vue3`的发布,以及`Vite`的推出,其生态更加完善,基本是国内市场做前端的首选语言。
但和国内相悖的是,在`JetBrains`与`StackOverflow`在2023年的数据报告中,在前端方面数据,`Node`和`React`以压倒性优势领先其他语言
![2023 JetBrains开发者调查报告](/posts/2024/08/10003/image-20240626141050935-1719382253511-9.png)
![StackOverflow 2023调查报告](/posts/2024/08/10003/image-20240626141144499.png)
2023JavaScript趋势报告:
![image-20240626143412891](/posts/2024/08/10003/image-20240626143412891.png)
和国内不同的是,`React`在国外保持着惊人的活力
但也有很多反对`React`的声音,以及关于`React`辩论的文章,如:
[你因为 React 而忘记(或从来不知道)的事情 - Josh Collinsworth 博客](https://joshcollinsworth.com/blog/antiquated-react)
[React 的自我实现预言 - Josh Collinsworth 博客](https://joshcollinsworth.com/blog/self-fulfilling-prophecy-of-react)
[辛辣的 React 辩论秀 🌶️ (JS Party #244) (changelog.com)](https://changelog.com/jsparty/244)
如果各位有兴趣可以读原文,我这里为每篇文章简单的总结。
#### 第一篇:[你因为 React 而忘记(或从来不知道)的事情 - Josh Collinsworth 博客](https://joshcollinsworth.com/blog/antiquated-react)
>
> 该文章也是被 `2023 JavaScript 行业调研报告` 所提到的文章,并且一些观点的确值得思考,但有的就一般了,为了准确传达原作者的意思,我这里会尽量中立的概括,也会引用部分原文进行对应部分内容的解释。
1. ##### 并不是所有好的事物都会流行。
> 回首往事,我意识到:我天真地相信,任何好东西都不可避免地会流行起来——因此,任何值得了解的东西最终都会自己出现。
>
> 作者开篇就提到了好的事物并不会自然的流行,自然而然的出现到你面前,也就说明了另一个意思:**出现在你面前的,流行起来的可能并不是最好的选择**
2. ##### `React`已经老化了
> React 从早期的框架游戏中受益匪浅;它设定了标准。但这在敏捷性和适应性方面存在严重缺陷。自 2013 年左右成立以来,React 做出的每一个决定都是另一层技术债务——它的新同时代人不受其约束。
>
> [再次引用亚历克斯的话](https://toot.cafe/@slightlyoff/110512849934452558):
>
> > React 是 '13 技术,旨在 '08 约束。在 2023 年,它没有任何创新之处;事实上,这是现代获得函数式响应式前端编程的最慢方法......
3. ##### `React`是定制的,这使其在不同框架之间很难移植,几乎不和其他平台兼容
> 使用 React,每当我们需要一个模块、一个小部件或一个库来做一些特定的事情(旋转木马、地图、手风琴或其他任何东西)时,*它必须是 React 的东西*;一个普通的 Web 东西或一个普通的 JavaScript 东西是行不通的。React 的所有规则以及对组件生命周期状态和怪癖的处理意味着任何*没有*明确为 React 编写的可用包或库都可能无法工作。
>
> **React 训练我们,需要*专门为某个框架*构建东西。但这已经不是真的了,可以说永远不应该是这样。**
作者在这里举了个例子,通过框架之间的对比衬托`React`的独断专行
> [Preact Signals](https://preactjs.com/guide/v10/signals/) 就是一个非凡的例子:虽然它是为使用 Preact 而构建的,但它可以在*任何*框架中导入和使用,甚至可以在普通的 JavaScript 中使用。Web 组件也与几乎任何现代非 React 框架兼容。
4. ##### React 钩子实际上有点过时了
> 钩子是一种极好的模式,React 的普及值得称赞。但几乎所有其他框架都做得更好,规则更少,样板更少。
5. ##### `React`相对更加不智能
> 如今,框架足够聪明,可以处理这种事情,而不需要你握住他们的手并解释他们应该做什么。
"但React还是需要你做这些精细化管理"
6. ##### 服务器端渲染不再特别
> 几年前,曾经有一段时间,当涉及到服务器渲染的内容(主要是通过Next JS)时,React几乎是唯一的选择。人们理所当然地对 React 可以在服务器上以 HTML 形式呈现的想法感到兴奋,而不是在客户端上作为单页应用程序 (SPA) 呈现。速度和SEO的收益是不容忽视的,最初,其他框架需要一点时间才能赶上。不过选择更多框架都做到了。
7. ##### React 是由 Facebook 创建的,目的是解决 Facebook 独特的一系列问题
> 在 React 中使用表单是出了名的麻烦,因为每个用户击键都是一个两步过程:从输入中获取值;然后设置状态以匹配它(这反过来又不必要地重新渲染输入,以包含它已经完成的确切值,但与 React 状态同步)。当然,它通常太快而无法注意到,但这是很多额外的工作。
>
> 这就是为什么 React 的表单库激增的原因
8. ##### React 在性能上远远落后
> 在许多情况下,React 在性能方面(这里我指的是捆绑包大小和执行速度)落后 2 倍或更多。(捆绑包本身可以是 10 倍或更多。最新一轮[的 JS Web 框架基准测试](https://github.com/krausest/js-framework-benchmark)显示,React 的性能平均比 Solid 慢近 50%,比 Vue 慢 25%,比 Svelte 慢 40%,比 Preact 慢 35%。(其他框架在此特定测试中不可用。
9. ##### Svelte 是没有废话的 React
> Svelte 使用起来非常简单,相对容易学习(特别是如果你已经来自 React 世界,甚至语法通常也很相似),在几乎所有情况下都表现得更好,并且能够做任何 React 的事情,甚至更多
​ 这也是作者留下的解决方案,并且根据作者本人所说,他本人的所有博客和网站都是有此完成.
#### 第二篇:[React 的自我实现预言 - Josh Collinsworth 博客](https://joshcollinsworth.com/blog/self-fulfilling-prophecy-of-react)
>
> 该篇文章是同一个作者在不同时期写的,本文完成时间更早,同样是对`React`的批评
1. ##### React 除了受欢迎之外,什么都不擅长。
> 让我在 React 和根本没有前端框架之间做出选择,并且给定任何足够大小或复杂度的项目,我很乐意接受 React,非常感谢。
>
> React 为其他框架正在做和已经做的很多事情奠定了基础。[Vue 3](https://vuejs.org/guide/introduction.html) 及其组合 API 显然受到了 React hooks 的启发。[Svelte](https://svelte.dev/) 的许多约定都来自 React。[Nuxt](https://nuxtjs.org/) 是我使用过并喜欢的 Vue 框架,它的许多想法(以及 75% 的名称)都来自 [Next](https://nextjs.org/),一个 React 元框架。就此而言,整个基于组件的前端设计和开发模型的当前状态(哈)很大程度上归功于 React——尽管应该指出的是,其他人也帮助铺平了道路。
>
> **React 做了它擅长的事情,但它并没有比其他*框架做得更好。***
2. ##### 更多的社区和支持并不代表他是一个更好的选择
> 一个大的社区也可能是一个缺点,尤其是在所谓的“无主见”框架的情况下,比如 React。**这可能意味着有太多的套餐可供选择,以及太多不同的、相互竞争的意见,你必须在两者之间做出决定并采取立场。**
3. ##### 多年来,React 的满意度和兴趣一直在稳步下降,而其使用率却持平
> React 很容易成为最常用的前端框架,也是知名度最高的前端框架,但在今年的 [JS 现状调查结果](https://2021.stateofjs.com/en-US/libraries/front-end-frameworks/)中,React 在满意度方面落后于 Solid 和 Svelte。React 在兴趣方面也排在 Svelte、Solid 和 Vue 之后(对于 React 来说,兴趣*低于 50%)*。
>
> React 在 [StackOverflow 调查](https://insights.stackoverflow.com/survey/2021#technology-most-popular-technologies)中远远落后于 Svelte,也仅略微领先于 Vue。
4. ##### 为什么 React 会保持领先地位呢?
> “从来没有人因为选择 React 而被解雇”
>
> 这是安全的选择。它可能不是每个人的最爱,但不一定是。它可能也不会是任何人最不喜欢的。它会完成工作。
像`Vue`一样,因为市场需要所以企业招聘,因为企业招聘,所以更多人学习,同样的,因为会的人多,所以企业招聘。他们完成了各自的闭环,但**这个选择可能不是最明智的,但绝对是最安全的。**
#### 第三篇:[辛辣的 React 辩论秀 🌶️ (JS Party #244) (changelog.com)](https://changelog.com/jsparty/244)
>
> 本篇文章主要是关于是否应该放弃`React`而投身其他框架,以及`React`自身存在问题的讨论
>
> 本篇文章是对一个讨论节目的翻译版本,会有很多观点,本文只总结,不站队
##### 观点一:React赢被放弃
> - React 或多或少是任何给定项目的默认选择。它已经达到了临界质量,感觉就像是一切的安全默认值.
>
> - 从性能到捆绑包大小,到入职,再到复杂性、开发体验......一般来说,所有这些事情都是 React 本身并不能真正获胜的领域,在这些领域有更好的选择
> - 当你缩小并查看大局时,你会意识到 React 在几乎每个给定的类别中都非常平庸,它真正擅长的唯一类别就是成为流行的东西。
##### 观点二:React有自己的难处,他不完美但已经做的很好了,并且也被很多人所接受
> - 当你流行时,你必须处理所有不同的情况。所以你不能只关注一件事而忽略其他。
> - 如果我只关心绝对性能,我会发布静态 HTML。
> - Stack Overflow 调查,我注意到 React 的满意度为 84%,高于 Vue,几乎是 Angular 的两倍,紧随 Svelte 之后,使用率更高。如果React被很多人使用,如果他不被大多数人满意,那他的满意度将会低很多,但事实上这并没有
##### 观点三:React更像一个仁慈的君主
> - 拥有一个几乎可以在任何地方使用的框架不是很好吗?我想要一个带有线条的 3D 旋转地球仪的库。你知道吗?你只需谷歌一下,它就在那里。你把它拉进来。很简单。生活很美好。(作为 2015 年框架战争的老兵,我只想说我们不想回到那个时代。每周都要面对一个新的框架,这让人筋疲力尽。)
> - 如果我们要有一个统治框架,难道我们不希望它没有主见吗?所以 React 真的很好,因为它基本上只是一个带有最小状态管理解决方案的渲染库。有许多其他东西建立在 React 之上,这使得它与其他框架相当。所以即使你考虑元框架,也有很多流行的框架。
> - 如果你了解 JavaScript,你几乎了解 React 的所有内容。他不会像Angular那样有很多新的“魔法”(这里指的是Angular引入的新的改变增加了学习成本,如管道,过滤器等)
![image-20240627133055166](/posts/2024/08/10003/image-20240627133055166-1719466257222-11.png)
##### 观点四:从公司架构思考问题
> React 的优势和我们遇到的摩擦都与它仍然由 Facebook 构建的事实有关,而 Facebook 的重点和优化选择都是为了解决 Facebook 所面临的问题。这些问题与许多公司在构建 JavaScript 应用程序时遇到的问题相当吻合,但一方面它们肯定是错的。
>
> 实际上,我认为你会在 Angular 中遇到一些相同的问题。Angular 仍然主要由 Google 想要什么和 Google 需要什么驱动。所以我认为,如果 Angular 达到这个水平——那么,早期的 Angular 确实很受欢迎,然后经历了很多这样的事情。
>
> 现在,如果你看看满意度,你会发现人们对 Angular 非常不满意,尽管有很多人在使用它。React 也可能会走这个老路……
>
> Vue 目前感觉更像是社区驱动。我觉得 Svelte 也在朝这个方向发展,尽管它们都是从仁慈独裁者的方式开始的。它们都拥抱了更广泛的社区,而不是只满足一家公司的需求
这几篇是一些影响力比较大的文章,相对来说观点已经比较全面了,大致分为以下几点:
1. React 因为时代原因已经有些过时了,因为使用的人很多,所以他每一次新功能的增加都将非常谨慎像现在的Java一样,虽然版本号一路突飞猛进,但是变化依然不大
2. React 的除了流行外别无优点,整体的性能,包大小,等都不够拔
但React也有其自己的优点:
1. 最强大的社区支持,你几乎能找到你想要的所有库
2. React不是一个框架,而是一个库,你多了解 JavaScript 就有多了解 React。他可以让你进行最大限度的定制
> 本文参考的文章如下:
> [JetBrains2023行业状况调研](https://www.jetbrains.com/zh-cn/)
>
> [StackOverflow2023行业调查问卷](https://survey.stackoverflow.co/2023/)
>
> [你因为 React 而忘记(或从来不知道)的事情 - Josh Collinsworth 博客](https://joshcollinsworth.com/blog/antiquated-react)
>
> [React 的自我实现预言 - Josh Collinsworth 博客](https://joshcollinsworth.com/blog/self-fulfilling-prophecy-of-react)
>
> [辛辣的 React 辩论秀 🌶️ (JS Party #244) (changelog.com)](https://changelog.com/jsparty/244)
@@ -0,0 +1,314 @@
---
layout: doc
title: 中西文混排时汉字与拉丁字母之间差异
date: 2024-08-07
tags:
- 编程
- 学习
- 设计
head:
- - meta
- name: description
content: 有研究显示,打字的时候不喜欢在中文和英文之间加空格的人,感情路都走得很辛苦,有七成的比例会在 34 岁的时候跟自己不爱的人结婚,而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。与大家共勉之。
- - meta
- name: keywords
content: 文字排版 布局
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章讨论了中英文混排时,是否应在汉字与拉丁字母之间加入空格的争议。研究和实践表明,虽然专业排版软件如Adobe InDesign通常会自动调整汉字与西文之间的间距,但许多普通用户常常手动插入半角空格以保证文本的可读性和美观。文章介绍了“空格派”在排版中的一些习惯,阐述了何时应该加空格、何时不加空格,并引用了刘昕等人关于中英文混排的标准,最后强调了空格的使用对提高文本阅读体验的重要性。
<!-- DESC SEP -->
>
> 有研究显示,打字的时候不喜欢在中文和英文之间加空格的人,感情路都走得很辛苦,有七成的比例会在 34 岁的时候跟自己不爱的人结婚,而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。与大家共勉之。
>
> ​ ——[vinta/paranoid-auto-spacing](https://github.com/vinta/pangu.js)
### 参考1
> [中西文混排时汉字与拉丁字母之间是否要有空格? - 知乎 (zhihu.com)](https://www.zhihu.com/question/19587406/answer/12298128)
Adobe InDesign、Microsoft Word 等对中文与西文(这里用「西文」来泛指用空格分词的外文)混合排版支持较好的软件,都默认增大汉字和西文的间距。InDesign 的默认设置是 1/4 的[全角空格](https://www.zhihu.com/search?q=全角空格&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})宽度(遵从 JIS),也就是约等于一个半角空格。
但大多数情况下我们没有这样专业[排版软件](https://www.zhihu.com/search?q=排版软件&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})的支持,只能手动控制。这种时候,比较在意文字设计(typography,又译「字体排印」)的团队或个人往往都在操作系统界面或网站中的汉字和西文之间插入一个半角空格。
包括许多的
- 公司:苹果、微软、Google、Adobe……
- 团队:Apple4us、Type is Beautiful、[爱范儿](https://www.zhihu.com/search?q=爱范儿&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})……
- 个人:@刘昕、@江疆……
但这些确实是绝对的少数。大多数人都不/不喜欢/不愿意在汉字和[西文](https://www.zhihu.com/search?q=西文&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})之间加空格。毕竟加空格要考虑的东西太多了,而且加空格也会带来一些问题(见后面给出的在 public-html-ig-zh@w3.org 进行的讨论)。
总的来说,「[空格派](https://www.zhihu.com/search?q=空格派&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})」的习惯就是在汉字和西文之间留一个半角空格。但因为在中西文关系上有一些让人不好决定的特例,他们使用空格的方式有一些变数。
根据一般都「加」空格到一般「不加」空格的顺序,大致可以排出来:
- 内部有空格的西文短语(iPod touch)
- 独立的西文单词(iPhone)
- 全大写字母的西文缩写(HTML)
- 单个的西文字母(X)
- [阿拉伯数字](https://www.zhihu.com/search?q=阿拉伯数字&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})(7)和[西文标点](https://www.zhihu.com/search?q=西文标点&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})(/)
西文和汉字和标点三者相遇的时候问题更复杂(汗……「三体」),大家的习惯也有差异。
\- - -
以上是现状,下面是个人观点:
中文与西文混排时,不一定要有空格字符,但一定要有间距。
使用专业排版软件时,交由软件处理间距问题(Adobe Creative Suite 把这个事归入「[标点挤压](https://www.zhihu.com/search?q=标点挤压&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})」);纯文本写作时,插入半角空格。具体在上面列出的那五种情况下合适加空格,何时不加,至今还没有什么共识。
关注文字设计的朋友其实可以编写自己的 Manual of Style,最终或许能在[百家争鸣](https://www.zhihu.com/search?q=百家争鸣&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})中诞生中文互联网的一份事实标准。
我的空格习惯基本上是受[@刘昕](https://www.zhihu.com/people/455b06074c9e37df07146f86294f6373)
影响而建立的。因为这篇日志:
《中文 Web 阅读体验》:
[http://www.livid.cn/doc_view.php?doc_id=5332](https://link.zhihu.com/?target=http%3A//www.livid.cn/doc_view.php%3Fdoc_id%3D5332)
(原站已关闭,这里是存档:
[中文 Web 阅读体验](https://link.zhihu.com/?target=http%3A//web.archive.org/web/20110716090934/http%3A//www.livid.cn/doc_view.php%3Fdoc_id%3D5332)
)
在看过这篇日志后,我意识到、并至今坚定维护着空格的必要性。刘昕在文中的这句话解释了基本的空格使用规则:
> 中文正文及标题中出现的英文及数字应该使用[半角](https://www.zhihu.com/search?q=半角&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A12298128})方式输入,并且在左右各留一个半角空格。如果这些这些半角英文及数字的左边或者右边紧接着任何的中文全角括号或者其他标点符号的话,则不需要加入半角空格。
### 中英文混排参考规范
- 英文和数字使用半角字符
- 中文文字之间不加空格
- 中文文字与英文、阿拉伯数字及 @ # $ % ^ & * . ( ) 等符号之间加空格
- 中文标点之间不加空格
- 中文标点与前后字符(无论全角或半角)之间不加空格
- 如果括号内有中文,则使用中文括号,如果括号中的内容全部都是英文,则使用半角英文括号
- 当半角符号 / 表示“或者”之意时,与前后的字符之间均不加空格
##### 中英文之间需要增加空格
正确:
> 在 LeanCloud 上,数据存储是围绕 `AVObject` 进行的。
错误:
> 在LeanCloud上,数据存储是围绕`AVObject`进行的。
>
> 在 LeanCloud上,数据存储是围绕`AVObject` 进行的。
完整的正确用法:
> 在 LeanCloud 上,数据存储是围绕 `AVObject` 进行的。每个 `AVObject` 都包含了与 JSON 兼容的 key-value 对应的数据。数据是 schema-free 的,你不需要在每个 `AVObject` 上提前指定存在哪些键,只要直接设定对应的 key-value 即可。
例外:「豆瓣FM」等产品名词,按照官方所定义的格式书写。
中文与数字之间需要增加空格
正确:
> 今天出去买菜花了 5000 元。
错误:
> 今天出去买菜花了 5000元。
>
> 今天出去买菜花了5000元。
##### 数字与单位之间需要增加空格
正确:
> 我家的光纤入屋宽带有 10 Gbps,SSD 一共有 20 TB
错误:
> 我家的光纤入屋宽带有 10Gbps,SSD 一共有 20TB
例外:度数/百分比与数字之间不需要增加空格:
正确:
> 角度为 90° 的角,就是直角。
>
> 新 MacBook Pro 有 15% 的 CPU 性能提升。
错误:
> 角度为 90 ° 的角,就是直角。
>
> 新 MacBook Pro 有 15 % 的 CPU 性能提升。
##### 全角标点与其他字符之间不加空格
正确:
> 刚刚买了一部 iPhone,好开心!
错误:
> 刚刚买了一部 iPhone ,好开心!
>
> 刚刚买了一部 iPhone, 好开心!
##### 用 `text-spacing` 来挽救?
CSS Text Module Level 4 的 [`text-spacing`](https://www.w3.org/TR/css-text-4/#text-spacing-property) 和 Microsoft 的 [`-ms-text-autospace`](https://msdn.microsoft.com/library/ms531164(v=vs.85).aspx) 可以实现自动为中英文之间增加空白。不过目前并未普及,另外在其他应用场景,例如 macOS、iOS、Windows 等用户界面目前并不存在这个特性,所以请继续保持随手加空格的习惯。
#### 标点符号
##### 不重复使用标点符号
虽然中国大陆的标点符号用法允许重复使用标点符号,但是这么做会破坏句子的美观性。
正确:
> 德国队竟然战胜了巴西队!
>
> 她竟然对你说「喵」?!
错误:
> 德国队竟然战胜了巴西队!!
>
> 德国队竟然战胜了巴西队!!!!!!!!
>
> 她竟然对你说「喵」??!!
>
> 她竟然对你说「喵」?!?!??!!
#### 全角和半角
不明白什么是全角(全形)与半角(半形)符号?请查看维基百科条目『[全角和半角](https://zh.wikipedia.org/wiki/全形和半形)』。
##### 使用全角中文标点
正确:
> 嗨!你知道嘛?今天前台的小妹跟我说「喵」了哎!
>
> 核磁共振成像(NMRI)是什么原理都不知道?JFGI!
错误:
> 嗨! 你知道嘛? 今天前台的小妹跟我说 "喵" 了哎!
>
> 嗨!你知道嘛?今天前台的小妹跟我说"喵"了哎!
>
> 核磁共振成像 (NMRI) 是什么原理都不知道? JFGI!
>
> 核磁共振成像(NMRI)是什么原理都不知道?JFGI!
例外:中文句子内夹有英文书籍名、报刊名时,不应借用中文书名号,应以英文斜体表示。
##### 数字使用半角字符
正确:
> 这个蛋糕只卖 1000 元。
错误:
> 这个蛋糕只卖 1000 元。
例外:在设计稿、宣传海报中如出现极少量数字的情形时,为方便文字对齐,是可以使用全角数字的。
#### 遇到完整的英文整句、特殊名词,其内容使用半角标点
正确:
> 乔布斯那句话是怎么说的?「Stay hungry, stay foolish.」
>
> 推荐你阅读 *Hackers & Painters: Big Ideas from the Computer Age*,非常地有趣。
错误:
> 乔布斯那句话是怎么说的?「Stay hungry,stay foolish。」
>
> 推荐你阅读《Hackers&Painters:Big Ideas from the Computer Age》,非常的有趣。
#### 名词
##### 专有名词使用正确的大小写
大小写相关用法原属于英文书写范畴,不属于本 wiki 讨论内容,在这里只对部分易错用法进行简述。
正确:
> 使用 GitHub 登录
>
> 我们的客户有 GitHub、Foursquare、Microsoft Corporation、Google、Facebook, Inc.。
错误:
> 使用 github 登录
>
> 使用 GITHUB 登录
>
> 使用 Github 登录
>
> 使用 gitHub 登录
>
> 使用 gイんĤЦ8 登录
>
> 我们的客户有 github、foursquare、microsoft corporation、google、facebook, inc.。
>
> 我们的客户有 GITHUB、FOURSQUARE、MICROSOFT CORPORATION、GOOGLE、FACEBOOK, INC.。
>
> 我们的客户有 Github、FourSquare、MicroSoft Corporation、Google、FaceBook, Inc.。
>
> 我们的客户有 gitHub、fourSquare、microSoft Corporation、google、faceBook, Inc.。
>
> 我们的客户有 gイんĤЦ8、キouЯƧquムгє、๓เςг๏ร๏Ŧt ς๏гק๏гคtเ๏ภn、900913、ƒ4ᄃëв๏๏к, IПᄃ.。
注意:当网页中需要配合整体视觉风格而出现全部大写/小写的情形,HTML 中请使用标淮的大小写规范进行书写;并通过 `text-transform: uppercase;`/`text-transform: lowercase;` 对表现形式进行定义。
##### 不要使用不地道的缩写
正确:
> 我们需要一位熟悉 TypeScript、HTML5,至少理解一种框架(如 React、Next.js)的前端开发者。
错误:
> 我们需要一位熟悉 Ts、h5,至少理解一种框架(如 RJS、nextjs)的 FED。
@@ -0,0 +1,291 @@
---
layout: doc
title: 主流网站主页布局展示内容及渲染方式调研
date: 2024-08-03
tags:
- 设计
head:
- - meta
- name: keywords
content: 设计 html 布局
editLink: false
lastUpdated: true
---
对于大部分信息展示网站中都是通过大块的区域内容展示来展现更多信息,并且国内的对应的网站相对较老,所以UI设计以及UI风格上更加的质朴
如:
<img src="/posts/2024/08/10002/image-20240709144917869.png" alt="/posts/2024/08/10002/image-20240709144917869.png" />
![image-20240709145013421](/posts/2024/08/10002/image-20240709145013421.png)
![image-20240709145115262](/posts/2024/08/10002/image-20240709145115262.png)
根据几个常见的网址论坛可以看出,对于信息的展示主要通过以下几种方式:
## **标签(板块)**
![image-20240709150825480](/posts/2024/08/10002/image-20240709150825480.png)
![image-20240709151217902](/posts/2024/08/10002/image-20240709151217902.png)
![image-20240709151251709](/posts/2024/08/10002/image-20240709151251709.png)
![image-20240709151345083](/posts/2024/08/10002/image-20240709151345083.png)
![image-20240709151437319](/posts/2024/08/10002/image-20240709151437319.png)
![image-20240709151552809](/posts/2024/08/10002/image-20240709151552809.png)
![image-20240709151640633](/posts/2024/08/10002/image-20240709151640633.png)
![image-20240709151839807](/posts/2024/08/10002/image-20240709151839807.png)
![image-20240709151858347](/posts/2024/08/10002/image-20240709151858347.png)
![image-20240709151925258](/posts/2024/08/10002/image-20240709151925258.png)
![image-20240709151949506](/posts/2024/08/10002/image-20240709151949506.png)
![image-20240709152145202](/posts/2024/08/10002/image-20240709152145202.png)
![image-20240709152228527](/posts/2024/08/10002/image-20240709152228527.png)
![image-20240709152344199](/posts/2024/08/10002/image-20240709152344199.png)
大多数论坛网站中在主页显眼位置,主要方便用户快速进入不同的模块或专题,将网站的主要内容和业务展示到用户最先注意到的范围内。
## 轮播图
![image-20240709153709220](/posts/2024/08/10002/image-20240709153709220.png)
![image-20240709153737574](/posts/2024/08/10002/image-20240709153737574.png)
![image-20240709153808702](/posts/2024/08/10002/image-20240709153808702.png)
![image-20240709153845136](/posts/2024/08/10002/image-20240709153845136.png)
![image-20240709153901153](/posts/2024/08/10002/image-20240709153901153.png)
![image-20240709153924475](/posts/2024/08/10002/image-20240709153924475.png)
相对于在通过标签直接进行展示,轮播图的使用在整体调研的网站中占比较低,并且多为较为老旧的UI,并且现在的大环境下用户只想了解希望得到的信息,找到目标信息后,点击查看,得到想要的信息,离开网站。
所以相对来说目前轮播图所能传递给用户的信息相对比较有限,可以不用于主要信息的展示
## 排行(热门)
![image-20240709171101927](/posts/2024/08/10002/image-20240709171101927.png)
![image-20240709171148293](/posts/2024/08/10002/image-20240709171148293.png)
![image-20240709171249182](/posts/2024/08/10002/image-20240709171249182.png)
![image-20240709171310667](/posts/2024/08/10002/image-20240709171310667.png)
![image-20240709171406819](/posts/2024/08/10002/image-20240709171406819.png)
![image-20240709171423701](/posts/2024/08/10002/image-20240709171423701.png)
![image-20240709171439358](/posts/2024/08/10002/image-20240709171439358.png)
![image-20240709171502006](/posts/2024/08/10002/image-20240709171502006.png)
![image-20240709171529013](/posts/2024/08/10002/image-20240709171529013.png)
![image-20240709171610508](/posts/2024/08/10002/image-20240709171610508.png)
最新、最近回复、热门、精选、排行
这三个功能几乎是目前所有主流的信息展示平台都会具有的功能,通过用户`只希望获取有用信息`的信息,通过各种各样的排行来诱导用户点击、评论、停留
所以可以在模块中增加该功能
## 实时新闻(信息)
![image-20240709172705194](/posts/2024/08/10002/image-20240709172705194.png)
![image-20240709172741964](/posts/2024/08/10002/image-20240709172741964.png)
![image-20240709182327709](/posts/2024/08/10002/image-20240709182327709.png)
在大多数网站中该内容反而并不常见,可能对于论坛平台,更多的是需要有效的信息,而不是新的信息
## 推荐文章流
这几乎是所有论坛中一定会有,而且是必须要有的功能,如果要列出来的话,目前所调研的网站都有该功能,主要区别在于不同的网站对于信息展示的理解
主要分为以下几种展示方式:
### 列表形式
![image-20240709182940316](/posts/2024/08/10002/image-20240709182940316.png)
![image-20240709183028747](/posts/2024/08/10002/image-20240709183028747.png)
![image-20240709183038648](/posts/2024/08/10002/image-20240709183038648.png)
![image-20240709183047905](/posts/2024/08/10002/image-20240709183047905.png)
![image-20240709183111554](/posts/2024/08/10002/image-20240709183111554.png)
![image-20240709183434347](/posts/2024/08/10002/image-20240709183434347.png)
![image-20240709183450074](/posts/2024/08/10002/image-20240709183450074.png)
### 列表形式(展示更多信息)
![image-20240709183221209](/posts/2024/08/10002/image-20240709183221209.png)
--------
![image-20240709183238714](/posts/2024/08/10002/image-20240709183238714.png)
![image-20240709183253255](/posts/2024/08/10002/image-20240709183253255.png)
![image-20240709183308255](/posts/2024/08/10002/image-20240709183308255.png)
![image-20240709183338467](/posts/2024/08/10002/image-20240709183338467.png)
![image-20240709183348910](/posts/2024/08/10002/image-20240709183348910.png)
### 问答形式
![image-20240709183409479](/posts/2024/08/10002/image-20240709183409479.png)
### 卡片形式
![image-20240709183321435](/posts/2024/08/10002/image-20240709183321435.png)
### 预览形式
![image-20240709183144841](/posts/2024/08/10002/image-20240709183144841.png)
![image-20240709183507358](/posts/2024/08/10002/image-20240709183507358.png)
综合已经搜索到的信息来看,信息的展示通过内容的不同,也有不同的展示方式,大部分使用的是列表形式,并且尽量展示少的正文,而是主要展示标题(可能这种方式可以增加点击率吧)
而对于文章,主要以传递信息为主,尽可能需要用户直接可以看到一个内容的主要或大致内容,增加阅读效率,所以大多采用`预览形式`
如果对于一些问题的来说,更多的是采用问答的形式,比如比较著名的`StackOverflow`
,根据用户的回答,其他用户可以对每个回答进行评分,当新用户看到该内容是,可以更加直观了解到有多少种解决方式,并找到最推荐的解决方式。
对于不同的信息展示形式,我认为都可以采用,对于不同的模块采用不同的展示形式,不同的展示形式在不同的信息时可以体现出不同的优势
## 这些网站所使用的技术
无论是对于这些论坛网站还是那些炫酷的网站,主要使用的语言包括
| 所使用的技术 | 使用的网站数量 | |
|-------------------------------|---------|------|
| Vue | 10 | |
| Nuxt | 19 | |
| React | 8 | |
| Nextjs | 6 | |
| 其他(Jquery,Threejs,WordPress等) | 29 | |
* 如果同时使用两种技术如:Nuxt和Vue,则认为是Nuxt,不重复统计
目前来看`Nuxt`在一种开发语言中也占有一席之地,并且效果也很不错,但是在比较新的网站中如:`OpenAi`等使用Ntxtjs较多。
## 总结
针对目前的调研来看,信息的展示形式,主页面和信息页面的展示是相对比较主要的内容,对于他们的排版都可以借鉴
但我们目标并不是为了一个单一的用户服务,而是保罗万象,我们无法考虑到所有的模块,所以考虑像`豆瓣小组`
那种,让用户来创建对应的板块,根据板块的不同来对每个板块进行单独的优化。
现在无论是大站还是小站,只有一个样式或风格,无论是教程、博客、求助等等帖子都以同一种形式渲染;游戏模块、博客模块、教程模块等同样采用单一的排版布局,同一套样式布局。
我们可以解决这个问题,针对不同的板块提供不同的模板:
- 比如游戏模块可以提供背景图、轮播切换、所有元素替换为该游戏的风格;
- 对于博客模块可以对文章的渲染进行单独的优化,比如更加统一的排版、提供阅读清屏功能、屏蔽所有图片等功能;
- 对于摸鱼模块,提供每日新闻展示、热门主题展示等功能
**板块**和**板块**之间有用独立的配置和渲染效果,但是对于主页面,则采用更加简单更加现代化的布局展示,只有当进入不同的模块时才会有不同的渲染效果。
> 就像是一个商场,有的是服装店,那么他就有他独有的海报;对于酒馆,那就可以给他加更加应景的彩虹氛围灯;对于饭店,可以提供更大的位置,让他可以容纳更多食客
并且提供不同的模板后,在后期的维护中只需通过模板的改变就可以调整整体的功能或布局,而不用直接修改代码。但是相对的这会带来非常多的开发量。
## 参考的网站列表:
- https://edifis.ca/en/
- https://discoverybuildersllc.com/
- https://dgrees.studio/
- https://codeam.nl/
- https://lefruitstudio.fr/en/
- https://sandclock.madebyburo.com/
- https://euphemia.com/
- https://hotelodisej.com/
- https://www.teamgeek.io/
- https://nicolasloureiro.com/
- https://kentatoshikura.com/
- https://yoshikiokamura.art/
- https://kacper.ch/
- https://2022.ccncn.eu/
- https://www.wedesigndemand.com/
- https://plantica.net/
- https://www.dashq.io/
- https://www.artsphere.fr/
- https://the-haircut.webflow.io/
- https://hub.crunchdao.com/auth/register
- https://www.heights.agency/
- https://www.kirschberg.co.nz/work
- https://deidredriscoll.com/
- https://andpost.jp/
- https://aoba-kaikei.net/
- https://orf.sfc.keio.ac.jp/2019/
- https://www.sj33.cn/digital/wysj/202003/52607.html
- https://wickret.cuberto.com/
- https://www.hellomonday.com/
- https://www.wecargo.be/
- https://www.nikolastype.com/
- https://dennisberti.com/
- https://boy-coy.com/#contact
- https://int.pez.com/Geschichte/
- https://www.discourse.org/#
- https://www.resetera.com/
- https://dribbble.com/
- https://www.discourse.org/customers
- https://heydaystudio.fr/
- https://tilda.cc/cn/
- https://moreair.co/
- https://coolors.co/palettes/popular/bold
- https://joshcollinsworth.com/
- [Andrew McCarthy - Creative Developer & Designer, San Diego ✍](https://andrevv.com/)
- [Kyle Thacker — 设计师](http://kylethacker.com/)
- [Jim Ramsden - Designer of Internets](https://jimramsden.com/)
- [Shannon Lim](https://www.thisisveryrude.com/)
- [Advanced career for pilots on a Global Scale](https://www.pilot-republic.space/)
- [Ang Studio®](https://ang-studio.com/)
- [DES® | Design Education Series® by Obys®](https://des.obys.agency/)
- [PILOTE PARIS](https://piloteparis.com/)
- [Outsiders Fund](https://www.outsidersfund.com/)
- [Digital Design Days - Milan - 6-8 October 2024](https://ddd.live/)
- [Luis Bizarro - Creative Technologist](https://bizar.ro/)
- [Accueil - Storm Prod](https://www.stormprod.fr/)
- [Spaace | NFT Marketplace](https://spaace.io/)
- [Daylight | A More Caring Computer](https://daylightcomputer.com/)
- [Heyday Studio — Creative Studio, Branding & Places, Toulouse](https://heydaystudio.fr/)
- [Eva Sánchez - Interactive Designer](https://evasanchez.info/)
- [Flow/Digital Production House](https://www.justgowiththeflow.com/)
- [Oscar Pico — Digital Designer](https://www.oscarpico.es/project-page/viadomo-deco)
- [Augen — Pro Human](https://augen.pro/)
- [SPARKK | Web Agency and Website Creation in Nantes](https://sparkk.fr/en)
- [诺基亚 3310 eng](https://legendary3310.tilda.ws/en)
- [Ronin161 - D I G I T A L . G A N G](https://www.ronin161.com/)
- [TRIONN | Crafting Award-Winning Digital Journeys.](https://trionn.com/)
- [Pon The Store](https://pontheonlinestore.com/)
- [创建无偿的网站. 介绍TILDA PUBLISHING网站建设者](https://tilda.cc/cn/)
- [检查您自己的IP地址和DNS地址](https://whoer.net/zh)
- [PC游戏下载|PC破解游戏资源下载](https://www.gamer520.com/pcgame)
- [RequestBin — Collect, inspect and debug HTTP requests and webhooks](http://requestbin.cn/)
- [JavaScript Open Source Awards - GitNation](https://osawards.com/javascript/2023)
- [我告诉你](https://next.itellyou.cn/Original/Index?id=7ab5f0cb-7607-4bbe-9e88-50716dc43de6#)
+424
View File
@@ -0,0 +1,424 @@
---
layout: doc
title: 登录功能的思考
date: 2024-08-05
tags:
- 编程
- 学习
head:
- - meta
- name: keywords
content: 登录 架构
editLink: false
lastUpdated: true
---
> 文章摘要
<!-- DESC SEP -->
这篇文章介绍了如何安全地存储用户数据,包括用户名、密码哈希、邮箱等,强调使用 bcrypt 加密密码和唯一盐来提升安全性。它还详细解释了 HTTPS 如何通过加密、身份验证和数据完整性保护来确保通信安全,并探讨了前端加密与 HTTPS 的配合使用。此外,文章还讨论了如何管理 SSL/TLS 证书、Token 存储和安全性防护策略,如防止 XSS 和 CSRF 攻击。
<!-- DESC SEP -->
## 用户信息应该保存什么数据
- `username`:唯一的用户名。
- `password_hash`:使用加密算法(如 bcrypt)对密码加密后的结果。
- `email`:用户的邮箱,作为额外的联系方式和用户名的验证。
- `created_at` 和 `updated_at`:记录用户的创建时间和最后一次更新时间。
其他:
- **Email**:用于账户验证和找回密码。
- **创建时间(created_at)**:记录账户创建时间。
- **更新时间(updated_at)**:记录账户信息的最后更新时间。
- **用户角色(role)**:标识用户身份,如普通用户、管理员等,用于权限管理。
- **状态(status)**:标识用户状态,如活跃、锁定、禁用等,用于账户管理。
- **最后登录时间(last_login_at)**:记录用户最后一次登录时间。
- **安全问题及答案**(可选):用于找回密码。
- **登录失败次数(login_attempts)**:记录用户登录失败次数,用于防止暴力破解。
- 注册时间
- 注册IP
- 上次登录时间
- 登录地址
- 用户权限
- 手机号
## 密码如何保存
`bcrypt`是由`Niels Provos`和`David Mazières`设计的密码哈希函数,他是基于Blowfish密码而来的,并于1999年在USENIX上提出。
除了加盐来抵御`rainbow table` 攻击之外,`bcrypt`
的一个非常重要的特征就是自适应性,可以保证加密的速度在一个特定的范围内,即使计算机的运算能力非常高,可以通过增加迭代次数的方式,使得加密速度变慢,从而可以抵御暴力搜索攻击。
`bcrypt`函数是`OpenBSD`和其他系统包括一些Linux发行版(如SUSE Linux)的默认密码哈希算法。
**推荐使用**每个用户都拥有一个独立的、唯一的盐。这种方式可以显著提高安全性,因为即使两个用户使用相同的密码,由于盐不同,生成的哈希值也会完全不同。
#### 生成盐
- **随机生成**:使用密码学安全的随机数生成器生成盐,例如使用 `java.security.SecureRandom` 生成盐。
- **长度建议**:一般来说,盐的长度至少为 16 字节,以增加其随机性和安全性。
#### **盐的存储**
- **单独存储**:将每个用户的盐单独存储在数据库中,与用户的其他信息(例如 `user_id`、`hashed_password`)一起存储。
#### **处理已有用户的密码**
如果需要为已有用户的密码增加盐,可以采取以下步骤:
1. **逐步迁移**:在用户下次登录时,提示用户更改密码并生成新的哈希和盐(用户更改密码时应该重新生成**盐**)。
2. **批量更新**:将所有用户密码通过一次性脚本批量更新为新的加盐哈希值。
3. **新老兼容**:在验证用户密码时,首先尝试使用老的哈希方式,如果失败则使用新的加盐哈希方式。
#### **注意事项**
- **使用安全的随机数生成器**:避免使用 `Math.random()` 这样的伪随机数生成器,而是使用 `SecureRandom` 等密码学安全的随机数生成器。
- **避免重复使用盐**:每个用户的盐都应该是唯一的,避免不同用户之间的盐重复使用。
- **保护盐的安全**:尽管盐本身不需要加密,但它仍然是安全链条中的一部分,应妥善保护,避免泄露。
## HTTPS 如何保证数据安全
**HTTPS**(Hypertext Transfer Protocol Secure)是一种用于在客户端和服务器之间安全传输数据的协议。
> http是无状态的超文本传输协议,连接简单,信息是明文传输,端口为80。
> https协议是由http+ssl协议构建的可进行加密传输、身份认证的具有安全性网络协议,端口是443。
>
> https通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。
**加密方式**:
> HTTPS 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证信息传输过程的效率。非对称加密只作用在证书验证阶段。
**HTTPS的缺点**:
> 1)因为需要进行加密解密等过程,因此速度会更慢;
> 2)需要支付证书授权的高额费用。
**交互过程**:
客户端发起 HTTPS
请求,服务端返回证书,客户端对证书进行验证,验证通过后本地生成用于改造对称加密算法的随机数,通过证书中的公钥对随机数进行加密传输到服务端,服务端接收后通过私钥解密得到随机数,之后的数据交互通过对称加密算法进行加解密。
1. 客户端向服务器发起HTTPS请求,连接到服务器的443端口。
2. 服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人。
3. 服务器将自己的公钥发送给客户端。
4. 客户端收到服务器端的公钥之后,会对公钥进行检查,验证其合法性,如果发现发现公钥有问题,那么HTTPS传输就无法继续。严格的说,这里应该是验证
5. 务器发送的数字证书的合法性,关于客户端如何验证数字证书的合法性,下文会进行说明。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为client
key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。
6. 客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。
7. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
8. 然后服务器将加密后的密文发送给客户端。
9. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。
![image-20240709145013421](/posts/2024/08/img-1.webp)
它通过以下方式保证数据的安全性:
**数据加密**
- **加密数据传输**:HTTPS 使用 TLS(Transport Layer Security)协议对数据进行加密,确保传输中的数据无法被窃听或篡改。
- **对称加密**:在 TLS 连接建立后,双方使用对称加密算法(如 AES)加密传输的数据,速度快且效率高。
**身份验证**
- **服务器认证**:通过 SSL/TLS 证书,客户端可以验证服务器的身份,防止中间人攻击。
- **防止钓鱼网站**:用户可以确认访问的服务器是否为合法的服务器,避免被引导至钓鱼网站。
**数据完整性**
- **防止数据篡改**:通过哈希函数和 MAC(Message Authentication Code),确保数据在传输过程中未被篡改。
## 是否可以不依赖 HTTPS,仅依赖前端加密来保证安全性?
#### 安全性的局限
即使前端使用了加密技术,如果没有 HTTPS 作为底层保护,仍然存在以下安全隐患:
- **中间人攻击(MITM)**:攻击者可以拦截和修改前端与后端之间的通信内容,即使加密了密码,也可能无法防止攻击者篡改其他请求参数或注入恶意代码。
- **公钥篡改**:如果攻击者能够篡改你的网站或应用中的公钥,他们可以用自己的公钥替换你网站的公钥,从而获得解密私钥。
- **数据完整性**:没有 HTTPS 的保护,传输的数据可能被篡改,而不仅仅是密码,还包括其他敏感信息。
#### 攻击者可以如何篡改公钥?
公钥篡改主要涉及以下几种方式:
**中间人攻击(MITM)**
- **工作原理**:攻击者拦截用户和服务器之间的通信,并在其间插入自己的公钥。
- **实现方式**:攻击者可以通过DNS劫持、网络劫持或伪造Wi-Fi热点等方式拦截流量并插入恶意公钥。
**服务器端攻击**
- **访问服务器**:攻击者通过漏洞或其他方式入侵你的服务器,替换服务器上的公钥。
- **后果**:用户使用篡改后的公钥进行加密,攻击者可以用对应的私钥解密。
**前端代码篡改**
- **篡改代码**:攻击者可以通过入侵 CDN、服务器或开发者的本地机器,修改前端代码,替换其中的公钥。
- **发布渠道**:通过不安全的渠道发布应用,导致篡改的公钥传播给用户。
#### HTTPS 与前端加密的协作
为了最大限度地确保数据安全,推荐将前端加密与 HTTPS 结合使用:
- **HTTPS 传输**:通过 HTTPS 确保整个传输通道的安全性。
- **前端加密**:在前端对敏感数据(如密码)进行加密,作为额外的安全保障。
- **多层保护**:即使 HTTPS 被攻破,前端加密也能提供另一层保护。
## 前端如何防止公钥被篡改?
**使用 HTTPS 和证书验证**
- **确保公钥安全传输**:通过 HTTPS 传输公钥,防止中间人攻击。
- **使用可信证书**:通过可信的 CA 签发的证书确保公钥的合法性。
**内容安全策略(CSP)**
> CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。
- **定义内容来源**:使用 CSP 限制加载的资源,防止加载恶意的脚本和公钥。
- **用途**
:用于检测并削弱某些特定类型的攻击,包括跨站脚本([XSS](https://developer.mozilla.org/zh-CN/docs/Glossary/Cross-site_scripting)
)和数据注入攻击等。
- **示例配置**:
```javascript
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com
```
**前端代码完整性校验**
- **Subresource Integrity (SRI)**:使用 SRI 确保加载的前端资源(如 JS 脚本)未被篡改。
```html
<script src="https://trusted.cdn.com/script.js" integrity="sha384-..."></script>
```
- **代码签名**:对前端代码进行签名,用户可以验证下载的代码是否被篡改。
**前端代码加密**
- **代码混淆**:使用代码混淆工具增加代码的复杂性,防止反编译和分析。比如,Webpack、UglifyJS 等工具可以对 JavaScript 代码进行混淆。
- **加密重要数据**:将敏感数据和密钥加密存储在前端,并通过运行时解密使用。
**安全加载资源**
- **内容安全策略(CSP)**:使用 CSP 限制加载的脚本和资源来源,防止恶意代码篡改。
- **子资源完整性(SRI)**:通过 SRI 验证加载资源的完整性,确保资源未被篡改。
## 前端应该如何存储公钥?
**存储在代码中**
- **嵌入代码**:将公钥硬编码在前端代码中,这是最常见的做法,但容易被静态分析工具提取。
- **示例**:
```javascript
const publicKey = `-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----`;
```
**动态获取**
- **通过 HTTPS 获取**:从服务器动态获取公钥,并通过 HTTPS 确保传输安全。
- **防止缓存攻击**:设置合适的缓存策略,防止恶意缓存替换公钥。
**加密存储**
- **加密存储**:将公钥加密存储在前端,启动时解密使用。
- **密钥管理**:需要安全管理用于解密公钥的密钥。
**使用 CDN**:将公钥存储在可信的 CDN 上,并通过 HTTPS 获取,确保前端的公钥安全更新。
## 如何保证后端私钥的安全存储?
**私钥存储位置**
- **硬编码的风险**:将密钥直接硬编码在代码中非常危险,攻击者可以通过反编译或分析代码轻松获取密钥。
- **硬件安全模块(HSM)**:HSM 是专门设计用于存储密钥的设备,具有极高的安全性和防篡改特性。它能生成和存储密钥,并执行加密运算,极大减少私钥泄露的风险。
- **云密钥管理服务**:如 AWS KMS、Azure Key Vault、Google Cloud KMS 等,提供安全的密钥管理和加密服务,可以将密钥存储在云端,避免密钥管理的复杂性和潜在的安全风险。
- **安全文件存储**:如果无法使用 HSM 或云服务,可以将私钥存储在服务器的安全文件系统中,严格控制访问权限(如仅允许特定用户和进程访问)。
- **环境变量**:
- **安全存储**:将密钥或密钥文件路径存储在环境变量中,避免硬编码在代码中。
- **安全读取**:通过环境变量读取密钥,并确保环境变量的访问权限受控。
- **加密密钥管理库**:
- **专用库**:如 JCE(Java Cryptography Extension)、OpenSSL、bcrypt 等,可以提供密钥加密、解密和存储功能。
- **加密和解密流程**:利用这些库,密钥可以在需要时解密使用,避免直接暴露在代码中。
- **密钥保护库**:
- **使用库和框架**:使用像 HashiCorp Vault、Azure Key Vault 等专用库和框架来管理和保护密钥。
- **密钥存储服务**:利用操作系统或平台提供的密钥存储服务,如 macOS 的钥匙串、Windows 的 DPAPI(数据保护 API)等。
**安全更新和备份**
- **定期更新**:定期更换私钥,并确保旧密钥不再使用,减少长期使用同一密钥的风险。
- **安全备份**:将私钥备份在安全的地方,并确保备份数据的加密和访问控制。
## 如何保证公钥和私钥的安全更新?
**使用可信工具**:使用 OpenSSL、Java KeyTool 等工具生成密钥,确保密钥生成过程的安全性
**密钥替换周期**
- **定期轮换**:根据安全需求和行业标准,定期更换密钥。通常建议每 1-2 年更换一次,但对于高风险的应用,可以更频繁地更换。
- **紧急更换**:在检测到潜在的安全威胁或密钥泄露时,立即更换密钥。
**自动化密钥管理**
- **自动化工具**:使用工具和脚本自动化密钥生成、分发和更新流程,减少人为错误。
- **密钥版本管理**:实现密钥版本控制,每次更新都生成一个新版本,逐步替换旧版本,并确保新旧密钥的兼容性。
**密钥更新通知**
- **前端同步更新**:后端密钥更新后,通过安全的通知机制将新公钥分发到前端,前端在下一次更新时使用新公钥。
- **密钥有效期**:使用密钥的有效期管理机制,确保在旧密钥失效前及时更换新密钥。
## 登录标志(Token)
#### 推荐的存储方式
1. **使用 Cookie 存储 Token**
- **HttpOnly 和 Secure**:设置 HttpOnly 属性,防止 JavaScript 读取。设置 Secure 属性,仅允许在 HTTPS 连接中传输。
- **SameSite 属性**:设置为 `Strict` 或 `Lax`,防止 CSRF 攻击。
> **什么是 CSRF 攻击**?
>
> 这一天,小明同学百无聊赖地刷着Gmail邮件。大部分都是没营养的通知、验证码、聊天记录之类。但有一封邮件引起了小明的注意:
>
> > 甩卖比特币,一个只要998!!
>
> 聪明的小明当然知道这种肯定是骗子,但还是抱着好奇的态度点了进去(请勿模仿)。果然,这只是一个什么都没有的空白页面,小明失望的关闭了页面。一切似乎什么都没有发生……
>
>
在这平静的外表之下,黑客的攻击已然得手。小明的Gmail中,被偷偷设置了一个过滤规则,这个规则使得所有的邮件都会被自动转发到hacker@hackermail.com。小明还在继续刷着邮件,殊不知他的邮件正在一封封地,如脱缰的野马一般地,持续不断地向着黑客的邮箱转发而去。
>
> 不久之后的一天,小明发现自己的域名已经被转让了。懵懂的小明以为是域名到期自己忘了续费,直到有一天,对方开出了 $650
的赎回价码,小明才开始觉得不太对劲。
>
> 小明仔细查了下域名的转让,对方是拥有自己的验证码的,而域名的验证码只存在于自己的邮箱里面。小明回想起那天奇怪的链接,打开后重新查看了“空白页”的源码:
>
> ```javascript
> <form method="POST" action="https://mail.google.com/mail/h/ewt1jmuj4ddv/?v=prf" enctype="multipart/form-data">
> <input type="hidden" name="cf2_emc" value="true"/>
> <input type="hidden" name="cf2_email" value="hacker@hakermail.com"/>
> .....
> <input type="hidden" name="irf" value="on"/>
> <input type="hidden" name="nvp_bu_cftb" value="Create Filter"/>
> </form>
> <script>
> document.forms[0].submit();
> </script>
> ```
>
> > 这个页面只要打开,就会向Gmail发送一个post请求。请求中,执行了“Create Filter”命令,将所有的邮件,转发到“hacker@hackermail.com”。
> >
> > 小明由于刚刚就登陆了Gmail,所以这个请求发送时,携带着小明的登录凭证(Cookie),Gmail的后台接收到请求,验证了确实有小明的登录凭证,于是成功给小明配置了过滤器。
> >
> > 黑客可以查看小明的所有邮件,包括邮件里的域名验证码等隐私信息。拿到验证码之后,黑客就可以要求域名服务商把域名重置给自己。
>
> 小明很快打开Gmail,找到了那条过滤器,将其删除。然而,已经泄露的邮件,已经被转让的域名,再也无法挽回了……
>
> 以上就是小明的悲惨遭遇。而“点开一个黑客的链接,所有邮件都被窃取”这种事情并不是杜撰的,此事件原型是2007年Gmail的CSRF漏洞:
>
> [https://www.davidairey.com/google-Gmail-security-hijack/](https://www.davidairey.com/google-gmail-security-hijack/)
>
> 当然,目前此漏洞已被Gmail修复
2. **结合使用 LocalStorage 和内存**
- **LocalStorage**:用于存储 Token 的加密副本,用于持久化和恢复。
- **内存**:在运行时将 Token 加载到内存中,减少对 LocalStorage 的访问。
#### Token 存储时机
1. **登录成功后**:
- 在用户成功登录后,从服务器接收到 Token,并在前端存储。
2. **Token 刷新时**:
- 在 Token 即将过期时,通过刷新 Token API 获取新的 Token,并更新存储。
#### 携带 Token 的时机
1. **自动登录**:
- 在页面加载时检查 Token 是否存在并有效。
- 如果有效,自动发送 Token 进行身份验证。
2. **每次 API 请求**:
- 每次向受保护的 API 发送请求时,携带 Token 进行身份验证。
3. **页面重载**:
- 当页面重载或导航时,从存储中读取 Token 并加载到内存中。
#### Token 的携带方式
1. **HTTP Header**
- 最常用的方法是在请求头中携带 Token,通常使用 `Authorization` 头。
- 示例代码:
```javascript
javascript复制代码// 使用 Fetch API 发送请求
fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token
}
}).then(response => response.json())
.then(data => console.log(data));
```
2. **URL 参数**
- 一般不推荐,因为 URL 参数容易泄露和被篡改。
3. **Cookie**
- 在请求时,浏览器会自动携带符合条件的 Cookie。
- 需要确保 Cookie 设置了 Secure、HttpOnly 和 SameSite 属性。
### 安全性考虑
#### 防范 XSS 攻击
- **内容安全策略(CSP)**:通过 CSP 限制加载的脚本和资源来源,减少 XSS 攻击的风险。
- **输入验证**:对所有用户输入进行严格的验证和过滤,防止恶意代码注入。
#### 防范 CSRF 攻击
- **SameSite Cookie 属性**:设置为 `Strict` 或 `Lax`,限制跨站请求。
- **CSRF Token**:为每个请求生成一个唯一的 CSRF Token,并验证 Token 的有效性。
#### 防止 Token 泄露
- **HTTPS 加密**:确保所有 Token 传输都通过 HTTPS,加密传输的数据。
- **限制 Token 使用**:设置 Token 的作用域和权限,最小化 Token 被滥用的风险。
- **定期更新 Token**:定期刷新和更新 Token,减少长期暴露的风险。
## SSL/TLS 证书的设置和申请
**什么是 SSL/TLS 证书**
- **SSL/TLS 证书**:是一种数字证书,用于验证服务器的身份,并在客户端和服务器之间建立安全的加密连接。
- **证书内容**:包括服务器的公钥、服务器身份信息、证书签发机构(CA)的签名等。
**SSL/TLS 证书的类型**
- **自签名证书**:由自己签发的证书,适合测试或内部使用,但不适合公众访问的站点,因为它不受信任的 CA 机构的签名。
- **由可信 CA 签发的证书**:由公共信任的证书颁发机构(如 Let's Encrypt、DigiCert 等)签发,适合公开访问的站点。
**如何申请 SSL/TLS 证书**
**申请免费证书(例如 Let's Encrypt)**:
1. **选择证书颁发机构(CA)**:例如 Let's Encrypt,它提供免费的 SSL/TLS 证书。
2. **安装证书管理工具**:例如 Certbot,这是一个常用的自动化工具,用于申请和管理 Let's Encrypt 证书。
3. **生成和配置证书**:
- 运行 Certbot 并按照指示生成证书。
- 配置 Web 服务器(如 Apache 或 Nginx)使用生成的证书。