【起初的准备工作】

npm init
npm install --save react react-dom
npm install --save-dev html-webpack-plugin webpack webpack-dev-server babel-core babel-loader babel-preset-react
  • react react-dom是有关react
  • html-webpack-plugin:用来把源文件,比如把src/index.html复制到dest/中的index.html中,并引用经webpack捆绑后的js文件
  • webpack:不多说
  • webpack-dev-server:搭建一个本地服务器
  • babel-core, babel-loader用来把jsx转换成js文件
  • babel-preset-react:在babel中设置react

【文件结构】

app/
.....index.html
.....index.js
.babelrc
package.json
webpack.config.js
  • .babelrc:在其中设置react
  • webpack.config.js:webpack的配置文件

【使用webpack需求与实现】

  • 用webpack把js文件捆绑到根目录下的dist文件夹下的index_bundle.js
  • 复制app/index.html文件,在根目录下的dist文件夹下生成一个index.html文件,并引用index_bundle.js
  • 运行npm run 某某名称,来执行webpack -p命令

也就是:

app/
.....index.html
.....index.js
dist/
.....index.html
.....index_bundle.js
.babelrc
package.json
webpack.config.js

webpack.config.js, 这个webpack的配置文件中大致包括:接受源文件、放到目标文件夹、使用babel把jsx文件转换成js文件、对源文件进行复制,等等。

var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: __dirname + '/app/index.html',
filename: 'index.html',
inject: 'body'
}) module.exports = {
entry: [
'./app/index.js'
],
output: {
path: __dirname + '/dist',
filename: "index_bundle.js"
},
module: {
loaders: [
{test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"}
]
},
plugins: [
HtmlWebpackPluginConfig
]
}
  • HtmlWebpackPlugin:用来文件复制,template表示源文件的具体地址, filename表示复制到目标文件夹后的文件名称, inject:'body'表示把经webpack生成的index_bundle.js文件被引用到body中
  • module中的loaders使用babel进行jsx到js文件的转换, test使用正则表达式对需要被转换的文件进行限定,exclude是babel在进行转换时需要排除的源文件夹
  • entry:babel在这里找入口源文件
  • output:经babel转换后的文件保存位置
  • plugins:使用HtmlWebpackPlugin,用来文件复制

.babelrc文件中,babel需要对react的jsx进行转换,配置如下:

{
"presets": [
"react"
]
}

最后一点,如何运行npm run 某某名称,来执行webpack -p命令呢?

需要在package.json中配置

{
"name": "reactjspracticeswithtyler",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"production": "webpack -p"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^15.1.0",
"react-dom": "^15.1.0"
},
"devDependencies": {
"babel-core": "^6.9.0",
"babel-loader": "^6.2.4",
"babel-preset-react": "^6.5.0",
"bootstrap": "^3.3.6",
"html-webpack-plugin": "^2.17.0",
"webpack": "^1.13.1",
"webpack-dev-server": "^1.14.1"
}
}

以上,在scripts下的配置,意思是说运行npm run 某某名称,实际是执行webpack -p命令。

【第一个React组件】

app/index.html

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
</head>
<body> <div id="app"></div> </body>
</html>

以上,我们即将要把React组件插入到id为app的div中。

app/index.js,在这里创建组件

var React = require('react');
var ReactDOM = require('react-dom'); //1、生产Component
//这里是jsx的语法
var HelloWorld = React.createClass({
render: function(){
//console.log(this.props);
return (
<div>Hello {this.props.name}</div>
)
}
}); //2、渲染出来
ReactDOM.render(
<HelloWorld name="darren" anySortData={29}/>,
document.getElementById('app')
);
  • React.createClass创建组件, render必不可少否则报错,return返回的语法就是jsx语法,将来需要用Babel转换成js文件
  • 组件的名称,像这里的HelloWord,第一个字母一般大写
  • ReactDOM.Render用来把组件渲染到DOM上
  • 组件的表现形式就像html元素,比如这里的<HelloWorld name="darren" anySortData={29}/>,这里,对name和anySortData的赋值,实际上会赋值到组件的this.props.name和this.props.anySortData中

运行npm run production,因为我们在package.json中的scripts下已经有了设置,相当于运行webpack -p命令,接下来会根据webpack.config.js中的设置,找到app/index.js,使用babel把index.js中的jsx部分转换成js保存到dist/index_bundle.js中;这时候,html-webpack-plugin开始工作了,把app/index.html复制保存到dist/index.html中,并把index_bundle.js中注入到dist/index.html的body中。

