溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

前端工程師如何持續(xù)保持熱情(一)

發(fā)布時(shí)間:2020-07-07 06:00:12 來(lái)源:網(wǎng)絡(luò) 閱讀:260 作者:wx5d61fdc401976 欄目:web開發(fā)

對(duì)于一種事情,經(jīng)常重復(fù)的話,很容易就會(huì)厭煩、覺(jué)得無(wú)趣、失去了當(dāng)初的熱情。

做不完的業(yè)務(wù)需求,日復(fù)一日,就覺(jué)得工作乏味、都是體力活;
c端做多了,就覺(jué)得業(yè)務(wù)邏輯沒(méi)有挑戰(zhàn)性,沒(méi)意思,設(shè)計(jì)要求苛刻,特別煩;
b端做多了,就覺(jué)得天天寫平臺(tái),天天對(duì)著無(wú)味的數(shù)據(jù),沒(méi)機(jī)會(huì)玩一下炫酷的特效;
技術(shù)建設(shè)做多了,看著自己做的東西都膩了;
研究一些花哨的東西,又對(duì)工作內(nèi)容沒(méi)有什么意義;
想用一下最新技術(shù),然而項(xiàng)目歷史原因又望洋興嘆......

自然而然,就失去了當(dāng)初的熱情,找不到成就感,甚至還懷疑,自己是不是不適合做前端,是不是應(yīng)該換一份工作,是不是要轉(zhuǎn)行了?
避免重復(fù)用同樣的方法做同樣的事情
如果一直以同樣的姿態(tài)做一樣的事情,就很容易覺(jué)得無(wú)聊,沒(méi)有成就感。所以需要提升效率做同樣的事情,后面越來(lái)越快完成,每天都看見(jiàn)自己的進(jìn)步,自然就有了熱情
精簡(jiǎn)代碼,提高代碼質(zhì)量

當(dāng)你要copy的時(shí)候,就要想一下哪里可以封裝、哪里要抽離邏輯、要復(fù)用哪里的代碼了

不要讓一塊基本差不多的代碼重復(fù)存在
大家入門的時(shí)候,可能寫過(guò)這樣的代碼:
<a>首頁(yè)</a>
<a>關(guān)于我們</a>
<a>合作</a>
<a>加入我們</a>
復(fù)制代碼后來(lái)發(fā)現(xiàn),vue可以v-for,react可以map,原生可以循環(huán)插入fragment里面最后一次性append。
比較明顯的大家可以發(fā)現(xiàn),如果不太明顯又能復(fù)用的我怎么發(fā)現(xiàn)呢?還是那句話,當(dāng)你copy的時(shí)候,代碼就能復(fù)用。舉個(gè)antd Form常見(jiàn)的一個(gè)應(yīng)用場(chǎng)景:
<Form>
<Item label="名稱">
{getFieldDecorator('name', {
rules: [{
required: true,
message: '請(qǐng)輸入名稱',
}],
})(<Input />)}
</Item>
<Item label="描述">
{getFieldDecorator('desc', {
rules: [{
required: true,
message: '請(qǐng)輸入描述',
}],
})(<Input />)}
</Item>
<Item label="類型">
{getFieldDecorator('type', {
rules: [{
required: true,
message: '請(qǐng)選擇類型',
}],
})(<Checkbox />)}
</Item>
</Form>
復(fù)制代碼套路都是一樣的,結(jié)構(gòu)也是一樣,那我們就維護(hù)一個(gè)配置對(duì)象來(lái)維護(hù)這個(gè)form:
const items = [
{
label: '名稱',
key: 'name',
decorator: {
rules: [{
required: true,
message: '請(qǐng)輸入名稱',
}],
},
component: <Input />
},
// ...
]
復(fù)制代碼再比如一個(gè)很長(zhǎng)的if,后臺(tái)錯(cuò)誤碼在前端處理,很常見(jiàn)的一段代碼:
// before
if (type === 1) {
console.log('add')
} else if (type === 2) {
console.log('delete')
} else if (type === 3) {
console.log('edit')
} else {
console.log('get')
}

