加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

react(一)

发布时间:2020-12-15 20:27:51 所属栏目:百科 来源:网络整理
导读:1、es6的class、箭头函数 1)ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字 ,可以定义类。 class Point { constructor(x,y) { this .x = x; this .y = y; } toString() { return ‘(‘ + this .x + ‘,

1、es6的class、箭头函数

1)ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

class Point {
  constructor(x,y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return ‘(‘ + this.x + ‘,‘ + this.y + ‘)‘;
  }
}

var p1 = new Point(1,1);
console.log(p1.toString()); // (1,1)
console.log(typeof Point,Point === Point.prototype.constructor); // function true

上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法

Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错

上面代码表明,类的数据类型就是函数,类本身就指向构造函数。使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致

构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面

class Point {
  constructor() {
    // ...
  }
  toString() {
    // ...
  }
}

// 等同于
Point.prototype = {
  constructor() {},toString() {}
};

类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__; //true

上面代码中,p1p2都是Point的实例,它们的原型都是Point.prototype,所以__proto__属性是相等的。

__proto__?并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性,但依旧不建议在生产中使用该属性,避免对环境产生依赖。生产环境中,我们可以使用?Object.getPrototypeOf?方法来获取实例对象的原型,然后再来为原型添加方法/属性。

2)Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

class ColorPoint extends Point {
}

上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。

class ColorPoint extends Point {
  constructor(x,y,color) {
    super(x,y); // 调用父类的constructor(x,y)
    this.color = color;
  }
  toString() {
    return this.color + ‘ ‘ + super.toString(); // 调用父类的toString()
  }
}
let cp = new ColorPoint(1,1,‘red‘); 
console.log(cp,cp.toString()); // ColorPoint {x: 1,y: 1,color: "red"} "red (1,1)"

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。

如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

class ColorPoint extends Point {
}
// 等同于
class ColorPoint extends Point {
  constructor(...args) { super(...args); }
}
let cp = new ColorPoint(25,8,‘green‘);
cp instanceof ColorPoint // true
cp instanceof Point // true

上面代码中,子类ColorPoint实例对象cp同时是ColorPointPoint两个类的实例,这与 ES5 的行为完全一致。

?3)ES6 允许使用“箭头”(=>)定义函数

var f = v => v;
// 等同于
var f = function (v) {
  return v;
};

如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

var f = () => 5;
// 等同于
var f = function () { return 5 };

var sum = (num1,num2) => num1 + num2;
// 等同于
var sum = function(num1,num2) {
  return num1 + num2;
};

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

var sum = (num1,num2) => { return num1 + num2; }

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错

// 报错
let getTempItem = id => { id: id,name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id,name: "Temp" });

?

2、react

参考:http://www.voidcn.com/article/p-rkowmlmt-bnx.html

http://www.css88.com/react/docs/try-react.html

  • 第一个简单的react程序

参考:http://www.css88.com/react/docs/cdn-links.html

<div id="root"></div>
<!-- react.js 是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能 -->
<script crossorigin src="https://unpkg.com/[email?protected]/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/[email?protected]/umd/react-dom.development.js"></script>
<!-- 上面的版本只适合开发环境,不适合生产环境。下面为压缩优化版本
<script crossorigin src="https://unpkg.com/[email?protected]/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/[email?protected]/umd/react-dom.production.min.js"></script> -->
<!-- babel:将 JSX 语法转为 JavaScript 语法,这一步很消耗时间,实际上线的时候,应该将它放到服务器完成 -->
<script src="https://unpkg.com/[email?protected]/babel.min.js"></script>

<!-- <script> Uncaught SyntaxError: Unexpected token <-->
<!-- 
控制台有警告:You are using the in-browser Babel transformer. Be sure to precompile your scripts for production
网页中实时将ES6代码转为ES5,对性能会有影响。生产环境需要加载已经转码完成的脚本 
React 独有的 JSX 语法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel"
-->
<script type="text/babel">
const element = <h1>Hello,world</h1>;
ReactDOM.render(
  element,document.getElementById(root)
);
</script>

浏览器渲染结果:

a)crossorigin(实现更好的错误处理体验)

