Skip to content
  • P
    Projects
  • G
    Groups
  • S
    Snippets
  • Help

肖康 / H5_2.0

  • This project
    • Loading...
  • Sign in
Go to a project
  • Project
  • Repository
  • Issues 0
  • Merge Requests 0
  • Pipelines
  • Wiki
  • Snippets
  • Settings
  • Activity
  • Graph
  • Charts
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • Files
  • Commits
  • Branches
  • Tags
  • Contributors
  • Graph
  • Compare
  • Charts
Find file
Normal viewHistoryPermalink
Switch branch/tag
  • H5_2.0
  • node_modules
  • _uni-simple-router@1.5.5@uni-simple-r...
  • vueRouter
  • util.js
util.js 18.2 KB
肖康's avatar
first init
ddbafbda
 
肖康 committed 2 years ago
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
import { warn, err } from '../helpers/warn';
import { isObject, resolveRule, copyObject } from '../helpers/util';
import { proxyBeforeEnter } from './proxy/proxy';
import { Global } from '../helpers/config';

const pagesConfigReg = /props:\s*\(.*\)\s*(\([\s\S]*\))\s*},/;
const pagesConfigRegCli = /props:\s*Object\.assign\s*(\([\s\S]*\))\s*},/; // 脚手架项目
const defRoutersReg = /props:\s*{([\s\S]+)}\s*},/;

/**
 * 解析验证当前的 component 选项是否配置正确 只有vueRouterDev:false 才会调用此方法
 * @param {Function|Object} component
 * @param {Object} item
 * @param {Boolean} useUniConfig
 */
export const resolveRender = function ({
    component,
    components,
}, item, useUniConfig) {
    if (components != null) {
        warn(`vueRouterDev:false时 路由表配置中 ‘components’ 无效,\r\n\r\n ${JSON.stringify(item)}`);
    }
    if (useUniConfig == true) { // 采用uni-pages.json中的配置时component可以为空
        return false;
    }
    if (item.path == '*') { // 唯独这个情况在vue-router中可以不用component
        return true;
    }
    if (component == null) {
        return err(`vueRouterDev:false时 路由表中 ‘component’ 选项不能为空:\r\n\r\n ${JSON.stringify(item)}`);
    }
    if (component.constructor === Function) {
        item.component = {
            render: component,
        };
    } else if (component.constructor === Object) {
        if (component.render == null || component.render.constructor !== Function) {
            err(`vueRouterDev:false时 路由表配置中 ‘render’ 函数缺失或类型不正确:\r\n\r\n ${JSON.stringify(item)}`);
        }
    } else {
        err(
            `vueRouterDev:false时 路由表配置中 ‘component’ 选项仅支持 Function、Object 类型。并确保 Object 类型时传递了 ‘render’ 函数  :\r\n\r\n ${JSON.stringify(item)}`,
        );
    }
};
/**
 * 递归解析 H5配置中有存在嵌套对象的情况,优先以path为key存储。没有则找aliasPath作为key
 * @param {Object} objRoutes
 * @param {Array} children
 * @param {Boolean} useUniConfig 是否使用pages.json下的页面配置
 */
export const resloveChildrenPath = function (objRoutes, children, useUniConfig) {
    for (let i = 0; i < children.length; i += 1) {
        const item = children[i];
        resolveRender(item, item, useUniConfig);
        if (item.path != null) {
            objRoutes[item.path] = {
                ...item,
                ...{
                    _ROUTERPATH: true, // 使用page.json中的path为路径
                },
            };
        } else {
            objRoutes[item.aliasPath] = {
                ...item,
                ...{
                    _ROUTERPATH: false,
                },
            };
        }
        if (item.children && item.children.constructor === Array) {
            resloveChildrenPath(objRoutes, item.children, useUniConfig);
        }
    }
};
/**
 * 格式化原始路由表
 * @param {Object} routes  路由表
 * @param {Boolean} userRoute  是否为用户自己配置的路由表
 * @param {Boolean} H5CONFIG
 */
export const fromatRoutes = function (routes, userRoute, {
    vueRouterDev,
    useUniConfig,
}) {
    if (userRoute && vueRouterDev) { // 如果是用户的路由表并且 完全采用vueRouter开发 则不作处理直接返回
        return routes;
    }
    const objRoutes = {};
    for (let i = 0; i < routes.length; i += 1) {
        const item = routes[i];
        const path = item.path === '/' ? item.alias : item.path;
        if (userRoute) {
            if (item.children && item.children.constructor === Array) {
                resloveChildrenPath(objRoutes, item.children, useUniConfig);
            }
            resolveRender(item, item, useUniConfig); // 是否使用pages.json下的页面配置
        }
        objRoutes[path] = {
            ...item,
            ...{
                _PAGEPATH: path.substring(1),
            },
        };
    }
    return objRoutes;
};

