React进阶学习

-工具类

node、npm、eslint、pretter

语法类

es5、es6

概念类

SPA:单页面应用

MPA:多页面应用

PWA:渐进式 Web App

PWA组成技术

  1. Service Worker:服务工作线程

    1.1 常驻内存运行

    1.2 代理网络请求

    1.3 依赖HTTPS

    // npm install serve -g 全局安装服务模块
    // 在项目目录下执行 serve 快速启动http本地服务器 localhost:5000
    // index.html
    
        
            learn PWA
        
        
            
        
    
    
    // sw.js
    self.addEventListener('install', event => {
        console.log('install', event)
        // 在 install 执行后 5s 再激活 执行 activate
        /*
        event.waitUntil(new Promise(resolve => {
            setTimeout(resole, 5000)
        }))
        */
        // 强制停止旧的service worker 激活新的 service worker 
        event.waitUntil(self.skipWaiting())
    })
    
    self.addEventListener('activate', event => {
        console.log('activate', event)
        // 同样包含 event.waitUntil() 与 install 中用法一样
        event.waitUntil(self.clients.claim())    
    })
    
    self.addEventListener('fetch', event => {
        console.log('fetch', event)
    })
    
  1. Promise:”承诺”控制流

    2.1 优化回调地狱

    2.2 async/await 语法同步化

    2.3 Service Worker的API

    // callback回调方式
    readFile(filename, (err, content) => {
        parseXML(content, (err, xml) => {
            // 
        })
    })
    
    readFile(filename).then(content => parseXML(content)).then(xml => {}, err => {})
    // 推荐使用
    readFile(filename).then(content => parseXML(content)).then(xml => {}).catch(err => {})
    
    Promise.resolve(1)
    // 等价于
    new Promise(resolve => resolve(1))
    
    Promise.reject(error)
    // 等价于
    new Promise((resolve, reject) => reject(error))
    
    Promise.all()
    Promise.race()
    
    // 替代 Promise 需要 babel 转换
    async function readXML(filename) {
       const content = await readFile(filename) 
       const xml = await parseXML(content)
       return xml
    }
    
  2. fetch:网络请求

    3.1 比XMLHttpRequest更简洁

    3.2 Promise风格

    3.3 依旧存在不足

    
        
            learn PWA
        
        
            
        
    
    
  3. cache API:支持资源的缓存系统

    4.1 缓存资源(css/scripts/image)

    4.2 依赖 Service Worker 代理网络请求

    4.3 支持离线程序运行

    // index.html
    
        
            learn PWA
        
        
            

    hello PWA

    // sw.js
    const CACHE_NAME = 'cache-v1'
    
    self.addEventListener('install', event => {
        console.log('install', event)
        event.waitUntil(caches.open(CACHE_NAME).then(cache => {
            cache.addAll([
                './',
                './index.css'
            ])
        }))  
    })
    
    self.addEventListener('activate', event => {
        console.log('activate', event)
         // 同样包含 event.waitUntil() 与 install 中用法一样
        event.waitUntil(caches.keys().then(cacheNames => {
            return Promise.all(cacheNames.map(cacheName => {
                if(cacheName !== CACHE_NAME){
                    return caches.delete(cacheName)
                }
            }))
        }))    
    })
    
    self.addEventListener('fetch', event => {
        console.log('fetch', event)
        event.respondwWidth(caches.open(CACHE_NAME).then(cache => {
            return cache.match(event.request).then(res => {
                if(res) {
                    return res
                }
                return fetch(event.request).then(res => {
                    cache.put(event.request, res.clone())
                    return res
                })
            })
        }))
    })
    
  4. Notification API:消息推送

    5.1 依赖用户授权

    5.2 适合在Service Worker中推送

    // 在页面上下文中查看用户授权
    Notification.permission // defalut 默认 granted 允许 denied 拒绝
    // 在页面上下文中弹出授权
    Notification.requestPermission().then(permission => console.log(permission))
    // 弹出通知消息
    new Notification("HELLO NOTIFICATION", {body: 'this is from console'})
    // 在 Service Worker 上下文 中不允许弹出授权请求
    // 在 Service Worker 上下文中 弹出通知消息
    self.registration.showNotification("HELLO NOTIFICATION", {body: 'this is from sw'})
    

    在webpack中开启PWA(create-react-app中已经集成service worker)

    谷歌推出 workbox
    

效率类

原则类

eject

npm run eject 打开create-react-appwebpack.config,js

