{"id":20542209,"url":"https://github.com/weivea/fe-separate","last_synced_at":"2025-04-14T09:11:58.992Z","repository":{"id":83302244,"uuid":"48407370","full_name":"weivea/FE-separate","owner":"weivea","description":"前后端分离想法的一些实践","archived":false,"fork":false,"pushed_at":"2016-09-06T06:19:16.000Z","size":3101,"stargazers_count":13,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-14T09:11:40.472Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/weivea.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-12-22T03:07:35.000Z","updated_at":"2018-03-28T09:18:44.000Z","dependencies_parsed_at":"2024-01-21T02:15:09.353Z","dependency_job_id":null,"html_url":"https://github.com/weivea/FE-separate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weivea%2FFE-separate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weivea%2FFE-separate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weivea%2FFE-separate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weivea%2FFE-separate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/weivea","download_url":"https://codeload.github.com/weivea/FE-separate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248852182,"owners_count":21171842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-16T01:29:48.362Z","updated_at":"2025-04-14T09:11:58.975Z","avatar_url":"https://github.com/weivea.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 前后端分离\n\n前言：\n弄这个其实并不是有什么高大上的想法，只是觉得现在前端工作做得不太爽。遇到Python要怼Python模板，遇到PHP要怼PHP模板，遇到JAVA要怼JAVA模板。\n一个公司里面搞这么多种后台？是的~\n所以web前端就成了部门中的劳务输出，，今天跟phper玩儿，明天跟jsper玩儿，eclipse,phpstorm,PyCharm会完了，好牛逼，就是个切图滴~\n\n咳咳。那些都是开玩笑的，咱们真正目的是前后端开发解耦，职责分离，这种高大尚的目的。\n顺便把咱前端从劳务输出提升为业务承包的角色。\n\n#### 一、要解决的问题\n1. 套模板的忧桑，套各种模板，套各种后端下的模板。上线时常依赖后端（其实也有办法）。后端同学也觉得麻烦。\n2. 调试，套完模板要调试。最好的办法就是吧后台环境也搭载自己的机器上，喝喝。后端同学改了点啥，你不懂一刷就报错，只有求人家后端同学，帮你一次还行，帮你一百次啊？\n3. 代码复用问题，敢问a项目的模板你能拿到b项目来用吗？可以！copy过来：）a项目是php,b项目是JAVA，来呀，copy呀。小样~\n4. 今天我跟后端的同学说，这个，这个，和这个页面，在后端渲染。其他的在前端渲染。后端同学心中默念，人丑多做怪，，，然后我说，要不你帮我做个bigpipe。后端同学：要啥？\n要啥派？咱能不能好好把业务怼上去。别搞些有的没的~\n5. 。。。。。。\n\n#### 二、解决方案\n\n上图：\n\n![Alt 图片](img/liucheng.png)\n\n为什么用nodejs,因为~我只会nodejs.\n其实到这里，我要表达的差不多表达完了。\n\n策略就是这么个策略，主要看怎么实现。\n该解决方案，session，登录态什么的仍然是后端管理;\n前端仍然只管理模板，只不过起了后台来管理模板；对后端来说，除了nginx配置改变几乎没有任何新的东西。\n\n\n1、nginx配置：\n\n```shell\n\n#转发配置\n  server {\n    listen 80;\n    server_name  proxy.xiaoying.com;\n    #server_name _;\n\n    #root   /Users/weijianli/Work/proxy-test/laravel/public;\n    #index  index.php index.html index.htm;\n    access_log  /usr/local/var/log/nginx/proxy.xiaoying.log  main;\n\n    location ^~ / {\n        #proxy_redirect off;\n        proxy_pass_header X-CSRF-TOKEN;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-NginX-Proxy true;\n        proxy_set_header Host $host;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_pass_header Set-Cookie;\n        proxy_pass http://localhost:4000/;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        #proxy_redirect http:// https://;\n    }\n    location /api/ {\n        #proxy_redirect off;\n        proxy_pass_header X-CSRF-TOKEN;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-NginX-Proxy true;\n        proxy_set_header Host $host;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_pass_header Set-Cookie;\n        proxy_pass http://localhost:5000/api/;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        #proxy_redirect http:// https://;\n    }\n\n    location ^~ /api2/ {\n        proxy_pass_header X-CSRF-TOKEN;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-NginX-Proxy true;\n        proxy_set_header Host $host;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_pass_header Set-Cookie;\n        proxy_pass http://proxy.xiaoying.com:10001/api2/;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n#laravel服务\nserver {\n    listen 10001;\n    server_name  proxy.xiaoying.com;\n\n    root   /Users/weijianli/Work/proxy-test/laravel/public;\n    index  index.php index.html index.htm;\n    access_log  /usr/local/var/log/nginx/proxy10001.xiaoying.log  main;\n\n    location / {\n        try_files $uri $uri/ /index.php?$query_string;\n    }\n\n    location ~ \\.php {\n        fastcgi_pass 127.0.0.1:9000;\n        try_files $uri /index.php = 404;\n        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;\n        include fastcgi_params;\n    }\n}\n```\n\n2、web端的处理\n思路：\n![Alt 图片](img/webliucheng.png)\n\n//这是在，web与server在同一台机器上的解决办法。\n\n```javascript\n\n//关键之处就是我们要正确合适的传递header内容，做一个nice的proxy\nfunction proxy(ctx, cb){//ctx是http请求中的上下文，主要为获得其中的req\n    var opt = {\n        host:     127.0.0.1,\n        port:     serverPort,\n        agent:    false,\n        path:     getPath(ctx.req),\n        method:   ctx.req.method,\n        headers:  getHeader(ctx.req)\n    };\n    log('#%d\\t%s http://%s%s', num, ctx.req.method, opt.host, opt.path);\n    var req2 = http.request(opt, function (res2) {\n        //console.log(res2);\n        var serverData = '';\n        res2.on('data', function (chunk) {\n            //console.log('BODY: ' + chunk);\n            serverData += chunk;\n        });\n        res2.on('end', function() {\n            //回调函数，拿到server端传来的数据，选染成你想要的样子，或是直接转发给浏览器，记得setHeader\n            cb(null,{data:serverData,header:res2.headers});\n        })\n\n    });\n    if (/POST|PUT/i.test(ctx.req.method)) {\n        ctx.req.pipe(req2);\n    } else {\n        req2.end();\n    }\n    req2.on('error', function (err) {\n        log('#%d\\tERROR: %s', num, err.stack);\n        //res.end(err.stack);\n    });\n}\n\n```\n\n//这是在，web与server可能不在同一台机器上时。\n\n```javascript\n\n//关键之处就是我们要正确合适的传递header内容，做一个nice的proxy\nconst template = require('art-template');\nconst http = require(\"http\");\nconst url = require(\"url\");\nconst util = require('util');\nconst buffertools = require('buffertools');\nconst proxyCfg = require('./proxyCfg');\ntemplate.config('cache', false);\n\n/**\n * 模板代理方法\n * params:\n * sreq:服务请求实例\n * sres:服务返回实例\n *\n * */\nfunction proxyFun(sreq, sres) {\n  if(sreq.method == 'GET'){\n    var url_parts = url.parse(sreq.url);\n    var opts = {\n      host: proxyCfg.server.host,\n      port: proxyCfg.server.port,\n      path: url_parts.path + (url_parts.hash || ''),\n      headers: sreq.headers\n    };\n    var chunkBuffers = [];\n    var re;\n    var creq = http.get(opts, (cres) =\u003e {\n      if (cres.statusCode != 200) {\n        sres.writeHead(cres.statusCode, cres.headers);\n        cres.pipe(sres);\n      } else {\n        cres.on('data', (chunk) =\u003e {\n            chunkBuffers.push(chunk);\n          })\n          .on(\"end\", function () {\n            if(chunkBuffers.length ==0){\n              re = '';\n            } else if(chunkBuffers.length\u003e1){\n              re = buffertools.concat(...chunkBuffers).toString();\n            }else {\n              re = chunkBuffers[0].toString()\n            }\n            try{\n              re = JSON.parse(re)\n            }catch (e){\n              util.log('data can`t be parsed:'+re);\n              re = {err:1,str:'data can`t be parsed'};\n            }\n            delete cres.headers['connection'];\n            delete cres.headers['content-type'];\n            delete cres.headers['content-length'];\n            sres.writeHead(cres.statusCode, cres.headers);\n            if(re){\n              sres.end(template(__dirname + proxyCfg.proxyView[url_parts.pathname],re));\n            }else{\n              sres.end('');\n            }\n          });\n      }\n    }).on('error', (e) =\u003e {\n      util.log(`Got error: ${e.message}`);\n      sres.writeHead(502, {'Content-Type': 'text/plain'});\n      sres.end(JSON.stringify(e));\n    });\n    sreq.pipe(creq);\n  }else {\n    var err = `error request method: ${JSON.stringify(sreq)}`;\n    util.log(err);\n    sres.writeHead(405, {'Content-Type': 'text/plain'});\n    sres.end(err);\n  }\n}\n\nmodule.exports = proxyFun\n```\n\n3、server端的处理：\n+ 不用套模板了；\n+ 不用管理模板了；\n+ 什么都是接口了。\n+ 爽！！！\n\n\n#### 三、实践样例\n+ proxy.conf是nginx配置\n+ /server为Server端项目\n+ /webpage为web端项目\n\n配置好nginx\n\n```\n    cd server\n    npm install\n    node index.js\n\n    cd webpage2\n    npm install\n    ndoe index.js\n```\n\n浏览器访问http://proxy.test.com/webpage/\n\n#### 四、未实践但是可以想象的，\n我们这里测试的是一对一，即一个Server一个web(因为太穷只有一台电脑，硬盘有是有128G,装不起虚拟机~),其实「一对多」或是「多对一」或是「多对多」都是可以实现的，或者是一个web集群支持所有项目web页面。\n例如多个重业务逻辑的Server可以公用一个web,web根据域名做vhost；\n又或是业务集中型Server，面对不同对象想使用不同web。用这种『类代理』的方式感觉可以比较好的分离前后端工作流程，解前后端工作耦合。\n##### 缺点：\n- 多一层的性能损耗\n- 对前端要求更高了\n- 不适用于业务不大或不多的情况（因为根本没人愿意配合你搞）\n\n#### 五、balabala\n\n以上观点，是在阅读「midway 前后端分离的思考与实践」相关文章后结合自身的不爽的自我总结与部分实践。是比较初级的实践与想法。\n欢迎大家来吐槽填坑:)，有新方案的一定要联系我QQ:550281353;招聘记得发红包至微信:weivea\n\n#### ps:现已增加php-laravel作为服务端，请使用webpage2","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweivea%2Ffe-separate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fweivea%2Ffe-separate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweivea%2Ffe-separate/lists"}