水笔

181 阅读7分钟

一. git命令

1. git stash

场景

当你处于某种状态的时候,你会发现有一些上游的变化可能与正在做的事情有关。当您的本地更改不会与上游的更改冲突时,简单的git pull将让您向前。但是,有些情况下,本地更改与上游更改相冲突,git pull拒绝覆盖您的更改。 在这种情况下,您可以将更改隐藏起来,执行git pull,然后解压缩。

使用
  1. 存储:当要记录工作目录和索引的当前状态,但想要返回到干净的工作目录时,则使用git stash。 该命令保存本地修改,并恢复工作目录以匹配HEAD提交。可以多次git stash 以此保存多个状态(git stash 以栈的形式进行存储)
  2. 取出:git stash apply命令, git会默认使用最近的储藏并应用它(栈的先进后出),如果存储了多个状态,可以采用 git stash list 查看所有状态, 使用git stash apply stash@{index} 应用你想要的状态。
  3. 移除:git stash drop stash@{index}或者是 git stash pop stash@{index}。将储藏内容移出栈。
疑惑

1.在不同分支下是否能获取到所有的stash并取出应用?

可以,但是应该没有这样的应用场景,常态应该是git pull获取。

2.git stash 所有操作都是遵循栈操作的(先进后出),不指定名称时候都会取最近的一次记录。

Git官网git stash git stash教程

二. antd在平常业务中(react)

1.业务问题

(1)单个输入框(input, textArea等)的校验

// 点击行为触发
const onClick = () => {
  if(value不满足校验){
    Toast提示 || 自定义校验文本
  }
}

// change时候触发:在setValue时候做处理
const onChange = (value) => {
  if(value不满足校验){
    return 
  }
  setValue(value)
}

// change时候触发:在setValue中的第二种情况
// 该方法很难用(尽量别用),例:禁止输入空格校验,在Mac原声输入法输入中文时有奇奇怪怪bug,搜狗不存在。
// 上述bug参考compositionstart 与 compositionend事件
const onChange = (value) => {
  setValue(value.replace(不满足条件的文本, ''))
}

// 可定义触发时机:加Form组件控制
const 校验方法 = (_, value) => {
  if(value不满足校验){
    return Promise.reject('错误文本')
  }
  return Promise.resolve()
}
'
<Form>
  <Form.Item rules=[{validator: 校验方法}]>
    <Input>
  <Form.Item>
</Form>
'

(2) 表格的编辑与选中操作(纯前端操作)

先选中某条记录后,再去修改该条记录的字段,记得同步修改选中记录变量的值。

(3)Tab实现原理

(4)Drawer实现原理

(5)Popover实现原理

(6)Table中selectRowKey原理

(7)Form表单中Form.list中的Form.Item中name为数组时,如何监听其中某一个item的变化,来渲染不同的页面

三. react与vue中diff算法的区别

react中diff算法

在react文档中提到,即使是最前沿的算法,要完全对比两棵树的算法的算法复杂度也是O(n 3 ),其中n代表树中元素数量。

为了降低算法复杂度,react中diff算法设定了3条限制

1.只对同一层dom节点进行对比,如果同一个dom节点在前后虚拟dom树中层级不一致出现跨级现象,react将不会尝试进行dom复用。

2.对于dom类型改变的节点,react会删除之前的dom节点与其子孙节点,建立一个新的dom节点与其子孙节点,而不是复用。

// 之前的dom
<div>你好啊</div>

// 更新后的dom
<p>你好啊</p>

// dom节点的类型不一致,react会直接创建更新后的dom,删除之前的dom

3.react用户可以通过定义key属性,来告诉react需要保持某些元素的稳定,key会明确告诉react dom节点的顺序。

// 之前dom
<div key='1'>你好啊</div>
<p key='2'>react</p>

// 更新后dom
<p key='2'>react</p>
<div key='1'>你好啊</div>

// 显然按照限制2来说的 更新后的p 与 div 标签都是重新创建的,
但是加key属性后,react将通过顺序对比.
react 将遍历更新后的dom 与之前dom相对比
第一次:之前dom存在key='2'的节点并且类型内容一致 可复用
第二次:之前dom存在key='1'的节点并且类型内容一致 可复用
通过上面的步骤。react将实现复用两个节点只是移动了他们的位置,而不是创建与销毁。

我们可以通过同级节点的数量将diff划分为两类

