2020-03-03-- umiJS

2020-03-03  本文已影响0人  小丹子1o1

umiJS

umiJS 有强的约束力

打包 umi build

两种路由使用方法

  1. 约定式路由:约定好的文件夹和文件,来代表页面,umi会根据开发者书写好的页面,生成路由配置;最终会变成配置式路由。
  2. 配置式路由:直接书写路由配置文件

约定式路由

路由跳转

获取路由信息

  1. match -> react-router的match
  2. history -> react-router的match
  3. match
  4. location
  5. route 路由对应的配置

配置式路由

  1. 项目根目录下的文件.umirc.js,他是umijs的配置,里面有个routes:[]
export default {
  routes:[
    {   
        title:"首页"
        path:"/"
        component: "./index" //相对于pages路径配置的,pages/index
        exact:false
        routes:[
            //包子路由 {...}
            ".src/routes/Private.js", ".src/routes/b.js" //这种可以实现路由权限
        ]
    }
  ]
}
  1. 项目根目录下的config/config.js

使用dva

  1. 全局模型:所有页面通用,工程一开始启动后,模型就会挂载到仓库
    // dva 写法,组件connect 之后,dispatch({ type:"counter/delete" })
    export default {
        state:0,           // counter.js
        reducers:{
            delete(state){ // type:"counter/delete"
                return state - 1
            },
            add(state){
                return state + 1
            }
        }
        effects: {
            *update(action, sageEffects){
                // 这里是一些逻辑
                // 副作用处理用 sageEffects 指令
            }
        }
    }
  1. 局部模型:只能被某些页面使用,访问具体的页面时才会挂载到仓库

样式

  1. 保证类样式名称的唯一性:css-module / BEM
  1. 样式代码的重复 less

代理和数据模拟

  1. 代理
  1. 数据模拟

umi 约定的mock

  1. mock文件夹中的文件
  2. src/pages文件夹中的_mock.js文件

dva

初始化dva

    import React from "react"
    import dva from "dva"
    const App = (props) => {
        return(
            <div>
                网站根App
            </div>)
    }
    // 得到一个dva对象
    const app = dva()

    // 启动之前定义一个模型,redux-saga/action/reducer 整合在一起
    // 必须在启动之前
    app.model({
        namespace:"student",   //不能缺省,仓库名。
        state:{                //默认值
        total:0,
        datas:[]
        },
        reducers:{             //reducer,dva会自动合并
        // dva 约定,方法的名字,就是具体action type
        // 组件使用dva下的connect链接,跟以前一样的用法    
        delete(state, action){
            //以前同步的逻辑
            return {...state}  //注意 immer 数据写法
        },
        },
        effects:{              //副作用,底层redux-saga,方法的名字就是触发的action
        /**
            * @param {*} action 
            * @param {*} sagaEffect redux-saga 对象
            *  {
            *     put({type:'delete'}) //重新触发自己的action,不用加前缀
            *     ...还有很多方法,查文档
            *  }
            */
        *asyncAdd(action, sagaEffect){
            // 这里推荐ES6 开发,阮老师的ES6
            // https://es6.ruanyifeng.com/#docs/generator
        }
        },
        /**
         * 每个对象是一个函数,订阅者。
         * 加入store的时候执行一次
         */
        subscriptions:{
        func1(obj){
            //处理一些订阅事件,因为它只运行一次,非常适合订阅一些事件
            //比如注册一些特别的事件
            //obj.dispatch({type:'delete'})  
        }
        }
    })

    // 设置启动后的根路由,
    app.router(() => <App/>)
    // 该函数传入一个选择器,相当于document.getElementById("root")
    app.start("#root")

组件使用dva

    import React from "react"
    import { connect } from "dva"

    const Test = (props) => {
        return(
            <div>
                网站根App
            </div>)
    }
    const mapStateToProps = (state) => ({
        total:state.student.total
    });

    const mapDispatchToProps = (dispatch) => ({
        // 传入的props方法以on开头。注意加命名空间
        onDelete:() => dispatch({type:"student/delete", playload:1})
    })

    export default connect(mapStateToProps, mapDispatchToProps)(Test)

