likes
comments
collection
share

QA转型之在公司快速接手开发前端工程「0经验开发前端2坤月」

作者站长头像
站长
· 阅读数 14

前言

测试,最为重要的就是质量和效率。谈到提效,就少不了测试工具、测试平台的开发。来到公司一年半的时候,因为公司业务的变更,我被调去了效能工程组,负责内部几个自动化平台的维护和开发工作。在此之前,对于前端我也仅仅是会一些 HTML/CSS/JavaScript基础语法,并没有太多实际项目工程的开发经验。于是,我走上了一条为期2坤月的前端开发之路 🧑‍💻

QA转型之在公司快速接手开发前端工程「0经验开发前端2坤月」

遂,将此五个月的0基础开发并且如何边学习的经验做一次总结,各位大佬如有疑问或沟通,欢迎随时私信。

第一步:了解项目技术栈

我们拿到项目的时候,首先确认下他是使用了什么技术和框架。如,我接手的几个项目技术栈情况如下:

  • React(使用React Hook语法)
  • Vue2(内部一个老测试工具平台在使用)
  • ES6(使用解构赋值、扩展运算符、箭头函数等)
  • umi3(前端路由+插件)
  • dva(数据层,相当于redux + redux-saga)
  • antd(UI组件库)
  • Bizcharts: 图表库(会考虑借鉴antD pro封装的图表组件)

推荐开发者工具:

  • VSCode(代码编辑器)
  • node环境
  • npm(包管理工具)
  • React Developer Tools(Chroma浏览器插件)
  • redux Devtools(Chroma浏览器插件)

vscode插件推荐:

  • eslint ,静态代码检查
  • gitlens, 查看git 记录
  • Bracket Pair Colorizer 彩虹括号
  • Indent Rainbow  彩虹缩进
  • Prettier - Code formatter, 设置上Format On Save
  • Simple React Snippets和ES7+ React/Redux/React-Native snippets, 代码块,快捷键带出大量代码
  • @popular 查找使用最多的插件

第二步:工程本地启动

在工程首页的README.md(仔细阅读)中,一般会介绍该工程的启动方式和注意事项。如

安装依赖:npm install
本地启动:npm start # localhost:9500

查看该项目工程的目录结构,大体要知道每个文件夹、每个文件是做什么用的。例如:

├── README.md
├── base   // !不要修改,脚手架自动生成,
├── config   // 配置文件夹
│   ├── theme   // 主题文件
│   ├── menu   // 菜单文件
│   │   ├── menus.json   //  用户菜单配置
│   │   └── menu.config.js  // !不要修改
│   ├── config.js   //  umi全局配置
│   ├── config.local.js   // umi本地配置
│   ├── config.prod.js   // umi生成环境配置
│   └── config.common.js  // 用户配置
├── package-lock.json 
├── package.json
├── src
│   ├── app.js
│   ├── assets  // 图片等静态资源
│   ├── components  // 通用组件
│   ├── global.less  // 全局样式文件
│   ├── layouts  // 整体布局相关(可以在这里做动态菜单)
│   ├── utils   // 通用工具
│   └── pages  // <---  用户编写页面的地方
│      └── demo-page  // <---  页面A
│          ├── service  // <---  页面A的service
│          ├── models  //  <---  页面A的models
│          ├── components  // <---  页面A相关组件
│          ├── index.jsx  // <---  页面A的主文件

第三步:菜单/路由/本地代理

一般会有一个地方可以配置菜单。如在config/menu文件夹下的文件menus.json。这里就是我们展示在首页的菜单选项。

{
"path": "auto",
"name": "UI自动化",
"icon": "TrophyOutlined",
"children": [
  { "path": "case", "name": "用例", "icon": "BoxPlotOutlined" },
  { "path": "execSet", "name": "执行集", "icon": "MenuOutlined" },
  { "path": "report", "name": "测试报告", "icon": "PieChartOutlined" }
]
}

找到路由相关文件,如routes.js,在这里我们可以将路径和组件关联起来。现在用的是配置式路由,根据path匹配显示的组件,如果没有匹配上就显示404。注意最后一行是配置404页面的,当路径和前面所有的配置路径匹配都不成功时,则匹配404页面。

module.exports = [
  {
    path: '/',
    component: '../layouts/index',
    routes: [
      { path: '/', redirect: '/auto/execSet/list' },
      { path: '/auto', redirect: '/auto/execSet/list' },
      { path: '/auto/execSet', redirect: '/auto/execSet/list' },
      { path: '/auto/execSet/list', component: '../pages/auto/execSet/list/index' },
      { path: '/auto/case', redirect: '/auto/case/list' },
      .....
      { component: '../pages/404' }
    ],
  },
];

找到proxy代理文件,如config.local.js,在这里我们可以配置接口请求代理地址。在这里切换target可以切换到本地服务端工程地址。

