初入項目,JS可能遇到的問題優化以及處理方法

<meta name="source" content="lake">

簡介: 在剛進入項目時候,很可能遇到一些問題,我這裏分成了三個模塊,第一個模塊是ES6的一些方法的妙用,整理了一些可能會被人忽略的函數以及一些參數的使用;第二個模塊是有關if else的一些技巧,說來也是剛到阿里園區收到一位老哥影響,做了一些學習,對一些使用做了下整理;最後一個模塊是有關react hook的入門,對於hook來說,最常用的一些地方就是組件傳值以及組件傳方法,在這裏我對這部分所涉及的各種情況做了一些整理,希望對大家有所幫助,當然我也是能力有限,一些筆誤或者使用上不夠規範的歡迎留言或私我討論。

ES6篇

1.Array.from()

爲什麼先提到Array.from(),在開發中發現數組纔是最常見的一種格式,不僅是在渲染列表,表格,還是在數據請求上,都有着重要的意義。

首先,先看看Array.from()定義,它可以將一個類數組或可遍歷對象轉成一個真實的數組。所謂類數組,就是我們在非箭頭函數中所屬的arguments類型等等;可遍歷對象,便如Map類型等等。

使用規則爲

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">Array.from(arrayLike, mapFn, thisArg) /* arrayLike: 必選,1、類數組(argumentg)2、可迭代對象(set,map)。 mapFn: 可選,相當於Array.from(arrayLike).map(mapFn, thisArg)。 thisArg: 可選,執行回調函數mapFn時候的this對象。非常有用,利於解耦。可以把被處理的數據和對象分離,thisArg中定義處理函數handle,用來在mapFn中返回調用handle之後的結果。 */</pre>

功能上他能實現什麼呢?

1.將string類型轉爲數組

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const str = 'wangjingyu'; const arr = Array.from(str); console.log(arr); // (10) ["w", "a", "n", "g", "j", "i", "n", "g", "y", "u"]</pre>

2.將Set類型轉爲數組

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const set = new Set([1,2,3]); const arr = Array.from(set); console.log(arr); // (3) [1, 2, 3]</pre>

3.將Map類型轉爲數組

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const map = new Map([[1,2,3],[4,5,6]]); const arr = Array.from(map); console.log(arr); // [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]</pre>

4.將類數組arguments轉爲數組

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const fun = function(a,b,c){ const args = Array.from(arguments); console.log(args) } fun(1,2,3) // [ 1, 2, 3 ]</pre>

5.使用第二個參數構造函數返回新數組

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const newArr = Array.from([1,2,3], value => value * value); console.log(newArr); // [ 1, 4, 9 ] const lenArr = Array.from({length: 10}, (v, i) => i + 10); console.log(lenArr); // [10, 11, 12, 13, 14,15, 16, 17, 18, 19]</pre>

6.數組去重合並

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const arr_a = [1,2,3]; const arr_b = [2,3,4]; const arr_c = [3,4,5]; const combine = function(a,b,c){ const arrAll = [].concat.apply([],arguments); console.log(Array.from(new Set(arrAll))) } combine(arr_a,arr_b,arr_c); // [ 1, 2, 3, 4, 5 ]</pre>

7.獲取數組對象中的某指定屬性的所有值

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const cities = [ { name: 'Paris', visited: 'no' }, { name: 'Lyon', visited: 'no' }, { name: 'Marseille', visited: 'yes' }, { name: 'Rome', visited: 'yes' }, { name: 'Milan', visited: 'no' }, { name: 'Palermo', visited: 'yes' }, { name: 'Genoa', visited: 'yes' }, { name: 'Berlin', visited: 'no' }, { name: 'Hamburg', visited: 'yes' }, { name: 'New York', visited: 'yes' } ]; console.log(Array.from(cities, ({name}) => name)) // (10) ["Paris", "Lyon", "Marseille", "Rome", "Milan", "Palermo", "Genoa", "Berlin", "Hamburg", "New York"]</pre>

8.採用第三個參數進行數據和對象抽離

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const arr = [1,2,3,4,5]; const obj = { double: x => x * 2, triple: x => x * 3, sqrt: x => x * x, } console.log(Array.from(arr, function(x){ return this.triple(x) }, obj)) // [ 3, 6, 9, 12, 15 ]</pre>

通過Array.from()方法,我們可以使用更少的代碼完成我們需要做到的功能。

2.解構原始數據