引入跨域的脚本(比如用了 apis.google.com 上的库文件),如果这个脚本有错误,因为浏览器的限制(根本原因是协议的规定),是拿不到错误信息的。当本地尝试使用?window.onerror?去记录脚本的错误时,跨域脚本的错误只会返回?Script error。但 HTML5 新的规定,是可以允许本地获取到跨域脚本的错误信息,但有两个条件:一是跨域脚本的服务器必须通过?Access-Control-Allow-Origin?头信息允许当前域名可以获取错误信息,二是当前域名的?script标签也必须指明?src?属性指定的地址是支持跨域的地址,也就是 crossorigin 属性。(参考:https://www.chrisyue.com/what-the-hell-is-crossorigin-attribute-in-html-script-tag.html)

b)更通用格式?UMD(Universal Module Definition)-希望提供一个前后端跨平台的解决方案

?UMD的实现很简单,先判断是否支持NodeJS模块格式(exports是否存在),存在则使用NodeJS模块格式。再判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。前两个都不存在,则将模块公开的全局(window或global)。

c)ReactDOM.render 是 React 的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点。

  • JSX(JavaScript eXtension)-?允许 HTML 与 JavaScript 混写

参考:https://segmentfault.com/q/1010000003877594/a-1020000003878406

JavaScrip 的一种扩展语法,推荐在 React 中使用这种语法来描述 UI 信息。React为了代码的可读性、更方便地创建虚拟DOM等原因,加入了一些类似XML的语法扩展。JSX是可选的,对于使用 React 而言不是必须的。

JSX 的基本语法规则:遇到 HTML 标签(以?<?开头),就用 HTML 规则解析;遇到代码块(以?{?开头),就用 JavaScript 规则解析

JSX代码并不能直接运行,需要将它编译成正常的JavaScript表达式才能运行,jsxTransformer.js就是这一编译器的角色。React官方博客在2015年6月发布了一篇文章,声明用于JSX语法解析的编译器JSTransform已经过期,不再维护,React JS和React Native已经全部采用第三方Babel的JSX编译器实现。Babel作为专门的JavaScript语法编译工具,提供了更为强大的功能。

function formatName(user) {
  return user.firstName + ‘ ‘ + user.lastName;
}
const user = { firstName: ‘Harper‘,lastName: ‘Perez‘ };
const title = ‘我是h1‘;
const element = ( <div>
        <h1 title={title}>Hello,{formatName(user)}!</h1>
        <img className="img" src="http://img2.imgtn.bdimg.com/it/u=3723784612,2573513060&fm=200&gp=0.jpg"/>
    </div>
);

ReactDOM.render(
  element,document.getElementById(‘root‘)
);

为便于阅读,我们将 JSX 分割成多行。我们推荐使用括号将 JSX 包裹起来,虽然这不是必须的,但这样做可以避免分号自动插入的陷阱。

在属性中嵌入 JavaScript 表达式时,不要使用引号来包裹大括号。否则,JSX 将该属性视为字符串字面量而不是表达式。对于字符串值你应该使用引号,对于表达式你应该使用大括号,但两者不能同时用于同一属性

jsx里的标签都应该闭合。

比起 HTML , JSX 更接近于 JavaScript , 所以 React DOM 使用驼峰属性命名约定,而不是HTML属性名称。例如,class?在JSX中变为className。

render()函数中返回的所有元素需要包裹在一个"根"元素里面。

  • 组件(Components) 和 属性(Props)

Props 是只读的:无论你用函数或类的方法来声明组件,它都无法修改其自身 props.

1)函数式组件

所有的React组件都有一个render函数,它指定了React组件的HTML输出。

<div id="root"></div>

以下代码在页面上渲染 “Hello,Sara”?

// 接收一个 props 参数,并返回一个 React 元素
function Welcome(props) {
  return <h1>Hello,{props.name}</h1>;
}
// 用户定义组件(Welcome 组件)将 JSX 属性以一个单独对象的形式传递给相应的组件,我们将其称为 “props” 对象
const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,document.getElementById(‘root‘)
);

组件名称总是以大写字母开始,否则会报错;组件可以在它们的输出中引用其它组件

function Welcome(props) {
  return <h1>Hello,{props.name}</h1>;
}
function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,document.getElementById(‘root‘)
);

2)提取组件

不要害怕把一个组件分为多个更小的组件。提取组件可能看起来是一个繁琐的工作,但是在大型的 Apps 中可以回报给我们的是大量的可复用组件。一个好的经验准则是如果你 UI 的一部分需要用多次 ,或者本身足够复杂,最好的做法是使其成为可复用组件

3)类组件

类组件允许我们在其中添加本地状态和生命周期钩子

