Table of Contents
之前看完了react官网的教程,写了一个react版本的todo。之后想写一个react版本的poem网站,主要是为了熟悉react和使用react-router。但在查了一圈资料后发现有点难以下手,所以就找了这么一套视频(React16.4 开发简书项目 从零基础入门到实战)全面学习一下。
git地址:https://github.com/tonyzhou1890/react-jianshu
学习内容
- creat-react-app
- 组件化思维
- JSX
- 开发调试工具
- 虚拟DOM
- 生命周期
- React-transition-group
- Redux
- Antd
- UI, 容器组件
- 无状态组件
- redux-thunk
- redux-saga
- Styled-components
- Immutable.js
- redux-immutable
- axios
- react-router
2. React 特点
- 声明式开发
- 可以与其他框架并存
- 组件化
- 单项数据流
- 视图层框架
- 函数式编程
JSX语法
- JSX中自定义标签(组件)需要采用“大骆驼拼写法”。
- 不想显示外层包裹元素可以使用Fragment代替。
- 事件名要用“小骆驼拼写法”。
- 注释:
<div> {/*注释*/} </div> // 或者 <div> { // 注释 } </div>
- 不对标签进行转译:dangerouslySetInnerHTML={{__html: content}}
- 属性映射
html | jsx |
---|---|
class | className |
for | htmlFor |
数据
- 要改变state里的数据需要调用this.setState方法。
作用域
- 组件内部的方法需要绑定组件作用域。
setState
- setState 可以采用对象写法,但现在不推荐了
this.setState({ item: content })
- setState 可以采用函数式写法,目前推荐写法
this.setState(() => ({ item: content }))
- setState 采用函数式写法可以提高性能,但是是异步的,如果是获取事件对象的值,要如下处理
handleInputChange(e) { const value = e.target.value this.setState(() => ({ inputValue: value })) }
props
propTypes
- 使用:
import PropTypes from 'prop-types' …… Component.propTypes = { a: PropTypes.string.isRequired, a: PropTypes.func }
- 多种检测类型:string、func、string……
- 是否必须:isRequired
defaultProps
- 为 props 提供默认值
Component.defaultProps = { a: 'hello world' }
ref
- React16 推荐的 ref 参数是一个函数。这个函数的默认参数是 ref 绑定的元素。
- 并不推荐使用 ref。在面向数据编程的思想中,尽量不要直接操作 DOM。
- 在 setState 操作后获取元素的操作会与预期不一样。因为 setState 是异步操作,获取元素时 setState 未完成。解决方法是:setState 的第二个参数是一个回调函数,在这个回调函数里操作 DOM。
虚拟DOM
- 原理:通过 js 对象模拟真实 DOM,当数据发生变化,再生成新的虚拟 DOM,对比旧的虚拟 DOM 和新的虚拟 DOM,收集两者的差异,更新真实的 DOM。因为 js 比 DOM 快很多,所以虚拟 DOM 可以极大地提升性能。
- react diff 算法采用的是同层对比。如果同层发生变化,则子级全部替换。这会消耗性能,但这种 diff 算法简单,节省了 diff 的性能消耗。
声明周期
- 定义:生命周期函数指在某一个时期组件会自动调用执行的函数。
- componentWillMount: 在组件即将被挂载到页面的时刻自动执行。
- componentDidMount: 组件被挂载到页面后自动执行。
- componentWillReceiveProps: 当一个组件从父组件接受了参数,当父组件 render 函数被重新执行,子组件的这个生命周期函数就会被执行。即子组件第一次存在于父组件中不会执行,只有之前已经存在于父组件中才会执行。这个组件会接受两个参数:nextProps、nextState。
- shouldComponentUpdate: 组件被更新之前自动执行。这个声明周期函数需要返回一个布尔值,表明是否需要更新组件。
- componentWillUpdate: 组件被更新之前自动执行。
- componentDidUpdate: 组件更新完成后自动执行。
- componentWillUnmount: 当组件即将被从页面中卸载时自动执行。
css动画
- animation 保留最后一帧的状态需要传递第四个参数为 forwards
.hide { animation: hide-item 1s ease-in forwards; } @keyframes hide-item { 0% { opacity: 1; color: red; } 50% { opacity: 0.5; color: green; } 100% { opacity: 0; color: blue; } }
- react-transition-group 包可以提供动画的简便实现。
Redux
- Redux = Reducer + Flux
- reducer 可以接受 state, 但是绝不能修改 state
- 要将 store 里数据的变化应用到组件,需要订阅。并且,处理函数要在订阅之前绑定作用域,否则会报错。
class TodoList extends Component { constructor(props) { super(props) this.state = store.getState() this.handleStoreChange = this.handleStoreChange.bind(this) store.subscribe(this.handleStoreChange) } handleStoreChange() { this.setState(store.getState()) } …… }
- action 的 type 可以统一管理——例如写入一个 actionTypes.js 的文件,避免拼写不一致出错以及方便错误排查。
- 对于 action 也可以统一管理,将所有 action 写入一个文件。
- 注意项目:
- store 是唯一的:项目中只有一个 store
- 只有 store 能改变自己的内容
- Reducer 必须是一个纯函数
- api
- createStore
- store.dispatch
- store.getStore
- store.subscribe
redux中间件
- redux 中间件是对 dispatch 的封装。
redux-thunk
- 使用了 redux-thunk 插件后,action 就可以返回函数,而不仅仅是对象。
redux-saga
组件
- 可以将应用内的组件分为 “UI 组件”和“容器组件”
- UI 组件–负责页面的渲染
- 容器组件–处理逻辑
- 无状态组件–纯函数组件。如果一个组件只有 render 方法,可以将其改为一个无状态组件。相比于普通组件(类),无状态组件(函数)的性能更好。UI 组件写成无状态组件更为合适。
React-redux
- react-redux 提供了
<Provider>
组件。在这个组件上传递的数据,子组件都可以使用。 - connect 将 store 将组件连接。
styled-components
安装的 styled-components 是 v4.2.0 的,与视频的不一样,所以写法也不一样。
- 没有 injectGlobal 这个 API 了,取而代之的是 createGlobalStyle。这个 API 是用来创建一个组件的。
// 创建css组件 export const Global = createGlobalStyle` html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } ` // 使用css组件 import React, { Fragment } from 'react' import { Global } from './style.js' function App() { return ( <Fragment> <Global /> <div> dd </div> </Fragment> ); } export default App;
react-router
- 安装包为 react-router-dom
npm i react-router-dom -s import { BrowserRouter, Route } from 'react-router-dom'
- 路径的严格匹配需要添加 exact 属性
- Link 组件必须写在 BrowserRouter 组件里面。比如可以在父组件用 BrowserRouter 包裹子组件,这样子组件就可以使用 Link 了。
immutable.js
- 用来确保数据不被改变。
- immutable 处理过的数据不能通过属性运算符取值,需要通过 get 方法得到值。
import { fromJS } from 'immutable' const im = fromJS({ p: 1 }) // 错误 console.log(im.p) // 正确 console.log(im.get('p'))
- immutable 处理过的数据的修改需要通过 set 方法实现。
- List 只能把外层数组变成 immutable,而 fromJS 可以将整个全部变成 immutable。
吐槽
- 数据的双向绑定竟然要自己监听事件来实现。
- redux 竟然要自己手动订阅。
- redux、redux-thunk、redux-saga、react-redux…… 还能更蛋疼一点吗-_-