概念

虚拟DOM

  • 由程序员写的、用来模拟页面DOM元素的 东西。
    • js对象来模拟DOM元素以及DOM之间的嵌套关系
  1. <div id="Adiv" title="today">
  2. 今天星期二
  3. <p>学习学习</p>
  4. </div>

用js模拟(以对象的形式)如下:

  1. var div = {
  2. tagName: 'div',
  3. attrs: {
  4. id: 'Adiv',
  5. title: 'today'
  6. },
  7. childrens: [
  8. '今天星期二',
  9. {
  10. tagName: 'p',
  11. attrs: {},
  12. childrens: [
  13. '学习学习'
  14. ]
  15. }
  16. ]
  17. }

使用babel语法表示:

  1. var div = <div className='mydiv'>今天202021414</div>

Diff 算法

  • tree diff:新旧两颗DOM树的对比,逐层比对
  • component diff:每层的组件对比
  • element diff:元素级别的对比

webpack

create-react-app脚手架自带有webpack,可在文档查看打包方法,且更方便。

  1. 在项目文件夹下运行npm init -y,快速初始化项目
  2. 在项目根目录创建src源代码目录和dist产品目录
  3. 在 src 目录下创建index.html
  4. 使用 cnpm 安装 webpack, 运行cnpm i webpack -D
    • 全局运行npm i cnpm -g
  5. 默认:
    • 打包入口:src->index.js
    • 打包的输出文件:dist->main.js
  6. 在项目根目录下创建webpack.config.js
    • 设置mode选项,可选值为:developmentproduction

简单框架

  1. 导入 react包
    • import React from 'react'创建 组件、虚拟DOM元素、生命周期
    • import ReactDOM from 'react-dom' 来把创建好的 组件、虚拟DOM 渲染到页面上
  2. 创建虚拟DOM元素
    • param1: 创建的元素的类型,字符串,表示元素名称
    • param2: 一个对象或null,表示这个DOM元素的属性
    • param3: 子节点,包括其子虚拟DOM或文本子节点
  1. const aTag = React.createElement(
  2. 'h1', // param1
  3. { id: 'aTag', title: 'Ah1'}, // param2
  4. 'the inner text' // param3
  5. );
  1. 使用 Babel 简化创建方式
  2. 渲染虚拟DOM到页面上
    • param1: 要渲染的虚拟DOM元素
    • param2: 页面上的一个容器
  1. ReactDOM.render(aTag, document.getElementById('root'));

Babel

  1. Babel 解析JSX语法代码转为纯JS语法代码
  • JSX —- Javascript XML
  1. 创建DOM
  1. const aDivTag = <div id='aDivTag'>react learn</div>
  1. JSX语法:
  2. 使用占位符 {} 来渲染变量、表达式、字符串、数组、标签…到页面或标签中
  1. let tmp = 10;
  2. let title = 'newTitle'
  3. let divTag = <div title={title}>{tmp}</div>
  1. 添加类名,使用 className 来代替 class
  2. JSX注释,用{/* zhushi */}
  3. 渲染数组,
    • map循环,返回一个对item处理了的值,每一个返回的元素需要有一个唯一的key值
  1. const result = list.map(item => return <tag xxx>xxx</tag>)
  2. const result = list.map(item => { return <tag xxx>xxx</tag> }) // 箭头后有大括号时,表示函数体,必需用return
  3. const result = list.map(item => <tag {...item} key='item.id'></tag>);
  • forEach循环,,没有返回值,在函数内部生成新的数据
  1. arrstr.forEach(item => {
  2. let temp = <h5>{item}</h5>
  3. tmparr.push(temp)
  4. })

