123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- class Router {
- constructor(arg) {
- if (arg && arg.constructor !== Object) {
- return console.error(`Routing configuration must be an Object`)
- }
- Router.$root = this;
- }
- // 路由跳转方法映射
- routeMap = {
- push: 'navigateTo',
- replace: 'redirectTo',
- replaceAll: 'reLaunch',
- pushTab: 'switchTab'
- }
- /**
- * 执行路由跳转
- */
- _pushTo() {
- return new Promise((resolve, reject) => {
- let {
- page,
- params,
- method
- } = this.tempRoute
- // 对首次进入页面执行路由守卫时如果放行,method page params都为空 此时应该直接中断流程,无需抛出异常
- if (!method && !page && !params) {
- return
- }
- let urlParams = '?'
- if (!page) {
- reject(new Error('参数page未填写'))
- return
- } else if (params && typeof(params) === 'object') {
- // 处理参数,转换为url字符串
- Object.keys(params).forEach(k => {
- // 深度对象转为json字符串(包含数组)
- if (typeof(params[k]) === 'object') {
- if (params[k]) {
- const json = JSON.stringify(params[k])
- urlParams += `${k}=${json}&`
- } else {
- urlParams += `${k}=&`
- }
- } else if (typeof(params[k]) === 'number' || typeof(params[k]) === 'string' || typeof(params[k]) ===
- 'boolean') {
- // 基础值直接写入
- urlParams += `${k}=${params[k]}&`
- } else if (typeof(params[k]) === 'undefined') {
- urlParams += `${k}=&`
- }
- })
- }
- // 参数组装
- if (urlParams.length === 1) {
- urlParams = ''
- } else {
- urlParams = urlParams.substr(0, urlParams.length - 1)
- }
- // 设置路由跳转方式
- if (!method) {
- method = 'navigateTo'
- }
- if (this.routeMap[method]) {
- method = this.routeMap[method]
- }
- // 调用系统跳转方法
- uni[method]({
- url: page + urlParams,
- success: () => {
- // 执行路由后置守卫
- if (this._afterEach && typeof(this._afterEach) === 'function') {
- this._afterEach.call(this, this.tempRoute, this.route)
- }
- // 更新路由信息
- this.route = {
- path: page + urlParams,
- params: params || {},
- page
- }
- this.tempRoute = null
- resolve()
- },
- fail: (e) => {
- reject(new Error('路由跳转失败!'))
- }
- })
- })
- }
- /**动态的导航到一个新 URL 保留浏览历史
- * navigateTo
- * @param {Object} rule
- */
- push(arg) {
- const rule = {
- method: 'navigateTo'
- }
- if (typeof(arg) === 'string') {
- rule.page = arg
- } else if (typeof(arg) === 'object') {
- rule.page = arg.page
- rule.params = arg.params
- }
- this.next(rule)
- }
- /**动态的导航到一个新 URL 关闭当前页面,跳转到的某个页面。
- * redirectTo
- * @param {Object} rule
- */
- replace(arg) {
- const rule = {
- method: 'redirectTo'
- }
- if (typeof(arg) === 'string') {
- rule.page = arg
- } else if (typeof(arg) === 'object') {
- rule.page = arg.page
- rule.params = arg.params
- }
- this.next(rule)
- }
- /**动态的导航到一个新 URL 关闭所有页面,打开到应用内的某个页面
- * reLaunch
- * @param {Object} rule
- */
- replaceAll(arg) {
- const rule = {
- method: 'reLaunch'
- }
- if (typeof(arg) === 'string') {
- rule.page = arg
- } else if (typeof(arg) === 'object') {
- rule.page = arg.page
- rule.params = arg.params
- }
- this.next(rule)
- }
- /** 跳转Tabbar
- * switchTab
- * @param {Object} rule
- */
- pushTab(arg) {
- const rule = {
- method: 'switchTab'
- }
- if (typeof(arg) === 'string') {
- rule.page = arg
- } else if (typeof(arg) === 'object') {
- rule.page = arg.page
- rule.params = arg.params
- }
- this.next(rule)
- }
- /**
- * 返回到指定层级页面上
- */
- back(delta = 1) {
- // 返回上级
- if (delta.constructor != Number) {
- this._errorHandler(new Error('返回层级参数必须是一个Number类型且必须大于0:'))
- return
- }
- uni.navigateBack({
- delta
- })
- }
- /**
- * 分发路由
- * @param {Object} args
- */
- _next() {
- return new Promise((resolve, reject) => {
- if (this._beforeEach && typeof(this._beforeEach) === 'function') {
- // 需要传给守卫 to from next
- this._beforeEach.call(this, this.tempRoute, this.route, resolve)
- } else {
- this._pushTo().catch(e => {
- reject(e)
- })
- }
- })
- }
- next(args) {
- if (args) {
- // 保存临时数据
- if (typeof(args) === 'object') {
- this.tempRoute = {
- // 第一次调用next一定存在method,后续循环调用可能不会存在,不存在时使用上次缓存的method
- method: args.method || this.tempRoute.method,
- page: args.page,
- params: args.params
- }
- } else if (typeof(args) === 'string') {
- this.tempRoute = {
- page: args
- }
- } else if (!args) {
- // 中断路由 args = false
- this.tempRoute = null
- return
- }
- if (!this.route) {
- this.route = {
- page: '/' + getCurrentPages()[0].route
- }
- }
- this._next().then(args => {
- this.next(args)
- }).catch(e => {
- this.tempRoute = null
- this._errorHandler(e)
- })
- } else {
- this._pushTo().catch(e => {
- this.tempRoute = null
- this._errorHandler(e)
- })
- }
- }
- /**
- * 应用启动时执行一次路由检查(前置守卫,若通过则不做事情)
- */
- doBeforeHooks() {
- this.tempRoute = {}
- this.next({})
- }
- // 设置路由前置/后置守卫
- beforeEach(fn) {
- this._beforeEach = fn
- }
- afterEach(fn) {
- this._afterEach = fn
- }
- // 设置路由跳转错误处理
- onError(fn) {
- if (fn && typeof(fn) === 'function') {
- this._errorHandler = fn
- }
- }
- // 获取当前路由信息
- getCurrentRoute() {
- return this.route
- }
- }
- // 路由对象属性定义
- Router.$root = null
- // 当前路由内容
- Router.route = null
- // 临时路由信息
- Router.tempRoute = null
- // 路由前置后置守卫
- Router._beforeEach = null
- Router._afterEach = null
- // 路由跳转错误处理
- Router._errorHandler = function(e) {
- console.error(e)
- }
- Router.install = function(Vue) {
- Vue.mixin({
- onLaunch: function() {},
- onLoad: function(props) {
- // 首次进入页面时,缓存中不存在当前路由信息,需要初始化路由信息
- if (!Router.$root.getCurrentRoute()) {
- const rt = {
- params: {},
- page: '/' + getCurrentPages()[0].route
- }
- if (props) {
- Object.keys(props).forEach(k => {
- // url转的对象全部都是字符串,需要识别其中的对象和基本数据类型
- try {
- const obj = JSON.parse(props[k])
- if (typeof(obj) === 'string') {
- // 只有字符串还会是字符串,数字、布尔、数组均会转换为正常类型
- rt.params[k] = props[k]
- } else {
- rt.params[k] = obj
- }
- } catch (e) {
- rt.params[k] = props[k]
- }
- })
- }
- Router.$root.route = rt
- // 执行路由前置守卫
- Router.$root.doBeforeHooks()
- }
- // 自动获取页面标题(app端可能获取不到)
- const pages = getCurrentPages()
- let pageTitle = pages[pages.length - 1].pageTitle
- // #ifdef H5
- if (!pageTitle) {
- pageTitle = document.title
- }
- // #endif
- Router.$root.route.pageTitle = pageTitle
- },
- onShow() {
- if (!getCurrentPages().length) {
- return
- }
- // 获取当前路由信息
- const pages = getCurrentPages()
- const page = pages[pages.length - 1]
- let pageTitle = page.pageTitle
- const rt = {
- params: {},
- page: '/' + page.route,
- }
- if (!pageTitle) {
- // #ifdef H5
- rt.pageTitle = document.title
- // #endif
- } else {
- rt.pageTitle = pageTitle
- }
-
- const route = Router.$root.route
- // 若当前页面地址不等于缓存中地址,则更新缓存路由信息
- if (!route || route.page !== rt.page) {
- Router.$root.route = rt
- }
- }
- })
- Object.defineProperty(Vue.prototype, "$Router", {
- get: function() {
- return Router.$root
- }
- })
- Object.defineProperty(Vue.prototype, "$Route", {
- get: function() {
- return Router.$root.getCurrentRoute()
- }
- })
- }
- export default Router
|