React,我从未见过如此好看的教程(1)

96
Yugo
2015.11.03 17:10* 字数 5415
跟我一起学 React 😄,只为了缓解大家学习React的痛苦。
懂得也不多,所以有神马我理解错误的地方,请指正。
React 各大BAT都在用的框架,学到就是赚到。

这是我的 Learn 笔记,同样也是一份简明教程。

列一下资料:
React英文版指南

# 先来准备一个基本页面
已经替换为国内的 CDN。
react.js -> React主要核心
react-dom.js -> React DOM 操作
browser.min.js -> 将 babel 转换成浏览器可用的 ES5
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React</title>
    <script src="http://cdn.bootcss.com/react/0.14.1/react.js"></script>
    <script src="http://cdn.bootcss.com/react/0.14.1/react-dom.js"></script>
    <script src="http://cdn.bootcss.com/babel-core/5.8.32/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">

      // ** Your code goes here! **

    </script>
  </body>
</html>
# 写一个简单时间(它看起来想这样)

 props -> properties 属性们
 render -> 渲染
 ReactDom.render(实例化的组件,要渲染到得对象)

 在HelloWorld组件中,传入了一个data属性,对应的是一个Date对象。
 在组件中,通过 this.props.date 拿到Date对象,调用它的toTimeString方法。
 而且本例中我们是用了一个定时器,每500ms我们实例化渲染一次组件,更新时间的值。
 <HelloWorld date={new Date()}/> 这是jsx语法,有点类似于HTML5自定义语义标签吧。
 可以这么理解,每一个标签就是一个组件,一个对象,因为我们是用的React.createClass方法去创建的,从方法名上就可以看出。
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React</title>
    <script src="http://cdn.bootcss.com/react/0.14.1/react.js"></script>
    <script src="http://cdn.bootcss.com/react/0.14.1/react-dom.js"></script>
    <script src="http://cdn.bootcss.com/babel-core/5.8.32/browser.min.js"></script>

  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">

      var HelloWorld = React.createClass({
        render: function() {
          return (
            <p>
              你好, <input type="text" placeholder="输入你的名字"/>! <br/><br/>
              现在的时间是:{this.props.date.toTimeString()}
            </p>
          );
        }
      });

      setInterval(function() {
        ReactDOM.render(
          <HelloWorld date={new Date()}/>,
          document.getElementById('example')
        );
      }, 500);

    </script>
  </body>
</html>
# 让我们细化知识点吧(JSX语法)
var myDivElement = <div className="foo" />;
// className 就代表 class属性,因为怕属性冲突,所有就另外给了一个名字。
ReactDOM.render(
  myDivElement, 
  document.getElementById('example')
);
// 输入:
React.createElement(Profile, null, "click")
// 转义输出为:
<Profile>click</Profile>
var Nav, Profile;
// 输入 (JSX):
var app = <Nav color="blue"><Profile>click</Profile></Nav>;

// 转义输出为 (JS):
var app = React.createElement(
  Nav,
  {color:"blue"},
  React.createElement(Profile, null, "click")
);
# 组件的命名空间(JSX语法)
// App组件就把这几个组件整合,打包起来了。
var Form = MyFormComponent;
var FormRow = Form.Row;
var FormLabel = Form.Label;
var FormInput = Form.Input;

var App = (
  <Form>
    <FormRow>
      <FormLabel />
      <FormInput />
    </FormRow>
  </Form>
);

//用子组件的方式去整合打包
var MyFormComponent = React.createClass({ ... });

MyFormComponent.Row = React.createClass({ ... });
MyFormComponent.Label = React.createClass({ ... });
MyFormComponent.Input = React.createClass({ ... });

//使用 React.createElement 的第三个参数
var App = (
  React.createElement(Form, null,
    React.createElement(Form.Row, null,
      React.createElement(Form.Label, null),
      React.createElement(Form.Input, null)
    )
  )
);
# 布尔属性、表达式与注释(JSX语法)
// 禁用样式的按钮
<input type="button" disabled />;
<input type="button" disabled={true} />;

// 正常使用的按钮
<input type="button" />;
<input type="button" disabled={false} />;

// 输入 (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// 三元运算符,window.isLoggedIn 存在输出<Nav />组件,否则输出<Login/>组件

// 输出 (JS):
var content = React.createElement(
  Container,
  null,
  window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);


var content = (
  <Nav>
    {/* 子组件注释,加上 {} 花括号 */}
    <Person
      /* 组件
         属性
         注释
       */
      name={window.isLoggedIn ? window.name : ''} // end of line comment
    />
  </Nav>
);
# 属性传入组件的多种方式(JSX语法)
//变量放在{}中
var component = <Component foo={x} bar={y} />;


