create-react-app 脚手架构建项目,搬砖过程当中持续总结心得

之前都没用过快速构建工具来搭react环境,此次经过学习来走一遍,而且记录下知识点(纯属我的愚见)

1.构建出现public文件目录

  • 架手架 create-react-app demo 后生成public文件-----> public文件 是放一些静态资源的,是不会参与构建的,你输入是怎样,最终打包构建仍是怎样,不像src文件下的代码会参与打包构建

2.自定义数据.json 一些操做指令 好比:test文件下有api文件下的data.json

  • test ls ---> 出现 api
  • test ls api/ ---> api文件下出现data.json
  • test vi api/data.json -----> 进入到 .json 文件
  • 左下角 :q ------> 就是退出该文件

3.create-react-app 已经集成了代理转发的功能,须要在package.json中配置 (要先把mock数据放到mock服务器上并配置5000端口,好比mock服务器上有不少.json文件)

"proxy":{   
   "/api":{    (例子)
   	"target": "http://localhost:5000"	
   }
}
复制代码

4.直接把mock数据放在public文件夹下

public文件夹
    mock文件夹
        data.json文件
            {"data":"test"}
            
localhost:3000/mock/data.json   这样也能拿到mock的模拟数据
public文件下的内容会原封不动的打包到整个项目当中,是一些静态资源
复制代码

5.父级传参到子级,能够经过...扩展运算符来传递

todos是父级传给子级的props todos = [{id:1,text:"1111"},{id:2,text:"2222"}]

todos.map(todo=>{
    return <Todo key={todo.id} {...todo}/>
})
复制代码

6.经过...扩展运算符往旧数组里加数据

const todo = {
    id: this.indexId++,
    text: text,
    completed: true
}
const newTodoArr = [...this.state.todoArr,todo]
复制代码

7.经过...扩展运算符往旧对象里里更改某个数据

若是用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉,这用来修改现有对象部分的部分属性就很方便了。

const newTodo = this.state.todos.map(item=>{
    return item.id === 1? {...item, completed: !item.completed} :item
})
复制代码

8.用redux时,actions文件夹下通常有2个文件

index.js文件下:
    import {ADD_TODO, ………等} from "./actionTypes";
    export addTodo = (text)=>({
      type:ADD_TODO,
      text
    })
    ……………等……………
    通常状况下 type 同方法名称相同而且改为大写便可
    
actionTypes.js文件下:
    export const ADD_TODO = "ADD_TODO";
    …… …… ……
复制代码

9.用reducer时,reducer文件夹下的index.js文件

const initState = {
    filter: "",
    todos: [],
    text: ""
}

const todoApp = (state = initState, action)=>{
    switch(action.type){
        case ADD_TODO:
            return {
                ...state,
                todos:[
                    ...state.todos,
                    {
                        id: action.id,
                        text:action.text,
                        completed: false
                    }
                ]
            }
        case SET_FILTER:
            …… ……
        case SET_TODO_TEXT:
            …… ……
        
        default :
            return state
    }
}
export default todoApp;

reducer里的state是不能直接操做的,必须返回一个新的state,返回新的state对象的方法有不少,好比 Object.assign({},……)也是其中一种方法
复制代码

10. index.js文件下的 reducer拆分

其中一个例子,分解reducer中的 SET_TODO_TEXT

新建一个text.js 用来拆分 SET_TODO_TEXT

import {…… ……} from ""../actions/actionTypes;
const text = (state = "", action)=>{
    switch (action.type){
        case SET_TODO_TEXT:
            return action.text
        default:
            return state
    }
}
export default text

分解完上面那个大reducer以后,把reducer文件夹中的index里面的大reducer删掉,逐步引入拆分的各个小的reducer

index.js:
    import { combineReducers } from "redux";
    import text from "./text";
    import …… ……
    
    export default combineReducers({
        text,
        …… ……
    });
    
    必须安装redux这个依赖 npm install redux 由于create-react-app 并无安装redux
复制代码

11. 在src文件夹下建立 store.js

import { createStore } from "redux";
import rootReducer from "./reducers";
import { addTodo ,…… …… } from "./actions";