React 最新特性

  • context

    定义:提供一种方式,能够让数据在组件树中传递而不必一级一级手动传递

    import React, { createContext } from 'react'
    
    const BatteryContext = createContext()
    const OnlineContext = createContext()
    
    // 第三级组件
    class Leaf extend Component {
        render() {
            return (
                
                    {
                        battery => (
                          
                                {
                                    online => 

    Battery: {battery}, Online: {String(online)}

    }
    ) }
    ) } } // 中间组件 class Middle extend Component { render() { return } } // 顶级组件 class App extend Component { state = { battery: 60, online: false } render() { const { battery, online } = this.state return ( ) } }
  • ContextType简化context的使用

    import React, { createContext } from 'react'
    
    const BatteryContext = createContext(90) // 90 为默认值
    
    // 第三级组件
    class Leaf extends Component {
        static ContextType = BatteryContext
        render() {
            const battery = this.context
            return (
               

    Battery: {battery}

    ) } } // 中间组件 class Middle extends Component { render() { return } } // 顶级组件 class App extends Component { state = { battery: 60 } render() { const { battery, online } = this.state return ( ) } }
  • lazy 与 Suspense:组件的懒加载

    // about 组件
    import React, { Component } from 'react'
    
    export default class About extends Component {
        render () {
            return (
              

    About

    ) } }
    // APP 组件
    import React, { Component, lazy, Suspense } from 'react'
    
    const About = lazy(() => import(/* webpackChunkName: "about" */'./About.jsx'))
    
    class App extends Component {
        state = {
            hasError: false
        }
      // 捕捉异常的静态方法 
        static getDerivedStateFromError() {
            return { 
                hasError: true 
            }
        }
        // 捕获异常的生命周期函数
        /*
        componentDidCatch () {
            this,setState({
                hasErrir: true
            })
        }
        */
        render () {
            return (
                if (this.state.hasError) {
                  return 
    error
    }
    loading
    }>
) } } export default App
  • memo 和 PureComponent:避免子组件的重复渲染

    import React, { Component, PureComponent} from 'react'
    
    /*
    class Foo extends Component {
        
        shouldComponentUpdate(nextProps, nextState) {
            if (nextProps.name === this.props.name) {
                return false
            }
            return true
        }
      */
        
    class Foo extends PureComponent {
        
        /*
        shouldComponentUpdate(nextProps, nextState) {
            if (nextProps.name === this.props.name) {
                return false
            }
            return true
        }
        */
        render() {
            console.log('Foo render')
            return null
        }
    }
    
    class App extends Component {
        state = {
            count: 0
        }
        render() {
          return (
              
    ) } }
    import React, { Component, PureComponent, memo } from 'react'
    
    const Foo = memo((props) => {
        console.log('Foo render')
        return 
    {props.person.age}
    }) class App extends Component { state = { count: 0, person: { age: 1 } } render() { return (
    ) } }