还有一个问题需要解决,当在浏览器中输入localhost:8080的时候,能浏览到网页。怎么做呢?

需要在package.json中进行设置,在scripts中进行如下设置

{
"name": "reactjspracticeswithtyler",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"production": "webpack -p",
"start":"webpack-dev-server"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^15.1.0",
"react-dom": "^15.1.0"
},
"devDependencies": {
"babel-core": "^6.9.0",
"babel-loader": "^6.2.4",
"babel-preset-react": "^6.5.0",
"bootstrap": "^3.3.6",
"html-webpack-plugin": "^2.17.0",
"webpack": "^1.13.1",
"webpack-dev-server": "^1.14.1"
}
}

以上,在scripts下添加了"start":"webpack-dev-server"部分。

运行npm run start命令,在浏览器中输入localhost:8080就可以看到内容。

【组件嵌套】

var USER_DATA = {
name: 'darren',
username: 'DarrenActive',
image: 'https://avatars0.githubusercontent.com/u/2933430?v=38s=460'
}; var React = require('react');
var ReactDOM = require('react-dom'); //被嵌套组件
var ProfilePic = React.createClass({
render: function(){
return <img src={this.props.imageUrl} style={{height: 100, width: 100}} />
}
}) //被嵌套组件
var ProfileLink = React.createClass({
render: function(){
return (
<div>
<a href={'https://www.github.com/' + this.props.username}>
{this.props.username}
</a>
</div>
)
}
}); //被嵌套组件
var ProfileName = React.createClass({
render: function(){
return (
<div>{this.props.name}</div>
)
}
}) //嵌套组件
var Avatar = React.createClass({
render: function(){
return (
<div>
<ProfilePic imageUrl={this.props.user.image} />
<ProfileName name={this.props.user.name} />
<ProfileLink username={this.props.user.username} />
</div>
)
}
}) ReactDOM.render(
<Avatar user={USER_DATA} />,
document.getElementById('app')
);

嵌套组件和被嵌套组件的关系就像河流的上下游,这里的嵌套组件Avatar在河流的上游,这里的被嵌套组件ProfileName在河流的下游,Avatar组件通过user来获取外界的赋值,user接受到值后往下游的ProfileName流动,user中的值再赋值给ProfileName的name,依次类推。

所以,React的数据流动是单向的,由外向内的流动。

【组件元素嵌套】

组件嵌套和组件元素嵌套不一样。组件嵌套大致是:

var Room = React.createClass({
render: function(){
return (
<Table />
<Chair />
<Clock />
)
}
});

组件元素嵌套大致是:

var Room = React.createClass({
render: function(){
return (
<Table />
<Chair />
<Clock>
<Time />
<Period />
</Clock>
)
}
});

也就是说,组件元素嵌套中的被嵌套组件,这里的<Time /><Period />不是被放在一个外层的组件中,而是放在了一个组件元素<Clock></Clock>。Clock组件的显示依赖于<Time /><Period />。那么,在定义Clock组件的时候,如何把<Time /><Period />显示出来呢?

像这种需要显示组件元素内的被嵌套组件,就需要this.props.children的帮忙。

像在本项目的Avatar组件的写法还是不变:

var Avatar = React.createClass({
render: function(){
return (
<div>
<ProfilePic imageUrl={this.props.user.image} />
<ProfileName name={this.props.user.name} />
<ProfileLink username={this.props.user.username} />
</div>
)
}
})

ProfileLink组件,现在想把它作为另外一个组件元素内的被嵌套组件,这样定义ProfileLink组件:

var ProfileLink = React.createClass({
render: function(){
return (
<div>
<Link href={'https://www.github.com/' + this.props.username}>
{this.props.username}
</Link>
</div>
)
}
});

以上,{this.props.username}拿到的值是通过ProfileLink组件组件获取到的,如何传递给Link组件呢?

var Link = React.createClass({

    changeURL: function(){
window.location.replace(this.props.href);
}, render: function(){
return (
<span
style={{color: 'blue', cursor: 'pointer'}}
onClick={this.changeURL}>
{this.props.children}
</span>
)
}
});

可见,在Link组件中,通过this.props.children获取到ProfileLink组件这个组件元素内的组件。值得注意的是:this.props.children获取到的是包含在组件元素内的所有被嵌套组件。

【路由】

React的路由连接了component和url。