export const store = createStore(rootReducer);

// 获得初始的state
store.getState()
// 订阅state的变化
store.subscribe(()=>{
    console.log(store.getState)
})
// 发生actions
store.dispatch(addTodo("11111"));
复制代码

12. 集成react-redux(容器型组件拆分,编写 )

  • 向根组件注入 Store -> Provider 组件
  • 链接React组件和redux状态层 -> connect
  • 获取React组件所需的State和Actions -> map api
  • 先安装react-redux这个依赖 npm install react-redux 由于create-react-app 并无安装react-redux
例子:在src下建立containers文件夹,建立TodoListContainer.js文件 容器组件

    import { connect } from "react -redux";
    import { toggleTodo } from "../actions";
    import TodoList from "../components/TodoList";
    
    const mapStateToProps = (state)=>({
        todos: state.todos
    })
    const mapDispatchToProps = (dispatch)=>({
        toggleTodo: id => dispatch(toggleTodo(id))
    })
    
    export default connect(
        mapStateToProps,  //redux中的state映射到react组件当中的props上
        mapDispatchToProps  //react组件须要使用的action方法,映射到组件的props当中
    )(TodoList);
    
    connect()属于高阶组件,将完成react层和redux层的逻辑链接
    
例子:在src下的components文件夹的App.js文件下 引入containers里的容器组件

    import { TodoListContainer } from "../containers/TodoListContainer";    
    import …… ……
    
    return (
        <div>
            <TodoListContainer/>
            …… ……
        </div>
    )
    
例子:在src下的index.js 入口文件

    import { createStore } from "redux";
    import { Provider } from "react-redux";
    import rootReducer from "./reducers";
    
    const store = createStore(rootReducer);
    
    <Provider store={store}>
        <App/>
    </Provider>
复制代码

13. 异步Action 有时action里会经过调用api拿到一些数据,而后再dispatch(),这样就存在异步,数据会有延迟

例子:

    const getTodosSuccess = (data)=>({
        type: "TODOS_SUCCESS",
        data
    })
    
    const getTodosFailure = (error)=>({
        type: "TODOS_FAILURE",
        error
    })
    
    export const getTodos = ()=> dispatch =>{
        return new Promise((resolve,reject) => {
            axios.get({ url: 'xxxxxxxxx'}).then(res=>{
                dispatch(getTodosSuccess(res))
                resolve(res)
            }).catch(err=>{
                dispatch(getTodosFailure(err))
                reject(err)
            })
        })
    }
    
    getTodos()返回值是一个函数,这个函数咱们是没法处理的,因此要借助redux的中间件来完成异步action这个操做
    最经常使用的是 npm install redux-thunk
复制代码

14. 异步Action 要用中间件解决 redux-thunk

例子: 在src目录的 index.js下

    import thunkMiddleware from "redux-thunk";
    import { createStore, applyMiddleware } from "redux";
     
    const store = createStore(rootReducer, applyMiddleware(thunkMiddleware))
     
    这样建立出来的store 就能处理异步action了
复制代码

15. 谷歌浏览器安装redux调试工具

在github上寻找 redux-devtools-extension

    找到 Advanced store setup
    if you setup your store with middleware and enhancers,change:
    xxxxxxxxxxxxxxxxxxxxxxxxxxx 这里的东西能够用,复制下来
    
在src目录的 index.js下

    import { compose } from "redux";  //compose这个函数能够帮助咱们将多个enhancers组合到一块儿,compose是个加强器能够增长store的功能

    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION__COMPOSE__ || compose;
    const store = createStore(
        rootReducer, 
        composeEnhancers(applyMiddleware(thunkMiddleware))
    );
复制代码

16. 项目结构组件方式

按照类型:
    action/
        -- action1.js   action2.js
    components/
        -- component1.js   component2.js
    reducers/
        -- reducer1.js   reducer2.js  index.js(全部reducer组合成一个)
    containers/  (容器型组件)
        -- containser1.js  containser2.js
        
按照功能模块:(存在不一样模块之间状态互相耦合的场景)
    feature1/
        -- components/
        -- Container.js
        -- actions.js
        -- reducer.js
    feature2/
        -- components/
        -- Container.js
        -- actions.js
        -- reducer.js
    
