[关闭]
@Bios 2018-12-10T08:43:40.000000Z 字数 11012 阅读 905

React从入门到XXX

react js


理论知识点

组件定义

javascript函数方式定义

  1. function Welcome(props) {
  2. return <h1>hello,{props.name}</h1>
  3. }

class定义组件

  1. class Welcome extends React.Component {
  2. render(){
  3. return <h1>hello,{this.props.name}</h1>
  4. }
  5. }

调用组件

  1. <Welcome name="Sara">

函数式组件不能声明状态 state。状态只能由内部初始化,内部改变

正确地使用状态

  1. // Wrong
  2. this.state.comment = 'Hello';
  3. // Correct
  4. this.setState({comment: 'Hello'});
  1. // Wrong
  2. this.setState({
  3. counter: this.state.counter + this.props.increment,
  4. });
  5. // Correct
  6. //该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数
  7. this.setState((prevState, props) => ({
  8. counter: prevState.counter + props.increment
  9. }));

向事件处理程序传递参数

通常我们会为事件处理程序传递额外的参数。例如,若是 id 是你要删除那一行的id,以下两种方式都可以向事件处理程序传递参数:

  1. <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
  2. <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

上面两个例子中,参数 e 作为 React 事件对象将会被作为第二个参数进行传递。通过箭头函数的方式,事件对象必须显式的进行传递,但是通过 bind的方式,事件对象以及更多的参数将会被隐式的进行传递。

隐式传递就是不传递,照样可以用

值得注意的是,通过 bind方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面,例如:

  1. class Popper extends React.Component{
  2. constructor(){
  3. super();
  4. this.state = {name:'Hello world!'};
  5. }
  6. preventPop(name, e){ //事件对象e要放在最后
  7. e.preventDefault();
  8. alert(name);
  9. }
  10. render(){
  11. return (
  12. <div>
  13. <p>hello</p>
  14. {/* Pass params via bind() method. */}
  15. <a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
  16. </div>
  17. );
  18. }
  19. }

阻止组件渲染

当需要隐藏某个组件时,不需要触发一个hide函数,只需个组件传递个参数作为判断条件即可:

  1. function WarningBanner(props) {
  2. if (!props.warn) {
  3. return null; // 如果!props.warn则返回null,此时组件就不渲染
  4. }
  5. return (
  6. <div className="warning">
  7. Warning!
  8. </div>
  9. );
  10. }
  11. class Page extends React.Component {
  12. constructor(props) {
  13. super(props);
  14. this.state = {showWarning: true}
  15. this.handleToggleClick = this.handleToggleClick.bind(this);
  16. }
  17. handleToggleClick() {
  18. this.setState(prevState => ({
  19. showWarning: !prevState.showWarning
  20. }));
  21. }
  22. render() {
  23. return (
  24. <div>
  25. <WarningBanner warn={this.state.showWarning} />
  26. <button onClick={this.handleToggleClick}>
  27. {this.state.showWarning ? 'Hide' : 'Show'}
  28. </button>
  29. </div>
  30. );
  31. }
  32. }
  33. ReactDOM.render(
  34. <Page />,
  35. document.getElementById('root')
  36. );

属性

使用 JavaScript 表达式

你可以传递任何 {} 包裹的 JavaScript 表达式作为一个属性值。例如,在这个 JSX 中:

  1. <MyComponent foo={1 + 2 + 3 + 4} />

对于 MyComponent来说, props.foo 的值为 10,这是 1 + 2 + 3 + 4 表达式计算得出的。

if 语句和 for 循环在 JavaScript 中不是表达式,因此它们不能直接在 JSX 中使用,但是你可以将它们放在周围的代码中。

  1. function NumberDescriber(props) {
  2. let description;
  3. if (props.number % 2 == 0) {
  4. description = <strong>even</strong>;
  5. } else {
  6. description = <i>odd</i>;
  7. }
  8. return <div>{props.number} is an {description} number</div>;
  9. }

字符串常量

  1. <MyComponent message="hello world" />
  2. <MyComponent message={'hello world'} />
  3. <MyComponent message="&lt;3" />
  4. <MyComponent message={'<3'} />

默认为 True

如果你没有给属性传值,它默认为 true。因此下面两个 JSX 是等价的:

  1. <MyTextBox autocomplete />
  2. <MyTextBox autocomplete={true} />

一般情况下,我们不建议这样使用,因为它会与 ES6 对象简洁表示法 混淆。比如 {foo}{foo: foo}的简写,而不是{foo:true}。这里能这样用,是因为它符合 HTML 的做法。

扩展属性