npm install --save react-router@2.0.0-rc5

在app文件夹下创建config文件夹,并创建routes.js文件;在app文件夹下创建Home.js和Main.js,现在文件结构变为:

app/
.....config/
..........routes.js
.....components/
..........Home.js
..........Main.js
.....index.html
.....index.js
.babelrc
package.json
webpack.config.js

在Home.js中创建一个名称为Home的组件

var React = require('react');

var Home = React.createClass({
render: function(){
return (
<div>Hello From Home!</div>
)
}
}); module.exports = Home;

在Main.js中创建一个名称为Main的组件

var React = require('react');

var Main = React.createClass({
render: function(){
return (
<div>
Hello From Main!
{this.props.children}
</div>
)
}
}); module.exports = Main;

配置config/routes.js中的路由,其实就是配对url和组件的映射关系。

var React = require('react');
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var IndexRouter = ReactRouter.IndexRoute; var Main = require('../components/Main');
var Home = require('../components/Home'); var routes = (
<Router>
<Route path='/' component={Main}>
<Route path='/home' component={Home} />
</Route>
</Router>
); module.exports = routes;

以上,路由从本质上说也是组件。<Route path='/' component={Main}>...</Route>依赖于<Route path='/home' component={Home} />,当url为/的时候,只会显示Main组件内容,当url为/home的时候会同时显示Main组件和Home组件的内容。

在app/index.js中,把路由这个特殊的组件加载起来。

var React = require('react');
var ReactDOM = require('react-dom');
var routes = require('./config/routes'); ReactDOM.render(
routes,
document.getElementById('app')
);

npm run start

在浏览器中输入:localhost:8080

url变成:http://localhost:8080/#/?_k=dhjswq

内容为:Hello From Main!

因为,当url为/的时候,路由设置显示Main组件,虽然在Main组件定义的时候有{this.props.children},但因为Main组件元素嵌套的Home组件没有显示,所有只显示Main组件的内容。

在浏览器中输入:http://localhost:8080/#/home?_k=dhjswq

内容:

Hello From Main!

Hello From Home!

也很好理解,因为Home组件是被嵌套在Main这个组件元素中的,当url为Home路由的时候,不仅把Home组件显示了出来,还把Main组件显示了出来。

如果想始终都显示Home这个组件呢?

需要在app/config/routes.js按如下配置

var React = require('react');
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var hashHistory = ReactRouter.hashHistory;
var IndexRouter = ReactRouter.IndexRoute; var Main = require('../components/Main');
var Home = require('../components/Home'); var routes = (
<Router history={hashHistory}>
<Route path='/' component={Main}>
<IndexRouter component={Home} />
</Route>
</Router>
); module.exports = routes;

以上,IndexRouter表示始终都显示的路由。

【无状态函数式声明组件】

现在,已经习惯了按这样的方式声明组件:

var HelloWorld = React.createClass({
render: function(){
return (
<div>Hello {this.props.name}</div>
)
}
}); ReactDOM.render(<HelloWorld name='darren' />, document.getElementById('app'));

除了上面的声明方式,还有一种"无状态函数式声明方式"。当React.createClass中只有render方法的时候就可以按如下方式来替代。

function HelloWorld(props){
return (
<div>Hello {props.name}</div>
)
} ReactDOM.render(<HelloWorld name='darren' />, document.getElementById('app'));

**

【组件中的变量类型约定】**

定义组件的时候经常用到变量,这些变量的类型可以约定吗?

--答案是可以的,使用propTypes

var React = require('react');
var PropTypes = React.PropTypes;
var Icon = React.createClass({
propTypes: {
name: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
color: PropTypes.string.isRequired,
style: PropTypes.object
},
render: ...
});

【生命周期事件】

所有的组件在生命周期内有一些共同的事件。有些事件在组件与DOM的绑定或解除绑定的时候发生,有些事件在组件接受数据的时候发生。

给组件设置一些默认属性:

var Loading = React.createClass({
getDefaultProps: function(){
return {
text: 'Loading'
}
},
render: function(){
...
}
});

以上,通过getDefaultProps为组件设置了一些默认属性和其对应的值。

设置组件的初始状态:

var Login = React.createClass({
getInitialState: function(){
return {
email: '',
password:''
}
},
render: function(){
...
}
});

以上,通过getInitialState设置组件的初始状态。

组件绑定到DOM时触发的事件

比如,当组件绑定到DOM时从远程获取一些数据:

