4

背景

  说一下写这个文章的背景,在当前公司前后呆了3年了(前几天刚续签,又把自己卖了5年),不定期的轮岗是经常性的,业务调整后到了一个新部门服役,之前没有接触过新部门的业务,因为这个部门在另外一个办公地点,作为前端的小组长,需要重新了解业务,梳理技术点。
  来到新部门,业务大概了解了下,沉淀了多年的技术哈,前辈们给我们留下了宝贵的技术积累,fis+smartyJQVuejavascriptcss,sass,lee等组成,甚是喜人,8个前端硬生生的搞出了五大门派,每个FE'er都有其对应业务的技术实施方案,沟通了解一下,都说的非常在理,为了不打击积极性,暂维持现状,慢慢收敛泛滥的技术使用,形成统一“大业”。

起步

  我第一步工作是建立规范(--因为这个业务方向没有章法--),可能是我个人这几年的经验心得,先不管之前什么技术栈,也不care之前怎么个神写法。规范大概先从不痛不痒的搞起,以免抵触。

git分支命名规范

  git命名规范,这个大家开会讨论(换一种执行的方式),既然是大家伙开会沟通都无异议,那咱们得执行是不是,简单罗列下我们的“约定”:

分支 示例 备注 说明
上线分支 dev 主上线分支 该分支为上线分支,不允许任何人进行直接更改,研发分支测试达标后,合并请走git merge request,并由特定人CR后合并,以方便后续功能回退管理
功能(feat)分支 feat-tableColMerge-xwj[-20191108] feat-template-cfb-20191113 新需求分支:标识-功能-姓名缩写[-日期] 日期为可选,建议写上,独立功能可单独给测试
bug分支 bug-tableFix-hjj-20191109、bug-dileiti-qpy-20191201 线上bug分支:标识-功能-姓名-日期 因为bug分紧急跟非紧急,所以自己要区分好哪个着急上?哪个版本上?
多人协作分支 v20191111、v20191213 供测试:v-日期 多人合作的功能在dev基础上拉取v20191111后,新功能往该功能合并,最终提交多人协作分支,v20191111供测试使用,测试通过将该分支git merge request至dev

==【建议】
  1.完成一个业务功能或者小里程碑就提交一下代码到git仓库
  2.所有个人创建分支建议3次迭代后删除,以方便分支管理。==
附件:日常工作同事们提交代码:
3333.png

罗列了这个表格后,大家可能感觉很简单,不就是个命名吗?其实结合业务有一个适合自己团队的命名规范后,对工作是大有裨益的,小伙们肯定内心想“光讲有什么用,来点直观的吧”,直观的也可以的,再给上个图(自己经验沉淀的),来演示下上述表格的演进。
2.png

看了图如果还不明白,我就...了。

Vue+TypeScript+Mock+DIY契机

git地址
不光是我技术调整到新部门,PM也来了,要打算弄个平台,于是是一个新技术架子再新业务的首秀,所以有了标题,typescript经常听,但是这边业务没有用过啊,他们也特别想使用,好处就不说了,干起来。
那就一步步来吧。
node,vue及其脚手架安装,这个就不说了哈,网上一大推,虽然Vue3.0即将发布,毕竟业务稳定不能追求最新技术,坑也不敢猜,所以技术Vue2.6 +webpack4搞起。
本人使用iTem2,所以就用这个为基开始了,cd到你要存放新项目的地方,然后执行vue create 项目名字即可。
1.png
111.png

这个时候会让选一个创建的集成,大家可以选Manually,自己去配置(下图),因为我需要eslint代码规范,所以我选了zyb,可能你的上面没有,因为这是我之前创建的,你可以选择你想要的功能即可。
然后 回车,脚手架一顿狂装,等出现下图说明安装成功了。如图:
222.png

听人家的,来执行命令cd vue-tsnpm run serve,跑起来:
333.png

打开浏览器,输入本地URL,或者按着ctrl点击Local也可以打开。
444.png

至此,你的第一步OK了。