我們大多數在使用對象時候,會將一個大對象分出小的屬性,屬性值來使用,其中,我們可能會覺得某一個對象裏面的屬性值太多,而我們只需要其中的若干個,這裏我們就可以使用這種方法來做解構。

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const rawUser = { name: 'WangJingyu', surname: 'Jack', email: '[email protected]', displayName: 'WhmJack', joined: '2021-03-04', image: 'github', followers: 1000 } let user = {}, userDetails = {}; ({name:user.name,surname:user.surname,...userDetails} = rawUser) console.log(user,userDetails) // { name: 'WangJingyu', surname: 'Jack' } // { // email: '[email protected]', // displayName: 'WhmJack', // joined: '2021-03-04', // image: 'github', // followers: 1000 // }</pre>

這樣我們就可以把自己需要的某幾個對象屬性取出來來使用了。

3.動態屬性名

在ES5時代,我們獲取屬性一般有兩種方式,要不是直接.xxx,要不就是定義變量名去通過[]獲取:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const person = { name : 'wjy', age : 23, } const attName = 'name'; console.log(person[attName],person.name) // wjy wjy</pre>

我們可以把attName作爲變量名,但是隻能在對象之外對他做賦值操作,並不能做到讓person對象的屬性動態變化。可能有的人要說,那我把那麼當成一個變量就好了,就比如:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const attName = 'name'; const person = { attName : 'wjy', age : 23, } console.log(person[attName],person.attName) // undefined wjy</pre>

好像不太行的亞子,我們來看看原因,person.attName獲取的屬性就是字符串attName,所以可以獲取到,而person[attName]獲取的則是person.name,但是我們的attName在person對象裏只是一個字符串,並不是一個變量,所以獲取不到,那麼怎麼辦呢?

ES6中提出了把屬性名用[]包起來的方法,在括號中就可以使用前面定義的變量了。就是:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const attName = 'sex'; const person = { name : 'wjy', [attName] : 'male', age : 23, } console.log(person[attName]) // male</pre>

這樣我們就可以動態設定對象的屬性名了。

4.symbol的對象使用

作爲ES6新提出的變量類型,雖然symbol屬性已經出來很久了,但是很多時候我們還是很少使用它,這裏提到一種小小的用法,僅僅作爲拋磚,更多的方法等着各位dalao來補充擴展。

對於一個已經存在的對象,裏面可能存在一個屬性key,名字暫且爲'id',在多人開發時候可能會有其他人也對此對象做了內部屬性命名爲id的操作,或者說他繼承了這個對象,並且對這個對象做了重寫,那麼這樣會導致這個對象的id被覆蓋。從而出現一些問題。這個時候就可以讓symbol大顯身手了。

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const custId = Symbol.for('id'); const obj = { [custId] : '0100110100', 'id' : '23322232', 'iq' : '150', }; console.log(obj); // { id: '23322232', iq: '150', [Symbol(id)]: '0100110100' }</pre>

那麼問題來了,我們怎麼把他取出來呢,如果採用常規的for循環,結果是這樣的:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">for(let [key,value] of Object.entries(obj)){ console.log('let of',key,value) } // let of id 23322232 // let of iq 150</pre>

確實不太行,我們可以用symbol的api去獲取:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">Object.getOwnPropertySymbols(obj).forEach((item) => { console.log(obj[item]) }) // 0100110100</pre>

確實是可以的,但是如果我們想for循環整體obj呢,這時候可以使用反射Reflect的相關api:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">Reflect.ownKeys(obj).forEach(item => { console.log(obj[item]) }) // 23322232 // 150 // 0100110100</pre>

這樣我們就可以把值取出來了,並且達到了我們的效果。

分支優化篇

這一塊主要是針對if else做的一個優化方法策略的整理總結,在做項目中,難免會出現越來越多的判斷情況,而我們也需要根據這些判斷數值來做選擇,兩個三個分支選擇還好,如果選擇多了起來,那麼我們可能就會出現這樣的情況:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const ifOperation = (status) => { if(status === 1){ consoleLog('one'); consoleRun('oneEvent'); } else if(status === 2){ consoleLog('two'); consoleRun('TwoEvent'); } else if(status === 222){ consoleLog('two'); consoleRun('TwoEvent'); } else if(status === 3){ consoleLog('three'); consoleRun('ThreeEvent'); } else if(status === 4){ consoleLog('four'); consoleRun('FourEvent'); } else { consoleLog('other'); consoleRun('OtherEvent'); } }</pre>

