前端路由hash、history的实现

前言

这个坑,已经埋在笔者的待办事项里很久,就好比一滴酱油滴在白色的桌子上,现在有时间了,很想把它“擦掉”

正文

前端路由实现有两种,一是 hash,另一种是 histroy。

hash 原本是为了定位,笔者印象中最开始是 PC 端商城的楼层,点击跳转到某一模块

history 是 HTML5 新出的 API,它属于 bom。通过 history 对象可以操作浏览器的会话历史以及向前向后跳转

笔者是 React 开发者,Vue 好久没用了,所以以下内容是以 React 的角度描写

前端开发——React 全家桶——路由采用第三方库 react-router 和—— react-router 的底层是 history

我们在写 react 的路由写法时,通常为两种,hash 和 history ,使用它的本质是 history 库

笔者在这里写个简单的

hash 模式

它的本质是通过监听hash变化事件来修改路由内容

<body>
  <a href="#/home">Home</a>
  <a href="#/user">User</a>
  <a href="#/about">About</a>
  <div id="view"></div>
</body>
<script>
  function onHashChange() {
    const view = document.getElementById("view");
    switch (location.hash) {
      case "#/home":
        view.innerHTML = "Home";
        break;
      case "#/user":
        view.innerHTML = "User";
        break;
      case "#/about":
        view.innerHTML = "About";
        break;
      default:
        view.innerHTML = "Home";
        break;
    }
  }
  window.addEventListener("hashchange", onHashChange);
</script>

hash模式

history 模式

看了 hash 模式,我们会觉得很简单,无非是监听 hash 的变化,现在我们再看 history 模式

<body>
  <a href="/home">Home</a>
  <a href="/user">User</a>
  <a href="/about">About</a>
  <div id="view"></div>
</body>
<script>
  const elements = document.querySelectorAll("a[href]");
  elements.forEach((el) =>
    el.addEventListener("click", (e) => {
      e.preventDefault();
      const test = el.getAttribute("href");
      history.pushState(null, null, el.getAttribute("href"));
      onPopState();
    })
  );

  function onPopState() {
    const view = document.getElementById("view");
    switch (location.pathname) {
      case "/home":
        view.innerHTML = "Home";
        break;
      case "/user":
        view.innerHTML = "User";
        break;
      case "/about":
        view.innerHTML = "About";
        break;
      default:
        view.innerHTML = "Home";
        break;
    }
  }
  window.addEventListener("popstate", onPopState);
</script>

