-
Notifications
You must be signed in to change notification settings - Fork 140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add vendor code-splitting #1031
Conversation
Longer-term thinking: would you ever consider dropping gulp altogether and just using webpack for all building? |
src/index.html
Outdated
@@ -14,6 +14,5 @@ | |||
<div class="pop-throbber__image"></div> | |||
</div> | |||
</div> | |||
<script src="application.js"></script> | |||
</body> | |||
</html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I moved src/static/index.html
to src/index.html
since it's now used as a template for html-webpack-plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK—I would say let’s maybe move it to src/html/index.html
(mirroring src/css/application.css
)? Realistically all the JS should be in src/js
but that’s a reorganization for another day…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool!!
Left a few very minor inline comments. Also more thoughts:
As far as I can tell, our build process currently doesn't allow us to set
NODE_ENV
inwebpack.config.js
since it getsrequire
d beforeNODE_ENV
gets set in theenv
gulp task.
Oh, the --production
thing is kind of silly anyway—let’s just get rid of it. I can just set NODE_ENV
as an actual environment variable on Travis (in fact I just did—how’s that for service?). I like the idea of having the script in package.json
to make it easier to test production-mode builds in development.
Longer-term thinking: would you ever consider dropping gulp altogether and just using webpack for all building?
Absolutely! I think we the way we build the CSS bundle right now is obviously clunky, and it seems like for HTML this PR basically transfers the responsibility to Webpack already.
I’ve never worked with a fully Webpack-based build system; would we still use gulp for stuff like pushing Firebase permissions changes on deploy?
It also adds scaffolding for caching in production.
So we already do cache in production, with time-based expiries. We also purge the entire cache when releasing. I think moving to hashing filenames is a good call here, as it allows us to only worry about the expiry of a single resource—index.html
—and everything else can basically be cached forever.
But we will need to stop blowing away the entire CloudFlare cache when we release. I’m betting CF allows us to just purge index.html
?
Also, another thing to think about is the service worker—with the content hash in the filename, intuitively it seems like offline storage will accumulate every version of the assets it’s ever seen, which (especially given the size of our assets!) seems potentially bad…
src/index.html
Outdated
@@ -14,6 +14,5 @@ | |||
<div class="pop-throbber__image"></div> | |||
</div> | |||
</div> | |||
<script src="application.js"></script> | |||
</body> | |||
</html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK—I would say let’s maybe move it to src/html/index.html
(mirroring src/css/application.css
)? Realistically all the JS should be in src/js
but that’s a reorganization for another day…
webpack.config.js
Outdated
@@ -253,6 +261,27 @@ module.exports = { | |||
], | |||
ServiceWorker: {navigateFallbackURL: '/'}, | |||
}), | |||
new webpack.optimize.CommonsChunkPlugin({ | |||
name: 'vendor', | |||
minChunks({ context }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Project style would be no space inside the curly braces
webpack.config.js
Outdated
const isBowerComponent = context.indexOf('bower_components') !== -1; | ||
return isNodeModule || isBowerComponent; | ||
} | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor / I don’t feel strongly about this at all, but I would invert this and return false
early if context
is not truthy, so that the “meat” of the function body describes the expected case.
webpack.config.js
Outdated
@@ -66,8 +70,12 @@ module.exports = { | |||
entry: './src/application.js', | |||
output: { | |||
path: path.resolve(__dirname, './dist'), | |||
filename: 'application.js', | |||
sourceMapFilename: 'application.js.map', | |||
filename: process.env.NODE_ENV === 'production' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it’s worth declaring a local const isProduction
since we’re asking this question several times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also noticed this error when checking it out locally:
(node:31065) DeprecationWarning: Chunk.modules is deprecated. Use Chunk.getNumberOfModules/mapModules/forEachModule/containsModule instead.
In regard to pushing Firebase permissions changes on deploy: keeping gulp around just for that seems like overkill. If webpack doesn't cut it, maybe a simple |
|
15f6134
to
e3a62a9
Compare
|
Just pushed a few more changes. A couple of things I'd like to bring to your attention:
|
Whoa, really! Totally unintentional. That would explain why the build in development is so freaking slow… |
package.json
Outdated
@@ -173,6 +173,7 @@ | |||
"postinstall": "bower install", | |||
"pretest": "yarn run lint", | |||
"dev": "yarn install && gulp dev", | |||
"prod": "yarn install && NODE_ENV=production gulp build", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think there’s a need for the yarn install
here—that’s already a step in Travis, and best to keep separate things separate imo (the dev
thing is to make it more convenient for humans running the dev server, but no humans to inconvenience in production builds)
webpack.config.js
Outdated
@@ -16,6 +20,8 @@ const babel = require('babel-core'); | |||
const babelLoaderVersion = | |||
require('./node_modules/babel-loader/package.json').version; | |||
|
|||
const isProduction = process.env.NODE_ENV === 'production'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I just had a thought—yargs
presumably is just looking at the arguments passed to the current process when it started. That information should be no different in a webpack.config
module that’s require
d into the gulpfile
than it is in the gulpfile
itself, so could we just make this line const isProduction = yargs.argv.production
and roll back some of the other related changes?
webpack.config.js
Outdated
@@ -253,6 +259,38 @@ module.exports = { | |||
], | |||
ServiceWorker: {navigateFallbackURL: '/'}, | |||
}), | |||
new webpack.DefinePlugin({ | |||
'process.env': { | |||
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the stringify
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, I think we do:
Note that because the plugin does a direct text replacement, the value given to it must include actual quotes inside of the string itself. Typically, this is done either with either alternate quotes, such as
'"production"'
, or by usingJSON.stringify('production')
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well then, in my face.
12e8c0e
to
54065ee
Compare
Seems like offline-plugin adds its own content-based hash for cache invalidation. See NekR/offline-plugin#127 (comment) It's a matter of adding the |
54065ee
to
33ae910
Compare
I don’t think so—as of now, at least, ServiceWorker / AppCache cached assets are only used if there is no network connection. I think we probably want hashing at both levels for now, as long as they don’t interfere with each other? |
Okay cool, so I'll leave the content-hashing and possibly add That |
33ae910
to
20f72b2
Compare
|
@outoftime okay, looks like the default I'm not too familiar with ServiceWorkers or offline-plugin—do you know a way to view which resources have been cached? I'd like to be able to test that the cache behavior is correct (i.e., not accumulating copies of every bundle version.) |
Aaand, one more thing—you can see from the Travis output that the tests fail with Any preference on how to work around this issue? I could
|
You can go into the Application tab of the Chrome Dev Tools, and then look in Service Workers and Cache Storage
The second option sounds better but what did you have in mind for the flag? I think my ideal solution would basically have |
Nice, thanks! That's a great tip. I'll test this branch when I get home and make sure that the cache behaves as expected. webpack supports exporting a function that returns a configuration object instead of just a plain object. Check out this link. How about having module.exports = (env = 'development') => {
// ...
} And then we could require it in different files and manually create the configuration object by passing the flag. If that sounds good, I'll open a small merge request that just changes |
@ajgreenb just merged the webpack function PR! |
59fd886
to
cdc9343
Compare
CommonsChunkPlugin is incompatible with karma-webpack.
After doing some more research, it looks like offline-plugin doesn't actually care what you call your files. From what I understand, it will use its own content-based hashes as the sole source of truth for cache updates (regardless of the name of the file.)
So I think the caching behavior is identical across this branch and |
I think the only thing left to do is update the |
cdc9343
to
22ec435
Compare
I think that probably covers it! What about caching headers on S3? Right now the assets are set to expire fairly quickly—ideally everything that’s content-hashed should have ultra long lived headers right? |
Yeah, ultra-long-lived sounds good or maybe even Cache-Control: immutable? Where do you set those headers?
|
This may be naive/silly (not too experienced here), but what about just not sending caching headers at all from S3 since we already cache in Cloudflare (also browser caching and maybe ServiceWorker caching)? Doesn't having lots of different caching layers make it easier to end up with the wrong caching behavior?
|
Sorry, I’m a bozo—caching behavior is already configured at the CloudFlare, not the S3, level. We can just update them to be more aggressive with the JavaScript after this is released. |
Sweet, well, unless I'm forgetting something, I think I've addressed all your feedback thus far. Let me know if you have further comments! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lookin great @ajgreenb just a couple final items!
webpack.config.js
Outdated
'process.env': { | ||
NODE_ENV: JSON.stringify(isProduction ? 'production' : 'development'), | ||
}, | ||
}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think this is needed—we already set NODE_ENV
using the EnvironmentPlugin
(note that the values given there are just defaults in case the key isn’t defined in process.env
at compile time).
gulpfile.js
Outdated
{ | ||
files: [ | ||
'https://popcode.org/index.html', | ||
'https://popcode.org/application.css', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s grab the hostname from process.env
? It can just be added to .travis.yml
for that to work properly.
It's not needed because we use the EnvironmentPlugin!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to go!!
Aw yeahhh, thanks @outoftime! I'll continue picking away at #348. |
Sweet! I feel like it was broken at some point but the Webpack Visualizer is working great now, ton of low-hanging fruit in here. I’d be inclined to try pulling out some of the big libraries that we don’t need for minimal bootstrap ( |
This PR introduces code-splitting for vendor libraries. It also adds scaffolding for caching in production. When
NODE_ENV === 'production'
inwebpack.config.js
, the files will be tagged with a hash of their contents. As far as I can tell, our build process currently doesn't allow us to setNODE_ENV
inwebpack.config.js
since it getsrequire
d beforeNODE_ENV
gets set in theenv
gulp task. To address this, I couldwebpack.config.js
export a function that takes an environment parameter and use that to modify the webpack configuration.build:production
script inpackage.json
that looks something likeNODE_ENV=production gulp build --production
.Thoughts?