专栏首页爱编码番外篇:入门React

番外篇:入门React

背景

原生js代码乱七八糟的时候,那就来体验一下React。 Tip:内容有点乱,秘籍在最后

目标

  • 踢开React的大门。

简介

React 的核心思想是:封装组件。 各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件。 React 大体包含下面这些概念:

  • 组件:
  • JSX
  • Virtual DOM
  • Data Flow

经验: 前端框架的基本组成: 组件及其生命周期、样式、路由、网络请求、事件绑定、数据存储和传递。

Demo

import React, { Component } from 'react';
import { render } from 'react-dom';

class HelloMessage extends Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}

// 加载组件到 DOM 元素 mountNode
render(<HelloMessage name="John" />, mountNode);

解析:

  • 组件:HelloMessage 就是一个 React 构建的组件,最后一句 render 会把这个组件显示到页面上的某个元素 mountNode 里面,显示的内容就是
 <div>Hello John</div>
  • JSX: 将 HTML 直接嵌入了 JS 代码里面(上面的js里就写了个div),这个就是 React 提出的一种叫 JSX 的语法.
  • Virtual DOM:

虚拟DOM

当组件状态 state 有更改的时候,React 会自动调用组件的 render 方法重新渲染整个组件的 UI。当然如果真的这样大面积的操作 DOM,性能会是一个很大的问题,所以 React 实现了一个Virtual DOM,组件 DOM 结构就是映射到这个 Virtual DOM 上,React 在这个 Virtual DOM 上实现了一个 diff 算法,当要重新渲染组件的时候,会通过 diff 寻找到要变更的 DOM 节点,再把这个修改更新到浏览器实际的 DOM 节点上,所以实际上不是真的渲染整个 DOM 树。这个 Virtual DOM 是一个纯粹的 JS 数据结构,所以性能会比原生 DOM 快很多。

  • Data Flow: “单向数据绑定”是 React 推崇的一种应用架构的方式。

与webpack结合

package.json看依赖

{
  "name": "learning-01",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --config ./webpack.config.js --mode development --open",
    "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.3.3",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "clean-webpack-plugin": "^1.0.1",
    "html-webpack-plugin": "^3.2.0",
    "react-hot-loader": "^4.7.1",
    "webpack": "^4.29.5",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.2.0"
  },
  "dependencies": {
    "react": "^16.8.3",
    "react-dom": "^16.8.3"
  }
}

webpack.config.js看配置

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
      path: __dirname + '/dist',
      publicPath: '/',
      filename: 'bundle.js'
    }, module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: ['babel-loader']
        }
      ]
    },
    resolve: {
      extensions: ['*', '.js', '.jsx']
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new webpack.HotModuleReplacementPlugin(),
      new HtmlWebpackPlugin({template:'index.html'})
    ],
    devServer: {
      contentBase: './dist',
      hot: true
    }
  };

JSX

  • 1. HTML 里的 class 在 JSX 里要写成 className,因为 class 在 JS 里是保留关键字。
  • 2.同理某些属性比如 for 要写成 htmlFor。
  • 3.属性值使用表达式,只要用 {} 替换 ""
// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// Output (JS):
var person = React.createElement(
  Person,
  {name: window.isLoggedIn ? window.name : ''}
);
  • 4. 使用注释要用 {} 包起来。
 {/* child comment, put {} around */}
  • 5.React 会将所有要显示到 DOM 的字符串转义,防止 XSS。
<div dangerouslySetInnerHTML={{__html: 'cc &copy; 2015'}} />
  • 6.属性扩散
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;

组件

生命周期(主要两个)

componentWillMount 只会在装载之前调用一次,在 render 之前调用,你可以在这个方法里面调用 setState 改变状态,并且不会导致额外调用一次 render

componentDidMount 只会在装载完成之后调用一次,在 render 之后调用,从这里开始可以通过 ReactDOM.findDOMNode(this) 获取到组件的 DOM 节点。

var React = require('react');
var ReactDOM = require('react-dom');
import ComponentHeader from './components/ComponentHeader';
import ComponentFooter from './components/ComponentFooter';
import BodyIndex from './components/BodyIndex';
import BasicExample from './root'

export default class Index extends React.Component {

 constructor(props) {
    super(props);
    this.state = { count: props.initialCount };
  }