export default {
    proxy: {
        '/api/testCase': {
            // target: 'http://10.221.xx.xxx:xxxx',
            target: 'http://xxxx.qa.xxx.com',
            secure: false,
            changeOrigin: true
        },
        ....
    },
};

第四步:项目结构/数据流转

首先我们来看下dva的数据流转:

QA转型之在公司快速接手开发前端工程「0经验开发前端2坤月」

首先我们根据url访问相关的Route-Component,在组件中我们通过dispatch发送action到model里面的effect或者直接Reducer。

当我们将action发送给Effect,基本上是取服务器上面请求数据的,服务器返回数据之后,effect会发送相应的action给reducer,由唯一能改变state的reducer改变state,然后通过connect重新渲染组件。

当我们将action发送给reducer,那直接由reducer改变state,然后通过connect重新渲染组件。

简单点来说就是: 在subscription方法中我们监听页面路由变化,在effect中去调接口拿数据,在reducer中将返回的数据放到state。

service:

import Fetch from '@/utils/fetch';

const RpcBase = '/api/rpc';

/**
 * 获取RPC信息列表
 */
export function getRpcsList({
    produceId,
    searchWord,
    page
}) {
    const path = `${RpcBase}/queryRpcList`;
    return Fetch.postJson(
        path,
        {
            produceId,
            searchWord,
            page
        }
    );
}
...

models:

import * as service from '@/service/rpc';

export default {
    namespace: 'rpc',
    state: {
        // RPC列表
        rpcsList: { // data
            rpcList: [], // records
            page: {
                currentPage: 1,
                pageSize: 10,
                totalRecord: 0,
                totalPage: 0,
                cursor: 0,
                records: []
            }
        },
        ...
    },
    effects: {
        /** 获取RPC信息列表 */
        * getRpcsList({ payload }, { call, put }) {
            const {
                produceId, // 模块id
                searchWord,
                page = {
                    from: 1,
                    to: 1,
                    size: 10
                }
            } = payload;
            if (produceId) {
                const { data } = yield call(
                    service.getRpcsList,
                    {
                        produceId, // 模块id
                        searchWord,
                        page
                    }
                );
                yield put({ type: 'setRpcsList', payload: { data } });
            }
        },
        ...
    },
    reducers: {
        /** 设置RPC信息列表 */
        setRpcsList(state, { payload: { data } }) {
            const {
                page: {
                    current, size, total, recordCount
                },
                records,
            } = data;
            return {
                ...state,
                rpcsList: {
                    ...state.rpcsList,
                    rpcList: [...records],
                    page: {
                        ...state.rpcsList.page,
                        currentPage: current,
                        pageSize: size,
                        totalRecord: recordCount, // item总数
                        totalPage: total, // 总页数
                    },
                }
            };
        },
        ...
    },
    subscriptions: {
        setup({ dispatch, history }) {
            return history.listen(async ({ pathname, query }) => {
                const regex = /\/rpc\/([\w-_]+)/g;
                const { omProductId } = query;
                if (omProductId) {
                    if (regex.test(pathname)) {
                        switch (RegExp.$1) {
                            case 'rpcs':
                                if (pathname === '/rpc/rpcs') {
                                    const { id } = await dispatch({
                                        type: 'global/getCurrentProduct',
                                        payload: { omProductId }
                                    });
                                    dispatch({
                                        type: 'getRpcsList',
                                        payload: {
                                            produceId: id,
                                            page: {
                                                from: 1,
                                                to: 1,
                                                size: 10
                                            }
                                        }
                                    });
                                break;
                            ...
                            default:
                                break;
                        }
                    }
                }
            });
        }
    }
};

第五步:边开发边学

看懂了前面这些内容,你基本就可以开始介入开发了。照葫芦画瓢大法和Ctrl C+Ctrl V大法,可以帮你轻松实现一些页面和组件。但是,知其然不知其所以然依旧不是学习的态度,所以我们要在开发过程中不断去学习。

我是先从react学起,边看视频边敲代码边对比项目工程(感兴趣的可以看下我整理的学习笔记:「React」万字保姆级基础教程>1「React」万字保姆级基础教程>2),看完绝对让你感觉写react就是一种享(zhe)受(mo)

QA转型之在公司快速接手开发前端工程「0经验开发前端2坤月」

然后学习DvaJSECMAScript6(ES6)UmiJSUmiMax

这套闪电五连鞭组合拳下来,保证你醉生梦死 😎

QA转型之在公司快速接手开发前端工程「0经验开发前端2坤月」

第六步:道阻且长行则将至

因为还有一个老的测试平台(Vue的)在我这里维护,所以楼主又额外学习了Vue知识(学习方法同上)

看再多学习视频、整理再多笔记,都不如做一个项目学得快。所以各位一定记住,多写代码、多写代码、多写代码!

“Talk is cheap, show me the code” -- 布施沃硕德

QA转型之在公司快速接手开发前端工程「0经验开发前端2坤月」