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

對於一種事情,常常重複的話,很容易就會厭煩、以爲無趣、失去了當初的熱情。html

  • 作不完的業務需求,日復一日,就以爲工做乏味、都是體力活;
  • c端作多了,就以爲業務邏輯沒有挑戰性,沒意思,設計要求苛刻,特別煩;
  • b端作多了,就以爲每天寫平臺,每天對着無味的數據,沒機會玩一下炫酷的特效;
  • 技術建設作多了,看着本身作的東西都膩了;
  • 研究一些花哨的東西,又對工做內容沒有什麼意義;
  • 想用一下最新技術,然而項目歷史緣由又望洋興嘆......

天然而然,就失去了當初的熱情,找不到成就感,甚至還懷疑,本身是否是不適合作前端,是否是應該換一份工做,是否是要轉行了?前端

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

前端工程師如何持續保持熱情(二)react

避免重複用一樣的方法作一樣的事情

若是一直以一樣的姿態作同樣的事情,就很容易以爲無聊,沒有成就感。因此須要提高效率作一樣的事情,後面愈來愈快完成,天天都看見本身的進步,天然就有了熱情算法

精簡代碼,提升代碼質量

當你要copy的時候,就要想一下哪裏能夠封裝、哪裏要抽離邏輯、要複用哪裏的代碼了redux

不要讓一塊基本差很少的代碼重複存在

你們入門的時候,可能寫過這樣的代碼:緩存

<a>首頁</a>
<a>關於咱們</a>
<a>合做</a>
<a>加入咱們</a>
複製代碼

後來發現,vue能夠v-for,react能夠map,原生能夠循環插入fragment裏面最後一次性append。ruby

比較明顯的你們能夠發現,若是不太明顯又能複用的我怎麼發現呢?仍是那句話,當你copy的時候,代碼就能複用。舉個antd Form常見的一個應用場景:前端工程師

<Form>
  <Item label="名稱">
  {getFieldDecorator('name', {
    rules: [{
      required: true,
      message: '請輸入名稱',
    }],
  })(<Input />)}
  </Item>
  <Item label="描述">
  {getFieldDecorator('desc', {
    rules: [{
      required: true,
      message: '請輸入描述',
    }],
  })(<Input />)}
  </Item>
  <Item label="類型">
  {getFieldDecorator('type', {
    rules: [{
      required: true,
      message: '請選擇類型',
    }],
  })(<Checkbox />)}
  </Item>
</Form>
複製代碼

套路都是同樣的,結構也是同樣,那咱們就維護一個配置對象來維護這個form:antd