/**
 * 解析vueRouter中 component 下 render函数中的配置信息
 * @param {String} FunStr
 */
export const getFuntionConfig = function (FunStr) {
    let matchText = FunStr.match(pagesConfigReg);
    let prefix = '';
    if (matchText == null) { // 是uni-app自带的默认路由及配置 也可能是脚手架项目
        matchText = FunStr.match(pagesConfigRegCli);
        if (matchText == null) { // 确认不是脚手架项目
            try {
                // eslint-disable-next-line
                matchText = FunStr.match(defRoutersReg)[1];
                // eslint-disable-next-line
                matchText = eval(`Object.assign({${matchText}})`);
                prefix = 'system-';
            } catch (error) {
                err(`读取uni-app页面构建方法配置错误 \r\n\r\n ${error}`);
            }
        } else {
            // eslint-disable-next-line
            matchText = eval(`Object.assign${matchText[1]}`);
        }
    } else {
        // eslint-disable-next-line
        matchText = eval(`Object.assign${matchText[1]}`);
    }
    return {
        config: matchText,
        prefix,
        FunStr,
    };
};
/**
 * 通过一个未知的路径名称 在路由表中查找指定路由表 并返回
 * @param {String} path //不管是aliasPath名的路径还是path名的路径
 * @param {Object} routes//当前对象的所有路由表
 */
export const pathToRute = function (path, routes) {
    let PATHKEY = '';
    let rute = {};
    const routeKeys = Object.keys(routes);
    for (let i = 0; i < routeKeys.length; i += 1) {
        const key = routeKeys[i];
        const item = routes[key];
        rute = item;
        if (item.aliasPath == path) { // path参数是优先采用aliasPath为值得 所以可以先判断是否与aliasPath相同
            PATHKEY = 'aliasPath';
            break;
        }
        // eslint-disable-next-line
        if (`/${item._PAGEPATH}` == path) { // 路径相同
            PATHKEY = 'path';
            break;
        }
    }
    return {
        PATHKEY: {
            [PATHKEY]: path,
        },
        rute,
    };
};
/**
 * 通过一个路径name 在路由表中查找指定路由表 并返回
 * @param {String} name//实例化路由时传递的路径表中所匹配的对应路由name
 * @param {Object} routes//当前对象的所有路由表
 */
export const nameToRute = function (name, routes) {
    const routesKeys = Object.keys(routes);
    for (let i = 0; i < routesKeys.length; i += 1) {
        const key = routesKeys[i];
        const item = routes[key];
        if (item.name == name) {
            return item;
        }
    }

    err(`路由表中没有找到 name为:'${name}' 的路由`);
};
/**
 * 根据用户传入的路由规则 格式化成正确的路由规则
 * @param {Object} rule 用户需要跳转的路由规则
 * @param {Object} selfRoutes simple-router下的所有routes对象
 * @param {Object} CONFIG 当前路由下的所有配置信息
 */
export const formatUserRule = function (rule, selfRoutes, CONFIG) {
    let type = '';
    const ruleQuery = (type = 'query', rule.query || (type = 'params', rule.params)) || (type = '', {});
    let rute = {}; // 默认在router中的配置
    if (type == '' && rule.name != null) { // 那就是可能没有穿任何值咯
        type = 'params';
    }
    if (type != 'params') {
        const route = pathToRute(rule.path || rule, selfRoutes);
        if (Object.keys(route.PATHKEY)[0] == '') {
            err(`'${route.PATHKEY['']}' 路径在路由表中未找到`);
            return null;
        }
        rute = route.rute;
        if (rule.path) {
            rule.path = rute.path;
        }
    }
    if (type != '') { // 当然是对象啦 这个主要是首页H5PushTo调用时的
        if (type == 'params' && CONFIG.h5.paramsToQuery) { // 如果是name规则并且设置了转query,那么就转path跳转了
            const {
                aliasPath,
                path,
            } = nameToRute(rule.name, selfRoutes);
            delete rule.name;
            delete rule.params;
            rule.path = aliasPath || path;
            type = 'query';
        }
        const query = Global.$parseQuery.transfer(ruleQuery);
        if (CONFIG.encodeURI) {
            if (query != '') {
                rule[type] = {
                    query: query.replace(/^query=/, ''),
                };
            }
        } else {
            rule[type] = ruleQuery;
        }
    } else { // 纯字符串,那就只有是path啦
        rule = rute.path;
    }
    return rule;
};

/**
 * 根据是否获取非vue-Router next管道参数,来进行格式化
 *
 * @param {Object} to
 * @param {Object} from
 * @param {Router} Router  //router当前实例对象
 */
