前端之Javascript


认识前端

了解大前端知识体系,有全局的认知

工程师的技能树

在大前端知识体系中,每个阶段的工程师,需要有不同的知识技能储备。江湖封号程序猿(媛),业内一般称呼软件开发工程师。

初级工程师

初级前端工程师的标准,就是能够完成日常的功能开发和debug,可以在已有的代码基础上做修改,优化和维护;熟练使用平时开发需要用到的工具即可,无需理解原理。这时候需要学习的东西是非常多的,不过由于只要掌握80%(二八法则的比喻,前80%的知识只需要20%的精力学习,后20%深入的知识则需要投入80%的精力)的使用知识,只需要付出20%的精力就能够学会,所以会很有成就感,进步也会非常快。

阶段分析:
需要大量的项目实践经验,至少应用一个框架开发实际项目半年以上,才算真正掌握一个框架的使用。另外就是项目开发过程中运用到的各种工具,每个领域都要熟悉一种,当然,只要熟悉基本操作即可。最后,对于业内的各种话题和思想要有概念,起码做到听说过,大概知道是干什么的。整个初级工程师的学习过程大约会持续1-2年。

学习充电20%:
大量的实际开发经验;足够复杂的业务场景;业内最新信息等。

个人技能80%:

  • 熟练开发语言(js/es6/ts、html/template、css/less/scss、nodejs、json等)
  • 熟练开发框架(react全家桶+antd、vue全家桶+element、angular+material等)
  • 熟练开发工具(IDE、shell、fiddler/charles、git/svn、mock等)
  • 熟练常用库(axios、jquery、lodash、moment等)

中级工程师

在初级前端工程师的基础上,需要在原理上有更深入的理解。能够从较本质的层面分析和解决问题,需要具有从零搭建一个项目的能立,还要能够找出项目瓶颈和可优化的点。这时候就要触及那剩余的20%的知识了,进步的速度会慢下来,需要一定时间的沉淀。此时要适应这个节奏的变化,要耐得住寂寞,只有厚积才能勃发。

阶段分析:
经过初级工程师的阶段,现在最起码已经掌握了1个框架的使用。与初级相比,知识广度上,不会有特别大的变化,但是深度上就要差出一个档次了。当我们对一个事物熟悉到一定程度时,一般都会将其抽象化,以方便我们了解本质,寻找规律,最后达到触类旁通的境界。所以,如果你隐约的能够将之前你觉得不相关的知识联系起来时,那么知识的深度就已经达到了标准。举个最简单的例子:网络层面的缓存与计算机结构中的内存原理,有多少相似的地方?当然,单是某几个自己最常用的知识点达到足够的深度,就已经称得上是一个合格的中级工程师了。整个过程的积累需要大约2-3年的时间。

学习充电20%:
足够深度的原理干货;自己模拟造轮子;业内最佳实践等。

个人技能80%:

  • 项目工程搭建及自动化(webpack/rollup/parcel/gulp等)
  • 性能优化(交互、缓存、网络、运行效率等)
  • 代码质量保证(eslint、stylelint、jest/mocha等)
  • 其他常用领域达到初级水平以上(网络、后端、数据库等)
  • 开始带人

高级工程师

在中级工程师的基础上,具有更好的抽象能力,透过表面看本质。此时已经在开发过程中积累了相当的经验,理解也比较深入,达到了触类旁通的层次,已不局限于框架和库,甚至是前端领域的约束,可以开始自己无中生有的造轮子了。另外,更重要的是对于团队的贡献,要能够成为一个团队的主心骨,明确团队的方向,整合整个团队的力量来做事情。

阶段分析:
此时,自己已经有了多次触类旁通的经验,也就是说自己对于学习一个新知识应该深入到什么程度,已经有了一个比较明确的认知。那么此时的学习诉求便是能够高效的获取其他领域的足够深度的知识。从线到面,构建起一整套的知识系统。中级工程师只要一直积累便可水到渠成的达到这一步。不过,身为高级工程师,也要开始关注技术意外的东西了,比如过管理团队。就像一个初级工程师熟练使用了一个框架之后,要开始学习其他方面的知识一样。高级工程师也就是螺旋上升到了另一个层面,开始了另一个循环而已。到了这个层次,基本已经实现“技术自由”了,而且一定是公司里独当一面的中坚力量。整个过程需要持续3-5年的时间。

学习充电20%:
高效获取足够深度的知识;管理方面的知识;业内趋势的判断等。

个人技能80%:

  • 架构设计(UML)
  • 技术选型
  • 团队开发效率提升(公共组件、Travis CI、jenkins、gerrit/gitlab/Gogs、mock等的搭建)
  • 难题攻关
  • 项目管理(jira、asana、tpad、禅道等)
  • 其他常用领域达到中级水平以上(网络、后端、数据库、运维、分布式等)
  • 开始带团队

技术专家

在业内,要具备一定的技术影响力,以及“代表作”。所作所为可以影响整个公司,乃至整个行业。从流行的追逐者,变成了流行的创造者。当然,能力越大责任越大,公司的决策压力自然也要背负,此时已经不是管理单个团队了,而是对技术方向的全局把控,足够影响公司的战略甚至生存。

阶段分析:
到了这个境界,已经不能够简单的归为技术类了。就像前端高级工程师,已经不仅仅局限于前端这个领域一样。能够达到这个境界,相信已经不需要太多旁人的指手画脚了,能量已经大到足够影响上万人甚至更多。说白了,就是处于金字塔顶端少数的存在。除非转型,那么这个阶段基本可以一直干了,而且是自己挑公司,做自己想做的事,甚至自己当老板。

学习充电20%:
国内外顶级文章;业内痛点及瓶颈;行业及环境宏观分析等。