如果你已经有了个 props 对象,并且想在 JSX 中传递它,你可以使用 ... 作为扩展操作符来传递整个属性对象。下面两个组件是等效的:

  1. function App1() {
  2. return <Greeting firstName="Ben" lastName="Hector" />;
  3. }
  4. function App2() {
  5. const props = {firstName: 'Ben', lastName: 'Hector'};
  6. return <Greeting {...props} />; // 解构赋值
  7. }

defaultProps默认值和propTypes类型约束

  1. class Demo extends Component{
  2. static defaultProps = {
  3. name: '默认值' // 当使用到name时,如果父组件没有传值,则显示为‘默认值’
  4. }
  5. static propTypes = {
  6. age: React.propTypes.number,
  7. name: React.propTypes.string // 约定需要的类型(string)
  8. }
  9. render(){
  10. ……
  11. }
  12. }
  13. // 另一种方式
  14. Demo.defaultProps = {
  15. name: '默认值' // 当使用到name时,如果父组件没有传值,则显示为‘默认值’
  16. }
  17. Demo.propTypes = {
  18. name: React.propTypes.string // 约定需要的类型(string)
  19. }

propTypes类型约束只在开发阶段有效,发布时会自动移除。

状态提升

使用 react 经常会遇到几个组件需要共用状态数据的情况。这种情况下,我们最好将这部分共享的状态提升至他们最近的父组件当中进行管理。

动态列表与key

  1. class Demo extends Component{
  2. ……
  3. render(){
  4. <div>
  5. <ul>
  6. {
  7. arr.map((item,index) => <li key={index}>{item}</li>)
  8. }
  9. {
  10. arr.map((item,index) => {
  11. return(<li key={index}>{item}</li>)
  12. })
  13. }
  14. </ul>
  15. </div>
  16. }
  17. }

ReactDOM.render()方法

  1. // react 元素
  2. let eleObj = {
  3. type: 'div',
  4. props:{
  5. className: 'red',
  6. id: 'div',
  7. children:[
  8. 'hello',
  9. {
  10. type:'span',
  11. props:{
  12. className:'blue',
  13. children:['word']
  14. }
  15. }
  16. ]
  17. }
  18. }
  19. // 模拟render函数
  20. function render(eleObj, container){
  21. // 解构出标签的类型和属性对象
  22. let {type, props} = eleObj;
  23. // 创建一个DOM元素
  24. let ele = document.createElement(type);
  25. // 循环属性对象
  26. for (let attr in props){
  27. if(attr == 'children'){
  28. props[attr].forEach(item => {
  29. if(typeof item == 'string'){
  30. let textNode = document.createTextNode(item);
  31. ele.appendChild(textNode);
  32. } else {
  33. // 递归调用
  34. render(item, ele);
  35. }
  36. })
  37. } else if(attr == 'className') {
  38. ele.setAttribute('class',props[attr]);
  39. } else {
  40. ele.setAttribute(attr, props[attr]);
  41. }
  42. }
  43. }

router-router4

  1. // 页面路由
  2. window.location.href = 'http://www.baidu.com';
  3. history.back();
  4. // hash 路由
  5. window.location = '#hash';
  6. window.onhashchange = function(){
  7. console.log('current hash:', window.location.hash);
  8. }
  9. // h5 路由
  10. // 推进一个状态
  11. history.pushState('name', 'title', '/path');
  12. // 替换一个状态
  13. history.replaceState('name', 'title', '/path');
  14. // popstate
  15. window.onpopstate = function(){
  16. console.log(window.location.href);
  17. console.log(window.location.pathname);
  18. console.log(window.location.hash);
  19. console.log(window.location.search);
  20. }
  21. // react-router
  22. import React from 'react';
  23. import ReactDOM from 'react-dom';
  24. import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
  25. class A extends React.Component{
  26. constructor(props){
  27. super(props)
  28. }
  29. render(){
  30. return (
  31. <div>
  32. Component A
  33. <Switch>
  34. // 在route里还能写JSX
  35. <Route exact path={`${this.props.match.path}`} render={(route) => {
  36. return <div>当前组件是不带参数的A</div>
  37. }}/>
  38. // a/sub 要写在 a/:id的前面,因为sub也有可能被当作id
  39. <Route path={`${this.props.match.path}/sub`} render={(route) => {
  40. return <div>当前组件是Sub</div>
  41. }}/>
  42. <Route path={`${this.props.match.path}/:id`} render={(route) => {
  43. return <div>当前组件是带参数的A, 参数是:{route.match.params.id}</div>
  44. }}/>
  45. </Switch>
  46. </div>
  47. )
  48. }
  49. }
  50. class B extends React.Component{
  51. constructor(props){
  52. super(props)
  53. }
  54. render(){
  55. return <div>Component B</div>
  56. }
  57. }
  58. class Wrapper extends React.Component{
  59. constructor(props){
  60. super(props)
  61. }
  62. render(){
  63. return (
  64. <div>
  65. <Link to="/a">组件A</Link>
  66. <br/>
  67. <Link to="/a/123">带参数的组件A</Link>
  68. <br/>
  69. <Link to="/b">组件B</Link>
  70. <br/>
  71. <Link to="/a/sub">/a/sub</Link>
  72. {this.props.children}
  73. </div>
  74. );
  75. }
  76. }
  77. ReactDOM.render(
  78. <Router>
  79. <Wrapper>
  80. <Route path="/a" component={A}/>
  81. <Route path="/b" component={B}/>
  82. </Wrapper>
  83. </Router>,
  84. document.getElementById('app')
  85. );

