提交文章

This commit is contained in:
jimmy committed 2025-01-05 22:35:53 +08:00
1 parent cc7ce5cc81
commit c5f3e9ad2e
161 files changed
+3847 -84

No files matched your search

@@ -0,0 +1,516 @@
## 写在前面
本文主要介绍关于`JS`或`Vue`中如何进行表头,列固定,可以根据实际应用场景应用于`原生`,`Vue`,`移动端`,`小程序`中
实际效果展示:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8418a181ceda6f119f0d4bdd883dfec1.gif#pic_center =500x500)
对于列的固定,`table`中有对应的方法,但是如果列和表头都要固定,只能通过其他方式实现,<span style="color:red;">如果您找到了更好的自身方法,还请斧正</span>
## 思路概述
`表头,列头,表格本身,这三个内容被分为了三块元素,将这三个内容进行抽离,并且通过定位和大盒子的包裹进行样式管理。`
【当要进行滚动时,只要对滚动事件进行绑定,让`表头和列头显示对应的内容就完成了滚动的操作`】
下面有`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`和`小程序中`,二者都是可以进行数据的绑定,元素的循环处理更加方法,小程序中没有鼠标,可能要更换为手指滑动操作,对于这个逻辑相同,`绑定元素的移动距离`,从而让表头和列头跟随移动从而达到同步的效果和目的
`本文仅代表本人粗鄙的拙见,如果有疏忽和纰漏,还请大佬斧正`
-20
View File
@@ -1,20 +0,0 @@
---
date: '2025-01-05T19:33:58+08:00'
draft: false
title: 'First'
cover:
image: img.webp
alt: 'this is a post image'
caption: 'this is a caption'
# 为文章添加分类标签
tags: ['html','css']
categories: ["tech"]
#layout: "search" # necessary for search
#ShowToc: true
# 多个作者
author: ["Me", "You"]
---
你好
-8
View File
@@ -1,8 +0,0 @@
---
date: '2025-01-05T19:33:58+08:00'
draft: false
title: 'First'
---
这是测试的额例子
-37
View File
@@ -1,37 +0,0 @@
---
date: '2025-01-05T19:33:58+08:00'
draft: false
title: 'First'
# 文章置顶
weight: 1
keywords: ['页面']
---
这是测试的额例子123123
![img.webp](/img.webp)
再次测试
```js
console.log("你好,世界!")
```
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Example HTML5 Document</title>
<meta
name="description"
content="Sample article showcasing basic Markdown syntax and formatting for HTML elements."
/>
</head>
<body>
<p>Test</p>
</body>
</html>
```
@@ -0,0 +1,304 @@
---
date: '2025-01-05T19:33:58+08:00'
draft: false
title: '中西文混排时汉字与拉丁字母之间差异'
keywords: ['中西文混排时汉字与拉丁字母之间差异']
tags: ['设计']
categories: ["设计"]
---
[//]: # (# 文档编号:10001)
[//]: # (## 中英文混排时汉字与拉丁字母之间是否要有空格)
>
> 有研究显示,打字的时候不喜欢在中文和英文之间加空格的人,感情路都走得很辛苦,有七成的比例会在 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,338 @@
---
date: '2025-01-05T19:33:58+08:00'
draft: false
title: '主流网站主页布局展示内容及渲染方式调研'
keywords: ['主流网站主页布局展示内容及渲染方式调研']
tags: ['设计','html']
categories: ["设计"]
---
[//]: # (# 文档编号:10002)
对于大部分信息展示网站中都是通过大块的区域内容展示来展现更多信息,并且国内的对应的网站相对较老,所以UI设计以及UI风格上更加的质朴
如:
<img src="/10002/image-20240709144917869.png" alt="/10002/image-20240709144917869" />
![image-20240709145013421](/10002/image-20240709145013421.png)
![image-20240709145115262](/10002/image-20240709145115262.png)
根据几个常见的网址论坛可以看出,对于信息的展示主要通过以下几种方式:
## **标签(板块)**
![image-20240709150825480](/10002/image-20240709150825480.png)
![image-20240709151217902](/10002/image-20240709151217902.png)
![image-20240709151251709](/10002/image-20240709151251709.png)
![image-20240709151345083](/10002/image-20240709151345083.png)
![image-20240709151437319](/10002/image-20240709151437319.png)
![image-20240709151552809](/10002/image-20240709151552809.png)
![image-20240709151640633](/10002/image-20240709151640633.png)
![image-20240709151839807](/10002/image-20240709151839807.png)
![image-20240709151858347](/10002/image-20240709151858347.png)
![image-20240709151925258](/10002/image-20240709151925258.png)
![image-20240709151949506](/10002/image-20240709151949506.png)
![image-20240709152145202](/10002/image-20240709152145202.png)
![image-20240709152228527](/10002/image-20240709152228527.png)
![image-20240709152344199](/10002/image-20240709152344199.png)
大多数论坛网站中在主页显眼位置,主要方便用户快速进入不同的模块或专题,将网站的主要内容和业务展示到用户最先注意到的范围内。
## 轮播图
![image-20240709153709220](/10002/image-20240709153709220.png)
![image-20240709153737574](/10002/image-20240709153737574.png)
![image-20240709153808702](/10002/image-20240709153808702.png)
![image-20240709153845136](/10002/image-20240709153845136.png)
![image-20240709153901153](/10002/image-20240709153901153.png)
![image-20240709153924475](/10002/image-20240709153924475.png)
相对于在通过标签直接进行展示,轮播图的使用在整体调研的网站中占比较低,并且多为较为老旧的UI,并且现在的大环境下用户只想了解希望得到的信息,找到目标信息后,点击查看,得到想要的信息,离开网站。
所以相对来说目前轮播图所能传递给用户的信息相对比较有限,可以不用于主要信息的展示
## 排行(热门)
![image-20240709171101927](/10002/image-20240709171101927.png)
![image-20240709171148293](/10002/image-20240709171148293.png)
![image-20240709171249182](/10002/image-20240709171249182.png)
![image-20240709171310667](/10002/image-20240709171310667.png)
![image-20240709171406819](/10002/image-20240709171406819.png)
![image-20240709171423701](/10002/image-20240709171423701.png)
![image-20240709171439358](/10002/image-20240709171439358.png)
![image-20240709171502006](/10002/image-20240709171502006.png)
![image-20240709171529013](/10002/image-20240709171529013.png)
![image-20240709171610508](/10002/image-20240709171610508.png)
最新、最近回复、热门、精选、排行
这三个功能几乎是目前所有主流的信息展示平台都会具有的功能,通过用户`只希望获取有用信息`的信息,通过各种各样的排行来诱导用户点击、评论、停留
所以可以在模块中增加该功能
## 实时新闻(信息)
![image-20240709172705194](/10002/image-20240709172705194.png)
![image-20240709172741964](/10002/image-20240709172741964.png)
![image-20240709182327709](/10002/image-20240709182327709.png)
在大多数网站中该内容反而并不常见,可能对于论坛平台,更多的是需要有效的信息,而不是新的信息
## 推荐文章流
这几乎是所有论坛中一定会有,而且是必须要有的功能,如果要列出来的话,目前所调研的网站都有该功能,主要区别在于不同的网站对于信息展示的理解
主要分为以下几种展示方式:
### 列表形式
![image-20240709182940316](/10002/image-20240709182940316.png)
![image-20240709183028747](/10002/image-20240709183028747.png)
![image-20240709183038648](/10002/image-20240709183038648.png)
![image-20240709183047905](/10002/image-20240709183047905.png)
![image-20240709183111554](/10002/image-20240709183111554.png)
![image-20240709183434347](/10002/image-20240709183434347.png)
![image-20240709183450074](/10002/image-20240709183450074.png)
### 列表形式(展示更多信息)
![image-20240709183221209](/10002/image-20240709183221209.png)
--------
![image-20240709183238714](/10002/image-20240709183238714.png)
![image-20240709183253255](/10002/image-20240709183253255.png)
![image-20240709183308255](/10002/image-20240709183308255.png)
![image-20240709183338467](/10002/image-20240709183338467.png)
![image-20240709183348910](/10002/image-20240709183348910.png)
### 问答形式
![image-20240709183409479](/10002/image-20240709183409479.png)
### 卡片形式
![image-20240709183321435](/10002/image-20240709183321435.png)
### 预览形式
![image-20240709183144841](/10002/image-20240709183144841.png)
![image-20240709183507358](/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#)
@@ -0,0 +1,93 @@
# 写在前面
本文属于对`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,65 @@
## 使用环境
> `Vue`的版本为:`3.2.36`,
>
> `vue-router`的版本为:`4.0.16`
>
> `vue-i18n`的版本为:`9.2.0-beta.40`
几乎都最新版本了
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/39a8484eb3d06ca3c791910e2524d01b.png#pic_center =700x500)
## 问题复现
使用的`element-plus`框架进行项目的搭建,在登陆的时候在全局路由中进行密码的校验,如果密码错误或正确使用`element-plus`中的提示组件登录的提示,但是提示的内容要根据选择的语言进行分别的提示
这个似乎很正常的需求,但是因为使用的`vue3`中的`setup`,并且在路由中是使用的`ts`,所以不能使用this,并且像`vue`文件中使用的引入方式:
```js
const { t, d, n, locale } = useI18n()
```
这种方式也是不行的,因为执行顺序的前后问题,在路由加载时`useI18n()`还没有引入执行,所以这种方式的引入会直接报错的
## 问题解决
因为在`.vue`文件中会使用对应的方法将对应的内容引入,但是当执行路由时这个内容并没有加载,但是这个代码一定是存在并且可以直接运行的,所以可以通过其他方式直接手动将对应需要的代码引入,而不使用提供的方法
当在创建对应的i18n时,会先将对应的内容进行创建和导出:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/41d7a0dd1e001dff0cdc90824cd4a5c4.png#pic_center =800x600)
在这个文件中创建了i18n,并且对特定的配置项进行了指定,但是导出的这个i18n中有什么?
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/df27f14f4ea749627077c95169c280d3.png#pic_center =900x800)
在配置的文件中已经存在了要使用的方法,也就是导出的:`t`,`d`,`n`等,要使用的方法也在这里面
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0f553c0064f09f20bd80cd22090c691e.png#pic_center =900x700)
在这个`global`中存在了对应的全局的方法,不需要通过i18n指定的方法进行引入执行,可以直接使用,所以这个就是要手动引入的内容
## 解决
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c891798bb88738be6f9720255427d656.png#pic_center)
使用这种方法问题得到解决,不过在尝试过程中,这种两种方式使用的i18n也是有区别的
在引入的过程中,因为方式不同,所以引入的方法其实也不是同一个
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5ff67187219ee8afdbb23ffe27c4b99b.png#pic_center)
不过在对应的注释中也能略微看出二者的区别
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/bdbd1233e2788a14e07686524723ed8c.png#pic_center)
使用方法引入的‘t’应当也有特有的内容,这里只进行列举,不继续深入探讨。
+486
View File
@@ -0,0 +1,486 @@
---
date: '2025-01-05T19:33:58+08:00'
draft: false
title: '开发语言的选择'
# 文章置顶
weight: 1
keywords: [ '开发语言的选择','html','css','react','vue','ts','kotlin','java' ]
tags: [ 'react','vue','ts','kotlin']
categories: [ 'react','vue' ]
---
[//]: # (# 文档编号:10003)
**本文主要包含:**
<div style="color:red;">
<ol>
1. 对Kotlin语言以及他为何能替代Java的解释
</ol>
<ol>
2. React是否就是前端最终归宿的讨论
</ol>
<ol>
3. TS 应该被放弃吗?为什么那些大牛和社区会放弃TS?
</ol>
<ol>
4. 为什么反对跨平台框架
</ol>
> 对于使用语言的几乎没什么可争议的,整体的框架基本都是固定的,只是细节内容的变化
>
> 比如后端一定是Java或者是GO,绝不可能使用CPP或者他的替代者Rust;
>
> 而前端因为种类繁杂,不仅有一种以虚拟Dom为开发思路的框架,如(Vue,React),也有新兴的抛弃虚拟Dom方案的框架,如(servlet),还有基于Vue或React的新框架如(Nextjs,Nust等等)
对于语言的选择主要基于:**是否被大多数人接受**,一个项目想要长久的发展,出现的一些问题希望更好的解决,选择使用大多数人常用的解决方案往往可以更快的解决问题
基于以上准则,本文主要包含以下内容:
- ###### [Java还是Kotlin](#Java还是Kotlin)
- ###### [Vue和React、Nextjs如何选择](#Vue和React、Nextjs如何选择)
- ###### [TypeScript 应该放弃吗](#TypeScript 应该放弃吗)
- **[为什么不使用跨平台框架](#为什么不使用跨平台框架)**
### Java还是Kotlin
-------
`Java`目前几乎是最最烂大街的语言,一个不懂代码的人,他可能不知道`JavaScript`,但是他很大概率听过`Java`,并且`Java`自`1995`年在`Sun`公司发明后一路迭代,以独有的`JVM`虚拟机在各平台相对统一的表现和兼容性,在服务端以及安卓端一路高歌,现在也几乎成为了服务器开发的首选语言(除了这两年爆火的`GO`),并且得益于`Java`的强大以及一些组织(`apache`)的卓越贡献,让`Java`在生态上也发展迅速,如(`Spring`,`MyBaits`)等,所以无论是公司还是个人,新的服务端项目选择几乎是毋庸置疑的。
而`GO`语言这两年被`Google`一直推崇,发展也非常快,但是因为是新兴语言,在可用库以及复杂问题的解决方案上相对更加欠缺(我们不能浪费时间在解决非业务问题上),所在对于稳定解决方案,Java依然是首选。不过这也带来一个问题,既然`GO`都不能从`Java`那里抢到饭碗:
##### 那何为`Kotlin`,他凭什么能和`Java`相提并论?
`Kotlin`是`JetBrains`公司(就是开发`IDEA`的那个`JetBrains`)在`2011年`推出,并随后开源
2017年`Google I/O`大会上,`Google`宣布将对`Kotlin`提供一流支持
2019 年 5 月 7 日,`Google `宣布 `Kotlin `编程语言现在是 `Android `应用开发者的首选语言(原来是`Java`)
`Kotlin `被设计为一种工业级的面向对象语言,并且是比 `Java `“更好的语言”,但仍然与 `Java `代码完全互操作,被称之为 Android 世界的`Swift`
优势:
- 简洁: 大大减少样板代码的数量。
- 安全: 避免空指针异常等整个类的错误,旨在解决[十亿美元错误]([Null pointer - Wikipedia](https://en.wikipedia.org/wiki/Null_pointer#History))。
- 互操作性: 充分利用 JVM、Android 和浏览器的现有库。
- 工具友好: 可用任何 Java IDE 或者使用命令行构建。
所以,`Kotlin`是基于`Java`的衍生语言,并且比`Java`更高级,所以他优化了很多`Java`所容易出现的问题,如:大量模板代码、经常需要处理空指针异常、线程切换等问题。因为他更加高级所以也可以根据编译输出为其他语言,如:`JavaScript`等。由于`Kotlin`是基于`Java`完成的,所以他完全兼容`Java`,可以互相调用,这也就是说`Kotlin`可以兼容一些支持`Java`的库
根据`StatckOverflow`的数据:
![image-20240626111035291](/10003/image-20240626111035291-1719371456917-1-1719371462042-3.png)
![image-20240626111351542](/10003/image-20240626111351542-1719371636684-5.png)
`Kotlin`整体的期望相对`Java`更高,但在使用量上依然有一段差距,但整体来看差距不是很大
并且目前`Java`中的常用库,如`Spring`、`Spring Boot`对于`Kotlin`也有原生支持:[创建使用 Kotlin 的 Spring Boot 项目 · Kotlin 官方文档 中文版 (kotlincn.net)](https://book.kotlincn.net/text/jvm-create-project-with-spring-boot.html)
![image-20240626112220073](/10003/image-20240626112220073-1719372143155-7.png)
综上,在常规的`Java`后台项目中,`Kotlin`相对能提供更加简洁的语法,相对更少的代码量以及同等量级的社区支持,在综合方面可以和传统`Java`一较高低
### Vue和React、Nextjs如何选择
--------
`Vue` 在大前端时代,在国内市场可谓是大杀特杀,几乎以压倒性的优势占领着国内市场,并且随着 2020 年`Vue3`的发布,以及`Vite`的推出,其生态更加完善,基本是国内市场做前端的首选语言。
但和国内相悖的是,在`JetBrains`与`StackOverflow`在2023年的数据报告中,在前端方面数据,`Node`和`React`以压倒性优势领先其他语言
![2023 JetBrains开发者调查报告](1/10003/image-20240626141050935-1719382253511-9.png)
![StackOverflow 2023调查报告](10/10003/image-20240626141144499.png)
2023JavaScript趋势报告:
![image-20240626143412891](/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](/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。他可以让你进行最大限度的定制
### TypeScript 应该放弃吗
--------
抛出这个问题主要基于22年和23年发生的一些事件:
<img src="1001%20-%20%E6%88%91%E4%BB%AC%E5%BA%94%E8%AF%A5%E9%80%89%E6%8B%A9%E4%BB%80%E4%B9%88%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80.assets/up-58c4155f83bbd826f4bb0ec1ab8d1e32ccd-1719469395020-15-1719469397190-17.png" alt="img" style="zoom:67%;" />
<img src="1001%20-%20%E6%88%91%E4%BB%AC%E5%BA%94%E8%AF%A5%E9%80%89%E6%8B%A9%E4%BB%80%E4%B9%88%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80.assets/eed5a2dd0f71b8b4790a1b20c708416c-1719469475131-21.png" alt="图片" />
并且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](/10003/image-20240627144850640.png)
并且在知乎对于这个问题的评价更加激进:
![image-20240627143223214](/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 反而在某种程度还更能节约代码量了
### 为什么不使用跨平台框架
##### 跨平台还是原原生?
​ 如果我作为一个项目经理或技术总监,我现在收到了上级的要求,要我的团队做一款茶品出来,要有Web端,Android端,IOS端,外加微信小程序,京东小程序,支付宝小程序。最好再带上桌面端(Exe程序),并且最好在尽量短的时间内完成。
​ 这种情况下我绝对会选择一款跨平台的框架,有很多框架可以选择比如 Flutter 、Koltin、React Native 他们的初衷就是为了一端多用而诞生,为了兼容小程序可能会使用 Uniapp 选择会非常多。
但是这完全是为了做产品做做出的选择,为了满足快速的迭代,为了最快的满足最低要求做出的抉择,并且这更像是为了满足那些资本家更快占领市场所做的选择,无论如何,这绝不应该是第一选择。
##### 为什么反对跨平台
1. **跨平台永远落后**
比如在Android的原生开发中,如果想要使用官方提供的新推出的组件库,直接引入就好了,并且官方提供支持,如果是跨平台的话,则需要等待平台支持后才能使用,并且使用往往会更加麻烦
2. **无法使用原生的设计思路**
对于不同的平台,设计思路是完全不同的,比如Web端,Android端,IOS端 。
> Web端更考虑的时候灵活和在不同页面的统一表现,往往是为了方便用户而出现的,并且得益于浏览器的统一实现,任何设备都可以随时随地访问
>
> Android 端更多的是考虑安全性 、隐私性,系统提供的组件更加的风格化,统一化,并且可以用到更多的系统特性,如GPS,相机,本地文件等可以
>
> IOS端和Android类似,但是整体更加统一,动画的一致性效果也是最好的,并且同样可以更方便的访问相机、GPS、本地文件等内容
他们三者都有各自的特点,没有任何一端的设计语言可以完全照搬照抄,如果跨平台,那么他几乎要放弃所有的优化,抛弃除了方便之外的所有优势,并且也更难实现组件风格统一。
3. **兼容性**
如果是同一平台,夸设备主要包括不同设备和不同软件版本的兼容,但是往往针对这些,官方都会给出一些适配方式或替代方案
但是对于跨平台来说,几乎会将设备内容限制在一个固定的范围,并且很难向老的设备兼容,因为这意味着更多的投入,甚至压根无法完成
4. **长期维护和扩展**
这一点毋庸置疑,一定是原生会有更好的兼容和适配。
5. **一致的展示效果**
魔鬼在于细节。iOS 和 Android 等本机平台已经发展了很长时间。
成熟的工具已经解决了新框架甚至没有遇到的问题。复杂性的假象实际上是解决困难用例的成熟度。
> 不同的平台的语言有各自的设计思路,并且经过十几年的发展相对更加稳固,体会不一样的平台,反而能体验更多不同的设计思路和设计方式。
针对跨平台框架的讨论国内和国外的开发者都有比较多的文章可以查到
![image-20240627164520013](/10003/image-20240627164520013.png)
#### 为什么要做一个自己的网站
无论是在公司还是在原先的项目中,无论是谁都很难做到全栈,但是现在几乎所有的环节都已经打通,不为他人而工作,做一个自己的东西出来,可以熟悉各端的开发方式,也能在项目中加入一些功能,自己访问也更加方便,并且因为是自己的东西,所以所有的在网上或者任何平台看到的任何好的设计和内容都可以直接“借鉴”到网站中。
这样也不至于工作了多年,为公司做了很多很好的东西,最棒的思路都用到了公司,反而没有做一个自己想做的东西。
并且真的有一天因为各种原因离职了,简历的呈现方式绝对不是 PDF 了,完全可以是一个网址。
#### 它的最终样子会是如何
我也无法设想他以后的发展,就眼前来说,会先依据**网络论坛**作为一个基础,来先做出基础功能,这个论坛类似于豆瓣的小组,百度的贴吧,但要做的并不是和他们这些大公司竞争,而是做一个平台出来,只是一小部分人使用,如果能有更高的人气和注册量当然更好。依托这个网站平台,可以添加更多想做的东西出来,比如博客,以及在其他任何网页上所有看起来不错的CSS效果等
并且他可以做各种想做的东西,任何其他类型的网站也都可以在该平台完成
> 本文参考的文章如下:
> [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)
+412
View File
@@ -0,0 +1,412 @@
---
date: '2025-01-05T19:33:58+08:00'
draft: false
title: '登录功能的思考'
# 为文章添加分类标签
tags: ['think']
categories: ["think"]
keywords: ['登录功能']
---
## 用户信息应该保存什么数据
- `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传输完成。
<img src="/10004/sp5zj58b89.webp" alt="img" style="zoom:80%;" />
它通过以下方式保证数据的安全性:
**数据加密**
- **加密数据传输**: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)使用生成的证书。