个人技能80%:

  • 技术影响力
  • 战略级技术架构
  • 跨部门项目推动
  • 英语

七大知识库

HTML5知识库

HTML5知识图谱由前端技术专家、CSDN博客专家侯志强(@yisuowushinian)绘制

CSS3知识库

CSS3知识图谱由前端技术专家、CSDN博客专家侯志强(@yisuowushinian)绘制。

JavaScript知识库

JavaScript知识图谱由Java高级工程师王成委(@jaune161)绘制。

jQuery知识库

jQuery图谱由CSDN博客专家郭晓湉(@XTQueen_up)绘制。

Node.js知识库

Node.js知识图谱由腾讯前端高级工程师黄丹华(@danhuang2012)绘制

AngularJS知识库

AngularJS知识图谱由广发证券前端开发工程师李泽扬绘制

React知识库

React知识图谱由蚂蚁金服前端工程师林展新绘制

理解大前端的定义,不再迷茫

大前端就是所有前端的统称,比如Android、iOS、web、Watch等,最接近用户的那一层也就是UI层,然后将其统一起来,就是大前端。大前端最大的特点在于一次开发,同时适用于所有平台,开发者不用为一个APP需要做Android和iOS两种模式而担心。大前端是web统一的时代,利用web不仅能开发出网站,更可以开发手机端web应用和移动端应用程序。

项目规划及DevOps流程

项目规划

项目规划亦称 “项目设计”。专业人员对项目发起人拟建项目的全面、详细的规划。它是项目方案的具体化,是项目立项、筹资及施工阶段控制的主要依据,直接关系到项目预期目标的实现。项目规划的原则是根据一个研究得出的市场机会及项目发起人组织内的资源状况,使该项目在性能质量、成本及工期三者间达到最优的平衡。

DevOps流程

DevOps流程包含:计划(plan)、编码(code)、编译(build)、测试(test)、发布(release)、部署(deploy)、运营(operate)、监控(monitor),这是一个循环的过程。

DevOps是依托容器、自动化、云计算等技术及精益化管理形成的一种项目过程,有效的促进了开发、测试、运营、运维、QA等团队间的协作,使得团队内、跨团队之间的协作得到极大的提升,可以帮助企业做到产品精益化、运营精益化、管理精益化。

从项目的全生命周期来看,DevOps实现了项目全生命周期的团队高效协作、自动化。DevOps的职责包括:开发和运维的紧密协作、测试和运维的自动化、产品持续交付、持续集成。例如DevOps打通了开发和运维之间的隔阂,加之紫定华运维的出现,大大提高了系统部署的稳定性和安全性。

当团队甚至公司之间践行DevOps理念并且团队成员都能有DevOps的思维时,才能真正做到敏捷。

分析实际工作中遇到的痛点以及解决办法

工作中遇到的痛点

  • 产品的代码太复杂,结构不好,耦合太紧,架构设计完全错误,用户界面和核心逻辑代码混杂在一起,每当修复一个bug或者作出一些修改时,其他部分就像被病毒感染那样受到影响。
  • 代码出现问题,该重写吗?
  • 遇到技术难题怎么办?

解决办法

  1. 审视问题当你遇到麻烦时,首先审视你的问题,看它究竟是什么样的问题,然后针对不同问题想解决的对策。审视你的问题,就是了解问题,找到问题的根源,然后从根源上解决问题。
  2. 正视问题在遇到问题时,应勇于承担问题,正视你的问题,勇敢地面对,不要选择逃避。回避并不能解决问题,反而这种置之不理会带来的伤害更大,更持久。所以在遇到问题,面对问题时应勇敢坚强。
  3. 解决问题愤怒,着急和痛苦不能改变什么,问题只有你决定解决的时候才能消失,所以不要回避问题,有问题就解决,更不要一味地抱怨,抱怨不是解决问题的办法,抱怨过后,你还是要面对解决问题。所以,勇于解决问题,这样才能克服困难。
  4. 继续前进解决问题以后,你应当继续前进,不要再受已解决问题的干扰,不要再一遍一遍地思考问题,这样之只会给你带来紧张和焦虑。向前看,向前走,不要让过去的问题成为你现在的绊脚石。
  5. 总结经验在克服困难的过程中总结经验,当下一个类似问题出现时,你就会知道怎样解决问题了。从困难中学习,就会有新的感想,新的发现,这样可以不断补充自己的能力,使自己工作的更好。遇到困难,也是一种学习,在困难中总结经验,远胜过怨恨困难。
  6. 永远不要灰心丧气不要因为一次或几次问题的出现,就开始怀疑自己的能力,开始灰心丧气。问题是每个人都不可避免的,不要让问题成为衡量你人生价值的标尺,问题在解决的过程中还可以使你的能力得到提升。所以,永远不要被问题吓到,问题可以给你带来更多的机会。
  7. 时刻准备生活中会遇到各种各样的问题,在问题还未到来之前,我们应做好准备,面对困难,解决问题。问题有时是生活中最重要的一部分,接受问题,用积极地心态迎接问题,时刻准备解决问题,这样才会成功。
  8. 很多程序员看别人写的代码很痛苦,心里总有一个念头让你“不要看,快扔掉”,但重写代码比起你重新整理那一堆混乱的代码还要痛苦,bug陈出不穷,你就像面对着一只自己制造出来的怪兽,看到它要毁坏村庄,却又无可奈何。时间方面更值得考量,当你用上一年时间重写代码时,你确定你的软件还会再次受欢迎吗?所以,没有完善的重写计划,不要轻易重写代码。
  9. 理工科的人通常心比较大,做事不很仔细,但做开发人员却需要心细,譬如开发合同的订立,无论是合理不合理的,你想新增或者去掉某些功能等等,不可以随意按照自己的意愿去行事,必须按照合同办事;确实需要改变时,协商更改条约,再拟定新合同或者增加补充合同。
    10.我们相信问题早晚是可以得到解决的,但如果有一定数量的用户,时间就必须分秒必争,否则失去了信誉后,怎么更新、怎么完备的功能都无济于事了

