0%

全书的确包含了很多他自己的“偏见”,但我觉得他真实幽默,语言简单易懂,其实各个小标题就是告诉我们该做什么。

懂生活

生活从来不是你想怎样就能变成怎样的。生活中多发现美,多从他人角度看待事情,是好的。同时自己还要有自知之明。

懂爱情

谢耳朵说:人穷尽一生追寻另一个人类共度一生的事,我一直无法理解。或许我自己太有意思,无需他人陪伴。所以我祝你们在对方身上得到的快乐,与我给自己的一样多。
靠另一个人永远无法让你摆脱孤独,只能靠自己对生活的热情。经营一段感情要的是理智,自由是不可剥夺的。这让我想起对前任,我曾经有查看过他的手机,我觉得挺不对,以后不会再出现这种事情了。

懂兵法

主要就是介绍女追男或男追女的技巧,以及如何经营好一段感情,就是大智慧。同时千万别因为家庭放弃自己的事业,不要跟公公婆婆住一起(但我的观点还是觉得因人而异),要有属于自己的兴趣爱好。

懂婚姻

忍,忠一。对于不婚主义,当然也没什么想多说的了。

懂失意

每一次失恋,只不过是在为真爱让路。还是那句勉强不来,我爱你,所以我祝你一生如意,即使陪着你的人,不是我。
做自己想做的事,不要管他人怎么想。只关心自己在乎的人。

懂阅读

What Why How。一直阅读下去,不要求快,要求精。

懂情趣

介绍了他与妻子的日常 sweet,告诉我们不分手,就不知道世界上还有那么多人值得我们去爱。如果找到了,是我的幸运,如果找不到,我也有能力让自己快乐地活着。
高情商地活着,会发觉世界是那么美好。

懂交际

跟正经人交往,跟不同人要有不同方式去与之交往,拒绝别人也是一门艺术。如果没有厚脸皮去找别人还钱,干脆别借。

懂人性

只能说我可能在人性方面持怀疑态度,管不了别人,那么就只能管好自己,别做个恶俗的人。

懂善恶

有些人在你生命中出现,就是为了给你上一课。何为善何为恶,我宁愿相信他人的话,因为人与人之间的信任本来就是有一方愿意相信,另一方才会不负此信。
即使被骗,也是告诉我们下次同样的坑,别再跳了。但别因为入了一次坑,就不再相信了,善人始终存在。
上帝让右手成为右手,就是对右手最高的奖赏;同理,上帝让善人成为善人,也就是对善人的最高奖赏。
这让我想起,有次在外,洗手间里没有纸巾,原本是想问旁边的好朋友有没有纸巾的,结果旁边一位陌生女子听见了,立刻拿出包纸巾给我,此等善举,让我倍感荣幸。
而善良还需要有原则,否则害人害己。

懂财富

穷人之所以穷,是思维的匮乏。要想富起来,不能只在自己身体上投资,而要在大局上有思维的开拓。看透问题的本质。
时间是有限,在有限时间内如何快速致富?找到一门自己感兴趣的学科,学以致用,发散思维,与社会息息相关的行业,去做。

懂社会

要有抱负,一步一个脚印。要有自己独立的人格,要能为自己所做的事情负责。学会转化一种让自己幸福的能力,然后改变自己。

懂生死

生死学里几个词:无死不生,向死而生,知死而后生。颇为有趣且发人深思。人就是因为知道有一天会死去,才会争取好好地活着。
我想,真到了那天,我会主动拥抱死亡。

这世界上缺的不是完美的人,而是从心底给出的正义、真心、无畏和同情—这是今天看完《无问西东》后给我最感慨的话。生而为人,便要心之所向,无问西东。
大家都知道有因必有果,却不只因果皆由缘而起。叔本华跟尼采,有时间必好好读佳作。

懂远游

