Skip to content

Commit bb8b5df

Browse files
毛瑞Maorey
authored andcommitted
Feat: 开发环境history路由支持
1 parent 64ab1c2 commit bb8b5df

File tree

15 files changed

+143
-124
lines changed

15 files changed

+143
-124
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# 更新日志
22

3+
## v 1.2.18
4+
5+
- 完善多SPA history 路由支持
6+
37
## v 1.2.17
48

59
- 完善多SPA支持

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
328328
- 尽量**不要使用全局注册**(插件/组件/指令/混入等)以优化性能并且代码更清晰、更易维护
329329
- 尽量**按照依赖库的文档描述**来使用她, 从其源码(src)引入模块(css/scss/.../js/mjs/ts/jsx/tsx/vue), 将可能**不会被处理**且更可能随版本更新改变, 需要时可以从其构建后的 lib/dist 等目录引入或者增加一些配置(需要了解模块解析及转码规则和相关插件, 不推荐)
330330
- 若开发环境出现缓存相关错误信息导致热更新慢, 可以删除 `node_modules/.cache` 文件夹再试
331+
- 路由路径不应出现符号 `.` , 以方便 `history 路由` 模式开发/部署
331332

332333
### 风格建议
333334

@@ -656,8 +657,8 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
656657
```
657658
658659
- 所有视图组件可接收props:`route`代替`this.$route`, 区别是: **只在首次进入当前视图或当前视图url发生变化时改变**
659-
- 路由视图不需要被缓存的, 可以在meta申明/`deactivated`钩子销毁实例(`this.$destroy()`)或`activated`钩子进行更新
660-
- 为避免渲染错误, 请务必为<b style="color: red;">循环创建的组件</b>**加上 `key`**, 特别是 `tsx/ts/jsx/js`
660+
- **路由视图**不需要被缓存的, 可以在`meta`申明/`deactivated`钩子销毁实例(`this.$destroy()`)或`activated`钩子进行更新
661+
- 为避免渲染错误, 请务必为 <b style="color: red;">循环创建的组件</b> **加上 `key`**, 需要特别注意 `tsx/ts/jsx/js` 文件(没有代码提示)
661662
662663
### 配置和优化
663664
@@ -699,19 +700,21 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
699700
- 开启 `gzip` 压缩, 并重用已有 `gz` 文件 `gzip_static on;`
700701
- 缓存静态资源(html 可减少缓存时间)
701702
- [HTTP2 Server Push](https://www.nginx.com/blog/nginx-1-13-9-http2-server-push) 服务器推送, 需要 `nginx` 版本**1.13.9**及以上, [文档链接](http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_push_preload)
702-
- 多个SPA history路由部署, 只能一个SPA一个location了么(待运维大佬解决)?
703+
- 多个SPA(即 `html` 文件)以**history路由**(访问url不以`#`号标识)部署:
704+
- 一个 `html` 一个 location 或 待运维大佬完善(示例如下)
705+
- 按照注释修改**每个** `html` 的配置, 其中 `base` 改为对应访问路径
703706
704707
配置示例( `nginx.conf` 文件, `xxx` 换成对应值):
705708
706709
```bash
707710
http {
708-
include /etc/nginx/mime.types;
711+
include xxx/mime.types;
709712
default_type application/octet-stream;
710713

711714
# log_format main '$remote_addr - $remote_user [$time_local] "$request" '
712715
# '$status $body_bytes_sent "$http_referer" '
713716
# '"$http_user_agent" "$http_x_forwarded_for"';
714-
# access_log /var/log/nginx/access.log main;
717+
# access_log xxx/access.log main;
715718

716719
sendfile on;
717720
# tcp_nopush on;
@@ -771,7 +774,7 @@ http {
771774
# error_page 404 /404.html; # 未知页
772775

773776
location / {
774-
# rewrite ^/(?:path|path-alias)/(.*)$ /$1 last; # 兼容某些路由
777+
# rewrite ^/(?:path|path-alias)/(.*)$ /$1 last; # 兼容某些url
775778
# 设置静态资源缓存(文件名已带内容哈希了)
776779
if ($uri ~ .*\.(?:js|css|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|eot|mp4)$) {
777780
expires 7d; # d: 天
@@ -787,7 +790,7 @@ http {
787790

788791
set $u /; # for 多页history路由 其他location: ^/location([^/]+)
789792
if ($uri ~ ^/([^/]+)) {
790-
set $u $1.html; # 待测试
793+
set $u $1.html; # 待测试并完善
791794
}
792795
try_files $uri $uri/ $uri.html $u / =404;
793796
# try_files $uri $uri/ $uri.html /location$u /location =404;

build/devServer.js

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = function(ENV, PAGES) {
88
const TARGET = 'PROXY_TARGET'
99
const FIELD = ENV.PROXY_FIELD
1010
const REG_BASE = /^BASE_PATH(\d*)$/
11-
const REG_URL = /^((?:http|ws)s?:\/\/)[^:/]+(.*)/
11+
const REG_URL = /^((?:http|ws)s?:\/\/)[^:/]+(.*)$/
1212

1313
const removeField = (url, field) =>
1414
url.replace(
@@ -73,8 +73,11 @@ module.exports = function(ENV, PAGES) {
7373
}
7474

7575
// http2 应该是不能配置了
76-
const REG_SLASHES = /\/+/g
77-
const REG_FILES = /\..+$/
76+
const REG_SPA = /^\/([^/]+)(.*)$/
77+
const REG_HTML = /\.html$/
78+
const REG_FILES = /[^/]+\.[^/]+$/
79+
const REG_PATH = /^(?:http|ws)s?:\/\/[^/]+(.*)$/
80+
const REG_SLASHES = /\/+/
7881
return {
7982
host,
8083
port,
@@ -84,26 +87,35 @@ module.exports = function(ENV, PAGES) {
8487
overlay: { errors: true }, // lint
8588
openPage: ENV.DEV_SERVER_PAGE || '',
8689
historyApiFallback: {
90+
// index: '/index.html',
8791
rewrites: [
8892
{
8993
// SPA可省略.html 支持history路由(路径不能有'.', 因为用REG_FILES匹配文件)
90-
// TODO: 精确匹配已有资源
9194
from: /./,
92-
to({ parsedUrl: { pathname, search } }) {
93-
search || (search = '')
94-
const paths = pathname.replace(ENV.BASE_URL, '').split('/')
95-
paths[0] || paths.shift()
96-
const entry = paths.shift()
95+
to(context) {
96+
const parsedUrl = context.parsedUrl
97+
const search = parsedUrl.search || ''
98+
let pathname = REG_SPA.exec(parsedUrl.pathname)
9799

98-
return (
99-
(PAGES.includes(entry)
100-
? `${ENV.BASE_URL}/${
101-
paths.length && REG_FILES.test(pathname)
102-
? paths.join('/')
103-
: `${entry}.html`
104-
}`.replace(REG_SLASHES, '/')
105-
: pathname) + search
106-
)
100+
const entry = pathname[1].replace(REG_HTML, '')
101+
if (PAGES[entry]) {
102+
pathname = pathname[2]
103+
if (REG_FILES.test(pathname)) {
104+
let referer = context.request.headers.referer
105+
if (referer) {
106+
pathname = parsedUrl.pathname.split(REG_SLASHES)
107+
referer = referer.replace(REG_PATH, '$1').split(REG_SLASHES)
108+
while (pathname[0] === referer[0]) {
109+
pathname.shift()
110+
referer.shift()
111+
}
112+
return '/' + pathname.join('/') + search
113+
}
114+
return pathname + search
115+
}
116+
return `/${entry}.html${search}`
117+
}
118+
return parsedUrl.pathname + search
107119
},
108120
},
109121
],

build/development.config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
* @param {chainWebpack} config 配置对象
1111
* https://github.com/neutrinojs/webpack-chain#getting-started
1212
*/
13-
module.exports = function(config) {
13+
module.exports = function(config, ENV) {
1414
// https://webpack.js.org/configuration/devtool/#devtool
15-
config.devtool(process.env.DEV_TOOL || 'eval')
15+
config.devtool(ENV.DEV_TOOL || 'eval')
1616
/// 避免同名.vue文件sourceMap冲突 ///
1717
// https://webpack.js.org/configuration/output/#outputdevtoolmodulefilenametemplate
1818
// config.output.devtoolFallbackModuleFilenameTemplate(
@@ -35,7 +35,7 @@ module.exports = function(config) {
3535

3636
// return `webpack://${info.namespace}/${fileName}`
3737
// })
38-
// config.output.ecmaVersion(+process.env.ES_VERSION || 6) // WIP
38+
// config.output.ecmaVersion(+ENV.ES_VERSION || 6) // WIP
3939