掌握需求分析的要点及工具(墨刀/Axure/蓝湖/XMind)

需求分析也称为软件需求分析、系统需求分析或需求分析工程等,是开发人员经过深入细致的调研和分析,准确理解用户和项目的功能、性能、可靠性等具体要求,将用户非形式的需求表述转化为完整的需求定义,从而确定系统必须做什么的过程。

需求分析的要点

  • 功能性需求
    功能性需求即软件必须完成哪些事,必须实现哪些功能,以及为了向其用户提供有用的功能所需执行的动作。功能性需求是软件需求的主体。开发人员需要亲自与用户进行交流,核实用户需求,从软件帮助用户完成事务的角度上充分描述外部行为,形成软件需求规格说明书。
  • 非功能性需求
    作为对功能性需求的补充,软件需求分析的内容中还应该包括一些非功能需求。主要包括软件使用时对性能方面的要求、运行环境要求。软件设计必须遵循的相关标准、规范、用户界面设计的具体细节、未来可能的扩充方案等。
  • 设计约束
    一般也称做设计限制条件,通常是对一些设计或实现方案的约束说明。例如,要求待开发软件必须使用Oracle数据库系统完成数据管理功能,运行时必须基于Linux环境等。

需求分析的工具

1、墨刀是一款在线设计编辑原型的工具,特点短平快,适合一些APP,小型pc工程,以及一些频繁迭代的产品,优点协同办公效率比较高,目前国内个别大公司以及中小企业都有用到,个人版本免费,但是使用页面数量有限,编辑后产品都是保存直接保存在云上,很方便。不会出现文件丢失的情况,但是也有缺点,就是在网络不通畅,延迟比较高的时候,很麻烦,原型编辑操作会有卡顿现象以及版本不一致现象,还有原型模板暂时不像axure那么丰富。

2、Axure是是历史悠久的产品经理必备工具,功能齐全,交互方式多样,模板资源最丰富,基本上你想要的效果都可以实现,适合在制作PC端软件,尤其是一些针对偏B端的产品,有着明显的优势,而且有破解版,所以使用成本相对较低,而且相比较墨刀,可以离线工作。缺点吗?当然也有,就是前期稍微学习成本相对于来说上手难一点,不过也比较简单。只要用心也很快可以学会。

3、蓝湖也是国产的一款原型协作平台,在其官网上,蓝湖将自身定位为“简单好用的团队工作台”。使用蓝湖可以导入Sketch/Photoshop和Adobe XD的设计稿(通过插件),并在蓝湖上做自动标注和交互原型。对于设计师来说,可在蓝湖进行设计图管理和自动标注。对于产品经理来说,可以在蓝湖做页面逻辑流程图和汇集产品文档。

4、XMind 是一款非常实用的商业思维导图软件,应用全球最先进的Eclipse RCP 软件架构,全力打造易用、高效的可视化思维软件,强调软件的可扩展、跨平台、稳定性和性能,致力于使用先进的软件技术帮助用户真正意义上提高生产率。

从原型设计、接口设计到技术栈的宏观项目架构思维

原型设计

原型设计是交互设计师与PD、PM、网站开发工程师沟通的最好工具。而该块的设计在原则上必须是交互设计师的产物,交互设计以用户为中心的理念会贯穿整个产品。利用交互设计师专业的眼光与经验直接导致该产品的可用性。

接口设计

  • 安全机制的设计
    当接口涉及到用户状态时,每次请求都要带上身份验证信息。
  • 接口数据的设计
    接口的数据一般都采用JSON格式进行传输
  • 接口版本的设计
    数据的变化,比如增加了旧版本不支持的数据类型
    参数的变化,比如新增了参数
    接口的废弃,不再使用该接口了

四种核心架构思维

  • 抽象思维
    对某种事物进行简化表示或描述的过程,抽象让我们关注要素,隐藏额外细节。
  • 分层思维
    为了构建一套复杂系统,我们把整个系统划分成若干个层次,每一层专注解决某个领域的问题,并向上提供服务。有些层次是纵向的,它贯穿所有其它层次,称为共享层。分层也可以认为是抽象的一种方式,将系统抽象分解成若干层次化的模块。
  • 分治思维
    对于一个无法一次解决的大问题,我们会先把大问题分解成若干个子问题,如果子问题还无法直接解决,则继续分解成子子问题,直到可以直接解决的程度,这个是分解(divide)的过程;然后将子子问题的解组合拼装成子问题的解,再将子问题的解组合拼装成原问题的解,这个是组合(combine)的过程。
  • 演化思维
    在互联网软件系统的整个生命周期过程中,前期的设计和开发大致只占三分,在后面的七分时间里,架构师需要根据用户的反馈对架构进行不断的调整。我认为架构师除了要利用自身的架构设计能力,同时也要学会借助用户反馈和进化的力量,推动架构的持续演进,这个就是演化式架构思维。

JavaScript高效开发库

函数库

lodash

推荐指数:⭐️⭐️⭐️⭐️⭐️

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。

地址:https://www.lodashjs.com/

请求库

axios

推荐指数:⭐️⭐️⭐️⭐️⭐️

是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。Features从浏览器中创建 XMLHttpRequests 从node.js 创建 http 请求 支持Promise API 拦截请求和…

地址:http://www.axios-js.com/

flyio

推荐指数:⭐️⭐️⭐️⭐️⭐️

