Skip to content

Commit 7a5fbd3

Browse files
committed
Generating separate CSS file for prod build.
1 parent 3a665db commit 7a5fbd3

File tree

4 files changed

+43
-26
lines changed

4 files changed

+43
-26
lines changed

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,22 @@ Unfortunately, scripts in package.json can't be commented inline because the JSO
9797
Webpack serves your app in memory when you run `npm start`. No physical files are written. However, the web root is /src, so you can reference files under /src in index.html. When the app is built using `npm run build`, physical files are written to /dist and the app is served from /dist.
9898

9999
###How is Sass being converted into CSS and landing in the browser?
100-
Magic! Okay, more specifically: Webpack handles it like this:
100+
Magic! Okay, more specifically, we're handling it differently in dev (`npm start`) vs prod (`npm run build`)
101+
102+
When you run `npm start`:
101103
1. The sass-loader compiles Sass into CSS
102104
2. Webpack bundles the compiled CSS into bundle.js. Sounds odd, but it works!
103-
3. Loads styles into the <head> of index.html via JavaScript. This is why you don't see a stylesheet reference in index.html. In fact, if you disable JavaScript in your browser, you'll see the styles don't load either. This process is performed for both dev (`npm start`) and production (`npm run build`). Oh, and since we're generating source maps, you can even see the original Sass source in [compatible browsers](http://thesassway.com/intermediate/using-source-maps-with-sass).
105+
3. bundle.js contains code that loads styles into the <head> of index.html via JavaScript. This is why you don't see a stylesheet reference in index.html. In fact, if you disable JavaScript in your browser, you'll see the styles don't load either.
104106

107+
The approach above supports hot reloading, which is great for development. However, it also create a flash of unstyled content on load because you have to wait for the JavaScript to parse and load styles before they're applied. So for the production build, we use a different approach:
108+
109+
When you run `npm run build`:
110+
1. The sass-loader compiles Sass into CSS
111+
2. The [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin) extracts the compiled Sass into styles.css
112+
3. buildHtml.js adds a reference to the stylesheet to the head of index.html.
113+
114+
For both of the above methods, a separate sourcemap is generated for debugging Sass in [compatible browsers](http://thesassway.com/intermediate/using-source-maps-with-sass).
115+
105116
###I don't like the magic you just described above. I simply want to use a CSS file.
106117
No problem. Reference your CSS file in index.html, and add a step to the build process to copy your CSS file over to the same relative location /dist as part of the build step. But be forwarned, you lose style hot reloading with this approach.
107118

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-slingshot",
3-
"version": "1.0.3",
3+
"version": "1.1.0",
44
"description": "Starter kit for creating apps with React and Redux",
55
"scripts": {
66
"prestart": "npm run remove-dist",
@@ -40,6 +40,7 @@
4040
"eslint": "1.10.3",
4141
"eslint-loader": "1.2.0",
4242
"eslint-plugin-react": "3.15.0",
43+
"extract-text-webpack-plugin": "1.0.1",
4344
"file-loader": "0.8.5",
4445
"mocha": "2.3.4",
4546
"node-sass": "3.4.2",

tools/buildHtml.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,19 @@ var useTrackJs = true; //If you choose not to use TrackJS, just set this to fals
1414
var trackJsToken = ''; //If you choose to use TrackJS, insert your unique token here. To get a token, go to https://trackjs.com
1515

1616
fs.readFile('src/index.html', 'utf8', function (err,data) {
17-
if (err) {
18-
return console.log(err);
19-
}
17+
if (err) return console.log(err);
2018

21-
var trackJsCode = '';
19+
var $ = cheerio.load(data);
2220

23-
if (useTrackJs) {
24-
if (trackJsToken) {
25-
trackJsCode = "<!-- BEGIN TRACKJS Note: This should be the first <script> on the page per https://my.trackjs.com/install --><script>window._trackJs = { token: '" + trackJsToken + "' };</script><script src=https://d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js></script><!-- END TRACKJS -->";
26-
} else {
27-
console.log('To track JavaScript errors, sign up for a free trial at TrackJS.com and enter your token in /tools/build.html on line 10.'.yellow);
28-
}
29-
}
21+
//since a separate spreadsheet is only utilized for the production build, need to dynamically add this here.
22+
$('head').prepend('<link rel="stylesheet" href="styles.css">');
3023

31-
var $ = cheerio.load(data);
32-
$('head').prepend(trackJsCode); //add TrackJS tracking code to the top of <head>
24+
if (useTrackJs && trackJsToken) {
25+
var trackJsCode = "<!-- BEGIN TRACKJS Note: This should be the first <script> on the page per https://my.trackjs.com/install --><script>window._trackJs = { token: '" + trackJsToken + "' };</script><script src=https://d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js></script><!-- END TRACKJS -->";
26+
$('head').prepend(trackJsCode); //add TrackJS tracking code to the top of <head>
27+
} else {
28+
if (useTrackJs) console.log('To track JavaScript errors, sign up for a free trial at TrackJS.com and enter your token in /tools/build.html.'.yellow);
29+
}
3330

3431
fs.writeFile('dist/index.html', $.html(), 'utf8', function (err) {
3532
if (err) return console.log(err);

webpack.config.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file generates a webpack config for the environment passed to the getConfig method.
44
var webpack = require('webpack');
55
var path = require('path');
6+
var ExtractTextPlugin = require("extract-text-webpack-plugin");
67

78
var getPlugins = function(env) {
89
var GLOBALS = {
@@ -17,6 +18,7 @@ var getPlugins = function(env) {
1718

1819
switch(env) {
1920
case 'production':
21+
plugins.push(new ExtractTextPlugin('styles.css'));
2022
plugins.push(new webpack.optimize.DedupePlugin());
2123
plugins.push(new webpack.optimize.UglifyJsPlugin({minimize: true, sourceMap: true}));
2224
break;
@@ -40,19 +42,25 @@ var getEntry = function(env) {
4042
return entry;
4143
};
4244

43-
var getLoaders = function () {
44-
return [
45-
{
46-
test: /\.js$/,
45+
var getLoaders = function (env) {
46+
var loaders = [{test: /\.js$/, include: path.join(__dirname, 'src'), loaders: ['babel', 'eslint']}];
47+
48+
if (env == 'production') {
49+
//generate separate physical stylesheet for production build using ExtractTextPlugin. This provides separate caching and avoids a flash of unstyled content on load.
50+
loaders.push({
51+
test: /(\.css|\.scss)$/,
4752
include: path.join(__dirname, 'src'),
48-
loaders: ['babel', 'eslint']
49-
},
50-
{
53+
loader: ExtractTextPlugin.extract("css?sourceMap!sass?sourceMap")
54+
});
55+
} else {
56+
loaders.push({
5157
test: /(\.css|\.scss)$/,
5258
include: path.join(__dirname, 'src'),
5359
loaders: ['style', 'css?sourceMap', 'sass?sourceMap']
54-
}
55-
]
60+
});
61+
}
62+
63+
return loaders;
5664
};
5765

5866
module.exports = function getConfig(env) {
@@ -69,7 +77,7 @@ module.exports = function getConfig(env) {
6977
},
7078
plugins: getPlugins(env),
7179
module: {
72-
loaders: getLoaders()
80+
loaders: getLoaders(env)
7381
}
7482
};
7583
};

0 commit comments

Comments
 (0)