4040
/// 文件监听 ///
4141
config.watchOptions({ ignored: /node_modules/ })

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-tpl",
3-
"version": "1.2.17",
3+
"version": "1.2.18",
44
"private": false,
55
"description": "vue + vuex + vue router + TypeScript(支持 JavaScript) 模板",
66
"author": "毛瑞 <Maorey@Foxmail.com>",

src/components/RouterViewTransparent.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@ import getKey from '@/utils/getKey'
1616
// import(/* webpackChunkName: "ihOne" */ './ModuleOne')
1717
// )
1818

19-
/** 透明分发路由(支持嵌套)
19+
/** 透明分发路由(支持嵌套), props: { max: number }
2020
* 可以给个key防止<RVT>复用:
2121
* <KeepAlive>
2222
* <RouterView :key="$route.meta.code" />
2323
* <KeepAlive />
2424
*/
2525
export default {
2626
name: 'RVT',
27-
props: ['route'],
27+
props: ['route', 'max'],
2828
data() {
29-
return { d: 0 } // 是否失活/离开
29+
return { d: 0 } // d: 是否失活/离开
3030
},
3131
beforeRouteUpdate(this: any, to, from, next) {
3232
this.d = 0
@@ -47,22 +47,14 @@ export default {
4747
return this.n
4848
}
4949

50+
let max = this.max
51+
max > 1 || (max = CONFIG.subPage > 1 ? CONFIG.subPage : 1)
5052
const meta = (this.route || this.$route).meta
53+
meta.k || (meta.k = getKey('v'))
5154
return (this.n = h(
5255
'KeepAlive',
53-
{
54-
props: {
55-
exclude: this.$router.$.e,
56-
max: CONFIG.subPage > 1 ? CONFIG.subPage : 1,
57-
},
58-
},
59-
[
60-
h(
61-
'RouterView',
62-
{ key: meta.k || (meta.k = getKey('v')) },
63-
this.$slots.default
64-
),
65-
]
56+
{ props: { exclude: this.$router.$.e, max: max } },
57+
[h('RouterView', { key: meta.k }, this.$slots.default)]
6658
))
6759
},
6860
} as Component