一个支持所有JavaScript运行环境的基于Promise的、支持请求转发、强大的http请求库。可以让您在多个端上尽可能大限度的实现代码复用。

地址:https://www.npmjs.com/package/flyio

富文本

vue-ueditor-wrap

推荐指数:⭐️⭐️⭐️⭐️⭐️

一个“包装”了 UEditor 的 Vue 组件,支持通过 v-model 来绑定富文本编辑器的内容,让 UEditor 的使用简单到像 Input 框一样。省去了初始化 UEditor、手动调用 getContent,setContent 等繁琐的步骤。

地址:https://hc199421.gitee.io/vue-ueditor-wrap/#/home

动画库

Animate.css

推荐指数:⭐️⭐️⭐️⭐️⭐️

内置了很多典型的css3动画,兼容性好使用方便。

地址:http://www.animate.net.cn/

Magic.css

推荐指数:⭐️⭐️⭐️

一款独特的CSS3动画特效包。

网址:https://www.minimamente.com/project/magic/

move.js

推荐指数:⭐️⭐️

一个小型的JavaScript库,通过JS来控制一系列的CSS动画顺序执行,使CSS3动画变得非常简单和优雅。

网址:https://github.com/visionmedia/move.js

滚动库

BetterScroll

推荐指数:⭐️⭐️⭐️⭐️⭐️

better-scroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。

better-scroll 是基于原生 JS 实现的,不依赖任何框架。它编译后的代码大小是 63kb,压缩后是 35kb,zip 后仅有9kb,是一款非常轻量的 JS lib。

网址:https://github.com/ustbhuangyi/better-scroll

iscroll

推荐指数:⭐️⭐️⭐️⭐️

iScroll是一个高性能,资源占用少,无依赖,多平台的javascript滚动插件。

它可以在桌面,移动设备和智能电视平台上工作。它一直在大力优化性能和文件大小以便在新旧设备上提供最顺畅的体验。

网址:http://caibaojian.com/iscroll-5/

存储类

store.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

store.js 是一个兼容绝大部分主流浏览器的 LocalStorage 包装器,不需要借助 Cookie 或者 Flash。store.js 会根据浏览器自动选择使用 localStorage、globalStorage 或者 userData 来实现本地存储功能。

//存储键值对key-value
store.set('username', 'HQG') 

//根据key,获取存储的value值
store.get('username') 

//移除指定的key数据
store.remove('username') 

//清除所有key
store.clear() 

//运用store.js存储轻量级的json对象,自动stringify
store.set('user', { name: 'lcq' }) 

//根据key,获取一个存储对象,自动parse
var user = store.get('user')
console.log(user.name)

地址:https://www.npmjs.com/package/store

推荐指数:⭐️⭐️⭐️⭐️⭐️

js-cookie是一个简单的,轻量级的处理cookies的js API。

地址:https://www.npmjs.com/package/js-cookie

Mock数据类

Mockjs

推荐指数:⭐️⭐️⭐️⭐️⭐️

生成任意随机数据,拦截 Ajax 请求。

地址:https://www.npmjs.com/package/mockjs

数据可视化

ECharts

推荐指数:⭐️⭐️⭐️⭐️⭐️

一个基于 JavaScript 的开源可视化图表库。

地址:https://echarts.apache.org/zh/index.html

D3.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

D3js 是一个可以基于数据来操作文档的 JavaScript 库。可以帮助你使用 HTML, CSS, SVG 以及Canvas 来展示数据。

地址:https://www.d3js.org.cn/

Three.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能。

地址:http://www.webgl3d.cn/

hightchart

推荐指数:⭐️⭐️⭐️⭐️

兼容 IE6+、完美支持移动端、图表类型丰富、方便快捷的 HTML5 交互性图表库。

地址:https://www.highcharts.com.cn/

DataV

推荐指数:⭐️⭐️⭐️

Vue/React 大屏数据展示组件库。

地址:http://datav.jiaminghi.com/

地图数据源

推荐指数:⭐️⭐️⭐️⭐️⭐️

大屏/地图/echarts地图数据都可以从这里面取!

地址:http://datav.aliyun.com/tools/atlas/index.html

地图

高德地图

推荐指数:⭐️⭐️⭐️⭐️⭐️

地址:https://lbs.amap.com/

百度地图

推荐指数:⭐️⭐️⭐️⭐️

地址:https://lbsyun.baidu.com/

地图坐标系转换 (gcoord)

推荐指数:⭐️⭐️⭐️⭐️⭐️

gcoord主要解决了两个问题

  • 能将坐标在不同坐标系下相互转换
  • 能够处理GeoJSON

地址:https://github.com/hujiulong/gcoord

日期处理

moment.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

JavaScript日期处理插件。

moment().format('YYYY-MM-DD HH:mm:ss'); //2021-08-29 23:36:09 
moment("20120901", "YYYYMMDD").fromNow(); //2 years ago      
//等很多

地址:http://momentjs.cn/

day.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

一个轻量的处理时间和日期的 JavaScript 库。

地址:https://github.com/iamkun/dayjs

轮播

swiper.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

Swiper是纯javascript打造的滑动特效插件,面向手机、平板电脑等移动终端。

Swiper能实现触屏焦点图、触屏Tab切换、触屏轮播图切换等常用效果。

Swiper开源、免费、稳定、使用简单、功能强大,是架构移动终端网站的重要选择!

地址:https://www.swiper.com.cn/

slip.js

推荐指数:⭐️⭐️

移动端跟随手指滑动组件,零依赖。

地址:https://github.com/binnng/slip.js

复制粘贴插件

clipboard-polyfill

推荐指数:⭐️⭐️⭐️⭐️⭐️

这个库是现代基于Promise的异步剪贴板API的polyfill。

地址:https://www.npmjs.com/package/clipboard-polyfill