Ducks: …………
复制代码

17. Middleware(中间件) 加强store dispatch的能力

例子:redux-thunk  能够处理函数类型的action了

    View -> ( mid1 -> mid2 -> …… …… ) -> reducer  //页面触发action 会逐步通过这些中间件,加强dispatch,dispatch所作的事就是派发action
    Function:({getState, dispatch}) => next => action
    
在src文件夹下 新建 Middleware文件夹->logger.js
    
    // 打印action, state 的logger中间件
    const logger = ({getState, dispatch}) => next => action => {
        console.group(action.type);
        console.log("dispatching:", action);
        
        const result = next(action);
        
        console.log("next state:", getState()); 
        consoel.groupEnd();
        
        return result;
    }
    export default logger;
    
在index.js文件下 ,把本身写的中间件加进去
    
    import loggerMiddleware from "./middleware/logger";
    const store  = createStore(
        …… ……
        applyMiddleware(…… ……,loggerMiddleware)
    )
复制代码

18. store enhancer 加强redux store的功能

store enhancer 通常结构:

    function enhancerCreator(){
        return createStore => (...arg) => {
            // do something based old store
            // return a new enhanced store
        }
    }
    
在src文件夹下 新建enhancer文件夹 -> logger.js
    
    // 打印action, state 的加强器
    const logger = createStore => (...arg) => {
        const store = createStore(...arg);
        const dispatch = (action) => {
            console.group(action.type);
            console.log("dispatching:", action);
            
            const result = store.dispatch(action);
            
            console.log("next state:", store.getState()); 
            consoel.groupEnd();
            
            return result;
        }
        return {...store, dispatch}  // 把新的dispatch方法覆盖到原来对象的dispatch
    }
    export default logger;
    
在index.js文件下 ,把本身写的enhancer加进去

    import loggerEnhancer from "./enhancer/logger";
    const store  = createStore(
        …… ……
        compose(applyMiddleware(thunkMiddleware), loggerEnhancer)
    )
    
    compose是能够把多个 store enhancer 进行组合的
    在平时中应该尽可能多使用 Middleware 来加强dispatch的能力, 慎用 store enhancer 记住!
复制代码

18. Immutable.js 能够用来建立不可变的对象state 每次均可以返回个新的

npm install immutable
npm install redux-immutable

import Immutable from "immutable";
import { combineReducers } from "redux-immutable"; 

const initState = {
    …… ……,
    isFetching: false,
    todos: {
        ……,
        data: []
    }
}

//这样就建立了一个不可变的 immutable 对象
const state = Immutable.fromJS(initState) 

//设置属性,一次只能设置一个key
state.set("isFetching", true);  

//merge能够一下设置多个
state.merge({
    isFetching: false,
    todos: Immutable.fromJS(action.data)  // 数据类型必定要统一用immutable的类型
});

//get获得属性的值
state.get("isFetching");

//getIn 从外层到内层逐层获取
state.getIn(["todos","data"]);

//把immutable对象转化成普通JS对象
state.toJS();
由于在 mapStateToProps,…… 的对象要普通JS对象才能使用

在reducers文件下的 index.js中

    import { combineReducers } from "redux-immutable";  // redux中的combineReducers只能识别普通JS对象
复制代码

19. Reselect库常常和redux一块儿使用, Reselect减小state的重复计算

npm install reselect

import { createSelector } from "reselect"

const getFilter = (state) => state.filter;
const getTodos = (state) => state.todos.data;

export const getVisibleTodos = createSelector(
    [getTodos,getFilter],      //数组里面是其余所依赖的select函数
    (todos, filter) => {      //前2个函数返回的数据结果,可用来计算
        console.log("-----");  //用来测试看有没多余的重复执行
        switch(filter){
            case: "xxx":
                return
            case ……:
                ……
            default:
                ……
        }
    }
);

只有当select函数确实很是复杂而且整个redux性能有很大的问题才去考虑用reselect,并非必须的
复制代码