src/config/index.ts

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
export default {
77
/*! 【全局配置(时间单位ms)】 */
88

9-
/* !【↓应用跳转配置↓】history路由必须绝对路径 */
10-
/*! 索引页 */
11-
/** 索引页 */
9+
/*! 【↓ SPA配置 ↓】history路由必须绝对路径 */
10+
/*! 首页 */
11+
/** 首页 */
1212
index: './',
1313

1414
/*! 登录页 */
@@ -26,16 +26,26 @@ export default {
2626
/*! 错误页 */
2727
/** 错误页 */
2828
error: '50x',
29+
/*! 【↑ SPA配置 ↑】 */
2930

30-
/* !【↑应用跳转配置↑】 */
31-
32-
/*! 接口地址(hash路由建议相对路径, 比如'api') */
33-
/** 接口地址(hash路由建议相对路径, 比如'api') */
34-
baseUrl: process.env.BASE_PATH,
35-
36-
/*! 网站路径, history路由必须/开头 */
37-
/** 网站路径, history路由必须/开头 */
38-
base: '',
31+
/** 去指定SPA
32+
* @param id SPA ID, 见this键值
33+
*
34+
* falsy: 去登录页
35+
*
36+
* string: 去指定页
37+
*
38+
* 不存在的id: 未知页
39+
* @param query 查询参数 自己拼 ?foo=0&bar=1#hash...
40+
*/
41+
g(id?: string, search?: string) {
42+
try {
43+
window.stop() // 停止加载资源
44+
} catch (error) {}
45+
location.href =
46+
(id ? (this as any)[id] || this.notFind : this.login) + (search || '')
47+
throw 0 // eslint-disable-line no-throw-literal
48+
},
3949

4050
/*! 接口请求超时 0表示不限制 */
4151
/** 接口请求超时 0表示不限制 */
@@ -49,14 +59,6 @@ export default {
4959
/** 全局接口响应缓存最大存活时间 */
5060
apiCacheAlive: 3 * 1000,
5161

52-
/*! token cookie 字段 */
53-
/** token cookie 字段 */
54-
cookie: 'Authorization',
55-
56-
/*! token head 字段 */
57-
/** token head 字段 */
58-
head: 'Authorization',
59-
6062
/*! 身份有效期(取与服务端有效期的最小值) */
6163
/** 身份有效期(取与服务端有效期的最小值) */
6264
tokenAlive: 2 * 60 * 60 * 1000,
@@ -72,22 +74,4 @@ export default {
7274
/*! 最大页面缓存时间 */
7375
/** 最大页面缓存时间 */
7476
pageAlive: 30 * 1000,
75-
76-
/** 去指定页
77-
* @param id SPA ID, 见this键值
78-
* falsy: 去登录页
79-
* string: 去指定页
80-
* 不存在的id: 未知页
81-
*/
82-
g(id?: string) {
83-
if (id) {
84-
location.href = (this as any)[id] || this.notFind
85-
} else {
86-
try {
87-
window.stop() // 停止加载资源
88-
} catch (error) {}
89-
location.href = this.login
90-
throw 0 // eslint-disable-line no-throw-literal
91-
}
92-
},
9377
}

src/pages/index/config/index.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
/*
2-
* @Description: index页全局配置
3-
* @Author: 毛瑞
4-
* @Date: 2019-07-08 17:00:16
5-
*/
1+
/** SPA 配置 */
62
export default {
7-
/*! 【index页配置】 */
3+
/*! 【↓ history路由必须绝对路径 ↓】 */
4+
/*! 网站路径 */
5+
/** 网站路径 */
6+
base: '',
7+
8+
/*! 接口地址 */
9+
/** 接口地址 */
10+
baseUrl: process.env.BASE_PATH,
11+
/*! 【↑ history路由必须绝对路径 ↑】 */
812

913
/*! 图表重绘间隔(ms) */
1014
/** 图表重绘间隔(ms)

src/pages/index/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import router from './router'
88
import store from './store'
99
import App from './App'
1010

11+
import CONFIG from './config'
1112
import mount from '@/functions/main'
13+
import { setBase } from '@/utils/ajax'
1214
import './registerServiceWorker'
1315

16+
setBase(CONFIG.baseUrl)
1417
mount(App, router, store)

src/pages/index/route/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
import { RouterOptions } from 'vue-router'
77

8-
import CONFIG from '@/config'
8+
import CONFIG from '../config'
99
import { home, about } from '@index/views'
1010

1111
export default {

0 commit comments

Comments
 (0)