create-react-app 脚手架构建项目,搬砖过程中持续总结心得
生活随笔
收集整理的這篇文章主要介紹了
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_TEXTimport {…… ……} from ""../actions/actionTypes; const text = (state = "", action)=>{switch (action.type){case SET_TODO_TEXT:return action.textdefault:return state} } export default text分解完上面那個大reducer之后,把reducer文件夾中的index里面的大reducer刪掉,逐步引入拆分的各個小的reducerindex.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
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 setupif 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.jscomponents/-- component1.js component2.jsreducers/-- reducer1.js reducer2.js index.js(所有reducer組合成一個)containers/ (容器型組件)-- containser1.js containser2.js按照功能模塊:(存在不同模塊之間狀態互相耦合的場景)feature1/-- components/-- Container.js-- actions.js-- reducer.jsfeature2/-- components/-- Container.js-- actions.js-- reducer.jsDucks: ………… 復制代碼17. Middleware(中間件) 增強store dispatch的能力
例子:redux-thunk 可以處理函數類型的action了View -> ( mid1 -> mid2 -> …… …… ) -> reducer //頁面觸發action 會逐步經過這些中間件,增強dispatch,dispatch所做的事就是派發actionFunction:({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-immutableimport 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 reselectimport { 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":returncase ……:……default:……}} );只有當select函數確實非常復雜并且整個redux性能有很大的問題才去考慮用reselect,并不是必須的 復制代碼20. Router相關庫
- react-router
- react-router-dom
- react-router-native
- 建議在項目之中使用<BrowserRouter>組件
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 />
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-PWAserviceWorker.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.jsexport const schema = {name: "products",id: "id"}…… …… ……*****************************************************文件 ./redux/home.jsimport 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.jsexport 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 } = callAPIif(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}} 復制代碼結語
前端react QQ群:788023830 ---- React/Redux - 地下老英雄
前端交流 QQ群:249620372 ---- FRONT-END-JS前端
(我們的宗旨是,為了加班,為了禿頂……,仰望大佬),希望小伙伴們加群一起學習
轉載于:https://juejin.im/post/5cc96be351882540b244d601
總結
以上是生活随笔為你收集整理的create-react-app 脚手架构建项目,搬砖过程中持续总结心得的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Class
- 下一篇: 94. Binary Tree Inor