clipboard.js

推荐指数:⭐️⭐️⭐️⭐️

Clipboard.js 实现了纯 JavaScript (无 Flash)的浏览器内容复制到系统剪贴板的功能。可以在浏览器和 Node 环境中使用。支持 Chrome 42+、Firefox 41+、IE 9+、Opera 29+。

地址:https://mateusmirandaalmeida.github.io/clipboard.js/index.html

二维码插件

qrcode.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

qrcode.js 是一个用于生成二维码的 JavaScript 库。主要是通过获取 DOM 的标签,再通过 HTML5 Canvas 绘制而成,不依赖任何库。

地址:https://www.npmjs.com/package/qrcode

拖拽插件

Draggabilly

推荐指数:⭐️⭐️⭐️

可帮你轻松实现网页上各种元素的拖放操作。支持 IE8+ 和多触摸。

地址:https://draggabilly.desandro.com/

dragula

推荐指数:⭐️⭐️⭐️⭐️

dragula让你能够很方便地实现拖拽功能的JS库。Dragula 是一个 JavaScript 库,实现了网页上的拖放功能。提供 JavaScript、AngularJS 和 React 版本。

地址:https://www.npmjs.com/package/dragula

文件上传

WebUploader

推荐指数:⭐️⭐️⭐️⭐️⭐️

WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。

采用大文件分片并发上传,极大的提高了文件上传效率。

地址:http://fex.baidu.com/webuploader/

Canvas库

html2canvas

推荐指数:⭐️⭐️⭐️⭐️⭐️

html2canvas是一款使你可以直接在用户浏览器上截取网页或部分网页的“屏幕快照”的库。

地址:https://www.npmjs.com/package/html2canvas

Fabric.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

是一个强大而简单的 Javascript HTML5 画布库 Fabric 在画布元素之上提供交互式对象模型 Fabric 还具有 SVG-to-canvas(和 canvas-to-SVG)解析器。

地址:http://fabricjs.com/

图片处理

cropperjs

推荐指数:⭐️⭐️⭐️⭐️⭐️

cropperjs是一款非常强大却又简单的图片裁剪工具,它可以进行非常灵活的配置,支持手机端使用,支持包括IE9以上的现代浏览器。(关键是使用方法简单,几行代码就可以搞定)。

地址:https://www.npmjs.com/package/cropperjs

gif.js

推荐指数:⭐️⭐️⭐️⭐️⭐️

gif.js 是一个可直接在浏览器上运行的 JavaScript GIF 编码器。直接生成gif。

地址:https://www.npmjs.com/package/gif.js

LazyLoad

推荐指数:⭐️⭐️⭐️⭐️⭐️

Lazy Load帮助高度较长的网页进行延迟载入图片,尚未浏览到该部分时,不会载入视角外的图片,提高效率。衍生有也有vue-lazyload以及react-lazyload。

地址:https://www.lazyloadjs.cn/

前端国际化

i18n

推荐指数:⭐️⭐️⭐️⭐️⭐️

地址:https://www.npmjs.com/package/i18n

视频/音频

xgplayer

推荐指数:⭐️⭐️⭐️⭐️⭐️

一款带解析器、支持音频、歌词解析、能节省流量的HTML5视频播放器。

地址:https://v2.h5player.bytedance.com/

dplayer

推荐指数:⭐️⭐️⭐️⭐️

一个很好看的弹幕视频播放器。

地址:http://dplayer.js.org/

video.js

推荐指数:⭐️

Video.js 是一个通用的在网页上嵌入视频播放器的 JS 库,Video.js 自动检测浏览器对 HTML5 的支持情况,如果不支持 HTML5 则自动使用 Flash 播放器。说真的(不好看且难改)

地址:https://github.com/videojs/video.js

其他插件

screenfull.js

推荐指数:⭐️⭐️

浏览器全屏插件,消除浏览器差异。

地址:https://www.npmjs.com/package/screenfull

highlight.js

推荐指数:⭐️⭐️⭐️⭐️

针对Web程序所做的高亮显示上色。

地址:https://www.fenxianglu.cn/highlight.html

提升Javascript代码质量

简介

主要介绍以下几点:

  1. 提炼函数
  2. 合并重复的条件片段
  3. 把条件分支语句提炼成函数
  4. 合理使用循环
  5. 提前让函数退出代替嵌套条件分支
  6. 传递对象参数代替过长的参数列表
  7. 少用三目运算符
  8. 合理使用链式调用
  9. 分解大型类
  10. 活用位操作符
  11. 纯函数

1. 提炼函数

好处:

  • 避免出现超大函数。
  • 独立出来的函数有助于代码复用。
  • 独立出来的函数更容易被覆写。
  • 独立出来的函数如果拥有一个良好的命名,它本身就起到了注释的作用。
  • 语义化将多段分离的逻辑放在不同的函数中实现,可以使代码逻辑清晰,清楚的看到每一步在做什么。

代码举例:

实现获取数据,然后操作dom显示数据,最后添加事件

  • 函数提炼前
// 逻辑都写在一起,需要将所有逻辑看完才知道这段代码是干嘛的,局部逻辑无法复用
function main() {
    $.ajax.get('/getData').then((res) => {
        const ul = document.getElementById('ul');
        ul.innerHTML = res.list.map(text => `<li class="li">${text}</li>`).join('\n');
        const list = document.getElementsByClassName('li');
        for (let i = 0; i < list.length; i ++) {
            list[i].addEventListener('focus', () => {
                // do something
            });
        }
    });
}
  • 函数提炼后