1.节点为object, number, string类型。(节点数量唯一)

2.节点为array。(节点数量大于一)

对于object,number,string类型(单节点)

(以object为例,react对于object,number,string类型有着不同的处理)对于单节点类型,diff算法首先判断前后dom是否存在key,要是key不相同,进行摧毁之前dom创建新dom,要是key相同,判断type是否相同,type相同进行复用,type不同摧毁之前dom,创建新dom。

对于array类型(多节点)

对于多节点更新,遍历每个节点只有三种类型

1.更新后删除节点(delete)

2.更新后添加了节点 (add)

3.更新后只更新节点(update)

// 删除节点
// 之前dom
<span>1<span>
// 更新后dom
<div>1</div> // 之前dom删除
  
// 添加节点
// 之前dom
<span>1<span>
// 更新后dom
<span>1<span> // 复用之前节点
<span>2<span> // 为添加节点
  
// 更新节点
// 之前dom
<span>1<span>
// 更新后dom
<span>2</span> // 可复用节点

react 中diff算法存在两轮遍历

1.第一轮遍历:遍历newDomList,用newDomList[i]与oldDomList[i]进行对比,相同的话oldDomList[i]进行复用,如果不相同立马结束第一轮遍历。

2.第二轮遍历:对于第二轮遍历我们可以将完成第一轮遍历后newDomList与oldDomList分成以下情况:

(1)newDomList与oldDomList都遍历完成——意味着只存在组件更新。

(2)newDomList遍历完,oldDomList未遍历完成——意味着old中有些节点被删除。

(3)newDomList没遍历完,oldDomList遍历完——意味着new中有新的节点添加。

(4)newDomList与oldDomList都没有遍历完——这是diff中最复杂精髓的地方。

针对第二轮遍历的第四种情况diff的处理逻辑为下:

1.将第一轮遍历中最后一个被复用的节点的index赋值给lastReplaceIndex变量。新的newDomList为剩余未复用的节点记为restNewDom,新的oldDomList为剩余未复用的节点记为restOldDom。

2.遍历restNewDom,在restOldDom中查找与restNewDom[i]相同的节点,又分为以下情况

(1)如果restOldDom中存在则复用(打上update标签):

如果该节点在oldDomList的index大于等于lastReplaceIndex,那么进行赋值lastReplaceIndex = 该节点在oldDomList的index,如果该节点在oldDomList的index小于lastReplaceIndex,将该节点右移,进入下一个节点的对比。

(2)如果restOldDom中不存在则添加(打上add标签):进行下一个节点的对比。

3.当restNewDom遍历完成后,将对OldDomList进行遍历,将OldDomList中存在,但是newDomList中不存在的节点删除(打上delete 标签)。

通过上述操作,对于第二轮遍历中newDomList与oldDomList都没有遍历完的情况就已经解决了。

练习

// 为简洁表示,我们将数字12345代表不同key值的节点
oldDomList = 12345
newDomList = 16324

// 第一轮遍历
// 剩余的oldDomList与newDomList分别为2345,5324
restOldDom = 2345
restNewDom = 6324
lastReplaceIndex = 0

// 进入第二轮遍历
// restNewDom第一个节点对比
// 6不存在于restOldDom 打上add标签 restNewDom为324
restNewDom = 324

// restNewDom下一个节点对比
// 3存在于restOldDom中 且在oldDomList的index为2
// 因为2>lastReplaceIndex 所以lastReplaceIndex = 2
// 给该节点打上update标签可复用 restNewDom为24
restNewDom = 24

// restNewDom下一个节点对比
// 2存在于restOldDom中 且在oldDomList的index为1
// 因为1<lastReplaceIndex 所以该节点右移
// 给该节点打上update标签可复用 restNewDom为4
restNewDom = 4

// restNewDom下一个节点对比
// 4存在于restOldDom中 且在oldDomList的index为3
//  因为3>lastReplaceIndex 所以lastReplaceIndex = 3
// 给该节点打上update标签复用 restNewDom遍历完成

// 对oldDomList遍历查找newDomList中不存在的key
// 发现5节点未存在打上delete标签

// 此时我们发现 从12345 变为 16324 react的操作为:
// 复用1节点,生成6节点,复用3节点,将2节点复用并且右移,4节点复用,删除5节点。

react中diff中还有很多细节,比如说对于单节点中object,string,number类型的不同处理方式,生成虚拟dom节点时候的各种属性的含义等等