雖然只有六個分支,但是已經看起來臃腫不堪了,可能我們可以把某一個else if變得厚重一下?

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const ifOperation = (status) => { if(status === 1){ consoleLog('one'); consoleRun('oneEvent'); } else if(status === 2 || status === 222){ consoleLog('two'); consoleRun('TwoEvent'); } else if(status === 3){ consoleLog('three'); consoleRun('ThreeEvent'); } else if(status === 4){ consoleLog('four'); consoleRun('FourEvent'); } else { consoleLog('other'); consoleRun('OtherEvent'); } }</pre>

看起來可能好一些,不過更多的人應該會選擇switch case,那麼它就會變成:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const switchOperation = (status) => { switch(status){ case 1: consoleLog('one'); consoleRun('oneEvent'); break; case 2: case 222: consoleLog('two'); consoleRun('TwoEvent'); break; case 3: conosleLog('three'); consoleRun('ThreeEvent'); break; case 4: consoleLog('four'); consoleRun('FourEvent'); break; default: consoleLog('other'); consoleRun('OtherEvent'); break; } }</pre>

在工作中其實這種已經是我們的常態了,不過我們可以更進一步,藉助其他數據類型幫助我們簡化代碼,比如用個對象存儲if else的各種情況:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const obj = { 1 : ['one','oneEvent'], 2 : ['two','TwoEvent'], 3 : ['three','ThreeEvent'], 4 : ['four','FourEvent'], 222 : ['two','TwoEvent'], 'default' : ['other', 'OtherEvent'] } const consoleLog = (status) => { console.log(status); } const consoleRun = (status) => { console.log(status); } const objOperation = (status) => { let operation = obj[status] || obj['default']; consoleLog(operation[0]); consoleRun(operation[1]) } objOperation('222')</pre>

這樣就清爽很多了,比如我在做播放錄音點擊修改倍速時候可以寫的更加精簡,比如:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">// 設置點擊修改倍速條件 const obj = { 1: [1.5], 1.5: [2], 2: [0.5], 0.5: [1], }; const objOperation = (status) => { const operation = obj[status]; const speedChoose = operation[0]; setSpeed(speedChoose); }; objOperation(speed);</pre>

當然,如果你不想用Object表示,你還可以用Map來表示呀:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const map = new Map([ [1 , ['one','oneEvent']], [2 , ['two','TwoEvent']], [3 , ['three','ThreeEvent']], [4 , ['four','FourEvent']], [222 , ['two','TwoEvent']], ['default' , ['other', 'OtherEvent']] ]) const consoleLog = (status) => { console.log(status); } const consoleRun = (status) => { console.log(status); } const mapOperation = (status) => { let operation = map.get(status) || map.get('default'); consoleLog(operation[0]); consoleRun(operation[1]) } mapOperation(222)</pre>

不過,在Object對象和Map對象都能使用的情況下,我們優先選擇哪種呢?

1.Object一般是有自己的原型的,所以每個對象都是有一個prototype鍵

2.Object對象的鍵一般可以是數字類型,字符串類型以及symbol類型,但是一個Map的鍵可以是任意類型,使用起來更爲方便

3.對於獲取鍵值對個數之類,Map可以直接用map.size獲取,而Object則需要先用Object.key()獲取所有的鍵數組,然後再去獲取length

現在我們可能需要做更多的事情,比如我們現在有兩層判斷了,不僅僅需要判斷條件a,還要判斷條件b,比如:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">/* * param {number} status 表示狀態 * param {string} roommate 表示舍友名稱 */ const ifOperation = (status,roommate) => { if(roommate === 'ly'){ if(status === 1){ consoleLog('lyone'); } else if(status === 2){ consoleLog('lytwo'); } else if(status === 3){ consoleLog('lythree'); } else if(status === 4){ consoleLog('lyfour'); } else { consoleLog('sbother'); } } else if(roommate === 'wjy'){ if(status === 1){ consoleLog('wjyone'); } else if(status === 2){ consoleLog('wjytwo'); } else if(status === 3){ consoleLog('wjythree'); } else if(status === 4){ consoleLog('wjyfour'); } else { consoleLog('sbother'); } } else { consoleLog('sbother'); } }</pre>