20. Router相关库

  • react-router
  • react-router-dom
  • react-router-native
  • 建议在项目之中使用<BrowserRouter>组件
react-router这个库实现了路由的核心功能,react-router-dom是在react-router之上的一层封装,将react-router的路由功能和web的API进行了绑定

<Router>经常使用组件: <BrowserRouter>
    1 . Html5 history API (pushState, repalceState等)
    2. 须要Web服务器额外配置
    localhost:3000/home     localhost:3000/about
    
<Router>经常使用组件: <HashRouter>
    1. 使用url的hash部分做为路由的信息
    2. 主要为了兼容老版本浏览器
    localhost:3000/#/home     localhost:3000/#/about
复制代码

21. 路由配置:Route

<BrowserRouter>
    <Route path="/" exact component={Home}/>
    <Route path="/about" component={About}/>
    <Route path="/user/:aaa" component={User}/>
    …… ……
<BrowserRouter/>

组件中获得传过来的参数: this.props.match.params.aaa
复制代码

22. 路由匹配

<Route exact />  exact : url和path彻底相同的状况下才会匹配成功

import { Switch } from "react-router-dom";
<Switch>
    <Route path="/about" component={About}/>
    <Route path="/user/:aaa" component={User}/>
    <Route path="/" component={Home}/>    // 当前面都匹配不成功时,根路径才会被匹配
<Switch/>

Swicth 的功能是会去匹配第一个,被匹配到的<Route />,当第一个<Route/>匹配到的时候,其余的<Rpute>都不会被匹配
复制代码

23. 路由渲染组件的方式

  • <Route component />
  • <Rute render />
  • <Route children />
<Route path="/about" render={
    (props) => <About {...props}/>   //props是一个对象,包含了match,location,history
} />

在App.js:
    <Router>
        <Route path="/user" component={User}>
    </Router>
在User.js:
    <div>
        <Route path={`${this.props.match.path}/:user`} component={UserDetail}>
    </div>
在UserDetail.js:
    <div>
        this.props.match.params.user
    </div>
复制代码

24. create-rwact-app 建立出来的默认的入口文件

// If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers: https://bit.ly/CRA-PWA
    serviceWorker.unregister();
    
   serviceWorker  离线存储,加载优化的事情, 作项目时没使用到的话要把这句话删掉
复制代码

25. store.js 文件中的 process.env.NODE_ENV 环境变量 && 判断浏览器有没装redux调试工具

判断当年程序的执行是开发环境仍是生产环境 && 若是浏览器安装redux的调试工具 window对象下会有redux的调试方法添加进去
    
    if (
        process.env.NODE_ENV !== "production" &&
        window.__REDUX_DEVTOOLS_EXTENSION__
    ){
        const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;  // 做为store的加强器
        store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));  // 将REDUX_DEVTOOLS的功能加强到store当中
    } else {
        store = createStore(rootReducer, applyMiddleware(thunk));
    }
    
复制代码

26. fetch()或者 axios 请求url

axios 优缺点:
    - 从 node.js 建立 http 请求。
    - 支持 Promise API。
    - 提供了一些并发请求的接口(重要,方便了不少的操做)。
    - 在浏览器中建立 XMLHttpRequests。
    - 在 node.js 则建立 http 请求。(自动性强)
    - 支持 Promise API。
    - 支持拦截请求和响应。
    - 转换请求和响应数据。
    - 取消请求。
    - 自动转换 JSON 数据。
    - 客户端支持防止CSRF。
    - 客户端支持防护 XSRF。
    
Fetch 连接 https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch (MDN)

axios既提供了并发的封装,也没有fetch的各类问题,并且体积也较小,当之无愧如今最应该选用的请求的方式。
复制代码

27. 中间件的业务流程

文件 ./entities/products.js
    export const schema = {
        name: "products",
        id: "id"
    }
    …… …… ……
    
*****************************************************