这部分写了琢磨先生在西藏的经历,看过很多书,都是大家想去西藏的。也许去了那里,人真的能体会到人生的不易。
美国不如英国自律,日本对美食态度极佳,常把“对不起”挂在嘴边,不喜欢给别人添麻烦。

懂职场

首先要懂得吃苦,然后要清楚自己,发掘自己的天赋所在。以终为始,聚焦所做。

懂教育

我只知道,每一个出生在这世上的小孩都是纯粹的,如果不好好教育,长大后便不可能为国之栋梁。

懂娱乐、懂自由

人生有两个方向:一个方向是往大处拼搏,保持对事业的激情;一个方向是往小处生活,保持做人的温度。
每天睁开眼,对自己说声:欧耶

var、let、const 三者的区别?

✅ 作用域

  1. var 定义的变量没有块作用域,可以跨块访问,但不能跨函数访问
  2. let 定义的变量有块作用域,只能在声明的代码块中访问
  3. const 定义的是常量,必须初始化,只能在声明的代码块中访问,且值不可更改

✅ 声明规则

  • 同一个变量名,只能用一种声明方式,否则会报错。

✅ 结合 this 的区别

特性 var / let / const
改变 this 指向 ✅ 可以
第一个参数是 this 要指向的对象 ✅ 是
没传对象或为 undefined/null 默认指向全局 window
参数传递 apply 用数组,call 用参数列表,bind 可多次传参
执行方式 apply/call 立即执行,bind 返回新函数
什么是事件委托?
  • 事件委托,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,而不是目标元素
  • 当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数
防抖跟节流的区别是什么?
  • 防抖:只执行最后一次。事件持续触发,但只有等事件停止触发后 n 秒后才执行函数。
  • 节流:控制执行频率。持续触发,每 n 秒执行一次函数。
Web 前端预防 SQL 注入的方法有哪些?
  1. 参数化查询:使用预处理语句或参数化查询来避免 SQL 注入。
  2. 验证用户输入:验证用户输入的数据,确保其符合预期格式,避免非法字符等。
  3. 转义特殊字符:在使用用户输入的数据构造 SQL 语句时,对特殊字符进行转义,以防止注入。
  4. 限制权限:限制用户的权限,使其不能执行不安全的 SQL 操作。
  5. 白名单验证:白名单验证是指确保用户输入的数据只能是允许的值,避免非法数据的输入。
  6. 审核代码:定期审核代码,以确保代码的安全性,特别是对于数据库连接和查询部分的代码。
http 和 https 的区别
  1. https 协议需要到 ca 申请证书,一般免费证书较少,因而需要一定费用。
  2. http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议。
  3. http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
  4. http 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。
created跟mounted的区别
  • created发生在mounted之前,此时DOM元素还没完全渲染出来,不过跟后端联调时候的请求可以放在这里,尽早获取到数据返回给变量。
  • mounted这时候已经可以获取页面中的DOM元素了。
说说事件循环机制

🔁 什么是事件循环?

JavaScript 是单线程语言,执行代码时除了依赖调用栈,还依赖任务队列来控制异步代码的执行顺序。

整个执行过程称为 事件循环(Event Loop)

🧠 事件循环的核心概念:

  • 一个线程中只有一个事件循环(主循环)。
  • 可以有多个任务队列,按类型分为:
    • 宏任务(Macro Task)
    • 微任务(Micro Task)

⏱ 执行顺序是怎样的?

执行顺序:

宏任务 ➝ 微任务(清空) ➝ 下一个宏任务 ➝ 微任务(清空)… 依此循环

  1. 执行一个宏任务(如 script 整体代码)
  2. 在当前宏任务中执行所有产生的微任务
  3. 若微任务中又产生新的微任务,继续执行,直到清空微任务队列
  4. 开始下一轮宏任务循环

🧩 宏任务(Macro Task)有哪些?

  • script(整体代码)
  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • I/O 操作
  • UI 渲染

🧬 微任务(Micro Task)有哪些?

  • process.nextTick(Node.js 专属,优先级更高)
  • Promise.then / catch / finally
  • async / await
  • MutationObserver