  //组件即将加载
  componentWillMount() {
    //定义你的逻辑即可
    console.log("Index - componentWillMount");
  }
  //组件加载完毕
  componentDidMount() {
    console.log("Index - componentDidMount");
  }

  render() {

        /*
        var component;
        if (用户已登录) {
            component = <ComponentLoginedHeader/>
        }
        else{
            component = <ComponentHeader/>
        }
        */

    return (
      <div>
        <ComponentHeader />
        <BodyIndex />
        <ComponentFooter />
      </div>
    );
  }
}

ReactDOM.render(<BasicExample/>,document.getElementById('app'))

事件处理

给事件处理函数传递额外参数的方式: bind(this, arg1, arg2, …)

render: function() {
    return <p onClick={this.handleClick.bind(this, 'extra param')}>;
},
handleClick: function(param, event) {
    // handle click
}

DOM操作

Refs 另外一种方式就是通过在要引用的 DOM 元素上面设置一个 ref 属性指定一个名称,然后通过 this.refs.name 来访问对应的 DOM 元素。

比如有一种情况是必须直接操作 DOM 来实现的,你希望一个input输入框元素在你清空它的值时 focus,你没法仅仅靠 state 来实现这个功能。

class App extends Component {
  constructor() {
    return { userInput: '' };
  }

  handleChange(e) {
    this.setState({ userInput: e.target.value });
  }

  clearAndFocusInput() {
    this.setState({ userInput: '' }, () => {
      this.refs.theInput.focus();
    });
  }

  render() {
    return (
      <div>
        <div onClick={this.clearAndFocusInput.bind(this)}>
          Click to Focus and Reset
        </div>
        <input
          ref="theInput"
          value={this.state.userInput}
          onChange={this.handleChange.bind(this)}
        />
      </div>
    );
  }
}

组合组件

使用组件的目的就是通过构建模块化的组件,相互组合组件最后组装成一个复杂的应用。在 React 组件中要包含其他组件作为子组件,只需要把组件当作一个 DOM 元素引入就可以了。

var React = require('react');
var ReactDOM = require('react-dom');
import ComponentHeader from './components/ComponentHeader';
import ComponentFooter from './components/ComponentFooter';
import BodyIndex from './components/BodyIndex';

class Index extends React.Component {
  //组件即将加载
  componentWillMount() {
    //定义你的逻辑即可
    console.log("Index - componentWillMount");
  }
  //组件加载完毕
  componentDidMount() {
    console.log("Index - componentDidMount");
  }

  render() {

        /*
        var component;
        if (用户已登录) {
            component = <ComponentLoginedHeader/>
        }
        else{
            component = <ComponentHeader/>
        }
        */

    return (
      <div>
        <ComponentHeader />
        <BodyIndex />
        <ComponentFooter />
      </div>
    );
  }
}


ReactDOM.render(
  <Index />, document.getElementById('app'));

组件间通信

父子组件间通信

  • 1.父组件传递到子组件: 就是通过 props 属性传递,在父组件给子组件设置 props,然后子组件就可以通过 props 访问到父组件的数据方法,这样就搭建起了父子组件间通信的桥梁。
  • 2.父组件访问子组件? 用 refs

非父子组件间的通信 使用全局事件 Pub/Sub 模式, 在 componentDidMount 里面订阅事件, 在 componentWillUnmount 里面取消订阅, 当收到事件触发的时候调用 setState 更新 UI。 一般来说,对于比较复杂的应用,推荐使用类似 Flux 这种单项数据流架构

使用css样式

1.内联样式

在render函数里定义