文件 ./redux/home.js
    import FETCH_DATA from "../middleware/api"
    import schema from "./entities/products"
    
    export const types = {
        FETCH_LIKES_REQUEST:"FETCH_LIKES_REQUEST",
        FETCH_LIKES_SUCCESS:"FETCH_LIKES_SUCCESS",
        FETCH_LIKES_FAILURE:"FETCH_LIKES_FAILURE"
    }
    
    export const actions = {
        loadLikes: ()=>{
            return (dispatch, getState) => {
                const endpoint = url.getProductList(0,10);   // utils中的方法返回一个url路径
                return dispatch(fetchLikes(endpoint));
            }
        }
    }
    
    const fetchLikes = (endpoint)=>({
        [FETCH_DATA]: {                         
            types:[
                types.FETCH_LIKES_REQUEST,
                types.FETCH_LIKES_SUCCESS,
                types.FETCH_LIKES_FAILURE
            ],
            endpoint,
            schema
        },
    });
    …… …… ……
    
*****************************************************

文件 middleware/api.js
    export const FETCH_DATA = "FETCH_DATA";
    
    export default store => next => action => {
        const callAPI = action[FETCH_DATA];
    
        if(typeof callAPI === "undefined"){
            return next(action);
        }

        const { endpoint, schema, types } = callAPI
    
        if(typeof endpoint !== "string"){
            throw new Error("endpoint要为字符串类型的url");
        }
        if(!schema){
            throw new Error("必须指定领域实体的schema");
        }
        if(!Array.isArray(types) && types.length !== 3){
            throw new Error("须要指定一个包含了3个action type的数组");
        }
        if(!types.every(type => typeof type === "string")){
            throw new Error("action types 必须为字符串类型");
        }
    
        // 里面有新东西,加强dispatch()
        const actionWith = (data) =>{
            const finalAction = {...action, ...data};
            delete finalAction[FETCH_DATA];
    
            return finalAction 
        }

        const [requestType, successType, failureType] = types;
    
        next(actionWith({type: requestType}));
    
        return fetchData(endpoint, schema).then(
            response => next(actionWith({
                type: successType,
                response
            })),
            error => next(actionWith({
                type: failureType,
                error: error.message || "获取数据失败"
            })) 
        )
    };
    
    // 执行网络请求
    const fetchData = (endpoint, schema) => {
        return get(endpoint).then(data => {
            return normalizeDate(data,schema)
        })
    }
    
    // 根据schema,将获取的数据扁平化处理
    const normalizeDate = (data,schema) => {
        const { id,name } = schema;
        let kvObj = {};
        let ids = [];
    
        if(Array.isArray(data)){
            data.forEach(item => {
                kvObj[item[id]] = item;
                ids.push(item[id]);
            })
        }else{
            kvObj[data[id]] = data;
            ids.push(data[id]);
        }
    
        return { 
            [name]: kvObj,
            ids
        }
    }
复制代码

28. 对于一些从redux取得的数据加载,判断下是否能够避免重复请求

// 对于一些变化频率不高的数据不须要重复请求,好比首页进详情页,详情页又回到首页的某些数据,能够直接拿redux里的缓存
this.removeListener = false;
componentDidMount(){
  // 凡是要加载数据都要多想一想什么状况要加载,何时能够避免运算一些逻辑
  if(this.props.pageCount < 3){
    document.addEventListener("scroll", this.handleScroll);
  }else{
    this.removeListener = true;
  }
  // 不是每次都要加载数据,经过一些判断
  if(this.props.pageCount == 0){
    this.props.fetchData();
  }
}

componentDidUpdate(){
  if(this.props.pageCount >= 3 && !this.removeListener){
    document.removeEventListener("scroll", this.handleScroll);
    this.removeListener = true; 
  }
}

componentWillUnmount(){
  if(!this.removeListener){
    document.removeEventListener("scroll", this.handleScroll);
  }
}
复制代码

29. 删除对象里的某个键值对 ,key:value 解构出来的对象,须要删掉某个key的键值对

const finalAction = {...action, ...data}
delete finalAction[FETCH_DATA]
return finalAction

var a = {"a":"a","b":"b","c":"c"}
delete a.a
a = {b: "b", c: "c"};
复制代码

30. React.createRef()

Refs 提供了一个得到DOM节点或者建立在render方法中的React元素的方法前端