function getData() {
    return $.ajax.get('/getData').then((res) => res.data.list);
}
function showList(list) {
    const ul = document.getElementById('ul');
    ul.innerHTML = list.map(text => `<li class="li">${text}</li>`).join('\n');
}
function addEvent() {
    const list = document.getElementsByClassName('li');
    for (let i = 0; i < list.length; i ++) {
        list[i].addEventListener('focus', () => {
            // do something
        });
    }
}
// 逻辑清晰,一眼读懂每一步在做什么,某些提炼出来的函数还可以被复用
async function main() {
    const list = await getData(); // 获取数据
    showList(list); // 显示页面
    addEvent(); // 添加事件
}

2. 合并重复的条件片段

如果一个函数体内有一些条件分支语句,而这些条件分支语句内部散布了一些重复的代码,那么就有必要进行合并去重工作。

// 合并前
function main( currPage ){
    if ( currPage <= 0 ){
        currPage = 0;
        jump( currPage ); // 跳转
    }else if ( currPage >= totalPage ){
        currPage = totalPage;
        jump( currPage ); // 跳转
    }else{
        jump( currPage ); // 跳转
    }
};

// 合并后
function main( currPage ){
    if ( currPage <= 0 ){
        currPage = 0;
    }else if ( currPage >= totalPage ){
        currPage = totalPage;
    }
    jump( currPage ); // 把jump 函数独立出来
};

3. 把条件分支语句提炼成函数

复杂的条件分支语句是导致程序难以阅读和理解的重要原因,而且容易导致一个庞大的函数。有时可以将条件分支语句提炼成语义化的函数,使代码更加直观,逻辑清晰。

// 根据不同季节决定打折力度
function getPrice( price ){
    var date = new Date();
    if ( date.getMonth() >= 6 && date.getMonth() <= 9 ){ // 夏天
        return price * 0.8;
    }
    return price;
};


// 是否是夏天
function isSummer(){
    var date = new Date();
    return date.getMonth() >= 6 && date.getMonth() <= 9;
};
// 提炼条件后
function getPrice( price ){
    if ( isSummer() ){
        return price * 0.8;
    }
    return price;
};

4. 合理使用循环

如果多段代码实际上负责的是一些重复性的工作,那么可以用循环代替,使代码量更少。

// 判断是什么浏览器
function getBrowser(){
    const str = navigator.userAgent;
    if (str.includes('QQBrowser')) {
 return 'qq';
    } else if (str.includes('Chrome')) {
 return 'chrome';
    } else if (str.includes('Safari')) {
        return 'safri';
    } else if (str.includes('Firefox')) {
        return 'firefox';
    } else if(explorer.indexOf('Opera') >= 0){
        return 'opera';
    } else if (str.includes('msie')) {
        return 'ie';
    } else {
        return 'other';
    }
};


// 循环判断,将对应关系抽象为配置,更加清晰明确
function getBrowser(){
    const str = navigator.userAgent;
    const list = [
        {key: 'QQBrowser', browser: 'qq'},
        {key: 'Chrome', browser: 'chrome'},
        {key: 'Safari', browser: 'safari'},
        {key: 'Firefox', browser: 'firefox'},
        {key: 'Opera', browser: 'opera'},
        {key: 'msie', browser: 'ie'},
    ];
    for (let i = 0; i < list.length; i++) {
        const item = list[i];
        if (str.includes(item.key)) {return item.browser};
    }
    return 'other';
}

5. 提前让函数退出代替嵌套条件分支

让函数变成多出口提前返回,替换嵌套条件分支

function del( obj ){
    var ret;
    if ( !obj.isReadOnly ){ // 不为只读的才能被删除
        if ( obj.isFolder ){ // 如果是文件夹
            ret = deleteFolder( obj );
        }else if ( obj.isFile ){ // 如果是文件
            ret = deleteFile( obj );
        }
    }
    return ret;
};

function del( obj ){
    if ( obj.isReadOnly ){ // 反转if 表达式
        return;
    }
    if ( obj.isFolder ){
        return deleteFolder( obj );
    }
    if ( obj.isFile ){
        return deleteFile( obj );
    }
};

6. 传递对象参数代替过长的参数列表

函数参数过长那么就增加出错的风险,想保证传递的顺序正确就是一件麻烦的事,代码可读性也会变差,尽量保证函数的参数不会太长。如果必须传递多个参数的话,建议使用对象代替。

一般来说,函数参数最好不要超过3个

function setUserInfo( id, name, address, sex, mobile, qq ){
    console.log( 'id= ' + id );
    console.log( 'name= ' +name );
    console.log( 'address= ' + address );
    console.log( 'sex= ' + sex );
    console.log( 'mobile= ' + mobile );
    console.log( 'qq= ' + qq );
};
setUserInfo( 1314, 'sven', 'shenzhen', 'male', '137********', 377876679 );

function setUserInfo( obj ){
    console.log( 'id= ' + obj.id );
    console.log( 'name= ' + obj.name );
    console.log( 'address= ' + obj.address );
    console.log( 'sex= ' + obj.sex );
    console.log( 'mobile= ' + obj.mobile );
    console.log( 'qq= ' + obj.qq );
};
setUserInfo({
    id: 1314,
    name: 'sven',
    address: 'shenzhen',
    sex: 'male',
    mobile: '137********',
    qq: 377876679
});

7. 少用三目运算符

三目运算符性能高,代码量少。

但不应该滥用三目运算符,我们应该在简单逻辑分支使用,在复杂逻辑分支避免使用。

// 简单逻辑可以使用三目运算符
var global = typeof window !== "undefined" ? window : this;

// 复杂逻辑不适合使用
var ok = isString ? (isTooLang ? 2 : (isTooShort ? 1 : 0)) : -1;

8. 合理使用链式调用

优点: 链式调用使用简单,代码量少。