// after
const MAP = {
1: 'add',
2: 'delete',
3: 'edit',
4: 'get',
}
console.log(MAP[type])
復(fù)制代碼
通過(guò)配置對(duì)象、循環(huán)渲染來(lái)減少重復(fù)代碼

要有一種“懶得寫代碼”的心態(tài)
比如redux的action type
const FETCH_LIST = 'FETCH_LIST'
const FETCH_LIST_SUCCESS = 'FETCH_LIST_SUCCESS'
const FETCH_LIST_FAILED = 'FETCH_LIST_FAILED'
const FETCH_USERINFO = 'FETCH_USERINFO'
const FETCH_USERINFO_SUCCESS = 'FETCH_USERINFO_SUCCESS'
const FETCH_USERINFO_ERROR = 'FETCH_USERINFO_ERROR'
復(fù)制代碼很整齊又看起來(lái)很舒服的代碼,但是它們都有共性,異步請(qǐng)求,請(qǐng)求中、請(qǐng)求成功、請(qǐng)求失敗的type。每次新增,我們先來(lái)這里復(fù)制三行,再改改。既然都差不多,我們可以寫個(gè)type生成器:
function actionGenerator(k = '') {
const key = k.toUpperCase()
return {
...(k
? {

    [`FETCH_${key}_SUCCESS`]: `FETCH_${key}_SUCCESS`,
    [`FETCH_${key}_ERROR`]: `FETCH_${key}_ERROR`,
  }
  : {}),

};
}
// 從此以后,action_type代碼行數(shù)大大減少
復(fù)制代碼再比如一個(gè)函數(shù)里面對(duì)一個(gè)對(duì)象反復(fù)賦值操作:
// before
obj.a = 1
obj.b = 2
obj.c = 5
// after
const newVals = {
a: 1,
b: 2,
c: 5
}
// 如果業(yè)務(wù)里面的obj很依賴原本引用,不能改變?cè)瓕?duì)象
Object.keys(newVals).forEach(key => {
obj[key] = newVals[key]
})
// 如果業(yè)務(wù)里面的obj不依賴原本引用,可以改變?cè)瓕?duì)象
obj = { ...obj, ...newVals}
// 以后要改什么,我只要去改一行newVals就可以
復(fù)制代碼再比如頁(yè)面文案,我們可以單獨(dú)拎出去到一個(gè)文件里面統(tǒng)一配置,以后修改很方便
<header>練習(xí)不足兩年半的練習(xí)生</header>
<section>我只是一個(gè)練習(xí)生</section>
<ul>
<li>唱</li>
<li>跳</li>
<li>rap</li>
</ul>
<footer>聯(lián)系方式:000</footer>
復(fù)制代碼const CONSTANT = {
title: '練習(xí)不足兩年半的練習(xí)生',
desc: '我只是一個(gè)練習(xí)生',
hobbies: ['唱', '跳', 'rap'],
tel: '000'
}

<header>{CONSTANT.title}</header>
<section>{CONSTANT.desc}</section>
<ul>
{
CONSTANT.hobbies.map((hobby, i) => <li key={i}>{hobby}</li>)
}
</ul>
<footer>聯(lián)系方式:{CONSTANT.tel}</footer>
復(fù)制代碼這是一個(gè)看起來(lái)好像寫了更多代碼,變復(fù)雜了。一般情況下,是不需要這樣的。對(duì)于運(yùn)營(yíng)需求,這種方案應(yīng)付隨時(shí)可以變、說(shuō)改就要改的文案是輕輕松松,而且還不需要關(guān)心頁(yè)面結(jié)構(gòu)、不用去html里面找文案在哪里,直接寫一個(gè)文件放CONSTANT這類東西的。而且這個(gè)對(duì)象還可以復(fù)用,就不會(huì)有那種“改個(gè)文案改了幾十個(gè)頁(yè)面”的情況出現(xiàn)。
還有一個(gè)場(chǎng)景,我們平時(shí)可能寫過(guò)很多這樣的代碼:
function sayHi(name, word) {
console.log(${name}: ${word})
}
const aSayHi = () => sayHi('a', 'hi')
const aSayGoodbye = () => sayHi('a', 'goodbye')
const aSayFuck = () => sayHi('a', 'fuck')
復(fù)制代碼當(dāng)然這是很簡(jiǎn)單的場(chǎng)景,如果sayHi函數(shù)傳入的參數(shù)有很多個(gè),而且也有很多個(gè)是重復(fù)的話,代碼就存在冗余。這時(shí)候,需要用偏函數(shù)優(yōu)化一下:
const aSay = (name) => sayHi('a', name)
const aSayHi = () => aSay('hi')
const aSayGoodbye = () => aSay('goodbye')
const aSayFuck = () => aSay('fuck')
復(fù)制代碼三元、短路表達(dá)式用起來(lái),舉幾個(gè)例子
// before
if (type === true) {
value = 1
} else {
value = 2
}
//after
value = type ? 1 : 2