class Clock extends React.Component {
  // 添加一个类构造函数初始化this.state
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  // 挂载—组件输出被渲染到 DOM 之后运行(设置定时器)
  componentDidMount() {
    // 如果需要存储一些不用于视觉输出的内容,则可以手动向类中添加额外的字段,如下面的timerID
    // 如果在 render() 方法中没有被引用,它不应该出现在 state 中
    this.timerID = setInterval(
      () => this.tick(),1000
    );
  }
  // 卸载—DOM 被销毁时运行(清除计时器)
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
  tick() {
    this.setState({ date: new Date() });
  // this.state.date = new Date(); //这样将不会重新渲染一个组件
  }
  render() {
    return (
      <div>
        <h1>Hello,world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,document.getElementById(‘root‘)
);

分析:

  1. 当?<Clock />?被传入?ReactDOM.render()?时,React 会调用?Clock组件的构造函数。 因为?Clock?要显示的是当前时间,所以它将使用包含当前时间的对象来初始化?this.state?。我们稍后会更新此状态。

  2. 然后 React 调用了?Clock?组件的?render()?方法。 React 从该方法返回内容中得到要显示在屏幕上的内容。然后,React 然后更新 DOM 以匹配?Clock?的渲染输出。

  3. 当?Clock?输出被插入到 DOM 中时,React 调用?componentDidMount()?生命周期钩子。在该方法中,Clock?组件请求浏览器设置一个定时器来一次调用?tick()

  4. 浏览器会每隔一秒调用一次?tick()方法。在该方法中,?Clock?组件通过?setState()?方法并传递一个包含当前时间的对象来安排一个 UI 的更新。通过?setState(),React 得知了组件?state(状态)的变化,随即再次调用?render()?方法,获取了当前应该显示的内容。 这次,render()?方法中的?this.state.date?的值已经发生了改变, 从而,其输出的内容也随之改变。React 于是据此对 DOM 进行更新。

  5. 如果通过其他操作将?Clock?组件从 DOM 中移除了,React 会调用?componentWillUnmount()生命周期钩子,所以计时器也会被停止。

正确地使用 State(状态)

?a)不要直接修改 state。

例如,这样将不会重新渲染一个组件:this.state.date = new Date();

用?setState()?代替:this.setState({ date: new Date() });

唯一可以分配?this.state?的地方是构造函数。

b)state更新可能是异步的

React 为了优化性能,有可能会将多个?setState()?调用合并为一次更新

class Clock extends React.Component {
  // 添加一个类构造函数初始化this.state
  constructor(props) {
   super(props);
this.state = { date: new Date(),counter: 0 }; } // 挂载—组件输出被渲染到 DOM 之后运行(设置定时器) componentDidMount() { this.timerID = setInterval( () => this.tick(),1000 ); } // 卸载—DOM 被销毁时运行(清除计时器) componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); this.setState((prevState,props) => ({ counter: prevState.counter + parseInt(props.increment) })); } render() { return ( <div> <h1>Hello,world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> <h2>counter:{this.state.counter}</h2> </div> ); } } ReactDOM.render( <Clock increment = ‘2‘/>,document.getElementById(‘root‘) );

因为?this.props?和?this.state?可能是异步更新的,你不能依赖他们的值计算下一个state。例如,以下代码可能导致?counter(计数器)更新失败

// 错误
this.setState({
  counter: this.state.counter + this.props.increment,});

要弥补这个问题,使用另一种 setState() 的形式,它接受一个函数而不是一个对象。这个函数将接收前一个状态作为第一个参数,应用更新时的 props 作为第二个参数

// 正确
this.setState((prevState,props) => ({
  counter: prevState.counter + props.increment
}));

?c)state更新会被合并

当你调用?setState(), React 将合并你提供的对象到当前的状态中。this.setState({comments})会完全替换this.state.comments

4)数据向下流动

一个组件可以选择将 state(状态) 向下传递,作为其子组件的 props(属性)。这通常称为一个“从上到下”,或者“单向”的数据流。任何 state始终由某个特定组件所有,并且从该 state导出的任何数据 或 UI 只能影响树中 “下方” 的组件

class Clock extends React.Component {
  // ...
  render() {
    return (
      <div>
        <FormattedDate date={this.state.date}/>
      </div>
    );
  }
}
function FormattedDate(props) {
    return <h2>It is {props.date.toLocaleTimeString()}.</h2>
}

ReactDOM.render(
  <Clock />,document.getElementById(‘root‘)
);

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读