起因
目前项目采用的是经典的MVC架构,前端模版使用vm,对于即会java又懂前端的人来说,这种方式没有什么不好,反而开发会很快。
但是,现在的互联网是分工协作的,我们现在的方式是前端开发人员负责静态页面样式及交互的开发,后端人员拿到静态页面进行数据的绑定,如果一次能搞定,那当然没什么问题,但是一旦涉及到bug的修改,功能的调整,那就是折磨了。再加上几经人手,前端代码已经丑陋不堪。所以,我萌生出了对项目进行重构并进行前后端分离的想法。为什么要前后端分离,这篇文章讲得不错
刚好最近学习了react,实战中也尝试了下,并且我们的这个项目特别适合做成一个SPA(single page application,单页面应用),所以就决定利用其进行逐步重构。
项目路径规划
因为SPA的代码最后都需要编译打包成一个文件,所以我把我的前端工程跟java工程放在一起,方便文件直接生成到java工程中
webpack打包路径设置
1 | ... |
如何方便的与后端进行接口联调
前后端分离时,数据交换一般采取http的方式。开发时由于前后端不在同一个域,需要进行一些处理,以解决js跨越的限制。
常见的方法有:
- 浏览器设置(chrome),原来确实是一个最方便的方式,在新版的chrome浏览器上试过,但是没成功
- jsonp的方式,有点麻烦,放弃
- 后端设置http头(Access-Allow-Control-Origin),本公司后端不会,懒得去求他们,还是自己想办法吧。
- ngix,最后决定用这个
如果我将ngix配置成如下所示: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#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
#access_log logs/host.access.log main;
location /api {
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
#rewrite (.*) site-web-personal/$1 permanent;
proxy_set_header Host $host;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.10.171:8282/api;
}
location / {
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
#rewrite (.*) site-web-personal/$1 permanent;
proxy_set_header Host $host;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.10.169:8888/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
这样当我访问http://localhost/*
时会转发到我本地起的前端调试web服务,当我访问http://localhost/api
时会转发到后端接口服务。
但是,我们的后端一开始并没有统一规划,所以也就没有区别接口的url地址和访问页面的url地址。
最后想到的办法是,利用eclipse->server可以给工程配置一个统一的url根目录来解决:
然后ngix
配置改成:1
2
3
4
5
6
7
8
9
10
11
12
13...
location /house-web-web {
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
#rewrite (.*) site-web-personal/$1 permanent;
proxy_set_header Host $host;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.10.171:8282/house-web-web;
}
...
此时,访问http://localhost/house-web-web
的请求会转发给后台接口,当然前端也需要进行配置:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 上线需要改这两个参数
window.debug = true;
// baseurl
window.apiBase = window.debug ? '/house-web-web' : ''
// 前端路由
window.url = {
'discoverIndex' : '/discover/index.htm', // 发现首页
'discoverDetail' : '/discover/detail.htm', // 发现详情
'searchIndex' : '/search/index.htm', // 搜索首页
'searchDetail' : '/search/detail.htm' // 搜索详情
}
// api
window.api = {
'discoverIndex': window.apiBase + '/discover/getIndexJson.htm', //发现首页
'discoverDetail': window.apiBase + '/discover/detailPage.htm', // 发现详情
}
这样,此时在浏览器中访问http://localhost/#/discover/index.htm
则会到本地前端调试的服务,在该页面中访问http://localhost/house-web-web/discover/getIndexJson.htm
则会访问后台的接口
前端路由
react的前端路由有两种方式hashHistory
和browserHistory
由于开发时webpack-dev-server
启动的服务不支持browserHistory
,所以开发时用hashHistory
,生产上用browserHistory
1
2
3
4
5
6
7
8<Router history={window.debug ? hashHistory : browserHistory}>
<Route path="/">
<Route path={window.url.discoverIndex} component={DiscoverList}/>
<Route path={window.url.discoverDetail} component={DiscoverDetail}/>
<Route path={window.url.searchIndex} component={SearchIndex}/>
<Route path={window.url.searchDetail} component={SearchDetail}/>
</Route>
</Router>
图片处理
图片有点不好弄,因为react中引入图片是相对于工程的路径,而到浏览器中访问时是相对于java静态文件目录的路径,所以这里的处理方式是:
- 对于小图片,统一使用base64格式,这样不仅优化了访问速度,也解决了图片路径问题
- 对于大图片,保证前端图片路径与java静态文件图片路径一致,且图片的访问路径用绝对路径
例子:1
background-image: url('/assets/i/mmsf/search-banner.png')
为了确保webpack对图片进行处理,在react中对用到的图片均import
1
2
3// 这里引入只是为了使webpack将图片拷贝到正确的路径
import bannerElderly from '../../assets/i/mmsf/discover-detail-banner.png'
import iconShare from '../../assets/i/mmsf/icon-share.png'
后端url处理
由于生产上还是需要使用真实路径,所以需要后端进行配合,所有用前端路由的url地址均渲染mmsfIndex.vm
总结
总的下来,实现这一方案,用了很多非主流的方法,也让我知道了做技术只要掌握了原理,是可以根据自己的需要进行变通的。接下来就是逐步把原来的页面都迁移到新项目中来了,可以跟原来丑陋的代码说拜拜咯。