var FriendsList = React.createClass({
componentDidMount: functioin(){
return Axios.get(this.props.url).then(this.props.callback);
},
render: function(){
...
}
});

以上,通过componentDidMount在组件绑定到DOM上后发生事件。

比如,当组件绑定到DOM时设置监听:

var FriendsList = React.createClass({
componenetDidMount: function(){
ref.on('value', function(snapshot){
this.setState({
friends: snapshot.val()
});
})
},
render:...
});

组件与DOM解除绑定时触发的事件

var FriendList = React.createClass({
componentWillUnmount: function(){
ref.off();
},
render:...
});

以上,通过componentWillUnmount来触发当组件与DOM解除绑定时的事件。

当组件接受到新的属性值时触发的事件:componentWillReceiveProps

决定组件是否需要渲染的事件:shouldComponentUpdate

所有的事件大致如下图:

【this关键字】

隐式绑定

var me = {
name: 'Darren',
age:25,
sayName: function(){
console.log(this.name);
}
}; me.sayName();

以上,当调用sayName的时候,隐式用到了this,这里的this指的是点左边的me.

来看一个在嵌套函数中使用this关键字的例子。

var sayNameMixin = function(obj){
obj.sayName = function(){
console.log(this.name);
};
} var me = {
name: 'Darren',
age: 25
}; var you = {
name: 'Joey',
age: 21
}; sayNameMixin(me);
sayNameMixin(you); me.sayName();//Darren
you.sayName();//Joey

以上,sayNameMixin接受me这个参数,然后在其内部给me定义了一个sayName方法,这里的this也是值me。

再来看用构造函数创建对象,使用this关键字的例子。

var Person = function(name, age){
return {
name: name,
age: age,
sayName: function(){
console.log(this.name);
}
}
}; var jim = Person('Jim', 42);
jim.sayName();

以上,调用sayName方法的时候隐式用到了this,这里的this还是指的是点左边的jim。

所以,这里关于隐式绑定的的总结是:当隐式调用this的时候,this通常指的是点左侧的那个变量。这里的this在定义的时候就很明确。

显式绑定

1、使用call方法显式指定this关键字。

//在定义的时候this指的谁并不明确
var sayName = function(){
console.log('My name is ' + this.name);
}; var stacy = {
name: 'Stacy',
age: 34
}; sayName.call(stacy);

以上,在定义sayName方法的时候并没有明确定义this指的谁。使用call方法的时候把stacy传值给了this关键字。

call方法不仅可以指向this,还可以传递参数。

var sayName = function(lang1, lang2, lang3){
console.log('My name is ' + this.name + ' and I know ' + lang1 + ', ' + lang2 + ', and ' + lang3);
}; var stacy = {
name: 'Stacey',
age:28
}; var languages = ['JavaScript', 'Ruby', 'Pythos']; sayName.call(stacey, languages[0], languages[1], languages[2]);

以上,通过调用call方法不仅指定了this关键字,还传递了sayName方法所需的参数。

2、使用apply方法显式绑定this关键字

var sayName = function(lang1, lang2, lang3){
console.log('My name is ' + this.name + ' and I know ' + lang1 + ', ' + lang2 + ', and ' + lang3);
}; var stacy = {
name: 'Stacey',
age:28
}; var languages = ['JavaScript', 'Ruby', 'Pythos']; sayName.apply(stacey, languages);

以上,apply和call的区别可见一斑,apply接受的实参数组。

3、使用bind方法显式绑定this关键字

var sayName = function(lang1, lang2, lang3){
console.log('My name is ' + this.name + ' and I know ' + lang1 + ', ' + lang2 + ', and ' + lang3);
}; var stacy = {
name: 'Stacey',
age:28
}; var languages = ['JavaScript', 'Ruby', 'Pythos']; var newFn = sayName.bind(stacey, languages[0], languages[1], languages[2]); newFn();

以上,使用bind方法可以产生一个新的函数,再调用该函数。

New Binding

var Animal = function(color, name, type){
//this = {}
this.color = color;
this.name = name;
this.type = type;
} var zebra = new Animal('black and white', 'Zorro', 'Zebra');

以上,当定义Animal这个函数的时候,此时的this指的是一个空对象,当实例化Animal的时候this就有值了。

Window Binding

var sayAge = function(){
console.log(this.age);
} var me = {
age: 25
}; sayAge(); //undefined
window.age = 35;
sayAge(); //35

可见,当没有给this显式绑定的时候,this指的是window。