const styleComponentHeader = { header: { backgroundColor: '#333333', color: '#FFFFFF', 'padding-top': '12px', 'paddingBottom: '16px' } };

注意样式的驼峰写法 style = {styleComponentHeader.header}

文件中引用css的样式 注意class需要更改成className确定是动画、伪类(hover)等不能使用

2.内联样式中的表达式

paddingBottom:(this.state.minHeader)?"3px":"15px" 

注意好好理解这里的state引起样式的即时变化

3.CSS模块化

原因:避免全局污染、命名混乱、依赖管理不彻底、无法共享变量、代码压缩不彻底

npm install --save-dev style-loader css-loader npm install --save-dev babel-plugin-react-html-attrs  //为了使用原生的html属性名

全局样式和局部样式

:local(.normal){color:green;}  //局部样式
:global(.btn){color:red;}  //全局样式

CSS模块化的优点 所有样式都是local的,解决了命名冲突和全局污染问题 class名生成规则配置灵活,可以此来压缩class名 只需引用组件的JS就能搞定组件所有的js和css 依然是css,几乎零学习成本

react-router

注意点: 1.引用的包是有区别的。

2.控制页面的层级关系 单页面构建Router控制 底层机制 React: state/props -> Components ->UI Router: location ->Router -> UI

3.router传参 定义: path="list/:id" 使用: this.props.match.params.id

4.地址无法刷新(巨坑) 表现:'/' 根节点 Home 显示无误,不过其他任何路由 例如 /detail,均显示 Cannot GET /detail。 解决方法:

  • 4.1 用的 BrowserRouter 改为 HashRouter 即可
  • 4.2 修改 webpack.config.js 配置文件
module.exports = {
    // 省略其他的配置
    devServer: {
        historyApiFallback: true
    }
}

详情可以参考: https://blog.csdn.net/zwkkkk1/article/details/83411071

网络请求Fetch

官网:https://github.com/github/fetch

fetch('/users.json')
  .then(function(response) {
    return response.json()
  }).then(function(json) {
    console.log('parsed json', json)
  }).catch(function(ex) {
    console.log('parsing failed', ex)
  })

Redux

下期再讲

学习资料

练习代码 学习Demo样例: https://github.com/xbmchina/react-learning 项目Demo样例: https://github.com/xbmchina/react-project-news


React相关资料

React官网: https://reactjs.org/docs/hello-world.html React中文网: https://react.docschina.org/ React学习文档: http://caibaojian.com/react/ webpack搭建React: https://segmentfault.com/a/1190000018025963?utm_source=tag-newest React-router官网: https://reacttraining.com/react-router/ 阿里UI库Ant Design: https://ant.design/index-cn 阿里图标库: https://www.iconfont.cn/ 谷歌的ReactUI库: https://material-ui.com/ css转React: https://staxmanade.com/CssToReact/ Fetch请求: https://github.com/github/fetch

本文分享自微信公众号 - 爱编码(ilovecode),作者:明大侦探

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 分布式Session

    Session 是客户端与服务器通讯会话跟踪技术,服务器与客户端保持整个通讯的会话基本信息。

    用户3467126
  • 【数据库】MySQL锁机制、热备、分表

    加锁的方式:自动加锁。查询操作(SELECT),会自动给涉及的所有表加读锁,更新操作(UPDATE、DELETE、INSERT),会自动给涉及的表加写锁。

    用户3467126
  • 分布式事务

    不知道你是否遇到过这样的情况,去小卖铺买东西,付了钱,但是店主因为处理了一些其他事,居然忘记你付了钱,又叫你重新付。又或者在网上购物明明已经扣款,但是却告诉我没...

    用户3467126
  • React新特性——Protals与Error Boundaries

    在React 16.x 新增了一个名为“Protals”的特性,直接按照字面意思翻译实在不靠谱。在描述这个特性时,我们还是用官方的英文单词来指定它。Portal...

    随风溜达的向日葵
  • React创建组件的三种方式及其区别

    虽然有三种方式可以定义react的组件,那么这三种定义组件方式有什么不同呢?或者说为什么会出现对应的定义方式呢?下面就简单介绍一下。

    前朝楚水
  • [OHIF-Viewers]医疗数字阅片-医学影像-REACT-React.createRef()-Refs and the DOM关于回调 refs 的说明

    Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。

    landv
  • React Native 每日一学(Learn a little every day)

    本文出自《React Native学习笔记》系列文章。 每天一个知识点(技巧,经验,填坑日记等),每天学一点,离大神近一点。 汇聚知识,分享精华。 如果你是一...

    CrazyCodeBoy
  • 浅谈 React Refs

    在React组件中,props是父组件与子组件的唯一通信方式,但是在某些情况下我们需要在props之外强制修改子组件或DOM元素,这种情况下React提供了Re...

    IMWeb前端团队
  • 使用React.Suspense显示loading效果

    它必须返回一个 Promise,该 Promise 需要 resolve 一个 defalut export 的 React 组件。

    Qiang
  • 通往全栈工程师的捷径 —— React

    首先,我们来看看 React 在世界范围的热度趋势,下图是关键词“房价”和 “React” 在 Google Trends 上的搜索量对比,蓝色的是 React...

    腾讯Bugly

扫码关注云+社区

领取腾讯云代金券