摘要:背景最近接了个新项目,遇到一些问题,在这整理分享下。你可能要问,既然可以不用配为什么你还要用呢可能是因为,加个看起来比较丑吧未完待续,持续更新目前处于阶段,主要功能开发完成,计划号上测试,进度上问题不大。
背景
最近接了个新项目, 遇到一些问题, 在这整理分享下。前期规划
需求是这样的,需要做一套后台管理系统: 一个主系统,一个子系统,开发时间6个周。 前期开发有两个人, 再加一个人。
说实话时间有点紧, 所以前期做好规划就很重要。 实现先做一个规划,技术选型,文档分析,分页面, 有个大致的评估。
技术选型首先确定的还是 React 这一套, 即: React,Redux,TypeScript, 样式管理 styled-components, 国际化 react-intl, 组件库 antd, 脚手架,自己配。 本来想图省事用 CRA(create-react-app),后来觉得用rewired 重写不太灵活, 而且有个小伙伴也想自己配,熟悉下 webpack , 还是决定自己搭, 后面会把配置贴出来。
开发计划和后端负责人讨论之后决定把这一期的开发任务分成三个小阶段: P1, P2, P3
P1 完成之后发布, 先跑通主流程,P2 P3 继续迭代功能。
P1 主要包括:
开发环境搭建
test环境资源申请
Nginx 配置
主系统功能开发
三个功能模块开发
登陆注册流程
子系统两个模块的开发
开发时间: 两周
压力还是有的,时间紧,任务重,而且是第一次带人做项目, 好在内心犹如一条老狗,一点都不慌。
后面就进入了开发阶段, 遇到了挺多问题。
进入开发 搭建开发环境这一步大家就都很熟悉了,添加各种配置和打包。 因为主系统和子系统页面风格都是一样的, 没必要分成两个系统, 把新开一个文件夹,里面放子系统的页面, 然后打成不同的包就可以了。就有了如下配置:
// webpack.config.js const path = require("path") const HtmlWebpackPlugin = require("html-webpack-plugin") const webpack = require("webpack") const fs = require("fs") const lessToJS = require("less-vars-to-js") const { NODE_ENV } = process.env const isAdminApp = process.env.APP_TYPE === "admin" const getBaseurl = () => { switch (process.env.ENV) { case "id": return "https://xxx.test.shopee.co.id" default: return "" } } const plugins = [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "template.html"), title: isAdminApp ? "WMS LITE ADMIN" : "WMS LITE", }), new webpack.DefinePlugin({ __BASEURL__: JSON.stringify(getBaseurl()), }), new webpack.IgnorePlugin(/^./locale$/, /moment$/) ] if (NODE_ENV !== "production") { plugins.push(new webpack.SourceMapDevToolPlugin({})) } const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, "./assets/antd-custom.less"), "utf8")) const port = isAdminApp ? 9527 : 8080 module.exports = { entry: [ "@babel/polyfill", isAdminApp ? "./admin/index.js" : "./pages/index.js" ], output: { filename: isAdminApp ? "admin.[hash:8].js" : "main.[hash:8].js", path: path.resolve(__dirname, isAdminApp ? "dist/adminstatic" : "dist/static"), publicPath: isAdminApp ? "/admin/" : "/", }, mode: NODE_ENV, devtool: false, plugins, module: { rules: [ { test: /.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader", }, }, { test: /.less$/, use: [ { loader: "style-loader", }, { loader: "css-loader", }, { loader: "less-loader", options: { javascriptEnabled: true, sourceMap: true, modifyVars: themeVariables, }, } ], }, { test: /.css$/, use: [ { loader: "style-loader", }, { loader: "css-loader", } ], }, { type: "javascript/auto", test: /.mjs$/, use: [], }, { test: /.(png|jpg|gif|svg)$/i, use: [ { loader: "url-loader", options: { limit: 8192, }, } ], } ], }, optimization: { runtimeChunk: { name: "manifest", }, splitChunks: { chunks: "all", minSize: 30000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, cacheGroups: { vendor: { test: /[/]node_modules/, filename: "vendor.[chunkhash:8].js", enforce: true, priority: 5, }, antd: { test: /[/]node_modules[/]antd[/]/, filename: "antd.[chunkhash:8].js", priority: 10, }, antdIcons: { test: /[/]node_modules[/]@ant-design[/]/, filename: "antd-icons.[chunkhash:8].js", priority: 15, }, styles: { test: /.(scss|css)$/, filename: "styles.[hash:8].css", minChunks: 1, reuseExistingChunk: true, enforce: true, priority: 20, }, }, }, }, devServer: { historyApiFallback: isAdminApp ? { rewrites: [{ from: /.*/g, to: "/admin/index.html", }], } : true, hot: true, port, proxy: [{ context: ["/admin/api", "/api"], target: "https://gudangku.test.shopee.co.id", changeOrigin: true, onProxyRes(proxyRes, _, res) { const cookies = proxyRes.headers["set-cookie"] || [] const re = /domain=[w.]+;/i const newCookie = cookies.map(cookie => cookie.replace(re, "Domain=localhost;")) res.writeHead(200, { ...proxyRes.headers, "set-cookie": newCookie, }) }, }], }, }
// package.json "scripts": { "start": "NODE_ENV=development APP_TYPE=main webpack-dev-server", "build": "NODE_ENV=production APP_TYPE=main webpack", "start:admin": "NODE_ENV=development APP_TYPE=admin webpack-dev-server", "build:admin": "NODE_ENV=production APP_TYPE=admin webpack", "lint": "eslint ./ --ext js", "i18n": "node i18n/index.js" },
根据不同的参数打包, 主系统打包到 dist/static, 子系统打包到dist/adminstatic.
解决完打包的问题, 还有另一个问题, 就是本地开发的时候需要配置代理。
目前比较通用的做法有:
devServer 配置 proxy
修改 host
Nginx 做反向代理
// 也可以说只有两种。
我用的是1, 原因是比较灵活, 这个系统后面要发布到7个或者更多的国家, 改host 总归是不太优雅, 来回倒腾Nginx 又费时费力, 提个单大半天不批,不太方便。
后面又遇到的问题是登陆的时候需要请求一次csrftoken, 因为 domain 不匹配所以cookie 种不进来, 所以就改了下配置,代码见 devServer 部分,这个问题就解决了。
打包优化初步做了个优化, 代码分包, 这个系统antd 用的比较多,代码体积, 和业务代码打在一个包里明显是不合适的,就简单分了一下:
压缩后总体积900K。
FCP 1s, 勉强还能接受, 后面有需要再做优化。
国际化实现国际化用的是`react-intl`, 用起来就很简单了: 主要就两种形式:
直接翻译:
需要特殊传, 比如 placeholder, Modal 的title等,如果直接用1 的方式会显示一个[object Object] ,好在react-intl 提供了 injectIntl 方法可以解决这个问题:
is a component which cannot be placed to placeholder which expects a raw String.
用法:
import {injectIntl} from "react-intl"; class TestComponent extends React.Component{ render(){ const { intl } = this.props; return ( ) } } export default injectIntl(TestComponent);
传入的 id, 是你自己定义的,如果有翻译平台的话, 可以自己添加这些key:
在翻译平台完成翻译后, 需要下载到本地, 需要手动下载, 感觉很麻烦, 于是我就写了个脚本来自动下载, 翻译平台更新后, 执行下 yarn i18n 就可以更新过来了:
页面字段的替换就按上面的两种方法, 纯粹的体力活, 没什么好说的。
Nginx 配置功能开发完之后, 要发布到测试环境, 中间要配置Nginx, 我这有个配置平台, 加配置之后提单, 自动部署。
配置的时候还是遇到一些问题的。
首先解决 index.html 访问路径的问题:
需要配置的路径有:
/
/index.html
/admin
/admin/index.html
首先看 / 和 /index.html
还需要配置环境和地区:
/admin 和 /admin/index.html 也一样的配置。
不过需要注意的是, / 和 /admin 需要配置 try_files :
/ :
/admin :
对应生成的 conf 文件:
什么是try_files从字面上理解就是尝试文件,再结合环境理解就是尝试读取文件, 那是想读取什么文件呢,读取 静态文件.
$uri, 这个是nginx的一个变量,存放着用户访问的地址. 比如:http://www.xxx.com/index.html, 那么$uri就是 /index.html
$uri/ 代表访问的是一个目录,比如:http://www.xxx.com/hello/test/, 那么$uri/就是 /hello/test/
完整的解释就是:
try_files 去尝试到网站目录读取用户访问的文件, 如果第一个变量存在,就直接返回;
不存在继续读取第二个变量,如果存在,直接返回;不存在直接跳转到第三个参数上。
至于为什么要配try_files , 因为我们的路由是基于browserHistory的, 如果用 hashHistory 就不用配 try_files。 你可能要问, 既然 hashHistory 可以不用配 try_files, 为什么你还要用browserHistory 呢?
可能是因为, 加个/#/ 看起来比较丑吧 :)
未完待续, 持续更新..5.21 目前处于P3阶段, 主要功能开发完成,计划5.27号上测试,进度上问题不大。 后面有什么值得分享的问题再说。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/109717.html
摘要:作为一个老前端给新人们的一些关于开发流程上的建议拒绝和设计稿没有封板就要求排期,和产品设计充分沟通。双方还是要找到平衡点确保产品质量和上线时间点的前提下,前端的开发难度和工作量是合理的。 作为一个老前端给新人们的一些关于开发流程上的建议 1. 拒绝PRD和设计稿没有封板就要求排期,和产品设计充分沟通。 前端开发经常会遇到这样的情况,项目经理莫名奇妙发了个会议邀请,然后一股脑把产品下阶段...
摘要:因为网站建设一般项目比较小,我一个人是可以完成前后端开发的,如果做成成品当然得加上小苏设计师。关键词选择因为面向的是单个城市业务,在城市选择上犹豫了不少时间,首先得是一个大城市,客户量足,当时我在北京,小苏是在一个省会城市。 我技术之外的资本是零,如果你也是这样,那这篇文章适合你! 这是我的故事之一,希望对你有启发。如果你每天下班后就是躺在床上刷刷斗音,看看微博。但是又总想摆脱黑暗迷乱...
摘要:因为网站建设一般项目比较小,我一个人是可以完成前后端开发的,如果做成成品当然得加上小苏设计师。关键词选择因为面向的是单个城市业务,在城市选择上犹豫了不少时间,首先得是一个大城市,客户量足,当时我在北京,小苏是在一个省会城市。 我技术之外的资本是零,如果你也是这样,那这篇文章适合你! 这是我的故事之一,希望对你有启发。如果你每天下班后就是躺在床上刷刷斗音,看看微博。但是又总想摆脱黑暗迷乱...
摘要:因为网站建设一般项目比较小,我一个人是可以完成前后端开发的,如果做成成品当然得加上小苏设计师。关键词选择因为面向的是单个城市业务,在城市选择上犹豫了不少时间,首先得是一个大城市,客户量足,当时我在北京,小苏是在一个省会城市。 我技术之外的资本是零,如果你也是这样,那这篇文章适合你! 这是我的故事之一,希望对你有启发。如果你每天下班后就是躺在床上刷刷斗音,看看微博。但是又总想摆脱黑暗迷乱...
阅读 1634·2021-10-09 09:44
阅读 2766·2021-10-08 10:04
阅读 2459·2021-09-26 09:55
阅读 3830·2021-09-22 10:02
阅读 3303·2019-08-29 17:08
阅读 1063·2019-08-29 15:08
阅读 2951·2019-08-26 13:52
阅读 3266·2019-08-26 13:34