Generator 函数的语法

  // 伪代码,Generator 是js引擎的用的,外部不能用
  // 注意不能箭头函数,箭头函数没有this 
  var g = new Generator()
  // 迭代器
  g.next()
  // 它也是一个可迭代对象
  // for of   
  var iterator = g[Symbol.iterator]; 
  1. 生成器函数,该函数用于创建一个生成器
   //function *func1
   function* create(){

   }
   //生成器函数create调用后,返回的一定是一个生成器,而函数体里面的代码不会执行
   //函数体的代码是生成器控制执行的    
   var g = create()    
  1. 每当调用一次生成器的next方法,生成器的函数体会从上次的yield的位置(或开始位置)运行到下一个yield, yield只能在生成器函数里面使用。
  2. yield 表达式返回的数据,会当做当前迭代的数据,提前return 会提前done:true
  3. 生成器的返回值,是结束后的返回值,一旦结束后继续next(),
   function* create(){
     console.log("kaishi")
     yield;     //g.next() -> { value:undefined, done:false }
     yield 1;   //g.next() -> { value:1, done:false }
     return 2;  //g.next() -> { value:2, done:true }     
   }  
   var g = create()  
  1. 生成器调用next的时候,可以传递参数,该参数会作为生成器函数体上一次yield表达式的值
   function* create(){
     console.log("开始")  
     //将 1 作为这次的迭代结果,等待第二次next,result才是1 
     //每次会卡在yield等待你 next() 走
     //yield 受外面控制的,next() 才往下走,参数是yield的返回值
     let result = yield 1;          // 等待,我的返回值是上次next(b)中的b    
     console.log("yield 1", result) // yield 1, b
     result = yield 2;              // 等待,我的返回值是上次next(c)中的c 
     console.log("yield 2", result) // yield 2, c
     result = yield 3;              // 等待,我的返回值是上次next(d)中的d 
     console.log("yield 3", result) // yield 3, d
     //yield + 1 = 4 次就完成了,后面再next都是 { value:undefined, done:true } 
   }  
   var g = create() 
   g.next("a") // 第一次传参无任何意义 { value:1, done:false }
   g.next("b") // { value:2, done:false }
   g.next("c") // { value:3, done:false }
   g.next("d") // { value:undefined, done:true }
  1. 如果生成器函数嵌套生成器函数,如果单单只是写执行,没有任何作用
   function* g2(){}
   function* create(){
     console.log("开始")  
     let result = yield 1;          // 等待,我的返回值是上次next(b)中的b    
     console.log("yield 1", result) // yield 1, b
     result = yield 2;              // 等待,我的返回值是上次next(c)中的c 
     g2();                //没有任何反映,想下为什么?
     result = yield* g2() //这样才会进入这个生成器。result是g2的返回值  
     console.log("yield 2", result) // yield 2, c
     result = yield 3;              // 等待,我的返回值是上次next(d)中的d 
     console.log("yield 3", result) // yield 3, d
     //yield + 1 = 4 次就完成了,后面再next都是 { value:undefined, done:true } 
   }  
   var g = create() 
   g.next("a") 
   g.next("b") 
   g.next("c") 
   g.next("d") 

redux-saga

  1. 最开始的时候会启动一个saga任务,其实就是生成器函数,因为外部可控,不用等到action
  2. saga任务提供了很多任务指令,可以通过指令控制它的运行,说白了就是通过指令来控制redux流程
  3. sage任务就是一个生成器函数
function* task(){ console.log('启动了') }
const sagaMid = createSagaMiddleware()
// store 后面运行
sagaMid.run(task) //启动了生成器,生成器就受外面控制了
  1. 一般会把生成器函数放在saga文件夹
    function* task(){ 
        console.log('启动了')
        yield 3; // yield 普通数字没什么意义,会立即调用,同步代码一样
        yield `saga effects`; //放的是saga指令,会特殊处理,控制整个流程
    }
  1. saga effects
    function* task(){ 
        console.log('启动了')
        yield 3; // yield 普通数字没什么意义,会立即调用,同步代码一样
        yield takeEvery(actionTypes.a, task()); //放的是saga指令,会特殊处理,控制整个流程
    }

react hook

