工欲善其事必先利其器, 因为react崇尚的是, react一体化. 即, 使用js拯救世界. 所以, 我们需要先将支持react的webpack 工具解决.
webpack 配置
这里主要使用的是这些模块:
"babel-loader": "^6.2.4",
"babel-core": "^6.8.0",
"babel-loader": "^6.2.4",
"babel-plugin-transform-es2015-arrow-functions": "^6.8.0",
"babel-preset-es2015": "^6.6.0",
"react-dom": "^15.2.1",
"react" : "^15.2.1"
将这些文件放到package.json中,然后使用npm install 即可.
这里就不赘述了. 先放一个最简单的webpack.config.js文件.
var path = require('path'),
node_modules = path.resolve(__dirname, 'node_modules');
module.exports = {
// context: __dirname + "/app/src/entry",
entry: {
app:"./dev/app"
}, //演示单入口文件
output: {
path: path.join(__dirname, '/dev/dist'), //打包输出的路径
filename: '[name].entry.js', //打包后的名字
},
module: {
loaders: [{
test: /\.js|\.jsx$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel?presets[]=es2015' //加载使用loader
}
]
},
plugins: [
],
watch: true
};
这里需要注意一下, 由于版本的问题, webpack 在v1.13之后, 会默认给entry文件添加extensions. 所以如果你的entry写成了,./dev/app.js
的话, 恭喜你, 踩坑了. 如果万一出错了, 你可以使用
webpack --display-error-details
来进行排查. ok , 现在我们正式进入react时间
react 入门
初期, react基本的操作无外乎就是围绕着,Component和ReactDOM进行编写和渲染。 以前, react并没有分的太细化, 不过在version 0.14版本后, ReactDOM和Component就分开了, 主要还是因为React Native的缘由.
不过, 两者分开过后,造成的影响来说, 还是不小的. 主要影响到三个方面:
ReactDOM.render(): 渲染UI层
ReactDOM.findDOMNode(): 获取节点
ReactDOM.renderToString(): 后端运用的转义String方法
上面, 我们已经下载好了react
和react-dom
. 我们可以来简单看一个demo:
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
class Hello extends Component{
render(){
return (
<h1>Hello world</h1>
);
}
}
ReactDOM.render(<Hello />,document.getElementById('container'));
重要的是Hello 类中的render方法. 通过return, 返回虚拟的DOM, 然后经过ReactDOM将虚拟的DOM, 渲染到指定节点内部.
动态内容
假如我们现在想要在render中, 使用变量,so how to do?
很简单, 只要在DOM里面用到curly braces({})即可.
class Hello extends Component{
render(){
let name="jimmy";
return (
<h1>Hello {name}</h1>
);
}
}
嵌套UI
React之所以这么流行,就是因为他的可复用性. 通过< classname />
这样的调用, 就可以完美的实现, 组件的复用
class UI_item extends Component{
render(){
return (
<UL></UL>
);
}
}
class UL extends Component{
render(){
return (
<ul>
<Li_item name="jimmy">ok</Li_item>
<Li_item name="sam">yes</Li_item>
</ul>
)
}
}
class Li_item extends Component{
render(){
return (
<li>my name is {this.props.name} and my content is {this.props.children}</li>
)
}
}
在进行组件复用时, 重用UI 和写HTML一样, 使用闭合标签,添加属性等等. 不过,这里需要注意一下, 在JSX语法中, class属性需要换成 className(因为内部冲突).
另外, 提及一下上文出席那的两个内容. this.props.xx 和this.props.children
this.props.xxx : 是用来获得节点中的属性内容. 比如: name, data-name,data-set等等.
this.props.children: 用来获取闭合tag中的内容. 比如: < div>
abc
< /div> 获取得到为:abc
this.props 在组件复用中,是占很重要的地位的, 可以说,他是parent和children 组件通信的重要通道, 父组件 赋值, 子组件处理. like:
通常来说, 写一个整体的UI 有两种方式, 一种是Top-Down, 一种是Bottom-Up. 两者都行, 只是思考的方式不一样, 不过一般TD(自顶向下)方式比较普遍些.
如果你想利用循环生成jsx的话, 可以使用:
class UL extends Component{
render(){
let tasks = [];
for(var i=1,len=this.props.number;i<=len;i++){
tasks.push(
<Li_item key={i} number={i}>list</Li_item>
)
}
return (
<ul>
{tasks}
</ul>
);
}
}
但需要注意的是, 你不能直接写为:
return (
{tasks}
)
如果这样写的话,react是不会让你通过的. 因为他认为你这个是不合法数据, 一般来说, 不能在第一层里面直接写入: arrar
,object
对象.
状态改变
react 提供另外一个状态属性-this.state
. React 通过改变父组件本身的state, 会导致DOM的重新渲染, 相当于重新调用ReactDOM.render()
.
have a try
这里, 我们通过简单的改变, 父组件的state, 来实现节点的重流和重绘.
实现的demo 放在jsFiddler上, 可以参考一下. 这里我只贴精华的demo:
class UI_item extends Component{
constructor(){
super();
this.state={
origin:true
}
}
render(){
let number = 5;
if(!this.state.origin){
number*=2;
}
return (
<div>
<button onClick={()=>{this.doubleNumber()}}></button>
<UL number={number}></UL>
</div>
);
}
doubleNumber(){
this.setState({
origin:!this.state.origin
});
}
}
上面的代码其实有点绕的. 这里补充几点, 相信看过之后,再回去看代码也就明白了.
this.setState({xxx:xxx}): 该方法是父组件的专属方法, 其可以手动的修改state的状态, 来实现状态的更新.
this.state: 初始情况下,是在constructor 方法中, 直接设置。 不过, 你也可以放在render 方法中, 这也是没问题的.
事件绑定: React中绑定事件的代码虽然有点奇葩, 但他确实是绑定事件最简洁的办法. 他会在后面的流程中,自动解析事件内容, 然后使用addEventListener进行绑定.
另外, 你新增this.props也是会重新渲染节点的.
结合,上面的this.props 可以大概了解, React 提供的精华架构.
事件绑定解析
React 之所以高性能, 不仅在于虚拟DOM的算法, 事件绑定也在一定程度提升性能. 因为React的所有事件都是绑定在document 根元素上.
比较好的绑定方式是,通过函数+bind方法进行绑定的
render(){
let number = 5;
if(!this.state.origin){
number*=2;
}
return (
<div>
<button onClick={this.doubleNumber.bind(this)}}></button>
<UL number={number}></UL>
</div>
);
}
来看一下,React 提供的事件.
from pro react
小心JSX的坑
React 在提出本身的同时, 也提出了JSX语法. 这是为什么呢?
我们可以看一下,React 是如何翻译JSX语法, 你就应该知道为什么JSX很受欢迎~
// JSX
<h1>Hello World</h1>
// React 方法调用
React.createElement("h1", null, "Hello World");
当你写出JSX 的时候,React在背后已经帮你解析成对应的方法了. 所以, 这样隐形的API, 为什么不用呢?
不过JSX 中有部分内容需要注意一下.
驼峰命名的属性
在JSX中, 给tag添加属性时,需要使用驼峰命令. 即.
// HTML
<input type="text" name="usrname" maxlength="10">
// JSX
<input type="text" name="usrname" maxLength="10"><br>
但像,这样的data-set
使用dash连接的就不需要额外的注意. 另外, react 特别强调了class
需要写为className
. 因为在React内容,class 存在冲突, 所以强制性将class property改为了className
.
标签闭合
很简单, 就是所有标签必须闭合. 比如像<img src="...">
这样的, 后面的/
可以写可不写. 但是, 现在在JSX中,所有的都必须写.
<img src="..." />
only one node
这里,应该是很多初入React 童鞋常常犯的~ 可能对函数的了解还未透彻. 比如一个函数:
function add(){
return (1,2);
}
add();
上面的结果是多少?
很简单是2. 这是解析引擎所决定的. 因为, 直接通过return 语句返回, 永远只能返回一个值.所以, 这也决定了在React中, 你使用render返回JSX时, 只能带上一个节点.
// 正确
return(
<h1>Hello World</h1>
)
// go die
return (
<h1>Hello World</h1>
<h2>Have a nice day</h2>
)
改写一下, 你只能返回一个节点:
return (
<p>
<h1>Hello World</h1>
<h2>Have a nice day</h2>
</p>
)
don't use if-else
在JSX, 一般不要乱用if-else. 因为if-else 是不会返回任何值的。
// 报错
return (
<li data-set={if(true){"ok"}}>jimmy</li>
)
推荐情况是, 使用三元运算(ternary). 因为他可以返回值.
return (
<li data-set={true?'ok':0}>jimmy</li>
)
置于为什么, 我们可以在这里剖析一下JSX语法的结构. 前文已经提到了, JSX其实就是React.createElement(). 我们的属性其实,就是当做createElment参数的
// JSX
<div className={if (condition) { "salutation" }}>Hello JSX</div>
// 翻译
React.createElement("div", {className: if (condition) { "salutation"}}, "Hello JSX");
这里, 有点基本常识的应该知道, 对象里面怎么执行... 除非你有返回值.
空格省略
React在渲染的时候,会默认忽略掉元素间的空格. 估计是考虑到了inline-block
造成的3~4px的影响.
<li >{this.props.children}_{this.props.number}
<a href="">ok</a>
<a href="">yes</a>
</li>
这样渲染的结果是
不过, 如果你仅仅是填写文本的话, 里面的空格就不会被忽略。如果, 你想在元素之间添加上空格的话. 可以这样写:
<li >
<a href="">ok</a> {" "}
<a href="">yes</a>
</li>
JSX的评论
在JSX中,写评论的话, 需要注意他的注释方式:
/* comment */
另外,如果你的注释是childElement, 则需要使用{}
括起来. 但在元素内部评论的话,就不需要了
<Nav>
{/* this is a child comment */}
<Person
/* multi line
comment */
name="sam" // end of line comment
/> </Nav>
上面的demo 基本上把所有注释情况都包含了.
动态插入HTML
以前,我们纯原始写组件的时候, 常常利用的就是element的innerHTML属性. 即, 直接在html tag里面添加string, 然后利用内部渲染,将element渲染出来.
<li>
{this.dynamicP()}
</li>
// 动态脚本
dynamicP(){
let p = "<p>123</p>";
return p;
}
但是, 这样容易犯的一个错误就是XSS, 因为XSS最常用的手段就是动态恶意脚本的插入. 有兴趣的童鞋,可以参考一下我的xss之网页安全.
React考虑到这点, 果断将所有一切插入的String进行转义.
所以, 上面渲染出来的会是这样的:
当有时候,我们又不得不使用动态的String当做HTML插入, 那应该怎么做呢?
官方给出的解决办法是, 使用dangerouslySetInnerHTML
这个属性.
<span dangerouslySetInnerHTML={{__html:dynamicP()}} />
这样, React就会对插入的HTML解除XSS的限制.
表单输入
为什么说表单输入在React里面也是一块需要注意的呢?
因为, 一旦你设置在input的value属性. 后面你所有的输入都是无效的.
<input type="search" value="React" />
so how to solve it?
这里就需要使用onChange和this.state来帮助我们进行重绘.
class Search extends Component {
constructor(){
super();
this.state={
value:"React"
}
}
render() {
return ( <div>
Search Term: <input type="search" value={this.state.value} onChange={this.handleChange.bind(this)} /> </div>
)
}
handleChange(event){
this.setState({
value:event.target.value
})
}
}
通过输入,触发onChange, 然后onChange 触发this.setState, 重新渲染DOM。get~ 线上demo
另外,表单输入,还需要注意另外两个ele. textarea和select.
textArea 在React中, textarea的书写格式和HTML中有些不同
// HTML
<textarea>This is the description.</textarea>
// React
<textarea value="This is a description." />
该element 也需要绑定onChange时间才行.
select 元素的写法和原始HTML差别不大,只是里面的选取默认的option需要改变一下。 在HTML中, 书写默认的option 只需要添加selected属性即可. 但在React, 则需要手动指定value才行.
<select value="B">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
</select>
反模式表单
这里所谓的anti-pattern(反模式) 听起来逼格挺高的. in fact, 他还是表单. 只是你不用写value属性. 而这里的反模式实际体现在, 你不用绑定onChange事件, 元素中的内容,也能随着用户的输入自动变化.
看个demo:
render() {
return (
<form>
{/*
controled component
*/}
<input type="text" value="fixed" /><br/>
{/*
uncontroled component
*/}
<input type="text" />
</form>
);
线上demo请参考: JSfiddle
那 uncontroled component 和 controled component 之间的区别是神马嘞?
简而言之, 如果你的表单需要有很多限制那么就用controled component, 如果没有可以考虑uncontroled.
我们总结一下:
内部属性trick
key的使用
React 以他Virtual DOM 而闻名遐迩, 通过造出一个Virtual DOM, 然后, 通过一种比较算法,得出最新的DOM结构, 从而让页面性能损耗降到最低. 现在我们考虑一种情况.
即, 渲染重复的list.即:
return (
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
);
当其中的几个节点需要变为:
<ul>
<li></li>
<li></li>
<li>1</li>
<li></li>
<li>2</li>
<li></li>
</ul>
React会怎么做... 他首先会提示你, 孩纸, 你这样不行... 因为, 这样的实现方法是在是太多了. CRUD 哪一种都可以实现上述行为. 所以,React需要你手动的设置一个key值,来帮助他标识, 你改动的list是哪一个~
上面的render我们就需要改为:
render() {
let lis = [];
for(var i=0;i<4;i++){
lis.push((
<li key={i} >{i}</li>
))
}
return (
<ul>
{lis}
</ul>
);
}
谨慎的refs
refs 实际上是一种,黑魔法,又可以称为语法糖. 因为他不需要我们手动的使用document.getElementxxx 来获取节点. 他已经帮你把dirty work 给干好了. 我们只需要使用this.refs.xx 来获取指定节点即可. 但是, 官方是不提倡这种做法的,因为这样破坏了数据的流向性. React本来是用过render, 来改变元素的状态, 但是, 如果从外面使用this.refs 改变 DOM的话, 有可能会造成virtual DOM和actual DOM不统一的情况.
如果你真的想用的话, 那就需要使用ref了, 这样至少可能保证DOM状态的统一. 可以说, 在React中,你基本上可以和document.xxx系列说拜拜了.
constructor(){
super();
this.number = 0;
}
render() {
return(
<div>
<button onClick={this.clickButton.bind(this)}>click me</button>
<span ref="text">I will be changed</span>
</div>
)
}
clickButton(){
let text = this.refs.text;
text.innerHTML = this.number++;
}
注意一下, 属性书写是用ref
, 获取是使用refs
. 线上demo, 参考:JSfiddle
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。