从开发到编译部署——SAST_Link前端开发笔记

0x00写在前面

本次的项目是我与風楪两人合作完成的,他负责后端,而我负责前端,合作十分愉快(确信)。

这次项目的想法源于我和他的一次天台闲聊,闲聊时感叹于科协成员之多,每次只能通过头像辨别他人,而对于其年级专业乃至真实姓名一无所知,所以我们俩有了个写通讯录的想法。

在以后,我们也考虑会把它做成一个SAST资源导航网站,甚至考虑过做成内部论坛(QQ群TG群不香吗?还是不怕学校介入?)

本次开发经历时长三天左右,部署和微调花了一天多时间,这几天总是熬夜到两三点,之前也在忙招新宣讲,经常在101折叠床上包夜。这些熬夜行为对大脑损害极大,希望我能在清醒时把这次开发经历里学到的东西记录下来。

0x01React-router和cookie,和页面污染

在这次开发经历前,其实我在暑假已经参加过一次大规模的项目开发,当时我对react不甚了解,以至于出现了很多现在看起来非常愚蠢的错误。但是好在那个项目是React+AntD的,很多东西都是给我们封装好的,整个开发过程对我来说就像是配置一个”更精细化“的Hexo博客。而这次,我要写一个用户系统,还是有管理员和普通用户两个身份的用户系统,我很多东西得自己手搓,比如路由鉴权。但是好在,React下还有一个叫做 React-router 的优秀插件。

react-router对我来说还有个好处,就是让我领悟到了模块化的便捷之处,将每个功能模块封装起来,随时调用非常方便。

这是我的App.js,其主要功能就是通过react-router组件来判断用户权限,从而有选择性地渲染各个页面,对于不符合要求的用户会直接返回到backUrl

function App() {
  const [user, setUser] = useState({});

  const loginAsUser = () => {            //还未上真实接口时,用来模拟的权限的方法。
    setUser({
      role: ["user"],
    });
  };
  const loginAsAdmin = () => {
    setUser({
      role: ['admin',],
    });
  }
  return (
    <Router>
      <Switch>
        {publicRoutes.map(({ path, component, ...route }) => (
          <Route
            key={path}
            path={path}
            {...route}
            render={(routeProps) => {
              const Component = component;
              return <Component loginAsUser={loginAsUser} loginAsAdmin={loginAsAdmin} {...routeProps} />;
            }}
          />
        ))}
        {privateRoutes.map((route) => (
          <AuthRoute key={route.path} {...route} user={user} />
        ))}
        {adminRoutes.map((route) => (
          <AuthRoute key={route.path} {...route} user={user} />
        ))}
        <Route component={NoMatch} />
      </Switch>
    </Router>
  );
}
export default App;

存储不同用户信息(可访问路径,所用组件)的,是写在privateRoutes.js(意思是用户可访问的)等的几个对象数组里面的

import Link from '../user/link';
import Setting from '../user/setting';
const privateRoutes = [
  {
    path: '/link',
    component: Link,
    exact: true,
    role: 'user',
    backUrl: '/'
  },
  {
    path: '/setting',
    component: Setting,
    exact: true,
    role: 'user',
    backUrl: '/'
  },
];

export default privateRoutes;

真正进行鉴权的, 是写在AuthRoute.js里面的

function AuthRoute(props) {
  const {
    user: { role: userRole },
    role: routeRole,
    backUrl,
    ...otherProps
  } = props;
  //
  if (
    (userRole && userRole.indexOf(routeRole) > -1) ||  //已经不生效
    routeRole === loginRole() ||   //读取cookie
    loginRole() === "admin"
  ) {
    return <Route {...otherProps} />;
  } else {
    return <Redirect to={backUrl} />;
  }
}

export default AuthRoute;

用户纯前端模拟鉴权已经写好了,但是放在state里面的信息并不长久,一个刷新就没了。怎么办呢,我这里就用把用户身份存储在cookie里的方式解决了(确实有一定的安全问题,但是用户身份不是唯一验证手段)

另外,在写css时也发现一个问题,如果直接对某一类标签(例如body,div)直接进行css渲染,而不是通过特征性的id或者class选择,会造成下一个渲染的页面样式”继承“也就是被之前的样式所污染。

0x02地区选择器省市区的三级联动

这次写前端,用了个比较中意的组件库——MUI:https://mui.com/

风格挺喜欢的,但是这个东西我越用越不对劲:用户文档更新极其滞后;官网bug不断;语言翻译也做的不是很好。让人有种金玉其外败絮其中的感觉。而其中多级联动选择框的缺失,不得不让我手写一个新的(恼)

其实也还好,就是子组件父组件之间的传参以及状态管理,还有对异步调用的调教(最后一点不讲了,自己遇到状况试试)

