[React] 01 - Intro: javaScript library for building user interfaces

教学视频: http://www.php.cn/code/8217.html

React 教程: http://www.runoob.com/react/react-tutorial.html

本篇是菜鸡水准了解轮廓的难度等级,菜鸡啄米,叽叽喳喳


 

先介绍仨工具:

  • Babel: 用于编写下一代 JavaScript 的编译器
  • jspm: 是一个一个浏览器端包管理器;SystemJS加载js的模块,也有Babel编译js,JSX编译为jsjspm & SystemJS 教程

  • webpack: WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。什么是WebPack,为什么要使用它?

 

创建工程

Ref: https://blog.csdn.net/rical730/article/details/77206601

1. 先安装:nodejs npm

2. 新建一个项目文件夹。

mkdir React & cd React

3. 使用 create-react-app 快速构建 React 开发环境

sudo npm install -g create-react-app
create-react-app my-app

4. 安装结束

cd my-app
npm start

5. 其他(option)

sudo npm install -g browser-sync
  Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。
  更重要的是 Browsersync可以同时在PC、平板、手机等设备下进项调试。
jspm install semantic
-ui jspm install css

 

npm start 时,可见实际执行的是:npm run dev,而dev指得是另一坨东西。

babel 帮助将es6编译为es5,而后执行。

 

小马技术版本

推荐利用 脚手架 创建项目,这里提到了一个轻量版本的css库。

# Init.
(react-learning) jeffrey@JeffreyH:~/.../web$ node -v
v14.18.2

(react-learning) jeffrey@JeffreyH:~/.../reactweb$ npx -v
6.14.15

npx create-react-app reactweb   # 脚手架工具
cd reactweb/
npm start


# --------------------------
# Project structure.
# --------------------------

reactweb/
├── node_modules
├── package.json
├── package-lock.json
├── public
│   ├── favicon.ico
│   ├── index.html      # id="root", ref: 24:28
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json   # 生成pwa应用时用到的文件, 渐进式Web应用
│   └── robots.txt      # 搜索引擎优化用到的文件
├── README.md
├── run.sh
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── reportWebVitals.js
    └── setupTests.js

 

 

React 特点

总体而言,有如下几个特点:

1.声明式设计 −React采用声明范式,可以轻松描述应用。 

2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互

3.灵活 −React可以与已知的库或框架很好地配合。

4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

 

  • React JSX

Figure, 可见不适用jsx写的形式(下面),可读性很差

 

React 使用 JSX 来替代常规的 JavaScript。

JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

我们不需要一定使用 JSX,但它有以下优点:

    • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
    • 它是类型安全的,在编译过程中就能发现错误。
    • 使用 JSX 编写模板更加简单快速。

混编方式 in ReactDOM.render示范如下:

<!DOCTYPE html>
<html>