创建组件

  1. 直接用<组件名></组件名>来渲染组件 or <组件名 />
  2. 名字首字母必须大写,参数是 readonly
  3. 单独的组件文件时,将组件单独写在一个.jsx文件里面时,这个jsx里面也需要import React from 'react',并且export 组件
  4. 默认属性值defaultProps,参数的默认值,在定义组件之后,export之前

    1. App.defaultProps = {
    2. gender: 'female'
    3. }
  5. 验证父组件传入参数的合法性propTypes
    ```javascript // 1. 在子组件里面 import propTypes from ‘prop-types’

// 2. 在定义子组件之后,export之前 App.propTypes = { num: PropTypes.number }

// 2. 或者写在组件内部 static propTypes={ num: PropTypes.number // 表示参数num的类型需要是number name: PropTypes.string.isRequired // name为string类型 且不能为空 }

  1. <a name="753a15f4"></a>
  2. ##### 第一种 function
  3. 1. 用`function`关键字来创建
  4. 2. 使用`return`返回内容
  5. ```javascript
  6. function Component(props){
  7. return <p>组件内部...</p>;
  8. }
  9. ReactDOM.render(
  10. <div>渲染组件
  11. <Component></Component>
  12. </div>,
  13. document.getElementById('root')
  14. )

第二种 class
  1. 使用**class**来创建
    • 必须继承自 Component
    • 必须实现 render函数
    • 传入的参数,直接通过 {this.props.xx} 来使用
  1. class App extends Component {
  2. // 构造函数
  3. constructor(props){
  4. super(props)
  5. }
  6. render(){
  7. return (
  8. <div className="div">
  9. {this.props.name}
  10. </div>
  11. )
  12. }
  13. }
  14. // 使用
  15. const usr = {
  16. name:'zs',
  17. age: 9
  18. }
  19. ReactDOM.render(
  20. <App {...usr} />,
  21. document.getElementById('root')
  22. )
  1. class组件内部,类似render等重写组件类的方法 内部的this表示组件对象; 其他新创建的函数内部的thisundefined,使用.bind方式来使this表示为组件对象
    • 使用箭头函数就好了,不需要bind
      1. // 在构造函数里面

this.myClick = this.myClick.bind(this);

  1. 4. 在子组件里面改变父组件的状态,不能直接改变
  2. - 状态在哪个组件,更新状态的行为就应该在哪个组件
  3. - 所以,子组件需要调用父组件的方法 来改变父组件的状态
  4. - 将父组件函数的引用传给子组件 `name = {this.name}`
  5. ### 3种基本属性
  6. #### state
  7. 1. 使用`class`来创建组件时,该组件拥有自身的一个属性`state`,一般可定义在构造函数里面,但是也可以直接写在类里面
  8. ```javascript
  9. // 构造函数里面的
  10. this.state = {msg: 'my name is msg'}
  11. // 类里面
  12. state = {msg: 'my name is msg'}
  1. 注意,不要在render里面改变state的值
  2. 更改state的值,使用this.setState()

    1. this.setState({msg: 'haha'}, function(){
    2. console.log(this.state.msg) // 输出1
    3. });
    4. console.log(this.state.msg) // 输出2
    • 该设置是异步执行的,需要在回掉函数中输出新的结果,在输出2中输出的仍然是旧的值
    • 且先 输出2 再输出1

props

  1. 传入的参数,在组件标签里面propName = {xxx},可以是变量、对象、函数等
  2. 传入对象时,可以一个一个写,或者用...

    1. function Component(props){
    2. return <p>{props.name}</p>;
    3. }
    4. const user = {
    5. name: 'lin',
    6. age: 21,
    7. gender: 'female'
    8. }
    9. ReactDOM.render(
    10. <div>渲染组件
    11. <Component name={user.name} age={user.age} gender={user.gender}></Component>
    12. </div>,
    13. document.getElementById('root')
    14. )
    • 使用ES6的写法 传入参数, ...展开运算符,<Component {...user}></Component>
    • ...的作用:
    • 打包
      ```javascript // 定义一个函数 function fn(…por){}

// 调用这个函数 fn(1,2,3)

