「时光不负,创作不停,本文正在参加2021年终总结征文大赛」,在这一年的工作中,都沉浸在一个项目中,但收获了不少,从什么也不会,写个翻页框花了两天的我,到现在可以自己封装组件。每一天都在进步,这一年,收获了许多。
前端的项目规范整理
刚入门前端到现在一年多了,在不断的挨批中成长,虽然现在也只是踏入了半个门,但希望未来能很强大,慢慢努力,不断成长。回首刚开始写的项目,看了真是离谱到家,什么命名啊,什么一长串不必要的逻辑啊,真是不忍直视。刚好最近在做一个大项目,遇到非常多以后值得借鉴的东西,尤其是规范的问题,开始的时候真真是痛苦,到后来真香~好,开始整理。文章不过多详细介绍知识点,这是个总概括,主要记录一下项目中的感谢(狗头保命,大佬们看到有啥不对多多指点,在成长中,谅解谅解)
目录
- 项目技术
- 概况
- 常见规范
- 捕捉异常
- ES6 与 lodash
- 模块化
- 常用页面模板
- 代码管理gitlow
- 常用插件
- 关于我自己封装的组件
- 项目理解
一. 项目概况
我主要简单介绍一下使用到的一些技术栈。在项目中使用的是vue-element-admin。 该官网地址为:panjiachen.github.io/vue-element… 这个集成方案,插件使用到视频监控的插件,地图依赖库,图片标注插件,echarts图表库,element ui组件库
二. 常见规范
在我的leader无数次教导下,质问年轻人行不行啊?我也要好好努力。在这一年中特别的感谢有两个教导我的人,一个是我尊敬的师傅,一个是严格又温柔的大前辈。他们的代码风格是有差异的,一个是简洁简洁简洁,一个是规范规范规范。最开始我以为代码只要足够简洁就是最好的,后来大前辈说的一句话我一直都记得,"有时候不一定简洁就好,你要让另一个代码具有可读性,让后面的人能快速接手,能快速看出你的接口要什么数据”。这位大前辈在代码规范上有许多经典语录:
eslint 给我开起来,一定不要给我忽略。
eslint这个代码审核的工具,不得不说,刚学前端的时候觉得它很烦,稍微语法不合格就报错,控制台刷刷刷的一片红。但在团队项目中,eslint这个代码检查工具至少可以让大家的语法比较统一,减少不必要的冲突。
如何配置eslint规则参考:eslint.bootcss.com/docs/user-g… 安装了eslint插件后,都会有一个.eslintrc.js 配置文件,你就可以在这个文件下配置代码规则啦。 这个文件对外暴露的对象有个属性:env、extends、parserOptions、plugins、rules。详细参考上面那条官网链接。
方法名采用驼峰命名法,首字母小写,动词加事件
一般情况来说,这样命名看起来比较舒服,而且能快速理解此方法的含义作用。当然事件不一定是必要的的,例如:selectUserId(), select()。这些方法命名不强制规则,但是最起码你得让我知道这个大概是用来干啥子的吧。
组件文件名首字母大写大写大写 css命名请使用BEM模式
长话短说,类似于一下的命名方式:
.listing-card是一个块(block),表示高层次的组件。.listing-card__title是一个元素(element),它属于.listing-card的一部分,因此块是由元素组成的。.listing-card--featured是一个修饰符(modifier),表示这个块与.listing-card有着不同的状态或者变化。
三. 捕捉异常
在一个项目中,捕捉异常还是很有必要的。从我的理解角度,一个完善的监测机制可以更完善的处理 程序,去增强程序的可适应能力。而捕捉异常有需要许多方式。
介绍一下3种捕获异常的方式:try catch, window.onerror, window的unhandledrejection事件。
- try catch:可捕获同步代码。不捕获语法错误,以及异步代码
- window.onerror:捕获决大多数异常,解决try catch的缺点。捕获不到promise的reject抛出的异常
- window的unhandledrejection:捕获promise的reject抛出的异常
- 其他:vue框架自带的Vue.config.errorHandler,errorCaptured(cn.vuejs.org/v2/api/#err…)
四. ES6 与 lodash
为了项目的代码简洁化,在项目中使用ES6和lodash是很有用的。ES6想必大家都知道,那lodash呢?其实是一个js方法库www.lodashjs.com/
1. ES6
与其说ES6 , 其实应该在这指的是ES6之后的语法。以下说的是比较常用的一些内容
类Class, 常用于表单等类似的内容赋值
使用ES6的class关键字创建一个类后,使用new就可以创建一个对象了,这意味着当你有一个公用类,在不同的地方可以快速的创建一个实例
使用ES6的class关键字创建一个类后,使用new就可以创建一个对象了,这意味着当你有一个公用类,在不同的地方可以快速的创建一个实例
解构赋值,很香,可以用于数组或对象 es6.ruanyifeng.com/#docs/destr…
用法:
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let [a, b, c] = [1, 2, 3]; // 输出a:1 b:2 c:3
为什么说很香呢?想想,假如你接口拿回来个对象 a = { data: {}, id: 1, name: 2 },你想拿到对象里的data, 直接这样就能拿到 let { data: res } = a , 这时你的变量res保存的就是a的data值啦。
Promise是异步解决方案,这个容器中常执行异步操作(例如调接口),而它有好多方法,可以对异步操作后的结果进行处理。以下是它提供的方法
-
Promise.all() :里面包含多个promise实例,只要一个失败,返回第一个失败的实例的结果。成功则以数组的形式返回所有实例的结果
Promise.all([promise1, promise2, promise3]).then((values) => {\ console.log(values);\ }); -
Promise.allSettled() :里面包含多个promise实例,以数组的形式返回所有实例的结果
-
Promise.race() :里面包含多个promise实例,只要有一个实例改变状态,就返回这个实例的结果
-
Promise.any() :里面包含多个promise实例,只要有一个实例成功状态,这个包装了多个实例的Promise就会变成
fulfilled状态;只有全部失败时才变成rejected状态,参数为成员错误结果数组....
题外话了,其实想说的是,在项目中解决异步的问题的方式,常用的是ES8的async/await ,常用以下布局
2. lodash
lodash是一个库,使用js封装了许多方法。介绍一下我常用的几个:
数据判空处理 _.isEmpty(value) , 数据存在是否 _.isNil(value)
_.isEmpty(value): 常用于判断数据value是否为空,比如{},[], ' '
_.isNil(value): 常用于检查 value 是否是 null 或者 undefined
获取数据 _.get('对象'、'路径','默认值')
在项目中,会遇到从接口拿不到想要的值,这时候控制台报错该值不存在。
那该如何从如下的data中拿到name呢?,使用get这个方法,常用于获取数组或对象中的数值。\
let res = { data: { name: 李四 } };
_.get(res,'data.name','张三')
如果name不存在,就返回默认的'张三',存在则返回李四。具体用法参考文档lodash
获取数组对象中的某值的时候,使用
_.map(collection, iteratee),返回新数组
collection:迭代的集合
iteratee:迭代调用函数function (value, index|key, collection) {}
快速获取数组对象中的值:
let data= [ { id: 1 }, { id: 2 } ];
_.map(data, 'id') // 返回 [1, 2]
数组分组,常用于前端分页 _.chunk(array, [size=1])
_.chunk(['a', 'b', 'c', 'd'], 2);
// [['a', 'b'], ['c', 'd']]
深拷贝: _.cloneDeep(objects), 浅拷贝: _.clone(objects)
let a = {name:'李二狗'}
let b = _.cloneDeep(a) // {name:'李二狗'}
a.name = {name:'老王'}
let c = _.cloneDeep(a) //c: {name:'老王'} b:{name:'李二狗'}
**判断是否为数组: _.isArray(Array) **
_.isArray([1, 2, 3]) //true
_.isArray(1) //false
数组去重: _.uniq(Array) 只用于数组去重
_.uniq([2, 1, 2]); // => [2, 1]
生成唯一id:
_.uniqueId([prefix=''])可以用于常见的创建假数据时,生成唯一id
_.uniqueId('contact_');
// => 'contact_104'
_.uniqueId();
// => '105'
_.compact(array) 去除假值 例如
false,null,0,"",undefined, 和NaN都是被认为是“假值”。
_.compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]
替换: _.fill(Array)
_.fill([1,2,3], 'a'); // ['a','a','a']
_.fill(Array(3), 2); // => [2, 2, 2]
合并对象属性:
_.assign(object, [sources])与ES6的assign作用一样,与
_.assign({ 'a': 0 }, {'b': 0}); //{ 'a': 0,'b': 0}
补充:_.assign(object, [sources] 和 _.merge(object, [sources]) 功能类似,但merge会深入递归,当在对象中有其他引用类型的值时,不是直接覆盖目标对象中的同名属性的值,而是进行合并。 而assign则是直接拷贝源对象的引用。
五. 模块化
说到模块化,就会提到ES6的模块化,CommonJs, AMD,CMD。而常用的是ES6中的模块化引入方法,和CommonJs。日常一般使用ES6的模块引入和Commonjs比较多。
ES6 的
importexport引入导出 常通过babel转译后用于浏览器端
CommonJs
requiremodule.exports引入导出 常用于服务端 (nodejs)
AMD 是用于浏览器端的,是RequireJS推崇的规范,
requiredefine
CMD是SeaJS推崇的,
requiredefine补充: 可参考文章:zhuanlan.zhihu.com/p/108217164
六. 页面模板
七. gitflow
在团队的开发中,最常面对的就是代码合并管理问题,为了能让团队高效开发,互不影响,使用gitflow。gitflow是基于Git的强大分支能力所构建的一套软件开发流程。
这里推荐sourceTree应用,它可以简化与git存储库的交互,视图化,极大方便操作。以下都是基于sourceTree去操作gitflow工作流程。这是常用的几个操作:
- 创建仓库
- 新建功能(新建补丁)
- 完成功能 (完成修复补丁) 这是sourceTree的界面:
可以看到左侧有多个不同的分支,它的意义不同:
- develop:基于master分支上创建的,是主要的开发分支。
- feature:当新建功能时,基于devolop创建的分支。它主要是用来开发一个新功能,完成后,就会合并到develop上
- hotfix:当新建修复补丁时,基于devolop创建的分支。它主要是用来修复某个bug,完成后,就会合并到develop和master分支上
- release: 当需要发布一个版本时,基于develop分支创建一个release分支,完成release后,会和报哪个到master和develop
- production : 这个分支就是常使用的master,最近发布的release,这个分支只能由其他分支合并,不能直接修改。
操作流程
首先点击上方仓库,再点击Git工作流,再看弹出的小框。 如果没有初始化仓库则先
初始化仓库,一个项目只需要初始化一次。然后根据需求,如果创建新功能则点击创建新功能。需要修复bug,就点击建立新的修复补丁。这时可以在featur下看到新建的功能分支,在此分支上进行开发,完成开发后,回到这个小框点击完成功能,则会把该分支自动合并到develop。 补充:在开发的过程中,怎么实时更新新建功能分支的代码。
右键新建功能分支,弹出图中的框框,点击推送则会推送到远程(远程会新建该分支)。其他功能根据字面意思自己探索一下。
八.常用插件
element ui: 目前使用最多的组件库,基于vue版的 element.eleme.cn/#/zh-CN/com…
zTree:树插件,最大的优点就是加载大量数据,不卡段,上万节点轻松加载。官网地址:www.treejs.cn/v3/main.php… 效果图:
LivePlayer H5播放器:该视频播放插件支持 WebRTC、RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS 等多种协议流输出; 使用文档:www.liveqing.com/docs/manual…
webrtcPlayer:也是基于webrtc协议的一个实时传输数据的工具。如何使用参考:github.com/mpromonet/w… , www.cnblogs.com/mq0036/p/14…
js-file-download: 下载excle文件的插件。
首先下载插件:npm install js-file-download
在页面引入: import fileDownload from 'js-file-download';
从后端接口获取blob类型的文件流(data) ,接口如下图
传入插件的方法,参数一为excle的blob类型数据,参数二为自定义文件名称记得加后缀哦。 fileDownload(data,'文件名称.xlsx')
图片标注工具:AiLabel。比较轻量的一个标注工具,使用链接,blog.csdn.net/u012967771/… , ailabel.com.cn/public/aila…
日期处理类库:高效快捷的处理时间日期,例如:
this.$moment().format('YYYY-MM-DD HH:mm:ss')
更多设置参考教程 => momentjs.cn/
crypto-js: 前端实现加密解密的库,实现基于AES + BASE64 算法加密。 也可参考网址:www.jianshu.com/p/a47477e81…
下载 :npm install crypto-js --save
引入:
import CryptoJS from 'crypto-js'
// 与后端的key,iv保持一致
const key = 'C2JAXZG3G6Y9MSDD'
const iv = 'ID3HSV3P0RHOWKVT'
// 加密
function Encrypt(text) {
return EncryptWithKey(text, key, iv)
}
function EncryptWithKey(text, key, iv) {
const s = CryptoJS.AES.encrypt(text, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString()
// 最后Base64再加一次密
// var s = CryptoJS.enc.Utf8.parse(str);
// base64 = CryptoJS.enc.Base64.stringify(s);
return s
}
// 解密
function Decrypt(text) {
return DecryptWithKey(text, key, iv)
}
function DecryptWithKey(text, key, iv) {
// 先Base64解一次密
// var s = CryptoJS.enc.Base64.parse(base64);
// str = s.toString(CryptoJS.enc.Utf8);
const s = text
const decrypted = CryptoJS.AES.decrypt(s, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
})
return decrypted.toString(CryptoJS.enc.Utf8)
}
export default {
Encrypt,
Decrypt,
DecryptWithKey,
EncryptWithKey
}
封装两个方法后,在其他界面即可使用Encrypt, Decrypt,去解密加密数据