一个前端大佬的十年回顾 | 漫画前端的前世今生
Posted Jcloud
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个前端大佬的十年回顾 | 漫画前端的前世今生相关的知识,希望对你有一定的参考价值。
作者:京东科技 胡骏
引言
岁月如梭,十载流年
前端技术,蓬勃向前
HTML,CSS,JavaScript
演绎出璀璨夺目的技术画卷
回到十年前,前端技术就像一名戴着厚重眼镜的书呆子,总是小心翼翼,被各种各样的浏览器兼容性问题欺负(就像在小学被欺负一样)。
但随着时间的推移,这个书呆子开始锻炼,变得越来越强壮,终于能够对抗那些讨厌的兼容性问题
进入中学时期,前端技术遇到了那个改变它一生的朋友——jQuery。在jQuery的帮助下,前端技术变得更加自信,能够在各种浏览器之间轻松穿梭(就像找到了武林秘籍,功力大增)。
随后,前端技术开始追求更高的境界。它遇到了三位美丽的姑娘:Angular、React和Vue。这三位姑娘带给了前端技术无尽的魅力,让它迅速崛起,成为了技术江湖中的一股新兴力量。
如今,前端技术已经变得越来越强大,像一个熟练掌握各种武功的高手。它的发展速度之快,令人瞠目结舌,仿佛在短短十年内成为了武林盟主。它带领着一群忠诚的拜金党(程序员),在技术江湖中闯荡,创造了一个又一个的传奇。
而现在,前端技术正在为未来的挑战做准备,它还能带给我们多少惊喜,以及如何抵抗那些不断涌现的挑战者?让我们一起拭目以待,看这场武林大戏如何演绎。
一、历程
前端技术开发在过去的十年里经历了从HTML、CSS到JavaScript的演变。在这个历程中,前端工程师的角色也发生了变化,他们不再只是单纯的代码开发者,还需要与设计师、产品经理、运营人员等其他团队成员协作,共同完成网站的开发。
• _2010年以前,_前端工程师主要负责网站的静态页面开发,如网页设计、图片处理等。在这个阶段,前端工程师的技能主要包括HTML、CSS和JavaScript等基本技术。
• _2010年,_JavaScript成为了前端开发的主要语言。随着JavaScript的普及和发展,越来越多的前端工程师开始关注JavaScript的应用和开发。
• _2011年,_jQuery成为了前端开发的主流库,并且HTML5和CSS3开始受到重视。这也是前端开发变得更加动态和交互性的开始。
• _2012年,_响应式设计和移动设备优先的设计理念开始流行,前端开发在移动端上崭露头角。
• _2013年,_Angular引入了模块化和数据绑定的概念,Bootstrap实现了响应式框架,前端开发变得更加简单和高效。
• _2014年,_React发布,革新出组件化的思想,前端开发变得更加灵活和可维护。
• _2015年,_ES6发布,带来了诸如箭头函数、模板字符串和解构赋值等语言的改进,使JavaScript变得更加易用和现代化。同年,Vue的发布迅速获得了广泛应用。
• _2016年,_前端工具链的发展得到了加速,例如Webpack和Babel等工具的普及使得前端工程化得到了广泛推广。
• _2017年,_JavaScript库和框架更加多样,Angular、React和Vue等都在不断地演进和优化。PWA技术的普及使得网页更接近原生应用的用户体验。
• _2018年,_JavaScript框架的选择更加复杂,同时CSS预处理器(例如Sass和Less)和CSS-in-JS的技术也逐渐成熟。
• _2019年,_前端技术继续保持快速发展的趋势,更加注重用户体验和开发效率。例如,React Hooks和Vue 3等技术的推出使得前端代码更简洁并可维护。
• _2020年,_因新冠疫情影响,居家办公及远程工作成为新趋势。虚拟会议和在线教育等普及推动了前端技术的发展,也更加重视了访问性和用户体验。
• _2021年,_新技术和工具不断推陈出新。Web Assembly使得前端代码获得更高的效率,而预渲染和静态站点生成等技术让前端应用可以获得更快的加载速度。
• _2022年,_VR(虚拟现实)和AR(增强现实)技术的不断发展,前端开发者需要开发出更加适合VR/AR场景的应用程序。
• _2023年至今,_AI(人工智能)技术的突破性进展,前端技术将在AI 技术的加持下得到更广泛的应用,从而带来更智能和更高效的前端开发体验。
二、HTML5和CSS3的普及
HTML5和CSS3,这两个神秘代码世界的统治者,它们的名字听起来像是一对科学家的昵称,但它们的影响力却是无与伦比的:让我们的网页从普通变得绚丽多彩。
作为一名网页开发者,我们经常需要面对一些令人头疼的问题:浏览器兼容性、页面加载速度缓慢等。但是,当HTML5和CSS3出现在我们的视野中时,一切都变得不一样了。
HTML5是一种用于网页开发的语言,它具有更强的多媒体功能,比如说可以轻松地嵌入音频和视频。它还具有更强的语义,使我们可以更容易地描述页面内容。
而CSS3则是一种用于美化网页的语言,它提供了更多的样式选项,比如说可以实现圆角、阴影等效果。它还支持响应式设计,可以让我们的网页在不同的设备上都能得到最佳的展示效果。
用HTML5和CSS3开发的网页不仅美观,而且更快。我们不再需要使用大量的JavaScript代码来实现一些简单的功能,因为HTML5和CSS3已经帮我们完成了这些工作。
不仅如此,HTML5和CSS3还使得网页开发变得更有趣。我们可以创造出各种各样的动画效果,比如说滚动、旋转等,而不需要依赖任何第三方工具。这不仅让我们的网页更具吸引力,也使我们的用户更容易理解和使用。
HTML5就像一个网页的“建造师”,它负责把网页的框架建造出来,而CSS3则是一个“装饰师”,它负责把网页的外观和感觉打造出来。这对搭档携手合作,把一栋美丽的大厦(网站)拔地而起。
三、JavaScript框架的崛起
JavaScript框架,从这个词语中我们就能感受到它的强大和威力,如同统治世界的巨龙,横行天下,让所有的开发者都震撼不已。
在过去的十年里,我们见证了许多JavaScript框架的诞生和发展。最早的Angular和Backbone逐渐被React和Vue等框架所取代。这些框架不仅简化了开发者的工作流程,还引入了组件化的开发思想,提升了Web应用的可维护性和可扩展性。
另外,JavaScript框架也推动了Web前端技术的进步,引入了许多新的概念和理念,如组件化、数据驱动等等,使得Web前端开发变得更加简单而清晰。
3.1 React:让你的用户界面如此简单
React,这是一个神奇的JavaScript框架,它可以让你的用户界面变得如此简单,以至于你会想:“这就是魔法吗?”
React的核心思想是组件化,它把用户界面拆分成许多小的组件,每个组件都可以独立运行,并且可以方便地复用。这样,你就可以更加简单高效地开发出高质量的用户界面。
React的另一个优秀特性是Virtual DOM,它可以帮助你更快速地渲染用户界面,并且不会影响用户体验。这就像是一个超级快速的缓存,让你的用户界面飞快地呈现在用户面前。
React还提供了一些非常实用的功能,比如说React Router,它可以帮助你管理路由,让用户界面更加流畅;而React Redux可以帮助你管理应用状态,让你的代码更加整洁。
此外,React是一个非常活跃的开源项目,它的开发者们一直在不断改进和完善,值得每一个前端开发者去学习和使用。
3.2 Vue:充满了年轻的活力和智慧
Vue是另一个JavaScript框架,可以让你快速构建网页,就像是一个魔术师,把一堆杂乱无章的东西变成了一个美丽的魔术。
Vue的核心思想是数据驱动+组件化。这听起来很高大上,但其实就像是你在做一道数学题,先把问题分解成若干小问题,再一步步解决。
Vue有一个很酷的特性:双向绑定。这听起来很神秘,但实际上就像是你和你的好朋友之间的对话,你说了什么,他就知道了。
学习和使用Vue的过程中,你会发现开发变得更加简单和有趣,就像是在做一道神奇的拼图,一步步把图片拼出来,比如说它有很多组件,就像是一个工具箱,你可以随时随地使用。组件的好处在于,它可以把复杂的功能分解成若干个简单的部分,这样就可以很容易地管理和维护你的代码。
同时,Vue有很多很多的插件,可以让你的开发体验更加顺畅。插件的好处在于,它可以帮助你实现一些复杂的功能,这样就不必自己写一坨代码。
Vue还有一个很棒的社区,可以帮助你解决一些棘手的问题,也方便了你与其他开发者交流经验,编码世界有了朋友,永远不会孤单。
3.3 谨慎:利剑具有两面性
JavaScript框架是一个非常重要的工具,就像一把利剑帮助开发者切开困难,让开发者更加简便高效地开发前端应用,也推动了前端技术的进步,并抵达成功的彼岸。
但是,请记住,刀刃朝向你,也有可能伤到自己,因此请开发者在使用JavaScript框架时要谨慎小心。
四、Node.js和前后端分离
首先,让我们回顾一下过去,那时候前后端是紧密结合在一起的,像一对结婚多年的夫妇。它们有着许多共同的爱好,但是有时它们也会产生冲突,就像夫妇间的争吵一样,前后端争吵也是不可避免。
但是,随着技术的发展,我们发现了一个新的解决方案:前后端分离。就像夫妇分居一样,前后端也可以分开,以避免冲突,Node.js就是这个分离的功臣。
Node.js可以帮助前端和后端分开,各自独立工作。前端可以专注于用户界面的开发,后端可以专注于数据的处理,就像夫妇分别在各自的工作岗位上工作一样,前后端也可以分别在各自的领域里工作。
Node.js的出现让JavaScript可以在服务器端运行,为前后端分离的架构模式提供了可能。前后端分离使开发者可以更加专注于前端应用的开发,提高开发效率。同时,Node.js的诞生也带来了诸如npm、yarn等包管理器的出现,开发者可以轻松地引入和管理第三方库。
4.1 npm:被忽视的少年
首先,让我们了解一下npm的历史。它曾经是一个年轻的少年,总是被忽视。但是随着它长大,它变得越来越强大,并且成为了Node.js开发的重要组成部分。
以前,如果我们想要安装一个库,需要手动下载,并且手动安装它。这是一件非常繁琐的事情,而且很容易出错。但是,随着npm的出现,一切都变得更简单了。只需要运行一条命令(如:npm install
),就可以轻松地安装任何库。
npm还提供了一个巨大的软件仓库,其中包含了数以千计的库和工具。它就像一个图书馆,你可以随心所欲地查阅和使用。
但是,npm不仅仅是一个简单的安装工具。它还像一个管家,辅助我们管理依赖关系,并帮助我们发布代码和维护代码库。它还有许多其他功能,例如构建工具,测试工具等。因此,如果你想充分利用npm,请不要仅仅停留在它的基础功能上。
4.2 yarn:少年的替身
首先,让我们了解一下yarn的由来。它的诞生是为了解决npm的一些问题,就像是一个少年的替身,它试图取代npm并成为新的领导者。
yarn可以帮助我们快速安装依赖包,并管理依赖关系,像一个组织者可以帮助我们维护代码库,以此节省时间并提高开发效率。
yarn还提供了一个更好的版本控制系统,可以帮助我们管理依赖项的版本。如果你在多个项目中使用相同的依赖项,可以确保所有项目使用相同的版本,从而避免了版本冲突,譬如一个和平协调员。
除了管理依赖关系和解决依赖冲突外,yarn还可以帮助我们更快地进行安装,因为它可以在本地缓存安装过的依赖项。这意味着,如果你在多个项目中使用相同的依赖项,它们将不会再次下载,从而减少了安装时间。
此外,yarn支持并行安装,这意味着它可以同时安装多个依赖项,从而加快安装速度。这是一个非常有用的功能,特别是当你需要安装大量依赖项时。
yarn也有一个很棒的社区,可以帮助你解决任何问题。如果你在使用yarn时遇到问题,可以在社区中寻求帮助。这是一个非常有价值的资源,可以帮助你更快地解决问题。
五、构建工具和自动化
构建工具和自动化是现代软件开发的重要组成部分,就像给你的代码加上糖衣一样,帮助我们提高开发效率,并且可以让我们更专注于代码本身。
Grunt、Gulp、Webpack等工具的出现,使得开发者可以方便地实现代码压缩、合并、优化以及模块化等功能。而随着CI/CD的普及,自动化测试和部署变得更加便捷,大大提高了软件开发的质量和开发速度。
5.1 Grunt:猪叫的声音?
Grunt,这不是一个军人,也不是一个猪叫的声音。实际上,它是个非常酷的JavaScript任务运行器,可以帮助你自动化各种任务,如代码构建,单元测试和文件合并。它的目的是让你的工作变得更轻松、更有效率,而且不需要你不停地敲代码。
想象一下,每次你修改了一个文件,你就需要手动编译、压缩、合并、测试等等。这听起来很枯燥,对吧?但是,如果有一个工具能帮你自动完成这些任务,那该有多好!这就是Grunt的作用。
Grunt的核心思想是使用插件(plugins)来完成各种任务。有数以百计的插件可以帮助你实现从编译Sass到压缩JavaScript的各种任务。插件是通过npm安装的。Grunt有许多内置任务,例如:文件压缩,CSS预处理,JavaScript检查等。此外,还有大量第三方插件,也可以助你完成更多任务。
Grunt的配置文件是Gruntfile.js
,用于定义任务和任务的配置。Grunt使用JavaScript代码配置任务,因此对JavaScript基础知识的了解是使用Grunt的必备条件。
Grunt的任务可以在命令行中通过运行以下命令执行:grunt task-name
。如果你想要实时监控文件的变化,并在文件变化时自动执行任务,你可以使用grunt watch
命令。
如果你是一个JavaScript开发者,那么Grunt是一个不可或缺的工具。它可以让你的工作变得更快捷、更高效,让你有更多的时间去做其他有趣的事情,比如喝咖啡、写文章或者是找对象。
5.2 Gulp:古老的咒语?
让我们来说说Gulp的名字。它的名字听起来有点像一个古老的魔法咒语,你想:“Gulp!” 然后你的代码就会变得更快、更简洁、更酷。不过,实际上Gulp并不是魔法,而是非常实用的构建工具。
Gulp的工作原理很简单:它通过创建一系列的任务,来自动完成你的工作流程。比如说,你可以创建一个任务,来自动编译你的Sass文件,或者压缩你的JavaScript文件。这样,你就不需要手动执行这些步骤了,Gulp会帮你完成。
Gulp还有一个非常酷的功能:它可以实时监控你的文件,并在你修改了文件后立即执行相应的任务。这样,你就可以实时看到更改的内容,而不需要手动重新执行。
Gulp如何使用呢?首先,你需要安装Node.js和npm,因为Gulp是基于Node.js的。其次,安装Gulp的命令行工具,只需在终端中运行以下命令即可:npm install gulp-cli -g
。接下来,你需要在项目目录中创建一个package.json
文件,这是npm的配置文件,用于管理项目依赖。你可以通过运行以下命令来创建一个package.json
文件:npm init
。然后,你需要安装Gulp,只需在项目目录中运行以下命令即可:npm install gulp--save-dev
。最后,创建一个gulpfile.js
文件,这是Gulp的配置文件,用于编写你的任务。
现在,你已经准备好使用Gulp了。开始编写你的任务,并运行以下命令来执行吧:gulp task-name
。
5.3 Webpack:订制的包包?
Webpack可以帮你把代码压缩成小而美的包,就像私人订制的收纳柜,它可以装下你所有的包包,并且把它们整齐地放在一起,使你的“奢侈”更加有序!
但是,如果你犯了错误,它就像一个恶魔般出现在你面前,吼叫着告诉你:“Error: This is error!”所以,请小心使用Webpack。
不过,只要你已经掌握了Webpack的使用方法,那么它将成为你的最佳伙伴,因为它可以为你节省大量的时间,并且让你的代码变得更加整洁。
你可以告诉Webpack:“嘿,Webpack!帮我处理图片和字体!” 然后Webpack就会用它的魔力,将它们变成小小的Data URL或文件。你不会相信,Webpack的魔力是如此的强大,它甚至可以帮你处理模块依赖。
那么,如何使用Webpack呢?首先,你需要安装它(就像是奢侈品店要先开门才能买包)。安装很简单,只需要在终端中输入:npm install webpack
;然后,创建一个配置文件(就像是奢侈品店的导览图,告诉你每样包包在哪里)。配置文件一般命名为webpack.config.js
,内容如下:module.exports = entry:\'./src/index.js\',output: filename:\'bundle.js\',path: __dirname +\'/dist\';
。接下来,只需要在终端中输入打包命令:npx webpack
;最后,引用打包后的文件bundle.js
就可以了(背起新包包,开启一场冒险之旅)。
六、PWA和Web性能优化
在这个快节奏的数字化时代,越来越多的用户转向使用移动设备和Web应用程序。
PWA成为了一个重要的技术趋势,它的全称是“Progressive Web App”,翻译成中文就是“渐进式Web应用程序”。简单来说,PWA是一个既可以在浏览器上运行的Web应用程序,同时也可以像原生应用一样在离线时使用。它的最大优点就是可靠性,因为PWA可以像原生应用一样缓存数据和资源,这意味着它可以在离线时运行,而不会像普通的Web应用程序一样无法使用。
此外,Web性能优化也成为了开发者关注的重点。我们需要知道一个简单的事实,那就是用户喜欢快速的网站。如果你的网站速度太慢,那就会让你的用户感觉像一头正在沙漠里跑步的骆驼一样疲惫不堪,感到痛苦和沮丧,这会让他们不得不离开,去寻找新的绿洲。
所以,为了确保你的网站速度足够快,你需要采取一些优化措施。以下是一些可以提高Web应用性能的技巧:
-
使用CDN(内容分发网络):CDN是一组分布在世界各地的服务器,它们可以缓存你的网站内容,并将其分发到全球各地的用户。这可以大大加快你的网站加载速度,因为用户可以从离他们最近的服务器获取内容。
-
压缩文件大小:压缩你的HTML、CSS和JavaScript文件可以减少它们的大小,从而加快它们的加载速度。你可以使用像Gzip这样的压缩算法来实现这一点。
-
使用缓存:缓存是一种将网站数据存储的技术。例如浏览器缓存:在响应头中设置缓存策略来控制缓存时间;以及服务器端缓存:使用Memcached或Redis等缓存服务器,以减少响应时间。这样一来,当用户再次访问你的网站时,它们可以从缓存中加载数据,而不必重新下载,大大加快你的网站加载速度。
-
减少HTTP请求:有一个叫做“夹心饼干法则”的说法。这个法则认为,在一次HTTP请求中,中间的响应部分应该像夹心饼干一样短,而请求和响应头和尾应该像饼干的两端一样长。这听起来很有趣,但其实它也是有道理的,因为请求和响应头和尾中包含的信息比较少,而响应中间部分则包含了网页的实际内容,因此应该尽可能地减少其大小。你可以通过将HTML和CSS以及JavaScript文件合并成一个文件,或者通过使用CSS Sprites将多个图像合并成一个文件来减少HTTP请求的数量。
-
使用响应式图片:图片是网站加载速度最慢的资源之一。为了提高网站加载速度,你可以使用响应式图片,这些图片可以根据用户的设备屏幕大小来动态地调整大小。这样一来,用户只会下载他们所需的图像大小,而不是下载整个大图像。
-
使用懒加载技术:懒加载是一种延迟加载技术,它可以延迟加载页面上的图像、视频和其他资源,直到它们真正需要时才出现。这可以减少页面的初始加载时间,因为只有当用户滚动到需要加载的部分时,它们才会被加载。
你知道吗,Google Chrome浏览器可以使用一个名为“Lighthouse”的工具来检查网站的PWA和性能方面的指标。但你可能不知道的是,这个工具还有一个有趣的功能,就是可以为你的网站生成一份“独家报告”,这样你就可以像读报纸一样轻松地查看网站的PWA和性能状况了。但是,要牢记的是,优化Web应用性能是一个不断发展的过程,需要持续监测和调整以确保最佳体验。
七、Web组件和跨平台框架
Web组件和跨平台框架是现代Web开发中的两个热门话题,它们就像是现代Web开发的两座巨大城堡,吸引着无数开发者前来探索和征服。
首先,我们来谈谈Web组件。Web组件是一种现代的Web开发技术,它允许开发者将Web应用程序分解成可重用的组件,这些组件可以在不同的Web应用程序中共享和重用。比如,你可以将一个搜索框组件用于多个Web页面,而不必每次都重新编写。
Web组件的好处不仅在于可重用性,还在于它们的灵活性。你可以根据需要自定义Web组件,为你的Web应用程序添加新的功能和样式。
但是,Web组件并不是“银弹”,它们在某些方面仍然有限制。比如,Web组件难以处理动态数据,因为它们是静态的。此外,Web组件也不是完美的跨平台解决方案,因为它们可能无法兼容不同的Web浏览器和设备。
这就引出了我们的下一个话题:跨平台框架。跨平台框架是一种可以在多个平台上运行的软件框架,包括Web、移动和桌面应用程序。它们允许开发者编写一次代码,然后在不同的平台上运行,无需进行任何额外的修改。
跨平台框架的好处显而易见:它们可以大大减少开发时间和开发成本。但是,跨平台框架并非完美无缺。它们可能会受到不同平台的限制,从而无法充分利用每个平台的功能和性能。此外,跨平台框架还可能会导致性能问题和代码质量问题。
现在,我们来看看如何将这两种技术结合起来。使用Web组件和跨平台框架可以让你搭建你的虚拟王国,充分利用Web组件的可重用性和灵活性,同时充分利用跨平台框架的跨平台能力和效率。
当然,这并不是说将Web组件和跨平台框架混合在一起就是万无一失的。你需要仔细考虑你的应用场景,确保使用这两种技术的方式是最优的。
比如,你可以使用Web组件来构建你的用户界面,然后使用跨平台框架来将Web应用程序转换为移动应用程序。这样,你就可以在多个平台上运行相同的代码,而且用户体验也会更加一致。
或者,你可以使用跨平台框架来编写你的应用程序逻辑,然后使用Web组件来定制你的用户界面。这样,你可以在不同的Web应用程序中重用你的用户界面,而且你的应用程序逻辑也可以在多个平台上运行。
再者,你也可以将这两种技术都使用在同一个应用程序中。这样,你可以充分利用Web组件的可重用性和灵活性,同时充分利用跨平台框架的跨平台能力和效率。只要你能合理地使用这些技术,就可以打造出更好的Web应用程序。
Web组件和跨平台框架都是非常有前途的技术,它们可以为现代Web开发带来很多好处,为我们带来更加灵活、高效和强大的Web开发工具和平台。无论是Web组件还是跨平台框架,它们都是我们构建虚拟王国的重要基石。
八、前端安全问题
在当今数字化时代,前端安全已成为互联网世界中的重要一环。不管是个人用户,还是企业机构,前端安全都需要被高度重视。尽管我们已经发展出了各种各样的安全技术和防御手段,但是前端安全问题仍然是一个不断增长的挑战。
8.1 XSS攻击:你的网站很容易被攻击
你听说过XSS攻击吗?这种攻击方式是通过篡改网页的HTML并在用户浏览器中注入恶意代码的一种攻击方式。这些恶意代码通常是JavaScript脚本,它们可以被用来窃取用户的敏感信息,如用户名、密码、银行账户信息等等。
如果你的网站存在XSS漏洞,那么恶意攻击者就可以在你的网站上注入一些不良代码,这些代码可能会窃取用户的登录凭证或者其他敏感信息。所以,尽管你的网站已经被SSL加密保护,你的用户仍然面临着被XSS攻击的风险。
如何防御XSS攻击呢?其实非常简单,你只需要在所有的输入框中过滤掉所有的HTML标签和JavaScript脚本即可。但是,如果你认为这么做会影响用户体验,那么你可以考虑使用HTML的特殊字符转义功能来替换这些标签和脚本。
8.2 CSRF攻击:请勿相信恶意链接
现在让我们来谈谈CSRF攻击。这种攻击方式是通过篡改用户的HTTP请求来伪造用户的身份,从而进行一些非法的操作。这种攻击方式通常是通过欺骗用户点击一个恶意链接来实现的。一旦用户点击了这个链接,攻击者就可以获得用户的凭证,然后模拟用户的请求,从而执行一些非法的操作。
假设,你的网站有一个删除账户的功能,攻击者就可以利用CSRF攻击来让用户误删除自己的账户。这听起来非常可怕,但是不要担心,我们可以通过一些简单的方法来防御这种攻击。
首先,我们可以在所有的表单提交中添加一个随机的Token值。这个Token值可以通过后台生成,然后在前端将其嵌入到表单中。当用户提交表单时,我们可以检查这个Token值是否匹配,如果不匹配,则拒绝这个请求。这样就可以简单的避免CSRF攻击了。
8.3 CSP策略:请勿允许不信任的资源
CSP策略是一种非常有用的前端安全措施。CSP策略可以帮助我们限制网页中可加载的资源,从而减少被攻击的风险。例如,我们可以限制只允许加载来自指定域名的JavaScript文件,这样就可以避免恶意代码的注入。
但是,如果你不小心将不信任的资源允许加载到你的网页中,那么你的网站就可能面临被攻击的风险。假设你的网站允许用户上传文件,并在网页中显示这些文件,如果你没有限制文件的类型和内容,那么攻击者就可能上传恶意文件,并在用户浏览器中注入恶意代码。
所以,如果你想保证你的网站的安全性,那么你应该始终谨慎地过滤用户上传的文件,只允许加载来自可信任来源的资源。
我们可以认识到,前端安全是一项非常重要的技术挑战。如果你是一位前端开发人员,那么应该始终将前端安全作为开发过程中的一个重要考虑因素。只有这样,我们才能够为用户提供安全可靠的Web服务。
九、前端工程师的多元化技能
作为一名前端工程师,一定是个充满多元化技能的大神。不仅仅要会写代码,还要会与设计师沟通,管理版本控制,解决兼容性,甚至还要有点艺术细胞。
-
代码技能:前端工程师最基本的技能,也是最重要的技能。不仅需要掌握 HTML、CSS、JavaScript,还需要掌握一些前端框架和库,比如 React、Vue、Angular 等。当然,这些都不是问题,对于一名优秀的前端工程师来说,这只是小菜一碟。
-
与设计师沟通:设计师们总是有各种奇怪的想法,然后她们会告诉你:“我要实现这个效果,你帮我写一下”。但是,很快会发现这个效果并不现实,于是你需要与设计师进行沟通,告诉她们这个效果无法实现。当然,你不能用技术术语来向她们解释,否则她们会摆出一副“我听不懂”的表情。所以,你需要用她们喜欢听的语言,比如“我理解你的设计需求,并深刻认识到其对于网站效果的重要性。不过,由于技术和浏览器的限制,我们需要寻找其他的可行方案来实现类似的效果,以保证网站的性能和可访问性,我会尽最大的努力提供最佳的解决方案。”
-
管理版本控制:代码管理是一个很重要的问题,特别是当你和其他人合作的时候。你需要使用Git进行版本控制,这样才能确保代码的稳定性和可靠性。当然,你也需要了解一些Git的命令,比如 commit、push、pull 等等。不过,如果你不小心把代码弄挂了,那也不用担心,只要跟团队的其他成员说“我不小心把代码弄挂了”,他们就会告诉你怎么做了。
-
解决兼容性:不同的浏览器之间有很多不兼容,而前端工程师需要解决这些问题。比如,IE浏览器总是出现各种奇怪的bug,但是你不能告诉用户:“你用的是IE,这不是我的问题”。相反,你需要找到问题的根源,然后解决它。这是一个非常重要的技能,因为它涉及到用户体验和网站的稳定性。
-
有点艺术细胞:前端工程师不仅仅是一个代码的机器,还需要有一点艺术细胞。毕竟,好的界面设计是网站的关键之一。所以需要了解一些基本的设计原则,比如颜色搭配、排版等等。当然并不需要成为一个设计师,但是需要知道如何运用这些原则来改进网站的设计。
-
学习新技能:前端工程师是一个不断学习的过程。每天都有新的技术和框架出现,并且要不断学习并掌握这些技能。但是,并不需要成为一个全栈工程师,只要掌握所需要的技能,然后专注于自己的领域即可。当然,这也意味着要学会如何筛选有用的信息,因为不可能学习完所有的技术和框架。
-
解决问题:前端工程师是一个解决问题的岗位。当网站出现问题时,需要迅速找到问题的根源,并解决它。但是,也不一定要独自解决所有的问题,可以向同事寻求帮助,或者参加一些开发者社区来寻找解决方案。最终要记住的是,解决问题是前端工程师最重要的技能之一。
-
与团队合作:前端工程师需要和设计师、后端工程师、测试人员等等进行合作,以确保网站的成功。在与团队合作中,要学会如何与不同的人合作,并且尽力避免出现冲突。
前端工程师需要掌握很多不同的技能,但这并不意味着要成为一个万能的人。相反,只需要专注于自己的领域在不断地技术学习过程中成长。
十、AI与前端技术结合
回顾过去,畅想未来,立足当下,来讲个故事吧。
在一个遥远的星球上,有一个叫做前端技术的孤独王国。这个王国的居民们都是非常优秀的程序员,他们用HTML、CSS和JavaScript这三种神奇的武器来构建网站,为用户带来无尽的愉悦。然而,这个王国有一个问题,那就是他们一直无法征服一个名为AI的神秘国度。
终于有一天,一个勇敢的前端战士——HTML骑士,决定向AI国度发起挑战。他带着两个小伙伴:CSS猎人和JavaScript法师,踏上了一段充满挑战的探险之旅。
他们沿着神秘的网络海洋航行,一路上遇到了各种令人捧腹大笑的趣事。先是在一个叫做布局的洲际,他们被一群叫做“浮动”的怪兽困扰,CSS猎人拔出了他的弹性盒子弓箭,一箭穿心,解决了怪兽。接下来,他们来到了一个充满奇特生物的动画之地,JavaScript法师用他的神奇魔法,让这些生物如同表演马戏团一般,给他们带来了一场场精彩绝伦的表演。
然后,他们终于来到了AI国度的边境。在那里,他们遇到了一个脾气古怪的巨人,他叫做机器学习。这个巨人用一种叫做数学的强大力量来支配着这片土地。HTML骑士认为,要征服这个国度,就必须挑战巨人,并将他的力量与前端技术融合。
于是,在他们与巨人大战三百回合后,JavaScript法师从中意外领悟了神奇魔法,召唤出一个叫做TensorFlow.js的强大法宝。这个法宝让前端技术也能够掌握机器学习的力量。HTML骑士和CSS猎人纷纷表示赞叹,他们觉得自己终于找到了一种将AI与前端技术结合的方法。
在这之后,他们三人一起用TensorFlow.js建立了一个名为“智能前端”的新城堡。这座城堡里,前端技术与AI融合得天衣无缝,为用户带来前所未有的体验。
城堡的大门上,HTML骑士精心设计了一个智能问答系统。这个系统可以回答用户关于前端技术的各种问题,让新手程序员们感叹不已。而这一切,都得益于TensorFlow.js和机器学习的神奇力量。
城堡的内部,CSS猎人则利用AI技术打造了一套全新的自适应布局。这套布局能够根据用户的喜好和设备自动调整,让每个访问者都能享受到最佳的浏览体验。他还研发了一种名为“智能配色”的神奇法术,能够根据用户的喜好自动调整网页的颜色搭配,让网站变得更加美观大方。
而在城堡的核心区域,JavaScript法师则运用AI技术开发了一系列令人惊叹的交互功能。比如,他创造了一种可以根据用户行为自动优化的推荐算法,将用户感兴趣的内容精准推送。此外,他还设计了一款智能聊天机器人,可以与用户进行即时互动,解答他们的疑问。
在“智能前端”城堡的建设过程中,他们三人不仅发挥出了各自的特长,还不断地学习AI技术,将其与前端技术相互融合。这让他们的作品充满了趣味与智慧,吸引了无数程序员和用户前来参观。
在这段充满挑战的探险之旅中,HTML骑士、CSS猎人和JavaScript法师用他们的智慧和勇气,成功地将AI技术引入前端领域。他们的故事传遍了整个网络世界,成为了程序员们争相传颂的佳话。
如今,前端技术和AI的结合已经成为了一种趋势,越来越多的开发者开始探索这个领域的无限可能。而在这个探索过程中,他们总是能从HTML骑士、CSS猎人和JavaScript法师的故事中汲取勇气与智慧,勇往直前,为未来的网络世界描绘出一个更加美好、充满创意和智慧的蓝图。
有人说,前端技术与AI的结合就像一场狂欢。程序员们欢笑着跳动,发挥着自己的想象力,创造出一个又一个令人叹为观止的作品。在这场狂欢中,每个人都是舞者,每个人都是艺术家,每个人都在为这个美丽的网络世界贡献着自己的力量。
如同那个遥远的星球上,那个欢呼雀跃的前端王国,如今我们所生活的这个网络世界也充满了欢声笑语。而在这片欢乐的土地上,那些勇敢的前端战士们正携手AI,共同书写着属于他们的传奇!
随着技术的不断发展,我们相信前端技术与AI的结合将会走得更远,创造出更多不可思议的奇迹。也许有一天,我们的网络世界将变得如同童话般美好,充满智慧的光辉。而在那个时候,我们将不禁想起那个勇敢的HTML骑士、CSS猎人和JavaScript法师,怀念他们当年那段充满挑战的探险之旅,为他们的勇气与智慧而感慨不已。
在探险的道路上,我们将一路欢笑并肩前行,勇敢地追求那个梦寐以求的未来。也许在某个不经意的瞬间,我们会发现,那个童话般的前端王国,其实就在我们心中,等待着我们去探索、去发现、去唤醒它,让它绽放出最耀眼的光芒。
后记
前端技术的演进从未停歇,仍然充满了机遇与挑战……
让我们一起期待下一个十年,见证前端技术的更多精彩!
知乎高赞:前端模块化的十年征程
作者:@外婆的https://zhuanlan.zhihu.com/p/265632724
前言
夫以铜为镜,可以正衣冠;以史为镜,可以知兴替 ——《旧唐书·魏徵传》
也许在谈论具体的内容之前,我们需要谈论一下关键词的定义。 什么是"模块"?在不同的语境下模块有不同的含义。
但在本文中,我们从广义的角度出发,将它解释为两个方面
外部的模块: 指代引入前端工程的某个外部的包(package),可能由多个JS文件组成,但会通过入口暴露给我们项目调用
内部的模块: 指代我们自己的工程项目中编码的最小单元: 即单个的JS文件
模块化已经发展了有十余年了,不同的工具和轮子层出不穷,但总结起来,它们解决的问题主要有三个
外部模块的管理
内部模块的组织
模块源码到目标代码的编译和转换
时间线
下面是最各大工具或框架的诞生时间,不知不觉,模块化的发展已有十年之久了。
生态 诞生时间
Node.js 2009年
NPM 2010年
requireJS(AMD) 2010年
seaJS(CMD) 2011年
broswerify 2011年
webpack 2012年
grunt 2012年
gulp 2013年
react 2013年
vue 2014年
angular 2016年
redux 2015年
vite 2020年
snowpack 2020年
外部模块的管理
在模块化的过程中,首先要解决的就是外部模块的管理问题。
Node.js和NPM的发布
时间倒回到2009年,一个叫莱恩(Ryan Dahl)的精神小伙创立了一个能够运行JavaScript的服务器环境——Node.js,并在一年之后,发布了Node.js自带的模块管理工具npm,npm的全称是node package manager,也就是Node包管理器。
Node的出现给JavaScript的带来了许多改变:
一方面, Node使JavaScript不局限于前端,同时还成为了一门后端语言。更重要的是: 经过10年的发展,Node.js已经完全融入到了前端开发流程中。我们用它创建静态资源服务器,实现热重载和跨域代理等功能,同时还用它源代码中的特殊写法做编译转换处理(JSX/Sass/TypeScript),将代码翻译成浏览器可以理解的格式(ES5/CSS)。到今天,即使我们不用Node.js独立开发程序后台,它作为开发工具的重要性也不会改变
另一方面, Node.js自带的JS模块管理工具npm,从根本上改变了前端使用外部模块的方式,如果要打个比方的话,就好比从原始社会进入了现代社会
NPM时代以前的外部模块使用方式
在一开始没有npm的时候,如果我们需要在项目里使用某个外部模块,我们可能会去官网直接把文件下载下来放到项目中,同时在入口html中通过script标签引用它。
每引用一个外部模块,我们都要重复这个过程
需要用到jQuery,去 jQuery 官网下载 jQuery库,导入到项目中
需要用到lodash,去lodash官网下载lodash库
需要用到某个BootStrap,去BootStrap官网官网下载BootStrap库,导入到项目中
...
除了这些全局的UI库或工具库,我们可能还会使用到很多实现细节功能的辅助模块,如果都按这种方式使用未免过于粗暴,而且给我们带来许多麻烦
使用上缺乏便利性
难以跟踪各个外部模块的来源
没有统一的版本管理机制
而npm的出现改变了这种情况
NPM时代以后外部模块的使用方式
我们上面说过,NPM在2010年伴随着Node.js的新版本一起发布,是一个Node自带的模块管理工具。
从概念上看它由以下两个部分组成
NPM是一个远程的JavaScript代码仓库,所有的开发者都可以向这里提交可共享的模块,并给其他开发者下载和使用
NPM还包含一个命令行工具,开发者通过运行npm publish命令把自己写的模块发布到NPM仓库上去,通过运行npm install [模块名],可以将别人的模块下载到自己项目根目录中一个叫node_modules的子目录下
每次npm install的时候,都会在package.json这个文件中更新模块和对应的版本信息。
// package.json
{
...
"dependencies": {
"bootstrap": "^4.5.2",
"jquery": "^3.5.1"
}
}
于是乎,包括jQuery等知名模块开发者的前端工程师们,都通过npm publish的方式把自己的模块发布到NPM上去了。前端开发者们真正有了一个属于自己的社区和平台,如万千漂泊游船归于港湾,而NPM也名声渐噪
早在2019年6月,NPM平台上的模块数量就超过了100万,而到写下这篇文章的时候,NPM模块数量已超过了140万
NPM的出现实际上是一个必然,前端工程的复杂化要求我们必须要有这么一个集中的JS库管理平台。但为什么它会是NPM呢?这和后来Node.js的火热有很大关系,因为NPM是Node.js内置的包管理器,所以跟随着Node得到了开发者的追捧。
综上所述,NPM解决了外部模块的管理问题。
内部模块的组织
在模块化的过程中,还需要解决的是内部模块的组织问题。
模块化第一阶段:原生JS组织阶段
在最原始的时代,我们是通过下面这种方式组织我们的模块代码的,将不同的JS文件在html中一一引入。每个文件代表一个模块
// index.html
<script src="./a.js"></script>
<script src="./b.js"></script>
<script src="./c.js"></script>
<script src="./d.js"></script>
并通过模块模式去组织代码:如下所示,我们通过一个“立即调用的函数表达式”(IIFE)去组织模块
将每个模块包裹在一个函数作用域里面执行,这样就可以最大程度地避免污染全局执行环境
通过执行匿名函数得到模块输出,可以暴露给下面的其他模块使用
<script>
var module1 = (function(){
var x = 1
return { a: x };
})();
</script>
<script>
var module2 = (function(){
var a = module1.a;
return { b: a };
})();
</script>
但这种使用方式仍然比较粗暴
随着项目扩大,html文件中会包含大量script标签。
script标签的先后顺序并不能很好地契合模块间的依赖关系。在复杂应用中,模块的依赖关系通常树状或网状的,如a.js依赖于b.js和c.js,b.js依赖于b1.js和b2.js。相对复杂的依赖关系难以用script标签的先后顺序组织。
让代码的逻辑关系难以理解,也不便于维护,容易出现某个脚本加载时依赖的变量尚未加载而导致的错误。
因为对script标签顺序的要求而使用同步加载,但这却容易导致加载时页面卡死的问题
仍然会因为全局变量污染全局环境,导致命名冲突
我们需要针对这些问题提出解决方案,而AMD和CMD就是为解决这些问题而提出的规范
模块化的第二阶段:在线处理阶段
模块化规范的野蛮生长
10多年以前,前端模块化刚刚开始,正处在野蛮生长的阶段。这个过程中诞生了诸多模块化规范: AMD/CMD/CommonJS/ES6 Module。没错,前端并没有一开始就形成统一的模块化规范,而是多个规范同时多向发展。直到某一类规范占据社区主流之时,模块化规范野蛮生长的过程才宣告结束。
首先开始在前端流行的模块化规范是AMD/CMD, 以及实践这两种规范的require.js和Sea.js, AMD和CMD可看作是"在线处理"模块的方案,也就是等到用户浏览web页面下载了对应的require.js和sea.js文件之后,才开始进行模块依赖分析,确定加载顺序和执行顺序。模块组织过程在线上进行。
AMD && CMD
AMD和CMD只是一种设计规范,而不是一种实现。
AMD
我们先来说下AMD,它的全称是Asynchronous Module Definition,即“异步模块定义”。它是一种组织前端模块的方式
AMD的理念可以用如下两个API概括: define和require
define方法用于定义一个模块,它接收两个参数:
第一个参数是一个数组,表示这个模块所依赖的其他模块
第二个参数是一个方法,这个方法通过入参的方式将所依赖模块的输出依次取出,并在方法内使用,同时将返回值传递给依赖它的其他模块使用。
// module0.js
define([ Module1 , Module2 ], function (module1, module2) {
var result1 = module1.exec();
var result2 = module2.exec();
return {
result1: result1,
result2: result2
}
});
require用于真正执行模块,通常AMD框架会以require方法作为入口,进行依赖关系分析并依次有序地进行加载
// 入口文件
require([ math ], function (math) {
math.sqrt(15)
});
define && require的区别
可以看到define和require在依赖模块声明和接收方面是一样的,它们的区别在于define能自定义模块而require不能,require的作用是执行模块加载。
通过AMD规范组织后的JS文件看起来像下面这样
depModule.js
define(function () {
return printSth: function () {
alert("some thing")
}
});
app.js
define([ depModule ], function (mod) {
mod.printSth();
});
index.html
// amd.js意为某个实现了AMD规范的库
<script src="...amd.js"></script>
<script>
require([ app ], function (app) {
// ...入口文件
})
</script>
我们可以看到,AMD规范去除了纯粹用script标签顺序组织模块带来的问题
通过依赖数组的方式声明依赖关系,具体依赖加载交给具体的AMD框架处理
避免声明全局变量带来的环境污染和变量冲突问题
正如AMD其名所言(Asynchronous), 模块是异步加载的,防止JS加载阻塞页面渲染
遵循AMD规范实现的模块加载器
我们前面说过,AMD只是一个倡议的规范,那么它有哪些实现呢?
根据史料记载,AMD的实现主要有两个: requireJS和curl.js, 其中requireJS在2010年推出,是AMD的主流框架
CMD
CMD是除AMD以外的另外一种模块组织规范。CMD即Common Module Definition,意为“通用模块定义”。
和AMD不同的是,CMD没有提供前置的依赖数组,而是接收一个factory函数,这个factory函数包括3个参数
require: 一个方法标识符,调用它可以动态的获取一个依赖模块的输出
exports: 一个对象,用于对其他模块提供输出接口,例如:exports.name = "xxx"
module: 一个对象,存储了当前模块相关的一些属性和方法,其中module.exports属性等同于上面的exports
如下所示
// CMD
define(function (requie, exports, module) {
//依赖就近书写
var module1 = require( Module1 );
var result1 = module1.exec();
module.exports = {
result1: result1,
}
});
// AMD
define([ Module1 ], function (module1) {
var result1 = module1.exec();
return {
result1: result1,
}
});
CMD && AMD的区别
从上面的代码比较中我们可以得出AMD规范和CMD规范的区别
一方面,在依赖的处理上
AMD推崇依赖前置,即通过依赖数组的方式提前声明当前模块的依赖
CMD推崇依赖就近,在编程需要用到的时候通过调用require方法动态引入
另一方面,在本模块的对外输出上
AMD推崇通过返回值的方式对外输出
CMD推崇通过给module.exports赋值的方式对外输出
遵循CMD规范实现的模块加载器
sea.js是遵循CMD规范实现的模块加载器,又或者更准确的说法是: CMD正是在sea.js推广的过程中逐步确立的规范,并不是CMD诞生了sea.js。相反,是sea.js诞生了CMD
CMD和AMD并不是互斥的,require.js和sea.js也并不是完全不同,实际上,通过阅读API文档我们会发现,CMD后期规范容纳了AMD的一些写法。
AMD && CMD背后的实现原理
下面以sea.js为例
解析define方法内的require调用
我们之前说过,sea.js属于CMD, 所以它的依赖是就近获取的,
所以sea.js会多做一项工作:也就是对define接收方法体内require调用的解析。
先定义parseDependencies方法: 通过正则匹配获取字符串中的require中的参数并存储到数组中返回
var REQUIRE_RE = /"(?:\\"|[^"])*"| (?:\\ |[^ ])* |/*[Ss]*?*/|/(?:\\/|[^/
])+/(?=[^/])|//.*|.s*require|(?:^|[^$])requires*(s*([" ])(.+?)s*)/g
var SLASH_RE = /\\\\/g
function parseDependencies(code) {
var ret = []
code.replace(SLASH_RE, "")
.replace(REQUIRE_RE, function(m, m1, m2) {
if (m2) {
ret.push(m2)
}
})
return ret
}
然后通过toString将define接收的方法转化为字符串,然后调用parseDependencies解析。这样我们就获取到了一个define方法里面所有的依赖模块的数组
// Parse dependencies according to the module factory code
if (!isArray(deps) && isFunction(factory)) {
deps = parseDependencies(factory.toString())
}
然后Sea.js执行的时候,会从入口开始遍历依赖模块,并依次将它们加载到浏览器中,加载方法如下所示。
function request(url, callback, charset, crossorigin) {
var node = doc.createElement("script")
addOnload(node, callback, url) // 添加回调,回调函数在 3 中
node.async = true //异步
node.src = url
head.appendChild(node)
}
而且在每个依赖加载完后都会通过回调的方式调用3中的onload方法
在onload方法中,sea.js会设置一个计数变量remain,用来计算依赖是否加载完毕。每加载完一个模块就执行remain - 1操作,并通过remain === 0 判断依赖是否全部加载完毕。
如果全部加载完毕就执行4中的mod.callback方法
Module.prototype.onload = function() {
var mod = this
mod.status = STATUS.LOADED
for (var i = 0, len = (mod._entry || []).length; i < len; i++) {
var entry = mod._entry[i]
if (--entry.remain === 0) {
entry.callback()
}
}
delete mod._entry
}
大概因为require.js出来比较早的原因,所以没有用Promise.all一类的API
当判断entry.remain === 0时,也即依赖模块全部加载完毕时,会调用一开始callback方法,去依次执行加载完毕的依赖模块,并将输出传递给use方法回调
// sea.js的use方法类似于AMD规范中的require方法,用于执行入口函数
Module.use = function (ids, callback, uri) {
var mod = Module.get(uri, isArray(ids) ? ids : [ids])
mod.callback = function() {
var exports = []
var uris = mod.resolve();
// 依次执行加载完毕的依赖模块,并将输出传递给use方法回调
for (var i = 0, len = uris.length; i < len; i++) {
exports[i] = cachedMods[uris[i]].exec()
}
// 执行use方法回调
if (callback) {
callback.apply(global, exports)
}
}
}
参考资料: https://segmentfault.com/a/1190000016001572
ES6的模块化风格
关于AMD/CMD的介绍到此为止,后面的事情我们都知道了,伴随着babel等编译工具和webpack等自动化工具的出现,AMD/CMD逐渐湮没在历史的浪潮当中,然后大家都习惯于用CommonJS和ES6的模块化方式编写代码了。
这一切是怎么发生的呢? 请看
CommonJS && ES6
CommonJS是Node.js使用的模块化方式,而import/export则是ES6提出的模块化规范。它们的语法规则如下。
// ES6
import { foo } from ./foo ; // 输入
export const bar = 1; // 输出
// CommonJS
const foo = require( ./foo ); // 输入
module.exports = { 。 // 输出
bar:1
}
实际上我们能感觉到,这种模块化方式用起来比CMD/AMD方便。
但在最开始的时候,我们却不能在前端页面中使用它们,因为浏览器并不能理解这种语法。
但后来,编译工具babel的出现让这变成了可能
babel的出现和ES6模块化的推广
在2014年十月,babel1.7发布。babel是一个JavaScript 编译器,它让我们能够使用符合开发需求的编程风格去编写代码,然后通过babel的编译转化成对浏览器兼容良好的JavaScript。
Bablel的出现改变了我们的前端开发观点。它让我们意识到:对前端项目来说,开发的代码和生产的前端代码可以是不一样的,也应该是不一样的。
在开发的时候,我们追求的是编程的便捷性和可阅读性。
而在生产中,我们追求的是代码对各种浏览器的兼容性。
babel编译器让我们能做到这一点。在babel出现之前的AMD/CMD时代,开发和生产的代码并没有明显的区分性,开发是怎样的生产出来后也就是怎样的。
而babel则将开发和生产这两个流程分开了,同时让我们可以用ES6中的import/export进行模块化开发。
至此,AMD/CMD的时代宣告结束,ES6编程的时代到来
Babel的工作原理
Babel的工作流程可概括为三个阶段
Parse(解析): 通过词法分析和语法分析,将源代码解析成抽象语法树(AST)
Transform(转换):对解析出来的抽象语法树做中间转换处理
Generate(生成):用经过转换后的抽象语法树生成新的代码
模块化的第三阶段:预处理阶段
现在时间来到了2013年左右,AMD/CMD的浪潮已经逐渐退去,模块化的新阶段——预编译阶段开始了。
一开始的CMD/AMD方案,可看作是“在线编译”模块的方案,也就是等到用户浏览web页面下载了js文件之后,才开始进行模块依赖分析,确定加载顺序和执行顺序。但这样却不可避免的带来了一些问题
在线组织模块的方式会延长前端页面的加载时间,影响用户体验。
加载过程中发出了海量的http请求,降低了页面性能。
于是开发者们想了对应的方法去解决这些问题:
开发一个工具,让它把组织模块的工作提前做好,在代码部署上线前就完成,从而节约页面加载时间
使用工具进行代码合并,把多个script的代码合并到少数几个script里,减少http请求的数量。
在这样的背景下,一系列模块预处理的工具如雨后春笋般出现了。
典型的代表是2011年出现的broswerify 和2012年发明的webpack。
它们一开始的定位是类似的,都是通过预先打包的方式,把前端项目里面的多个文件打包成单个文件或少数几个文件,这样的话就可以压缩首次页面访问时的http请求数量,从而提高性能。
当然后面的事情我们都知道了,webpack因为发展得更好而占据了主流的前端社区,而broswerify则渐渐消失在红尘之中。
broswerify
以broswerify为例,我们可以通过npm安装它
npm install -g browserify
broswerify允许我们通过CommonJS的规范编写代码,例如下面的入口文件main.js
// main.js
var a = require( ./a.js );
var b = require( ./b.js );
...
然后我们可以用broswerify携带的命令行工具处理main.js,它会自动分析依赖关系并进行打包,打包后会生成集合文件bundle.js。
browserify main.js -o bundle.js
webpack
webpack是自broswerify出现一年以后,后来居上并占据主流的打包工具。webpack内部使用babel进行解析,所以ES6和CommonJS等模块化方式是可以在webpack中自由使用的。
通过安装webpack这一npm模块便可使用webpack工具
npm install --save-dev webpack
它要求我们编写一份名为webpack.config.js的配置文件,并以entry字段和output字段分别表示打包的入口和输出路径
// webpack.config.js
const path = require( path );
module.exports = {
entry: ./src/index.js ,
output: {
path: path.resolve(__dirname, dist ),
filename: bundle.js
}
};
打包完毕后,我们的index.html只需要加载bundle.js就可以了。
<!doctype html>
<html>
<head>
...
</head>
<body>
...
<script src="dist/bundle.js"></script>
</body>
</html>
打包工具面临的问题 && 解决方案
代码打包当然不是一本万利的,它们也面临着一些副作用带来的问题,其中最主要的就是打包后代码体积过大的问题
代码打包的初衷是减少类似CMD框架造成的加载脚本(http请求)数量过多的问题,但也带来了打包后单个script脚本体积过大的问题:如此一来,首屏加载会消耗很长时间并拖慢速度,可谓是物极必反。
webpack于是引入了代码拆分的功能(Code Splitting)来解决这个问题, 从全部打包后退一步:可以打包成多个包
虽然允许拆多个包了,但包的总数仍然比较少,比CMD等方案加载的包少很多
Code Splitting有可分为两个方面的作用:
一是实现第三方库和业务代码的分离:业务代码更新频率快,而第三方库代码更新频率是比较慢的。分离之后可利用浏览器缓存机制加载第三方库,从而加快页面访问速度
二是实现按需加载: 例如我们经常通过前端路由分割不同页面,除了首页外的很多页面(路由)可能访问频率较低,我们可将其从首次加载的资源中去掉,而等到相应的触发时刻再去加载它们。这样就减少了首屏资源的体积,提高了页面加载速度。
A.实现第三方库和业务代码的分离
这种代码拆分可通过webpack独特的插件机制完成。plugins字段是是一个数组,可接收不同的plugins实例,从而给webpack打包程序附加不同的功能,CommonsChunkPlugin就是一个实现代码拆分的插件。
// webpack.config.js
module.exports = {
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: commons , // the commons chunk name
filename: commons.js , // the filename of the commons chunk)
minChunks: 3, // Modules must be shared between 3 entries
});
]
};
通过上面的配置,webpack在执行打包的时候会把被引用超过3次的依赖文件视为"公共文件",并单独打包到commons.js中,而不是打包到主入口文件里。
对于React,Redux,lodash这些第三方库,因为引用次数远远超过3次,当然也是会被打包到common.js中去的。
B.实现按需加载
正如其字面意思,按需加载就是等到需要的时候才加载一部分模块。并不选择将其代码打包到首次加载的入口bundle中,而是等待触发的时机,届时才通过动态脚本插入的方式进行加载: 即创建script元素,添加脚本链接并通过appendChild加入到html元素中
例如我们需要实现一个功能,在点击某个按钮的时候,使用某个模块的功能。这时我们可以使用ES6的import语句动态导入,webpack会支持import的功能并实现按需加载
button.addEventListener( click ,function(){
import( ./a.js ).then(data => {
// use data
})
});
模块化的第四阶段:自动化构建阶段
正当打包工具方兴未艾的时候,另外一个发展浪潮也几乎在同步发生着。
它就是 —— 全方位的自动化构建工具的发展。
什么叫自动化构建工具呢? 简单的说就是: 我们需要这样一个工具,专门为开发过程服务,尽可能满足我们开发的需求,提高开发的效率。
前面说过,在模块化的过程中,我们渐渐有了“开发流程”和“生产流程”的区分,而自动化构建工具就是在开发流程中给开发者最大的自由度和便捷性,同时在生产流程中能保证浏览器兼容性和良好性能的工具。而所有的功能已经由插件直接提供,所以被称作“自动化” 构建工具。
在这时,我们已经不再满足于“打包”这个功能了,我们渴望做更多的事情:
开发时使用丰富且方便的JS新特性,如用ES6,typescript编程,由自动化构建工具转化成浏览器兼容的ES5格式的JS代码
用Sass,less编写阅读性和扩展性良好的样式代码,由自动化构建工具转化成浏览器兼容的CSS代码
提供开发时SourceMap功能,也即提供生产代码(如ES5)到源代码(typescript)的映射,方便开发调试
提供生产时代码压缩功能,压缩js和css,删除注释,替换变量名(长变短),减少代码加载体积
提供开发热重载功能(Hot Module Reload), 也即在编辑器保存代码的时候自动刷新浏览调试页面。
当然也还包括基本的模块打包功能
其他.....
自动化构建工具的代表性工具有三个,分别是
2012年出现的webpack
2012年出现的grunt
2013年出现的gulp
下图中,左中右分别是gulp, grunt和webpack
这一次,webpack并没有止步于成为一个单纯的打包工具,而是参与到自动化构建的浪潮里,并且成为了最后的赢家。而grunt和gulp则像过去的Sea.js,Require.js等工具一样。逐渐地从热潮中隐退,静静地待在前端社区里的一方僻静的角落里
gulp && webpack
因为篇幅关系,我们下面只来介绍下gulp和webpack这两个自动化构建工具。
gulp和webpack的区别
对于使用者来说,gulp和webpack最大的区别也许在它们的使用风格上
gulp是编程式的自动化构建工具
webpack是配置式的自动化构建工具
下面我们以less代码的编译为例,展示Gulp和webpack的区别
Gulp
Gulp基本的风格是编程式的, 它是一种基于流即Node.js 封装起来的 stream 模块的自动化构建工具,一般先通过gulp.src将匹配的文件转化成stream(流)的形式,然后通过一连串的pipe方法进行链式的加工处理处理,对后通过dest方法输出到指定路径。
// gulpfile.js
const { src, dest } = require( gulp );
const less = require( gulp-less );
const minifyCSS = require( gulp-csso );
function css() {
return src( client/templates/*.less )
.pipe(less())
.pipe(minifyCSS())
.pipe(dest( build/css ))
}
Webpack
webpack的基本风格则是配置式的,它通过loader机制实现文件的编译转化。通过配置一组loader数组,每个loader会被链式调用,处理当前文件代码后输出给下一个loader, 全部处理完毕后进行输出
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.less$/, // 正则匹配less文件
use: [
{ loader: style-loader }, // creates style nodes from JS strings
{ loader: css-loader }, // translates CSS into CommonJS
{ loader: less-loader }, // compiles Less to CSS
],
},
],
},
};
gulp和webpack的共同点
gulp和webpack并没有自己完成所有的功能,而是搭建起一个平台,吸引世界各地的开发者们贡献插件,并构建起来一个繁荣的生态。
从提供的功能上看,gulp和webpack在很多方面是类似的,这从它们的相关生态上也可以看得出来
Gulp
gulp-uglify : 压缩js文件
gulp-less : 编译less
gulp-sass:编译sass
gulp-livereload : 实时自动编译刷新
gulp-load-plugins:打包插件
Webpack
uglifyjs-webpack-plugin: 压缩js文件
less-loader: 编译less
sass-loader: 编译sass
devServer.hot配置为true: 实时自动编译刷新
....
Gulp的没落和webpack的兴起
经过了七八年的发展,webpack逐渐取代了gulp成为前端开发者的主流自动化构建工具。
究其原因
一方面,是因为gulp是编程式的,webpack是配置式的,webpack用起来更加简单方便,上手难度相对低一些,所以得到众多开发者的喜欢另一方面,从2014年React,Vue等SPA应用的热潮兴起后,webpack和它们的结合性更好,所以也助长了webpack生态的繁荣
模块化的故事,到这里就先告一段落了。
十年征程,前端模块化终于从呱呱坠地到长大成人,
自动构建工具的新趋势:bundleless
webpack之所以在诞生之初采用集中打包方式进行开发,有几个方面的原因
一是浏览器的兼容性还不够良好,还没提供对ES6的足够支持(import|export),需要把每个JS文件打包成单一bundle中的闭包的方式实现模块化
二是为了合并请求,减少HTTP/1.1下过多并发请求带来的性能问题
而发展到今天,过去的这些问题已经得到了很大的缓解,因为
主流现代浏览器已经能充分支持ES6了,import和export随心使用
HTTP2.0普及后并发请求的性能问题没有那么突出了
bundleless就是把开发中拖慢速度的打包工作给去掉,从而获得更快的开发速度。代表性工具是vite和snowpack
vite: 尤雨溪开发的bundleless工具,能很好的配合Vue框架的开发,github上star为11k
snowpack: 另一个bundleless工具,目前框架生态更广泛一些,支持React/Vue/Svelte,github上star为11.9k
以上是关于一个前端大佬的十年回顾 | 漫画前端的前世今生的主要内容,如果未能解决你的问题,请参考以下文章