React学习 & 与Vue的比较总结

React文档学习

学习资源:react中文文档

前言

有过Vue与Vuex的学习基础;
在重新学习React的时候,与过去的Vue开发经历进行了简单比较,写下了这篇笔记

总结

重点:摆正心态
过去使用双向绑定的Vue进行开发,虽然对于Vue与Vuex的使用也不过皮毛,不过也确实感觉到了其中的简便;而对于React的第一印象便是数据单项绑定,一开始便有了畏难情绪。在这一次的文档学习中,理解了React所谓的单向数据流思想,感觉从某些角度看,它的存在还是比Vue有着一定优势的,因此调整心态还是挺重要的。
明天继续学习Redux,并且与Vuex进行比较。

目录

  • 1、JSX文件类型:react组件文件格式
  • 2、渲染:Virtual DOM渲染机制
  • 3、组件与依赖:父组件传递给子组件的props属性
  • 4、组件状态的作用:state与props的区别
  • 5、组件状态与生命周期:生命周期钩子
  • 6、事件处理
  • 7、条件渲染
  • 8、列表渲染
  • 9、状态提升:通过父组件共享状态
  • 10、终极实例:一个demo的代码分析

1、JSX文件类型:融合了js与html两种语言

const element = 

Hello, world!

;

html段中可包含js语法

// 调用函数
function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  

Hello, {formatName(user)}!

); // 设置标签属性 const element = ;

在js中处理html段

function getGreeting(user) {
  if (user) {
    return 

Hello, {formatName(user)}!

; } return

Hello, Stranger.

; } // 闭合式标签需要在结尾加/ const element = ;

防止注入式攻击:
推荐嵌套写入数据:

const title = response.potentiallyMaliciousInput;
// This is safe:
const element = 

{title}

;
// 完整示例
// 每秒更新html段
// react仅仅更新必要的部分,也就是变量部分
// 由此体现Virtual DOM tree的高效
function tick() {
  const element = (
    

Hello, world!

It is {new Date().toLocaleTimeString()}.

); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000); // 每过一秒,重新传入render函数

2、渲染

const element = 

Hello, world

; // 传入ReactDOM.render方法进行渲染 ReactDOM.render( element, document.getElementById('root') );

react元素在创建后是不可变的,更新的唯一方法是创建新元素,然后传入ReactDOM.render方法


3、组件与依赖

props属性可读,可用,不可修改

渲染

function Welcome(props) {
  return 

Hello, {props.name}

; } // react会将jsx属性作为一个对象props,传递给组件实例 // 组件的name属性必须大写开头 const element = ; ReactDOM.render( element, document.getElementById('root') );

多次渲染

// 多次渲染该组件时
function Welcome(props) {
  return 

Hello, {props.name}

; } function App() { return (
); } ReactDOM.render( , document.getElementById('root') );

组件的提取,细化,提高可复用性

// 提取前的comment组件,混乱,层次复杂
function Comment(props) {
  return (
    
{props.author.name}
{props.author.name}
{props.text}
{formatDate(props.date)}
); } // 提取后的comment组件 function Avatar(props) { return ( {props.user.name} ); } function UserInfo(props) { return (
{props.user.name}
); } function Comment(props) { return (
{props.text}
{formatDate(props.date)}
); }

4、组件状态的作用

以时钟为例 ===> 利用组件状态优化

// 按照以下写法,需要对Clock实例添加单独的实现,而不是属于Clock组件的自身方法,需要多次渲染Clock实例
// 理想状态下,应该是渲染一次(调用ReactDOM.render())即可,生成的Clock实例会更新自身
function Clock(props) {
  return (
    

Hello, world!

It is {props.date.toLocaleTimeString()}.

); } function tick() { ReactDOM.render( , document.getElementById('root') ); } setInterval(tick, 1000);

5、组件状态与生命周期

状态(state):组件的私有属性,是局部的,完全受控于当前组件;
属性(JSX属性):属性是被传入的,只可读不可修改的;
生命周期:在重新渲染组件前,释放组件所占用的资源(DOM、计时器、eventListener)
函数组件 ==> 类 :使组件拥有状态,拥有生命周期

// 1、扩展为 React.Component 的ES6 类
class Clock extends React.Component {
    // 2. 添加类构造函数来初始化状态this.state,用来替代原本的props
  constructor(props) {
    super(props);    // 类组件应始终使用props调用基础构造函数。
    this.state = {date: new Date()};   // 不再需要从外部传入jsx属性
  }

  // 2、 将函数组件中的函数体移动到render方法中
  render() {
    return (
      

Hello, world!

It is {this.state.date.toLocaleTimeString()}.

); } // 3、添加生命周期钩子 // 当组件输出到 DOM 后会执行 componentDidMount() 钩子 // 挂载 当dom中添加了该元素,就挂载定时器,用于更新时间 componentDidMount() { this.timerID = setInterval( () => this.tick() ,1000); } // 在卸载组件时,连同计时器一并卸载 // 在卸载组件前执行componentWillUnmount()函数 componentWillUnmount() { clearInterval(this.timerID); } // tick方法:调用setState更新组件的局部状态,来调度UI更新 tick() { this.setState({ date: new Date() }); } } // 最终达到我们的理想效果,一次渲染即可 ReactDOM.render( , document.getElementById('root') );

关于setState()