const items = [
  {
    label: '名稱',
    key: 'name',
    decorator: {
      rules: [{
      required: true,
      message: '請輸入名稱',
    }],
    },
    component: <Input /> }, // ... ] 複製代碼

再好比一個很長的if,後臺錯誤碼在前端處理,很常見的一段代碼:

// 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])
複製代碼

經過配置對象、循環渲染來減小重複代碼

要有一種「懶得寫代碼」的心態

好比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'
複製代碼

很整齊又看起來很舒服的代碼,可是它們都有共性,異步請求,請求中、請求成功、請求失敗的type。每次新增,咱們先來這裏複製三行,再改改。既然都差很少,咱們能夠寫個type生成器:

function actionGenerator(k = '') {
  const key = k.toUpperCase()
  return {
    ...(k
      ? {
        [`FETCH_${key}`]: `FETCH_${key}`,
        [`FETCH_${key}_SUCCESS`]: `FETCH_${key}_SUCCESS`,
        [`FETCH_${key}_ERROR`]: `FETCH_${key}_ERROR`,
      }
      : {}),
  };
}
// 今後之後,action_type代碼行數大大減小
複製代碼

再好比一個函數裏面對一個對象反覆賦值操做:

// before
obj.a = 1
obj.b = 2
obj.c = 5
// after
const newVals = {
  a: 1,
  b: 2,
  c: 5
}
// 若是業務裏面的obj很依賴本來引用,不能改變原對象
Object.keys(newVals).forEach(key => {
  obj[key] = newVals[key]
})
// 若是業務裏面的obj不依賴本來引用,能夠改變原對象
obj = { ...obj, ...newVals}
// 之後要改什麼,我只要去改一行newVals就能夠
複製代碼

再好比頁面文案,咱們能夠單獨拎出去到一個文件裏面統一配置,之後修改很方便

<header>練習不足兩年半的練習生</header>
<section>我只是一個練習生</section>
<ul>
  <li>唱</li>
  <li>跳</li>
  <li>rap</li>
</ul>
<footer>聯繫方式:000</footer>
複製代碼
const CONSTANT = {
  title: '練習不足兩年半的練習生',
  desc: '我只是一個練習生',
  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>聯繫方式:{CONSTANT.tel}</footer>
複製代碼

這是一個看起來好像寫了更多代碼,變複雜了。通常狀況下,是不須要這樣的。對於運營需求,這種方案應付隨時能夠變、說改就要改的文案是輕輕鬆鬆,並且還不須要關心頁面結構、不用去html裏面找文案在哪裏,直接寫一個文件放CONSTANT這類東西的。並且這個對象還能夠複用,就不會有那種「改個文案改了幾十個頁面」的狀況出現。

還有一個場景,咱們平時可能寫過不少這樣的代碼:

function sayHi(name, word) {
  console.log(`${name}: ${word}`)
}
const aSayHi = () => sayHi('a', 'hi')
const aSayGoodbye = () => sayHi('a', 'goodbye')
const aSayFuck = () => sayHi('a', 'fuck')
複製代碼

固然這是很簡單的場景,若是sayHi函數傳入的參數有不少個,並且也有不少個是重複的話,代碼就存在冗餘。這時候,須要用偏函數優化一下:

const aSay = (name) => sayHi('a', name)
const aSayHi = () => aSay('hi')
const aSayGoodbye = () => aSay('goodbye')
const aSayFuck = () => aSay('fuck')
複製代碼

三元、短路表達式用起來,舉幾個例子

// 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
this[type === DEL ? 'delateData' : 'addData'](id)
// or
;(type === DEL ? this.delateData : this.addData)(id)

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

// before
if (a) {
  return C
}
// after
return a && C
複製代碼

最後一個例子,好比一個重複的key賦值過程,能夠用變量key簡化

switch(key) {
  case 'a':
  return { a: newVal }
  case 'b':
  return { b: newVal }
  case 'c':
  return { c: newVal }
}
// after
return { [key]: newVal }
複製代碼

however, 不管當時多熟悉,代碼寫得多好,也必須寫註釋。核心算法、公共組件、公共函數尤爲須要註釋

/** * 建立樹狀組織架構 * @param {Object} orgs * @param {Array} [parent=[]] * @param {Boolean} check 是否須要校驗 */
複製代碼

小結:代碼簡短,沒有重複,本身看了也不會膩;抽離邏輯,封裝公共函數,無形提升代碼質量。最後帶來的效益是,加快了開發效率,也提高開發體驗與成就感:「終於不是每天copy了,看着本身一手簡單優雅的代碼,愈來愈想作需求了」

如何讓運營需求不枯燥無味

大部分公司都是以業務需求爲主,除了本身家的主打產品迭代需求外,另外的一般是運營需求做爲輔助。運營類需求,一般具備短時效性、頻率高、有deadline的特色。

一個運營活動,可能有多種模式、多種展現佈局、多種邏輯,產品運營會隨機組合,根據數據來調整尋求最佳方案,這就是場景的運營打法——ab test。

有的人面對這種狀況,老是會感嘆:又改需求了、每天改又沒什麼用、怎麼又改回來了、你開心就好。其實這種狀況,咱們只要給本身規劃好將來的路,後面真的隨意改,甚至基本不須要開發

咱們從一個簡單的用戶信息頁面的例子入手:

render() {
  const { name, score } = this.state.info
  return (
    <main> <header>{name}</header> 分數:<section>{score}</section> </main>
  )
}
複製代碼

增長適配層

咱們儘可能不要動核心公共邏輯代碼,不要說"只是加個if而已"。引入新的代碼,可能會引入其餘bug(常見錯覺之一: 我加一兩行,必定不會形成bug),有必定的風險。

回到主題,若是忽然要說這個活動要拉取另外一次遊戲的分數,直接去把請求改了,而後再把組件全部的字段改了,固然是一個方法。若是改完不久,又要說改回去,那是否是吐血了。顯然咱們須要一個適配層:

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 直接修改原請求函數
function fetchA() {
  return request('/a').then(res => {
    // 把適配後的結果返回,對外使用的時候不變
    return adapter(res, {
      name: 'nickname',
      score: 'counts'
    })
  })
}
複製代碼

咱們把適配集中到一塊兒了,每次要改,只要來這裏改適配層、改請求接口便可。固然,後臺若是所有統一的話是最好,只是有歷史緣由不是說改就改的。

拓展: 若是改來改去的接口不少呢? 此時,咱們須要維護一個配置表,保存某個請求與適配對象,而不是直接去把以前的代碼改掉

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
  }, {})
}
const reqs = reqGenerator(cgiMAp)
reqs.a()
複製代碼