ref

  1. // ref等于一个函数,表示当元素被挂载到页面中之后立即调用此函数,并传入渲染后的DOM元素
  2. // this 指的是这个组件实例 也就是当页面渲染之后,this.div 也就表示的是 div这个元素
  3. class A extends Component{
  4. render(){
  5. return (
  6. <div ref= {ref=>this.div=ref}>hahahhah</div>
  7. )
  8. }
  9. }

实际问题

动态添加class

  1. <li className={['mingxi-tit-one', this.state.buttonType === 1 && 'mingxi-active'].join(' ')}></li>
  2. // 数组元素为className,
  3. // && 符号为判断符,若条件成立则执行后面的内容
  4. // join为数组的方法,将数组元素拼接为字符串,链接符为一个空字符串

react-creat-app

安装sass

  1. yarn add node-sass-chokidar
  1. // package.json
  2. "build-css": "node-sass-chokidar src/ -o src/",
  3. "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",

执行 npm run watch-css

npm run eject

修改端口:

  1. // 新建.env.local
  2. PORT = 2000

配置代理

vue中 config/index.js

react:

  1. //package.json
  2. "proxy":{
  3. "/api":{
  4. "target": "https://m.weibo.cn",
  5. "changeOrigin": true
  6. }
  7. }

图片引入

  1. //方式一:
  2. import tsIcon from '../images/typescript.jpeg';
  3. //方式二:
  4. const tsIcon = require('../images/typescript.jpeg');
  5. //使用方式一:
  6. <img src={tsIcon} alt="" />
  7. //使用方式二:
  8. <div style={{background: `url(${tsIcon}) no-repeat`}}></div>
  9. //使用方式三:
  10. const styles = { test: { background: `url(${tsIcon}) no-repeat center` }}render() { return ( <div style={styles.test}></div> )}

表单相关

多个表单输入

  1. handleInputChange(event) {
  2. const target = event.target;
  3. const value = target.type === 'checkbox' ? target.checked : target.value;
  4. const name = target.name;
  5. this.setState({
  6. [name]: value
  7. });
  8. }

父组件与子组件

父组件调用子组件方法

  1. class App extends Component{
  2. add = ()=>{
  3. this.child.add(); // 调用子组件的方法
  4. }
  5. render(){
  6. return (
  7. <Child ref={instance => this.child = instance}>
  8. )
  9. }
  10. }

子组件向父组件通信

父组件将函数作为props传递给子组件,子组件在需要时调用,将数据作为函数参数传回——回调(callback)

  1. // 子组件
  2. class Child extends Component{
  3. call=()=>{
  4. this.props.fun && this.props.fun('听到了吗?');
  5. }
  6. render(){
  7. return(
  8. 我是子组件
  9. <button onClick={()=>this.call()}>按钮</button>
  10. )
  11. }
  12. }
  13. // 父组件
  14. class Parent extends Component{
  15. listen= (callback)=> {
  16. console.log(callback); // 听到了吗?
  17. }
  18. render(){
  19. return(
  20. <Child fun={this.listen} />
  21. )
  22. }
  23. }

react-router4 路由后退一步

  1. this.props.history.goBack();

react-router4 页面跳转与传递参数

可选参数

  1. <Route path="/Search/:category/:keyword?" component={...}/>