  1. 通过对象属性更新,并不会重新渲染
this.state.comment = 'Hello';   // 此代码不会重新渲染组件
// 应当使用setState
  1. 状态更新可能是异步的
    react可将多个setState()合并;所以不要依赖他们的只进行计算……可能用到的是脏值
// 此代码无法用于实时更新的state,因为具体的set时间未知…… 
this.setState({
  counter: this.state.counter + this.props.increment,
});  
// 对应修复的方法:**向setState传入函数**,而不是对象   
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));
  1. 状态合并更新
constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }
this.setState({comments})   // 保留this.state.posts,但替换了this.state.comments

自顶向下单项数据流: 也就是react的特色(与vue的双向通信大不相同)

任何状态始终由某些特定(上层)组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。


6、事件处理

事件属性:驼峰式命名法
jsx语法:传入函数作为事件处理函数,如:onclick={activate};而不是字符串形式,如onclick="activateLasers()"

类的监听事件默认不会绑定this,因此添加事件绑定时

方法1:需要手动bind(this)

this.handleClick = this.handleClick.bind(this);

方法2:使用箭头函数

handleClick = () => {
    console.log('this is:', this);
  }

7、条件渲染

  • if:判断分支渲染
  • && : true && expression 总是返回 expression,而 false && expression 总是返回 false。
{unreadMessages.length > 0 &&
  

You have {unreadMessages.length} unread messages.

}
  • 阻止组件渲染: render() { return null } 但不影响生命周期

8、列表渲染

原始渲染

// 简单粗暴的渲染方法,手动生成html代码段
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  
  • {number}
  • ); // listItems包含了多个li标签 ReactDOM.render(
      {listItems}
    , document.getElementById('root') );

    优化列表组件渲染

    // 创建基础列表组件   只需传入列表数组,即可自动生成
    // ## 使用li标签必须添加一个key值,主要用于在li的一系列标签中唯一标识该元素,用于后续的删除、修改操作
    // key的作用域是同级节点之间 
    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number,index) => 
        // 当数组元素没有确定的id用于标识,可以用index替代
        
  • {number}
  • ); return (
      {listItems}
    ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( , document.getElementById('root') );

    9、状态提升

    目的:用于父组件中使用多个子组件,则将子组件之间的共享状态提升到父组件中进行管理
    问题:子组件之间的共享状态state保存到父组件中,子组件通过props读取,但是不能修改;
    解决途径:为了使子组件能够修改提升后的共享状态,将父组件的方法作为props属性值传入子组件来实现;子组件借用父组件的方法来修改父组件的state,“虽然很麻烦,但是有助于查找bug”,在今后的实践中检验一下这句官网的话....

    变量提升的简单实例


    10、终极实例

    “假双向通信”实例的逐步实现

    在看完这一个代码之后,可以很明显的感觉到Vue与React的不同;React更加注重细化提取子组件,在单文件中定义一个多级组件,而Vue仅仅定义单个组件,然后引用其他....
    而react的组件代码虽然真的很长很长,但是耐心的看来下,结构还是比较清晰的,重要的是要先分清组件之间的层级关系,还有共享数据的存放位置;

    在以下这一个“假双向通信”的组件中,包含组件的层级关系表现为 :

       FilterableProductTable   
               /\  
              /  \  
     SearchBar    ProductTable  
                      /\  
                     /  \  
    ProductCategoryRow   ProductRow  
    

    其中FilterableProductTable是顶级组件,保存state,将setState分发给SearchBar;而ProductTable以及它下属的ProductCategoryRow与ProductRow仅仅是获取state进行数据更新

    class ProductCategoryRow extends React.Component {
      render() {
        return ({this.props.category});
      }
    }
    
    class ProductRow extends React.Component {
      render() {
        var name = this.props.product.stocked ?
          this.props.product.name :
          
            {this.props.product.name}
          ;
        return (
          
            {name}
            {this.props.product.price}
          
        );
      }
    }
    
    class ProductTable extends React.Component {
      render() {
        var rows = [];
        var lastCategory = null;
        console.log(this.props.inStockOnly)
        this.props.products.forEach((product) => {
          if (product.name.indexOf(this.props.filterText) === -1 || (!product.stocked && this.props.inStockOnly)) {
            return;
          }
          // 为不同的category创建一个ProductCategoryRow,每遍历一次都为当前category的所有product创建一个新ProductRow
          // ProductCategoryRow与ProductRow 属于平级元素,都是ProductTable的td标签元素
          if (product.category !== lastCategory) {
            rows.push();
          }
          rows.push();
          lastCategory = product.category;
        });
        return (
          {rows}
    Name Price
    ); } } class SearchBar extends React.Component { constructor(props) { super(props); this.handleFilterTextInputChange = this.handleFilterTextInputChange.bind(this); this.handleInStockInputChange = this.handleInStockInputChange.bind(this); } handleFilterTextInputChange(e) { this.props.onFilterTextInput(e.target.value); } handleInStockInputChange(e) { this.props.onInStockInput(e.target.checked); } render() { return (

    {' '} Only show products in stock

    ); } } class FilterableProductTable extends React.Component { constructor(props) { super(props); this.state = { // 关键字搜索 filterText: '', // checkbox状态 inStockOnly: false }; this.handleFilterTextInput = this.handleFilterTextInput.bind(this); this.handleInStockInput = this.handleInStockInput.bind(this); } handleFilterTextInput(filterText) { this.setState({ filterText: filterText }); } handleInStockInput(inStockOnly) { this.setState({ inStockOnly: inStockOnly }) } render() { return (
    ); } } var PRODUCTS = [ {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'}, {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'}, {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'}, {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'}, {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'}, {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'} ]; ReactDOM.render( , document.getElementById('container') );

    你可能感兴趣的:(React学习 & 与Vue的比较总结)