class Utils { getHashRoute() { let hashDetail = window.location.hash.split('?'); let hashName = hashDetail[0].split('#')[1]; let params = hashDetail[1] ? hashDetail[1].split('&') : []; let query = {}; params.map((item) => { let temp = item.split('='); query[temp[0]] = temp[1]; }); return { path: hashName, query: query }; } getHistoryRoute() { let path = (window.history.state && window.history.state.path) || ''; let queryStr = window.location.hash.split('?')[1]; let params = queryStr ? queryStr.split('&') : []; let query = {}; params.map((item) => { let temp = item.split('='); query[temp[0]] = temp[1]; }); return { path: path, query: query }; } /** * 发送Get请求 * * @param {!string} url 请求地址 * @param {?function} next 回调函数 */ static ajaxGet(url, next) { let xhr = new XMLHttpRequest(); if (url.includes('?')) { // 在URL有其他参数时,添加一个date参数加入当前时间以避免缓存 xhr.open('GET', `${url}&date=${new Date().getTime()}`, true); } else { // 添加一个date参数加入当前时间以避免缓存 xhr.open('GET', `${url}&date=${new Date().getTime()}`, true); } xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 更改XMLHttpRequest对象withCredentials属性以支持跨域Cookies xhr.withCredentials = true; xhr.responseType = 'json'; xhr.onload = function (res) { // 获取请求接口返回值 let response = res.target.response; next && next(response); }; xhr.send(); } /** * 发送Post请求 * * @param {!string} url 请求地址 * @param {?string} data post请求参数 * @param {?function} next 回调函数 */ static ajaxPost(url, data, next) { let xhr = new XMLHttpRequest(); if (url.includes('?')) { // 在URL有其他参数时,添加一个date参数加入当前时间以避免缓存 xhr.open("POST", `${url}&${new Date().getTime()}`, true); } else { // 添加一个date参数加入当前时间以避免缓存 xhr.open("POST", `${url}?${new Date().getTime()}`, true); } xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 更改XMLHttpRequest对象withCredentials属性以支持跨域Cookies xhr.withCredentials = true; xhr.responseType = 'json'; xhr.onload = function (res) { // 获取请求接口返回值 let response = res.target.response; next && next(response); }; xhr.send(data); } /** * 事件监听处理类 * * @param {string} eventName 表示监听事件类型的字符串 * @param {object} [{ onElement, withCallback, useCapture = false }={}] 监听对象 回调函数 useCapture * @param {function} thisArg * @returns function */ static handleEvent(eventName, { onElement, withCallback, useCapture = false } = {}, thisArg) { const element = onElement || document.documentElement function handler(event) { if (typeof withCallback === 'function') { withCallback.call(thisArg, event) } } handler.destroy = function () { return element.removeEventListener(eventName, handler, useCapture) } element.addEventListener(eventName, handler, useCapture) return handler } } /** * declare route: { path: '/xx', fileName: 'xxx', initFunc(){}} * * * @class SPARouter */ class SPARouter { constructor(el, routers, mode) { this.el = el; this.mode = mode || 'hash'; this.utils = new Utils(); this.currentRoute = {}; this.beforeFunc = null; this.afterFunc = null; this.initRouters(routers); this.init(); } init() { window.SPA_RESOLVE_INIT = null; this.initEvent(); } initRouters(routers) { this.routers = routers.map((item) => { item.$router = this; return item; }); } initEvent() { window.addEventListener('load', () => { console.log('load') this.routeUpdate(); }); if (this.mode === 'history') { window.addEventListener('popstate', (e) => { console.log('popstate') this.routeUpdate(); }); // 禁用所有a 链接默认跳转事件 let self = this; document.addEventListener('click', function (e) { let target = e.target || e.srcElement; if (target.tagName === 'A') { e.preventDefault(); let href = target.getAttribute('href'); let path = href.split('?')[0]; window.history.pushState({ path: path }, null, href); self.routeUpdate(); } }) } else { window.addEventListener('hashchange', () => { console.log('hashchange') this.routeUpdate(); }); } } loadComponent() { let self = this; if (typeof (self.currentRoute.fn) === 'function') { self.currentRoute.fn(self.el, self.currentRoute); } else { if (this.currentRoute.fileName) { let _body = document.getElementsByTagName('body')[0]; let scriptEle = document.createElement('script'); scriptEle.src = self.currentRoute.fileName; scriptEle.async = true; scriptEle.type = 'text/javascript'; window.SPA_ROUTE_INIT = null; scriptEle.onload = () => { self.afterFunc && self.afterFunc(self.currentRoute); self.currentRoute.fn = window.SPA_RESOLVE_INIT; self.currentRoute.fn(self.el, self.currentRoute); } _body.appendChild(scriptEle); } else { if (self.currentRoute.initFunc) { self.currentRoute.initFunc(self.el, self.currentRoute); self.afterFunc && self.afterFunc(self.currentRoute); } else { console.trace('该路由定义出错,fileName 和 initFunc 必须定义一个') } } } } refresh(currentHash) { let self = this; if (self.beforeFunc) { self.beforeFunc({ path: self.currentRoute.path, query: self.currentRoute.query }, () => { self.loadComponent(); }) } else { self.loadComponent(); } } routeUpdate() { let getLocation = this.mode === 'history' ? this.utils.getHistoryRoute : this.utils.getHashRoute; let currentLocation = getLocation(); this.currentRoute.query = currentLocation['query'] this.routers.map((item) => { if (item.path === currentLocation.path) { this.currentRoute = item; this.refresh(); } }); if (!this.currentRoute.path) { if (this.mode === 'history') { window.history.pushState({ path: '/index' }, null, '/index'); this.routeUpdate(); } else { location.hash = '/index'; } } } beforeEach(callback) { if (Object.prototype.toString.call(callback) === '[object Function]') { this.beforeFunc = callback; } else { console.trace('路由切换前钩子函数不正确') } } afterEach(callback) { if (Object.prototype.toString.call(callback) === '[object Function]') { this.afterFunc = callback; } else { console.trace('路由切换后钩子函数不正确') } } }