Vue前端路由及异步组件

hash 和 history的区别

  1. hash 有#,history 没有,Hash式基于锚点,History 模式 基于H5的History API
  2. hash 的#部分内容不会传给服务端, history 的所有内容都会给服务端,应用在部署的时候需要注意html的访问,因为无论访问什么/a,/b,/c之类的路径,其实都要访问那个html文件
  3. hash 通过 hashchange 监听变化,history 通过 popstate 监听变化

Hash

  1. url中带有一个#,它只是浏览器/客户端的状态,不会传递给服务器,服务器拿到的都是#前面的

  2. hash值的改变不会导致页面的刷新

    location.hash = '#aaa';
    location.hash = '#bbb';
    
  3. hash值的更改可以在浏览器访问历史中添加一条记录,所以可以通过浏览器的返回前进来控制hash的切换

  4. hash值的更改会触发hashchange事件

History

window.history.pushState();// 页面的浏览记录里添加一个历史记录
window.history.replaceState(); // 替换当前历史记录
window.history.go(-1)

pushState的参数

  1. state, 是一个对象,是一个与指定网址相关的对象,当 popstate 事件触发的时候,该对象会传入回调函数
  2. title, 新页面的标题,浏览器支持不一,可以传入null
  3. url, 页面的新地址

​ 当点击浏览器的前进和后退按钮,或者调用history的back和forward方法的时候,会触发popstate事件,可以记录改变后的地址并渲染。(要注意的是,当调用pushState,或者replaceState时,并不会触发该事件)

部署时如何配置nginx

假设访问www.lubai.com/main/

  • 假设index.html 存在服务器本地(index.html文件与nginx在同一个服务器)(vue官方的写法):就可以通过try file的形式,假设访问main这个路径的话
location /main/ { // 如果是根路由可以直接 /
  try_files $uri $uri/ /home/dist/index.html 
}

​ 这样无论访问 /main/a 或 /main/b 就都会访问那个 index.html

  • 假设html文件 存在于远程地址,oss或cdn,地址为www.lubai-cdn.com/file/index.html
location /main/ { // 路径的匹配规则
  rewrite ^ /file/index.html // ^指的是全部,全部重写成这个路径
  proxy_pass https://www.lubai-cdn.com,// 通过代理的形式
}

手写一个hash-router

<div class="container">
  <a href="#gray">灰色</a>
  <a href="#green">绿色</a>
  <a href="#">白色</a>
  <button onclick="window.history.go(-1)">返回</button>
</div>

​ 希望能达到下面的效果,点击三个不同的a标签,页面的背景色会随之改变。使用 new BaseRouter 的形式创建并使用

const Router = new BaseRouter();

Router.route('/', function () {
  changeBgColor('white');
});
Router.route('/green', function () {
  changeBgColor('green');
});
Router.route('/gray', function () {
  changeBgColor('gray');
});
// ---
const body = document.querySelector('body');

function changeBgColor(color) {
  body.style.backgroundColor = color;
}

​ 实现BaseRouter

class BaseRouter {
  constructor() {
    this.routes = {}; // 存储path以及callback的对应关系
    this.refresh = this.refresh.bind(this);
    window.addEventListener('load', this.refresh); // 处理页面首次加载
    window.addEventListener('hashchange', this.refresh); // 处理页面hash的变化
  }

  route(path, callback) {
    // 向this.routes存储path以及callback的对应关系
    this.routes[path] = callback || function () { };
  }

  refresh() {
    // 刷新页面
    const path = `/${location.hash.slice(1) || ''}`;
    console.log(location.hash);
    this.routes[path]();
  }
}

手写一个history-router

​ 改路径

<div class="container">
  <a href="/gray">灰色</a>
  <a href="/green">绿色</a>
  <a href="/">白色</a>
  <button onclick="window.history.go(-1)">返回</button>
</div>

​ 此外我们不希望a标签它执行,这个相当于执行了location.href = '/a',就出问题了,我们先阻止默认行为

const container = document.querySelector('.container');

container.addEventListener('click', e => {
  if (e.target.tagName === 'A') {
    e.preventDefault();
    Router.go(e.target.getAttribute('href'));
  }
});

​ 实现BaseRouter

class BaseRouter {
  constructor() {
    this.routes = {};
    // location.hash; hash的方式
    this.init(location.pathname);
    this._bindPopState();
  }

  init(path) {
    window.history.replaceState({
      path
    }, null, path);
    const cb = this.routes[path];
    // cb && cb();
    if (cb) {
      cb();
    }
  }

  route(path, callback) {
    this.routes[path] = callback || function () { };
  }

  go(path) {
    // 跳转并执行对应的callback
    window.history.pushState({
      path
    }, null, path);
    const cb = this.routes[path]; // 这里pushState无法被popState监听
    // cb && cb();
    if (cb) {
      cb();
    }
  }

  _bindPopState() {
    // 演示一下popstate事件触发后,会发生什么
    window.addEventListener('popstate', (e) => {
      const path = e.state && e.state.path;
      console.log(`in popstate listener path=${path}`);
      this.routes[path] && this.routes[path]();
    })
  }
}
上一篇:gitbook init报错解决


下一篇:封装绑定事件的处理函数(事件代理)