嚴格遵照組件化

對於前面的那個用戶信息頁面的例子,若是用戶信息頁面須要填更多的內容呢,若是不想展現分數呢?

這種狀況,先要和產品側確認,哪些內容是之後必須在的,哪些是會變的。會多變的部分,咱們直接使用content讀進來:

class App extends Component {
  // ...
  render() {
    const { name, score } = this.state.info
    return (
      <main> <header>{name}</header> <section>{this.props.children}</section> 分數:<section>{score}</section> </main>
    )
  }
}
複製代碼

這樣子,組件上層只要包住個性化組件內容就ok

<App>
  <section>我只是一個練習生</section>
  <ul>
    <li>唱</li>
    <li>跳</li>
    <li>rap</li>
  </ul>
</App>
複製代碼

固然,事情確定不會這麼簡單人都是善變的,如今和你說標題永遠不變,轉身就變給你看:若是用戶沒參加過,那就展現另外一個灰色提示、若是用戶分數很高,給他標題加一個角標、若是充錢,標題換個皮膚

咱們仍是保持儘可能少改核心邏輯原則,先把header部分抽出一個組件:

const [IS_NO_JOIN, IS_PAY, IS_SCORE_HIGH] = [1, 2, 3]
const MAP = {
  [IS_NO_JOIN]: 'no-join',
  [IS_PAY]: 'has-pay',
  [IS_SCORE_HIGH]: 'high-score',
}
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>
  )
}
複製代碼

核心邏輯仍是不動,渲染的結構和順序也不動。更多定製化的功能,咱們從上層的父組件控制,控制渲染邏輯、展現組件、顯示的文案等等,上層的父組件拼裝好,就經過props傳入核心組件。這個過程就好比a同事以前寫了核心渲染邏輯。組件Header是a寫的,b同事不負責這塊,但b是此次需求的開發者,因此應該把渲染的姿式調整好、適配,注入到a同事寫的核心邏輯組件中去。最後的表現是,把控制權交給了b,讓b來改,這樣子也是侵入性小、風險小、擴展性強的一種方案。

"成功甩了個鍋",a把文檔甩給b,和b交代完邏輯後笑嘻嘻地下班了——這是比較優雅穩妥的方案,你們都理解的

反着來看,若是讓a來改(假設核心模塊是巨複雜並且沒法快速入手的只能由a才能hold住的)

// 加樣式n行
// 加處理state邏輯、加業務邏輯並且不能影響原有邏輯,改得當心翼翼
// 改字段來適配,又幾行
  render() {
    const { name, score } = this.state.info
    return (
      <main> <header className="xxx">{name}</header> <section>{this.props.children}</section> 分數:<section>{score}</section> </main>
    )
  }
// after coding: mmp,b的需求爲何要我出來幫忙,還要我從頭開始看需求並瞭解需求
複製代碼

改完了,上線了,忽然後面運營那邊又說,要不就xxx....此時求a內心陰影面積

運營配置接口

前面的例子,咱們都是作很小改動就完成了。固然每一次改個數字改個詞,前端就要發佈,這也不是優雅的解決方案。因此最終解決方案應該是,把這部分配置抽離出來,作到一個運營配置平臺上,前端經過接口讀取該平臺的配置回來渲染頁面。運營須要更改,咱們只須要去平臺上把配置修改便可。

// 讓配置寫在一個可視化配置平臺上
// 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()
})

複製代碼

可是也要考慮一下緩存、容災,提升一下頁面的可用性、可訪問性:

  • 若是接口掛了,如何提高用戶體驗
  • 怎樣才能讓頁面掌控在手中,好比監控、埋點
  • 怎樣作到頁面的健壯,經得起各類機器以及用戶的亂操做的蹂躪

最後

放下前端這個標籤,不管作的是什麼,對本身都是一種成長,除了技術上,更多的是各類軟技能、方法論、思惟方式。出社會才發現,也許coding纔是生活中最簡單的一部分

以上是工做一年以來的沉澱的一部分,算得上是工做總結吧,待續......

關注公衆號《不同的前端》,以不同的視角學習前端,快速成長,一塊兒把玩最新的技術、探索各類黑科技

相關文章
相關標籤/搜索