无非是把 hashchange 改成了 popstate,当 history 对象变化时,会出发 onPopState 事件,而我们的 history 模式不想 hash 模式那样,点击hash 对象时(#xx)会自动监听,所以我们遍历监听路由点,点击时实现和 hash 一样的模式,即点击时往历史中添加一条记录

线上demo:

芝麻开门,显示全文!

手把手提高开发体验:dev-container

之前看过方应杭的一期工作流视频,讲他是如何配置开发环境的。讲真,这期视频给我一丝震撼,原来不用虚拟机也能在 linux 上开发,但是它的配置过于复杂,我想配置一套自己的一套开发环境

视频中的 remote-container 已经更名为 dev-container,而 0.245.2 以上版本的 dev-container 有问题,会报”An error occured setting up the container - Remote Docker”,在 Issues 中有人给出了方法是降级到 0.245.2

当降级到 0.245.2,就可以愉快的使用 dev-container 了

现在我们已经成功了 dev-container 插件

现在我们要做的是,通过 dockerfile 生成一个 linux 容器,并在这个容器中开发

小试牛刀

FROM centos:8

RUN uname -a

RUN cat /etc/os-release

我们基于 centos:8 来构建一个镜像,其中,打印系统名和查看系统

基于centos的镜像

点击添加终端,就进入容器中了,这个容器就是我们基于 vscode 的 dev-container 插件

vscode中的linux

如果只是个裸机,是万万不行的,我们需要安装一些开发环境和应用,提高我们的开发效率

更新所有包

在更新包之前,我们要先修改 centos 的镜像源

RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*

修改成国内的镜像,再更新包

RUN yum update -y

下载 vim

先下载 vim

RUN yum install vim -y

如何配置,怎么配置 vim,介于还处于新手阶段,就常规操作即可,不用配置什么

下载 oh-my-zsh

如果冒冒失用它官网的下载链接,会报错

RUN sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
# curl: (7) Failed to connect to raw.githubusercontent.com port 443: Connection refused

意思是说 Github 的 raw.githubusercontent.com 域名解析污染,访问不了

解决方法有不少,如通过修改 hosts 解决此问题,先在 ipaddress查询 raw.githubusercontent.com 的真实 IP,在修改 hosts

vim /etc/hosts

添加199.232.68.133 raw.githubusercontent.com

hosts文件

重新下载 zsh

sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

zsh安装成功

这方法只能在 linux 中使用,如果你要用 dockerfile 中修改 /etc/hosts,就行不通,因为 /etc/hosts 文件是只读的,笔者在这里花了不少时间想解决,但还是没找到解决方法

还有一种方法是修改下载源,换成国内地址

sh -c "$(curl -fsSL https://gitee.com/mirrors/oh-my-zsh/raw/master/tools/install.sh)"

并将默认命令行修改成 on-my-zsh

RUN chsh -s /bin/zsh

但这个方法需要两个前提,一是需要下载 git

RUN yum install git -y

二是因为 centos 8 系统中并未带 chsh 命令工具,会提示 chsh: command not found,原因是系统没有自带的 util-linux-user 工具包导致,所以在执行 chsh 命令前,先安装它

RUN yum install util-linux-user -y

如此,我们就配置好了一个基于 centos 的有 vim、on-my-zsh 的 linux 环境

完整的 dockerfile 如下所示:

FROM centos:8

RUN uname -a

RUN cat /etc/os-release

# 修改镜像源
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*

# 更新系统
RUN yum update -y

# vim
RUN yum install vim -y

# chsh
RUN yum install util-linux-user -y

# git
RUN yum install git -y

# oh-my-zsh
RUN yum install zsh -y
RUN sh -c "$(curl -fsSL https://gitee.com/mirrors/oh-my-zsh/raw/master/tools/install.sh)"

RUN chsh -s /bin/zsh

光有这些不够,还要下载前端环境、如 node 、pnpm,还有后端环境,笔者最近也对 ruby 很感兴趣,也会在这里安装 ruby 环境

前端环境

我们采用 nvm 来控制 node 版本

安装 nvm 的方式有很多种,像方应杭是下载源码,再拷贝到容器中,如果没有网络限制,也可以用 curl 下载等等,我采用的是 git install

ENV NVM_DIR /root/.nvm
RUN git clone https://github.com/nvm-sh/nvm.git /root/.nvm/
# RUN git checkout v0.39.3
RUN sh ${NVM_DIR}/nvm.sh &&\
	echo '' >> /root/.zshrc &&\
	echo 'export NVM_DIR="$HOME/.nvm"' >> /root/.zshrc &&\
	echo '[ -s "${NVM_DIR}/nvm.sh" ] && { source "${NVM_DIR}/nvm.sh" }' >> /root/.zshrc &&\
	echo '[ -s "${NVM_DIR}/bash_completion" ] && { source "${NVM_DIR}/bash_completion" } ' >> /root/.zshrc

再通过 nvm 下载最新版本的 node

RUN nvm install stable

但是这样会报错,说 nvm: command not found,为什么这样,不求甚解,去找另一种解决方法

笔者选择先安装个默认版本的 node 和 npm,按照这里的包管理安装

RUN dnf module install nodejs:16

默认dnf module install nodejs 会安装 v10 版本,太低了,最高 16,选新不选久

再下载 pnpm ,将其设为包管理器

RUN curl -fsSL https://get.pnpm.io/install.sh | sh -

如此一来,前端的开发环境以及 node 环境就在这个 dockerfile 中了

ruby 环境

我找的教程是这个,日本人做的,内容很齐全,按照步骤,能运行好环境

RUN dnf module install ruby:3.0 -y
RUN gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
RUN curl https://dl.yarnpkg.com/rpm/yarn.repo > /etc/yum.repos.d/yarn.repo
RUN dnf -y install ruby-devel rpm-build make gcc gcc-c++ gcc-gdb-plugin libxml2 libxml2-devel mariadb-devel zlib-devel libxslt-devel
RUN gem install nokogiri -- --use-system-libraries
RUN gem install rails --version="~>7.0"

如此一来,我们的开发环境就初步弄好了,我将它发布到 dockerhub 上,如果有需要,可以以此改造

总结

这篇文章花了我不少时间,主要很多 linux 知识点为涉猎过,之前也没在虚拟机上配置 linux 的经验。不过还好,算完成了,如此一来,开发时就可以用 vscode + linux 开发应用了,剩下的就是熟悉它

参考资料

芝麻开门,显示全文!

从面试题到插件机制的小思考

起初是因为一道面试题,这个面试题是去年遇到的,即如下代码:

class Operator {...}
var op = new Operator(1)

op.add(3).minus(2).multi(2).division(1)

写出 Operator 中的代码,当初不会写,因为完全没遇到过笔试写这类题目。当然,这也暴露了我基础薄弱的问题

现在,可以一解,其实很简单,add、minus、multi、division 都是 Operator 的方法,调用后能返回是因为返回了 this,this 指向调用者,所以还是指向实例(即 op)

class Operator {
  constructor(initial) {
    this.num = initial;
  }

  add(num) {
    this.num = this.num + num;
    return this;
  }

  minus(num) {
    this.num = this.num - num;
    return this;
  }

  multi(num) {
    this.num = this.num * num;
    return this;
  }

  division(num) {
    this.num = this.num / num;
    return this;
  }
}

var op = new Operator(1);
op.add(3).minus(2).multi(2).division(1);

注意,一定要 return,你调用一个方法,不 return,就不会有结果。一般你调用方法总是要有返回值吧

再次提醒,能链式调用的关键在于调用方法后返回 this,this 指向调用者即实例

而此类又能衍生思考一个问题,库的「插件」机制

无论是 Chrome 中的插件,还是 PhotoShop 中的插件,还是 Webpack(前端打包库) 中的插件,还是 jQuery、Axios、BetterScroll 等库的插件,在写应用程序时,为了扩展性,我们都会使用“插件思维”,把核心的功能实现出来,再通过插件机制来扩展自身

多说无益,如果使用插件机制来实现此功能,该如何改造呢?

class Operator {
  plugins = [];
  constructor(initial) {
    this.num = initial;
  }

  use(plugin) {
    this.plugins.push(plugin);
    this[plugin.name] = plugin.exec.bind(this);
  }

  // 这里加一个方法,调用显示结果
  result() {
    return this.num;
  }
}

const AddPlugin = {
  name: "add",
  exec: function (num) {
    this.num = this.num + num;
    return this;
  },
};

const minusPlugin = {
  name: "minus",
  exec: function (num) {
    this.num = this.num - num;
    return this;
  },
};

const op = new Operator(5);
op.use(AddPlugin);
op.use(minusPlugin);
op.add(5).minus(2).result(); // 8

我们打印 op 能看到方法 add、minus 都作用到实例上了

实例op

当然,这只是冰山一角,如果说到如何写库,按照现代 JavaScript 库的写法要考虑的东西还有很多,等笔者对其研究有所收获后,会写一篇

芝麻开门,显示全文!

记录服务器内存爆炸

最近在准备全栈项目,本想拿以前的项目练练手,一测试,发现有bug。遂修改代码,推到远程仓库,登录服务器,拉代码时,报错,说error: unable to create temporary file: No space left on device,意思是说「无法创建临时文件:设备上没有剩余空间」

纳尼~~

我的小服务器上都没挂什么服务啊,为什么会没内存了

不管怎么样,先查一下

有人也遇到这样的问题:解决Linux出现“cannot create temp file for here-document: No space left on device”的问题

解决方案:

1、df -h 查看硬盘空间

2、top 查看cpu及内存

3、du -h –max-depth=1 /var/log/* 查看/var/log路径下文件的大小

4、du -sh /* 查看哪个目录最大

5、cat /dev/null > /var/log/mongodb/mongod.log 清空mongodb日志文件

使用 df -h 查看硬盘的空间,发现全被/dev/vda1 占据了

查看硬盘空间

而后面又有个 overlay,也是 40G,不明白没关系,继续往后步骤找

第二、三步没啥用,主要是第四步。现在根目录下输入du -sh * ,发现 var 目录占了36个G,不得了

查看根目录下的各文件所占内存

进入 var 目录查询du -sh *,发现 lib 目录占了 35G

var目录下的各文件所占内存

继续进入 lib 目录,查询du -sh *,发现 docker 目录占了 35G。明白了,问题出在 docker 容器上,也许是镜像,也许是容器,所占的内存太大了

那就把没用的镜像和容器删除吧

删到后面发现是启动的 check 酱容器占满了内存,删除了这个容器后,内存就恢复了

删除内存吸盘后的内存

check 酱或许是写入了什么错误日志之类的,导致占满了内存。想想,也就不用

为此,也不去纠结,再说了,这个服务个人感觉并不是很好用,老是报错

学到的东西

主要是 linux 的操作

例如:

  • df -h:查看硬盘空间
  • du -sh *: 查看哪个目录最大

芝麻开门,显示全文!

Docker 内部DNS解析失败

最近在玩 Easy 的开源应用:check 酱,按照它的教程,我启动了云端检测,但出问题了

监听报错

提问 Easy

提问 Easy

笔者比较菜,不知道他说的 「DNS 解析有问题」 指的是什么?

我当时的理解是 DNS 不是和域名有关吗,我外网能访问啊

后来他重要的话说三次之后,我思考了下,想着是我容器内部的 DNS 解析失败,一查,果然是这样

docker容器中ping不通

在容器中,能 ping 的通 ip,但是不能 ping 域名

找了一圈,有让我重启 docker 服务的,有说让我修改/etc/docker/daemon.json 添加 dns 解析的,还有一些指导,但都无济于事

到最后想到用英文解决,一搜”Docker containers can’t resolve DNS on centos“

答案就有了——Containers cannot resolve DNS in CentOS

原来是防火墙做了拦截,只要把 docker 添加为防火墙的信任接口中就能解决

firewall-cmd --permanent --zone=trusted --add-interface=docker0 # 添加 docker 至信任接口
firewall-cmd --reload # 重载

ping通了

如此就解决了,果然,还是要靠英文啊

Check 酱能做什么

check 酱是一个通用网页内容监控工具,可以监测网页内容变化,并发送异动到微信

如果在云端架上服务,那么只要检测数据,满足条件后的就会通知到我的微信

我会拿它干什么呢?

我觉得最好的用处就是检测加密货币的数据,当满足某个价格,某个指标后,提醒自己

减少看盘,回归生活才是正道

check 酱的 B 站教程合集:https://www.bilibili.com/video/BV1JY4y1P71b

付费课程:https://next.ftqq.com/31

芝麻开门,显示全文!

网页长按保存及识别二维码

网易哒哒的 H5 一向是业界精品,其中不少 H5 会成为爆款,能在朋友圈广泛流传的那种。同时,他们还写了一本很水的书——制造爆款:H5营销策划一本通,草草介绍了下各式各样的H5,但相关的技术文章却都没这么介绍,笔者一直想仿做类似的H5,找来找去,也只找到两篇网易同厂的技术类文章:

就此,笔者做个简易H5,介绍一下个人认为H5中比较重要的功能点——长按保存图片及识别二维码

此项目主要用到三个库

前期准备

设计稿:魔改公司H5中的其中一页

字体:寻找开源字体,这款不错——LXGW WenKai / 霞鹜文楷

此外,就是布局,笔者在移动端法门:自适应方案和高清方案 中阐述过一个观点:

不同的布局方式作用不同,像新闻类的H5,采用 px 为单位,是为了让大手机看到更多的信息;像应用型的H5,采用 rem/vw 为单位,力求在各种手机上能保持一致UI

像营销页面,是希望在各种手机上保持UI一致,理论上采用 rem/vw 是没问题的,但是 ggvswild 在高质量前端快照方案:来自页面的「自拍」 中曾说:

为了给到html2canvas明确的整数计算值,避免因小数舍入而导致的拉伸模糊,建议将布局中使用中使用%vwvhrem等单位的元素样式,统一改为使用px

而笔者在实际项目开发时,采用 rem 为单位并没有发现拉伸模糊问题。除此之外,笔者又寻找了几个网易的 H5

个人总结:在布局上它们都使用绝对定位布局,在长度单位上各有特色,所以做 H5 布局是无所谓用那种方式,只要在截图页不让元素拉伸即可,也就是说如果拉伸模糊了,可查一下此元素的单位是否是小数,至于其他页的布局,习惯用那个就用那个

实战开始

字体的运用

字体「 霞鹜文楷」大约4.4M,太大了,用 fontmin 提取用到的字体,这里我直接使用 Fontmin 的客户端,无它,命令行执行出错,营销页只用到了9个汉字,裁剪后从4.4M减少到 44kb

二维码功能的实现

很简单,看文档就能学会

var qrcode = document.getElementById("qrcode");
new QRCode(qrcode, {
  width: 200,
  height: 200,
  colorDark: "#000000",
  colorLight: "#ffffff",
  correctLevel: QRCode.CorrectLevel.L,
}).makeCode(window.location.href);

快照实现

将 html2canvas 和 canvas2image 结合,将 HTML 转成 base64 图片,而这一功能可以做成一个库:

var convertToImage = (function () {
  function createBaseCanvas(scale, width, height) {
    const canvas = document.createElement("canvas");

    canvas.width = width * scale;
    canvas.height = height * scale;

    const context = canvas.getContext("2d");

    // 关闭抗锯齿
    context.mozImageSmoothingEnabled = false;
    context.webkitImageSmoothingEnabled = false;
    context.msImageSmoothingEnabled = false;
    context.imageSmoothingEnabled = false;

    context.scale(scale, scale);

    return canvas;
  }

  function convertToImage(container, options = {}) {
    const scale = window.devicePixelRatio;

    const width = container.offsetWidth;
    const height = container.offsetHeight;

    const canvas = createBaseCanvas(scale, width, height);

    const ops = {
      useCORS: true, // 如果截图的内容里有图片,解决文件跨域问题
      allowTaint: false, // 是否允许跨源图像污染画布
      ...options,
    };

    return html2canvas(container, ops).then((canvas) => {
      const imageEl = Canvas2Image.convertToPNG(
        canvas,
        canvas.width,
        canvas.height
      );
      return imageEl;
    });
  }
  return convertToImage;
})();

使用:

convertToImage(document.querySelector("#capture")).then((imageEl) => {
  document.getElementsByClassName("save")[0].appendChild(imageEl);
});

canvas2image 的坑点

  1. 最新版本(1.4.1)已支持缩放,已解决图片不清晰的问题
    • 图片不清晰以前是个大问题,不少博文都有对其说明,目前的版本没看到模糊
  2. 文档上写支持 background-image:linear-gradient(),但是如果是渐变至透明是不行的

渐变背景色

而我希望呈现这样的样式:

背景图

背景渐变方案:

background-image: linear-gradient(90deg, $white, transparent);

改成背景图:

background: url("../bg.png") no-repeat;
background-size: 100% 100%;
  1. 文字会出现位移,这个问题至今一直存在,作者也没有修复

以上就是所遇到的问题,像跨域之类的问题,随着时间的推移,文档上都有说明,已经不是什么问题

线上预览地址:这里

2023.3.14 更新

文字会出现位移小爝 给出了方案html2canvas 文字向下偏移兼容方法,主要改动源码中的两点:

// var baseline = img.offsetTop - span.offsetTop +2;
var baseline = parseInt(getComputedStyle(span, null).lineHeight, 10) - 5;
_this.ctx.textBaseline = "alphabetic";

主要是修正字体的基准位移。给我开了一个思路,找不到解决方案就直接去找源码debugger

参考资料

芝麻开门,显示全文!

工作中使用 Git 解决问题的场景

简单来说,就这七点:

  • 使用 git rebase 让提交记录更加清晰可读

  • 使用 git reflog + git reset 跳到任意 commit

  • 使用 git cherry-pick 获取指定的 commit

  • 使用 git commit —amend 更改提交内容

  • 使用 git revert 回滚某次的提交

  • 使用 git stash 来暂存文件

  • 配置 git alias 提升工作效率

使用 git rebase 让提交记录更加清晰可读

rebase 基本用法

rebase 翻译为变基,它的作用和 merge 相似,用于把一个分支的修改合并到当前分支

如下图所示,经过 rebase 后提交历史的变化情况

rebase

不明白单分支的好处,可以在看看知乎的这个问题:Git commits 历史是如何做到如此清爽的?

Vue 的作者尤雨溪就是说:多用 rebase

具体用法:

  • 基于 master 分支创建 feature 分支
  • 在 feature 分支上开发功能点
  • master 上也提交了 commit
  • 在 feature 分支上执行 git rebase master,意为以 master 分支最后的提交作为基点,逐个应用 feature 的每个更改

git rebase VS git merge

合并分支有两种,即 rebase 、merge

merge 翻译为合并,即 git merge branchname,即合并分支代码,这种方法会保存每次 commit 的,当你使用 gitk 查看时就发现好几条颜色的线

另一种是 rebase,即去除一系列的提交记录,“复制”它们,然后在另一个地方逐个放下去

所以 rebase 的优势就明了了,它能创造更清晰的提交记录

但 merge 会保留你所有的 commit 的历史时间,当开发人员一多,历史记录就会变得混乱

rebase 的交互模式

在开发中,通常会在一个分支上产生很多无效的提交,这种情况下使用 rebase 的交互模式可以把多次 commit 压缩成一次提交,得到一个干净的提交历史

# 先看提交
git log
# f9f6f3b commit 3
# 2feb45f commit 2
# 07a3cb6 commit 1
# 我们要修改 2 的话,rebase 到它的下一个 commit,这里是 1
git rebase 07a3cb6 -i
# 然后在打开的对话框里面修改,之后还要一个 rebase continue
git rebase -i <base-commit>
# 或者是 git rebase -i HEAD~2 对最近的两次 commit 进行合并

也有人称之为后悔药功能,即你无论写什么 commit,最后都可以修改,无论提交什么,都可以合并,DIY 性强

使用 git reflog + git reset 跳到任意 commit

换个说法叫时光机,即通过查找所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作),通过 reset HEAD 跳到指定 commit

git reflog
#afa2f45 HEAD@{10}: checkout: moving from 今天 to 明天
#4abcda5 HEAD@{11}: commit: 打通1800处仙窍
#de42069 HEAD@{12}: commit: 真言轮经大成
git reset HEAD@{10}
# 或者 git reset --hard afa2f45

如此一来,就回到了 afa2f45 commit 处,熟悉「时间法则」、「时光机」的人都知道,这是回到过去

使用 git cherry-pick 获取指定的 commit

意为“挑拣”提交,和 merge 合并一个分支的所有提交不同,它会获取某个分支的单个提交,并作为一个新的提交接入到当前分支上

这个需要故事背景才容易理解

张三在分支上开发功能,每个功能点提交一次 commit,共六个提交六个功能点(分别是 feature1~feature6),再回到第一个提交点,即他使用 git reset --hard feature1 跳转第一个 commit,在此基础上开发一个新功能,即 feature7,那么如果把 feature7 合并到 feature6 上怎么做?

git reflog
# git reflog 查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作)
# 找到 feature7 的 commit 4c97ff3
# 回到 feature6 的 commit cd52afc
git reset --hard cd52afc
# 使用 cherry-pick 拿到 feature7 的代码
git cherry-pick 4c97ff3

具体可看小蝌蚪的这篇 小蝌蚪传记:git 时光穿梭机—女神的侧颜 来体会一二

简单来说,你的每一次 commit,就是一次记录,可以合并到任意地方。所以开发功能点或者修复 bug 之类,尽量做到一个功能点一个 commit,方便出错时挑拣代码

使用 git commit —amend 更改提交内容

amend 的意思是修正

# 继续改动你的文件
git add .
git commit --amend --no-edit
# 你这次的改动会被添加进最近一次的 commit 中

合并到上次的 commit 中

git commit --amend:弹出让你修改内容

git commit --amend --no-edit:保持上一次的 commit 内容

PS:假如你的代码已经 push 了的话,要慎用,因为会修改提交历史。

使用 git revert 回滚某次的提交

上文提到一个回滚操作:git reset --hard xxx,能回到某次的 commit,除此之外,还有一种则是能撤销某次 commit

# 先找到你想撤销的那个 commit hash值
git log
git revert <commit-id>

这种做法会新建一条 commit 信息,来撤回之前的修改。

而 git reset 会直接提交记录退回到指定的 commit 上。

所以就个人开发或个人 feature 分支而言,可以使用 git reset 来回滚代码,但在多人协作的集成分支上,git revert 更适合。这样,提交的历史记录不会被抹去,可以安全地进行撤回

使用 git stash 来暂存文件

顾名思义,就是把本地的改动暂存起来

先了解下 git 的四大工作区域

四大工作区域

Git工作区

  • Workspace(工作区):本地电脑所见的文件和目录

  • Index/Stage(暂存区):一般存放在 .git 目录下,当你 git add 改动文件,改动的文件就放入在「暂存区」

  • Respository(本地仓库):当你 git clone 地址,就将远程仓库克隆到本地仓库。它是存在本地的版本库,其中 HEAD 指向最新放入仓库的版本。当你执行 git commit,文件改动就到本地仓库

  • Remote(远程仓库):类似 Github、Gitlab、码云等放在代码托管平台

常见的场景是你还在开发一个功能点的时候,突然有个线上 bug 需要你紧急修复,这次你可以 git commit 提交到本地仓库,后续通过 git commit --amend 继续在原 commit 上修改内容。但这里还有一种方法,即将代码存在暂存区,等 bug 修复完后,再从暂存区取出

基本命令如下:

git stash # 将本地的改动暂存
git stash save "message" # 执行存储时,添加备注
git stash pop # 应用最近一次暂存,并删除暂存记录
git stash apply #恢复最近的存储,但不会把存储从存储列表中删除,某人使用第一个存储,即 stash@{0},如果要使用其他,git stash apply stash@{$num}
git stash list # 查看 stash 了哪些存储
git stash clear #删除所有缓存的 stash
git ls-files --stage #查看 index 暂存区

配置 git alias 提升工作效率

主要是为了简化命令,它的基本用法是 git config --global alias.<简化的字符> 原始命令

如下面的例子:

git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch

当然,另一种方法是在 .gitconfig 文件中设置

[alias]
st = status -sb
co = checkout
br = branch
mg = merge
ci = commit
ds = diff --staged
dt = difftool
mt = mergetool
last = log -1 HEAD
latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"
ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green)[%cn]\" --decorate --date=short
hist = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(reset)%s %C(green)[%an] %C(blue)%ad\" --topo-order --graph --date=short
type = cat-file -t
dump = cat-file -p
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

参考政采云的配置

除此之外

还有一些不常见却好用的命令

  • gitk:打开 git 的图形化工具
  • gitjk:撤消您刚刚在 git 中所做的操作
  • git help -g:展示帮助信息
  • cat .git/HEAD:查看分支文件
  • git fetch --all && git reset --hard origin/master:回到远程仓库的状态
    • 抛弃本地所有的修改,回到远程仓库的状态
  • git push -f origin master:强行获取远程最新代码
  • git reset --merge:merge 代码后想测回到原来版本

参考资料

芝麻开门,显示全文!

加密货币预测记录

虽然有在公众号「随朱波流」写有关理财的文章,但介于这篇文章很可能会被禁掉,所以在个人博客上做备份

最近LUNA暴跌,从200美元到归零,大概花了3天时间,一个市值高达400亿美元的币种,一个曾今进入前五,最近一年来稳坐前十的币种,就这样崩盘了,伴随而来的是加密货币的崩盘,总市值跌没了5000千万美元(一个礼拜时间)

具体事情自招财大牛猫描述的很到位:惊天动地

有一个韩国团队做的项目叫**UST,这是一个算法稳定币,1:1挂钩美元。**怎么挂钩呢,它采用预言机,通过燃烧另一个叫做LUNA的虚拟币来铸造。

比如LUNA现在价格5美元,那么要铸造1美元的UST,就要燃烧0.2个LUNA。如果LUNA涨到10美元,那就燃烧0.1个。

在这个机制下,如果想买UST的人越多,就会燃烧越多的LUNA,从而推高LUNA的价格,这个大家都能理解吧。LUNA过去一年涨了几十倍。

那怎么刺激大家来买UST呢?韩国团队搞了一个存款项目,只要你存UST进来,**每年固定给你19.8%的利息。**这个收益非常高,因此一经推出就遭到哄抢,UST的锁仓资金一路飙升,最高峰超过了300亿美元。

看到这里你一定会感到疑问,这19.8%的利息是从哪来的,谁给出?确实,由于贷款的利息不够高(12%左右),贷款资金也远远跟不上存款增长的速度,整个项目每天都净亏损几百万。去年底的时候资金池就告急了,韩国团队临时追加了4.5亿进去续命,于是才又拖了半年。

他们之所以花这个钱,是希望趁着快速扩张的阶段,尽可能的给这个UST创造更多线下应用的场景。比如韩国以及东南亚的一些自动售货机都有支持UST支付的网点,只要需求上去了,盘子就能稳住。

但无论怎么样19.8%的利息都很难维持,终于到了今年5月初,韩国团队宣布把利息下调到18%,之后每个月动态调整。他们也不敢一口气把利息降的太多,因为一旦降太多,别人不存UST了,选择出售或者兑换等值的LUNA退出,那汹涌的抛盘有可能引发负面螺旋,直接崩盘。

利息下调后的刚开始几天没啥事,但最近美联储加息,整个加密货币都跌的惨惨的,**终于在前天晚上突然有大资金集中抛售UST,把与美元的钩子打脱掉了。**恐慌逐渐发酵,韩国团队立刻调集资金护盘,想要保住挂钩。

但雪崩效应逐渐被激发,想逃命的人越来越多,抛盘一波比一波猛,韩国团队库存的20多亿资金很快就打光了,然后就是UST的汇率开始一泻千里。

截止最新情况,**UST=0.4美元,脱钩60%,LUNA更惨,2天时间从80元跌到2元。**因为这个盘子的规模很大,UST+LUNA有接近400亿美元的市值,国外很多人和机构都进行了投资,所以这次爆炸可以说是震惊了整个币圈和金融圈。

我这两天看戏看的惊心动魄,有很多韩文的帖子,我用软件翻译,大意都是我不想活了,我这辈子的积蓄都完蛋了,我的杠杆爆仓了之类的。

财富它不会凭空增长,也不会无故消失,它只是不停的在流动。

以及后续文章保大弃小

UST和美元的汇率最低到过0.2,之后触底反弹,一度涨到0.8,之后回落目前在0.6附近。韩国团队围绕UST苦心建设了一整套公链生态,自然不愿意轻易放弃,**他们想出来的办法就是超发LUNA,用这部分LUNA去兑换和烧毁市场上过剩的UST,以挽回汇率。**但LUNA就被砸的很难看,已经从80跌到2毛钱了。

其实就是保一个,弃一个。但这也是没办法的事,因为UST不恢复挂钩LUNA也是必死无疑,等到UST勾回去了,没准LUNA到时候也有机会捞上来。

以及最后的结局——湮灭

UST最新汇率是0.07,已经脱钩美元高达93%,为了拯救UST而疯狂增发的LUNA更惨,跌的只剩1分钱。目前看起来是大的没保住,小的也完蛋,死亡螺旋正在毁灭这两个一周前市值还有400亿美金的项目。

不跌还好,一跌我就来劲了,这几天没怎么写技术文章,一是因为这段时间业务多了,二是加密市场跌出了翔,我,想抄底了。

于是乎,这几天研究了一轮,再次做一下笔记更新

有人刻舟求剑的画了这样的图

加密货币未来预测

来源自推特,出自谁未知

周末看了ahr999的微博合集,很多人估计对他的囤币指标有所了解,看完之后,加深了一点我对“比特币四年一个周期”的认可度,我扒拉下最近几次加密货币的高点和低点,也来刻舟求剑预测下

历年高点时机历年低点时机时间跨度跌幅
2013年11月:¥80002015年1月:¥90014个月88.75%
2017年12月:$200002018年11月:$320011个月84%
2021年11月:$68900?

而更之前的是从32美元跌到2美元,时间不详,来源ahr999

按照时间跨度来看,下一次的低点节点在2022年的10月——2023年1月;而低点价位在7750——11024;低点的时机和跌幅应该有一定的关系。我个人觉得本轮跌幅不会像以往那么多,就像这次BTC没有如愿涨到10万美元一样

此外,我还有一个指标,就是ahr999指标,虽然他现在微博被封,但在github上能找到此前微博的记录,我看完了2020年到2021年的微博,他说的有一点我很赞同——“我们第一个周期能够囤的币,大概会等于我们这辈子能囤的币”

所以这次熊市我会看两个指标,一个是ahr999,如果跌倒0.45以下,我会进场。另一个是跌破80%,也就是13780点,我大概会在一个星期内打完所有的可投资加密货币的钱

而我会买什么币

2022年5月份的想法是

  • BTC
  • ETH
  • ADA
  • XHR

为什么要买这些币,怎么个买入策略?等有空再写吧

我希望低点时机出现在2023年的1月份,因为这个时候会发年终奖,正好可以入场

附加阅读

芝麻开门,显示全文!

Vercel 部署 Node 服务

引子

之前在写面试常客:HTTP 缓存时,曾经就强缓存和协商缓存写过两个 demo,但缓存要在服务端做,只能贴上代码,不能在网页上感受(虽然我贴了 gif)

笔者的所有 demo 例子都放在 github page 上,其特点是不需要服务器即可部署静态资源,但它不具备部署服务端应用能力

最近笔者在了解 CI/CD 方面的知识点,想起了 Vercel,就想着能否将服务端应用架在 vercel 上呢?

Vercel 是什么

Vercel 是一个开箱即用的网站托管平台,方便开发者快速部署自己的网站。它在全球都拥有 CND 节点,因此比 Github 官方自带的 github pages 更加稳定,访问速度更快

Koala 聊开源 曾经对其有过介绍:Vercel 与 Next.js:开源全明星团队背后的商业逻辑

文字版:Vercel 与 Next.js:开源全明星团队背后的商业逻辑

简单的说,它能极简部署应用到服务端,且是免费不用买服务器

官网

Vercel 官网

Vercel 工作流官网(网页效果炫酷)

常见命令行

将 Vercel 下载到全局(npm i vercel -g),不知道有什么命令就-h

vercel帮助

笔者对其了解有限,这里罗列下笔者知道的命令

  • vercel login:登录 Vercel 账号
  • vercel dev:本地开启服务
  • vercel dev --bug:本地开启服务并打印日志
  • vercel:部署本地资源到 Vercel 上
  • vercel --prod:更新本地网页

vercel 可以用 vc 来代替,vc 是 Vercel 的缩写

部署静态服务

我们现在已经对 vercel 有所了解,前文中说到 Vercel 能简化开发者部署服务,那它能简化到什么程度呢?

这里我们从零部署一个简易静态服务

本地安装 Vercel

npm i vercel -g

登录 Vercel

vercel login

vercel login

选择好连接的方式后,会在网站弹出

vercel 登录成功

哟哟,man。what’s your name?

创建一个 HTML 文件,后续我们要将其上传至 Vercel 服务器上

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vercel Demo</title>
  </head>

  <body>
    <h1>Vercel Demo</h1>
  </body>
</html>

本地测试一番,输入命令行

vercel dev

vercel dev

因为我们这是第一次执行,根目录下没有.vercel,所以要填写一些必要信息,这时你的本地和 Vercel 服务器就绑定好了

部署服务

vercel

vercel部署

https://vercel-sample-ten.vercel.app/ 中能访问到我们的静态服务

在截图中我们也看到了这句话Deployed to production. Run vercel --prod to overwrite later,后续我们要更新资源,用 vercel --prod 即可

好了,除去必要的登录,我们就用了三个命令就把本地服务部署到 Vercel 服务器上

  • vercel dev :开发时使用,查看应用是否跑得起来
  • vercel:部署服务
  • vercel --prod:更新应用(资源)

可以登录 Vercel 后台查看部署情况

vercel 后台

部署 Node 服务

回归主题,最终我们想部署的是 Node 服务,是后端服务,而非前端静态资源服务,这是关键

同样建立新项目

mkdir vercel-koa2
cd vercel-koa2
npm init -y
npm i koa -S
touch index.js

编写 index.js 中的内容

const Koa = require("koa");
const app = new Koa();

app.use(async (ctx) => {
  ctx.body = "Hello Vercel";
});

app.listen(3008, () => {
  console.log("3008项目启动");
});

PS: 3000 端口默认会被 Vercel 使用,所以 Koa 服务要换个端口

使用命令vercel dev

vercel koa dev

发现给我报错了,理由是 package.json 的 scripts 中没有 build 快捷符,修改之

...
"scripts": {
    "build": "node index.js",
},
...

再次使用 vercel dev,node 服务跑起来了

于是乎我们部署它

vercel

vercel 部署失败

搞半天没部署上去,后台查看也是无果,呜呼悲哉

google 后,发现原来还有一个 vercel.json,可以用 vercel.json 配置和覆盖 vercel 默认行为

下载 @vercel/node

npm i @vercel/node -S

填写配置:

{
  "version": 2,
  "builds": [
    {
      "src": "index.js",
      "use": "@vercel/node"
    }
  ]
}

执行 vercel 部署服务

vercel koa

访问地址:https://vercel-koa2-t511069160.vercel.app

至此,就完成了 Koa 服务的部署

与部署静态资源多了两个步骤

下载 @vercel/node 和配置 vercel.json

延伸思考

Vercel 当然不止笔者所说的这一功能,它还可以自定义域名、serverless、全球支持的 CDN 等等

可以毫不夸张地说,用 Vercel 来代替繁琐的云服务器,配合 Github Action 做 CI/CD,就

个人开发者或小团队而言,这或许就是神器

后续笔者也会尝试用 Vercel 部署一些小应用,实践出真理

附上项目地址:https://github.com/johanazhu/vercel-demo

参考资料

芝麻开门,显示全文!