1
0
mirror of https://github.com/mgerb/mywebsite synced 2026-03-05 07:55:23 +00:00

Merge pull request #6 from mgerb/react

React
This commit is contained in:
2016-12-07 14:24:57 -06:00
committed by GitHub
9 changed files with 358 additions and 12 deletions

3
.gitignore vendored
View File

@@ -6,4 +6,5 @@ dist
public public
npm-debug.log npm-debug.log
mywebsite mywebsite
stats.html tls.key
tls.crt

View File

@@ -1,12 +1,15 @@
{ {
"database": { "Database": {
"url": "", "url": "",
"database": "", "database": "",
"username": "", "username": "",
"password": "" "password": ""
}, },
"api": { "Api": {
"key": "" "key": ""
}, },
"port": 8080 "Port": 8080,
"TLSPort": 443,
"TLSCertFile": "",
"TLSKeyFile": ""
} }

View File

@@ -12,6 +12,8 @@ import ncp from 'ncp';
import marked from 'marked'; import marked from 'marked';
import highlight from 'highlight.js'; import highlight from 'highlight.js';
const debug = process.env.NODE_ENV !== 'production';
marked.setOptions({ marked.setOptions({
header: true, header: true,
highlight: (code) => { highlight: (code) => {
@@ -32,10 +34,9 @@ function parse_dir(dir, folder_name = null){
for(let post of posts){ for(let post of posts){
const stats = fs.statSync(dir + post); const stats = fs.statSync(dir + post);
if(stats.isDirectory()){ if(stats.isDirectory()){
parse_dir(dir + post + '/', post); parse_dir(dir + post + '/', post);
} else if(folder_name !== null){ } else if(folder_name !== null && dir !== './posts/extras/'){
const file = fs.readFileSync(dir+post, 'utf8'); const file = fs.readFileSync(dir+post, 'utf8');
const tokens = marked.lexer(file, null); const tokens = marked.lexer(file, null);
const temp = { const temp = {
@@ -59,8 +60,10 @@ json.posts.sort((a, b) => {
return new Date(b.date) - new Date(a.date); return new Date(b.date) - new Date(a.date);
}); });
const prettyJson = debug ? JSON.stringify(json, null, 4) : JSON.stringify(json, null, null);
//output to public path //output to public path
fs.writeFile('./public/metadata.json', JSON.stringify(json,null,4), (err) => { fs.writeFile('./public/metadata.json', prettyJson, (err) => {
if (err) throw err; if (err) throw err;
console.log("Saved metadata.json"); console.log("Saved metadata.json");
}) })

View File

@@ -5,11 +5,12 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"analyze": "webpack --json | webpack-bundle-size-analyzer", "analyze": "webpack --json | webpack-bundle-size-analyzer",
"build": "NODE_ENV=production webpack -p --progress --colors && babel-node metadata.js", "build": "NODE_ENV=production webpack -p --progress --colors && NODE_ENV=production babel-node metadata.js",
"c9": "webpack-dev-server --port $PORT --host $IP --hot --content-base dist --history-api-fallback", "c9": "webpack-dev-server --port $PORT --host $IP --hot --content-base dist --history-api-fallback",
"check-gzip-size": "gzip -9 -c ./public/client.min.js | wc -c | numfmt --to=iec-i --suffix=B --padding=10", "check-gzip-size": "gzip -9 -c ./public/client.min.js | wc -c | numfmt --to=iec-i --suffix=B --padding=10",
"deploy": "npm run get_dependencies && npm run prod && ./mywebsite", "deploy": "npm run get_dependencies && npm run prod && ./mywebsite",
"dev": "webpack-dev-server --content-base public --inline --hot --history-api-fallback", "dev": "webpack-dev-server --content-base public --inline --hot --history-api-fallback",
"generate-tls": "sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./tls.key -out ./tls.crt",
"get_dependencies": "go get ./server && npm install", "get_dependencies": "go get ./server && npm install",
"prod": "npm run build && go build ./server/mywebsite.go", "prod": "npm run build && go build ./server/mywebsite.go",
"prod-win": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors && babel-node metadata.js && go build ./server/mywebsite.go", "prod-win": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors && babel-node metadata.js && go build ./server/mywebsite.go",

View File

@@ -0,0 +1,129 @@
# Building for production with Webpack
I've been using webpack for almost 6 months now and I still only know a fraction of the tools it provides. Bundling my files for production has been a bit of an annoyance, but I'm at a point where everything works. View my current webpack configuration file at the bottom of the post.
## Production vs. Development
So the goal is to have two different build options for webpack: one for production and one for development. The development build needs to include things like source maps and logging while the production build needs JS minification. There are a few different ways to do this.
### Multiple webpack configuration files
Some choose to create a `webpack.prod.config.js` file, which could be useful for projects of massive scale. I found this option a bit tedious because I did not want to update two different configuration files when I found a new tool for webpack.
### NODE_ENV=production webpack -p
NODE_ENV is an environment variable that can be used within the code itself. I use one `webpack.config.js` file and enable certain plugins based on this condition. The environment variable can be passed to webpack with `NODE=<variable> webpack`. On Windows the command translates to the command below. For some reason Windows likes to add the space after production to the variable so that must be removed. There is a tool called [cross-env](https://github.com/kentcdodds/cross-env) that solves this issue, but I have not used it as I primarily develop on a unix system.
```bash
set NODE_ENV=production&&webpack -p
```
I'm not going to discuss the specific plugins I use as they change quite often, but a few of the production plugins are below. More information about these can be found in the webpack documentation.
```javascript
var debug = process.env.NODE_ENV !== 'production';
if (!debug){
plugins = plugins.concat([
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false})
])}
}
```
This environment variable can be passed to the application this way by using the webpack environment plugin.
```javascript
new webpack.EnvironmentPlugin([
"NODE_ENV"
])
```
`process.env.NODE_ENV` can then be checked for within the application itself. I currently use this to enable Redux logging for development.
```javascript
const debug = process.env.NODE_ENV !== 'production';
//run redux logger if we are in dev mode
const middleware = debug ? applyMiddleware(thunk, logger()) : applyMiddleware(thunk);
```
I was originally going to discuss build optimization here, but I think I will save that for another post. My JS bundle was almost 1mb and I got it down to a little less than 400kb.
## My current webpack configurations
Here is my webpack.config.js file at the time of writing this post. My current webpack config file can be found here. [webpack.config.js](https://github.com/mgerb/mywebsite/blob/master/webpack.config.js)
```javascript
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var autoprefixer = require('autoprefixer');
var Visualizer = require('webpack-visualizer-plugin');
module.exports = {
devtool: debug ? "inline-sourcemap" : null,
entry: ["babel-polyfill", "./client/js/app.js"],
module: {
loaders: [
{
test: /\.js?$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'],
}
},
{ test: /\.scss$/, loader: "style-loader!css-loader!postcss-loader!sass-loader"},
{ test: /\.css$/, loader: "style-loader!css-loader" },
{ test: /\.png$/, loader: "url-loader?limit=100000&name=images/[hash].[ext]" },
{ test: /\.jpg$/, loader: "url-loader?limit=100000&name=images/[hash].[ext]" },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml&name=images/[hash].[ext]"},
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff&name=fonts/[hash].[ext]"},
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff&name=fonts/[hash].[ext]"},
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream&name=fonts/[hash].[ext]"},
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file?name=fonts/[hash].[ext]"}
]
},
postcss: function(){ return [autoprefixer]},
output: {
path: __dirname + "/public/",
publicPath: "/public/",
filename: "client.min.js"
},
plugins: getPlugins(),
externals:{
"hljs": "hljs",
"react": "React",
"react-dom": "ReactDOM",
"react-router": "ReactRouter"
}
};
function getPlugins(){
var plugins = [
new HtmlWebpackPlugin({
fileName: 'index.html',
template: 'index.html',
inject: 'body',
hash: true
}),
new webpack.EnvironmentPlugin([
"NODE_ENV"
]),
new Visualizer({
filename: "../stats.html"
})
];
if(!debug){
plugins = plugins.concat([
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false})
])}
return plugins;
}
```

189
posts/extras/stats.html Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
# Minimizing JS bundle size with Webpack
I haven't been paying much attention to the bundle size of my JS files until recently I discovered it was almost 1mb (after minification)! Even after gzip the bundle was over 300kb. Although this site doesn't get much traffic this was unacceptable in my eyes.
## Why was it so big?
I had no idea why my bundle size was so huge because I wasn't using an large libraries or anything. I needed something like a profiler, or a way to analyze each mondule being loaded because I had no idea where to start.
## Webpack visualizer plugin
I came across this plugin and it is what saved me. I shows an interactive graph of each module being loaded along with sub modules. It was just what I needed! To my surprise, highlight.js was taking up almost 600kb (keep in mind webpack visualizer shows sizes before minification)

View File

@@ -24,5 +24,11 @@ func main(){
handle := gziphandler.GzipHandler(route.Routes()) handle := gziphandler.GzipHandler(route.Routes())
log.Println("Starting Server...") log.Println("Starting Server...")
log.Println(http.ListenAndServe(":"+strconv.Itoa(configurations.Port), handle)) go func(){
log.Println(http.ListenAndServe(":"+strconv.Itoa(configurations.Port), handle))
}()
if configurations.TLSCertFile != "" && configurations.TLSKeyFile != "" {
log.Println(http.ListenAndServeTLS(":"+strconv.Itoa(configurations.TLSPort), configurations.TLSCertFile, configurations.TLSKeyFile, handle))
}
} }

View File

@@ -12,9 +12,12 @@ import (
//structure for application configurations //structure for application configurations
type Config struct { type Config struct {
Database db.DatabaseInfo `json:"database"` Database db.DatabaseInfo `json:"Database"`
Api api.ApiInfo `json:"api"` Api api.ApiInfo `json:"Api"`
Port int `json:"port"` Port int `json:"Port"`
TLSPort int `json:"TLSPort"`
TLSCertFile string `json:"TLSCertFile"`
TLSKeyFile string `json:"TLSKeyFile"`
} }
//read the config file and return JsonObject struct //read the config file and return JsonObject struct