说说 css 盒模型

css 中的盒子模型包括 IE 盒子模型和标准的 W3C 盒子模型

在标准的盒子模型中,width 指 content 部分的宽度,box-sizing:content-box(默认为content-box);

在 IE 盒子模型中,width 指的是 content+padding+border,box-sizing:border-box;

Box-sizing:padding-box 宽度包含了左右 padding+width

vue2 和 vue3 的区别
  1. 双向数据绑定原理不同,vue2 使用 Es5 的 API defineProperty()对数据进行劫持, 只能监听某个属性,不能对整个对象监听;vue3 使用 ES6 的 proxy API 对数据代理, 可以监听整个对象和数组
  2. 生命周期不同
    vue2:beforeCreate、created 、beforeMount、mounted、beforeUpdate、updated、
    beforeDestroy、destroyed
    vue3:setup 、onBeforeMount 、 onMounted 、onBeforeUpdate 、onUpdated 、
    onBeforeUnmount、onUnmounted
  3. vue2 必须有根标签,vue3 可以没有根标签,会默认将多个根标签包裹在 fragment虚拟标签中
  4. vue2 采用选项式 API,将函数和数据统一起来处理,将功能点切割了,当逻辑复杂时不利于代码阅读;vue3 采用组合式 API,将同一个功能的代码统一起来处理,使代码更有序,有利于代码的书写和维护
  5. vue2 和 vue3 匿名插槽不一样
    具名插槽使用方式不同:vue2 使用 slot=’’,vue3 使用 v-slot:’’
    作用域插槽使用方式不同:vue2 中在父组件中使用 slot-scope=”data”从子组件获取数据,vue3 中在父组件中使用 #data 或者 #default=”{data}”获取

推荐

目录

redis-token缓存
jwt跟redis存token的区别
redis相关知识
推荐

redis-token缓存

  • redis.js创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    const redis = require("redis");

    // Redis 链接的配置内容
    const client = redis.createClient({
    socket: {
    host: '192.168.64.2', // Redis 服务器地址
    port: 6379, // Redis 端口
    },
    password: '123456' // Redis 密码
    });

    // 连接到 Redis
    client.connect()
    .then(() => {
    console.log('Connected to Redis');
    })
    .catch((err) => {
    console.error('Redis connection failed:', err);
    });

    // 处理连接错误
    client.on("error", (err) => {
    console.error('Redis error:', err);
    });

    // 定义 Redis 操作方法
    const redisOps = {
    // 创建值
    set: async (key, value, expiration = null) => {
    try {
    value = JSON.stringify(value);
    if (Number.isInteger(expiration) && expiration > 0) {
    // 如果设置了有效时间并且是整数,使用 SETEX 命令
    await client.setEx(key, expiration, value);
    } else {
    // 否则直接使用 SET 命令
    await client.set(key, value);
    }
    console.log(`Key "${key}" set successfully with expiration: ${expiration || 'no expiration'}`);
    } catch (err) {
    console.error('Error setting value:', err);
    }
    },

    // 删除值
    del: async (key) => {
    try {
    await client.del(key);
    console.log(`Key "${key}" deleted successfully`);
    } catch (err) {
    console.error('Error deleting key:', err);
    }
    },

    // 获取值
    get: async (key) => {
    const result = await client.get(key);
    console.log('99999=', result)
    try {
    return result; // 尝试解析为 JSON
    } catch (error) {
    // 如果不是 JSON 格式,直接返回原始值
    console.warn(`Warning: Value for key "${key}" is not JSON formatted.`);
    return result;
    }
    },

    // 获取键的 TTL
    ttl: async (key) => {
    try {
    const timeLeft = await client.ttl(key);
    console.log(`Key "${key}" has TTL: ${timeLeft}`);
    return timeLeft;
    } catch (err) {
    console.error('Error getting TTL:', err);
    return err;
    }
    },
    };

    module.exports = redisOps;

  • redis的使用

