├── .babelrc ├── .gitignore ├── rollup.config.js ├── package.json ├── README.md ├── src └── install.js └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "es2015", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by IDE 2 | .idea/ 3 | *.iml 4 | .DS_Store 5 | 6 | # Created by Builder 7 | coverage 8 | dist 9 | 10 | # Node 11 | logs 12 | *.log 13 | npm-debug.log* 14 | node_modules 15 | .npm 16 | 17 | .git/ -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const buble = require('rollup-plugin-buble') 2 | const flow = require('rollup-plugin-flow-no-whitespace') 3 | const cjs = require('rollup-plugin-commonjs') 4 | const node = require('rollup-plugin-node-resolve') 5 | const replace = require('rollup-plugin-replace') 6 | const version = process.env.VERSION || require('./package.json').version 7 | 8 | module.exports = { 9 | entry: 'src/install.js', 10 | dest: 'index.js', 11 | format: 'cjs', 12 | moduleName: 'weex-vue-router', 13 | plugins: [replace({ 14 | 'process.env.NODE_ENV': '"development"' 15 | }), flow(), node(), cjs(), buble()], 16 | banner: `/** 17 | * weex-vue-router v${version} 18 | * (c) ${new Date().getFullYear()} dongnaebi 19 | * @license Apache-2.0 20 | */` 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weex-vue-router", 3 | "version": "0.0.4", 4 | "description": "router for weex as you can use some feature of vue-router", 5 | "main": "index.js", 6 | "keywords": [ 7 | "weex", 8 | "vue", 9 | "javascript", 10 | "android", 11 | "ios" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/dongnaebi/weex-vue-router.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/dongnaebi/weex-vue-router/issues" 19 | }, 20 | "scripts": { 21 | "build": "rollup -c ./rollup.config.js", 22 | "test": "echo \"Error: no test specified\" && exit 1" 23 | }, 24 | "engines": { 25 | "node": ">=4.0" 26 | }, 27 | "author": "dongnaebi", 28 | "license": "Apache-2.0", 29 | "dependencies": { 30 | "path-to-regexp": "^1.7.0", 31 | "vue": "^2.1.8", 32 | "vue-router": "^2.1.1" 33 | }, 34 | "devDependencies": { 35 | "babel-core": "^6.20.0", 36 | "babel-loader": "^6.2.9", 37 | "babel-plugin-external-helpers": "^6.22.0", 38 | "babel-preset-es2015": "^6.22.0", 39 | "rollup": "^0.41.4", 40 | "rollup-plugin-buble": "^0.15.0", 41 | "rollup-plugin-commonjs": "^7.0.0", 42 | "rollup-plugin-flow-no-whitespace": "^1.0.0", 43 | "rollup-plugin-node-resolve": "^2.0.0", 44 | "rollup-plugin-replace": "^1.1.1", 45 | "rollup-watch": "^3.2.2", 46 | "uglify-js": "^2.7.5" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Demo ## 2 | [xiazhou-weex](https://github.com/dongnaebi/xiazhou-weex) 3 | 4 | ## Install ## 5 | ```bash 6 | $ npm install weex-vue-router 7 | ``` 8 | ## Usage ## 9 | ```html 10 | 15 | ``` 16 | 17 | ```javascript 18 | import weexVueRouter from 'weex-vue-router' 19 | import routes from './native-router'//web-router and native-router need to be defined separately。 20 | Vue.use(weexVueRouter,{routes,weex}) 21 | 22 | export default { 23 | methods:{ 24 | jump(url) { 25 | this.$router.push(url) 26 | }, 27 | getParams(){ 28 | return this.$route.params 29 | } 30 | } 31 | } 32 | ``` 33 | ## Construction options ## 34 | ```javascript 35 | //native-router.js 36 | const domain='http://domain.com'; 37 | const routes = [{ 38 | path:'/product/:id'; 39 | component:domain+'/dist/product/detail.js';//js bundle address,must end with '.js' 40 | name:'product'; 41 | }]; 42 | export default routes; 43 | ``` 44 | ## Component injections ## 45 | $router 46 | - push() -only surport string featrue, like `/path/:foo/:bar` 47 | - back() 48 | 49 | $route 50 | - path 51 | - params 52 | - query 53 | - hash 54 | - fullpath 55 | - matched 56 | - name 57 | 58 | ## 原理(求翻译) ## 59 | 需编译成两套,web端使用`vue-router`做SPA架构,单独编译出一个js(demo中的`dist/app.js`)。 60 | 61 | native端则对应`.vue`文件编译成js bundle(demo中`app/pages`目录下的文件编译后分别对应dist中的js文件)。 62 | 63 | 在组件中写跳转`$this.router.push('/path/1')`,web端用vue-router跳转。 64 | 65 | native端接收到`/path/1`,对应自己定义的routes匹配出js bundle地址,并使用`navigator.push()`方法跳转 66 | 67 | ## TODO ## 68 | - 分模块&更严谨的逻辑 69 | - 加单测 70 | - meta和别名 71 | -------------------------------------------------------------------------------- /src/install.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by ebi on 2017/2/14. 3 | */ 4 | import pathToRegexp from 'path-to-regexp' 5 | const weexVueRouter = { 6 | install(Vue, {routes, weex}){ 7 | let platform = weex.config.env ? weex.config.env.platform : weex.config.platform; 8 | if (platform.toLowerCase() == 'web')return; 9 | const navigator = weex.requireModule('navigator'); 10 | let bundleUrl = weex.config.bundleUrl; 11 | const route = bundleToPath(bundleUrl, routes); 12 | Object.defineProperty(Vue.prototype, "$router", { 13 | value: { 14 | push(url){ 15 | let bundle = pathToBundle(url, routes); 16 | if (navigator) { 17 | console.log(bundle); 18 | navigator.push({ 19 | 'url': bundle, 20 | 'animated': 'true' 21 | }, function () { 22 | console.log('skip complete') 23 | }); 24 | } 25 | }, 26 | back(){ 27 | if (navigator) { 28 | navigator.pop(); 29 | } 30 | } 31 | }, 32 | configurable: false 33 | }); 34 | Object.defineProperty(Vue.prototype, '$route', { 35 | configurable: false, 36 | value: { 37 | path: route.path, 38 | params: route.params, 39 | query: route.query, 40 | hash: route.hash, 41 | fullPath: route.fullPath, 42 | matched: route.matched, 43 | name: route.name 44 | } 45 | }); 46 | } 47 | } 48 | function pathToBundle(url,routes){ 49 | /* url='/list/2-1?from=1#2' 50 | * r={path:'/list/:cid-:id',bundle:'/product/list.js'} 51 | * */ 52 | if(url.indexOf('/')!=0){ 53 | console.error("the url must begin with '/'"); 54 | return ''; 55 | } 56 | 57 | //copy from vue-router 58 | const encodeReserveRE = /[!'()*]/g; 59 | const encodeReserveReplacer = c => '%' + c.charCodeAt(0).toString(16); 60 | const encode = str => encodeURIComponent(str) 61 | .replace(encodeReserveRE, encodeReserveReplacer) 62 | .replace(/%2C/g, ',') 63 | 64 | /*find out the rule*/ 65 | let matchRule={}; 66 | routes.forEach(r => { 67 | let re=pathToRegexp(r.path); 68 | let match=re.exec(url); 69 | if(match!=null){ 70 | matchRule = r; 71 | } 72 | }); 73 | 74 | /*get the key and value*/ 75 | let keys = []; 76 | let pathReg = pathToRegexp(matchRule.path, keys); 77 | let values=pathReg.exec(url); 78 | let lastValue=values[values.length-1];//save the last value to find query and hash 79 | values[values.length-1]=lastValue.split(/\?|\#/)[0];//the true value 80 | 81 | /*parse params to key/value object*/ 82 | let params={}; 83 | if(keys.length>0){ 84 | keys.forEach((key,i)=>{ 85 | params[key.name]=values[i+1]; 86 | }); 87 | } 88 | 89 | /*get query and hash*/ 90 | const queryIndex=lastValue.indexOf('?'); 91 | const hashIndex=lastValue.indexOf('#'); 92 | if (queryIndex > 0 && hashIndex > 0 && queryIndex > hashIndex) { 93 | console.error("Could not set '#' behind '?'"); 94 | return ''; 95 | } 96 | let queryStr=queryIndex>0?lastValue.substring(queryIndex+1,hashIndex>0?hashIndex:lastValue.length):""; 97 | let hashStr=hashIndex>0?lastValue.substring(hashIndex,lastValue.length):""; 98 | let query=getParams(queryStr);//{from:1} 99 | 100 | /*add the bundleUrl's params and hash*/ 101 | let componentPath=matchRule.component; 102 | for(let k in params){ 103 | componentPath+=(componentPath.indexOf('?')>0?'&':'?')+k+'='+encode(params[k]); 104 | } 105 | for(let q in query){ 106 | componentPath+=(componentPath.indexOf('?')>0?'&':'?')+q+'='+encode(query[q]); 107 | } 108 | componentPath+=hashStr; 109 | return componentPath; 110 | } 111 | function bundleToPath(url,routes){ 112 | //url='domain/product/list.js?cid=2&id=1&from=1' 113 | //matchRule={path:'/list/:cid-:id',component:'domain/product/list.js'} 114 | let route={ 115 | params:null, 116 | query:null, 117 | hash:null, 118 | path:null, 119 | fullPath:null, 120 | matched:null, 121 | name:null 122 | }; 123 | let jsBundle=url.split(/\?|\#/)[0]; 124 | /*find out the rule*/ 125 | let matchRule=null; 126 | routes.forEach(r => { 127 | r.component==jsBundle&&(matchRule=r); 128 | //http://192.168.253.124:8080/dist/product/list.js 129 | }); 130 | if(!matchRule){ 131 | console.error(`your component must be like '${jsBundle}',can not find it in routes,please check up`); 132 | return route; 133 | } 134 | 135 | /*use pathToRegexp*/ 136 | let keys = []; 137 | pathToRegexp(matchRule.path, keys); 138 | 139 | /*get query and hash*/ 140 | const queryIndex=url.indexOf('?'); 141 | const hashIndex=url.indexOf('#'); 142 | let queryStr=queryIndex>0?url.substring(queryIndex+1,hashIndex>0?hashIndex:url.length):""; 143 | route.hash=hashIndex>0?url.substring(hashIndex,url.length):""; 144 | 145 | const allQuery=getParams(queryStr);//{cid:2,id:1,from:1} 146 | 147 | let params={},//{cid:2,id:1} 148 | query={},//{from:1} 149 | paramsKey=[];//['cid','id'] 150 | if(keys.length>0){ 151 | paramsKey=keys.map(key=>key.name); 152 | } 153 | for(let q in allQuery){ 154 | allQuery[q]=decodeURIComponent(allQuery[q]); 155 | paramsKey.indexOf(q)<0?query[q]=allQuery[q]:params[q]=allQuery[q]; 156 | } 157 | route.params=params; 158 | route.query=query; 159 | 160 | //path and fullPath 161 | let path=matchRule.path; 162 | for(let p in params){ 163 | path=path.replace(':'+p,params[p]); 164 | } 165 | route.path=path; 166 | let queryArr=[]; 167 | for(let i in query){ 168 | queryArr.push(i+'='+query[i]); 169 | } 170 | route.fullPath=path+'?'+queryArr.join('&')+route.hash; 171 | route.matched=matchRule; 172 | route.name=matchRule.name; 173 | 174 | return route; 175 | } 176 | function getParams(str) { 177 | let temp={}; 178 | if(!str){ 179 | return temp; 180 | } 181 | if(str.indexOf('=')<0){ 182 | temp[str]=""; 183 | return temp; 184 | } 185 | let arr = str.split('&'); 186 | arr.forEach(function(item) { 187 | let w = item.match(/([^=]*)=(.*)/); 188 | temp[w[1]] = w[2]; 189 | }); 190 | return temp; 191 | } 192 | export default weexVueRouter; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * weex-vue-router v0.0.3 3 | * (c) 2017 dongnaebi 4 | * @license Apache-2.0 5 | */ 6 | 'use strict'; 7 | 8 | var index$1 = Array.isArray || function (arr) { 9 | return Object.prototype.toString.call(arr) == '[object Array]'; 10 | }; 11 | 12 | var isarray = index$1; 13 | 14 | /** 15 | * Expose `pathToRegexp`. 16 | */ 17 | var index = pathToRegexp; 18 | var parse_1 = parse; 19 | var compile_1 = compile; 20 | var tokensToFunction_1 = tokensToFunction; 21 | var tokensToRegExp_1 = tokensToRegExp; 22 | 23 | /** 24 | * The main path matching regexp utility. 25 | * 26 | * @type {RegExp} 27 | */ 28 | var PATH_REGEXP = new RegExp([ 29 | // Match escaped characters that would otherwise appear in future matches. 30 | // This allows the user to escape special characters that won't transform. 31 | '(\\\\.)', 32 | // Match Express-style parameters and un-named parameters with a prefix 33 | // and optional suffixes. Matches appear as: 34 | // 35 | // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] 36 | // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] 37 | // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] 38 | '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))' 39 | ].join('|'), 'g'); 40 | 41 | /** 42 | * Parse a string for the raw tokens. 43 | * 44 | * @param {string} str 45 | * @param {Object=} options 46 | * @return {!Array} 47 | */ 48 | function parse (str, options) { 49 | var tokens = []; 50 | var key = 0; 51 | var index = 0; 52 | var path = ''; 53 | var defaultDelimiter = options && options.delimiter || '/'; 54 | var res; 55 | 56 | while ((res = PATH_REGEXP.exec(str)) != null) { 57 | var m = res[0]; 58 | var escaped = res[1]; 59 | var offset = res.index; 60 | path += str.slice(index, offset); 61 | index = offset + m.length; 62 | 63 | // Ignore already escaped sequences. 64 | if (escaped) { 65 | path += escaped[1]; 66 | continue 67 | } 68 | 69 | var next = str[index]; 70 | var prefix = res[2]; 71 | var name = res[3]; 72 | var capture = res[4]; 73 | var group = res[5]; 74 | var modifier = res[6]; 75 | var asterisk = res[7]; 76 | 77 | // Push the current path onto the tokens. 78 | if (path) { 79 | tokens.push(path); 80 | path = ''; 81 | } 82 | 83 | var partial = prefix != null && next != null && next !== prefix; 84 | var repeat = modifier === '+' || modifier === '*'; 85 | var optional = modifier === '?' || modifier === '*'; 86 | var delimiter = res[2] || defaultDelimiter; 87 | var pattern = capture || group; 88 | 89 | tokens.push({ 90 | name: name || key++, 91 | prefix: prefix || '', 92 | delimiter: delimiter, 93 | optional: optional, 94 | repeat: repeat, 95 | partial: partial, 96 | asterisk: !!asterisk, 97 | pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?') 98 | }); 99 | } 100 | 101 | // Match any characters still remaining. 102 | if (index < str.length) { 103 | path += str.substr(index); 104 | } 105 | 106 | // If the path exists, push it onto the end. 107 | if (path) { 108 | tokens.push(path); 109 | } 110 | 111 | return tokens 112 | } 113 | 114 | /** 115 | * Compile a string to a template function for the path. 116 | * 117 | * @param {string} str 118 | * @param {Object=} options 119 | * @return {!function(Object=, Object=)} 120 | */ 121 | function compile (str, options) { 122 | return tokensToFunction(parse(str, options)) 123 | } 124 | 125 | /** 126 | * Prettier encoding of URI path segments. 127 | * 128 | * @param {string} 129 | * @return {string} 130 | */ 131 | function encodeURIComponentPretty (str) { 132 | return encodeURI(str).replace(/[\/?#]/g, function (c) { 133 | return '%' + c.charCodeAt(0).toString(16).toUpperCase() 134 | }) 135 | } 136 | 137 | /** 138 | * Encode the asterisk parameter. Similar to `pretty`, but allows slashes. 139 | * 140 | * @param {string} 141 | * @return {string} 142 | */ 143 | function encodeAsterisk (str) { 144 | return encodeURI(str).replace(/[?#]/g, function (c) { 145 | return '%' + c.charCodeAt(0).toString(16).toUpperCase() 146 | }) 147 | } 148 | 149 | /** 150 | * Expose a method for transforming tokens into the path function. 151 | */ 152 | function tokensToFunction (tokens) { 153 | // Compile all the tokens into regexps. 154 | var matches = new Array(tokens.length); 155 | 156 | // Compile all the patterns before compilation. 157 | for (var i = 0; i < tokens.length; i++) { 158 | if (typeof tokens[i] === 'object') { 159 | matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$'); 160 | } 161 | } 162 | 163 | return function (obj, opts) { 164 | var path = ''; 165 | var data = obj || {}; 166 | var options = opts || {}; 167 | var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent; 168 | 169 | for (var i = 0; i < tokens.length; i++) { 170 | var token = tokens[i]; 171 | 172 | if (typeof token === 'string') { 173 | path += token; 174 | 175 | continue 176 | } 177 | 178 | var value = data[token.name]; 179 | var segment; 180 | 181 | if (value == null) { 182 | if (token.optional) { 183 | // Prepend partial segment prefixes. 184 | if (token.partial) { 185 | path += token.prefix; 186 | } 187 | 188 | continue 189 | } else { 190 | throw new TypeError('Expected "' + token.name + '" to be defined') 191 | } 192 | } 193 | 194 | if (isarray(value)) { 195 | if (!token.repeat) { 196 | throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') 197 | } 198 | 199 | if (value.length === 0) { 200 | if (token.optional) { 201 | continue 202 | } else { 203 | throw new TypeError('Expected "' + token.name + '" to not be empty') 204 | } 205 | } 206 | 207 | for (var j = 0; j < value.length; j++) { 208 | segment = encode(value[j]); 209 | 210 | if (!matches[i].test(segment)) { 211 | throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`') 212 | } 213 | 214 | path += (j === 0 ? token.prefix : token.delimiter) + segment; 215 | } 216 | 217 | continue 218 | } 219 | 220 | segment = token.asterisk ? encodeAsterisk(value) : encode(value); 221 | 222 | if (!matches[i].test(segment)) { 223 | throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') 224 | } 225 | 226 | path += token.prefix + segment; 227 | } 228 | 229 | return path 230 | } 231 | } 232 | 233 | /** 234 | * Escape a regular expression string. 235 | * 236 | * @param {string} str 237 | * @return {string} 238 | */ 239 | function escapeString (str) { 240 | return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1') 241 | } 242 | 243 | /** 244 | * Escape the capturing group by escaping special characters and meaning. 245 | * 246 | * @param {string} group 247 | * @return {string} 248 | */ 249 | function escapeGroup (group) { 250 | return group.replace(/([=!:$\/()])/g, '\\$1') 251 | } 252 | 253 | /** 254 | * Attach the keys as a property of the regexp. 255 | * 256 | * @param {!RegExp} re 257 | * @param {Array} keys 258 | * @return {!RegExp} 259 | */ 260 | function attachKeys (re, keys) { 261 | re.keys = keys; 262 | return re 263 | } 264 | 265 | /** 266 | * Get the flags for a regexp from the options. 267 | * 268 | * @param {Object} options 269 | * @return {string} 270 | */ 271 | function flags (options) { 272 | return options.sensitive ? '' : 'i' 273 | } 274 | 275 | /** 276 | * Pull out keys from a regexp. 277 | * 278 | * @param {!RegExp} path 279 | * @param {!Array} keys 280 | * @return {!RegExp} 281 | */ 282 | function regexpToRegexp (path, keys) { 283 | // Use a negative lookahead to match only capturing groups. 284 | var groups = path.source.match(/\((?!\?)/g); 285 | 286 | if (groups) { 287 | for (var i = 0; i < groups.length; i++) { 288 | keys.push({ 289 | name: i, 290 | prefix: null, 291 | delimiter: null, 292 | optional: false, 293 | repeat: false, 294 | partial: false, 295 | asterisk: false, 296 | pattern: null 297 | }); 298 | } 299 | } 300 | 301 | return attachKeys(path, keys) 302 | } 303 | 304 | /** 305 | * Transform an array into a regexp. 306 | * 307 | * @param {!Array} path 308 | * @param {Array} keys 309 | * @param {!Object} options 310 | * @return {!RegExp} 311 | */ 312 | function arrayToRegexp (path, keys, options) { 313 | var parts = []; 314 | 315 | for (var i = 0; i < path.length; i++) { 316 | parts.push(pathToRegexp(path[i], keys, options).source); 317 | } 318 | 319 | var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)); 320 | 321 | return attachKeys(regexp, keys) 322 | } 323 | 324 | /** 325 | * Create a path regexp from string input. 326 | * 327 | * @param {string} path 328 | * @param {!Array} keys 329 | * @param {!Object} options 330 | * @return {!RegExp} 331 | */ 332 | function stringToRegexp (path, keys, options) { 333 | return tokensToRegExp(parse(path, options), keys, options) 334 | } 335 | 336 | /** 337 | * Expose a function for taking tokens and returning a RegExp. 338 | * 339 | * @param {!Array} tokens 340 | * @param {(Array|Object)=} keys 341 | * @param {Object=} options 342 | * @return {!RegExp} 343 | */ 344 | function tokensToRegExp (tokens, keys, options) { 345 | if (!isarray(keys)) { 346 | options = /** @type {!Object} */ (keys || options); 347 | keys = []; 348 | } 349 | 350 | options = options || {}; 351 | 352 | var strict = options.strict; 353 | var end = options.end !== false; 354 | var route = ''; 355 | 356 | // Iterate over the tokens and create our regexp string. 357 | for (var i = 0; i < tokens.length; i++) { 358 | var token = tokens[i]; 359 | 360 | if (typeof token === 'string') { 361 | route += escapeString(token); 362 | } else { 363 | var prefix = escapeString(token.prefix); 364 | var capture = '(?:' + token.pattern + ')'; 365 | 366 | keys.push(token); 367 | 368 | if (token.repeat) { 369 | capture += '(?:' + prefix + capture + ')*'; 370 | } 371 | 372 | if (token.optional) { 373 | if (!token.partial) { 374 | capture = '(?:' + prefix + '(' + capture + '))?'; 375 | } else { 376 | capture = prefix + '(' + capture + ')?'; 377 | } 378 | } else { 379 | capture = prefix + '(' + capture + ')'; 380 | } 381 | 382 | route += capture; 383 | } 384 | } 385 | 386 | var delimiter = escapeString(options.delimiter || '/'); 387 | var endsWithDelimiter = route.slice(-delimiter.length) === delimiter; 388 | 389 | // In non-strict mode we allow a slash at the end of match. If the path to 390 | // match already ends with a slash, we remove it for consistency. The slash 391 | // is valid at the end of a path match, not in the middle. This is important 392 | // in non-ending mode, where "/test/" shouldn't match "/test//route". 393 | if (!strict) { 394 | route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?'; 395 | } 396 | 397 | if (end) { 398 | route += '$'; 399 | } else { 400 | // In non-ending mode, we need the capturing groups to match as much as 401 | // possible by using a positive lookahead to the end or next path segment. 402 | route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)'; 403 | } 404 | 405 | return attachKeys(new RegExp('^' + route, flags(options)), keys) 406 | } 407 | 408 | /** 409 | * Normalize the given path string, returning a regular expression. 410 | * 411 | * An empty array can be passed in for the keys, which will hold the 412 | * placeholder key descriptions. For example, using `/user/:id`, `keys` will 413 | * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. 414 | * 415 | * @param {(string|RegExp|Array)} path 416 | * @param {(Array|Object)=} keys 417 | * @param {Object=} options 418 | * @return {!RegExp} 419 | */ 420 | function pathToRegexp (path, keys, options) { 421 | if (!isarray(keys)) { 422 | options = /** @type {!Object} */ (keys || options); 423 | keys = []; 424 | } 425 | 426 | options = options || {}; 427 | 428 | if (path instanceof RegExp) { 429 | return regexpToRegexp(path, /** @type {!Array} */ (keys)) 430 | } 431 | 432 | if (isarray(path)) { 433 | return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options) 434 | } 435 | 436 | return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options) 437 | } 438 | 439 | index.parse = parse_1; 440 | index.compile = compile_1; 441 | index.tokensToFunction = tokensToFunction_1; 442 | index.tokensToRegExp = tokensToRegExp_1; 443 | 444 | /** 445 | * Created by ebi on 2017/2/14. 446 | */ 447 | var weexVueRouter = { 448 | install: function install(Vue, ref){ 449 | var routes = ref.routes; 450 | var weex = ref.weex; 451 | 452 | var platform = weex.config.env ? weex.config.env.platform : weex.config.platform; 453 | if (platform.toLowerCase() == 'web'){ return; } 454 | var navigator = weex.requireModule('navigator'); 455 | var bundleUrl = weex.config.bundleUrl; 456 | var route = bundleToPath(bundleUrl, routes); 457 | Object.defineProperty(Vue.prototype, "$router", { 458 | value: { 459 | push: function push(url){ 460 | var bundle = pathToBundle(url, routes); 461 | if (navigator) { 462 | console.log(bundle); 463 | navigator.push({ 464 | 'url': bundle, 465 | 'animated': 'true' 466 | }, function () { 467 | console.log('skip complete'); 468 | }); 469 | } 470 | }, 471 | back: function back(){ 472 | if (navigator) { 473 | navigator.pop(); 474 | } 475 | } 476 | }, 477 | configurable: false 478 | }); 479 | Object.defineProperty(Vue.prototype, '$route', { 480 | configurable: false, 481 | value: { 482 | path: route.path, 483 | params: route.params, 484 | query: route.query, 485 | hash: route.hash, 486 | fullPath: route.fullPath, 487 | matched: route.matched, 488 | name: route.name 489 | } 490 | }); 491 | } 492 | }; 493 | function pathToBundle(url,routes){ 494 | /* url='/list/2-1?from=1#2' 495 | * r={path:'/list/:cid-:id',bundle:'/product/list.js'} 496 | * */ 497 | if(url.indexOf('/')!=0){ 498 | console.error("the url must begin with '/'"); 499 | return ''; 500 | } 501 | 502 | //copy from vue-router 503 | var encodeReserveRE = /[!'()*]/g; 504 | var encodeReserveReplacer = function (c) { return '%' + c.charCodeAt(0).toString(16); }; 505 | var encode = function (str) { return encodeURIComponent(str) 506 | .replace(encodeReserveRE, encodeReserveReplacer) 507 | .replace(/%2C/g, ','); }; 508 | 509 | /*find out the rule*/ 510 | var matchRule={}; 511 | routes.forEach(function (r) { 512 | var re=index(r.path); 513 | var match=re.exec(url); 514 | if(match!=null){ 515 | matchRule = r; 516 | } 517 | }); 518 | 519 | /*get the key and value*/ 520 | var keys = []; 521 | var pathReg = index(matchRule.path, keys); 522 | var values=pathReg.exec(url); 523 | var lastValue=values[values.length-1];//save the last value to find query and hash 524 | values[values.length-1]=lastValue.split(/\?|\#/)[0];//the true value 525 | 526 | /*parse params to key/value object*/ 527 | var params={}; 528 | if(keys.length>0){ 529 | keys.forEach(function (key,i){ 530 | params[key.name]=values[i+1]; 531 | }); 532 | } 533 | 534 | /*get query and hash*/ 535 | var queryIndex=lastValue.indexOf('?'); 536 | var hashIndex=lastValue.indexOf('#'); 537 | if (queryIndex > 0 && hashIndex > 0 && queryIndex > hashIndex) { 538 | console.error("Could not set '#' behind '?'"); 539 | return ''; 540 | } 541 | var queryStr=queryIndex>0?lastValue.substring(queryIndex+1,hashIndex>0?hashIndex:lastValue.length):""; 542 | var hashStr=hashIndex>0?lastValue.substring(hashIndex,lastValue.length):""; 543 | var query=getParams(queryStr);//{from:1} 544 | 545 | /*add the bundleUrl's params and hash*/ 546 | var componentPath=matchRule.component; 547 | for(var k in params){ 548 | componentPath+=(componentPath.indexOf('?')>0?'&':'?')+k+'='+encode(params[k]); 549 | } 550 | for(var q in query){ 551 | componentPath+=(componentPath.indexOf('?')>0?'&':'?')+q+'='+encode(query[q]); 552 | } 553 | componentPath+=hashStr; 554 | return componentPath; 555 | } 556 | function bundleToPath(url,routes){ 557 | //url='domain/product/list.js?cid=2&id=1&from=1' 558 | //matchRule={path:'/list/:cid-:id',component:'domain/product/list.js'} 559 | var route={ 560 | params:null, 561 | query:null, 562 | hash:null, 563 | path:null, 564 | fullPath:null, 565 | matched:null, 566 | name:null 567 | }; 568 | var jsBundle=url.split(/\?|\#/)[0]; 569 | /*find out the rule*/ 570 | var matchRule=null; 571 | routes.forEach(function (r) { 572 | r.component==jsBundle&&(matchRule=r); 573 | //http://192.168.253.124:8080/dist/product/list.js 574 | }); 575 | if(!matchRule){ 576 | console.error(("your component must be like '" + jsBundle + "',can not find it in routes,please check up")); 577 | return route; 578 | } 579 | 580 | /*use pathToRegexp*/ 581 | var keys = []; 582 | index(matchRule.path, keys); 583 | 584 | /*get query and hash*/ 585 | var queryIndex=url.indexOf('?'); 586 | var hashIndex=url.indexOf('#'); 587 | var queryStr=queryIndex>0?url.substring(queryIndex+1,hashIndex>0?hashIndex:url.length):""; 588 | route.hash=hashIndex>0?url.substring(hashIndex,url.length):""; 589 | 590 | var allQuery=getParams(queryStr);//{cid:2,id:1,from:1} 591 | 592 | var params={},//{cid:2,id:1} 593 | query={},//{from:1} 594 | paramsKey=[];//['cid','id'] 595 | if(keys.length>0){ 596 | paramsKey=keys.map(function (key){ return key.name; }); 597 | } 598 | for(var q in allQuery){ 599 | allQuery[q]=decodeURIComponent(allQuery[q]); 600 | paramsKey.indexOf(q)<0?query[q]=allQuery[q]:params[q]=allQuery[q]; 601 | } 602 | route.params=params; 603 | route.query=query; 604 | 605 | //path and fullPath 606 | var path=matchRule.path; 607 | for(var p in params){ 608 | path=path.replace(':'+p,params[p]); 609 | } 610 | route.path=path; 611 | var queryArr=[]; 612 | for(var i in query){ 613 | queryArr.push(i+'='+query[i]); 614 | } 615 | route.fullPath=path+'?'+queryArr.join('&')+route.hash; 616 | route.matched=matchRule; 617 | route.name=matchRule.name; 618 | 619 | return route; 620 | } 621 | function getParams(str) { 622 | var temp={}; 623 | if(!str){ 624 | return temp; 625 | } 626 | if(str.indexOf('=')<0){ 627 | temp[str]=""; 628 | return temp; 629 | } 630 | var arr = str.split('&'); 631 | arr.forEach(function(item) { 632 | var w = item.match(/([^=]*)=(.*)/); 633 | temp[w[1]] = w[2]; 634 | }); 635 | return temp; 636 | } 637 | 638 | module.exports = weexVueRouter; 639 | --------------------------------------------------------------------------------