<head> <meta charset="UTF-8" /> <title>菜鸟教程 React 实例</title> <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script> <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script> <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script> </head>
<body> <div id="example"></div> <script type="text/babel"> ReactDOM.render( <div> <h1>菜鸟教程</h1> <h2>欢迎学习 React</h2> <p data-myattribute = "somevalue">这是一个很不错的 JavaScript 库!</p>   // p 元素添加了自定义属性 data-myattribute,需要使用 data- 前缀。 </div>  // 第一个参数 , document.getElementById('example')  // 第二个参数 ); </script> </body> </html>

 

  • 自己独特的命名:class, for, style

    <script>
      ReactDOM.render(
        /*
        // COUNTEREXAMPLE
        // this doesn't work
        React.DOM.h1(
          {
            class: "pretty",
            for  : "me",
          },
          "Hello world!"
        ),
        */
        
        // PROPER EXAMPLE
        // this works
        React.DOM.h1(
          {
            className: "pretty",
            htmlFor  : "me",
          },
          "Hello world!"
        ),        
        
        document.getElementById('app')
      );
    </script>

style倒是经常用到。

    <script>
      ReactDOM.render(
        /*
        // COUNTEREXAMPLE
        // this doesn't work
        React.DOM.h1(
          {
            style: "background: black; color: white; font-family: Verdana",
          },
          "Hello world!"
        ),
        */
        
        // PROPER EXAMPLE
        // this works
        React.DOM.h1(
          {
            style: {
              background: "black",
              color: "white",
              fontFamily: "Verdana",
            }
          },
          "Hello world!"
        ),      
        
        document.getElementById('app')
      );
    </script>

 

  • 独立文件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
       <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script>
       <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script>
       <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script>
  </head>
  <body>
     <div id="example"></div>
     <script type="text/babel" src="helloworld_react.js"></script>  // 另外存在单独文件更好
  </body>
</html>

 

  • 不能 if ... else

// 在 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式来替代

ReactDOM.render(
<div> <h1>{i == 1 ? 'True!' : 'False'}</h1> </div> , document.getElementById('example') );

 

  • 内联样式

React 推荐使用内联样式

我们可以使用 camelCase 语法来设置内联样式.

React 会在指定元素数字后自动添加 px 。

以下实例演示了为 h1 元素添加 myStyle 内联样式:

var myStyle = {
    fontSize: 100,
    color: '#FF0000'
};
ReactDOM.render(
    <h1 style = {myStyle}>菜鸟教程</h1>,
    document.getElementById('example')
);

数组,JSX 允许在模板中插入数组,数组会自动展开所有成员:

var arr = [
  <h1>菜鸟教程</h1>,
  <h2>学的不仅是技术,更是梦想!</h2>,
];
ReactDOM.render(
  <div>{arr}</div>,
  document.getElementById('example')
);

 

 

React 组件

  • 组件类

[传统写法] 开头大写React.createClass。

var HelloMessage = React.createClass({    // 组件类 HelloMessage
  render: function() {
    return <h1>Hello World!</h1>;
  }
});
 
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

 

[精简写法]Define ItemDetail Component.

import React from 'react';

const ItemDetail = () => {
    return (
        <div>ItemDetail</div>
    )
}

export default ItemDetail;

Use ItemDetail Component.

import React from 'react';
import ItemDetail from './components/ItemDetail';

function App() {
    return (
        <div className="App">
            <h1>I love React.</h1>
            <hr />
            <ItemDetail></ItemDetail>
        </div>
    );
}

export default App;

 

  • 传参数

向组件传递参数,可以使用 this.props 对象,实例如下:

var HelloMessage = React.createClass({
  render: function() {
    return <h1>Hello {this.props.name}</h1>;   // name 属性通过 this.props.name 来获取。
  }
});
 
ReactDOM.render(
  <HelloMessage name="Runoob" />,
  document.getElementById('example')
);

 

小马技术

需要实现如下效果。

定义如下component,该组件的样式使用的是bootstrap5.1。

 

  • 复合组件

创建多个组件来合成一个组件,把组件的 不同功能点 进行分离

var WebSite = React.createClass({
  render: function() {
    return (
      <div>
        <Name name={this.props.name} />
        <Link site={this.props.site} />
      </div>
    );
  }
});
 
var Name = React.createClass({ render: function() { return ( <h1>{this.props.name}</h1> ); } });
var Link = React.createClass({ render: function() { return ( <a href={this.props.site}> {this.props.site} </a> ); } });
ReactDOM.render(
<WebSite name="菜鸟教程" site=" http://www.runoob.com" />, document.getElementById('example') );

 

 

React State (状态)

React 把组件看成是一个 状态机(State Machines)。通过

    • 与用户的交互,实现不同状态,
    • 然后渲染 UI,让用户界面和数据保持一致。

根本思想:React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

 

小马技术

通过Form 强化学习 state variables。

    • (1) 定义“状态变量”
    • (2) 触发“状态变量”

通过state,变量的改变才能与html中的生命周期同步。

 

状态变量自动的同步赋值 by 用户输入的value。

如下可见,event.target.value通过事件自动trigger "onChange",得到了real-time changed txt value here;再通过上面的useState赋值该 “状态变量”。

 

 如上,结合prop就可以实现:父组件 参数  --> 子组件。

 这是 "参数通信",还是有“事件通信”(类似传了个函数指针)。

 

表单提交,注意,提高的具体方法是在父组件中定义,传给子组件去执行;当然,参数也是父组件已定义。

那么,子组件做了什么?只做了 value的提取和同步。 子组件的实现,更多的像是提供一个框架,让父组件使用起来更加灵活。

    • (1) 父组件定义了 init values。
    • (2) 父组件定义了 button click method。

 

  • LikeButton 组件

 子组件 若靠自己定义自己的初始化状态,则可以使用 getInitialState。

 与上面的相比,这个是个junior例子,不建议工程中使用。

 

例子:创建了 LikeButton 组件,点击后 “喜欢不喜欢” 相互切换。

    1.  getInitialState 方法用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。
    2.  当用户点击组件,导致状态变化,this.setState 方法就修改状态值,
    3.  每次修改以后,自动调用 this.render 方法,再次渲染组件。
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>菜鸟教程 React 实例</title>
    <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script>
    <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script>
    <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script>
  </head>
<body> <div id="example"></div> <script type="text/babel">
var LikeButton = React.createClass({ getInitialState: function() {             // 1 定义初始状态,也就是一个对象 return {liked: false}; }, handleClick: function(event) { this.setState({liked: !this.state.liked});    // 2 有点击事件,就改变状态:setState ----> render func }, render: function() {   // 3 渲染组件var text = this.state.liked ? '喜欢' : '不喜欢'; return ( <p onClick={this.handleClick}> // <---- 这里自定义了一个触发函数<b>{text}</b>我。点我切换状态。 </p> ); } }); ReactDOM.render( <LikeButton />, document.getElementById('example') );
</script> </body>
</html>

 

复习一下传统方式。

这里与小马技术中的写法稍有不同。

 

React Props

  • props & state

props 是不可变的:子组件只能通过 props 来传递数据

state 可以根据与用户交互来改变:有些容器组件需要定义 state 来更新和修改数据。

var HelloMessage = React.createClass({
  getDefaultProps: function() {
    return {
      name: 'Runoob'
    };
  },
  render: function() {
    return <h1>Hello {this.props.name}</h1>;
  }
});
 
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

分离的例子:(其实就是复合组件)

---------------------------------------------------------------
# 定义一个大组件

var WebSite = React.createClass({
  getInitialState: function() {
    return {
      name: "菜鸟教程",
      site: "http://www.runoob.com"
    };
  },

  render: function() {    // 1.0 在render函数中, 我们设置 name 和 site 来获取父组件传递过来的数据
    return (
      <div>
        <Name name={this.state.name} />      // 在父组件中设置 state
        <Link site={this.state.site} />
      </div>
    );
  }
});

---------------------------------------------------------------
# 定义大组件中的两个小组件

var Name = React.createClass({
  render: function() {
    return (
      <h1>{this.props.name}</h1>          // 2.1 在子组件上使用 props 将其传递到子组件上
    );
  }
});
 

var Link = React.createClass({
  render: function() {
    return (
      <a href={this.props.site}>
        {this.props.site}              // 2.2 在子组件上使用 props 将其传递到子组件上
      </a>
    );
  }
});
 
---------------------------------------------------------------

ReactDOM.render(
  <WebSite />,
  document.getElementById('example')
);
View Code

 

  • Props 传参类型验证

实例创建一个 Mytitle 组件,属性 title 是必须的且是字符串,非字符串类型会自动转换为字符串 :

var title = "菜鸟教程";
// var title = 123;  <---- 非字符串类型会自动转换为字符串 
var MyTitle = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,   <---- 对类型做了限制
  },

  render: function() {
     return <h1> {this.props.title} </h1>;
   }
});
ReactDOM.render(
    <MyTitle title={title} />,
    document.getElementById('example')
);

更多验证器说明如下:

React.createClass({
  propTypes: {
    // 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
 
    // 可以被渲染的对象 numbers, strings, elements 或 array
    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),
 
    // 特定 shape 参数的对象
    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!');
      }
    }
  },
  /* ... */
});
View Code

 

  • 获取 "子节点" - this.props.children