var component = <Component />;
component.props.foo = x; // 不推荐,最丑的做法
component.props.bar = y; // 不推荐,颜值低得人可以这么干

//传入对象的方式传入属性
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;

//注意,后面会覆盖前面的
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 输出为'override'
# JSX 陷阱(JSX做了一些处理防止XSS攻击)
// 会显示 “First · Second” 
<div>{'First · Second'}</div>

// 它会显示 "First · Second"
<div>First · Second</div>

// 正确做法,帅的人都这么干
<div>{'First \u00b7 Second'}</div>
<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>
// 同时你还可以这样玩,加上[],以数组的形式。
<div>{['First ', <span>·</span>, ' Second']}</div>

//但是有的适合,我的项目中就需要这样干,就要原来的。
//给dangerouslySetInnerHTML传入一个对象,其中有一个__html属性,其中写了你的文本。
<div dangerouslySetInnerHTML={{__html: 'First · Second'}} />

//假如你想加上自定义属性,必须加上data-前缀
//以aria-开头的属性页可以被渲染出来
<div data-custom-attribute="foo" />
<div aria-hidden={true} />
# 组件生命周期(别纠结这是干什么,纠结你就输了,先看一看文字,不用去弄懂他们,跳过都行,细节后面在讲解)
初始化阶段
getDefaultPropos:只调用一次,实力之间共享引用
getInitialState:初始化每个实例特有的状态
componentWillMount:render之前最后一次修改状态的机会
render:只能访问this.props和this.state,只有一个顶层组件,不允许修改状态和DOM输出
componentDidMount:成功render并渲染完成真实DOM后触发,可以修改DOM
运行中阶段
componentWillReceiveProps:父组件修改属性触发,可以修改新属性,修改状态
shouldComponentUpdate:返回false会阻止render调用
componentWillUpeate:不能修改属性和状态
render:只能访问this.props和this.state,只有一个顶层组件,不允许修改状态和DOM输出
componentDidUpdate:可以修改DOM
销毁阶段:
componentWillUnMount:在删除组件之前进行清理操作,比如计时器和事件监听器。

附上一些我当初的截图笔记,可以看出他们运行顺序(当初用的是JSX转换库):

初始化阶段

<script type="text/jsx">
$(function(){
      var count = 0;
      var style={
        color:"red",
        border:"1px #000 solid"
      };
      var HelloWorld = React.createClass({
        getDefaultProps:function(){
          console.log(1);
          return {name:"Yugo"};
        },
        getInitialState:function(){
          console.log(2);
          return {
              myCount: count = count+1 ,
              ready: "false"
            };
        },
        componentWillMount:function(){
          console.log(3);
          // 修改状态
          this.setState({ready:"true"});
          },
        render:function(){
          console.log(4);
          return <p ref="childp">Hello,{this.props.name?this.props.name:"World"}<br/> {this.state.ready} {this.state.myCount}</p>;
        },
        componentDidMount:function(){
          console.log(5);
          //改变DOM
          $(React.findDOMNode(this)).append("666666");
        },
      })
      React.render(<div style={style}><HelloWorld></HelloWorld></div>,document.body)
});
</script>

初始化阶段事件顺序

运行中阶段

     var style={
        color:"red",
        border:"1px #000 solid"
      };
      var HelloWorld = React.createClass({
          componentWillReceiveProps:function(){console.log(1);},
          shouldComponentUpdate:function(){console.log(2);return true;},
          componentWillUpdate:function(){console.log(3);},
          render:function(){
            console.log(4);
            return <p>Hello,{this.props.name?this.props.name:"World"}</p>;
          },
          componentDidUpdate:function(){console.log(5);},
      });
      var HelloUniverse = React.createClass({
        getInitialState:function(){
          return {name:''};
        },
        handleChange:function(event){
          this.setState({name: event.target.value})
        },
        render:function(){
          return <div>
          <HelloWorld name={this.state.name}></HelloWorld>
          <br/>
          <input type="text" onChange={this.handleChange} />
          </div>
        },
      });
      React.render(<div style={style}><HelloUniverse></HelloUniverse></div>,document.body)

运行中阶段事件顺序