// before
if (type === DEL) {
this.delateData(id)
} else {
this.addData(id)
}
// after
thistype === DEL ? 'delateData' : 'addData'
// or
;(type === DEL ? this.delateData : this.addData)(id)

// before
if (!arr) {
arr = []
}
arr.push(item)
// after 這個(gè)屬于eslint不建議的一種
;(arr || (arr = [])).push(item)

// before
if (a) {
return C
}
// after
return a && C
復(fù)制代碼最后一個(gè)例子,比如一個(gè)重復(fù)的key賦值過(guò)程,可以用變量key簡(jiǎn)化
switch(key) {
case 'a':
return { a: newVal }
case 'b':
return { b: newVal }
case 'c':
return { c: newVal }
}
// after
return { [key]: newVal }
復(fù)制代碼however, 無(wú)論當(dāng)時(shí)多熟悉,代碼寫得多好,也必須寫注釋。核心算法、公共組件、公共函數(shù)尤其需要注釋
/**

  • 創(chuàng)建樹狀組織架構(gòu)
  • @param {Object} orgs
  • @param {Array} [parent=[]]
  • @param {Boolean} check 是否需要校驗(yàn)
    */
    復(fù)制代碼
    小結(jié):代碼簡(jiǎn)短,沒(méi)有重復(fù),自己看了也不會(huì)膩;抽離邏輯,封裝公共函數(shù),無(wú)形提高代碼質(zhì)量。最后帶來(lái)的效益是,加快了開發(fā)效率,也提升開發(fā)體驗(yàn)與成就感:“終于不是天天copy了,看著自己一手簡(jiǎn)單優(yōu)雅的代碼,越來(lái)越想做需求了”

如何讓運(yùn)營(yíng)需求不枯燥無(wú)味
大部分公司都是以業(yè)務(wù)需求為主,除了自己家的主打產(chǎn)品迭代需求外,另外的通常是運(yùn)營(yíng)需求作為輔助。運(yùn)營(yíng)類需求,通常具有短時(shí)效性、頻率高、有deadline的特點(diǎn)。
一個(gè)運(yùn)營(yíng)活動(dòng),可能有多種模式、多種展示布局、多種邏輯,產(chǎn)品運(yùn)營(yíng)會(huì)隨機(jī)組合,根據(jù)數(shù)據(jù)來(lái)調(diào)整尋求最佳方案,這就是場(chǎng)景的運(yùn)營(yíng)打法——ab test。
有的人面對(duì)這種情況,總是會(huì)感嘆:又改需求了、天天改又沒(méi)什么用、怎么又改回來(lái)了、你開心就好。其實(shí)這種情況,我們只要給自己規(guī)劃好未來(lái)的路,后面真的隨意改,甚至基本不需要開發(fā)
我們從一個(gè)簡(jiǎn)單的用戶信息頁(yè)面的例子入手:
render() {
const { name, score } = this.state.info
return (
<main>
<header>{name}</header>
分?jǐn)?shù):<section>{score}</section>
</main>
)
}
復(fù)制代碼增加適配層
我們盡量不要?jiǎng)雍诵墓策壿嫶a,不要說(shuō)"只是加個(gè)if而已"。引入新的代碼,可能會(huì)引入其他bug(常見(jiàn)錯(cuò)覺(jué)之一: 我加一兩行,一定不會(huì)造成bug),有一定的風(fēng)險(xiǎn)。
回到主題,如果突然要說(shuō)這個(gè)活動(dòng)要拉取另一次游戲的分?jǐn)?shù),直接去把請(qǐng)求改了,然后再把組件所有的字段改了,當(dāng)然是一個(gè)方法。如果改完不久,又要說(shuō)改回去,那是不是吐血了。顯然我們需要一個(gè)適配層:
function adapter(response, info) {
return Object.keys(newConf).reduce((res, key) => {
res[key] = response[newConf[key]]
return res
}, {})
}
// before
function fetchA() {
return request('/a')
}
fetchA.then(res => {
this.setState({
info: { name: res.nickname, score: res.counts }
})
})