export const getRouterNextInfo = function (to, from, Router) {
    let [toRoute, fromRoute] = [to, from];
    const H5 = Router.CONFIG.h5;
    if (H5.vueNext === false && H5.vueRouterDev === false) { // 不采用vue-router中的to和from,需要格式化成Router中$Route获取的一样一样的
        let [toPath, fromPath] = [{}, {}];
        toPath[to.meta.PATHKEY] = to.meta.PATHKEY === 'path' ? `/${to.meta.pagePath}` : `${to.path}`;
        fromPath[from.meta.PATHKEY] = from.meta.PATHKEY === 'path' ? `/${from.meta.pagePath}` : `${from.path}`;

        if (to.meta.PATHKEY == null) { // 未使用uni-pages.json中的配置、通过addRoutes时 meta.PATHKEY 可能未undefined
            toPath = pathToRute(to.path, Router.selfRoutes).PATHKEY;
        }
        if (from.meta.PATHKEY == null) {
            fromPath = pathToRute(from.path, Router.selfRoutes).PATHKEY;
        }

        const isEmptyTo = Object.keys(to.query).length != 0 ? copyObject(to.query) : copyObject(to.params);
        const isEmptyFrom = Object.keys(from.query).length != 0 ? copyObject(from.query) : copyObject(from.params);
        /* eslint-disable */
        delete isEmptyTo.__id__; // 删除uni-app下的内置属性
        delete isEmptyFrom.__id__;
        /* eslint-enable */
        const toQuery = Global.$parseQuery.queryGet(isEmptyTo).decode;
        const fromQuery = Global.$parseQuery.queryGet(isEmptyFrom).decode;
        toRoute = resolveRule(Router, toPath, toQuery, Object.keys(toPath)[0]);
        fromRoute = resolveRule(Router, fromPath, fromQuery, Object.keys(fromPath)[0]);
    } else {
        if (fromRoute.name == null && toRoute.name != null) { // 这种情况是因为uni-app在使用vue-router时搞了骚操作。
            fromRoute = {
                ...fromRoute,
                ...{
                    name: toRoute.name,
                },
            }; // 这个情况一般出现在首次加载页面
        }
    }
    return {
        toRoute,
        fromRoute,
    };
};
export const vueDevRouteProxy = function (routes, Router) {
    const proxyRoutes = [];
    for (let i = 0; i < routes.length; i += 1) {
        const item = routes[i];
        const childrenRoutes = Reflect.get(item, 'children');
        if (childrenRoutes != null) {
            const childrenProxy = vueDevRouteProxy(childrenRoutes, Router);
            item.children = childrenProxy;
        }
        const ProxyRoute = proxyBeforeEnter(Router, item);
        proxyRoutes.push(ProxyRoute);
    }
    return proxyRoutes;
};
/**
 * 组装成编码后的路由query传递信息
 * @param {Object} CONFIG simple-router 对象配置
 * @param {Object} query 传递的参数
 * @param {Object} mode 路由模式
 */

export const encodeURLQuery = function (CONFIG, query, mode) {
    if (Object.keys(query).length == 0) { // 没有传值的时候 我们啥都不管
        return '';
    }
    if (CONFIG.h5.vueRouterDev === false) { // 没有采取完全模式开发时 才转换
        const { strQuery, historyObj } = Global.$parseQuery.queryGet(query);
        if (mode === 'history') {
            return historyObj;
        }
        return strQuery;
    } // 完全彩种 vue-router 开发的时候 我们不用管
    if (mode === 'history') { // 此模式下 需要的就是对象
        return query;
    }
    return Global.$parseQuery.stringify(query); // hash转成字符串拼接
};
/**
 * 把一个未知的路由跳转规则进行格式化为 hash、history 可用的,主要表现在 history模式下直接传入path会报错__id__错误的问题
 * @param {*} path 需要判断修改的路径规则
 */
export const strPathToObjPath = function (path) {
    if (path == null) { // 我们也不用管啦,这个情况是路由守卫中传递的
        return path;
    }
    if (isObject(path)) { // 是对象我们不用管
        return path;
    }
    return { // 这种情况就是只有path时,直接返回path对象了
        path,
    };
};
/**
 * 通过 getCurrentPages() api 获取指定页面的 page 对象 默认是获取当前页面page对象
 * @param {Number} index //需要获取的页面索引
 * @param {Boolean} all //是否获取全部的页面
 */
export const getPages = function (index = 0, all) {
    const pages = getCurrentPages(all);
    return pages.reverse()[index];
};
/**
 * 获取当前页面下的 Route 信息
 *
 * @param {Object} pages 获取页面对象集合
 * @param {Object} Vim 用户传递的当前页面对象
 */
export const H5GetPageRoute = function (pages, Vim) {
    if (pages.length > 0) { // 直接取当前页面的对象
        const currentRoute = pages[pages.length - 1].$route;
        return getRouterNextInfo(currentRoute, currentRoute, this).toRoute;
    } if (Vim && Vim.$route) {
        return getRouterNextInfo(Vim.$route, Vim.$route, this).toRoute;
    }
    return {};
};