這。。。看起來也太長了,如果說單層判斷還可以接受,那麼這種長度的判斷,看起來確實有點難受,不過這種情況也很普遍,可能我遇到了這樣的問題,也會首先採用這種模式去進行代碼的編寫。但是我們要知道,一旦判斷層級多了一級,那麼我們增加的條件就是2(n)倍,這時候要怎麼去寫呢?我們仍然可以採用map或者object去處理:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const map = new Map([ ['ly_1', () => {consoleLog('lyone');consoleRun('ly_1')}], ['ly_2', () => {consoleLog('lytwo');consoleRun('ly_2')}], ['ly_3', () => {consoleLog('lythree');consoleRun('ly_3')}], ['ly_4', () => {consoleLog('lyfour');consoleRun('ly_4')}], ['wjy_1', () => {consoleLog('wjyone');consoleRun('wjy_1')}], ['wjy_2', () => {consoleLog('wjytwo');consoleRun('wjy_2')}], ['wjy_3', () => {consoleLog('wjythree');consoleRun('wjy_3')}], ['wjy_4', () => {consoleLog('wjyfour');consoleRun('wjy_4')}], ['default', () => {consoleLog('sbother');consoleRun('other roommate')}], ]) const mapOperation = (status,roommate)=>{ let mapAction = map.get(${roommate}_${status}) || map.get('default') mapAction.call(this) } mapOperation(1,'wjy') // wjyone // wjy_1</pre>

用Object來寫也是一樣,如下:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const obj = { 'ly_1': () => {consoleLog('lyone');consoleRun('ly_1')}, 'ly_2': () => {consoleLog('lytwo');consoleRun('ly_2')}, 'ly_3': () => {consoleLog('lythree');consoleRun('ly_3')}, 'ly_4': () => {consoleLog('lyfour');consoleRun('ly_4')}, 'wjy_1': () => {consoleLog('wjyone');consoleRun('wjy_1')}, 'wjy_2': () => {consoleLog('wjytwo');consoleRun('wjy_2')}, 'wjy_3': () => {consoleLog('wjythree');consoleRun('wjy_3')}, 'wjy_4': () => {consoleLog('wjyfour');consoleRun('wjy_4')}, 'default': () => {consoleLog('sbother');consoleRun('other roommate')}, } const objOperation = (status,roommate)=>{ let objAction = obj[${roommate}_${status}] || obj['default'] objAction.call(this) } objOperation(1,'wjy') // wjyone // wjy_1</pre>

這類方法的核心就是把兩個條件拼接成一個unique的字符串,然後將拼接的字符串作爲主鍵,來進行對應的函數處理,從原理上講,條件層級越多,這個方法就越簡單越省事。當然,可能有些同學覺得這樣不夠規範,覺得下劃線不正規,也可以採用對象方式。

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const map = new Map([ [{status:'1',roommate:'ly'}, () => {consoleLog('lyone');consoleRun('ly_1')}], [{status:'2',roommate:'ly'}, () => {consoleLog('lytwo');consoleRun('ly_2')}], [{status:'3',roommate:'ly'}, () => {consoleLog('lythree');consoleRun('ly_3')}], [{status:'4',roommate:'ly'}, () => {consoleLog('lyfour');consoleRun('ly_4')}], [{status:'1',roommate:'wjy'}, () => {consoleLog('wjyone');consoleRun('wjy_1')}], [{status:'2',roommate:'wjy'}, () => {consoleLog('wjytwo');consoleRun('wjy_2')}], [{status:'3',roommate:'wjy'}, () => {consoleLog('wjythree');consoleRun('wjy_3')}], [{status:'4',roommate:'wjy'}, () => {consoleLog('wjyfour');consoleRun('wjy_4')}], ['default', () => {consoleLog('sbother');consoleRun('other roommate')}], ]) const mapOperation = (status,roommate)=>{ let mapAction = [...map].filter(([key,value])=>(key.roommate === roommate && key.status === status)) if(mapAction.length === 0){ mapAction = map.get('default'); mapAction.call(this); } else { mapAction.forEach(([key,value])=>value.call(this)) } }</pre>

這樣我們就可以看到Object和Map的一個主要區別了,在使用Map類型時候我們可以選擇通過對象來做爲key值。當然還有可能出現這種情況,比如同一個方法在某幾個條件中都被同時使用,並且傳了相同的參數比如:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const map = new Map([ [{status:'1',roommate:'ly'}, () => {consoleRun('ly')}], [{status:'2',roommate:'ly'}, () => {consoleRun('ly')}], [{status:'3',roommate:'ly'}, () => {consoleRun('ly')}], [{status:'4',roommate:'ly'}, () => {consoleRun('ly4')}], ['default', () => {consoleRun('other roommate')}], ])</pre>