useState:

  1. 运行函数组件时,调用useState(第一次运行)
  2. 检查一个状态表格
  1. 重新渲染界面又调用一次
  1. 表格附着在函数组件上的,所以不会共享状态(各有各的状态表格)
  1. useState最好写在函数起始位置,方便维护
  2. useState不能出现在判断条件if里面,这样对维护表格不利(react根本不让这么写)。
  3. useState返回的第二项,引用不变,节省内存
  4. 如果使用函数改变数据,若数据和之前的数据完全相等(使用Object.is),不会导致重新渲染。
  5. 使用函数改变数据,传入的值不会和原来的合并,而是直接替换。(setState是混合的,可以局部修改)
  6. 也不能直接修改数据,和之前类组件一样。
  7. 如果要强制刷新组件
  1. 和类组件一样,组件中改变状态可能是异步的(在dom事件中),多个状态会合并提高效率,此时,不能 信任之前的状态,应该使用回调函数的方式。

如果某些状态之间没有必然的联系,应该切分为不同的状态,而不要合并成一个状态 因为代码耦合度越低,越好维护

Effect Hook:用于函数组件中处理副作用

细节

  1. useEffect运行的时间点是页面真实dom渲染完成后。因此它是异步执行的,并且不会阻塞浏览器
  2. 相当于类组件的生命钩子DidMount和DidUpdate,挂载完和更新完
  3. 但是又有区别的,类组件的两个生命钩子更改了真实的dom,但是用户没有看到UI界面,同步的
  4. useEffect中的副作用函数,更改了真实的dom,并且用户已经看到了UI更新,异步的。不会阻塞界面
  5. 每个函数组件中可以多次使用,但是也不能放在判断或循环等代码块中,和useState一样,后面的hook基本一致
  6. useEffect是有返回值的,返回的是一个清理函数,函数的运行时间点是每次运行副作用的函数之前,首次渲染组件不会运行。组件销毁时,一定会运行。
  7. 重新渲染组件的时候,不想重新运行副作用函数,可以使用第二个参数,这个参数是数组,然后副作用函数就依赖这个数组,只有依赖的数据和上一次不一样时,才会运行执行第一个副作用函数参数。
  8. 所以当传递了依赖数据之后,如果数据没有变化 8.1 副作用函数仅在第一次渲染后运行 8.2 清理函数仅在卸载组件后运行
  9. 所以,就可以利用useEffect来实现之前类组件的挂载运行一次,卸载时运行一次。(特别是监听移除dom事件)
  10. 如果依赖项使用的是空数组,清理函数就没有作用了,但是卸载还是会运行一次
  11. 副作用函数中,如果使用了函数组件上下文的变量,由于闭包的影响,会导致副作用函数中变量不会实时变化。js的知识点。
  12. 如果副作用函数在每次注册时,会覆盖之前的副作用函数,因此,尽量保持副作用函数稳定,否则控制起来会比较复杂。(把副作用函数抽出去写,动态改变)

自定义hook:将一些常用的、跨组件的hook功能,抽离出去形成一个函数,该函数就是自定义hook

  1. render props 数据一样,界面不一样,其实就是传递一个render函数下去运行。
  2. withComponent 高阶组件就是把相同的逻辑抽出来。
  1. 函数名必须以use开头
  2. 调用自定义hook函数时,应该放到顶层
  3. 自定义hook其实就是把hook抽成一个函数出去,多个hook就可以抽多个,横切关注点,用组合compose编程。

reducer hook

// redux:action -> store.dispatch(action.type) -> reducer -> newState -> store
// 通用的reducer hook, 其实就是一个store
export default function useReducer(reducer, initState, initFunc) {
    // initFunc是一个对第二个参数进行计算的一个回调函数
    const [state, setState] = useState(initFunc ? initFunc(initState) : initState)
    // 相当与store.dispatch
    function dispatch(action){
        const newState = reducer(state, action)
        setState(newState)
    }
    return [state, dispatch]
}
// reducer hook 未来可能会结合redux使用

Context hook

callback hook

  1. 函数,useCallback会固定该函数的引用,只要依赖项没发生变化则会保持之前函数的地址
  2. 依赖项,也是个数组

Memo hook

Ref hook 一个参数:默认值,返回一个固定的对象。{current:值}

imperativeHandle Hook

useImperativeHandle(ref, () => {
  //如果不给依赖项,则每次运行函数组件都会调用该函数
  //如果使用了依赖项,则第一次调用后,则进行缓存,依赖项变化才重新运行
  <!-- ref.current = 1 -->
  return 1
}, [])
// 这个hook,主要是用来使用ref转发的

LayoutEffect Hook

细节

antd pro

上一篇 下一篇

猜你喜欢

热点阅读