原文html
懒加载是一种将资源初始化推迟到须要时再加载的一种设计模式。本文展现了如何进行ES6模块的懒加载来提神页面的性能。node
过去几年来,开发者逐渐开始把代码从服务端向移动端迁移并达到性能上的提高。webpack
然而,这可能还不够。你有想过页面也许会加载许多实际不会用到的资源?经过懒加载,一种设计模式,来实现推迟初始化(加载/获取/分配)资源(代码/数据/静态资源)直到你须要使用的时候再加载。git
与此同时,经过Babel这样的编译器,ES6已经能够在实际产品中运行。如今无需care AMD与CommonJS之争,本文中将能够直接写ES6模块,并将其转译运行在浏览器,同时还支持已存在的CommonJS/AMD模块。es6
在本文中,我将会演示如何同步加载ES6模块(在页面加载时)以及如何使用System.js异步加载ES6模块(执行懒加载)。github
开发须要在浏览器环境中执行的JavaScript代码时,你总须要决定什么时候执行这段代码。web
总有一些代码块须要在页面加载时就执行,例如构建SPA时须要的结构性框架如Angular,Ember,Backbone或React。在页面请求发起时,这些代码必须被包含在返回的主题HTML文件中,一般由一个或多个<script>
标签包裹。typescript
另外一方面,你也许有更多的代码块须要在促发条件达成时再执行。包括:设计模式
被折叠的内容:例如评论板,在用户下滑时才显示。promise
在触发事件后响应的内容:例如用户点击放大后呈现的高清图。
不使用/频率不高的内容, 例如,"免费送货"这类应用面较窄的部件。
指定状况下呈现的内容, 例如,客服聊天窗口。
对于上述情形,若是促发条件不成立,代码块就不会执行。所以,代码块在页面加载是不须要的,能够被延迟。
为了实现懒加载,你只须要将这部分代码从页面加载的代码块中分离出来。直到促发条件达成后,再下载并执行代码。
这种异步加载延迟代码或懒加载的方式,在缩短页面加载时间与页面指数上对页面性能提高大有裨益。
AMD标准被提出用来在浏览器端加载图片,它第一个解决了js全局文件杂乱地散落在页面中的窘境。如下引用Require.js文档。
相较于当下使用的多个script标签隐式并依赖于你手动排列的顺序,AMD格式但愿能自由地使用模版样式,并使之能够直接在浏览器环境中简便地被使用。
其基于一套拥有模块加载,依赖注入,别名解析,异步能力的模块设计模式。其中一个主要的用处就是执行页面懒加载。
尽管这是一个绝妙的点子,但也带来了内在的复杂:即须要理解运行时的时间线。这使得开发者须要了解每一个模块在什么时候完成工做。
若是没法理解这些,在竞争条件下开发者就会陷入时而成功时而bug的抓狂中,这是很难调试的。所以,AMD不幸地失去了至关大的势头与推进力。
要了解更多坑,可阅读本文
首先,让咱们先来温习一下ES6的模版。若是你对其足够熟悉,就看成是一个快速复习。
模块功能最终被做为JS语言2015版的一部分。站在了CommonJS巨人的肩膀上,其强大且易于使用。
基本上,ES6的模块存在于它自生的文件里。全部的“全局”变量的做用域仅仅在文件中。模块能够暴露(export)出数据也能够引用(import)其余模块。
经过export
关键字,ES6模块接口暴露出你想要暴露的数据(变量,函数或类)。在下面的例子中咱们暴露出狗(Dog)
与狼(Wolf)
。
// zoo.js var getBarkStyle = function(isHowler) { return isHowler? 'woooooow!': 'woof, woof!'; }; export class Dog { constructor(name, breed) { this.name = name; this.breed = breed; } bark() { return `${this.name}: ${getBarkStyle(this.breed === 'husky')}`; }; } export class Wolf { constructor(name) { this.name = name; } bark() { return `${this.name}: ${getBarkStyle(true)}`; }; }
让咱们看一下在Mocha/Chai的单元测试中是若是引用模块,使用import <object> from <path>
语法。对于<object>
能够按需引用,这被称做“具名引用”。咱们能够从chai
中引用expect
一样也能够从Zoo
中引用Dog
和Cat
。另外,这种具名引用的语法相似于ES6中一个好用的特性:对象解构。
// zoo_spec.js import { expect } from 'chai'; import { Dog, Wolf } from '../src/zoo'; describe('the zoo module', () => { it('should instantiate a regular dog', () => { var dog = new Dog('Sherlock', 'beagle'); expect(dog.bark()).to.equal('Sherlock: woof, woof!'); }); it('should instantiate a husky dog', () => { var dog = new Dog('Whisky', 'husky'); expect(dog.bark()).to.equal('Whisky: woooooow!'); }); it('should instantiate a wolf', () => { var wolf = new Wolf('Direwolf'); expect(wolf.bark()).to.equal('Direwolf: woooooow!'); }); });
若是须要暴露的数量仅有一个,你可使用export default
,这将会直接暴露该对象,而非包含该对象的容器对象。
// cat.js export default class Cat { constructor(name) { this.name = name; } meow() { return `${this.name}: You gotta be kidding that I'll obey you, right?`; } }
与对象的结构相比,引用默认模块更加简单,你只需直接从模块中引用便可。
// cat_spec.js import { expect } from 'chai'; import Cat from '../src/cat'; describe('the cat module', () => { it('should instantiate a cat', () => { var cat = new Cat('Bugsy'); expect(cat.meow()).to.equal('Bugsy: You gotta be kidding that I\'ll obey you, right?'); }); });
ES6 explore可让你了解到更多关于ES6模块方面的知识。
使人意外的是,ES6并无模块加载器的规范。System.js的灵感源于一个受欢迎的动态模块加载器提案es6-module-loader。虽然该体案被撤回,但还有由WhatWG提出的新的加载器体案以及由Domenic Denicola提出的动态import体案。
然而,System.js是当下使用频率最高的支持ES6的模块加载器中之一。在浏览器及NodeJS上支持ES2015,AMD,CommonJS以及全局脚本。其提供了一步模块加载器(与Require.js匹配)以及经过Babel,Traceur或Typescript的ES6编译。
System.js经过基于Promises的API实现异步模块加载。因为promises既能链式调用又能够捆绑使用,使其成为一种即强大又灵活的方法:例如,你可使用Promises.all
来平行加载多个模块,仅须要监听全部promises是否都被执行。
最近,动态引用规则正逐渐受到关注并被引入Webpack 2。你能够查阅Webpack 2文档的指南中ES6代码分割一节。其也受到System.js的启发,所以转换速度很快。
为了更通俗地阐述以同步/异步方式加载模块,我建立了案例项目,在页面加载时同步引用Cat
模块,在用户点击按钮后异步引用Zoo
模块。能够经过lazy-load-es2015-systemjs查看这个项目的代码。
看一看在页面加载时引入的主代码块main.js
。
首先,经过import
执行Cat
的同步加载。其后建立一个Cat实例,调用meow()
方法,添加DOM:
// main.js // Importing Cat module synchronously import Cat from 'cat'; // DOM content node let contentNode = document.getElementById('content'); // Rendering cat let myCat = new Cat('Bugsy'); contentNode.innerHTML += myCat.meow();
最后,让咱们看看经过System.import('zoo')
异步加载Zoo
,最终,Dog
与Wolf
的实例调用bark()
方法添加DOM:
// Button to lazy load Zoo contentNode.innerHTML += `<p><button id='loadZoo'>Lazy load <b>Zoo</b></button></p>`; // Listener to lazy load Zoo document.getElementById('loadZoo').addEventListener('click', e => { // Importing Zoo module asynchronously System.import('zoo').then(Zoo => { // Rendering dog let myDog = new Zoo.Dog('Sherlock', 'beagle'); contentNode.innerHTML += `${myDog.bark()}`; // Rendering wolf let myWolf = new Zoo.Wolf('Direwolf'); contentNode.innerHTML += `<br/>${myWolf.bark()}`; }); });
掌握将同步代码控制在最小并异步加载代码能够极大提高网站的性能。AMD 与 CommonJS为ES6模块铺平了道路,如今你能够经过编译器进行使用。开始加载你的ES6模块经过System.js
或在官方还未给出解决方案时,经过Webpack 2实现动态加载。