熟悉我的朋友都知道,在制造业网站开发过程中,除了内容、版式和 SEO 之外,我一直比较关注网站性能。因为对于制造业企业来说,网站很多时候并不只是一个展示页面,它既是企业的品牌窗口,也是客户了解企业、进入转化流程的重要入口。
尤其是中小制造业企业,很多客户第一次接触一个品牌,就是从网站开始。页面打开速度、滚动是否流畅、交互是否顺畅,这些看起来比较细节的问题,实际上都会影响用户对企业的第一印象。特别是在广告着陆页场景中,用户可能只停留几十秒。
这个时候,网站性能就更加重要。但制造业网站还有一个特点:页面又不能为了追求速度,做得过于简单。
很多时候,丰富的内容展示本身就是用户体验的一部分。比如:产品轮播、图库、数字增长、标签切换、滚动动画、视频模块……
这些功能可能只在某一个页面使用,也可能被多个页面重复使用。这就带来了一个很现实的问题:一个网站到底应该怎么管理这些前端功能?
早期网站:功能集中加载
以前很多网站的做法都比较简单。所有页面统一加载 jQuery,然后各种页面效果全部基于 jQuery 开发,这种方式在当年非常流行。一方面是因为开发比较快,另一方面是因为当时的插件生态也非常丰富。但随着网站越来越复杂,问题也慢慢暴露出来。
首先,jQuery 本身就有一定的体积,但很多页面实际上只使用了其中很少的一部分能力。更大的问题是,很多项目并没有按照页面实际需求加载功能,而是所有页面统一加载一套公共 JS。
main.js
app.js
common.js
之类的统一文件里。这样做虽然简单,但一个页面即使只需要一个轮播,也可能把其他功能一起加载进来。很多代码当前页面根本用不到。但浏览器还是需要:下载、解析、执行。页面上的功能越来越多,公共 JS 也会越来越大,最后一个看起来并不复杂的企业网站,前端资源反而变得越来越重。
后来开始拆 JS 功能文件
再后来,有些项目开始按照页面拆分 JS 文件。哪个页面需要什么功能,就引入对应的 JS 文件。这样确实比所有代码统一加载合理了很多,因为至少可以避免一部分无关代码被加载。
但管理上的问题也随之出现了,页面模板开始变得越来越复杂。开发人员需要不断记住:
- 这个页面要加载哪个 JS?
- 那个页面还需要哪个文件?
- 以后增加一个功能,又应该放在哪里?
时间一长,前端的依赖关系就会越来越混乱。还有一些项目,直接把功能逻辑写进页面模板或者页面模块内部。刚开始开发的时候很方便。但是网站运行几年之后,通常就会遇到:功能无法复用、升级困难、配置混乱、维护成本越来越高。
而企业网站恰恰又是生命周期很长的一类网站。一个项目可能运行很多年。前端代码如果没有一个比较清晰的结构,时间越长,越容易失控。
RequireJS模块化加载方案
后来,为了解决前端模块之间的依赖问题,也出现了 RequireJS 这样的模块化加载方案。
RequireJS 可以根据页面需要动态加载不同的 JavaScript 模块,同时处理模块之间的依赖关系。相比把所有代码都塞进一个 main.js,这种方式已经进步了很多。
但对于现在的企业网站来说,RequireJS 这套完整的模块化机制,很多时候又显得有些重了。
一方面,企业网站本身的功能通常比较明确,页面之间也相对独立,并不需要复杂的模块依赖关系;另一方面,现在主流浏览器对原生 JavaScript 的支持已经非常完善,很多过去需要依赖额外库,或者为了兼容不同浏览器而做的处理,现在都可以直接交给浏览器完成。
所以,模块化加载的思路当然可以保留,但实现方式其实没有必要那么复杂。
结合制造业网站的实际特点,我更希望做到一件简单的事情:页面需要什么功能,就加载什么功能。
让 HTML 自己声明需要什么功能
于是,我给网站设计了一个主功能文件 app.js。它除了统一处理页面公共逻辑、初始化逻辑和基础功能之外,还负责检查 HTML 中声明的组件,并根据这些声明异步加载对应的功能模块。
HTML 依然按照正常方式编写,只是在需要某个功能的时候,在对应元素上声明组件:
<div component="gallery"></div>
这里的 component="gallery",就是一个模块声明。页面加载之后,app.js 会检查当前页面中有没有需要加载的组件。
如果发现:
component="gallery"
就知道当前页面需要 gallery 模块,然后异步加载:
gallery.js
如果页面没有这个组件,就不会请求对应的 JS 文件。
也就是说:页面需要什么,就加载什么。没有出现的功能,就不加载。这样,一个只使用图库功能的页面,不需要为了一个图库,把轮播、视频、动画等其他代码一起加载进来。
现成的 Demo
这套模块化加载方式,实际上现在这个网站就在使用。比如你正在查看此页面的评论功能,就不是所有页面统一加载 comment.js。你可以直接查看网站源代码,在评论区域的 section 上,可以看到:
component="comment"
这个属性就是模块声明。
网站主要的 app.js 加载运行之后,会自动检查当前页面中有没有需要加载的组件。发现 component="comment",就会加载对应的 comment.js 模块,没有这个组件的页面,自然也不会加载这部分代码。
所以整个网站需要哪些功能,其实从 HTML 结构本身就能够很直观地看出来。
这也是我采用这种方式的一个原因:功能跟着页面走,需要什么加载什么,不需要的就不加载。
有兴趣的同学可以直接研究我的网站源代码,也可以在评论区交流。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。