js实现SPA单页面应用路由

js实现SPA单页面应用路由

router.js

/**
 * @description SPA路由
 * @author https://github.com/kliuj
 * @use new Router([{},{},...]);
 */
var util = {
  //获取路由的路径和详细参数
  getParamsUrl: function () {
    var hashDeatail = location.hash.split("?"),
      hashName = hashDeatail[0].split("#")[1], //路由地址
      params = hashDeatail[1] ? hashDeatail[1].split("&") : [], //参数内容
      query = {};
    for (var i = 0; i < params.length; i++) {
      var item = params[i].split("=");
      query[item[0]] = item[1]
    }
    return {
      path: hashName,
      query: query
    }
  }
}
function Router(route) {
  this.route = route || []; // 路由集合
  this.routers = {}; // 保存注册的所有路由
  this.beforeFun = undefined;
  this.afterFun = undefined;
  this.init(route);
}
Router.prototype = {
  init: function (route) {
    var self = this;
    //页面加载匹配路由
    window.addEventListener('load', function () {
      self.urlChange()
    })
    //路由切换
    window.addEventListener('hashchange', function () {
      self.urlChange()
    })
    //异步引入js通过回调传递参数
    window.SPA_RESOLVE_INIT = null;
    if (!route.length) {
      console.trace('路由注册不能为空');
    }
    route.forEach(item => {
      self.register(item.path, transition => {
        self.asyncFun(item.component, transition);
      })
    })
  },
  refresh: function (currentHash) {
    var self = this;
    if (self.beforeFun) {
      self.beforeFun({
        to: {
          path: currentHash.path,
          query: currentHash.query
        },
        next: function () {
          self.routers[currentHash.path].callback.call(self, currentHash)
        }
      })
    } else {
      self.routers[currentHash.path].callback.call(self, currentHash)
    }
  },
  //路由处理
  urlChange: function () {
    var currentHash = util.getParamsUrl();
    console.log(this.routers);
    if (this.routers[currentHash.path]) {
      this.refresh(currentHash)
    } else {
      //不存在的地址重定向到首页
      location.hash = '/'
    }
  },
  //单层路由注册
  register: function (path, callback) {
    path = path.replace(/\s*/g, ""); //过滤空格
    if (callback && Object.prototype.toString.call(callback) === '[object Function]') {
      this.routers[path] = {
        callback: callback, //回调
        fn: null //存储异步文件状态
      }
    } else {
      console.trace('注册' + path + '地址需要提供正确的的注册回调')
    }
  },
  //切换之前一些处理
  beforeEach: function (callback) {
    if (Object.prototype.toString.call(callback) === '[object Function]') {
      this.beforeFun = callback;
    } else {
      console.trace('路由切换前钩子函数不正确')
    }
  },
  //切换成功之后
  afterEach: function (callback) {
    if (Object.prototype.toString.call(callback) === '[object Function]') {
      this.afterFun = callback;
    } else {
      console.trace('路由切换后回调函数不正确')
    }
  },
  //路由异步懒加载js文件
  asyncFun: function (file, transition) {
    var self = this;
    if (self.routers[transition.path].fn) {
      self.afterFun && self.afterFun(transition)
      self.routers[transition.path].fn(transition)
    } else {
      console.log("开始异步下载js文件" + file)
      var _body = document.getElementsByTagName('body')[0];
      var scriptEle = document.createElement('script');
      scriptEle.type = 'text/javascript';
      scriptEle.src = file;
      scriptEle.async = true;
      SPA_RESOLVE_INIT = null;
      scriptEle.onload = function () {
        console.log('下载' + file + '完成')
        self.afterFun && self.afterFun(transition)
        self.routers[transition.path].fn = SPA_RESOLVE_INIT;
        self.routers[transition.path].fn(transition)
      }
      _body.appendChild(scriptEle);
    }
  },
  //同步操作
  syncFun: function (callback, transition) {
    this.afterFun && this.afterFun(transition)
    callback && callback(transition)
  }
}

test.html

<!DOCTYPE HTML>
<html>
<head>
  <title>test hash route</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <script type="text/javascript" src="./router.js"></script>
  <style type="text/css">
    body {
      font: 10.5pt arial;
      color: #4d4d4d;
      line-height: 150%;
      width: 90%;
    }
    a {
      font-size: 16px;padding: 10px
    }
    #content{
      font-size:20px;font-weight:bold;padding: 20px
    }
  </style>
</head>
<body>
  <a href="#/index?index=1">异步加载首页</a>
  <a href="#/list?list=1">异步加载列表页</a>
  <a href="#/detail?detail=1">异步加载详情页</a>
  <a href="#/detail2?detail=2">同步加载详情页</a>
  <div id="content">
    <p style="color:#333">默认首页内容</p>
  </div>
  <script type="text/javascript" >
    const routers = [
      {
        name: 'index',
        path: '/index',
        component: 'js/index.js'
      },
      {
        name: 'list',
        path: '/list',
        component: 'js/list.js'
      },
      {
        name: 'detail',
        path: '/detail',
        component: 'js/detail.js'
      },
      {
        name: 'detail2',
        path: '/detail2',
        component: 'js/detail.js'
      },
    ]
    window.$router = new Router(routers);
  </script>
</body>
</html>

Js/index.js

SPA_RESOLVE_INIT = function(transition) { 
	document.getElementById("content").innerHTML = '<p style="color:#099fde;">当前异步渲染首页'+ JSON.stringify(transition) +'</p>'
	console.log("首页回调" + JSON.stringify(transition))
}

js/list.js

SPA_RESOLVE_INIT = function(transition) { 
	document.getElementById("content").innerHTML = '<p style="color:#F8C545;">当前异步渲染列表页'+ JSON.stringify(transition) +'</p>'
	console.log("首页回调" + JSON.stringify(transition))
}

js/detail.js

SPA_RESOLVE_INIT = function(transition) { 
	document.getElementById("content").innerHTML = '<p style="color:red;">当前异步渲染详情页'+ JSON.stringify(transition) +'</p>'
	console.log("首页回调" + JSON.stringify(transition))
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!