销毁阶段

 var style={
        color:"red",
        border:"1px #000 solid"
      };
      var HelloWorld = React.createClass({
          render:function(){
            console.log(4);
            return <p>Hello,{this.props.name?this.props.name:"World"}</p>;
          },
          componentWillUnmount:function(){
            console.log("Delete`````````!");
          },
      });
      var HelloUniverse = React.createClass({
        getInitialState:function(){
          return {name:''};
        },
        handleChange:function(event){
          if(event.target.value == 123){
            React.unmountComponentAtNode(document.body);
            return;
          }
          this.setState({name: event.target.value})
        },
        render:function(){
          return <div>
          <HelloWorld name={this.state.name}></HelloWorld>
          <br/>
          <input type="text" onChange={this.handleChange} />
          </div>
        },
      });
      React.render(<div style={style}><HelloUniverse></HelloUniverse></div>,document.body)

销毁阶段
# 来一个简单小栗子(根据状态去做不同的行为)
state -> 状态
React.createClass方法,我们是传入的一个对象。
而这个对象的生命周期分为三个,初始化、运行中、销毁阶段。
再介个栗子中,getInitialState是属于初始化阶段的。
他返回了一个对象,这个对象中包含组件的状态。
我们可以通过this.state去获得这个对象。
通过setState(data, callback)方法去设置状态,同样是传入对象。
onClick就是鼠标单击事件喽,鼠标单击执行自定义函数handleClick。
var LikeButton = React.createClass({
  getInitialState: function() {
    return {liked: false};
  },
  handleClick: function(event) {
    this.setState({liked: !this.state.liked});
  },
  render: function() {
    var text = this.state.liked ? 'like' : 'haven\'t liked';
    return (
      <p onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </p>
    );
  }
});

ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);
什么时候使用状态(百度翻译了一下原文)?
你的大部分组件应该只需从props中取一些数据并渲染它。然而,
有时你需要对用户输入、服务器请求或时间的推移作出反应。为此您使用state状态。
尽可能保持尽可能多的组件成为可能的state状态。
通过这样做,您将分离到它最合乎逻辑的地方,并尽量减少冗余。
一个常见的模式是创建多个无状态的组件,只是提供props数据,
并有一个有state状态的组件上面,通过其state状态通过子组件的层次。
有状态组件封装了所有的交互逻辑,而无状态的组件专注呈现数据。
建设一个有状态的组件时,考虑其状态的最小可能性,
this.state应该只包含需要代表你的UI状态的最小数据量,不要包含计算数据、反应元件(基于基本的道具和状态建立他们)

用我自己的话说吧(2个小栗子):
我能吐槽这个最小可能性,和最小数据量吗?


当需要改变组件的行为的时候,我们可能要去判断状态。
🌰一:有病状态就该吃药,没病也别没事瞎嗑药,容易出事,但是有病的还是少数人。

🌰二:师傅有五个徒弟,分为大师兄徒弟(武力值最高),和其他徒弟俩类。
师傅出去云游,估计某处青楼里创作惊世神功,大师兄通常是代理师傅。根据门派的状态去给其他师弟师妹派任务。
大师兄:二师弟,厨房木有米了,你负责下山去买米。
       只见大师兄拿出笔和纸,写了一大页满的。考虑到师傅10年后回来的概率(20%),与各位师兄弟的饭量,刚刚好200万。
       买200万斤吧!,多一斤也不要,我就要这个最小数据量,不包含其他算法处理,不要因为价格,带的钱不够,就买少了,就要实实在在的这么多,带上神兽麒麟,驼回来,考虑到200年之后没米吃,对,就这么干。
       还有你不要有反应元件,我知道你比较冲动,不能用为老板给的价格不合理,你就砍了他,咱门派的人不乱杀人,摊上事了,别冲动,好好解决。跟他谈个好几年的价格也是可以的。
二师弟:( ⊙ o ⊙ )啊!!!,师兄高瞻远就,我这就去办。
      (尼玛,考虑最小可能性也没必要这么干吧,200年啊!!!,还最小数据量,尼玛怎么算得啊!拉格朗日一下就出来了?)
大师兄:三师妹,你就负责服侍大师兄我吧!来,先捶捶腿。
三师妹:~~~~(>_<)~~~~ 
大师兄:四师弟,马桶堵了,你去掏马桶。
四师弟:哦!(逼了狗了~,还不如买米去。)
大师兄:五师弟,念你年纪小,上山去砍柴,多锻炼才能长得结实。
五师弟:.....(默默背上柴刀走了)
故事就是这样,这个故事告诉我们,要么做大师兄,要么回家种田。
(管事的,管状态的越少越好,一个最好,这样才不会乱。)
# 看看大师兄和师弟们是如何协作的。

介个是一个评论中的头像组件

