2022年总结:不进则退,2021年终总结

485 阅读11分钟

「时光不负,创作不停,本文正在参加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 配置文件,你就可以在这个文件下配置代码规则啦。 这个文件对外暴露的对象有个属性:envextendsparserOptionspluginsrules。详细参考上面那条官网链接。

方法名采用驼峰命名法,首字母小写,动词加事件

一般情况来说,这样命名看起来比较舒服,而且能快速理解此方法的含义作用。当然事件不一定是必要的的,例如: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就可以创建一个对象了,这意味着当你有一个公用类,在不同的地方可以快速的创建一个实例

image.png

解构赋值,很香可以用于数组或对象 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

www.lodashjs.com/

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) 去除假值 例如falsenull,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 的 import export 引入导出 常通过babel转译后用于浏览器端

CommonJs require module.exports 引入导出 常用于服务端 (nodejs)

AMD 是用于浏览器端的,是RequireJS推崇的规范,require define

CMD是SeaJS推崇的,require define 补充: 可参考文章:zhuanlan.zhihu.com/p/108217164

六. 页面模板

七. gitflow

在团队的开发中,最常面对的就是代码合并管理问题,为了能让团队高效开发,互不影响,使用gitflow。gitflow是基于Git的强大分支能力所构建的一套软件开发流程。 这里推荐sourceTree应用,它可以简化与git存储库的交互,视图化,极大方便操作。以下都是基于sourceTree去操作gitflow工作流程。这是常用的几个操作:

  • 创建仓库
  • 新建功能(新建补丁)
  • 完成功能 (完成修复补丁) 这是sourceTree的界面:

1640051336(1).png

可以看到左侧有多个不同的分支,它的意义不同:

  • develop:基于master分支上创建的,是主要的开发分支。
  • feature:当新建功能时,基于devolop创建的分支。它主要是用来开发一个新功能,完成后,就会合并到develop上
  • hotfix:当新建修复补丁时,基于devolop创建的分支。它主要是用来修复某个bug,完成后,就会合并到develop和master分支上
  • release: 当需要发布一个版本时,基于develop分支创建一个release分支,完成release后,会和报哪个到master和develop
  • production : 这个分支就是常使用的master,最近发布的release,这个分支只能由其他分支合并,不能直接修改。

操作流程

1640051568(1).jpg

首先点击上方仓库,再点击Git工作流,再看弹出的小框。 如果没有初始化仓库则先初始化仓库,一个项目只需要初始化一次。然后根据需求,如果创建新功能则点击创建新功能。需要修复bug,就点击建立新的修复补丁。这时可以在featur下看到新建的功能分支,在此分支上进行开发,完成开发后,回到这个小框点击完成功能,则会把该分支自动合并到develop。 补充:在开发的过程中,怎么实时更新新建功能分支的代码。

image.png

右键新建功能分支,弹出图中的框框,点击推送则会推送到远程(远程会新建该分支)。其他功能根据字面意思自己探索一下。

八.常用插件

element ui: 目前使用最多的组件库,基于vue版的 element.eleme.cn/#/zh-CN/com…

zTree:树插件,最大的优点就是加载大量数据,不卡段,上万节点轻松加载。官网地址:www.treejs.cn/v3/main.php… 效果图:

1640222975(1).jpg

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')

1640848470(1).png

图片标注工具: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,去解密加密数据

未完待续......