// after 直接修改原請(qǐng)求函數(shù)
function fetchA() {
return request('/a').then(res => {
// 把適配后的結(jié)果返回,對(duì)外使用的時(shí)候不變
return adapter(res, {
name: 'nickname',
score: 'counts'
})
})
}
復(fù)制代碼我們把適配集中到一起了,每次要改,只要來(lái)這里改適配層、改請(qǐng)求接口即可。當(dāng)然,后臺(tái)如果全部統(tǒng)一的話是最好,只是有歷史原因不是說(shuō)改就改的。

拓展: 如果改來(lái)改去的接口很多呢?
此時(shí),我們需要維護(hù)一個(gè)配置表,保存某個(gè)請(qǐng)求與適配對(duì)象,而不是直接去把之前的代碼改掉

const cgiMAp = {
'/a': {
name: 'nickname',
score: 'counts'
},
// ...
}
復(fù)制代碼通過(guò)讀取這個(gè)配置,在請(qǐng)求函數(shù)里面封裝一個(gè)讀取邏輯,即可適配所有的相關(guān)接口。后面如果換了一種數(shù)據(jù)來(lái)源渠道,那也光速解決需求
function reqGenerator(cfg) {
return Object.keys(cfg).reduce((res, key) => {
res[key.slice(1)] = () => request(key).then(r => adapter(r, cfg[key]))
return res
}, {})
}
const reqs = reqGenerator(cgiMAp)
reqs.a()
復(fù)制代碼嚴(yán)格遵守組件化

對(duì)于前面的那個(gè)用戶信息頁(yè)面的例子,如果用戶信息頁(yè)面需要填更多的內(nèi)容呢,如果不想展示分?jǐn)?shù)呢?

這種情況,先要和產(chǎn)品側(cè)確認(rèn),哪些內(nèi)容是以后必須在的,哪些是會(huì)變的。會(huì)多變的部分,我們直接使用content讀進(jìn)來(lái):
class App extends Component {
// ...
render() {
const { name, score } = this.state.info
return (
<main>
<header>{name}</header>
<section>{this.props.children}</section>
分?jǐn)?shù):<section>{score}</section>
</main>
)
}
}
復(fù)制代碼這樣子,組件上層只要包住個(gè)性化組件內(nèi)容就ok
<App>
<section>我只是一個(gè)練習(xí)生</section>
<ul>
<li>唱</li>
<li>跳</li>
<li>rap</li>
</ul>
</App>
復(fù)制代碼當(dāng)然,事情肯定不會(huì)這么簡(jiǎn)單人都是善變的,現(xiàn)在和你說(shuō)標(biāo)題永遠(yuǎn)不變,轉(zhuǎn)身就變給你看:如果用戶沒(méi)參加過(guò),那就展示另一個(gè)灰色提示、如果用戶分?jǐn)?shù)很高,給他標(biāo)題加一個(gè)角標(biāo)、如果充錢,標(biāo)題換個(gè)皮膚
我們還是保持盡量少改核心邏輯原則,先把header部分抽出一個(gè)組件:
const [IS_NO_JOIN, IS_PAY, IS_SCORE_HIGH] = [1, 2, 3]
const MAP = {

}
function Header(props) {
const [type, setType] = useState('normal')
useEffect(() => {
// 用名字獲取用戶身份
requestTypeByName(props.children).then(res => {
setType(res.type)
})
}, [])
return (
<header
className={classnames(MAP[type])}

{props.children}
</header>
)
}
復(fù)制代碼核心邏輯還是不動(dòng),渲染的結(jié)構(gòu)和順序也不動(dòng)。更多定制化的功能,我們從上層的父組件控制,控制渲染邏輯、展示組件、顯示的文案等等,上層的父組件拼裝好,就通過(guò)props傳入核心組件。這個(gè)過(guò)程就比如a同事之前寫了核心渲染邏輯。組件Header是a寫的,b同事不負(fù)責(zé)這塊,但b是這次需求的開發(fā)者,所以應(yīng)該把渲染的姿勢(shì)調(diào)整好、適配,注入到a同事寫的核心邏輯組件中去。最后的表現(xiàn)是,把控制權(quán)交給了b,讓b來(lái)改,這樣子也是侵入性小、風(fēng)險(xiǎn)小、擴(kuò)展性強(qiáng)的一種方案。

