├── .gitignore ├── README.md ├── content ├── Assets.md ├── Authoring-libraries.md ├── Automatic-CSS-refresh.md ├── Automatic-browser-refresh.md ├── Basics.md ├── Configuring-react-js.md ├── Creating-a-dev-and-production-config.md ├── Custom-workflow-entry.md ├── Getting-started.md ├── Hot-loading-components.md ├── Inlining-fonts.md ├── Inlining-images.md ├── Introduction-to-React-JS.md ├── Introduction-to-Webpack.md ├── Isomorphic-app.md ├── Javascript-next.md ├── Lazy-loaded-entry-points.md ├── Loading-CSS.md ├── Loading-LESS-or-SASS.md ├── Loading-SVG.md ├── Matchers.md ├── Multiple-entry-points.md ├── Navigation.md ├── Optimizing-caching.md ├── Optimizing-development.md ├── Optimizing-rebundling.md ├── Optimizing-workflow.md ├── README.md ├── Requiring-files.md ├── Requiring-images-and-fonts.md ├── Running-a-workflow.md ├── SUMMARY.md ├── Single-bundle.md ├── Split-app-and-vendors.md ├── Structuring-configuration.md ├── Type-checking-with-flow.md ├── Understanding-chunks.md ├── Wing-It-Like-a-Pro.md ├── Writing-loaders.md ├── _Footer.md └── book.json ├── package.json └── scripts ├── deploy-gh-pages.js ├── deploy-wiki.sh └── generate-wiki.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | gh-pages/ 3 | wiki/ 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-webpack-cookbook - A cookbook for using Webpack with React JS. 2 | 3 | > [Go to cookbook](https://christianalfoni.github.io/react-webpack-cookbook/) 4 | 5 | > [中文版](https://fakefish.github.io/react-webpack-cookbook/) 6 | 7 | ## Contributing 8 | 9 | If you notice something to improve, the easiest way to do that is to 10 | 11 | 1. Fork this repo 12 | 2. Set up a branch 13 | 3. Make the changes (see `/content`) 14 | 4. Submit a PR 15 | 16 | So all in all it's just a regular GitHub PR flow. 17 | 18 | Alternatively you can [open an issue](https://github.com/christianalfoni/react-webpack-cookbook/issues/new) and we'll look into it. 19 | 20 | Note that `gh-pages` branch and wiki content gets generated based on the main repository content. 21 | 22 | ## Gitbook Generator 23 | 24 | The generator converts the wiki content to Gitbook (standalone site). In this case it is pushed to `gh-pages`. Use it as follows: 25 | 26 | 1. `npm install` 27 | 2. `npm run generate-gitbook` 28 | 29 | This should generate `/gh-pages`. You can serve that directory through some static server (ie. hit `serve` at `/gh-pages`). 30 | 31 | It is possible to deploy the book by hitting `npm run deploy-gitbook`. This will update `gh-pages` branch. 32 | -------------------------------------------------------------------------------- /content/Assets.md: -------------------------------------------------------------------------------- 1 | Historically web frontends have been split in three separate parts: structure markup (ie. HTML), style (ie. CSS) and logic (ie. JavaScript). You end up with something functional once you have these together. A well designed site should work even without JavaScript enabled and the content should make sense even without CSS. In practice this isn't always true and depending on your requirements you may be able to flex here. 2 | 3 | I won't go into detail to explain why we ended up with HTML, CSS and JavaScript. This is the triad what you'll build will most likely be using given that's what browsers support. In case of JavaScript you might compile down to something that's ES5 compatible and set up shims where needed. Shims can come in handy if you need to patch functionality for old browsers (IE 8 and such) and can help to an extent. A site such as [caniuse.com](http://caniuse.com/) can be helpful in figuring out what works and where and whether shims exist. 4 | 5 | ## CSS 6 | 7 | In its essence CSS is aspect oriented programming. It will allow you attach some behavior to selected features. Sometimes the boundary between CSS and JavaScript can be quite fuzzy and at times people have achieved amazing feats just by using CSS. That doesn't mean it should be used that way but it's always fun to push the envelope a bit. 8 | 9 | These days CSS incorporates a powerful selector syntax, media queries (customize per resolution), animation capabilities, whatnot. Depending on what you are doing you may whether love or hate its layout capabilities. Originally CSS was designed page layout in mind. These days we live more and more in the world of apps so you might need to struggle every once in a while a bit. 10 | 11 | Understanding the box model of CSS can help a lot. In addition the usage of CSS reset can be helpful. That eliminates some of the rendering differences between various browsers and provides more consistent results. In addition settings attributes such as `box-sizing: border-box;` can make the language more intuitive to use. 12 | 13 | > Instead of CSS we could be writing something like JavaScript, namely [JSS](https://en.wikipedia.org/wiki/JavaScript_Style_Sheets), if things had gone differently. 14 | 15 | ### Missing Features 16 | 17 | The core language is missing a lot of power programmers take granted. You won't have things such as variables or functions. This is the main reason why various preprocessors have appeared. They add some new functionality that has great potential to simplify your work as a developer. It isn't very [hard to write a system of your own](http://www.nixtu.info/2011/12/how-to-write-css-preprocessor-using.html). That said these days people like to stick with something like [Sass](http://sass-lang.com/), [Less](http://lesscss.org/) or [Stylus](http://learnboost.github.io/stylus/). Each to his own. 18 | 19 | Using webpack makes it easy to utilize these solutions. Often all you need to do is to set up a loader and then just `require` the assets you need. Images, fonts and such take extra setup but that's what this chapter is about so read on. 20 | 21 | ### Conventions 22 | 23 | There isn't single right way to develop CSS. As a result many approaches have appeared. Examples: [SMACSS](https://smacss.com/), [Suit CSS](http://suitcss.github.io/), [BEM](https://bem.info/). 24 | 25 | On macro level there are entire schools of design such as [Responsive Web Design](https://en.wikipedia.org/wiki/Responsive_web_design), Mobile First Design or [Atomic Web Design](http://bradfrost.com/blog/post/atomic-web-design/). 26 | 27 | With React we might be forced to rethink some of our approaches as highlighted by [a presentation by @vjeux](https://speakerdeck.com/vjeux/react-css-in-js). There are already tools such as [react-style](https://github.com/js-next/react-style) and [JSS](https://github.com/jsstyles/jss) which allow us to author CSS within components. 28 | 29 | You could say moving declarations back to markup is backwards. After all, inline styles have been frowned upon a long time. Every once in a while it is a good idea to challenge the dogma and move ahead. Maybe it can be useful to have all relevant information on component level. Even if you have basic style declarations on component level this doesn't mean you couldn't inject customizations from higher level if you design your components right. 30 | 31 | Of course there are questions such are these kind of ideas compatible with CSS frameworks such as [Bootstrap](http://getbootstrap.com/), [Foundation](http://foundation.zurb.com/) or [Pure](http://purecss.io/) but that's another topic. 32 | 33 | ## Fonts 34 | 35 | The simplest way to make your page look nicer is to set 36 | 37 | ```css 38 | body { 39 | font-family: Sans-Serif; 40 | } 41 | ``` 42 | 43 | Of course CSS provides [a ton of other options](https://developer.mozilla.org/en/docs/Web/CSS/font). Even more interesting these days we have [web fonts](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face) which provide additional options for designers. 44 | 45 | ## Images 46 | 47 | A large amount of content we consume is image based. Especially thanks to the introduction of high resolution displays on mobile devices, there are additional challenges available. You could say the situation is still not ideal but we are getting there thanks to initiatives such as [Responsive Images Community Group](http://responsiveimages.org/). It will take a little bit of effort to serve the correct images for various devices but in the end if you want to provide the best service, you should go for the extra mile. 48 | -------------------------------------------------------------------------------- /content/Authoring-libraries.md: -------------------------------------------------------------------------------- 1 | Webpack can be handy for packaging your library for general consumption. You can use it to output UMD, a format that's compatible with various module loaders (CommonJS, AMD) and globals. 2 | 3 | ## How can I output UMD for my library? 4 | 5 | Especially if you are creating a library, it can be useful to output an UMD version of your library. This can be achieved using the following snippet: 6 | 7 | ```javascript 8 | output: { 9 | path: './dist', 10 | filename: 'mylibrary.js', 11 | libraryTarget: 'umd', 12 | library: 'MyLibrary', 13 | }, 14 | ``` 15 | 16 | In order to avoid bundling big dependencies like React, you'll want to use a configuration like this in addition: 17 | 18 | ```javascript 19 | externals: { 20 | react: 'react', 21 | 'react/addons': 'react' 22 | }, 23 | ``` 24 | 25 | ## How can I output a minified version of my library? 26 | 27 | Here's the basic idea: 28 | 29 | ```javascript 30 | output: { 31 | path: './dist', 32 | filename: 'awesomemular.min.js', 33 | libraryTarget: 'umd', 34 | library: 'Awesomemular', 35 | }, 36 | plugins: [ 37 | new webpack.optimize.UglifyJsPlugin({ 38 | compress: { 39 | warnings: false 40 | }, 41 | }), 42 | ] 43 | ``` -------------------------------------------------------------------------------- /content/Automatic-CSS-refresh.md: -------------------------------------------------------------------------------- 1 | When **webpack-dev-server** is running with [Automatic browser refresh](Automatic-browser-refresh) the CSS will also update, but a bit differently. When you do a change to a CSS file the style tag belonging to that file will be updated with the new content... without a refresh! -------------------------------------------------------------------------------- /content/Automatic-browser-refresh.md: -------------------------------------------------------------------------------- 1 | When **webpack-dev-server** is running it will watch your files for changes. When that happens it rebundles your project and notifies browsers listening to refresh. To trigger this behavior you need to change your *index.html* file in the `build/` folder. 2 | 3 | **build/index.html** 4 | 5 | ```html 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ``` 16 | 17 | We added a script that refreshes the application when a change occurs. You will also need to add an entry point to your configuration: 18 | 19 | **webpack.config.js** 20 | 21 | ```javascript 22 | var path = require('path'); 23 | 24 | module.exports = { 25 | entry: [ 26 | 'webpack/hot/dev-server', 27 | 'webpack-dev-server/client?http://localhost:8080', 28 | path.resolve(__dirname, 'app/main.js') 29 | ], 30 | output: { 31 | path: path.resolve(__dirname, 'build'), 32 | filename: 'bundle.js', 33 | }, 34 | }; 35 | ``` 36 | 37 | Thats it! Now your application will automatically refresh on file changes. 38 | 39 | ## Default environment 40 | 41 | In the example above we created our own *index.html* file to give more freedom and control. It is also possible to run the application from **http://localhost:8080/webpack-dev-server/bundle**. This will fire up a default *index.html* file that you do not control. It also fires this file up in an iFrame allowing for a status bar to indicate the status of the rebundling process. 42 | 43 | > I discuss an alternative, `inline` based approach at the [Developing with Webpack](http://survivejs.com/webpack_react/developing_with_webpack/) chapter of *SurviveJS - Webpack and React*. 44 | -------------------------------------------------------------------------------- /content/Basics.md: -------------------------------------------------------------------------------- 1 | ## Why use Webpack? 2 | - Minimal configuration for the common workflow 3 | - Analyzes your project code as chunks that can be rearranged to optimize user experience 4 | - Especially useful with React JS 5 | - Powerful features like automatically inlining images and fonts 6 | - ES6 support right out of the box -------------------------------------------------------------------------------- /content/Configuring-react-js.md: -------------------------------------------------------------------------------- 1 | ## Installing React JS 2 | 3 | `npm install react --save` 4 | 5 | There is really nothing more to it. You can now start using React JS in your code. 6 | 7 | ## Using React JS in the code 8 | 9 | **component.jsx** 10 | 11 | ```javascript 12 | import React from 'react'; 13 | 14 | export default class Hello extends React.Component { 15 | render() { 16 | return

Hello world

; 17 | } 18 | } 19 | ``` 20 | 21 | **main.js** 22 | 23 | ```javascript 24 | import React from 'react'; 25 | import Hello from './component.jsx'; 26 | 27 | main(); 28 | 29 | function main() { 30 | React.render(, document.getElementById('app')); 31 | } 32 | ``` 33 | 34 | **build/index.html** 35 | 36 | ```javascript 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 47 | 48 | 49 | ``` 50 | 51 | ## Converting JSX 52 | 53 | To use the JSX syntax you will need webpack to transform your JavaScript. This is the job of a loader. We'll use [Babel](https://babeljs.io/) as it's nice and has plenty of features. 54 | 55 | `npm install babel-loader --save-dev` 56 | 57 | Now we have to configure webpack to use this loader. 58 | 59 | *webpack.config.js* 60 | ```javascript 61 | var path = require('path'); 62 | var config = { 63 | entry: path.resolve(__dirname, 'app/main.js'), 64 | output: { 65 | path: path.resolve(__dirname, 'build'), 66 | filename: 'bundle.js' 67 | }, 68 | module: { 69 | loaders: [{ 70 | test: /\.jsx?$/, // A regexp to test the require path. accepts either js or jsx 71 | loader: 'babel' // The module to load. "babel" is short for "babel-loader" 72 | }] 73 | } 74 | }; 75 | 76 | module.exports = config; 77 | ``` 78 | 79 | Webpack will test each path required in your code. In this project we are using ES6 module loader syntax, which means that the require path of `import MyComponent from './Component.jsx';` is `'./Component.jsx'`. 80 | 81 | Run `npm run dev` in the console and refresh the page to see something. 82 | -------------------------------------------------------------------------------- /content/Creating-a-dev-and-production-config.md: -------------------------------------------------------------------------------- 1 | - Split configuration into two files -------------------------------------------------------------------------------- /content/Custom-workflow-entry.md: -------------------------------------------------------------------------------- 1 | When running your workflow from **http://localhost:8080/web-dev-server/bundle** you do not control the *index.html* file where the scripts are loaded. 2 | 3 | ## Running your own index.html file 4 | In your *package.json* you have your *dev* script. `"webpack-dev-server --devtool eval --progress --colors --content-base build/"`. The *--content-base build/* parameter tells webpack-dev-server where to load your application from. In this example that would be `build/`. 5 | 6 | ## Create the index.html file 7 | In the `build/` folder create a new *index.html* with this content. -------------------------------------------------------------------------------- /content/Getting-started.md: -------------------------------------------------------------------------------- 1 | > Before getting started you should make sure you have a recent version of Node.js and NPM installed. See [nodejs.org](http://nodejs.org/) for installation details. We'll use NPM to set up various tools. 2 | 3 | Getting started with Webpack is straightforward. I'll show you how to set up a simple project based on it. As a first step, set a directory for your project and hit `npm init` and fill in some answers. That will create a `package.json` for you. Don't worry if some fields don't look ok, you can modify those later. 4 | 5 | ## Installing Webpack 6 | 7 | Next you should get Webpack installed. We'll do a local install and save it as a project dependency. This way you can invoke the build anywhere (build server, whatnot). Run `npm i webpack --save-dev`. If you want to run the tool, hit `node_modules/.bin/webpack`. 8 | 9 | ## Directory Structure 10 | 11 | Structure your project like this: 12 | 13 | - /app 14 | - main.js 15 | - component.js 16 | - /build 17 | - bundle.js (automatically created) 18 | - index.html 19 | - package.json 20 | - webpack.config.js 21 | 22 | In this case we'll create `bundle.js` using Webpack based on our `/app`. To make this possible, let's set up `webpack.config.js`. 23 | 24 | ## Creating Webpack Configuration 25 | 26 | In our case a basic configuration could look like this: 27 | 28 | *webpack.config.js* 29 | 30 | ```javascript 31 | var path = require('path'); 32 | 33 | 34 | module.exports = { 35 | entry: path.resolve(__dirname, 'app/main.js'), 36 | output: { 37 | path: path.resolve(__dirname, 'build'), 38 | filename: 'bundle.js', 39 | }, 40 | }; 41 | ``` 42 | 43 | ## Running Your First Build 44 | 45 | Now that we have basic configuration in place, we'll need something to build. Let's start with a classic `Hello World` type of app. Set up `/app` like this: 46 | 47 | *app/component.js* 48 | 49 | ```javascript 50 | 'use strict'; 51 | 52 | 53 | module.exports = function () { 54 | var element = document.createElement('h1'); 55 | 56 | element.innerHTML = 'Hello world'; 57 | 58 | return element; 59 | }; 60 | ``` 61 | 62 | *app/main.js* 63 | 64 | ```javascript 65 | 'use strict'; 66 | var component = require('./component.js'); 67 | 68 | 69 | document.body.appendChild(component()); 70 | 71 | ``` 72 | 73 | Now run `webpack` in your terminal and your application will be built. A *bundle.js* file will appear in your `/build` folder. Your *index.html* file in the `build/` folder will need to load up the application. 74 | 75 | *build/index.html* 76 | 77 | ```html 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | ``` 88 | 89 | > It would be possible to generate this file with Webpack using [html-webpack-plugin](https://www.npmjs.com/package/html-webpack-plugin). You can give it a go if you are feeling adventurous. It is mostly a matter of configuration. Generally this is the way you work with Webpack. 90 | 91 | ## Running the Application 92 | 93 | Just double-click the *index.html* file or set up a web server pointing to the `build/` folder. 94 | 95 | ## Setting Up `package.json` *scripts* 96 | 97 | It can be useful to run build, serve and such commands through `npm`. That way you don't have to worry about the technology used in the project. You just invoke the commands. This can be achieved easily by setting up a `scripts` section to `package.json`. 98 | 99 | In this case we can move the build step behind `npm run build` like this: 100 | 101 | 1. `npm i webpack --save` - If you want to install Webpack just a development dependency, you can use `--save-dev`. This is handy if you are developing a library and don't want it to depend on the tool (bad idea!). 102 | 2. Add the following to `package.json`: 103 | 104 | ```json 105 | "scripts": { 106 | "build": "webpack" 107 | } 108 | ``` 109 | 110 | To invoke a build, you can hit `npm run build` now. 111 | 112 | Later on this approach will become more powerful as project complexity grows. You can hide the complexity within `scripts` while keeping the interface simple. 113 | 114 | The potential problem with this approach is that it can tie you to a Unix environment in case you use environment specific commands. If so, you may want to consider using something environment agnostic, such as [gulp-webpack](https://www.npmjs.com/package/gulp-webpack). 115 | 116 | > Note that NPM will find Webpack. `npm run` adds it to the `PATH` temporarily so our simple incantation will work. -------------------------------------------------------------------------------- /content/Hot-loading-components.md: -------------------------------------------------------------------------------- 1 | So this part is just freakin' awesome. With React JS and the react-hot-loader you can change the class code of your component and see the instances update live in the DOM, without losing their state! This is pretty much exactly how CSS updates behave, only that it is your components. 2 | 3 | ## Setting it up 4 | This setup requires that you use the **webpack-dev-server** as introduced in earlier chapters. Now we just have to install the loader with `npm install react-hot-loader --save-dev`, do a small config change: 5 | 6 | ```javascript 7 | var webpack = require('webpack'); 8 | var path = require('path'); 9 | 10 | var config = { 11 | entry: ['webpack/hot/dev-server', './app/main.js'], 12 | output: { 13 | path: path.resolve(__dirname, './build'), 14 | filename: 'bundle.js' 15 | }, 16 | module: { 17 | loaders: [{ 18 | test: /\.js$/, 19 | 20 | // Use the property "loaders" instead of "loader" and 21 | // add "react-hot" in front of your existing "jsx" loader 22 | loaders: ['react-hot', 'babel'] 23 | }] 24 | } 25 | }; 26 | 27 | module.exports = config; 28 | ``` 29 | 30 | And you will also need a small snippet of code in your main entry file. In the example above that would be the *main.js* file located in the `app/` folder. 31 | 32 | *app/main.js* 33 | ```javascript 34 | // You probably already bring in your main root component, 35 | // maybe it is your component using react-router 36 | var RootComponent = require('./RootComponent.jsx'); 37 | 38 | // When you render it, assign it to a variable 39 | var rootInstance = React.render(RootComponent(), document.body); 40 | 41 | // Then just copy and paste this part at the bottom of 42 | // the file 43 | if (module.hot) { 44 | require('react-hot-loader/Injection').RootInstanceProvider.injectProvider({ 45 | getRootInstances: function () { 46 | // Help React Hot Loader figure out the root component instances on the page: 47 | return [rootInstance]; 48 | } 49 | }); 50 | } 51 | 52 | ``` 53 | 54 | It is that simple. Render a component to the DOM and make a code change on the component's class. It will render itself again, keeping the existing state. Cool? 55 | 56 | Read more about the [react-hot-loader](http://gaearon.github.io/react-hot-loader/getstarted/). 57 | -------------------------------------------------------------------------------- /content/Inlining-fonts.md: -------------------------------------------------------------------------------- 1 | Fonts can be really difficult to get right. First of all we have typically 4 different formats, but only one of them will be used by the respective browser. You do not want to inline all 4 formats, as that will just bloat your CSS file and in no way be an optimization. 2 | 3 | ## Choose one format 4 | 5 | Depending on your project you might be able to get away with one font format. If you exclude Opera Mini, all browsers support the .woff and .svg format. The thing is that fonts can look a little bit different in the different formats, on the different browsers. So try out .woff and .svg and choose the one that looks the best in all browsers. 6 | 7 | There are probably other strategies here too, so please share by creating an issue or pull request. 8 | 9 | ## Doing the actual inlining 10 | 11 | You do this exactly like you do when inlining images. 12 | 13 | ```javascript 14 | var path = require('path'); 15 | var config = { 16 | entry: path.resolve(__dirname, 'app/main.js') 17 | output: { 18 | path: path.resolve(__dirname, 'build'), 19 | filename: 'bundle.js' 20 | }, 21 | module: { 22 | loaders: [{ 23 | test: /\.jsx$/, 24 | loader: 'babel' 25 | }, { 26 | test: /\.woff$/, 27 | loader: 'url?limit=100000' 28 | }] 29 | } 30 | }; 31 | ``` 32 | 33 | Just make sure you have a limit above the size of the fonts, or they will of course not be inlined. 34 | -------------------------------------------------------------------------------- /content/Inlining-images.md: -------------------------------------------------------------------------------- 1 | Until HTTP/2 is here you want to avoid setting up too many HTTP requests when your application is loading. Depending on the browser you have a set number of requests that can run in parallel. If you load a lot of images in your CSS it is possible to automatically inline these images as BASE64 strings to lower the number of requests required. This can be based on the size of the image. There is a balance of size of download and number of downloads that you have to figure out for your project, and Webpack makes that balance easy to adjust. 2 | 3 | ## Installing the url-loader 4 | `npm install url-loader --save-dev` will install the loader that can convert resolved paths as BASE64 strings. As mentioned in other sections of this cookbook Webpack will resolve "url()" statements in your CSS as any other require or import statements. This means that if we test on image file extensions for this loader we can run them through it. 5 | 6 | ```javascript 7 | var path = require('path'); 8 | var config = { 9 | entry: path.resolve(__dirname, 'app/main.js') 10 | output: { 11 | path: path.resolve(__dirname, 'build'), 12 | filename: 'bundle.js' 13 | }, 14 | module: { 15 | loaders: [{ 16 | test: /\.jsx$/, 17 | loader: 'babel' 18 | }, { 19 | test: /\.(png|jpg)$/, 20 | loader: 'url?limit=25000' 21 | }] 22 | } 23 | }; 24 | ``` 25 | 26 | The limit is an argument passed to the url-loader. It tells it that images that are 25KB or smaller in size will be converted to a BASE64 string and included in the CSS file where it is defined. 27 | -------------------------------------------------------------------------------- /content/Introduction-to-React-JS.md: -------------------------------------------------------------------------------- 1 | I remember when I saw React the first time around the time it was announced I was skeptical. Particularly mixing some sort of HTML within your code seemed against good conventions. It just felt like a "bad idea"®. 2 | 3 | But that's what React and similar approaches are doing. They challenge some of the conventions and replace them with something more palatable. Sometimes a bigger change in thinking is needed for you to move forward as a developer. That's what React did for me. It takes some powerful ideas from the world of functional programming and then builds on top of those. 4 | 5 | ## Basic Features 6 | 7 | Before you can understand React and how it changes web development, there are a few things you should know about it. React itself won't be enough. It solves only the problem of views. You still need to complement it with something else. But that's a good thing. 8 | 9 | The greatest and worst feature of frameworks is that they sort of cage you in. As long as you are doing what they expect you to do within their boundaries, everything is fine. It is only after you start to reach beyond those boundaries that problems begin to appear. In a library driven approach you aren't as bound. Initially you might not be as fast or efficient but over time as problems become harder, you will have more choices available. 10 | 11 | ### Basics of JSX 12 | 13 | React provides a component centric approach to frontend development. You will design your application as smaller components, each of which has it own purpose. Taken to the extreme a component may contain its logic, layout and basic styling. To give you an example of JSX: 14 | 15 | ```html 16 | ... 17 | 18 | ... 19 | ``` 20 | 21 | You can see a couple of basic features of JSX here. Instead of using `class`, we'll use the JavaScript equivalent. In addition we have defined a couple of custom properties in form of `owner` and `task`. `owner` is something that is injected from a variable named `owner` that's within the same scope as our JSX. For `task` we provide a fixed value. 22 | 23 | In practice you would most likely structure this a little differently to fit your data model better. That goes a little beyond basic React, though. 24 | 25 | We can mix normal JavaScript code within those \{\}'s. We can use this idea to render a list of `TodoItem`s like this (ES syntax): 26 | 27 | ```html 28 | 31 | ``` 32 | 33 | You probably noticed something special here. What is that `key` property about? It is something that tells React the exact ordering of your items. If you don't provide unique keys for list items like this, React will warn you as it won't be able to guarantee the correct ordering otherwise. 34 | 35 | This has to do with the fact that React actually implements something known as Virtual DOM (VDOM for short) on top of actual DOM. It is a subset of DOM that allows React to optimize its rendering. The primary advantage of this approach is that it allows React to eschew a lot of legacy our good old DOM has gained through years. This is the secret to React's high performance. 36 | 37 | ### Entire Component 38 | 39 | To give you a better idea of what components look like, let's expand our `TodoItem` example into code (ES6 + JSX). I've done this below and will walk you through it: 40 | 41 | ```javascript 42 | var React = require('react'); 43 | 44 | 45 | module.exports = React.createClass({ 46 | getInitialState() { 47 | return { 48 | // let's keep track of how many times we have liked the item 49 | likes: 0, 50 | }; 51 | }, 52 | render() { 53 | var owner = this.props.owner; 54 | var task = this.props.task; 55 | var likes = this.state.likes; 56 | 57 | return
58 | {owner} 59 | {task} 60 | {likes} 61 | Like 62 |
; 63 | }, 64 | like() { 65 | this.setState({ 66 | likes: this.state.likes + 1 67 | }); 68 | }, 69 | }); 70 | ``` 71 | 72 | You can see some basic features of a React component above. First we create a class for our component. After that we define some initial state for it, then we render and finally we define some custom callbacks for our handlers if they exist. In this case I decided to implement an extra feature, liking. The current implementation just keeps track of the amount of likes per component. 73 | 74 | In practice you would transmit like amounts to a backend and add some validation there but this is a good starting point for understanding how state works in React. 75 | 76 | `getInitialState` and `render` are a part of a [React component's lifecycle as documented officially](http://facebook.github.io/react/docs/component-specs.html). There are additional hooks that allow you to do things like set up adapters for `jQuery` plugins and such. 77 | 78 | In this example CSS naming has been modeled after [Suit CSS](http://suitcss.github.io/) conventions as those look clean to me. That's just one way to deal with it. 79 | 80 | ### Dealing with Manipulation 81 | 82 | Let's say we want to modify the owner of our TodoItems. For the sake of simplicity let's expect it's just a string and owner is the same for all TodoItems. Based on this design it would make sense to have an input for owner at our user interface. A naive implementation would look something like this: 83 | 84 | ```javascript 85 | var React = require('react'); 86 | var TodoItem = require('./TodoItem.jsx'); 87 | 88 | 89 | module.exports = React.createClass({ 90 | getInitialState() { 91 | return { 92 | todoItems: [ 93 | { 94 | task: 'Learn React', 95 | }, 96 | { 97 | task: 'Learn Webpack', 98 | }, 99 | { 100 | task: 'Conquer World', 101 | } 102 | ], 103 | owner: 'John Doe', 104 | }; 105 | }, 106 | 107 | render() { 108 | var todoItems = this.state.todoItems; 109 | var owner = this.state.owner; 110 | 111 | return
112 |
113 | 114 |
115 | 116 |
117 |
    {todoItems.map((todoItem, i) => 118 |
  • 119 | 120 |
  • 121 | )}
122 |
123 |
; 124 | }, 125 | 126 | updateOwner() { 127 | this.setState({ 128 | owner: e.target.value, 129 | }); 130 | }, 131 | }); 132 | ``` 133 | 134 | We could push `TodoItems` and `ChangeOwner` to separate components but I've kept it all in the same for now. Given React has one way binding by default, we get some extra noise compared to some other setups. React provides [ReactLink](http://facebook.github.io/react/docs/two-way-binding-helpers.html) helper to help deal with this particular case. 135 | 136 | Even though lack of two way binding might sound like a downer, it actually isn't that bad a thing. It makes it easier to reason about the system. You simply have to follow the flow. This idea is highlighted in the Flux architecture. The easiest way to visualize it is to think up an infinite waterfall or a snake eating its tail. That's how the flow in the world of React generally works. Compared to this two way binding feels more chaotic. 137 | 138 | ### Using a Mixin 139 | 140 | If we wanted to model the code above using a ReactLink, we would end up with something like this: 141 | 142 | ```javascript 143 | // ReactLink is an addon so we have to load addons 144 | var React = require('react/addons'); 145 | 146 | ... 147 | 148 | module.exports = React.createClass({ 149 | mixins: [React.addons.LinkedStateMixin], 150 | 151 | ... 152 | 153 | render() { 154 | var todoItems = this.state.todoItems; 155 | 156 | return
157 |
158 | 159 |
160 | 161 |
162 |
    {todoItems.map((todoItem, i) => 163 |
  • 164 | 165 |
  • 166 | )}
167 |
168 |
; 169 | }, 170 | }); 171 | ``` 172 | 173 | Now we can skip that `onChange` handler. That `React.addons.LinkedStateMixin` encapsulates the logic. [Mixins](http://facebook.github.io/react/docs/reusable-components.html#mixins) provide us one way to encapsulate shared concerns such as this into something which can be reused easily. 174 | 175 | > It would be easy to start expanding the example now. You could for instance provide means to manipulate the contents of the Todo list or start extracting various parts into components of their own. It is up to you to make the app yours. If you are still feeling a bit lost, please read on. This is supposed to be a brief introduction to the topic! 176 | 177 | ### Testing 178 | 179 | If you get serious about the Todo app, I recommend trying [Jest](https://facebook.github.io/jest/) out. Getting the initial test run might be a bit challenging but after you learn the basics of the API, it gets a lot simpler. The basic idea is that you instantiate a component with some properties and then query DOM using Jest and finally assert that the values in the UI are what you expect. 180 | 181 | When you go beyond component level, that is where tools such as Selenium come in. You can use standard end to end testing tools on a higher level. 182 | 183 | ## Flux Architecture and Variants 184 | 185 | As you saw above, it is quite simple to throw together a couple of components and start building an app. You can get quite far with `props` and `state`. Just load up some data over AJAX at `getInitialState` and pass it around. After a while this all might start feeling a bit unwieldy. Why, for instance, my components should have to know something about how to communicate with the backend? 186 | 187 | This is where Flux architecture and its variants come in. I will start by describing [Reflux](https://github.com/spoike/refluxjs), a simplified variant of it. You can then work up to [understanding Flux](http://facebook.github.io/flux/docs/overview.html) in fuller detail once you understand this simplified setup. 188 | 189 | In addition to View Components which we just discussed, Reflux introduces the concepts of Actions and Stores. The flow goes like this: Components -> Actions -> Stores -> Components. Ie. you could have some control in a Component which then triggers some Action which then performs some operation (say PUT) and updates Store state. This state change is then propagated to Components listening to the Store. 190 | 191 | In case of our Todo example we would define basic `TodoActions` like create, update, delete and such. We would also have a `TodoStore`. It would maintain a data structure of a `TodoList`. Our components would then consume that data and display it as appropriate. 192 | 193 | As development of Reflux is quite in flux I won't give you a full example in this case. I just wanted to illustrate one possible way to deal with scaling up from bare React. You should explore various options and deepen your understanding of possible architectures. The ideas are quite similar but the devil is in the details as always. There are always drawbacks to consider. 194 | 195 | ### Isomorphic Rendering 196 | 197 | One of the big features which React provides thanks to its design is so called isomorphic rendering. Back in the day we used to render whole HTML in the backend and provide just that for the client to render. Then we would sprinkle a little JavaScript magic to make things more interactive and so on. After a while the pendulum swung to frontend side. We served minimal amount of HTML to the client and constructed the rest, including routing, using JavaScript entirely on frontend. 198 | 199 | The main problems with frontend driven rendering have to do with performance, high dependency on JavaScript (think of the noscript folk!) and poor SEO. With isomorphic rendering you can mitigate these problems effectively. React allows you to prerender HTML at backend side. You can also hydrate some stores with pre-existing data making it possible to skip certain data queries altogether initially! Even web crawlers will be happy as they get some HTML to scrape. 200 | 201 | This is still partly uncharted territory. Various implementations of Flux still struggle with the concept. I have no doubt we will see stronger solutions in the future, however, as people learn to deal with isomorphism better. That said isomorphic rendering can be considered a nice extra capability to have but it definitely isn't something that's just must have. There are some ways to work around certain issues, such as poor SEO, even without it. It just depends on where you want to put the effort. 202 | -------------------------------------------------------------------------------- /content/Introduction-to-Webpack.md: -------------------------------------------------------------------------------- 1 | In web development we deal with a lot of small technical artifacts. You use HTML to describe page structure, CSS how to style it and JavaScript for logic. Or you can replace HTML with something like Jade, CSS with Sass or LESS, JavaScript with CoffeeScript, TypeScript and the ilk. In addition you have to deal with project dependencies (ie. external libraries and such). 2 | 3 | There are good reasons why we use these various technologies. Regardless of what we use, however, we still want to end up with something that can be run on the browsers of the clients. This is where build systems come in. Historically speaking there have been many. [Make](https://en.wikipedia.org/wiki/Make_%28software%29) is perhaps the most known one and still a viable option in many cases. In the world of frontend development particularly [Grunt](http://gruntjs.com/) and [Gulp](http://gulpjs.com/) have gained popularity. Both are made powerful by plugins. [NPM](https://www.npmjs.com/), the Node.js package manager, is full of those. 4 | 5 | ## Grunt 6 | 7 | Grunt is the older project. It relies on plugin specific configuration. This is fine up to a point but believe me, you don't want to end up having to maintain a 300 line `Gruntfile`. The approach simply turns against itself at some point. Just in case you are curious what the configuration looks like, here's an example from [Grunt documentation](http://gruntjs.com/sample-gruntfile): 8 | 9 | ```javascript 10 | module.exports = function(grunt) { 11 | 12 | grunt.initConfig({ 13 | jshint: { 14 | files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'], 15 | options: { 16 | globals: { 17 | jQuery: true 18 | } 19 | } 20 | }, 21 | watch: { 22 | files: ['<%= jshint.files %>'], 23 | tasks: ['jshint'] 24 | } 25 | }); 26 | 27 | grunt.loadNpmTasks('grunt-contrib-jshint'); 28 | grunt.loadNpmTasks('grunt-contrib-watch'); 29 | 30 | grunt.registerTask('default', ['jshint']); 31 | 32 | }; 33 | ``` 34 | 35 | ## Gulp 36 | 37 | Gulp takes a different approach. Instead of relying on configuration per plugin you deal with actual code. Gulp builds on top of the tried and true concept of piping. If you are familiar with Unix, it's the same here. You simply have sources, filters and sinks. In this case sources happen to match to some files, filters perform some operations on those (ie. convert to JavaScript) and then output to sinks (your build directory etc.). Here's a sample `Gulpfile` to give you a better idea of the approach taken from the project README and abbreviated a bit: 38 | 39 | ```javascript 40 | var gulp = require('gulp'); 41 | var coffee = require('gulp-coffee'); 42 | var concat = require('gulp-concat'); 43 | var uglify = require('gulp-uglify'); 44 | var sourcemaps = require('gulp-sourcemaps'); 45 | var del = require('del'); 46 | 47 | var paths = { 48 | scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee'], 49 | }; 50 | 51 | // Not all tasks need to use streams 52 | // A gulpfile is just another node program and you can use all packages available on npm 53 | gulp.task('clean', function(cb) { 54 | // You can use multiple globbing patterns as you would with `gulp.src` 55 | del(['build'], cb); 56 | }); 57 | 58 | gulp.task('scripts', ['clean'], function() { 59 | // Minify and copy all JavaScript (except vendor scripts) 60 | // with sourcemaps all the way down 61 | return gulp.src(paths.scripts) 62 | .pipe(sourcemaps.init()) 63 | .pipe(coffee()) 64 | .pipe(uglify()) 65 | .pipe(concat('all.min.js')) 66 | .pipe(sourcemaps.write()) 67 | .pipe(gulp.dest('build/js')); 68 | }); 69 | 70 | // Rerun the task when a file changes 71 | gulp.task('watch', function() { 72 | gulp.watch(paths.scripts, ['scripts']); 73 | }); 74 | 75 | // The default task (called when you run `gulp` from cli) 76 | gulp.task('default', ['watch', 'scripts']); 77 | ``` 78 | 79 | Given the configuration is code you can always just hack it if you run into troubles. You can wrap existing Node.js modules as Gulp plugins and so on. You still end up writing a lot of boilerplate for casual tasks, though. 80 | 81 | ## Browserify 82 | 83 | Dealing with JavaScript modules has always been a bit of a problem given the language actually doesn't have a concept of module till ES6. Ergo we are stuck with the 90s when it comes to browser environment. Various solutions, including [AMD](http://browserify.org/), have been proposed. In practice it can be useful just to use CommonJS, the Node.js format, and let tooling deal with the rest. The advantage is that you can often hook into NPM and avoid reinventing the wheel. 84 | 85 | [Browserify](http://browserify.org/) solves this problem. It provides a way to bundle CommonJS modules together. You can hook it up with Gulp. In addition there are tons of smaller transformation tools that allow you to move beyond the basic usage (ie. [watchify](https://www.npmjs.com/package/watchify) provides a file watcher that creates bundles for you during development automatically). This will save some effort and no doubt is a good solution up to a point. 86 | 87 | ## Webpack 88 | 89 | Webpack expands on the idea of hooking into CommonJS `require`. What if you could just `require` whatever you needed in your code, be it CoffeeScript, Sass, Markdown or something? Well, Webpack does just this. It takes your dependencies, puts them through loaders and outputs browser compatible static assets. All of this is based on configuration. Here is a sample configuration from [the official Webpack tutorial](http://webpack.github.io/docs/tutorials/getting-started/): 90 | 91 | ```javascript 92 | module.exports = { 93 | entry: "./entry.js", 94 | output: { 95 | path: __dirname, 96 | filename: "bundle.js" 97 | }, 98 | module: { 99 | loaders: [ 100 | { test: /\.css$/, loader: "style!css" } 101 | ] 102 | } 103 | }; 104 | ``` 105 | 106 | In the following sections we'll build on top of this idea and show how powerful it is. You can, and probably should, use Webpack with some other tools. It won't solve everything. It does solve the difficult problem of bundling, however, and that's one worry less during development. -------------------------------------------------------------------------------- /content/Isomorphic-app.md: -------------------------------------------------------------------------------- 1 | So the great thing about React JS is that it runs on the server too. But that does not mean you can just create any app and run it on the server. You have to make some decisions on the architecture. The reason is that even though React JS and the components run on the server, you might be having dependencies in those components that does not run on the server. 2 | 3 | ## Injecting state 4 | One of the most important decisions you make is to inject the state of your application through the top component. This basically means that your components does not have any external dependencies at all. All they need to know comes through this injected state. 5 | 6 | This cookbook is not about isomorphic apps, but let us take a look at an example. We will not use ES6 syntax here because Node JS does not support it yet. 7 | 8 | *main.js (client)* 9 | ```javascript 10 | var React = require('react'); 11 | var AppState = require('./client/AppState.js'); 12 | var App = require('./App.js'); 13 | 14 | React.render(, document.body); 15 | ``` 16 | 17 | *router.js (server)* 18 | ```javascript 19 | var React = require('react'); 20 | var App = require('./App.js'); 21 | var AppState = require('./server/AppState.js'); 22 | var index = '{{component}}'; 23 | 24 | app.get('/', function (req, res) { 25 | var componentHtml = React.renderToString(App({state: AppState})); 26 | var html = index.replace('{{component}}', componentHtml); 27 | res.type('html'); 28 | res.send(html); 29 | }); 30 | ``` 31 | 32 | So this was a very naive and simple way of showing it, but what you should notice here is that we use the same **App.js** file on the client and server, but we have two different ways of producing the state. 33 | -------------------------------------------------------------------------------- /content/Javascript-next.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 | As of React JS 0.13 you will be able to define components as classes. 4 | 5 | ```javascript 6 | class MyComponent extends React.Component { 7 | constructor() { 8 | this.state = {message: 'Hello world'}; 9 | } 10 | render() { 11 | return ( 12 |

{this.state.message}

13 | ); 14 |  } 15 | } 16 | ``` 17 | 18 | This gives you a very short and nice syntax for defining components. A drawback with using classes though is the lack of mixins. That said, you are not totally lost. Lets us see how we could still use the important **PureRenderMixin**. 19 | 20 | ```javascript 21 | import React from 'react/addons'; 22 | 23 | class Component extends React.Component { 24 | shouldComponentUpdate() { 25 | return React.addons.PureRenderMixin.shouldComponentUpdate.apply(this, arguments); 26 | } 27 | } 28 | 29 | class MyComponent extends Component { 30 | constructor() { 31 | this.state = {message: 'Hello world'}; 32 | } 33 | render() { 34 | return ( 35 |

{this.state.message}

36 | ); 37 | } 38 | } 39 | ``` -------------------------------------------------------------------------------- /content/Lazy-loaded-entry-points.md: -------------------------------------------------------------------------------- 1 | It is also possible to lazy load entry points. This means that you load parts of your application as they are requested. A typical scenario for this would be that your users only visits specific parts of the application. And an example of that would be twitter.com. You do not always visit your profile page, so why load the code for that? Here is a summary of requirements: 2 | 3 | - You have a relatively big application where users can visit different parts of it 4 | - You do care a lot about initial render time 5 | 6 | *webpack.production.config.js* 7 | ```javascript 8 | var path = require('path'); 9 | var webpack = require('webpack'); 10 | var node_modules_dir = path.resolve(__dirname, 'node_modules'); 11 | 12 | var config = { 13 | entry: { 14 | app: path.resolve(__dirname, 'app/main.js'), 15 | vendors: ['react'] 16 | }, 17 | output: { 18 | path: path.resolve(__dirname, 'dist'), 19 | filename: 'app.js' 20 | }, 21 | module: { 22 | loaders: [{ 23 | test: /\.js$/, 24 | exclude: [node_modules_dir], 25 | loader: 'babel' 26 | }] 27 | }, 28 | plugins: [ 29 | new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js') 30 | ] 31 | }; 32 | 33 | module.exports = config; 34 | ``` 35 | So we are pretty much back where we started with a split application and vendors bundle. You do not really define your lazy dependencies in a configuration, Webpack automatically understands them when analyzing your code. So let us see how we would lazy load a **profile page**: 36 | 37 | *main.js (Using ES6 syntax)* 38 | ```javascript 39 | import React from 'react'; 40 | import Feed from './Feed.js'; 41 | 42 | class App extends React.Component { 43 | constructor() { 44 | this.state = { currentComponent: Feed }; 45 | } 46 | openProfile() { 47 | require.ensure([], () => { 48 | var Profile = require('./Profile.js'); 49 | this.setState({ 50 | currentComponent: Profile 51 | }); 52 | }); 53 | } 54 | render() { 55 | return ( 56 | return
{this.state.currentComponent()}
57 | ); 58 | } 59 | } 60 | React.render(, document.body); 61 | ``` 62 | So this is just an example. You would probably hook this up to a router, but the important part is using `require.ensure`. 63 | 64 | **What is the array on the first argument?**: If you try to lazy load a chunk that depends on an other lazy loaded chunk you can set it as a dependency in the array. Just type in the path to the chunk. E.g. `['./FunnyButton.js']` 65 | -------------------------------------------------------------------------------- /content/Loading-CSS.md: -------------------------------------------------------------------------------- 1 | Webpack allows you to load CSS like you load any other code. What strategy you choose is up to you, but you can do everything from loading all your css in the main entry point file to one css file for each component. 2 | 3 | Loading CSS requires the **css-loader** and the **style-loader**. They have two different jobs. The **css-loader** will go through the CSS file and find `url()` expressions and resolve them. The **style-loader** will insert the raw css into a style tag on your page. 4 | 5 | ## Preparing CSS loading 6 | 7 | Install the two loaders: `npm install css-loader style-loader --save-dev`. 8 | 9 | In the *webpack.config.js* file you can add the following loader configuration: 10 | 11 | **webpack.config.js** 12 | 13 | ```javascript 14 | var path = require('path'); 15 | var config = { 16 | entry: path.resolve(__dirname, 'app/main.js') 17 | output: { 18 | path: path.resolve(__dirname, 'build'), 19 | filename: 'bundle.js' 20 | }, 21 | module: { 22 | loaders: [{ 23 | test: /\.jsx$/, 24 | loader: 'babel' 25 | }, { 26 | test: /\.css$/, // Only .css files 27 | loader: 'style!css' // Run both loaders 28 | }] 29 | } 30 | }; 31 | 32 | module.exports = config; 33 | ``` 34 | 35 | ## Loading a CSS file 36 | 37 | Loading a CSS file is a simple as loading any file: 38 | 39 | **main.js** 40 | 41 | ```javascript 42 | import './main.css'; 43 | // Other code 44 | ``` 45 | 46 | **Component.jsx** 47 | 48 | ```javascript 49 | import './Component.css'; 50 | import React from 'react'; 51 | 52 | export default React.createClass({ 53 | render: function () { 54 | return

Hello world!

55 | } 56 | }); 57 | ``` 58 | 59 | **Note!** You can of course do this with both CommonJS and AMD. 60 | 61 | ## CSS loading strategies 62 | 63 | Depending on your application you might consider three main strategies. In addition to this you should consider including some of your basic CSS inlined with the initial payload (index.html). This will set the structure and maybe a loader while the rest of your application is downloading and executing. 64 | 65 | ### All in one 66 | 67 | In your main entry point, e.g. `app/main.js` you can load up your entire CSS for the whole project: 68 | 69 | **app/main.js** 70 | 71 | ```javascript 72 | import './project-styles.css'; 73 | // Other JS code 74 | ``` 75 | 76 | The CSS is included in the application bundle and does not need to download. 77 | 78 | ### Lazy loading 79 | 80 | If you take advantage of lazy loading by having multiple entry points to your application, you can include specific CSS for each of those entry points: 81 | 82 | **app/main.js** 83 | 84 | ```javascript 85 | import './style.css'; 86 | // Other JS code 87 | ``` 88 | 89 | **app/entryA/main.js** 90 | 91 | ```javascript 92 | import './style.css'; 93 | // Other JS code 94 | ``` 95 | 96 | **app/entryB/main.js** 97 | 98 | ```javascript 99 | import './style.css'; 100 | // Other JS code 101 | ``` 102 | 103 | You divide your modules by folders and include both CSS and JavaScript files in those folders. Again, the imported CSS is included in each entry bundle when running in production. 104 | 105 | ### Component specific 106 | 107 | With this strategy you create a CSS file for each component. It is common to namespace the CSS classes with the component name, thus avoiding some class of one component interfering with the class of an other. 108 | 109 | **app/components/MyComponent.css** 110 | 111 | ```css 112 | .MyComponent-wrapper { 113 | background-color: #EEE; 114 | } 115 | ``` 116 | 117 | **app/components/MyComponent.jsx** 118 | 119 | ``` 120 | import './MyComponent.css'; 121 | import React from 'react'; 122 | 123 | export default React.createClass({ 124 | render: function () { 125 | return ( 126 |
127 |

Hello world

128 |
129 | ) 130 | } 131 | }); 132 | ``` 133 | 134 | ## Using inline styles instead of stylesheets 135 | 136 | With "React Native" you do not use stylesheets at all, you only use the *style-attribute*. By defining your CSS as objects. Depending on your project, you might consider this as your CSS strategy. 137 | 138 | **app/components/MyComponent.jsx** 139 | 140 | ```javascript 141 | import React from 'react'; 142 | 143 | var style = { 144 | backgroundColor: '#EEE' 145 | }; 146 | 147 | export default React.createClass({ 148 | render: function () { 149 | return ( 150 |
151 |

Hello world

152 |
153 | ) 154 | } 155 | }); 156 | ``` 157 | -------------------------------------------------------------------------------- /content/Loading-LESS-or-SASS.md: -------------------------------------------------------------------------------- 1 | If you want to use compiled CSS, there are two loaders available for you. The **less-loader** and the **sass-loader**. Depending on your preference, this is how you set it up. 2 | 3 | ## Installing and configuring the loader 4 | 5 | `npm install less-loader` or `npm install sass-loader`. 6 | 7 | **webpack.config.js** 8 | 9 | ```javascript 10 | var path = require('path'); 11 | var config = { 12 | entry: path.resolve(__dirname, 'app/main.js') 13 | output: { 14 | path: path.resolve(__dirname, 'build'), 15 | filename: 'bundle.js' 16 | }, 17 | module: { 18 | loaders: [{ 19 | test: /\.jsx$/, 20 | loader: 'babel' 21 | }, 22 | 23 | // LESS 24 | { 25 | test: /\.less$/, 26 | loader: 'style!css!less' 27 | }, 28 | 29 | // SASS 30 | { 31 | test: /\.scss$/, 32 | loader: 'style!css!sass' 33 | }] 34 | } 35 | }; 36 | ``` 37 | 38 | ## What about imports in LESS and SASS? 39 | If you import one LESS/SASS file from an other, use the exact same pattern as anywhere else. Webpack will dig into these files and figure out the dependencies. 40 | 41 | ```less 42 | @import "./variables.less"; 43 | ``` 44 | 45 | You can also load LESS files directly from your node_modules directory. 46 | ```less 47 | $import "~bootstrap/less/bootstrap"; 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /content/Loading-SVG.md: -------------------------------------------------------------------------------- 1 | Webpack has a [few ways](https://github.com/webpack/webpack/issues/595) to load SVG. However the simplest way is through **file-loader**. 2 | 3 | ## Installation and configuration 4 | 5 | Install the loader: `npm install file-loader --save-dev`. 6 | 7 | In the webpack config file you can add the following loader configuration: 8 | 9 | **webpack.config.js** 10 | 11 | ```javascript 12 | var path = require('path'); 13 | var config = { 14 | entry: path.resolve(__dirname, 'app/main.js') 15 | output: { 16 | path: path.resolve(__dirname, 'build'), 17 | filename: 'bundle.js' 18 | }, 19 | module: { 20 | loaders: [{ 21 | test: /\.jsx$/, 22 | loader: 'babel' 23 | }, { 24 | test: /\.svg$/, 25 | loader: 'file-loader' 26 | }] 27 | } 28 | }; 29 | ``` 30 | 31 | Then in your CSS: 32 | 33 | ```css 34 | .icon { 35 | background-image: url(../assets/icon.svg); 36 | } 37 | ``` 38 | 39 | In this example the `assets` folder is relative to your CSS file. 40 | 41 | For SVG compression check out the [svgo-loader](https://github.com/pozadi/svgo-loader). -------------------------------------------------------------------------------- /content/Matchers.md: -------------------------------------------------------------------------------- 1 | ## What kind of matchers can I use? 2 | 3 | * `{ test: /\.js$/, loader: 'babel-loader' }` - Matches just .js 4 | * `{ test: /\.(js|jsx)$/, loader: 'babel-loader' }` - Matches both js and jsx 5 | * Generally put it's just a JavaScript regex so standard tricks apply -------------------------------------------------------------------------------- /content/Multiple-entry-points.md: -------------------------------------------------------------------------------- 1 | Maybe you are building an application that has multiple urls. An example of this would be a solution where you have two, or more, different URLs responding with different pages. Maybe you have one user page and one admin page. They both share a lot of code, but you do not want to load all the admin stuff for normal users. That is a good scenario for using multiple entry points. A list of use cases could be: 2 | 3 | - You have an application with multiple isolated user experiences, but they share a lot of code 4 | - You have a mobile version using less components 5 | - You have a typical user/admin application where you do not want to load all the admin code for a normal user 6 | 7 | Let us create an example with a mobile experience using less components: 8 | *webpack.production.config.js* 9 | ```javascript 10 | var path = require('path'); 11 | var webpack = require('webpack'); 12 | var node_modules_dir = path.resolve(__dirname, 'node_modules'); 13 | 14 | var config = { 15 | entry: { 16 | app: path.resolve(__dirname, 'app/main.js'), 17 | mobile: path.resolve(__dirname, 'app/mobile.js'), 18 | vendors: ['react'] // And other vendors 19 | }, 20 | output: { 21 | path: path.resolve(__dirname, 'dist'), 22 | filename: '[name].js' // Notice we use a variable 23 | }, 24 | module: { 25 | loaders: [{ 26 | test: /\.js$/, 27 | exclude: [node_modules_dir], 28 | loader: 'babel' 29 | }] 30 | }, 31 | plugins: [ 32 | new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js') 33 | ] 34 | }; 35 | 36 | module.exports = config; 37 | ``` 38 | This configuration will create three files in the `dist/` folder. **app.js**, **mobile.js** and **vendors.js**. Most of the code in the **mobile.js** file also exists in **app.js**, but that is what we want. We will never load **app.js** and **mobile.js** on the same page. -------------------------------------------------------------------------------- /content/Navigation.md: -------------------------------------------------------------------------------- 1 | 1. [something](/christianalfoni/react-webpack-cookbook/wiki/02.-Why-Webpack-and-React-JS%3F) -------------------------------------------------------------------------------- /content/Optimizing-caching.md: -------------------------------------------------------------------------------- 1 | When users hit the URL of your application they will need to download different assets. CSS, JavaScript, HTML, images and fonts. The great thing about Webpack is that you can stop thinking how you should download all these assets. You can do it through JavaScript. 2 | 3 | 4 | > OccurenceOrderPlugin 5 | 6 | 7 | ## How can I attach hashes to my production output? 8 | 9 | * Use `[hash]`. Example: `'assets/bundle.[hash].js'` 10 | 11 | The benefit of this is that this will force the client to reload the file. There is more information about `[hash]` at [the long term caching](http://webpack.github.io/docs/long-term-caching.html) section of the official documentation. 12 | 13 | > Is it possible to change the hash only if bundle changed? -------------------------------------------------------------------------------- /content/Optimizing-development.md: -------------------------------------------------------------------------------- 1 | We talked about how you could use the minified versions of your dependencies in development to make the rebundling go as fast as possible. Let us look at a small helper you can implement to make this a bit easier to handle. 2 | 3 | *webpack.config.js* 4 | ```javascript 5 | var webpack = require('webpack'); 6 | var path = require('path'); 7 | var node_modules_dir = path.join(__dirname, 'node_modules'); 8 | 9 | var deps = [ 10 | 'react/dist/react.min.js', 11 | 'react-router/dist/react-router.min.js', 12 | 'moment/min/moment.min.js', 13 | 'underscore/underscore-min.js', 14 | ]; 15 | 16 | var config = { 17 | entry: ['webpack/hot/dev-server', './app/main.js'], 18 | output: { 19 | path: path.resolve(__dirname, './build'), 20 | filename: 'bundle.js' 21 | }, 22 | resolve: { 23 | alias: {} 24 | }, 25 | module: { 26 | noParse: [], 27 | loaders: [] 28 | } 29 | }; 30 | 31 | // Run through deps and extract the first part of the path, 32 | // as that is what you use to require the actual node modules 33 | // in your code. Then use the complete path to point to the correct 34 | // file and make sure webpack does not try to parse it 35 | deps.forEach(function (dep) { 36 | var depPath = path.resolve(node_modules_dir, dep); 37 | config.resolve.alias[dep.split(path.sep)[0]] = depPath; 38 | config.module.noParse.push(depPath); 39 | }); 40 | 41 | module.exports = config; 42 | ``` 43 | Not all modules include a minified distributed version of the lib, but most do. Especially with large libraries like React JS you will get a significant improvement. 44 | 45 | ## Exposing React to the global scope 46 | You might be using distributed versions that requires React JS on the global scope. To fix that you can install the expose-loader by `npm install expose-loader --save-dev` and set up the following config, focusing on the *module* property: 47 | 48 | ```javascript 49 | var webpack = require('webpack'); 50 | var path = require('path'); 51 | var node_modules_dir = path.join(__dirname, 'node_modules'); 52 | 53 | var deps = [ 54 | 'react/dist/react.min.js', 55 | 'react-router/dist/react-router.min.js', 56 | 'moment/min/moment.min.js', 57 | 'underscore/underscore-min.js', 58 | ]; 59 | 60 | var config = { 61 | entry: ['webpack/hot/dev-server', './app/main.js'], 62 | output: { 63 | path: path.resolve(__dirname, './build'), 64 | filename: 'bundle.js' 65 | }, 66 | resolve: { 67 | alias: {} 68 | }, 69 | module: { 70 | noParse: [], 71 | 72 | // Use the expose loader to expose the minified React JS 73 | // distribution. For example react-router requires this 74 | loaders: [{ 75 | test: path.resolve(node_modules_dir, deps[0]), 76 | loader: "expose?React" 77 | }] 78 | } 79 | }; 80 | 81 | deps.forEach(function (dep) { 82 | var depPath = path.resolve(node_modules_dir, dep); 83 | config.resolve.alias[dep.split(path.sep)[0]] = depPath; 84 | config.module.noParse.push(depPath); 85 | }); 86 | 87 | module.exports = config; 88 | ``` -------------------------------------------------------------------------------- /content/Optimizing-rebundling.md: -------------------------------------------------------------------------------- 1 | You might notice after requiring React JS into your project that the time it takes from a save to a finished rebundle of your application takes more time. In development you ideally want from 200-800 ms rebundle speed, depending on what part of the application you are working on. 2 | 3 | > IMPORTANT! This setup a minified, production version of React. As a result you will lose `propTypes` based type validation! 4 | 5 | ## Running minified file in development 6 | 7 | Instead of making Webpack go through React JS and all its dependencies, you can override the behavior in development. 8 | 9 | **webpack.config.js** 10 | 11 | ```javascript 12 | var path = require('path'); 13 | var node_modules = path.resolve(__dirname, 'node_modules'); 14 | var pathToReact = path.resolve(node_modules, 'react/dist/react.min.js'); 15 | 16 | config = { 17 | entry: ['webpack/hot/dev-server', path.resolve(__dirname, 'app/main.js')], 18 | resolve: { 19 | alias: { 20 | 'react': pathToReact 21 | } 22 | }, 23 | output: { 24 | path: path.resolve(__dirname, 'build'), 25 | filename: 'bundle.js', 26 | }, 27 | module: { 28 | loaders: [{ 29 | test: /\.jsx?$/, 30 | loader: 'babel' 31 | }], 32 | noParse: [pathToReact] 33 | } 34 | }; 35 | 36 | module.exports = config; 37 | ``` 38 | 39 | We do two things in this configuration: 40 | 41 | 1. Whenever "react" is required in the code it will fetch the minified React JS file instead of going to *node_modules* 42 | 43 | 2. Whenever Webpack tries to parse the minified file, we stop it, as it is not necessary 44 | 45 | Take a look at [Optimizing development](Optimizing-development) for more information on this. 46 | -------------------------------------------------------------------------------- /content/Optimizing-workflow.md: -------------------------------------------------------------------------------- 1 | - Using bower files with noparse to improve rebundle speed -------------------------------------------------------------------------------- /content/README.md: -------------------------------------------------------------------------------- 1 | > [zh-cn](https://fakefish.github.io/react-webpack-cookbook/) 2 | 3 | > In case you want to contribute, please create a PR against [the main repo](https://github.com/christianalfoni/react-webpack-cookbook) or contact us through [the issue tracker](https://github.com/christianalfoni/react-webpack-cookbook/issues). 4 | 5 | The purpose of this cookbook is to guide you into the world of React and Webpack. Both are powerful technologies and when used together, frontend development becomes a joy. 6 | 7 | The cookbook should have something to offer for all skill levels. If you are interested in just React, skip the Webpack part and vice versa. A more tutorial oriented approach is available in the form of [SurviveJS - Webpack and React](http://survivejs.com/) book. 8 | 9 | ## React 10 | 11 | React is a library that greatly simplifies component development. Once you understand its basics, you can build your own stack around it. This is in contrast to frameworks, such as Angular, that try to offer everything and a kitchen sink. 12 | 13 | [The official tutorial](http://facebook.github.io/react/docs/tutorial.html) is a good starting point if you just want a quick look at the technology. 14 | 15 | Perhaps the most intriguing thing about React is that it keeps on challenging traditional web development. It allows us to rethink concepts such as separation of concern. It will also influence on mobile development in the form of [React Native](http://facebook.github.io/react-native). React Native allow native level performance while making it possible to develop apps in a tight realtime preview loop using JavaScript. 16 | 17 | ## Webpack 18 | 19 | Webpack operates on a lower level. It is a module bundler. In essence it is something that you use to build your project into deliverable components (HTML, CSS, JS). The nice thing about Webpack is that once you initially configure it, it deals with the nasty details for you. This allows you to mix various technologies within your project without a headache. 20 | 21 | If you are completely new to Webpack and want to go through a good introduction, check out [Pete Hunt's guide](https://github.com/petehunt/webpack-howto). You'll find the basics there. This guide merely complements his. 22 | 23 | ## Written by 24 | 25 | [Christian Alfoni](http://www.christianalfoni.com/) and [Juho Vepsäläinen](http://survivejs.com/). 26 | -------------------------------------------------------------------------------- /content/Requiring-files.md: -------------------------------------------------------------------------------- 1 | ## Modules 2 | 3 | Webpack allows you to use different module patterns, but "under the hood" they all work the same way. All of them also works straight out of the box. 4 | 5 | #### ES6 modules 6 | 7 | ```javascript 8 | import MyModule from './MyModule.js'; 9 | ``` 10 | 11 | #### CommonJS 12 | 13 | ```javascript 14 | var MyModule = require('./MyModule.js'); 15 | ``` 16 | 17 | #### AMD 18 | 19 | ```javascript 20 | define(['./MyModule.js'], function (MyModule) { 21 | 22 | }); 23 | ``` 24 | 25 | ## Understanding Paths 26 | 27 | A module is loaded by filepath. Imagine the following tree structure: 28 | 29 | - /app 30 | - /modules 31 | - MyModule.js 32 | - main.js (entry point) 33 | - utils.js 34 | 35 | Lets open up the *main.js* file and require *app/modules/MyModule.js* in the two most common module patterns: 36 | 37 | *app/main.js* 38 | ```javascript 39 | // ES6 40 | import MyModule from './modules/MyModule.js'; 41 | 42 | // CommonJS 43 | var MyModule = require('./modules/MyModule.js'); 44 | ``` 45 | 46 | The `./` at the beginning states "relative to the file I am in now". 47 | 48 | Now let us open the *MyModule.js* file and require **app/utils**. 49 | 50 | *app/modules/MyModule.js* 51 | ```javascript 52 | // ES6 relative path 53 | import utils from './../utils.js'; 54 | 55 | // ES6 absolute path 56 | import utils from '/utils.js'; 57 | 58 | // CommonJS relative path 59 | var utils = require('./../utils.js'); 60 | 61 | // CommonJS absolute path 62 | var utils = require('/utils.js'); 63 | ``` 64 | The **relative path** is relative to the current file. The **absolute path** is relative to the entry file, which in this case is *main.js*. 65 | 66 | ### Do I have to use file extension? 67 | 68 | No, you do not have to use *.js*, but it highlights better what you are requiring. You might have some .js files, and some .jsx files and even images and css can be required by Webpack. It also clearly differs from required node_modules and specific files. 69 | 70 | Remember that Webpack is a module bundler! This means you can set it up to load any format you want given there is a loader for it. We'll delve into this topic later on. -------------------------------------------------------------------------------- /content/Requiring-images-and-fonts.md: -------------------------------------------------------------------------------- 1 | - Inlining images and fonts -------------------------------------------------------------------------------- /content/Running-a-workflow.md: -------------------------------------------------------------------------------- 1 | Hitting `npm run build` all the time will get boring eventually. Fortunately we can work around that quite easily. Let's set up `webpack-dev-server`. 2 | 3 | ## Setting up `webpack-dev-server` 4 | 5 | As a first step, hit `npm i webpack-dev-server --save`. In addition we'll need to tweak `package.json` *scripts* section to include it. Here's the basic idea: 6 | 7 | *package.json* 8 | ```json 9 | { 10 | "scripts": { 11 | "build": "webpack", 12 | "dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build" 13 | } 14 | } 15 | ``` 16 | 17 | When you run `npm run dev` from your terminal it will execute the command stated as a value on the **dev** property. This is what it does: 18 | 19 | 1. `webpack-dev-server` - Starts a web service on localhost:8080 20 | 2. `--devtool eval` - Creates source urls for your code. Making you able to pinpoint by filename and line number where any errors are thrown 21 | 3. `--progress` - Will show progress of bundling your application 22 | 4. `--colors` - Yay, colors in the terminal! 23 | 5. `--content-base build` - Points to the output directory configured 24 | 25 | To recap, when you run `npm run dev` this will fire up the webservice, watch for file changes and automatically rebundle your application when any file changes occur. How neat is that! 26 | 27 | Go to **http://localhost:8080** and you should see something. -------------------------------------------------------------------------------- /content/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Navigation 2 | 3 | [Introduction](Home.md) 4 | 5 | * [Webpack](Introduction-to-Webpack.md) 6 | * [Getting started](Getting-started.md) 7 | * [Running a workflow](Running-a-workflow.md) 8 | * [Automatic browser refresh](Automatic-browser-refresh.md) 9 | * [Requiring files](Requiring-files.md) 10 | * [React JS](Introduction-to-React-JS.md) 11 | * [Configuring React JS](Configuring-react-js.md) 12 | * [Optimizing rebundling](Optimizing-rebundling.md) 13 | * [Flow](Type-checking-with-flow.md) 14 | * [CSS, Fonts and Images](CSS-Fonts-Images.md) 15 | * [Loading CSS](Loading-CSS.md) 16 | * [Automatic CSS refresh](Automatic-CSS-refresh.md) 17 | * [Loading LESS or SASS](Loading-LESS-or-SASS.md) 18 | * [Inlining images](Inlining-images.md) 19 | * [Inlining fonts](Inlining-fonts.md) 20 | * [Deployment strategies](Deployment-strategies.md) 21 | * [Deploy to production](Structuring-configuration.md) 22 | * [Single bundle](Single-bundle.md) 23 | * [Split app and vendors](Split-app-and-vendors.md) 24 | * [Multiple entry points](Multiple-entry-points.md) 25 | * [Lazy loaded entry points](Lazy-loaded-entry-points.md) 26 | * [Isomorphic app](Isomorphic-app.md) 27 | * [Wing It Like A Pro](Wing-It-Like-a-Pro.md) 28 | * [Optimizing development](Optimizing-development.md) 29 | * [Hot loading components](Hot-loading-components.md) 30 | * [Go JavaScript next](Javascript-next.md) 31 | * [Optimizing caching](Optimizing-caching.md) 32 | * [Matchers](Matchers.md) 33 | * [Lazy loading entry points](Lazy-loading-entry-points.md) 34 | * [Creating a common chunk](Creating-a-common-bundle.md) 35 | * [Chunks](Understanding-chunks.md) 36 | * [Authoring libraries](Authoring-libraries.md) -------------------------------------------------------------------------------- /content/Single-bundle.md: -------------------------------------------------------------------------------- 1 | Lets have a look at the simplest setup you can create for your application. Use a single bundle when: 2 | 3 | - You have a small application 4 | - You will rarely update the application 5 | - You are not too concerned about perceived initial loading time 6 | 7 | *webpack.production.config.js* 8 | ```javascript 9 | var path = require('path'); 10 | var config = { 11 | entry: path.resolve(__dirname, 'app/main.js'), 12 | output: { 13 | path: path.resolve(__dirname, 'dist'), 14 | filename: 'bundle.js' 15 | }, 16 | module: { 17 | loaders: [{ 18 | test: /\.js$/, 19 | loader: 'babel' 20 | }] 21 | } 22 | }; 23 | 24 | module.exports = config; 25 | ``` -------------------------------------------------------------------------------- /content/Split-app-and-vendors.md: -------------------------------------------------------------------------------- 1 | When your application is depending on other libraries, especially large ones like React JS, you should consider splitting those dependencies into its own vendors bundle. This will allow you to do updates to your application, without requiring the users to download the vendors bundle again. Use this strategy when: 2 | 3 | - When your vendors reaches a certain percentage of your total app bundle. Like 20% and up 4 | - You will do quite a few updates to your application 5 | - You are not too concerned about perceived initial loading time, but you do have returning users and care about optimizing the experience when you do updates to the application 6 | - Users are on mobile 7 | 8 | *webpack.production.config.js* 9 | ```javascript 10 | var path = require('path'); 11 | var webpack = require('webpack'); 12 | var node_modules_dir = path.resolve(__dirname, 'node_modules'); 13 | 14 | var config = { 15 | entry: { 16 | app: path.resolve(__dirname, 'app/main.js'), 17 | 18 | // Since react is installed as a node module, node_modules/react, 19 | // we can point to it directly, just like require('react'); 20 | vendors: ['react'] 21 | }, 22 | output: { 23 | path: path.resolve(__dirname, 'dist'), 24 | filename: 'app.js' 25 | }, 26 | module: { 27 | loaders: [{ 28 | test: /\.js$/, 29 | exclude: [node_modules_dir], 30 | loader: 'babel' 31 | }] 32 | }, 33 | plugins: [ 34 | new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js') 35 | ] 36 | }; 37 | 38 | module.exports = config; 39 | ``` 40 | This configuration will create two files in the `dist/` folder. **app.js** and **vendors.js**. 41 | 42 | #### Important! 43 | Remember to add both files to your HTML file, or you will get the error: `Uncaught ReferenceError: webpackJsonp is not defined`. -------------------------------------------------------------------------------- /content/Structuring-configuration.md: -------------------------------------------------------------------------------- 1 | There are two things you want to do preparing for a production build. 2 | 3 | 1. Configure a script to run in your package.json file 4 | 2. Create a production config 5 | 6 | ### Creating the script 7 | We have already used *package.json* to create the `npm run dev` script. Now let us set up `npm run deploy`. 8 | 9 | ```json 10 | { 11 | "name": "my-project", 12 | "version": "0.0.0", 13 | "description": "My awesome project!", 14 | "main": "app/main.js", 15 | "scripts": { 16 | "dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build", 17 | "deploy": "NODE_ENV=production webpack -p --config webpack.production.config.js" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "devDependencies": { 22 | "webpack": "^1.4.13", 23 | "webpack-dev-server": "^1.6.6" 24 | }, 25 | "dependencies": {} 26 | } 27 | ``` 28 | 29 | As you can see we are just running webpack with the production argument and pointing to a different configuration file. We also use the environment variable "production" to allow our required modules to do their optimizations. Lets us create the config file now. 30 | 31 | ### Creating the production config 32 | So there really is not much difference in creating the dev and production versions of your webpack config. You basically point to a different output path and there are no workflow configurations or optimizations. What you also want to bring into this configuration is cache handling. 33 | 34 | ```javascript 35 | var path = require('path'); 36 | var node_modules_dir = path.resolve(__dirname, 'node_modules'); 37 | 38 | var config = { 39 | entry: path.resolve(__dirname, 'app/main.js'), 40 | output: { 41 | path: path.resolve(__dirname, 'dist'), 42 | filename: 'bundle.js' 43 | }, 44 | module: { 45 | loaders: [{ 46 | test: /\.js$/, 47 | 48 | // There is not need to run the loader through 49 | // vendors 50 | exclude: [node_modules_dir], 51 | loader: 'babel' 52 | }] 53 | } 54 | }; 55 | 56 | module.exports = config; 57 | ``` 58 | 59 | ### Doing the deploy 60 | Run `npm run deploy` in the root of the project. Webpack will now run in production mode. It does some optimizations on its own, but also React JS will do its optimizations. Look into caching for even more production configuration. 61 | -------------------------------------------------------------------------------- /content/Type-checking-with-flow.md: -------------------------------------------------------------------------------- 1 | If you come to JavaScript from other programming languages you are familiar with types. You have types in JavaScript too, but you do not have to specify these types when declaring variables, receiving arguments etc. This is one of the things that makes JavaScript great, but at the same time not so great. 2 | 3 | Specifically when working on very large projects with many developers type checking gives stability to your project, much like a good test does. So using **Flow** is definitely not a requirement. It is for developers who depends on type checking as more of a routine and for the before mentioned large projects with many developers. Webpack makes it easy to include **Flow** in your workflow. 4 | 5 | ## Installing flow 6 | 7 | - Have to try this out :-) 8 | - What about "flowcheck-loader", tried it? https://www.npmjs.com/package/flowcheck-loader (probably works, haven't tried this one yet) 9 | - https://tryflow.org/ -------------------------------------------------------------------------------- /content/Understanding-chunks.md: -------------------------------------------------------------------------------- 1 | - Explain how webpack thinks chunks and not files 2 | - What are files to load? And what does webpack create for you? And how? -------------------------------------------------------------------------------- /content/Wing-It-Like-a-Pro.md: -------------------------------------------------------------------------------- 1 | > TODO: what to discuss in intro? Maybe summarize what makes a pro. Like using hot loader, caching etc. hm hm... maybe look at it after the sub pages are done, cherry on the top :-) -------------------------------------------------------------------------------- /content/Writing-loaders.md: -------------------------------------------------------------------------------- 1 | Let's say we want a custom Markdown loader. Ie. we would like to do something like `var readme = require('../README.md');` at our JavaScript file and inject some Markdown converted as HTML there. 2 | 3 | As it happens doing something like this is quite easy with Webpack. We'll need to implement a little loader for this and hook it up with our Webpack configuration. Consider the example below. I've included syntax highlighting just for the kicks. 4 | 5 | **loaders/markdown.js** 6 | 7 | ```javascript 8 | 'use strict'; 9 | 10 | var marked = require('marked'); 11 | var highlight = require('highlight.js'); 12 | 13 | marked.setOptions({ 14 | highlight: function(code) { 15 | return highlight.highlightAuto(code).value; 16 | } 17 | }); 18 | 19 | 20 | module.exports = function(markdown) { 21 | this.cacheable(); 22 | 23 | return marked(markdown); 24 | }; 25 | ``` 26 | 27 | **Webpack configuration** 28 | 29 | ```javascript 30 | resolve: { 31 | extensions: ['.md', ...], 32 | ... 33 | }, 34 | loaders: [ 35 | { 36 | test: /\.md$/, 37 | loader: 'html!./loaders/markdown', 38 | }, 39 | ] 40 | ``` 41 | 42 | Simple as that! You can read more about [loaders at the official documentation](http://webpack.github.io/docs/loaders.html). 43 | 44 | If you want to attach some CSS for syntax highlighting by the way, you can just `require` the needed CSS like this: `require('highlight.js/styles/github.css');`. That expects `highlight.js` has been installed correctly (ie. within `node_modules`) and Webpack can find it. You should also have `css-loader` set up like this: 45 | 46 | ```javascript 47 | { 48 | test: /\.css$/, 49 | loaders: ['style', 'css'], 50 | }, 51 | ``` 52 | 53 | This expects you have `style-loader` and `css-loader` installed into your project, preferably as development dependencies (`npm i style-loader css-loader --save-dev`). -------------------------------------------------------------------------------- /content/_Footer.md: -------------------------------------------------------------------------------- 1 | The React Webpack Cookbook is a work in progress by @bebraw and @christianalfoni -------------------------------------------------------------------------------- /content/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": ">=2.0.0", 3 | "title": "React Webpack cookbook", 4 | "description": "Recipes for solving common Webpack problems", 5 | "language": "en" 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-webpack-cookbook", 3 | "version": "0.0.0", 4 | "description": "React Webpack cookbook", 5 | "main": "./index.js", 6 | "scripts": { 7 | "generate-gitbook": "gitbook build ./content ./gh-pages", 8 | "generate-wiki": "node ./scripts/generate-wiki.js", 9 | "deploy-gitbook": "node ./scripts/deploy-gh-pages.js", 10 | "deploy-wiki": "./scripts/deploy-wiki.sh", 11 | "generate-and-deploy": "npm run generate-gitbook && npm run generate-wiki && npm run deploy-gitbook && npm run deploy-wiki", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/christianalfoni/react-webpack-cookbook.git" 17 | }, 18 | "keywords": [ 19 | "react", 20 | "webpack" 21 | ], 22 | "author": "", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/christianalfoni/react-webpack-cookbook/issues" 26 | }, 27 | "homepage": "https://github.com/christianalfoni/react-webpack-cookbook", 28 | "dependencies": { 29 | "async": "^0.9.0", 30 | "fs-extra": "^0.18.2", 31 | "gh-pages": "^0.2.0", 32 | "gitbook-cli": "^0.3.3" 33 | }, 34 | "devDependencies": {} 35 | } 36 | -------------------------------------------------------------------------------- /scripts/deploy-gh-pages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ghpages = require('gh-pages'); 4 | 5 | 6 | main(); 7 | 8 | function main() { 9 | ghpages.publish('./gh-pages', console.error.bind(console)); 10 | } 11 | -------------------------------------------------------------------------------- /scripts/deploy-wiki.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # this script expects that a gh-pages build exists already 3 | # adapted from https://gist.github.com/domenic/ec8b0fc8ab45f39403dd 4 | 5 | # go to the out directory and create a *new* Git repo 6 | cd wiki 7 | 8 | # Remove possible existing git repo. We'll replace entire wiki 9 | rm -rf .git 10 | 11 | # Init new repo 12 | git init 13 | 14 | # The first and only commit to this new Git repo contains all the 15 | # files present with the commit message "Deploy to GitHub Pages". 16 | git add . 17 | git commit -m "Deploy to Wiki" 18 | 19 | # Add origin 20 | git remote add origin git@github.com:christianalfoni/react-webpack-cookbook.wiki.git 21 | 22 | # Force push from the current repo's master branch to the remote 23 | # (All previous history on the master branch will be lost, since we are overwriting it.) 24 | git push --force origin master:master -------------------------------------------------------------------------------- /scripts/generate-wiki.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | 5 | var fs = require('fs-extra'); 6 | var async = require('async'); 7 | 8 | 9 | main(); 10 | 11 | function main() { 12 | var input = './content'; 13 | var output = './wiki'; 14 | 15 | fs.mkdir(output, function() { 16 | // if it dir exists already, just override content 17 | generateWiki(input, output, function(err) { 18 | if(err) { 19 | return console.error(err); 20 | } 21 | 22 | console.log('generated wiki'); 23 | }); 24 | }); 25 | } 26 | 27 | function generateWiki(input, output, cb) { 28 | async.series([ 29 | fs.copy.bind(null, 30 | input, 31 | output 32 | ), 33 | fs.copy.bind(null, 34 | path.join(input, 'README.md'), 35 | path.join(output, 'Home.md') 36 | ), 37 | generateSidebar.bind(null, { 38 | input: path.join(input, 'summary.md'), 39 | output: path.join(output, '_Sidebar.md') 40 | }), 41 | fs.remove.bind(null, path.join(output, 'README.md')), 42 | fs.remove.bind(null, path.join(output, 'summary.md')), 43 | ], cb); 44 | } 45 | 46 | function generateSidebar(config, cb) { 47 | var data = fs.readFileSync(config.input, { 48 | encoding: 'utf8' 49 | }); 50 | 51 | data = data.replace(/.md/g, ''); 52 | 53 | fs.writeFile(config.output, data, cb); 54 | } 55 | --------------------------------------------------------------------------------