// Avatar组件包含2个子组件 ProfilePic、ProfileLink。
// ProfilePic:个人头像
// ProfileLink:个人主页链接
// Avatar组件传入了username属性props
// 通过 this.props.username 再传递给子组件
var Avatar = React.createClass({
  render: function() {
    return (
      <div>
        <ProfilePic username={this.props.username} />
        <ProfileLink username={this.props.username} />
      </div>
    );
  }
});

var ProfilePic = React.createClass({
  render: function() {
    return (
      <img src={'https://graph.facebook.com/' + this.props.username + '/picture'} />
    );
  }
});

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

ReactDOM.render(
  <Avatar username="pwh" />,
  document.getElementById('example')
);
就如同大师兄知道没米,叫二师弟去买米。
师傅留下麒麟,没带走,然后又交给二师弟去驮米一样。
(不知道师傅回来会不会一巴掌打屎他)
# 关于子组件
<Parent><Child /></Parent>
//通过 this.props.children 可以操作子组件

// 第一次输出
<Card>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</Card>

// 第二次更新输出
<Card>
  <p>Paragraph 2</p>
</Card>

//Paragraph 1 将会被移除掉
今天是第二天,时间是1点半。
惆怅,今天11点钟起来,帮管朋友的Server2003服务器(老的不像样)挂掉了。
最近逃课也经常被点到,我也是醉了。
言归正传,继续学习。
# 动态组件
array.map(value)函数遍历一个数组。
result.id 是起了标识作用
render: function() {
    var results = this.props.results;
    return (
      <ol>
        {results.map(function(result) {
          return <li key={result.id}>{result.text}</li>;
        })}
      </ol>
    );
  }
标识id应该在组件上,
// 错误!丑的人都这样干,反正我是不这样干。
var ListItemWrapper = React.createClass({
  render: function() {
    return <li key={this.props.data.id}>{this.props.data.text}</li>;
  }
});
var MyComponent = React.createClass({
  render: function() {
    return (
      <ul>
        {this.props.results.map(function(result) {
          return <ListItemWrapper data={result}/>;
        })}
      </ul>
    );
  }
});

// 正确 :)
var ListItemWrapper = React.createClass({
  render: function() {
    return <li>{this.props.data.text}</li>;
  }
});
var MyComponent = React.createClass({
  render: function() {
    return (
      <ul>
        {this.props.results.map(function(result) {
           return <ListItemWrapper key={result.id} data={result}/>;
        })}
      </ul>
    );
  }
});
# PropTypes,类型约束
为了性能考虑,只在开发环境验证 propTypes
对于我这种没有队友的人来说,感觉鸡肋,多此一举,大家不喜欢就跳过吧~我也跳了。
React.createClass({
  propTypes: {
    // 可以声明 prop 为指定的 JS 基本类型。默认
    // 情况下,这些 prop 都是可传可不传的。
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,

    // 所有可以被渲染的对象:数字,
    // 字符串,DOM 元素或包含这些类型的数组。
    optionalNode: React.PropTypes.node,

    // React 元素
    optionalElement: React.PropTypes.element,

    // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    optionalMessage: React.PropTypes.instanceOf(Message),

    // 用 enum 来限制 prop 只接受指定的值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

    // 指定的多个对象类型中的一个
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),

    // 指定类型组成的数组
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

    // 指定类型的属性构成的对象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

    // 特定形状参数的对象
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),

    // 以后任意类型加上 `isRequired` 来使 prop 不可空。
    requiredFunc: React.PropTypes.func.isRequired,

    // 不可空的任意类型
    requiredAny: React.PropTypes.any.isRequired,

    // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接
    // 使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  },
});
# 把自身所有属性,传给子组件
var CheckLink = React.createClass({
  render: function() {
    // 这样会把 CheckList 所有的 props 复制到 <a>
    return <a {...this.props}>{'√ '}{this.props.children}</a>;
  }
});