1
2
3
4
5
6
const redisOps = require("./redis.js");
const moment = require('moment-timezone');

let token = jwt.sign({ id: user.userid, username: user.username }, secretKey, { expiresIn: '1h' });

await redisOps.set(user.userid.toString(), token, 60 * 60)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//token校验
const verifyToken = async (ctx, next) => {
const userid = ctx.headers['cookie'];
const match = userid.match(/userid=(\d+)/);
const userId = match ? match[1] : null;

if (!userId) {
ctx.status = 401;
console.log('No token provided');
ctx.body = { message: 'No token provided' };
return;
}

try {
const startTime = moment(); // 记录开始时间
const getuserid = await redisOps.get(userId)
const endTime = moment(); // 记录结束时间
const duration = moment.duration(endTime.diff(startTime)); // 计算持续时间

console.log(`Request completed in ${duration.asMilliseconds()} ms`); // 以毫秒为单位输出耗时
if(getuserid){
await next();
}else {
throw err;
}

} catch (err) {
console.log('Token verification failed:', err.message);
ctx.status = 401;
ctx.body = { message: 'Failed to authenticate token' };
}
};

jwt跟redis存token的区别

维度 JWT Redis
性能 - 无需查询数据库或访问外部存储,验证速度较快
- 适用于简单的身份验证场景
- 需要与 Redis 服务器进行通信,可能会有网络延迟
- 适用于复杂的会话管理和存储需求

redis相关知识

  • redis本质上是一个key-value类型的内存数据库,性能最快的key-value数据库。

  • 正常情况是把数据存储在数据库 ,数据库把数据存在磁盘。但越上层的存储器存储效率越高,内存位于磁盘之上。而redis是一款基于内存的存储系统,数据都存在内存里,所以从redis读取数据会比从数据库读取要快。

  • 内存有限,存储不了太多数据。出现故障时,用主从复制、哨兵法。集群就是一套完整的redis多机解决方案。他有效解决了单机redis的所有问题。当你在集群中为某个节点配置从机的时候,主从节点间同步就是主从复制。主节点挂掉之后,从节点的选取,内部逻辑与哨兵机制相似。

  • 支持发布/订阅模式,可以作为一个简单而高效的轻量级消息代理,用于实现消息队列、实时通知等。

推荐

使用 Docker 安装 ELK Stack(ElasticSearch、Logstash、Kibana)

建议在 Docker 上安装,资源占用少。以下安装版本统一为 8.12.2,建议保持一致避免冲突。

目录

1、安装 ElasticSearch
2、安装 Logstash
3、安装 Kibana
推荐


1、安装 ElasticSearch

  • 拉取镜像:
1
docker pull elasticsearch:8.12.2
  • 启动容器:
1
docker run --name some-elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -d elasticsearch:8.12.2
  • 进入容器内部:
1
sudo docker exec -u 0 -it some-elasticsearch bash
  • 重启容器:
1
docker restart some-elasticsearch

2、安装 Logstash

  • 拉取镜像:
1
docker pull docker.elastic.co/logstash/logstash:8.12.2
  • 启动容器:
1
sudo docker run -it -p 5044:5044 -p 9600:9600 --name logstash -v /usr/share/logstash/piplines:/usr/share/logstash/config --privileged=true docker.elastic.co/logstash/logstash:8.12.2 /bin/bash
  • 使用 scp 命令将本机下载好的 jar 包(MySQL Connector)上传至虚拟机:
1
scp "/Users/Downloads/logstash-8.12.2/mysql-connector-j-8.4.0.jar" 用户名@虚拟机IP:/home
  • 再将 jar 包从虚拟机移动到 logstash 容器中:
1
docker cp ./mysql-connector-j-8.4.0.jar logstash:/usr/share/logstash
  • 进入容器内部:
1
docker exec -u 0 -it logstash bash

3、安装 Kibana

  • 拉取镜像:
1
docker pull kibana:8.12.2
  • 启动容器:
1
docker run --name some-kibana -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -d elasticsearch:8.12.2
  • 进入容器:
1
docker exec -u 0 -it some-kibana bash

附:安装 Docker、Portainer 及相关命令

1
2
3
4
5
6
7
8
9
10
11
12
sudo apt update
sudo apt install docker.io docker-compose
docker -v
sudo systemctl start docker

sudo docker search portainer
docker pull portainer/portainer
sudo docker pull portainer/portainer
sudo docker run -d --name portainerUI -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer

sudo docker start portainerUI
sudo passwd root

推荐

前端项目 GitLab CI/CD 持续集成部署指南(Mac 环境)

概要

在日常开发过程中,我们经常需要将打包后的 dist 包手动发送给后端部署,效率低且容易出错。CI(Continuous Integration,持续集成)可以自动化这一过程,本文将介绍如何通过 GitLab CI 实现持续集成部署。


一、安装 GitLab Runner

请参考官方文档:GitLab Runner 安装指南


二、本地注册 Runner

  1. 打开你的 GitLab 项目页面,进入 Settings → CI/CD,展开 Runners 部分。
  2. 记住显示的 URLtoken,用于后续注册。

执行注册命令

1
gitlab-runner register

按照提示依次输入以下信息:

  • GitLab CI Coordinator URL: https://gitlab.com
  • GitLab CI Token: xxx(从项目中复制)
  • Tags: my-tag,another-tag(自定义)
  • Description: my-runner(自定义)
  • Executor: shell(Mac 上建议选择 shell)

注册完成后,回到 GitLab CI/CD 设置页面,可看到 Runner 状态为绿色,说明已成功运行。如果不是绿色,请执行以下命令启动 Runner 服务:

1
gitlab-runner run

三、编写 .gitlab-ci.yml 文件并提交

将以下内容保存为 .gitlab-ci.yml,并 push 到 GitLab:

1
2
3
4
5
6
7
8
9
10
11
stages:
- deploy

deploy_to_test:
stage: deploy
script:
- yarn
- rm -rf dist/
- yarn build
- ls -l -t ./dist/
- rsync -avz ./dist/ root@xxx.xx.xx.xxx:/dist
  • 这段 CI 脚本会自动打包项目,并使用 rsyncdist 目录部署到远程服务器。

四、通过 Docker 实现 CI(可选)

如果需要通过 Docker 实现更完整的 CI/CD 流程,继续以下步骤:

1. 安装 Docker(Mac)

1
brew install --cask docker

2. 拉取 GitLab 镜像

1
docker pull drud/gitlab-ce:v0.29.1

3. 创建 GitLab 容器

1
docker run -d -p 8443:443 -p 8090:80 -p 8022:22 --restart always --name gitlab drud/gitlab-ce:v0.29.1

说明:

  • -p 8443:443:映射 HTTPS 端口
  • -p 8090:80:映射 HTTP 端口
  • -p 8022:22:映射 SSH 端口
  • --restart always:容器崩溃或主机重启时自动恢复
  • --name gitlab:容器命名为 gitlab

访问地址:

1
http://localhost:8090/

五、Docker 模式下的 .gitlab-ci.yml 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
variables:
TEST_NAME: "tips"
OUT_PORT: "8081"
IN_PORT: "8081"

stages:
- deploy

deploy_to_test:
stage: deploy
before_script:
- if [ $(docker ps -aq --filter name=$CI_PROJECT_NAME) ]; then docker rm -f $CI_PROJECT_NAME; fi
script:
- docker build -f Dockerfile -t $TEST_NAME:latest .
- docker run -d -p $OUT_PORT:$IN_PORT --name $TEST_NAME $TEST_NAME:latest

部署成功后,在 GitLab 的 CI/CD Pipelines 页面可看到构建过程,在 Containers 页面会生成新容器,点击对应端口即可访问部署后的页面。


推荐