constructor(props) {
    super(props);
    this.myRef = React.createRef();
}
render() {
    return <div ref={this.myRef} />;
}

访问Refs:
当一个ref经过render放入一个元素中,一个对节点的引用能够经过ref的current属性获得;
const node = this.myRef.current;
好比:
this.myRef.current.offsetHeight  --- 就能获得DOM节点的高度
复制代码

31. 解构出来的对象,须要删掉某个key的键值对

const actionWith = data => {
    const finalAction = {...action, ...data}
    delete finalAction[FETCH_DATA]
    return finalAction
}

var a = {"sdf":111,"d":"sdfsdfsdf","23":11111}
delete a["d"]
a = {23: 11111, sdf: 111}
复制代码

32.this.props.history 返回上一页

<Router>
    <Switch>
        <Route />
        ………… ……
    </Switch>
</Router>

handleBack = () => {
    this.props.history.goBack();
}
须要在路由包裹里面才能生效
复制代码

32.push | Link 跳到某个页面

this.props.history.push("/");
this.props.history.push("/search");

<Link to="/search" className="">xxxxx</Link>
<Route path="/search" component={Search}/>
复制代码

33. 标签要加alt属性,否则会报黄色警告

<img alt="" />
复制代码

34. 异步actions 中的方法

const rootReducer = combineReducers({
    entities,
    login,
    …… …… ……… ……
}

const initialState = {
    username: localStorage.getItem("username") || "",
    password: ''
}
const login = (state=initialState, action) => {
    switch(){
        case:
        …… …… ……
    }
}
export default login;

login: () => {
    return (dispatch, getState) => {
        const {username, password} = getState().login;
        dispatch({type:…… ……});
    }
}

getState() 获得reducer中的全部数据,login是combineReducers其中一个,这又和上层Login组件有着联系
connect(
    mapStateToProps,
    mapDispatchToProps
)(Login);
复制代码

35. 和上面连起来一块儿使用

import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { actions as loginActions, getUsername, ……  } from …… 

// selectors
export const getUsername = (state) => state.login.username;
// action creators
export const actions = {
    login:() => …… ……
}

const mapStateToProps = (state, props) => {
    return {
        username: getUsername(state),   // 这里的state是数据,因此并不用state()获得,直接传递便可
        …… …… ……
    }
}
const mapDispatchToProps = (dispatch, props) => {
    return {
        loginActions: bindActionCreators(loginActions, dispatch)
    }
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Login);
复制代码

36. bind(this, 参数)

handleClick = (item) => {
    this.props.onClickItem(item);
}
    
<span key={item.id} 
    className="popularSearch__item"
    onClick={this.handleClick.bind(this, item)}
>{item.keyword}</span>

onClick事件要往其父级传参
复制代码

37. location这个属性是react-router注入的

const { location: {state} } = this.props;
if(state && state.from){
    return <Redirect to={state.from}/>
}
复制代码

38. 有时候route须要一些判断

<Router>
    <Switch>
        <Route exact path="/" component={Home}/>
        …… …… ……
        <PrivateRoute path="/user" component={User}/>
        <PrivateRoute path="/purchase/:id" component={Purchase}/>
    </Switch>
</Router>
                
class PrivateRoute extends React.Component{
    render(){
        const { component: Component, login, ...rest } = this.props;
        return (
            <Route 
                {...rest}
                render={(props) => {
                    return login ? (<Component {...props}/>) : (<Redirect to={{
                        pathname: "/login",
                        state: {from: props.location}
                    }}/>)
                }}
            />
        )
    }
}
…… …… ……
…… …… ……
复制代码

39. this.props 取值

const {
        data: { title, statusText, orderPicUrl, channel, text, type, commentId }, isCommenting
} = this.props;
复制代码

40. +new Date() 与 new Date()的区别

typeof new Date()  "object"
typeof +new Date()  "number"

new Date()
Mon Oct 14 2019 09:32:06 GMT+0800 (中国标准时间)

+new Date()
1571016720402
复制代码

41. mapDispatchToProps也能够从别处引来action, mapStateToProps中的state,props用法

const mapStateToProps = (state, props) => {
    const productId = props.match.params.id;
    return {
        product: getProduct(state, productId),
        …… ……
    }
};
const mapDispatchToProps = (dispatch) => {
    return {
        purchaseActions: bindActionCreators(purchaseActions, dispatch),
        detailActions: bindActionCreators(detailActions, dispatch)
    }
}
复制代码

42. asyncComponent 按需加载 dynamic import()实现动态加载模块功能的

项目跑起来后,network里会加载个 bundle.js 这个js文件涵盖了项目中全部js资源和全部js代码,所以这个bundle.js会比较大在项目里不点点击切换页面路由,可是network中并无加载新的js代码,依然只有bundle.js,显然对性能是有影响的, 咱们要作的就是,访问不一样页面加载不一样页面的JS代码node

import("../Login") 传入组件的路径,这里的import并非一个函数而是一个运算符,返回结果是一个promise对象,
当组件加载完成以后,promise状态会变成完成,那么咱们就能够正常使用

咱们须要定义一个高阶组件 function(){ import("../Login") }, 调用的时候会执行里面的代码
复制代码
const User = AsyncComponent(() => import("../User"));
const Purchase = AsyncComponent(() => import("../Purchase"));

<Router>
    <Switch>
        <PrivateRoute path="/user" component={User} />
        <PrivateRoute path="/purchase/:id" component={Purchase}/>
        …… …… ……
    </Switch>
</Router>

export default function asyncComponent(importComponent){
    class AsyncComponent extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                component: null
            }
        }

        componentDidMount(){
            importComponent().then((mod) => {
                this.setState({
                    component: mod.default
                })
            })
        }

        render(){
            const C = this.state.component;
            return C ? <C {...this.props}/> : null
        }
    }

    return AsyncComponent;
}

