This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| reactjs:getting-started [2015-02-17] – [React: Getting started with webpack] dcai | reactjs:getting-started [2020-04-19] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ===== React: Getting started with webpack ===== | ||
| + | Our team recently decided to adapt [[http:// | ||
| + | |||
| + | In this page, I would walk through the process of building a modern webapp using react.js and webpack. | ||
| + | |||
| + | ==== The workflow overview ==== | ||
| + | * Define the project with npm init | ||
| + | * Add project dependencies with `npm install --save ` | ||
| + | * Create " | ||
| + | * Create components as [[http:// | ||
| + | * Introduce the components in webpack entry script | ||
| + | * Have the project up and running | ||
| + | |||
| + | ==== Define the project ===== | ||
| + | I assume you are using Linux/Mac, and have [[http:// | ||
| + | |||
| + | Let's initialize the project: | ||
| + | < | ||
| + | mkdir new-react-project | ||
| + | cd new-react-project | ||
| + | npm init | ||
| + | </ | ||
| + | |||
| + | The above commands create package.json file, it defines the meta information of the project. | ||
| + | |||
| + | The project we are going to build requires react.js(obviously) and jquery(mostly for ajax request), so we add them to the project: | ||
| + | < | ||
| + | npm install --save react jquery | ||
| + | </ | ||
| + | |||
| + | We also need to use webpack dev tool for this project, so we do this: | ||
| + | < | ||
| + | npm install -g webpack webpack-dev-server | ||
| + | </ | ||
| + | |||
| + | Additionally, | ||
| + | < | ||
| + | npm install --save-dev jsx-loader | ||
| + | </ | ||
| + | |||
| + | ==== Config webpack ===== | ||
| + | Now we create `webpack.config.js` file, which tells webpack how to build this project, see inline comments for explaination: | ||
| + | < | ||
| + | module.exports = { | ||
| + | entry: ' | ||
| + | output: { | ||
| + | filename: ' | ||
| + | publicPath: ' | ||
| + | }, | ||
| + | module: { | ||
| + | loaders: [ | ||
| + | { | ||
| + | test: /\.jsx$/, | ||
| + | // harmony enable ES6 features | ||
| + | loader: ' | ||
| + | } | ||
| + | ] | ||
| + | }, | ||
| + | resolve: { | ||
| + | extensions: ['', | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== Create index page ==== | ||
| + | Now we want to have an index page showing "hey yo" alert on load: | ||
| + | index.html | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <div id=' | ||
| + | <script type=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Obviously, we don't have bundle.js yet, the idea of using webpack is pack all your resources in this bundle.js. | ||
| + | Remember we added " | ||
| + | index.js | ||
| + | < | ||
| + | alert(" | ||
| + | </ | ||
| + | |||
| + | Start dev server: | ||
| + | < | ||
| + | | ||
| + | </ | ||
| + | |||
| + | Open http:// | ||
| + | |||
| + | ==== Components ==== | ||
| + | But what's the point of doing all these? You may ask, it looks complicated! | ||
| + | |||
| + | Well, the purpose of this is enabling web components, if you're a backend development, | ||
| + | |||
| + | Before we dive into web components in react.js, let me show you an example of customized module first. | ||
| + | |||
| + | We all know is a better practice to put separated modules in separate places, | ||
| + | alert.js | ||
| + | < | ||
| + | module.exports = function() { | ||
| + | alert(" | ||
| + | } | ||
| + | </ | ||
| + | and in index.js, we just do: | ||
| + | < | ||
| + | (require(' | ||
| + | </ | ||
| + | Refresh the page, then you got the same alert, you can easily create other modules and require them in index.js, let try a little more complex task, grab the latest sydney weather, then show it on index page: | ||
| + | create weather.js: | ||
| + | < | ||
| + | 'use strict' | ||
| + | var JQ = require(' | ||
| + | module.exports = function() { | ||
| + | JQ(document.body).append('< | ||
| + | JQ.getJSON( " | ||
| + | JQ('# | ||
| + | JQ('# | ||
| + | var weather = data.weather[0]; | ||
| + | var linktoicon = ' | ||
| + | JQ('# | ||
| + | JQ('# | ||
| + | }); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | And in index.js: | ||
| + | < | ||
| + | // | ||
| + | (require(' | ||
| + | </ | ||
| + | ==== Bring on React.js ==== | ||
| + | |||
| + | Yeah, that works well. | ||
| + | |||
| + | Let's take a look at React.js, I won't repeat what's react.js here as there are plenty resources available, just outline a few key advantages of it here: | ||
| + | * Help you build web components yet friendly to web designer | ||
| + | * Ultra fast, it create an virtual DOM diff algorithm to render web contents, it could renders on server side too (WAT?!) | ||
| + | * one-way data binding, unlike angular | ||
| + | |||
| + | well, that's boring :) just show me the code :) | ||
| + | |||
| + | Let's create a classic(boring) hello world component. | ||
| + | JSX is XML-like javascript superset, consider it's the javascript you can directly write html instead quote them: | ||
| + | Hello.jsx: | ||
| + | < | ||
| + | 'use strict' | ||
| + | var React = require(' | ||
| + | module.exports = React.createClass({ | ||
| + | render: function() { | ||
| + | return < | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | </ | ||
| + | rename index.js to index.jsx, so we can use jsx syntax to make our life easier, don't forget to change webpack.config.js' | ||
| + | < | ||
| + | var React = require(' | ||
| + | var HelloTag = require(' | ||
| + | React.render(< | ||
| + | </ | ||
| + | |||
| + | Isn't it annoying to keep refreshing the page to see changes? Well, react.js does provide the tool to auto-refresh when code changes, simple add below line to index.html: | ||
| + | < | ||
| + | <script src=" | ||
| + | </ | ||
| + | Note, you must use the full url here. | ||
| + | |||
| + | ==== Refactor the weather component ==== | ||
| + | |||
| + | So I'm going to refactor the weather component, so that we can do this in the index.jsx: | ||
| + | < | ||
| + | React.render(< | ||
| + | </ | ||
| + | It will also include search box, so I can query weather of other regions. | ||
| + | |||
| + | First rename weather.js to Weather.jsx, | ||
| + | < | ||
| + | 'use strict' | ||
| + | var JQ = require(' | ||
| + | var React = require(' | ||
| + | module.exports = React.createClass({ | ||
| + | defaultState: | ||
| + | name: ' | ||
| + | icon: ' | ||
| + | description: | ||
| + | }, | ||
| + | getInitialState: | ||
| + | return this.defaultState; | ||
| + | }, | ||
| + | componentWillMount: | ||
| + | this.loadWeather(); | ||
| + | }, | ||
| + | loadWeather: | ||
| + | JQ.getJSON( " | ||
| + | var weather = data.weather[0]; | ||
| + | var result = {}; | ||
| + | result.name = data.name; | ||
| + | result.icon = this.getIcon(weather.icon); | ||
| + | result.description = weather.description; | ||
| + | this.setState(result); | ||
| + | }.bind(this)); | ||
| + | }, | ||
| + | getIcon: function(code) { | ||
| + | if (!code) { | ||
| + | code = ' | ||
| + | } | ||
| + | return ' | ||
| + | }, | ||
| + | render: function() { | ||
| + | return <div> | ||
| + | < | ||
| + | <img src={this.state.icon} /> | ||
| + | < | ||
| + | </ | ||
| + | } | ||
| + | }); | ||
| + | </ | ||
| + | We implemented two buildin callbacks here: componentWillMount and getInitialState, | ||
| + | |||
| + | We don't have a search box yet, it's going to be a sub-component, | ||
| + | < | ||
| + | render: function() { | ||
| + | var valueLink = { | ||
| + | value: this.state.searchname, | ||
| + | requestChange: | ||
| + | }; | ||
| + | return <div> | ||
| + | <input name=" | ||
| + | < | ||
| + | <img src={this.state.icon} /> | ||
| + | < | ||
| + | </ | ||
| + | } | ||
| + | </ | ||
| + | Note, we add an input element, which has valueLink prop, it helps use create Two-Way binding with state, requestChange will try to call this.handleChange method to trigger text change event, in this method we will call the loadWeather method to update the weather, please see the full Weather.jsx: | ||
| + | < | ||
| + | |||
| + | |||
| + | 'use strict' | ||
| + | var JQ = require(' | ||
| + | var React = require(' | ||
| + | module.exports = React.createClass({ | ||
| + | defaultState: | ||
| + | name: ' | ||
| + | icon: ' | ||
| + | description: | ||
| + | }, | ||
| + | getInitialState: | ||
| + | return this.defaultState; | ||
| + | }, | ||
| + | componentWillMount: | ||
| + | this.loadWeather(); | ||
| + | }, | ||
| + | loadWeather: | ||
| + | var cityname = ' | ||
| + | if (city) { | ||
| + | cityname = city; | ||
| + | } | ||
| + | JQ.getJSON( " | ||
| + | console.info(data); | ||
| + | if (data.cod && data.cod == ' | ||
| + | return; | ||
| + | } | ||
| + | var weather = data.weather[0]; | ||
| + | var result = {}; | ||
| + | result.name = data.name; | ||
| + | result.icon = this.getIcon(weather.icon); | ||
| + | result.description = weather.description; | ||
| + | this.setState(result); | ||
| + | }.bind(this)); | ||
| + | }, | ||
| + | getIcon: function(code) { | ||
| + | if (!code) { | ||
| + | code = ' | ||
| + | } | ||
| + | return ' | ||
| + | }, | ||
| + | handleChange: | ||
| + | this.loadWeather(e); | ||
| + | }, | ||
| + | render: function() { | ||
| + | var valueLink = { | ||
| + | value: this.state.searchname, | ||
| + | requestChange: | ||
| + | }; | ||
| + | return <div> | ||
| + | <input name=" | ||
| + | < | ||
| + | <img src={this.state.icon} /> | ||
| + | < | ||
| + | </ | ||
| + | } | ||
| + | }); | ||
| + | </ | ||
| + | |||
| + | You can also find an improved version of this component with delayed ajax search: https:// | ||
| + | |||