首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >番外篇:入门React

番外篇:入门React

作者头像
用户3467126
发布2019-07-03 18:14:03
1.4K0
发布2019-07-03 18:14:03
举报
文章被收录于专栏:爱编码爱编码

背景

原生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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱编码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目标
  • 简介
  • Demo
  • 与webpack结合
  • JSX
  • 组件
    • 生命周期(主要两个)
    • 事件处理
    • DOM操作
    • 组合组件
    • 组件间通信
    • 使用css样式
    • react-router
    • 网络请求Fetch
    • Redux
    • 学习资料
    相关产品与服务
    数据保险箱
    数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档