改为这样以后,在network里面能够看到 bundle.js 这是项目的骨架代码,1.chunk.js这是首页的js代码,
等下每点去另外一个页面都会加载一个不一样页面的 chunk.js

npm run build 以后
也能够在控制台看到代码被编译以后是分片的…… 0,1,2,3……chunk.js,骨架代码是 main.js
复制代码

43. "reselect": "^4.0.0" 的用法

  • 在 reducer 中,都是返回一个新对象,不然组件是不会更新 ui 的。而返回新对象,能够:
  1. Object.assign:比较 hack,每次新生成一个对象
  2. ...state
  3. immutable.js
  • 主要用来缓存衍生数据的:就是从 store 中的 A、B 要衍生出 C,就可使用 reselect 来优化性能,避免重复计算。
<div>
   <Count />  // 好比设置 setInterval定时器1000ms 执行一次Count 
   <Posts />
</div>

这样本没有什么问题,可是,一旦 Posts 的 mapStateToProps 是须要产生衍生数据的,即便 Posts 的 reducer 没有变化,
也仍是要随着 Count 每 1000ms 计算,浪费性能
Count 每 1000ms 执行一次自加,不光 Count 的 reducer 会过一遍 mapStateToProps,
Posts 的 reducer 也会过一遍。这是必然的,由于 connect 就是这样设计的。

const mapStateToProps = state => {
  const postsById = state.postsById
  const usersById = state.usersById
  const postListing = state.postListing

  return {
    posts: postListing.map(id => {
      const post = postsById[id];
      return {...post, user: usersById[post.author]}
    })
  }
}


使用 reselect 优化:
这样子,就能够作到:只有 postsById、usersById、postListing 其中之一变化时才会从新计算 posts
const getListing = createSelector(
  state => state.postsById,
  state => state.usersById,
  state => state.postListing,
  (posts, users, listing) => listing.map(id => {
    const post = posts[id];
    return {...post, user: users[post.author]}
  })
);

const mapStateToProps = (state) => {
  return {posts: getListing(state)};
};
复制代码

结语

前端react QQ群:788023830 ---- React/Redux - 地下老英雄

前端交流 QQ群:249620372 ---- FRONT-END-JS前端

(咱们的宗旨是,为了加班,为了秃顶……,仰望大佬),但愿小伙伴们加群一块儿学习react

相关文章
相关标签/搜索