背景
说一下写这个文章的背景,在当前公司前后呆了3年了(前几天刚续签,又把自己卖了5年),不定期的轮岗是经常性的,业务调整后到了一个新部门服役,之前没有接触过新部门的业务,因为这个部门在另外一个办公地点,作为前端的小组长,需要重新了解业务,梳理技术点。
来到新部门,业务大概了解了下,沉淀了多年的技术哈,前辈们给我们留下了宝贵的技术积累,fis+smarty
,JQ
,Vue
,javascript
,css
,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次迭代后删除,以方便分支管理。==
附件:日常工作同事们提交代码:
罗列了这个表格后,大家可能感觉很简单,不就是个命名吗?其实结合业务有一个适合自己团队的命名规范后,对工作是大有裨益的,小伙们肯定内心想“光讲有什么用,来点直观的吧”,直观的也可以的,再给上个图(自己经验沉淀的),来演示下上述表格的演进。
看了图如果还不明白,我就...了。
Vue+TypeScript+Mock+DIY契机
git地址
不光是我技术调整到新部门,PM也来了,要打算弄个平台,于是是一个新技术架子再新业务的首秀,所以有了标题,typescript
经常听,但是这边业务没有用过啊,他们也特别想使用,好处就不说了,干起来。
那就一步步来吧。
node,vue及其脚手架安装,这个就不说了哈,网上一大推,虽然Vue3.0即将发布,毕竟业务稳定不能追求最新技术,坑也不敢猜,所以技术Vue2.6 +webpack4搞起。
本人使用iTem2,所以就用这个为基开始了,cd到你要存放新项目的地方,然后执行vue create 项目名字
即可。
这个时候会让选一个创建的集成,大家可以选Manually,自己去配置(下图),因为我需要eslint
代码规范,所以我选了zyb,可能你的上面没有,因为这是我之前创建的,你可以选择你想要的功能即可。
然后 回车,脚手架一顿狂装,等出现下图说明安装成功了。如图:
听人家的,来执行命令cd vue-ts
,npm run serve
,跑起来:
打开浏览器,输入本地URL,或者按着ctrl
点击Local也可以打开。
至此,你的第一步OK了。
项目的文件目录及package如下,很原始,很干净,就如同什么什么系统纯净版:
第二步:router、VueX配置
完成第一步是基本功,咱们开始在基础文件上精雕细琢一下吧,就如同你拿到一个木头想要雕刻成一个艺术品,还是要有一些功底加上自己的思想的,话不多少,开始。
我的基本思路是做个后台系统,所以有个大概方向,好慢慢下手,布局就是左右结构,如下图:
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文件,如图:
在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>
以后可以跟图片say bye了。
npm上有更详细的文档及使用用法,可以去扒拉扒拉。
2.2 异步服务
完成2.1后,开始着手准备请求服务了,请求数据肯定需要本地模拟服务,所以先来mck数据吧,网上很多模拟方式或者服务,毕竟跟后端做交互,自己弄个小服务方便,开始吧,在src下面建立个文件夹,存放请求的地址及想要的数据,目录如下:
在项目根目录下创建两个文件夹,config
,mock
。config下面有两个文件:host.ts
、url.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文件夹里面主要放两个文件:data
、mock-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
,这样就可以启动了,如果成功就会有下图所示:
有了模拟接口,如何使用呢,来,继续,开始axios。
项目下安装下axios,这个都会,不想细说,因为项目请求较多,使用axios的语法,有一些啰嗦,所以简单封装一下,目录放在api下面:
(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封装好的文件,使用就直接请求即可,我执行文件后效果如下:
注意:如果使用跨域,需要使用非安全策略下的浏览器访问以解决跨域,方法自己百度下就可以了,我之前也文章过,参考:https://segmentfault.com/a/11...
2.3 Router配置
看到上面的图,嗯,不太美观啊,来搞起UI,这里先不用框架了,自己简单处理下。
作为设计逼格不好的程序员,使用开源的UI组件库是非常好的选择,市面上有很多,譬如vuefiy,iView等,这里使用了element-UI作为基础布局及各种小组件,增加后,布局就酱紫了:
效果凑合看吧,接下来处理这个主题,路由的配置,左侧有菜单了,点击右侧需要联动,搞起。
首先找到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>
创建完毕,复制两份,改名字,如下图:
我们使用这三个比较简单的页面进行配置。
来,大家移步到 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 />
。如下图:
编译后打开本地url就会自动跳转到index显示:
效果:
到这里,基本的路由配置就结束了,基本的功能就能干活了。更多案例上官网查文档。
今天就这样吧,下次继续ing
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。