接下来,会从一个例子来体会React的方方面面......

从一个例子中体会React的基本面的更多相关文章

  1. Spark小课堂Week7 从Spark中一个例子看面向对象设计

    Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...

  2. SWT中ole/activex实践--操作word的一个例子

    http://setting.iteye.com/blog/747295 ———————————————————————————————————————————————— 这几年,做了很多word/e ...

  3. Java编程思想中关于闭包的一个例子

    Java编程思想中的一个例子,不是很理解使用闭包的必要性,如果不使用闭包,是不是有些任务就不能完成?继续探索. package InnerClass; interface Incrementable ...

  4. spring笔记--使用springAPI以及自定义类 实现AOP的一个例子

    Spring的另一个重要思想是AOP,面向切面的编程,它提供了一种机制,可以在执行业务前后执行另外的代码,Servlet中的Filter就是一种AOP思想的体现,下面通过一个例子来感受一下. 假设我们 ...

  5. 在ASP.NET MVC项目中使用React

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:最近在开发钉钉的微应用,考虑到性能和UI库的支持,遂采用了React来开发前端. 目前 ...

  6. C# 关于委托和事件的妙文:通过一个例子详细介绍委托和事件的作用;Observer模式简介

    委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见 ...

  7. [SQL]复制数据库某一个表到另一个数据库中

    SQL:复制数据库某一个表到另一个数据库中 SELECT * INTO 表1 FROM 表2 --复制表2如果只复制结构而不复制内容或只复制某一列只要加WHERE条件就好了 例子:SELECT * I ...

  8. iOS原生项目中集成React Native

    1.本文的前提条件是,电脑上已经安装了CocoaPods,React Native相关环境. 2.使用Xcode新建一个工程.EmbedRNMeituan [图1] 3.使用CocoaPods安装Re ...

  9. (转)sscanf() - 从一个字符串中读进与指定格式相符的数据

    (转)sscanf() - 从一个字符串中读进与指定格式相符的数据 sscanf() - 从一个字符串中读进与指定格式相符的数据. 函数原型: Int sscanf( string str, stri ...

随机推荐

  1. jquery 让select元素中的某个option被选中

    jquery 操作select 取值,设置选中值 博客分类: javaScript selecttextvalue取值设置选中值 比如 <select class="type" ...

  2. GNU M4 - GNU Project - 免费软件基金会(FSF)

    -------------------------------------------------------------------------------------- GNU M4介绍: GNU ...

  3. 云计算和大数据时代网络技术揭秘(十七)VOQ机制

    VOQ机制 本章介绍的VOQ是一种新型的QoS机制,目的是为了解决著名的交换机HoL难题. 但VOQ强烈依赖于调度算法,例如,一个48口的交换机,每个端口都要维护48-1个FIFO缓存队列, 一共48 ...

  4. 根据UserAgent 获取操作系统名称

    /// <summary>        /// 根据 User Agent 获取操作系统名称        /// </summary>        private sta ...

  5. 傅盛谈管理的本质zz

    管理的本质就是树立一个核心的业务,让这个业务带着所有的员工和组织构架往前走,而不是去构建一个四平八稳的组织,让所有的业务井井有条. 今天,整个互联网都在回归本原.它让以前看上去极简单的点产生爆发,而不 ...

  6. sublime text 也能矩形选择

    原来用editplus,但发现sublime text后便果断选择这个,她真的是很完美,但有一点就是不能像editplus一样矩形选择(Ctrl+鼠标左键这我知道,但感觉很麻烦)而感到小小的不爽... ...

  7. 推荐一个C语言学习教程

    Linux C编程一站式学习 http://learn.akae.cn/media/index.html

  8. Android性能优化方法(五)

    有时候,我们的页面中可能会包含一些布局,这些布局默认是隐藏的,当用户触发了一定的操作之后,隐藏的布局才会显示出来.比如,我们有一个Activity用来显示好友的列表,当用户点击Menu中的“导入”以后 ...

  9. 【概念笔记】JAVA基础 - part2

    IT`huhiu前言录 这是续JAVA基础 - part1 链接http://www.cnblogs.com/ithuhui/p/5922067.html的. 笔记慢慢在修改和补充 JAVA里面重要的 ...

  10. Session为null无法访问

    我们在一般处理程序中需要访问Session为null 无法访问和操作 处理方案: 1.导入命名空间  System.Web.SessionState 2.实现IRequiresSessionState ...