Make Hot Loading faster

Summary:
public

Before this this when a file was changed besides sending the HMR update we rebuild every single bundle that the packager had build (to serve it faster when the user hit cmd+r). Since when hot loading is enabled we don't do cmd+r all this work was pointless (except for when you're developing multiple apps using the same packager instance at the same time, which we can assume is very uncommon). As a consequence, the HMR update was competing with the rebundling job making HMR quite slow (i.e.: on one huge internal app it took up to 6s for the HMR changes to get applied).

So, this diff tweaks the file change listener so that we don't rebundle nor invoke the fileWatchers (use for live reload which is also useless when hot load is enabled) when hot loading is enabled. Also, it makes the HMR listener more high pri than the other listeners so that the HMR dev experience is as good as it can get.

Reviewed By: vjeux

Differential Revision: D2793827

fb-gh-sync-id: 724930db9f44974c15ad3f562910b0885e44efde
This commit is contained in:
Martín Bigio 2015-12-29 18:24:10 -08:00 committed by facebook-github-bot-8
parent b5081abae3
commit e75e861116
2 changed files with 49 additions and 35 deletions

View File

@ -20,6 +20,7 @@ function attachHMRServer({httpServer, path, packagerServer}) {
function disconnect() {
client = null;
packagerServer.setHMRFileChangeListener(null);
}
// Returns a promise with the full list of dependencies and the shallow
@ -59,41 +60,6 @@ function attachHMRServer({httpServer, path, packagerServer}) {
});
}
packagerServer.addFileChangeListener(filename => {
if (!client) {
return;
}
packagerServer.getShallowDependencies(filename)
.then(deps => {
// if the file dependencies have change we need to invalidate the
// dependencies caches because the list of files we need to send to the
// client may have changed
if (arrayEquals(deps, client.shallowDependencies[filename])) {
return Promise.resolve();
}
return getDependencies(client.platform, client.bundleEntry)
.then(({dependenciesCache, shallowDependencies}) => {
// invalidate caches
client.dependenciesCache = dependenciesCache;
client.shallowDependencies = shallowDependencies;
});
})
.then(() => {
// make sure the file was modified is part of the bundle
if (!client.shallowDependencies[filename]) {
return;
}
return packagerServer.buildBundleForHMR({
platform: client.platform,
entryFile: filename,
})
.then(bundle => client.ws.send(bundle));
})
.done();
});
const WebSocketServer = require('ws').Server;
const wss = new WebSocketServer({
server: httpServer,
@ -115,6 +81,40 @@ function attachHMRServer({httpServer, path, packagerServer}) {
shallowDependencies,
};
packagerServer.setHMRFileChangeListener(filename => {
if (!client) {
return Promise.resolve();
}
return packagerServer.getShallowDependencies(filename)
.then(deps => {
// if the file dependencies have change we need to invalidate the
// dependencies caches because the list of files we need to send
// to the client may have changed
if (arrayEquals(deps, client.shallowDependencies[filename])) {
return Promise.resolve();
}
return getDependencies(client.platform, client.bundleEntry)
.then(({dependenciesCache, shallowDependencies}) => {
// invalidate caches
client.dependenciesCache = dependenciesCache;
client.shallowDependencies = shallowDependencies;
});
})
.then(() => {
// make sure the file was modified is part of the bundle
if (!client.shallowDependencies[filename]) {
return;
}
return packagerServer.buildBundleForHMR({
platform: client.platform,
entryFile: filename,
})
.then(bundle => client.ws.send(bundle));
});
});
client.ws.on('error', e => {
console.error('[Hot Module Replacement] Unexpected error', e);
disconnect();

View File

@ -191,6 +191,16 @@ class Server {
this._fileWatcher.on('all', this._onFileChange.bind(this));
this._debouncedFileChangeHandler = _.debounce(filePath => {
// if Hot Loading is enabled avoid rebuilding bundles and sending live
// updates. Instead, send the HMR updates right away and once that
// finishes, invoke any other file change listener.
if (this._hmrFileChangeListener) {
this._hmrFileChangeListener(filePath).then(() => {
this._fileChangeListeners.forEach(listener => listener(filePath));
}).done();
return;
}
this._fileChangeListeners.forEach(listener => listener(filePath));
this._rebuildBundles(filePath);
this._informChangeWatchers();
@ -208,6 +218,10 @@ class Server {
this._fileChangeListeners.push(listener);
}
setHMRFileChangeListener(listener) {
this._hmrFileChangeListener = listener;
}
buildBundle(options) {
return Promise.resolve().then(() => {
if (!options.platform) {