缺点: 链式调用带来的坏处就是在调试不方便,如果我们知道一条链中有错误出现,必须得先把这条链拆开才能加上一些调试 log 或者增加断点,这样才能定位错误出现的地方。

如果该链条的结构相对稳定,后期不易发生修改,可以使用链式。

var User = {
    id: null,
    name: null,
    setId: function( id ){
        this.id = id;
        return this;
    },
    setName: function( name ){
        this.name = name;
        return this;
    }
};
User
  .setId( 1314 )
  .setName( 'sven' );

var user = new User();
user.setId( 1314 );
user.setName( 'sven' );

9. 分解大型类

大型类的分解和函数的提炼很像,类太大会出现逻辑不清晰,难以理解和维护的问题。

合理的大类分解可以使类的逻辑清晰,且子模块可以方便复用。

10. 活用位操作符

编程语言计算乘除的性能都不高,但是某些情况使用位操作符可以提升乘除等运算的性能。

11. 纯函数

纯函数是指不依赖于不改变它作用域之外的变量状态的函数。

纯函数的返回值只由它调用时的参数决定,它的执行不依赖于系统的状态(执行上下文)。

相同的输入参数,一定会得到相同的输出,也就是内部不含有会影响输出的随机变量

不属于纯函数的特点:

  • 更改文件系统
  • 往数据库插入记录
  • 发送一个 http 请求
  • 可变数据
  • 打印/log
  • 获取用户输入
  • DOM 查询
  • 访问系统状态

纯函数的作用:

  • 可靠性:函数返回永远和预期一致
  • 可缓存性:因为只要输入一样输出一定一样,因此可将输入作为key,输出作为值,使用对象缓存已经计算的结果
  • 可移植性:因为没有外部依赖,所以移植到任何环境都可正确运行
  • 可测试性:方便针对函数做单元测试
  • 可并行性:对一些复杂计算,可以并行计算(例如使用nodejs多个子进程同时并行计算多个任务,提高计算速度)

应用场景:

  • 工具函数最好使用纯函数
  • 多平台使用的代码(nodejs、浏览器、微信小程序、native客户端等)
  • 相对独立的功能
var a = 1;
// 非纯函数
function sum(b) {
    return a + b;
}
// 非纯函数
function sum(b) {
    a = 2;
    return b;
}
// 非纯函数
function sum(b) {
    return b + Math.random();
}


// 纯函数
function sum (b, c) {
    return b + c;
}

20 个常用 JavaScript 单行代码

获取浏览器Cookie的值

通过document.cookie 来查找cookie

const cookie = name => `; ${document.cookie}`.split(`; ${name}=`).pop().split(';').shift();

cookie('_ga');
// Result: "GA1.2.1929736587.1601974046"

颜色RGB转十六进制

const rgbToHex = (r, g, b) => "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);

rgbToHex(0, 51, 255); 
// Result: #0033ff

复制到剪贴板

借助navigator.clipboard.writeText可以很容易的讲文本复制到剪贴板

规范要求在写入剪贴板之前使用 Permissions API 获取“剪贴板写入”权限。但是,不同浏览器的具体要求不同,因为这是一个新的API。有关详细信息,请查看compatibility table and Clipboard availability in Clipboard。

const copyToClipboard = (text) => navigator.clipboard.writeText(text);

copyToClipboard("Hello World");

检查日期是否合法

使用以下代码段检查给定日期是否有效。

const isDateValid = (...val) => !Number.isNaN(new Date(...val).valueOf());

isDateValid("December 17, 1995 03:24:00");
// Result: true

查找日期位于一年中的第几天

const dayOfYear = (date) =>
      Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24);

dayOfYear(new Date());
// Result: 272

英文字符串首字母大写

Javascript没有内置的首字母大写函数,因此我们可以使用以下代码。

const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)

capitalize("follow for more")
// Result: Follow for more

计算2个日期之间相差多少天

const dayDif = (date1, date2) => Math.ceil(Math.abs(date1.getTime() - date2.getTime()) / 86400000)

dayDif(new Date("2020-10-21"), new Date("2021-10-22"))
// Result: 366

清除全部Cookie

通过使用document.cookie访问cookie并将其清除,可以轻松清除网页中存储的所有cookie。

const clearCookies = document.cookie.split(';').forEach(cookie => document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date(0).toUTCString()};path=/`));

生成随机十六进制颜色

可以使用 Math.randompadEnd 属性生成随机的十六进制颜色。

const randomHex = () => `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")}`;

 console.log(randomHex());
// Result: #92b008

数组去重

可以使用 JavaScript 中的Set轻松删除重复项

const removeDuplicates = (arr) => [...new Set(arr)];

console.log(removeDuplicates([1, 2, 3, 3, 4, 4, 5, 5, 6]));
// Result: [ 1, 2, 3, 4, 5, 6 ]

从 URL 获取查询参数

可以通过传递 window.location 或原始 URL goole.com?search=easy&page=3 轻松地从 url 检索查询参数

