Starter
Using Directly
> bower install react react-dom babel
<div id="example"></div>
<script type="text/javascript" src="bower_components/react/react.js"></script>
<script type="text/javascript" src="bower_components/react/react-dom.js"></script>
<script type="text/javascript" src="bower_components/babel/browser.min.js"></script>
<script type="text/babel">
// write your reactjs here
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
</script>
Using Builded JS Depend on npm
下载ReactJS依赖包:
> npm install react react-dom --save
编写React:
// ./src/xx.jsx var React=require("react"); var ReactDOM=require("react-dom"); ReactDOM.render(<h1>Hello, world!</h1>,document.getElementById('app')); var element = React.createElement('h1', null, 'Hello, world!'); ReactDOM.render(element,document.getElementById('example'));
编译生成可直接在浏览器执行的JS (这里提供两种方法,参考下文)
- Method1: Build By Browserify
- Method2: Build By Webpack
HTML页面中直接调用生成的JS文件:
<div id="app" class="container"></div> <script type="text/javascript" src="xxx.js"></script>
- 可通过Gulp或Webpack插件实现Server热加载实时预览等功能,eg:
- gulp插件:
gulp-server-livereload
- webpack插件:
webpack-dev-server
(还可结合使用react-hot
高效react热加载功能)
- gulp插件:
Build by browserify
下载依赖包:
> npm install browserify -g > npm install babelify --save > npm install watchify -g
babelify
:合并模块时对 ES6 和 ES7 进行编译转换,同时还可以对 JSX 进行编译转换,也可使用 reactifywatchify
:监听,配合browserify
实现实时合并 (是一个browserify
的封装,配置同browserify
)
执行
browserify
合并模块> browserify ./src/app.jsx -t babelify -o ./dist/bundle.js //如果require的module的后缀不为.js,.json,需添加-extension参数,eg: > browserify ./src/app.jsx -t babelify -o ./dist/bundle.js --extension .jsx
watchify
监听> watchify ./src/app.jsx -t babelify --extension .jsx -o ./dist/bundle.js -v
可在
package.json
中配置browserify
"browserify" : { "transform": [ ["reactify", {"es6": true}] ], "extensions":[".jsx"] }
> browserify xxx.jsx -o xxx.js > watchify xxx.jsx -o xxx.js
可使用
gulp
var gulp=require("gulp"); var gutil=require("gulp-util"); var source = require('vinyl-source-stream'); var watchify=require("watchify"); var browserify = require('browserify'); var babelify=require("babelify"); var argv=require("yargs").argv; function doBuild(filename){ if(!filename) filename="app"; var b=watchify(browserify({ entries:["./src/"+filename+".jsx"], transform:[babelify], extensions:[".jsx"], debug: true, cache: {}, packageCache: {}, fullPaths: true })); function build(file){ if(file) gutil.log('Recompiling ' + file); b.bundle() .on('error',gutil.log.bind(gutil,"Browserify Error")) .pipe(source(filename+".js")) .pipe(gulp.dest('./dist')); }; build(); b.on('update',build); } gulp.task('build',function(){ var filename=argv.n || "app"; doBuild(filename); })
> gulp build
Build by webpack
下载依赖包
> npm install jsx-loader --save
编写webpack.config.js
//webpack.config.js module.exports = { entry: "./src/questionApp.jsx", output: { path: __dirname+"/dist", filename: "questionApp-webpack.js" publicPath:"/dist" }, resolve: { extensions: ['','.js','.jsx'] }, module: { loaders: [ //{ test: /\.jsx$/, loader: "babel-loader!jsx-loader?harmony" } { test: /\.jsx$/, loader: "jsx" } ] } };
执行
> webpack
或通过webpack-dev-server Visit http://127.0.0.1:8080 with your browser.(先下载包:
npm install webpack-dev-server -g
)> webpack-dev-server //启动热加载 > webpack-dev-server --hot --inline
Basic Features
Component
var reactComponent=React.createClass({
...
render:function(){
...
return ...
},
...
});
Element
var reactElement = React.createElement(type, props, children);
或通过Factory创建Element:
var tag = React.createFactory(type);
var reactElement = tag(props, children);
使用示例:
var element=React.createElement('div', {className:'my-div'}, 'Hello, world!');
//或:
var div = React.createFactory('div');
var element = div({ className: 'my-div' },'Hello, world!');
Render
ReactDOM.render(reactElement,domContainerNode,callback);
使用示例:
var Question=require("./components/question");
var propOpts={...};
//use createElement:
var element=React.createElement(Question, propOpts);
ReactDOM.render(element,document.getElementById('app'));
//or use createFactory:
var QuestionFactory=React.createFactory(Question);
ReactDOM.render(QuestionFactory(propOpts),document.getElementById('app'));
//or use JSX:
ReactDOM.render(<Question {...propOpts} />,document.getElementById('app'));
// ./components/question.jsx:
var React=require("react");
module.exports=React.createClass({
...
render:function(){
...
//use JSX:
return (<div className='my-div'>Question System</div>);
//or use createElement:
//return React.createElement('div',{className:my-div},'Question System');
},
...
})
LifeCycle
Component Lifecycle :
- Mounting :
- void componentWillMount()
- void componentDidMount()
- Updating :
- receive new props
- void componentWillReceiveProps (object nextProps)
- boolean shouldComponentUpdate(object nextProps, object nextState)
- void componentWillUpdate( object nextProps, object nextState)
- void componentDidUpdate(object prevProps, object prevState)
- receive new props
- UnMount :
- void componentWillUnmount()
note: shouldComponentUpdate(nextProps,nextStates) & render() should in mixin or component (only in one place)
Initial (第一次 render ) :
- -> getDefaultProps
- -> getInitialState
- -> componentWillMount
- -> render
- sub component (do same initial cycle)
- -> componentDidmount
- -> RenderFinished!
Update (第n次 render ):
- -> shouldComponentUpdate(nextProps,nextState)
- return false -- stop here
- return true -- go to next
- -> componentWillUpdate(nextProps,nextState)
- -> render
- Sub Component: componentWillReceiveProps(nextProps)
- Sub Component: shouldComponentUpdate(nextProps,nextState)
- Sub Component: componentWillUpdate(nextProps,nextState)
- Sub Component: componentDidUpdate(prevProps,prevState)
- -> componentDidUpdate(prevProps,prevState)
Initial (has Mixins):
- -> getDefaultProps -- Mixins + Component (merge return) -- can't set same property
- -> getInitialState -- Mixins + Component (merge return) -- can't set same property
- -> Mixins: componentWillMount
- -> componentWillMount
- -> render -- in Mixins or Component
- -> Mixins: componentDidMount
- -> componentDidMount
- -> RenderFinished!
update (has Mixins):
- -> shouldComponentUpdate -- in Mixins or Component
- -> Mixins: componentWillUpdate
- -> componentWillUpdate
- -> render -- in Mixins or Component
- -> Mixins: componentDidUpdate
- -> componentDidUpdate
Props
初始化props:
getDefaultProps:function(){
return {...}
},
可通过定义propTypes限定props,eg:
propTypes: {
value: React.PropTypes.object.isRequired,
onChange: React.PropTypes.func.isRequired,
onSubmit: React.PropTypes.func.isRequired,
},
使用时传入props参数:
<Tag {...opts} />
<Tag xxx='...' yyy={...} zzz=... ... />
当传入新的props参数时,会触发Component的以下方法:
void componentWillReceiveProps (object nextProps)
boolean shouldComponentUpdate(object nextProps, object nextState)
void componentWillUpdate( object nextProps, object nextState)
void render()
void componentDidUpdate(object prevProps, object prevState)
注意
this.props.children
是获取当前节点的全部子内容this.props.xxx
获取参数值(注意不可这样修改props)- Component间传递:
- Father state/props to Children' props
- refs
State
初始化state:
getInitialState:function(){
return {...}
}
获取state值:
this.state.xxx
设置state值:
this.setState({...})
this.state.xxx=...;
,还需调用this.forceUpdate()
生效
当state变化时会触发以下方法:
boolean shouldComponentUpdate(object nextProps, object nextState)
void componentWillUpdate( object nextProps, object nextState)
void render()
void componentDidUpdate(object prevProps, object prevState)
Refs
设置ref(可以绑定到 render() 输出的任何组件上去),eg:
<input type="text" defaultValue={this.state.inputValue} ref="goodInput"/>
获取:
- access the component instance:
this.refs.xxx
orthis.refs[xxx]
- access the DOM
ReactDOM.findDOMNode(componentInstance)
eg:
//在function中抓取我們動態render的某個UI元素
this.refs.goodInput
this.refs.goodInput.value
this.refs.goodInput.focus()
ReactDOM.findDOMNode(this.refs.goodInput).focus();
Form
- Interactive props:
value
,checked
,selected
- SyntheticEvent :React对Event进行了封装
- Form Events:
onChange
,onInput
,onClick
,onSubmit
,onReset
,...
eg:
render:function(){
return (
<form onSubmit={this.submitHandler}>
<input type="text" value={this.state.inputValue} onChange={this.inputHandler}/>
<br/>
<input type="radio" name="goodRadio" value="A" defaultChecked onChange={this.radioHandler}/>A
<input type="radio" name="goodRadio" value="B" onChange={this.radioHandler}/>B
<input type="radio" name="goodRadio" value="C" onChange={this.radioHandler}/>C
<button type="reset" >Reset</button> <button type="submit">Submit</button>
</form>
)
},
submitHandler:function(e){
// React对event进行了优化,若未使用则e填充值都为null
console.log(e);
console.log(e.target);
console.log(e.type);
console.log(this.state);
e.preventDefault();
},
resetHandler:function(e){
if(e) e.preventDefault();
ReactDOM.findDOMNode(this.refs.regForm).reset();
},
inputHandler:function(e){
this.setState({inputValue:e.target.value});
},
radioHandler:function(e){
this.setState({radioValue:e.target.value});
},
...
Isomorphic
同构,可在client或server端渲染React对象,统一使用,提供渲染速度,利于SEO
使用 ReactDOMServer
(in react-dom/server
package)
string renderToString(ReactElement element)
string renderToStaticMarkup(ReactElement element)
//doesn't create extra DOM attributes such as data-react-id, that React uses internally
一个简单示例
messageBox.jsx
定义需要同构的React Component:var MessageBox = React.createClass({ getInitialState: function() { return {value: 'Hello!'}; }, handleChange: function(event) { this.setState({value: event.target.value}); }, render: function () { var value = this.state.value; return ( <div> <h1>This is Isomorphic Test:</h1> <input type="text" value={value} onChange={this.handleChange} /> <p>Inputed:{value}</p> </div> ); } }); module.exports=MessageBox;
app.jsx
实现Client端渲染:var MessageBox=require('messageBox.jsx'); ReactDOM.render(<MessageBox/>,document.getElementById('app'));
app.js
编译生成的Client端可执行的js文件> browserify app.jsx -t babelify --presets react -o app.js --extension .jsx
生成的js如下:
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; var MessageBox = React.createClass({ displayName: 'MessageBox', getInitialState: function getInitialState() { return { value: 'Hello!' }; }, handleChange: function handleChange(event) { this.setState({ value: event.target.value }); }, render: function render() { var value = this.state.value; return React.createElement( 'div', null, React.createElement( 'h1', null, 'This is Isomorphic Test:' ), React.createElement('input', { type: 'text', value: value, onChange: this.handleChange }), React.createElement( 'p', null, 'Inputed:', value ) ); } }); ReactDOM.render(React.createElement(MessageBox, null), document.getElementById('app')); },{}]},{},[1]);
index.html
定义主页面,加载上面生成的Client的js文件app.js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello React!</title> </head> <body> <div id="app">{content}</div> <script type="text/javascript" src="libs/react/react.js"></script> <script type="text/javascript" src="libs/react/react-dom.js"></script> <script type="text/javascript" src="app.js"></script> </body> </html>
server.js
定义Server端响应渲染require("babel-core/register"); var React=require("react"); var ReactDOMServer=require("react-dom/server"); var http=require("http"); var fs=require("fs"); var server=http.createServer(function(req,res){ console.log(req.url); if(req.url=='/' || req.url=='/index.html'){ var element = React.createElement(require("./messageBox.jsx")); var reactHtml = ReactDOMServer.renderToString(element); fs.readFile("./index.html",function(err,data){ if (err) throw err; res.writeHead(200,{'Content-Type':'text/html'}); res.end(data.toString().replace("{content}",reactHtml)); }); }else if(req.url.startsWith("/libs")){ fs.readFile("."+req.url,function(err,data){ if(err) throw err; res.writeHead(200); res.end(data); }) }else{ res.writeHead(400,{'Content-Type':'text/plain'}); res.end("not found"); } }); server.listen(3000, 'localhost', function (err, result) { if (err) console.log(err); console.log('Listening at localhost:3000'); });
执行
node server
启动服务查看效果
PS:
如果出现无法识别解析JSX的错误,请检查使用的babel-core
版本,6和5的使用上会有些不同,具体可参考The Six Things You Need To Know About Babel 6
Use Extra JS in React
eg: Using JQuery in React
var $=require("jquery");
module.exports=React.createClass({
componentDidMount:function(){
//一般在这里进行初始化绑定dom
//eg: $('#xxx').modal('hide');
},
...
//可在其他方法中调用操作
//eg: $('#xxx').modal('show');
});
eg: Using highlight.js in React Issue on Github
var hljs =require("highlight.js");
var Pretty = React.createClass({
componentDidMount: function(){
var current = React.findDOMNode(this);
hljs.highlightBlock(current);
},
render: function() {
return <pre className="custom-json-body">
<code className="json">{JSON.stringify(this.props.json, null, 2)}</code>
</pre>;
}
});
PS: 一般不推荐在React中注入其他作用域的代码,推荐使用相应的React Plugins或自行扩展实现功能(尽量保持都在React的作用域)
Reference
- ReactJS
- Build
- Browserify
- Webpack
- Babel
- My Demos