// 这儿的’…’把传入的三个数打包成一个参数por,por就是一个数组

  1. - 解包
  2. ```javascript
  3. const arr1 = [1, 2, 3]
  4. const arr2 = [4, ...arr1, 8]
  5. // 这儿的'...'把arr1解开 放入arr2中

refs

  1. refs属性,可以绑定在render()输出的任意标签上
    ```javascript

var input = this.refs.myInput; var txt = input.value; // 这样可以获取上面input的值

  1. ```javascript
  2. <input ref={input => this.input = input} />
  3. const input = this.refs.content

添加css样式

添加行内样式

  1. 使用对象的形式,双{}: <div style={{width: '300px', height: '300px'}}></div>
  2. 两个单词连接的,第二个单词首字母大写 backgroundColor
  3. 封装成样式对象,然后在标签里面引用

导入css文件

  • import cssobj from css文件路径import from css文件路径
  • 注意: 在一个项目中导入了css样式文件,它的作用域在整个项目——-下面改作用域

css Module 样式表

  • 防止全局式的css 系统将你的本地类名(此模块中的类名)编译改成全局名,所以为何不在自己写的时候就对应全局的类名呢。毕竟动态赋予类名时还不知道改怎么弄…
  • css文件使用[name].module.css来命名.
  • import时,用
  1. import styles from '路径'
  2. ... return <div className={styles.classname}></div>

event

btn_click

  • 小驼峰命名法, onClick = { }
  1. <button style={{xxx}} onClick={ () => { this.display('参数1','参数2')} }>按钮</button>
  2. /// 或者
  3. <button onClick={this.search}>Search</button>
  4. display = (arg1, arg2) => {
  5. console.log("333")
  6. }

input的onChange事件

  • 分三步
    • 为文本框绑定onChange事件
    • 在这个事件中,拿到最新的文本框的值
    • 调用this.setState同步数据
  1. <input type='text' style={inputStyle} onChange={ (e) => {this.chagemy(e)}} value={this.state.msg} ref = 'txt'/>
  2. chagemy(e){
  3. // this.setState({ msg: this.refs.txt.value})
  4. this.setState({ msg: e.target.value})
  5. }

input_onBlur

  1. input的失去焦点事件
    ```javascript

handlerBlur = (event) => { alert(event.target.value) }

  1. <a name="kd66L"></a>
  2. ## 定时器
  3. 1. 只使用一次的定时器
  4. 2. 循环使用的定时器
  5. ```javascript
  6. // 生命周期函数
  7. componentDidMount() {
  8. // 设定循环定时器
  9. // this.intervalId 添加为组件对象的属性 是为了之后方便清除计时器
  10. this.intervalId = setInterval(function () {
  11. let opacity = this.state.opacity;
  12. opacity -= 0.1;
  13. console.log("定时器");
  14. if(opacity <= 0){
  15. opacity = 1;
  16. }
  17. this.setState({opacity});
  18. }.bind(this), 200); // bind 指定 this为组件对象
  19. }
  20. // 清除定时器
  21. clearInterval(this.intervalId);

组件间通信的传递

传统方式

使用 PubSubJS

  1. 下载 npm install pubsub-js --save
  2. 引入 import PubSub from 'pubsub-js'
  3. 发布消息

    1. PubSub.publish("search", searchName); // 消息名字,附带的数据
  4. 订阅消息

    1. PubSub.subscribe("search", (msg, searchName) => {
    2. // searchName 就是之前发布的消息附带的数据
    3. }
    4. )

redux

组件的生命周期

  • 组件的创建
  • 组件的运行
  • 组件的销毁
  • 流程:
    • 第一次初始化渲染显示:ReactDOM.render()
      • constructor(): 创建对象 初始化 state
      • componentWillMount(): 将要插入回调
      • render(): 用于插入虚拟DOM回调
      • componentDidMount(): 已经插入回调
    • 每次更新 state: this.setState()
      • componentWillUpdate(): 将要更新回调
      • render(): 更新渲染
      • componentDidUpdate(): 已经更新
    • 移除组件: RenderDOM.ummountComponentAtNode(containerDom)
      • componentWillUnmount(): 逐渐将要被移除回调

引入图片

  • import logo from '路径'

引入文件 与本文件在同一级 用’./‘,在上一级 用’../‘

other mind

  1. 箭头函数的使用
  2. 组件间的参数、函数传递的方法类似
  3. 将一个对象拆分成几个数值时,名字对应,,结构赋值 ES6语法
  1. var object = {
  2. name: 'lin',
  3. age: 22
  4. }
  5. const {name, age} = object;
  1. 非受控组件与受控组件(收集表单数据的)
    • 受控组件:表单项输入数据能自动收集成状态(收集到state里面,读组件的状态)
    • 非受控组件:需要手动读取表单输入框中的数据(直接读取数据) ```javascript
      // 使用ref直接读取数据,非受控组件 用户名: this.nameInput = input} /> // 读取的是state里面的值,受控组件 密码:

handlerSubmit = (event) => { const name = this.nameInput.value; alert(准备提交的用户名为${name},密码为${this.state.pwd}); // 这种${}是什么写法啊

  1. // 阻止提交表单
  2. event.preventDefault();

} handlerChange = (event) => { let pwd = event.target.value; this.setState({pwd: pwd}); // 修改state里面的值 }

  1. 6. 对象的`map`函数
  2. 7. 可以在组件里面调用其它的组件
  3. <a name="vQiBF"></a>
  4. ## ajax_get_post
  5. **使用axios**
  6. ```javascript
  7. axios.get(url)
  8. .then(response => {
  9. const result = response.data;
  10. const users = result.items.map(item => ({name: item.login, url: item.html_url, avatarUrl: item.avatar_url}));
  11. this.setState({loading: false, users});
  12. })
  13. .catch(error => {
  14. console.log('error');
  15. this.setState({loading: false, errorMsg: error.message});
  16. })

Router

  1. 专门用来实现 SPA 单页面应用
    • 点击页面不会刷新页面,也不会向服务器发送请求
    • 点击路由链接,页面局部刷新
    • 整个应用只有一个页面
  2. 路由,一个路由就是一个映射关系
    • key 为路由路径,value 可以是 function / component
    • 底层利用浏览器history实现
  3. 路由分类
    • 后台路由,node服务器端路由,value 是 function ,用来处理客户提交的请求并返回一个响应数据
      • 注册路由: router.get(path, function(req,res))
    • 前台路由,浏览器路由,value 是 component ,当请求某路由path时,浏览器没有发送http请求,但界面会更新显示对应的组件
      • 注册路由:<Route path='/about' component={About} />
  4. 组件
    • <BrowserRouter>,路由器组件
    • <HashRouter>,路由器组件
    • <Route>,路由
    • <Redirect>,重定向
    • <Link>,路由链接
    • <NavLink>,导航路由链接
      • activeClassName可指定自定义的active显示样式
    • <Switch>,多个
  5. 基本使用
    ```javascript // index.js // 使用路由器组件将整个应用包住 ReactDOM.render( , document.getElementById(‘root’) )

// app.jsx import {NavLink, Switch, Route, Redirect} from ‘react-router-dom’

// 两个供显示的简单组件 import Home from ‘./views/home’ import About from ‘./views/about’

// 在左边显示目录,右边显示组件内容 render(){ return (

/ 链接 / About Home
/ 表示可切换的路由 / // 默认显示哪个组件
) }

  1. PS
  2. ```javascript
  3. // 使用自定义的组件 MyNavLink
  4. <MyNavLink className='list-group-item' to='/about'>About</MyNavLink>
  5. // MyNavLink.jsx
  6. render(){
  7. return <NavLink {...this.props} activeClassName='activeClass'></NavLink>
  8. }
  9. /* 当页面里面所有的NavLink都需要设置自定义的样式,且都一样,这样可以减少代码 */