const getParameters = (URL) => {
  URL = JSON.parse(
    '{"' +
      decodeURI(URL.split("?")[1])
        .replace(/"/g, '\\"')
        .replace(/&/g, '","')
        .replace(/=/g, '":"') +
      '"}'
  );
  return JSON.stringify(URL);
};

getParameters(window.location);
// Result: { search : "easy", page : 3 }

或者更为简单的:

Object.fromEntries(new URLSearchParams(window.location.search))
// Result: { search : "easy", page : 3 }

时间处理

我们可以从给定日期以 hour::minutes::seconds 格式记录时间。

const timeFromDate = date => date.toTimeString().slice(0, 8);

console.log(timeFromDate(new Date(2021, 0, 10, 17, 30, 0))); 
// Result: "17:30:00"

校验数字是奇数还是偶数

const isEven = num => num % 2 === 0;

console.log(isEven(2)); 
// Result: True

求数字的平均值

使用reduce方法找到多个数字之间的平均值。

const average = (...args) => args.reduce((a, b) => a + b) / args.length;

average(1, 2, 3, 4);
// Result: 2.5

回到顶部

可以使用 window.scrollTo(0, 0) 方法自动滚动到顶部。将 xy 都设置为 0。

const goToTop = () => window.scrollTo(0, 0);

goToTop();

翻转字符串

可以使用 splitreversejoin 方法轻松反转字符串。

const reverse = str => str.split('').reverse().join('');

reverse('hello world');     
// Result: 'dlrow olleh'

校验数组是否为空

一行代码检查数组是否为空,将返回truefalse

const isNotEmpty = arr => Array.isArray(arr) && arr.length > 0;

isNotEmpty([1, 2, 3]);
// Result: true

获取用户选择的文本

使用内置的getSelection 属性获取用户选择的文本。

const getSelectedText = () => window.getSelection().toString();

getSelectedText();

打乱数组

可以使用sortrandom 方法打乱数组

const shuffleArray = (arr) => arr.sort(() => 0.5 - Math.random());

console.log(shuffleArray([1, 2, 3, 4]));
// Result: [ 1, 4, 3, 2 ]

检查用户的设备是否处于暗模式

使用以下代码检查用户的设备是否处于暗模式。

const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches

console.log(isDarkMode) 
// Result: True or False

10 个 JS 小技巧

1、空运算符

如果左侧为空或未定义,则此运算符返回右侧值。

const data= null ?? 'data';
console.log(data);
// expected output: "data"
const data1 = 1 ?? 4;
console.log(data1);
// expected output: 1

逻辑 OR (||) 运算符执行相同的操作,但是,当将 0 作为值传递时,它将视为 false,这使得它容易用于数字。

function add(a, b) {
    val1 = a || 1;
    val2 = b || 1;
    sum = val1 + val2;
    return sum;
}

console.log(add(0, 0)); //output:2

当我们使用 Nullish 运算符时,同样的事情

function add1(a, b) {
    val1 = a ?? 1;
    val2 = b ?? 1;
    sum = val1 + val2;
    return sum;
}

console.log(add1(0, 0)); //ouput:0

2、Switch 语句优化

如果你想优化你的 switch 语句,那么,这个语句会有所帮助。


// Longhand
switch (data) {
    case 1:
        data1();
        break;
    case 2:
        data2();
        break;
    case 3:
        data();
        break;
        // And so on...
}
// Shorthand
var data = {
    1: data1,
    2: data2,
    3: data
};
const val = 1
data[val]();
function data1() {
    console.log("data1");
}
function data2() {
    console.log("data2");
}
function data() {
    console.log("data");
}

3、控制台样式

您是否厌倦了使用相同的控制台?现在我们可以设计我们的控制台。

console.log(`%cabc`, 'font-weight:bold;color:red');

4、AND (&&) 运算符

如果我们想避免一个 if 语句,那么这个速记会很有帮助。

//Longhand 
if (test1) {
 callMethod(); 
}
//Shorthand 
test1 && callMethod();

5、短函数调用

我们可以使用三元运算符来实现这些功能。

// Longhand
function data1() {
    console.log('data1');
};
function data2() {
    console.log('data2');
};
var data3 = 1;
if (data3 == 1) {
    data1();
} else {
    data2();
} //data1
// Shorthand
(data3 === 1 ? data1 : data2)(); //data1

6、返回简写

这将有助于避免大量代码专门返回到基于返回语句的调用方法。

// Longhand
let value;
function returnMe() {
    if (!(value === undefined)) {
        return value;
    } else {
        return callFunction('value');
    }
}
var data = returnMe();
console.log(data); //output value
function callFunction(val) {
    console.log(val);
}
// Shorthand
function returnMe() {
    return value || callFunction('value');
}

7、 If… else 简写

当我们有 if-else 语句时,这会有所帮助(确保您有最多 2-3 个 if…else 语句,因为多于这些会降低代码的可读性)。

// Longhand
let mychoice: boolean;
if (money > 100) {
    mychoice= true;
} else {
    mychoice= false;
}
// Shorthand
let mychoice= (money > 10) ? true : false;
//or we can use directly
let mychoice= money > 10;
console.log(mychoice);

嵌套条件如下所示:

let salary = 300,
checking = (salary > 100) ? 'greater 100' : (x < 50) ? 'less 50' : 'between 50 and 100';
console.log(checking); // "greater than 100"

8、可选链

有时,访问未定义的属性会出错,我们需要为所有嵌套对象属性添加空检查。可以使用可选链接来减少它。

const data = {
    a: 1,
    b: 'atit',
    d: {
        test1: {
            test2: 'patel',
        },
    },
};
console.log(data.val.test1); // here val is not present in object which leads the error
Error: Cannot read properties of undefined (reading 'test1')
console.log(data?.val); // using this we can check if the val is present in the data or not

9、对象属性赋值

当我们想从两个字符串创建对象并保持与字符串相同的键时,可以使用这个技巧来完成。

let data1 = 'abcd'; 
let data2 = 'efgh';
//Longhand 
let data = {data1: data1, data2: data2};
//Shorthand 
let data = {data1, data2};

10、延迟

当 JavaScript 代码量增加时,可能会导致浏览器必须等到所有脚本都执行完后再加载 DOM,从而增加了等待时间。

通过使用这个属性,我们可以告诉浏览器不要等待脚本;相反,它将继续构建 DOM,并在后台加载脚本。

<p>heading before loads</p>
<script defer src="src/test.js"></script>
<p>heading after loads</p>

文章作者: 杰克成
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杰克成 !
评论
  目录