在render中,Child有了属性children,自然地,获取时就使用 this.props.children。

this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。

表示组件的所有子节点:【所有子节点可能有三种不同的情况】

  1. 如果当前组件没有子节点,它就是 undefined ;
  2. 如果有一个子节点,数据类型是 object
  3. 如果有多个子节点,数据类型就是 array【这个是正常情况】
var HelloWorld = React.createClass({
     render:function(){
        return (
             <ul>
              {
                   React.Children.map(this.props.children, function (value, key) {    // 1. 获取子节点
                          return <li>{value}----{key}</li>;                            // 2. 以怎样的html形式展现
                   })
               }
             </ul>
        );
     }
 });
 ReactDOM.render(
   <HelloWorld>
     <span>思考思考</span>      # <---- 这里的span就是HelloWorld的子节点
     <span>瞬间即逝</span>
     <span>阿达瓦得</span>
   </HelloWorld>,
   document.getElementById('root')
);
Result: 
思考思考—
-0 瞬间即逝—-1 阿达瓦得—-2

 

 

TodoList 小组件

2:10:20 始。

  • 定义子组件

只代表了 one row。值得注意的三个变量如下:

    • props.id
    • props.text
    • props.onDelete

 

  • 使用子组件

重点在于mytodolist.map()的使用。

 

  • 动态 style 样式单

加了样式,并且是动态的:

<h1 style={{
  color: FLG ? 'red' : 'blue',
  backgroundColor: FLG ? 'yellow' : 'gray',
}}>MyStyle</h1>

 

CSS -> StyleObject
background-color -> backgroundColor

ref: https://www.w3schools.com/jsref/dom_obj_style.asp

# backgroundColor | Sets or returns the background-color of an element
document.body.style.backgroundColor = "red";

 

  • 动态 class 样式单

较常用的是:动态 class 样式单。毕竟经常使用bootstrap。

<h1 className={FLG ? 'text-primary bg-light': 'text-danger bg-warning'}>MyStyle</h1>

 

End.

posted @ 2018-04-10 19:38  郝壹贰叁  阅读(216)  评论(0编辑  收藏  举报