【5】代码拆分
#翻译转载#Web 应用 101
这里的单页面应用主要指类似于 Vue 等框架构建的前端网站,下文的“单页面网站”与之同意思
现在我们知道了,单页面应用(默认)把所有页面打包到一个小小的 html 文件和一个初始 JS 文件上。因为所有的功能和页面都被打包到这个"小小的"JS 文件上,所以随着项目的发展,我们发现这个初始 JS 文件变得越来越大(比如我的某个 Vue 小项目的初始 JS 文件 111W 字符,112 行)。这导致了初始 JS 文件的加载速度变慢,用户的体验也随之受到影响。
不过解决方法也不是没有,对于复杂的项目,我们可以使用代码拆分(在一些框架下也叫懒加载)的功能,大部分的主流框架都有这个功能。代码拆分可以将代码以页面为界分块(也可以指定其他的),当用户打开某个页面时,浏览器只请求对应页面的 JS 文件,当用户打开另一个页面时,它再请求另外的 JS 文件。
如果你再看看传统网站的工作方式,你会发现:
“tmd 怎么又回去了,技术倒退了?”
你会发现使用了代码拆分的单页面应用和传统网站很像。访问传统网站时,每访问一个页面浏览器都会去获取对应的资源(JS,CSS 等文件);而访问一个在路由级有代码拆分的单页面应用网站时,会获取对应页面的 JS 文件。
这样的单页面网站还能叫单页面网站吗?或者我们应该叫多页面应用?
你会发现这些名词逐渐不能适应新的产物了
不过现在,我们也可以将一些较大的组件,放到单独的文件里,这样只有在组件被调用时,代码才会加载。
当用户多次访问同一个网站时,资源浪费还是会发生,不过大多数浏览器可以缓存获取过的资源,当你再次访问时,它可以直接从本地打开上次获取过的资源(你可以在打开网页时按 F12 查看哪些资源是本地的,哪些资源是从服务器获取的)。
但游览器缓存有个问题,当你更新文件时(比如更新了一个分页视图或者树视图),浏览器可能还是会加载上次的文件,这导致你在调试时查看效果会变得麻烦。或许你可以使用shift+ctrl+R
来解决问题,但用户不会这么做,他只会在你上线网页后发现什么都没变,甚至出现了 Bug。
主流框架的解决方案是,给每一个打包后的文件添加一个基于时间的哈希值,当打包项目时,检查每一个模块是否有代码变动,如果有,那就修改哈希值并修改引用链接(比如 table.hash123.js 到 table.hash345.js),当浏览器请求链接时,会认为这是与之前不同的文件,并向服务器请求一份,而不会使用旧文件。
代码拆分的另一用途就是处理第三方类库,对于第三方库,每个组件都有单独的 JS 文件,只有当引用到需要的组件时,才会解析对于的代码,如果不使用代码拆分,编译器可能会把整个库编译进去。
材料: 如何创建 React 按钮
材料: 如何创建 React 下拉菜单
现在出现了更精细的技术叫tree shaking,它会在编译的时候检查代码,把没有用到的代码删除,来进一步精简最后生成的代码。
下面列出了 JS 的历代编译器
课后练习:
- 如何使用 React 路由
- 学习如何使用路由级代码分割
原文
CODE SPLITTING
We have learned that SPAs get shipped in one small HTML file and one JS file as default. The JavaScript file starts small, but it grows in size when your application gets larger, because more JavaScript is packaged in one bundle.js file. This impacts the user experience of SPAs, because the initial load time to transfer the JavaScript file from web server to browser increases eventually. When all files are loaded, a user can navigate from page to page without interruptions (good). However, in contrast the initial load time decreases the user experience when the page is requested in the browser (bad).
Requesting the entire application as JavaScript file becomes a disadvantage once the application grows in size. For a more sophisticated single-page application, techniques like code splitting (also called lazy loading in React + React Router) are used to serve only a fraction of the application that is needed for the current page (e.g. mywebsite.com/home). When navigating to the next page (e.g. mywebsite.com/about), another request is made to the web server to request the fraction for this page.
If you recap how traditional websites work, you will find out that it's quite similar with code splitting enabled SPAs. For a traditional website, every time a user navigates to a new route a new HTML file (with optional CSS, JavaScript, and other asset files) is loaded. For the SPA with code splitting on a route level, every navigation leads to a newly requested JavaScript file.
Can we still call this single-page application or are we back at multi-page applications? You see how the terminology becomes fuzzy eventually ...
Code splitting does not need to happen on route level like in the previous scenario. For example, one can extract larger React components into their standalone JavaScript bundle too, so that it gets only loaded on pages where it is actually used.
However, as you can see this leads to redundant code that's requested from the web server. The same happens when a user navigates to a code split route twice, because it would get loaded from the web server twice as well. Hence we want the browser to cache (read: store in the browser's cache on the user's machine) the result.
Now what happens if the bundled table.js file changed, because we introduced a new feature to our table such as a paginated view or a tree view? If caching is enabled, we would still see the old version of the Table component in the browser.
As a solution to this problem, each new build of the application checks whether the bundled code has changed. If it has changed, it receives a new file name with a hash (e.g. table.hash123.js becomes table.hash765.js) based on a timestamp. When the browser requests a file with a cached file name, it uses the cached version. However, if the file has changed and therefore has a new hashed name, the browser requests the new file because the cashed version is outdated.
Another example is code splitting for third-party JavaScript libraries. For instance, when installing a UI library for React which comes with components such as Button and Dropdown, code splitting can be applied too. Every component is a standalone JavaScript file then. When importing the Button component from the UI library, only the JavaScript from the Button gets imported but not the JavaScript from the Dropdown.
Continue Reading: Learn how to create a React Button
Continue Reading: Learn how to create a React Dropdown
For bundling of a React application (or library) into one or multiple (with code splitting) JavaScript files, another technique called tree shaking comes into play which eliminates dead code (read: unused code) so that it is not packaged in the final bundle. Historically the following bundlers were used in JavaScript (from past to recent):
Exercises:
Learn
how to use React Router
for client-side routing in React.
- Learn how to use code splitting on a route level.
生词
splitting
- adj.(头痛)欲裂的;极快的;滑稽的
- *v.*裂开;(人群)分成多组;(非正式)头剧痛(split 的现在分词)
ship
- n.(大)船,舰;三(或多)桅帆船;<非正式> 艇(尤指赛艇);宇宙飞船;<美> 飞机
- v.(用船、飞机、卡车)运送,运输;遣送(某人);推出(计算机商品),(使)上市;(水手)在船上工作,在船上服役;舷侧进水;把(浆架中的浆)放在船内;给船安装(舵、桅杆等);<旧> 上船,乘船;乘船旅行;提供船只
impact
- *n.*撞击,冲击力;巨大影响,强大作用
- *v.*冲击,撞击;挤入,压紧;(对……)产生影响
transfer
- v.(使)转移,搬迁;转移(感情),传染(疾病),转让(权力等);(使)调动,转职;转会,使转会(尤指职业足球队);(将所得学分)转到(另一所学校);转接(电话);将(钱)转到(另一个账户上);转让(财产,权利),移交(责任);转用;转存,转录(资料、音乐等),改编;(旅途中)转乘,换乘;转印(图画,图案);(通过延伸、隐喻)转变(词义,短语义)
- *n.*转移,转让,调动;(运动员)转会;转换,过渡;已调动的人,已转移的东西;(旅行中)转乘,转搭;<美>转车票,换乘票;纸上可转印的图画或图案;迁移(将已习得的行为在新的情况下应用)
interruption
- *n.*打断,插嘴;暂停;阻断物,中断期
decrease
- v.(使)减少,(使)降低
- *n.*减少,降低
disadvantage
- *n.*不利条件,劣势;不利因素;损失,损害
- *vt.*使处于不利地位,损害
sophisticate
- *n.*老于世故的人,见多识广的人
- *v.*使老于世故;使(设备、技术等)更复杂先进;<古>(老练地)讲话(或推理);<古>通过诡辩误导(或曲解)
- adj.<古>老于世故的
fraction
- *n.*分数,小数;小部分,微量;持不同意见的小集团;分离;(基督教用语)分切圣餐面包
current
- *adj.*现行的,当前的;通用的,流行的;最近的
- *n.*水流,气流;电流;思潮,趋势
recap
- *v.*扼要重述,摘要说明;翻新胎面
- *n.*扼要的重述,概述;翻新的轮胎
terminology
- n.(某学科的)术语;有特别含义的用语,专门用语;术语学
fuzzy
- adj.(图片、声音等)不清楚的,模糊的;不明确的,含混的;(计算机,逻)模糊的,模糊逻辑的;有绒毛的,毛茸茸的;(人,头脑)迷糊的,稀里糊涂的;(头发)鬈曲的;<非正式>友善的,令人愉快的;带(或可产生)模糊音的(fuzzed 的别名)
scenario
- *n.*设想,可能发生的情况 ;(电影、戏剧等的)剧情梗概;(艺术或文学作品中的)场景
extract
- *n.*选段,引文;提取物,汁
- *v.*提取,提炼;取出,拔出;摘录;索取,设法得到;开(方),求(根)
standalone
- adj.(计算机)独立运行的;(公司)独立的
- *n.*脱机
redundant
- adj.<英> 被解雇的,被裁员的;多余的,累赘的;(为预防机器或系统中主要组件故障而设的)复置装置的,备用的
hence
- *adv.*因此;之后
cache
- *n.*隐藏处,秘窖;贮藏物,隐藏物(尤指武器);高速缓冲存储器
- *v.*匿藏,隐藏(尤指武器);把(数据)存入高速缓冲存储器;给(硬件)装备高速缓存
store
- *n.*商店;仓库,储存处;(事物的)储存,储备;大量,丰富;储存的物品(stores);军需品,补给品(stores);<英>(计算机的)存储器;重视,注重;待育肥的小羊(或小公牛、小母牛、小猪)
- *v.*贮存,储藏;(在头脑或计算机中)储存(事实或信息)
- 【名】 (Store)(德)施托雷(人名)
feature
- *n.*特点,特征;五官,面貌(特征);地貌;特写,专题节目;正片; 特点,特征
- *v.*以……为特色,以……为主要组成;起重要作用,占重要地位;放映,上演;担任主演
paginate
- *v.*为……标页数
caching
- n.[计]超高速缓存
- *v.*缓冲(cache 的 ing 形式)
checks
- *v.*检查,核对;查看,查询;克制,抑制;存放,寄放;托运;在……上打钩;将(对方的)军
- *n.*检查,核对;制止(手段),抑制(手段);结账单;支票(=cheque);方格图案;衣帽寄存处;寄存凭证;钩号;(国际象棋)将军
- 【名】 (Check)(英)切克(人名)
bundle
- *n.*束,捆,包;非常;一套,一系列;大量;婴儿;一大笔钱;软件包
- *v.*匆匆打发,乱塞;匆忙赶往;捆绑销售;与某人和衣同睡
receive
- *v.*得到,收到;遭受,经受(特定待遇);对……作出反应;接待,招待;接收(某人为成员);接收,收听(信号);(通过无线电)听到;购买,窝藏(赃物);接(球);领受(圣餐面包或葡萄酒);接受(治疗);形成(看法,印象);容纳,承接
outdate
- *v.*使过时;使陈旧
instance
- *n.*例子,实例
- *v.*举……为例
dropdown
- *adj.*可折叠的,可展开的;(计算机菜单)下拉式的
apply
- *v.*申请;涂,敷;施加,实施;应用,运用;踩(刹车);适用,适合;指称
shake
- *v.*摇动,抖动;削弱,动摇;发抖,颤抖;握手;使震惊,使难以置信;(通过惊吓或干扰而使心情或态度)改变;摆脱,克服;(因愤怒而)挥动,舞动(拳头、棍子等) ;甩掉,摆脱(跟踪者);抖掉,抖出;使劲甩动,急剧晃动;(因发怒或为弄醒对方而)晃动,摇醒;摇头(表示拒绝、怀疑或难过);(使)震颤,(使)震动;(声音因紧张、生气等)颤抖;使摒弃,使改变(态度、信念)
- *n.*摇动,摇晃;<非正式>地震;(摇动容器后的)撒出物;奶昔;<非正式>哆嗦,颤抖;(乐)颤音;<美>(尤用于乡村建筑的)木制盖屋板,木制墙面板
eliminate
- *v.*剔除,根除;对……不予考虑,把……排除在外;(比赛中)淘汰;铲除,杀害;(生理)排除,排泄;消去