/**
 * 在useUniConfig:true 的情况下重新拼装路由表 useUniConfig:false 不需要读取page.json中的数据 直接使用component作为页面组件
 * @param {Router} Router//unis-simple-router 路由对象
 * @param {vueRouter} vueRouter//vue-router对象
 * @param {Boolean} useUniConfig//是否采用uni-page.json中的配置选项
 * @param {Array} routes//需要循环的routes表
 */
export const diffRouter = function (Router, vueRouter, useUniConfig, routes) {
    const newRouterMap = [];
    if (useUniConfig) { // 使用pages.json的样式配置 只是单纯的把url路径改成用户自定义的 保留uni的所以的配置及生命周期、缓存
        const Routes = routes || vueRouter.options.routes;
        const cloneSelfRoutes = copyObject(Router.selfRoutes); // copy一个对象随便搞xxoo
        Routes.forEach(((item) => {
            const path = item.path === '/' ? item.alias : item.path;
            const vueRoute = (Router.vueRoutes[path] || Router.vueRoutes[item.path]) || Router.selfRoutes[path];
            const CselfRoute = Router.selfRoutes[path];
            delete cloneSelfRoutes[path]; // 移除已经添加到容器中的路由,用于最后做对比 是否page.json中没有,而实例化时传递了
            if (CselfRoute == null) {
                return err(
                    `读取 ‘pages.json’ 中页面配置错误。实例化时传递的路由表中未找到路径为:${path} \r\n\r\n 可以尝试把 ‘useUniConfig’ 设置为 ‘false’。或者配置正确的路径。如果你是动态添加的则不用理会`,
                );
            }
            let pageConfigJson = {
                config: {},
            };
            if (vueRoute.component) {
                pageConfigJson = getFuntionConfig(vueRoute.component.render.toString());
                CselfRoute.component = {
                    render: (h) => vueRoute.component.render(h),
                };
            }
            delete CselfRoute.components; // useUniConfig:true 时不允许携带components
            delete CselfRoute.children; // useUniConfig:true 时不允许携带children
            CselfRoute.meta = {
                ...pageConfigJson.config,
                ...item.meta || {},
                PATHKEY: CselfRoute.aliasPath ? 'aliasPath' : 'path',
                pagePath: CselfRoute.path.substring(1),
            };
            CselfRoute.path = CselfRoute.aliasPath || (item.path === '/' ? item.path : CselfRoute.path);
            item.alias = item.path === '/' ? item.alias : CselfRoute.path; // 重新给vueRouter赋值一个新的路径,欺骗uni-app源码判断
            const ProxyRoute = proxyBeforeEnter(Router, CselfRoute);
            newRouterMap.push(ProxyRoute);
        }));
        if (Object.keys(cloneSelfRoutes).length > 0) { // 确实page.json中没有,而实例化时传递了
            const testG = cloneSelfRoutes['*']; // 全局通配符,他是个例外'通配符'可以被添加
            if (testG && routes == null) {
                const ProxyRoute = proxyBeforeEnter(Router, Router.selfRoutes['*']);
                newRouterMap.push(ProxyRoute);
            }
            if (routes == null) { // 非动态添加时才打印警告
                const cloneSelfRoutesKeys = Object.keys(cloneSelfRoutes);
                for (let i = 0; i < cloneSelfRoutesKeys.length; i += 1) {
                    const key = cloneSelfRoutesKeys[i];
                    if (key !== '*') { // 通配符不警告
                        warn(`实例化时传递的routes参数:\r\n\r\n ${JSON.stringify(cloneSelfRoutes[key])} \r\n\r\n 在pages.json中未找到。自定排除掉,不会添加到路由中`);
                    }
                }
            }
        }
    } else { // 不使用任何的uni配置完全使用 完全使用component作为页面使用
        const Routes = routes || Router.selfRoutes;
        const RoutesKeys = Object.keys(Routes);
        for (let i = 0; i < RoutesKeys.length; i += 1) {
            const key = RoutesKeys[i];
            const item = Routes[key];
            // eslint-disable-next-line
            if (item._ROUTERPATH != null) { // 不寻找children下的路径,只取第一层
                continue;
            }
            delete item.components;
            delete item.children;
            item.path = item.aliasPath || item.path; // 优先获取别名为路径
            if (item.path !== '*') {
                item.component = item.component.render || item.component; // render可能是用户使用addRoutes api进行动态添加的
            }
            item.meta = {
                ...item.meta || {},
                PATHKEY: item.aliasPath ? 'aliasPath' : 'path',
                pagePath: item.path.substring(1),
            };
            const ProxyRoute = proxyBeforeEnter(Router, item);
            newRouterMap.push(ProxyRoute);
        }
    }
    return newRouterMap;
};