第一种 页面刷新就没有了

  1. // app.js
  2. import React, { Component } from 'react';
  3. import {BrowserRouter as Router,Redirect,Route,Link,Switch} from 'react-router-dom';
  4. import './app.css';
  5. import Login from './views/login/index.js';
  6. import Home from './views/home/index.js';
  7. class App extends Component {
  8. render() {
  9. return (
  10. <div className="app">
  11. <Router>
  12. <Switch>
  13. <Route exact path="/login" component={Login} />
  14. <Route exact path="/home" component={Home} />
  15. </Switch>
  16. </Router>
  17. </div>
  18. );
  19. }
  20. }
  21. export default App;
  1. // login.js
  2. import React, { Component } from 'react';
  3. import { Form, Icon, Input, Button, Checkbox } from 'antd';
  4. import './index.css';
  5. const FormItem = Form.Item;
  6. class LoginForm extends Component {
  7. constructor(props){
  8. super(props);
  9. this.state = {
  10. pid: '1111'
  11. }
  12. }
  13. handleSubmit = (e) => {
  14. e.preventDefault();
  15. this.props.form.validateFields((err, values) => {
  16. if (!err) {
  17. // params 只是个变量名可以换
  18. this.props.history.push({pathname:'home',params:{pid:this.state.pid}});
  19. }
  20. });
  21. }
  22. render() {
  23. ....
  24. }
  25. }
  1. // home.js
  2. import React, { Component } from 'react';
  3. export default class Home extends Component{
  4. constructor(props){
  5. super(props);
  6. }
  7. render(){
  8. return (
  9. <div>
  10. home
  11. {this.props.location.params.pid} // 获取参数
  12. </div>
  13. )
  14. }
  15. }

第二种

  1. // app.js
  2. <Route exact path="/home/:pid" component={Home} />
  3. // home.js
  4. {this.props.match.params.pid}
  5. // login.js
  6. this.props.history.push(`/home/${this.state.pid}`);

第三种

  1. // login.js
  2. this.props.history.push(`/home/?pid=${this.state.pid}`);
  3. // home.js
  4. getUrlParam = (name)=>{
  5. // xxxx.com?param1=123&param2=456
  6. let queryString = window.location.search.split('?')[1] || '',
  7. // (^|&) + name 以 name 或者 &name 开头
  8. // ([^&]*) 非 & 符的0个或多个字符
  9. // [^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'。
  10. // (&|$) 接上上面 就是 (0个或者多个字符)或者以&符结尾
  11. reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"),
  12. result = queryString.match(reg);
  13. // result : ['param1=123','','123','&']
  14. return result ? decodeURIComponent(result[2]) : null
  15. }
  16. {this.getUrlParam('pid')}

react打包部署到nginx,直接访问url和刷新页面报404错误

第一:修改nginx配置

第二:react将BrowserRouter改成HashRouter

参考文章

webpack配置react-hot-loader热加载局部更新

有人会问 webpack-dev-server 已经是热加载了,能做到只要代码修改了页面也自动更新了,为什么在 react 项目还要安装 react-hot-loader 呢?其实这两者的更新是有区别的,webpack-dev-server 的热加载是开发人员修改了代码,代码经过打包,重新刷新了整个页面。而 react-hot-loader 不会刷新整个页面,它只替换了修改的代码,做到了页面的局部刷新。但它需要依赖 webpack 的 HotModuleReplacement 热加载插件。

安装 react-hot-loader

npm install --save-dev react-hot-loader

webpack.config.js 的 entry 值里加上 react-hot-loader/patch,一定要写在entry 的最前面,如果有 babel-polyfill 就写在babel-polyfill 的后面。

  1. entry: [
  2. 'babel-polyfill',
  3. 'react-hot-loader/patch', //设置这里
  4. __dirname + '/app/main.js'
  5. ]

在 webpack.config.js 中设置 devServer 的 hot 为 true

  1. devServer: {
  2. contentBase: './build',
  3. port: '1188',
  4. historyApiFallback: true,
  5. inline: true,
  6. hot: true, //设置这里
  7. }
  1. {
  2. "presets": ['es2015', 'react'],
  3. "plugins": ["react-hot-loader/babel"] //设置这里
  4. }

在 webpack.config.js 的 plugins 里添加依赖的 HotModuleReplacement 插件

  1. plugins: [
  2. new HtmlWebpackPlugin({
  3. template: __dirname + "/app/index.tmpl.html"
  4. }),
  5. new webpack.HotModuleReplacementPlugin() //设置这里
  6. ]
  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import Greeter from './greeter';
  4. import "./main.css";
  5. import { AppContainer } from 'react-hot-loader'; //设置这里
  6. const render = (App) => {
  7. ReactDOM.render(
  8. <AppContainer>
  9. <App />
  10. </AppContainer>,
  11. document.getElementById('root')
  12. )
  13. }
  14. render(Greeter)
  15. // Hot Module Replacement API
  16. if (module.hot) {
  17. module.hot.accept('./greeter', () => {
  18. render(require('./greeter').default)
  19. })
  20. }

简写成这样试了一下也能成功

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import Greeter from './greeter';
  4. import "./main.css";
  5. ReactDOM.render(
  6. <App />,
  7. document.getElementById('root')
  8. )
  9. if (module.hot) {
  10. module.hot.accept()
  11. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注