"成功甩了個(gè)鍋",a把文檔甩給b,和b交代完邏輯后笑嘻嘻地下班了——這是比較優(yōu)雅穩(wěn)妥的方案,大家都理解的

反著來(lái)看,如果讓a來(lái)改(假設(shè)核心模塊是巨復(fù)雜而且無(wú)法快速入手的只能由a才能hold住的):
// 加樣式n行
// 加處理state邏輯、加業(yè)務(wù)邏輯而且不能影響原有邏輯,改得小心翼翼
// 改字段來(lái)適配,又幾行
render() {
const { name, score } = this.state.info
return (
<main>
<header className="xxx">{name}</header>
<section>{this.props.children}</section>
分?jǐn)?shù):<section>{score}</section>
</main>
)
}
// after coding: mmp,b的需求為什么要我出來(lái)幫忙,還要我從頭開始看需求并了解需求
復(fù)制代碼
改完了,上線了,突然后面運(yùn)營(yíng)那邊又說(shuō),要不就xxx....此時(shí)求a心里陰影面積

運(yùn)營(yíng)配置接口
前面的例子,我們都是做很小改動(dòng)就完成了。當(dāng)然每一次改個(gè)數(shù)字改個(gè)詞,前端就要發(fā)布,這也不是優(yōu)雅的解決方案。所以最終解決方案應(yīng)該是,把這部分配置抽離出來(lái),做到一個(gè)運(yùn)營(yíng)配置平臺(tái)上,前端通過(guò)接口讀取該平臺(tái)的配置回來(lái)渲染頁(yè)面。運(yùn)營(yíng)需要更改,我們只需要去平臺(tái)上把配置修改即可。
// 讓配置寫在一個(gè)可視化配置平臺(tái)上
// const cgiMAp = {
// '/a': {
// name: 'nickname',
// score: 'counts'
// },
// // ...
// }
function reqGenerator(cfg) {
return Object.keys(cfg).reduce((res, key) => {
res[key.slice(1)] = () => request(key).then(r => adapter(r, cfg[key]))
return res
}, {})
}
request('/config').then(res => {
const reqs = reqGenerator(res.cgiMAp)
reqs.a()
})

復(fù)制代碼但是也要考慮一下緩存、容災(zāi),提高一下頁(yè)面的可用性、可訪問(wèn)性:

如果接口掛了,如何提升用戶體驗(yàn)
怎樣才能讓頁(yè)面掌控在手中,比如監(jiān)控、埋點(diǎn)
怎樣做到頁(yè)面的健壯,經(jīng)得起各種機(jī)器以及用戶的亂操作的蹂躪

最后
放下前端這個(gè)標(biāo)簽,無(wú)論做的是什么,對(duì)自己都是一種成長(zhǎng),除了技術(shù)上,更多的是各種軟技能、方法論、思維方式。出社會(huì)才發(fā)現(xiàn),也許coding才是生活中最簡(jiǎn)單的一部分

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI