shuangmianxiaoQ / study-note

日常学习或工作笔记
6 stars 1 forks source link

利用栈模拟实现浏览器导航的前进后退 #43

Open shuangmianxiaoQ opened 5 years ago

shuangmianxiaoQ commented 5 years ago

最近做需求时有一个模仿在线Windows文件加的需求,难度不大,在模拟导航时发现了一个巧妙的方法,记录一下(下面是在线的完整Demo)。

Edit 仿windows文件夹

大致效果图如下:

image

模拟栈数据结构

模拟导航前进后退时,一开始想到的办法是设置两个状态,去根据条件判断什么时候能前进或后退,但是这个条件可能不太好找,写起来也啰嗦。于是在谷歌上搜索了一番,看到这个关键词,突发奇想,浏览器的前进后退不就是用栈结构进行入栈出栈来实现的嘛,那么先来看看怎么使用JS实现栈数据结构:

// 模拟一个简单的栈
class Stack {
  constructor() {
    this.stack = [];
    this.action = "";
  }

  push(...items) {
    this.action = "PUSH";
    return this.stack.push(...items);
  }

  pop() {
    this.action = "POP";
    return this.stack.pop();
  }
}

使用栈

上面这个构造函数很简单,就是设置两个状态,一个记录栈的内容,一个记录栈操作(入栈和出栈),那么后面就是使用这个栈了,就是在点击文件夹或点击导航时进行入栈和出栈操作即可

state = {
  stack: new Stack()
};

pushToStack = () => {
  this.state.stack.push(1);
  this.setState(() => ({ stack: this.state.stack }));
};

popFromStack = () => {
  this.state.stack.pop();
  this.setState(() => ({ stack: this.state.stack }));
};

实现前进后退功能

最后就是完成能否前进后退的功能了,根据简单的推演,可以得出下面的结论(下面isPrev表示能否后退,isNext表示能否前进): 初始状态 -> isPrev: false, isNext: false 入栈 -> 压入值为新值时,isPrev: true, isNext: false;压入之前存在的值,isPrev: true, isNext: true 出栈 -> 栈不空时,isPrev: true, isNext: true;栈空时,isPrev: false, isNext: true

下面是转换状态的一个简单实现:

// value 是上面的栈实例
const computedStatus = value => {
  const { stack, action } = value;

  let status = { isPrev: false, isNext: false };

  if (action === "PUSH") {
    status = { isPrev: true, isNext: false };
  } else if (action === "POP") {
    status =
      stack.length === 0
        ? { isPrev: false, isNext: true }
        : { isPrev: true, isNext: true };
  }

  return status;
};

分割线

经过实践之后,发现上面的入栈出栈方式并不能很好的解决导航前进后退,反而是将问题复杂化了。 在同事的指导下,采用了另一种思路,巧妙地解决了问题:

  1. 还是设置一个栈结构,并设置一个指针来指向当前位置
  2. 打开文件夹时进行入栈操作,并将指针指向尾元素
  3. 后退时不要进行出栈操作,只将栈指针迁移即可;前进时则需要将指针往前移动
  4. 比较特殊的是,如果后退之后重新打开另一个文件夹,则需要先将指针位置后的元素删除再进行入栈

下面是大致的栈数据结构:

栈结构

最后判断能否前进后退也很简单。如果指针指向头位置则导航不能后退,如果指针指向尾位置则导航不能前进

判断能否前进后退