const MapSelect = (props) => {
  var cityjson = [];
  var areajson = [];
  var cityjson_temp = mapjson.filter((item) => {
    return item.value === props.province;
  });
  cityjson = cityjson_temp.length !== 0 ? cityjson_temp[0].children : [];

  var areajson_temp = cityjson.filter((item) => {
    return item.value === props.city;
  });
  areajson = areajson_temp.length !== 0 ? areajson_temp[0].children : [];

  return (
    <>
      <Grid container>
        <Grid item xs={12} sm={12} md={4}>
          <FormControl sx={{ mt: 3, minWidth: "100%" }}>
            <InputLabel>省/直辖市</InputLabel>
            <Select
              value={props.province}
              label="省/直辖市"
              onChange={props.provinceChange}
            >
              {mapjson.map((item) => (
                <MenuItem key={item.value} value={item.value}>
                  {item.value}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
        </Grid>
        <Grid item xs={12} sm={12} md={4}>
          <FormControl sx={{ mt: 3, minWidth: "100%" }}>
            <InputLabel>市</InputLabel>
            <Select value={props.city} label="市" onChange={props.cityChange}>
              {cityjson.map((item) => (
                <MenuItem key={item.value} value={item.value}>
                  {item.value}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
        </Grid>
        <Grid item xs={12} sm={12} md={4}>
          <FormControl sx={{ mt: 3, minWidth: "100%" }}>
            <InputLabel>区/县</InputLabel>
            <Select
              value={props.area}
              label="区/县"
              onChange={props.areaChange}
            >
              {areajson.map((item) => (
                <MenuItem key={item.value} value={item.value}>
                  {item.value}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
        </Grid>
      </Grid>
    </>
  );
};

//……………………
//此处省略114514行
//……………………

const [province, setProvince] = useState("");

const provinceChange = (event) => {
    setProvince(event.target.value);
    setCity("");
    setArea("");
    setValues({
      ...values,
      address: `${event.target.value}##`, //为了对抗恶心人的异步调用
    });
  };

<MapSelect
                province={!province ? values.address.split("#")[0] : province}
//这里也是为了对抗恶心人的异步调用


                provinceChange={provinceChange.bind(this)}
                city={!city ? values.address.split("#")[1] : city}
                cityChange={cityChange.bind(this)}
                area={!area ? values.address.split("#")[2] : area}
                areaChange={areaChange.bind(this)}
              />

效果如下:

对了,json选的好,事情干的少;json选不好,数据洗死你。

0x03多表单的状态更新

感觉这是一个相当不错的写法

 const initialValues = {
    value1: "",
    value2: "",
    value3: "",
    //…………
  };

  const [values, setValues] = useState(initialValues); //初始化
  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setValues({
      ...values,
      [name]: value,
    });
  };


<TextField
   value={values.value1}
   name="value1"
   onChange={handleInputChange}
   />
//…………

0x04编译部署与proxy

执行yarn build就能将项目编译成在目录build下的静态网页文件。在服务器上搭建好nginx,修改配置文件,就能访问我们的项目了

我的项目搭建在/home/SAST-Link-frontend文件夹里,另外react-router有个特性,我们得对其进行一个微调

historyurl样例特点
hash history/#/user/profile不需要服务器支持
browser history/user/profilereact-router官方推荐,需要服务器支持(因为是SPA项目,url切换时需要服务器始终返回index.html)

我在项目中使用的是browser history模式,得在conf里加一句话

    location / {

        root   /home/SAST-Link-frontend;
        index  index.html index.htm;
        try_files $uri /index.html;
    }

另外编译完项目还需要配置proxy解决跨域问题,我的api全在/下,这在本地写代码时开的代理没啥问题,而在nginx里添上这个的话,所有的地址都被指向api了

        location / {
            proxy_pass  http://192.168.***.***:*****/;
        }

所以必须加个中间件啥的,但是这就对本地调试不方便了,因为react配置的代理直接是指向/下的:

  "proxy": {
    "/api/**": {
      "target": "http://172.16.***.***:*****",
      "changeOrigin": true
    }
  }

直接在package.json中设置proxy属性这么写会报错,这是因为在新版本的react项目中,在package.json中的proxy只能设置成字符串,如下:

"proxy": "http://172.16.136.249:8080"

想要编写成中间件,还得使用http-proxy-middleware这个插件

安装middleware插件后,在src目录中新建setupProxy.js文件,在文件中放入如下代码:

const { createProxyMiddleware } = require('http-proxy-middleware')

module.exports = function (app) {
  app.use(createProxyMiddleware('/api', {
    target: 'http://172.16.136.249:8080',
    secure: false,
    changeOrigin: true,
    pathRewrite: {
      "^/api": "/api"
    }
  }))
}

最后在每个接口在调用接口文件里前都加一个/api,conf也能改为/api,这样本地调试和部署就两不误了

评论

  1. Tastror
    Windows Chrome 94.0.4606.81
    2月前
    2021-10-21 22:46:39

    好耶!

    • PiCpo 博主
      Windows Firefox 93.0
      2月前
      2021-10-21 22:48:48

      好耶!

  2. 白音
    Android Chrome 88.0.4324.93
    2月前
    2021-10-21 23:36:33

    新项目好耶!

    • PiCpo 博主
      Windows Firefox 93.0
      2月前
      2021-10-21 23:38:11

      好耶!

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