這個時候就可以採用正則去進行了,把前四條作爲一個整體去匹配正則,比如:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const map = () => { return new Map([ [/^ly_[1,4]/, () => consoleRun('ly')], [/^default/, () => {consoleLog('sbother');consoleRun('other roommate')}], ]) }</pre>

通過正則去做匹配,當然這也是由於Map類型可以將各種類型作爲key。下面舉一個更爲具體的小例子,由於本人能力有限可能有很多沒想到的地方,所以寫出的代碼可能還是有些冗餘,歡迎大家提意見,先看看古老的if else方式:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const ifOperation = (status,roommate) => { if(roommate === 'ly'){ if(status === 1){ consoleLog('lyone'); } else if(status === 2){ consoleLog('lytwo'); } else if(status === 3){ consoleLog('lythree'); } else if(status === 4){ consoleLog('lyfour'); } else { consoleLog('lyother'); } } else if(roommate === 'wjy'){ if(status === 1){ consoleLog('wjyone'); } else if(status === 2){ consoleLog('wjytwo'); } else if(status === 3){ consoleLog('wjythree'); } else if(status === 4){ consoleLog('wjyfour'); } else { consoleLog('wjyother'); } } else { consoleLog('sbother'); } }</pre>

這裏可以發現,和前面只有一點不同,就是三個else分別是三個不同的條件,這樣用default處理就要分成三部分,這裏採用正則試試看咯:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">const map = () => { return new Map([ [/^ly_1/, () => consoleLog('lyone')], [/^ly_2/, () => consoleLog('lytwo')], [/^ly_3/, () => consoleLog('lythree')], [/^ly_4/, () => consoleLog('lyfour')], [/^ly_./, () => consoleLog('lyother')], [/^wjy_1/, () => consoleLog('wjyone')], [/^wjy_2/, () => consoleLog('wjytwo')], [/^wjy_3/, () => consoleLog('wjythree')], [/^wjy_4/, () => consoleLog('wjyfour')], // [/^ly_((?![1-4]{1}[\S]{0}).)*|^ly_[1-4]{2,}/g, () => consoleLog('lyother')], [/^wjy_./, () => consoleLog('wjyother')], [/^.*.*/, () => consoleLog('sbother')], ]) } const mapArray = new Map([ [2, (operation) => operation.pop()], [3, (operation) => { // console.log(operation); operation.pop(); operation.pop(); }], ['default', () => {}], ]) const mapOperation = (status,roommate) => { let operation = [...map()].filter(([key,value]) => { if(key.test(${roommate}_${status})){ return value; } }); let operationArr = mapArray.get(operation.length) || mapArray.get('default'); operationArr.call(this,operation); operation.forEach(([key,value]) => value.call(this)) } mapOperation(10000,'ly1')</pre>

原理很簡單,這裏我使用了兩層判斷邏輯,首先是進行正則表達式的編寫,由於有三個else,所以可能我們需要三個default,從正則角度來說就是任意字符了,這裏匹配會出現三種可能性,如果沒有匹配到,那麼只會存在一種可能爲[ [ /^.*.*$/, [Function (anonymous)] ] ],如果roommate正確匹配,而status沒有正確匹配,那麼會出現兩種情況,我將一定匹配的可能在數組存在最後就可以將其彈出,就可以獲得真實匹配正則,另外如果完全正確,那麼三個正則都會成功匹配,我們就需要彈出最後兩個,只取第一個。而彈出正則又是一個if else,我就放到了另一個map中,從而實現需求。

React hook篇

來到公司一開始做了一個前後端聯調的小項目,首先使用的就是React hook,那個時候還是不夠熟悉,後來參加其他項目又用了16.8之前的生命週期函數,最近重歸hook,發現項目中很多人都把組件的狀態存在了model裏,不過我還是覺得父子組件傳值傳函數這類基礎要拿出來整理一下,恰巧最近事情不多,就整理一下。

整體上分成四類:

1.父組件傳值給子組件

2.父組件傳方法給子組件

3.子組件傳值給父組件

4.子組件傳方法給父組件

1.父組件傳值給子組件

最簡單的最常用的就是父組件傳值給子組件,一般採用props傳值,我寫了一個最基本的例子:

父組件:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">import React,{useState} from 'react'; import {Button} from 'antd'; import styles from './HomePage.less'; import Children from './components/Children'; const HomePage = () => { const [value,setValue] = useState('first'); return ( <div className={styles.root}> <Button onClick={() => { setValue('second') }}>change</Button> <Children value={value} /> </div> ) } export default HomePage;</pre>