Hooks

  • 使用useState简化状态的写法

    import React, { component } from 'react'
    
    class App extends Component {
        state = {
            count: 0
        }
        render() {
          const { count } = this.state
             return (
              
             )
        }
    }
    
    export default App
    
    import React, { useState } from 'react'
    
    function App() {
        const [ count, setCount ] = useState(0)
        return (
            
        )
    }
    
    export default App
    

    npm i eslint-plugin-react-hooks -D 可以检测Hooks的错误,需要在package.json中配置

    "eslintConfig": {
        "extends": "react-app",
        "plugins": [
            "react-hooks/rules-of-hooks": "error"
        ]
    }
    
    import React, { useState } from 'react'
    
    function App(props) {
        const [ count, setCount ] = useState(() => {
            console.log('initial count')
            return props.defaultCount || 0
        })
        return (
            
        )
    }
    
    export default App
    
  • 使用useEffect简化副作用的写法

    import React, { component } from 'react'
    
    class App extends Component {
        state = {
            count: 0,
            size: {
                width: document.documentElement.clientWidth,
                height: document.documentElement.clientHeight
            }
        }
    
        onResize = () => {
            this.setState({
                size: {
                    width: document.documentElement.clientWidth,
                    height: document.documentElement.clientHeight
                }
            })
        }
      
        componentDidMount() {
          document.title = this.state.count
          window.addEventListenner('resize', this.onResize, false)
        }
    
        componentWillMount() {
          window.removeEventListener('resize', this.onResize, false)
        }
      
        componentDidUpdate() {
          document.title = this.state.count
        }
      
        render() {
          const { count, sizehis.state
             return (
              
             )
        }
    }
    
    export default App
    
    import React, { useState, useEffect } from 'react'
    
    function App(props) {
        const [ count, setCount ] = useState(0)
        const [ size, setSize ] = useState({
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight
        })
        
        const onResize = () => {
            setSize({
                width: document.documentElement.clientWidth,
              height: document.documentElement.clientHeight
            })
        }
        
        useEffect(() => {
            document.title = count
        })
        
        useEffect(() => {
            window.addEventListener('resize', onResize, false)
            
            return () => {
                window.removeEventListener('resize', onResize, false)
            }
        }, [])
        
        return (
            
        )
    }
    
    export default App
    
  • 使用useContext跨层级组件传值

    import React, { useState, createContext, useContext } from 'react'
    
    // 在类组件中使用context
    class Foo extends React.Component {
        render() {
            return (
              
                    {
                        count => 

    count: {count}

    }
    ) } } // 在类组件中使用 contextType 简化 context 的使用 class Bar extends React.Component { static contextType = CountContext render() { const count = this.context return (

    {count}

    ) } } // 在函数组件中使用 context function Counter() { const count = useContext(CountCountext) return (

    {count}

    ) } const CountContext = createContext() function App(props) { const [ count, setCount ] = useState(0) return (
    ) } export default App
  • 使用useMemo优化性能,避免子组件重复渲染

    import React, { useState, useMemo, memo, useCallback } from 'react'
    
    const Counter = memo(function Counter(props) {
        console.log('Counter render!')
        return (
          

    count:{props.count}

    ) }) function App(props) { const [ count, setCount ] = useState(0) const double = useMemo(() => { return count*2 }, [count===3]) const onClick = useCallback(() => { console.log('Click') }, []) // 如果 useMemo 返回的是一个函数 简写成 useCallback /* const onClick = useMemo(() => { return () => { console.log('Click') } }, []) */ return (
    ) } export default App
  • 使用useRef

    import React, { PureComponent, useState, useMemo, memo, useCallback, useRef } from 'react'
    
    /* 函数组件不能别 ref 获取
    const Counter = memo(function Counter(props) {
        console.log('Counter render!')
        return (
          

    count:{props.count}

    ) }) */ class Countter extends PureComponent { speak() { console.log(`now counter is:${this.props.count}`) } render() { const { props } = this return (

    count:{props.count}

    ) } } function App(props) { const [ count, setCount ] = useState(0) const counterRef = useRef() const double = useMemo(() => { return count*2 }, [count===3]) const onClick = useCallback(() => { console.log('Click') //console.log(counterRef.current) counterRef.current.speak() }, [counterRef]) return (
    ) } export default App
    import React, { PureComponent, useEffect, useState, useMemo, memo, useCallback, useRef } from 'react'
    
    /* 函数组件不能别 ref 获取
    const Counter = memo(function Counter(props) {
        console.log('Counter render!')
        return (
          

    count:{props.count}

    ) }) */ class Countter extends PureComponent { speak() { console.log(`now counter is:${this.props.count}`) } render() { const { props } = this return (

    count:{props.count}

    ) } } function App(props) { const [ count, setCount ] = useState(0) const counterRef = useRef() const it = useRef() const double = useMemo(() => { return count*2 }, [count===3]) const onClick = useCallback(() => { console.log('Click') //console.log(counterRef.current) counterRef.current.speak() }, [counterRef]) useEffect(() => { it.current = setInterval(() => { setCount(count => count + 1) }, 1000) }, []) useEffect(() => { if(count >= 10){ clearInterval(it.current) } },[]) return (
    ) } export default App
  • 自定义的Hooks

    import React, { PureComponent, useEffect, useState, useMemo, memo, useCallback, useRef } from 'react'
    
    // 自定义hook函数
    function useCount(defaultCount) {
        const [count, setCount] = useState(defaultCount)
        const it = useRef()
    
        useEffect(() => {
            it.current = setInterval(() => {
                setCount(count => count + 1)
            }, 1000)
        }, [])
        
        useEffect(() => {
            if(count >= 10){
                clearInterval(it.current)
            } 
        })
        return [count, setCount]
    }
    
    // 自定义hook函数,返回 jsx
    function useCounter(count) {
        return (
          

    {count}

    ) } function App(props) { const [ count, setCount ] = useCount(0) const Counter = useCounter(count) return (
    { Counter }
    ) } export default App

Hooks的使用规则

1. 只在最顶层中使用hooks,不在循环、判断等条件下使用
2. 只在函数组件中使用,不在普通函数中使用

Hooks常见问题

对传统React编程影响

  1. 生命周期


    图片.png
    1. constructor:在函数组件中用useState来初始化

    2. getDerivedStateFromProps

      class Counter extends Component {
          state = {
              overflow: false
          }
          static getDerivedStateFromProps(props, state) {
              if(props.count > 10) {
                  return {
                      overflow: true
                  }
              }
          }
      }
      
      function Counter(props) {
          const [overFlow, setOverflow] = useState(false)
          
          if(props.count > 10) {
              setOverflow(true)
          }
      }
      
    3. shouldComponentUpdate:在函数组件中 使用memo

    4. render:函数组件本身返回 jsx

    5. componentDidMount

    6. componentWillUnmount

    7. componentDidUpdate

      function App() {
          useEffect(() => {
              // componentDidMount
              return () => {
                  // componentWillUnmount
              }
          }, [])
          
          let renderCounter = useRef(0)
          renderCounter.current++
          
          useEffect(() => {
              if(renderCounter > 1){
                  // componentDidUpdate
              }
          })
      }
      
  1. 类成员变量如何映射到Hooks?
class App {
    it = 0
}

funtion App() {
    const it = useRef(0)
}
  1. Hooks中如何获取历史props和state?
function Counter() {
    const [count, setCount] = useState(0)
    const prevCountRef = useRef()
    
    useEffect(() => {
        prevCountRef.current = count
    })
    
    const prevCount = prevCountRef.current
    
    return 

Now: {count}, before: {prevCount}

}
  1. 如何强制更新一个hooks组件?
function Counter() {
    const [count, setCount] = useState(0)
    const [updater, setUpdater] = useState(0)
    const prevCountRef = useRef()

    function forceUpdater() {
        setUpdater(updater => updater + 1)
    }

    useEffect(() => {
        prevCountRef.current = count
    })

    const prevCount = prevCountRef.current

    return 

Now: {count}, before: {prevCount}

}

Redux

  1. 状态容器与数据流管理

  2. redux的三大原则

     2.1 单一数据源
    
     2.2 状态不可变
    
     2.3 纯函数修改状态 
    
  3. 没有redux的世界,纯 Hooks 开发 TodoList

    .todo-list {
        width: 550px;
        margin: 300px auto;
        background: #fff;
        box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2), 0 25px 50px 0 rgba(0,0,0,0.1)
    }
    .control h1 {
        width: 100%;
        font-size: 100px;
        text-align: center;
        margin: 0;
        color: rgba(175,47,47,0.15);
    }
    .control .new-todo {
     padding: 16px 16px 16px 60px;
        border: 0;
        outline: none;
        font-size: 24px;
        box-sizing: border-box;
        line-height: 1.4rem;
        box-shadow: inset 0 -2px 1px rgba(0,0,0,0.3);
    }
    .todos {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    .todo-item {
        margin: 0;
        padding: 0;
        list-style: none;
        font-size: 24px;
        display: flex;
        align-item: center;
    }
    .todo-item input {
        display: block;
        width: 20px;
        height: 20px;
        margin: 0 20px;
    }
    .todo-item label {
        flex: 1;
        padding: 0;
        line-height: 1.2rem;
        display: block;
    }
    .line-item label.complete {
        text-decoration: line-through;
    }
    .todo-item button {
        border: 0;
        outline: 0;
        display: block;
        width: 40px;
        text-align: center;
        font-size: 30px;
        color: #cc9a9a;
    }
    
    import React, { useState, useCallback, useRef, memo } from 'react'
    import './App.css'
    
    let idSeq = Date.now()
    const LS_KEY = '_$-todos_'
    
    const Control = memo(function Control(props) {
        const { addTodo } = props
        const inputRef = useRef()
        
        const onSubmit = (e) => {
            e.preventDefault()
            const newText = inputRef.current.value.trim()
            if(newText.length === 0) {
                return
            }
            addTodo({
                id: ++idSeq,
                value: newText,
                complete: false
            })
            inputRef.current.value = ''
        }
        
        return (
         

    todos

    ) }) const TodoItem = memo(function TodoItem(props) { const { todo: { id, text, complete }, toggleTodo, removeTodo } = props const onChange = () => { toggleTodo(id) } const onRemove = () => { removeTodo(id) } return (
  4. ) }) const Todos = memo(funtion Todos(props) { const { todos, toggleTodo, removeTodo } = props return (
      { todos.map(todo => { return () }) }
    ) }) function TodoList() { const [todos, setTodos] = useState([]) const addTodo = useCallback((todo) => { setTodos(todos => [...todos, todo]) },[]) const removeTodo = useCallback((id) => { setTodos( todos => todos.filter(todo => { return todo.id !== id })) }, []) const toggleTodo = useCallback((id) => { setTodos(todos => todos.map(todo => { return todo.id === id ? { ...todo, complete: !todo.complete } : todo })) }, []) // 读取的副作用必须在写入之前才不会永远是空数组 useEffect(() => { const todos = JSON.parse(localStorage.getItem(LS_KEY) || '[]') setTodos(todos) },[]) // 写入 useEffect(() => { localStorage.setItem(LS_KEY, JSON.stringify(todos)) }, [todos]) return(
    ) } export default TodoList

你可能感兴趣的:(React进阶学习)