项目的文件目录及package如下,很原始,很干净,就如同什么什么系统纯净版:
image.png

第二步:router、VueX配置

完成第一步是基本功,咱们开始在基础文件上精雕细琢一下吧,就如同你拿到一个木头想要雕刻成一个艺术品,还是要有一些功底加上自己的思想的,话不多少,开始。
我的基本思路是做个后台系统,所以有个大概方向,好慢慢下手,布局就是左右结构,如下图:
image.png

2.1 logo延伸出站点的icon

这两年因为工作性质原因,不使用设计的字体文件作为icon试用了,偶尔使用element UI的icon字体,目前使用的是SVG方式,因为svg是矢量的,兼容性好,而且现在到处是icon的各种svg文件,图标非常丰富,完全可以满足需求。所以基本上整站都没有图片,引用svg我目前使用了两种方式,都不错。第一种是阿里的iconfont,自己百度下如何使用吧,很简单;第二种是npm插件,我使用的就是这种,相关插件很多,推荐vue-svgicon [https://www.npmjs.com/package...]。 简单说说怎么用,首先安装vue-svgicon,命令npm install vue-svgicon --save-dev,我会把svg文件放在assets下面,路径是src/assets/svg/origin,这个放源文件,在创建一个放编译svg文件的目的地址,路径是src/assets/svg/web,这个就是编译后的文件,文件目录你可以根据个人需要随便放置,没有要求。

package.json的scripts增加一行命令:"svg": "vsvg -s src/assets/svg/origin -t src/assets/svg/web",这时候你可以放一个svg文件到origin里面,然后执行命令:npm run svg,这时候web目录会有一个同名的js文件,如图:
image.png

在main.ts内配置svg模式,代码如下:

import SvgIcon from 'vue-svgicon';
import '@/assets/svg/web/'; //可以不引用在这里,可以引用在具体文件具体的svg文件
Vue.use(SvgIcon, {
    tagName: 'svgicon' //名字可以随便定义,这个svgcion是插件默认的
})

使用方式:

<template>
    <div id="app">
        <p>
            <svgicon
                name="bell"
                width="200"
                height="200"
                color="#42b983 #35495e"
            ></svgicon>
        </p>
    </div>
</template>

image.png
以后可以跟图片say bye了。
npm上有更详细的文档及使用用法,可以去扒拉扒拉。

2.2 异步服务

完成2.1后,开始着手准备请求服务了,请求数据肯定需要本地模拟服务,所以先来mck数据吧,网上很多模拟方式或者服务,毕竟跟后端做交互,自己弄个小服务方便,开始吧,在src下面建立个文件夹,存放请求的地址及想要的数据,目录如下:
image.png

在项目根目录下创建两个文件夹,config,mock。config下面有两个文件:host.tsurl.ts;
host就是服务地址,内容很简单:

let server = {
  local: 'http://localhost:8080/',
  lone: 'http://www.web.com'
};
module.exports = server.local;

url就是api的列表了,因为大部分跟服务端请求分两种,所以明细化了下,可能会啰嗦,如下结构:

const url = {
  get: {
    getlist: 'getlist'
  },
  post: {
    postoptions: 'postoptions'
  },
  file: {
    // ...
  },
  ws: {
    // ...
  }
};
module.exports = url;

mock文件夹里面主要放两个文件:datamock-server.js
data就是放请求的json文件,mock-server就是node本地跑的模拟服务
data数据是有结构要求的,首先是个json文件,供node文件解析使用,毕竟现在基本不使用xml交互了,命名要跟内容的主键保持一致,等你看到mock-server就知道原因了。拿getlist.json为例:

{
  "getlist": {
    "errNo": 0,
    "errStr": "success",
    "data": {
      "num": "111",
      "orderInfo": {
        "id": "123",
        "name": "测试数据"
      }
    }
  }
}

mock-server.js内容:

/*
 * @Author: your name
 * @Date: 2020-06-23 09:26:09
 * @LastEditTime: 2020-06-23 17:08:16
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /vue-ts/mock/mock-server.js
 */
/*jshint esversion: 6 */
const glob = require('glob');
const path = require('path');
const express = require('express');
const app = express();
const apiRouters = express.Router();
const appData = require('../config/url.ts');
const getApi = appData['get']; // get请求
const postApi = appData['post']; // post 请求
let entryJs = {};
let jsonData = {};
const resolve = dir => {
  return path.join(__dirname, '..', dir);
};
entryJs = glob.sync(resolve('mock/data/*.json')).reduce(function(prev, curr) {
  prev[curr.slice(7)] = curr;
  return prev;
}, {});
for (const i in entryJs) {
  const data = require(entryJs[i]);
  jsonData = Object.assign(jsonData, data);
}
for (const i in getApi) {
  app.get(getApi[i], (req, res) => {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Allow-Headers', 'X-Requested-With');
    res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
    console.log('请求:' + getApi[i]);
    res.json(jsonData[i]);
  });
}
for (const j in postApi) {
  app.post(postApi[j], (req, res) => {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Allow-Headers', 'X-Requested-With');
    res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
    console.log('请求:' + postApi[j]);
    res.json(jsonData[j]);
  });
}
app.use('/', apiRouters);
app.listen(3000, function() {
  console.log('Mock server start: http://localhost:3000');
});

这样服务算是结束了,来吧,尝试一下启动,启动之前建议再package.json配置个命令:"mock": "nodemon mock/mock-server.js",省的每次启动都要输入一堆,加的位置在上图中有。
启动: npm run mock,这样就可以启动了,如果成功就会有下图所示:
image.png
有了模拟接口,如何使用呢,来,继续,开始axios。
项目下安装下axios,这个都会,不想细说,因为项目请求较多,使用axios的语法,有一些啰嗦,所以简单封装一下,目录放在api下面:
image.png(media/15925503612042/QQ20200709-112027.png)
命名根据个人爱好就可以了,pre就是自己封装的,代码如下:
pre-axios.ts

/*
 * @Author: your name
 * @Date: 2020-06-23 17:35:38
 * @LastEditTime: 2020-06-24 10:12:03
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /vue-ts/src/api/pre-axios.ts
 */ 
import axios from 'axios';
import qs from 'qs';
import Url from '../../config/host';

axios.defaults.withCredentials = true;
axios.defaults.baseURL = Url;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

// 请求拦截(配置发送请求的信息)
axios.interceptors.request.use((config) => {
  if (config.method === 'post' && config.headers['content-type'] !== 'multipart/form-data') {
    config.data = qs.stringify(config.data);
  }
  return config;
}, (error) => {
  return Promise.reject(error);
});

// 响应拦截(配置请求回来的信息)
axios.interceptors.response.use((res) => {
  const data = res.data;
  return data;
}, (error) => {
  const status = error && error.response && error.response.status;
  console.log(status);
  return Promise.reject(error);
});

// 查找请求类型及地址
const factory = (obj: {[key: string]: any}): { [key: string]: (...argv: object[]) => Promise<any> } => {
  const output: {[key: string]: any} = {};
  Object.keys(obj).forEach((methodKey) => {
    Object.keys(obj[methodKey]).forEach((apiKey) => {
      const api = obj[methodKey][apiKey];
      output[apiKey] = (data: object, args: object) => {
        switch (methodKey) {
          case 'get':
            return axios.get(`${api}`, {params: data});
          case 'post':
            return axios.post(`${api}`, data, args);
          case 'file':
            return axios.post(`${api}`, data, {headers: {'content-type': 'multipart/form-data', ...args }});
          default:
            return null;
        }
      };
    });
  });
  return output;
};
export default factory;

对get,post做了处理,如果你有ws,file等,自己添加即可。

api-axios.ts 这个文件是结合项目了,代码如下:

import Http from './pre-axios';
import url from '../../config/url';
import _ from 'lodash';
export const apis = _.mapValues(url,  _.bind(_.mapValues, null, _, (v: any) => v + ``));
export default Http(apis);

使用也简单,直接贴代码了:

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import Http from '@/api/api-axios';

@Component
export default class Left extends Vue {
  @Prop() private msg!: string;

  fetchData() {
    Http.usereditoptions().then(res => {
      console.log(res);
    });
  }

  mounted() {
    this.fetchData();
  }
}
</script>

直接import封装好的文件,使用就直接请求即可,我执行文件后效果如下:
image.png

注意:如果使用跨域,需要使用非安全策略下的浏览器访问以解决跨域,方法自己百度下就可以了,我之前也文章过,参考:https://segmentfault.com/a/11...

2.3 Router配置

看到上面的图,嗯,不太美观啊,来搞起UI,这里先不用框架了,自己简单处理下。
作为设计逼格不好的程序员,使用开源的UI组件库是非常好的选择,市面上有很多,譬如vuefiy,iView等,这里使用了element-UI作为基础布局及各种小组件,增加后,布局就酱紫了:
image.png
效果凑合看吧,接下来处理这个主题,路由的配置,左侧有菜单了,点击右侧需要联动,搞起。
首先找到src/router/index.ts文件,这个就是项目的路由文件,路径及展示的模块参数都要在这里配置,在配置之前先说一下,路由联动。
路由有两种方式: hash模式跟history模式,其实模式对研发来说没什么影响,举例子:
hash模式的url:http:// www.x.cn/#/urllist
history模式:http:// www.x.cn/urllist
这个只是简单的表示,这种#后面hash值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。每次hash值的变化会触发hashchange事件,通过这个事件我们就可以知道hash值发生了哪些变化,监听hashchange来实现更新页面部分内容的具体展示及操作。
HTML5新标准发布多了pushState 和 replaceState的API,通过这两个 API 可以改变 url 地址且不会发送请求,也就是history模式的演变,是通过popstate事件进行处理的。
想了解更多,直接去vue官网找下文档即可。
因为开发过程中有很多个组件,为了不以人肉的方式进行,所以以动态路由方式进行,其他方式及嵌套路由就不在这里细说了。
在src/views下简历index文件夹,并新建一个index.vue 文件,内容如下:

<template>
  <section class="index">
    {{ str }}
  </section>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Index extends Vue {
  str = '我是首页显示的内容';
}
</script>

<style lang="less">
.index {
  font-size: 18px;
  color: red;
}
</style>

创建完毕,复制两份,改名字,如下图:
image.png

我们使用这三个比较简单的页面进行配置。
来,大家移步到 src/router/index.ts
文件内容,大家可以不用关心,只要在const routes内写路由基本的嵌套配置就好了。增加以下内容:

 const routes: Array<RouteConfig> = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    redirect: '/index', // 默认显示的页面
    children: [
      {
        path: '/index',
        name: 'Index',
        component: () => import('../views/index/index.vue')
      },
      {
        path: '/another',
        name: 'Another',
        component: () => import('../views/index/another.vue')
      },
      {
        path: '/other',
        name: 'Other',
        component: () => import('../views/index/other.vue')
      }
    ]
  }

path就是url里面host后面的地址
name是路由的另一种方式,这两个写一个就可以,一般常态使用path
componentj就是这个地址对应的页面内容,url访问到这个path就会显示这个.vue的文件
redirect:是默认显示的页面,如果不增加就会显示home。
children:就是在容器内显示的组件列表
做到这里还没有结束,咱们的页面需要个容器放这些路由地址,不然切换菜单不知道在哪里展示。
我们打算在右侧显示我们需要改变的内容,所以找到这个组件,right.vue,让这个布局成为容器,很简单就是增加一个<router-view />或者<router-view><router-view />。如下图:
image.png

编译后打开本地url就会自动跳转到index显示:
image.png
效果:
屏幕录制 2020-07-23 时间 上午11.00.02.gif

到这里,基本的路由配置就结束了,基本的功能就能干活了。更多案例上官网查文档。

今天就这样吧,下次继续ing


mydetails
189 声望6 粉丝

一个技术