子組件:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">import React from "react"; import PropTypes from "prop-types"; import styles from "./Children.less"; const Children = (props) => { const { value } = props; return ( <div className={${styles.root}} > {value} </div> ); }; Children.propTypes = { value:PropTypes.string }; Children.defaultProps = { value:'' }; export default Children;</pre>

這是最簡單的,也是最常用的,基本任何一個項目都會用到,也是我們一般抽出業務組件最基本的類型。

2.父組件傳方法給子組件/子組件傳值給父組件

個人理解這兩類其實是一類,只是一個大的集合和一個小的集合的區別,就是說父組件傳方法給子組件是一個大的模塊,而恰巧這個模塊中囊括了子組件傳值給父組件這一個功能。舉個例子:

父組件:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">import React,{useState} from 'react'; import styles from './HomePage.less'; import Children from './components/Children'; const HomePage = () => { const [value, setvalue] = useState('') const method = (val) => { setvalue(val); } return ( <div className={styles.root}> <Children method={method} /> {value} </div> ) } export default HomePage;</pre>

子組件:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">import React from "react"; import PropTypes from "prop-types"; import {Button} from 'antd'; import styles from "./Children.less"; const Children = (props) => { const { method } = props; return ( <div className={${styles.root}} > <Button onClick={() => { method('children') }}>children</Button> </div> ); }; Children.propTypes = { method:PropTypes.func }; Children.defaultProps = { method:() => {} }; export default Children;</pre>

這裏的例子我寫的最基本,從子組件傳值給父組件的角度上講,就是子組件傳了一個value給父組件,讓父組件帶它去做他該做的事。那麼,從父組件傳方法給子組件上講,就是父組件傳了一個叫做method的方法給了子組件,這個方法裏,父組件想幹什麼就幹什麼,只需要子組件去調用就好了。當然,他們最天衣無縫的配合就是父組件已經把方法寫好了,只需要子組件傳值來讓父組件方法正常運轉就好了,perfect!

3.子組件傳方法給父組件

其實掌握了前面兩種基礎方法,在大多數項目已經夠用了,簡單的組件傳值用props,以及基於他的派生策略,複雜的可以在model裏存值去做傳遞,不過偶爾我們也需要在父組件中調用子組件的方法。我之前就遇到了這麼個問題,寫了一個大組件內容過多,我需要把他提出來,然後發現我使用了太多的useState,而且把子組件提出來後,發現事件是父組件的事件,但是需要修改子組件的state狀態,於是我就把修改子組件state的地方封裝成一個函數,供父組件去調用就好了。這裏舉個最簡單的例子:

父組件:

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">import React,{useRef} from 'react'; import styles from './HomePage.less'; import {Button} from 'antd'; import Children from './components/Children'; const HomePage = () => { const childRef = useRef<any>(); return ( <div className={styles.root}> <Children cRef={childRef} /> <Button onClick={() => { childRef.current.childrenMethod(); }}>click</Button> </div> ) } export default HomePage;</pre>

子組件

<pre class="cm-s-default" style="color: rgb(55, 61, 65); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">import React,{useImperativeHandle,useState} from "react"; import PropTypes from "prop-types"; import {Button} from 'antd'; import styles from "./Children.less"; const Children = (props) => { const { cRef } = props; const [value, setvalue] = useState('') useImperativeHandle(cRef,() => ({ childrenMethod:() => { setvalue('children'); } }), ); return ( <div className={${styles.root}} > {value} </div> ); }; Children.propTypes = { cRef:PropTypes.object }; Children.defaultProps = { cRef:{} }; export default Children;</pre>

說起來useRef也是很神奇的,在這裏,我們的子組件暴露給父組件的方法是childrenMethod,並且做了一個指代,父組件通過這個指代獲取到子組件方法,從而進行調用。當然這裏我只是針對hook做的分析,如果是類組件裏,就要使用createRef了。需要了解的也可以看看這個文章,個人感覺還不錯:https://zhuanlan.zhihu.com/p/269580627

到這裏,對於父子組件傳值,傳函數都做了一個介紹和描述,作爲新上手項目的同學來說,可以從我這個基礎例子出發,去感受React編程更爲神祕的地方。

結語

之前在分享React生命週期之後,就想對JS一些我用到的以及一些想要在接下來項目中用到的小知識點做一些整理,當然我接觸項目時間也不久,暫時就統計到了這些,後續有新的收穫,還會持續統計更新。

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章