ReactDOM.render(
  <CheckLink href="/checked.html">
    Click here!
  </CheckLink>,
  document.getElementById('example')
# Mixins(学过less、sass可能对这个比较熟悉)
说白了这货就是一个代码块,在哪用,就原封不动复制到哪。
不过要放在这个 mixins 对应的数组里面。
假如传入的多个mixins里面包含多个生命周期函数。
保证都会执行到得,首先按 mixin 引入顺序执行 mixin 里方法,
最后执行组件内定义的方法。
第一步,从getInitialState 得到初始化 seconds = 0
第二步,componentWillMount 中给当前对象挂了一个intervals数组。
第三步,render渲染模板到页面
第四步,componentDidMount,DOM渲染完毕后。
       调用 this.setInterval(this.tick, 1000)。
第五步,this.intervals.push(setInterval.apply(null, arguments));
       给intervals数组推入一个函数。里面一直就只有一个元素。
       至于为什么推入数组中,是为了闭包,更好的销毁。
       这个函数是系统的setInterval定时器函数。
       用apply触发,第一个参数是this代表的值,第二个是参数。
       setInterval是在全局window对象下面的。
       而当前环境的this,是 React 组件对象,所以才用apply方法。
       每一秒调用一次 this.tick,更新 seconds的状态值。
第六步,更新状态重新渲染。       
第七步,1秒后继续更新状态再渲染。

在控制台中的输出顺序是: 1234563636363....
var SetIntervalMixin = {
  componentWillMount: function() {
      console.log(2);

    this.intervals = [];
  },
  setInterval: function() {
      console.log(5);

    this.intervals.push(setInterval.apply(null, arguments));
  },
  componentWillUnmount: function() {
      console.log(7)
    this.intervals.map(clearInterval);
  }
};

var TickTock = React.createClass({
  mixins: [SetIntervalMixin], // 引用 mixin
  getInitialState: function() {
      console.log(1);
    return {seconds: 0};
  },
  componentDidMount: function() {
      console.log(4);

    this.setInterval(this.tick, 1000); // 调用 mixin 的方法
  },
  tick: function() {
      console.log(6);

    this.setState({seconds: this.state.seconds + 1});
  },
  render: function() {
      console.log(3);
      // console.log(this.intervals);
    return (
      <p>
        React has been running for {this.state.seconds} seconds.
      </p>
    );
  }
});

ReactDOM.render(
  <TickTock />,
  document.getElementById('example')
);
# 把自身部分传给子组件
利用了ES6的析构特性
var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }


关于bind方法
this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var getX = module.getX;
getX(); // 9, 因为在这个例子中,"this"指向全局对象

// 创建一个'this'绑定到module的函数
var boundGetX = getX.bind(module);
boundGetX(); // 81
var FancyCheckbox = React.createClass({
  render: function() {
    var { checked, ...other } = this.props;
    var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
    // `other` 包含 { onClick: console.log } 但 checked 属性除外
    return (
      <div {...other} className={fancyClass} />
    );
  }
});
ReactDOM.render(
  <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
    Hello world!
  </FancyCheckbox>,
  document.getElementById('example')
);
# 事件与表单组件
event.target -> 代表事件产生的对象(事件目标)
event.target.value -> 事件目标的value属性值
//受限组件,你要是把值直接写成Value属性。
//那你就什么都输入不了。
var FancyCheckbox = React.createClass({
  render: function() {
    return <input type="text" value="Hello!" />;
  }
});
ReactDOM.render(
  <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
    Hello world!
  </FancyCheckbox>,
  document.getElementById('example')
);

//设置初始值用 defaultValue
 render: function() {
    return <input type="text" defaultValue="Hello!" />;
  }

// 多选
  <select multiple={true} value={['B', 'C']}>

// 这样使用更为优雅,这是一个受限组件
<textarea name="description" value="This is a description." />

//正确的做吧是把它定义为状态,当值改变触发更新
 getInitialState: function() {
    return {value: 'Hello!'};
  },
  handleChange: function(event) {
    this.setState({value: event.target.value});
  },
  render: function() {
    var value = this.state.value;
    return <input type="text" value={value} onChange={this.handleChange} />;
  }
# React的虚拟DOM
我们可以知道React显示出来是在render渲染到页面之后。
而在之前的DOM标签全是虚拟的,所以才能让运行速度非常的快。
但是我们也可以照样操作DOM,不过要等真实DOM渲染之后。
# ref 属性
<input ref="myInput" />
var input = this.refs.myInput; // 得到实例
// this.refs['myInput'] 也可以
var inputValue = input.value; // 得到value属性值
var inputRect = input.getBoundingClientRect();  //得到React实例
ES6新特性
(ref) => this.myTextInput = ref

等于

function(ref){
  return this.myTextInput = ref
}
var MyComponent = React.createClass({
  handleClick: function() {
    //使得实例获得焦点
    this.myTextInput.focus();
  },
  render: function() {
    // 假如ref是一个函数,那么会传一个this对象到为参数传进去
   //(好像这样才解释的通)
    return (
      <div>
        <input type="text" ref={(ref) => this.myTextInput = ref} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.handleClick}
        />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);
这几天,有点忙,老是被朋友叫去帮他们调试JSP,一调一晚上。
对此事,我只想说一句,我在北方的寒夜里,玩着灵车漂移。
突然想多看看视频,自己了解深刻一点在写。
So Sorry!朋友们
接下来会有React事件,Mixin深入(源码阅读,双向绑定),动画,尽请期待。
前端