前端工程师如何持续保持热情(一)

对于一种事情,常常重复的话,很容易就会厌烦、以为无趣、失去了当初的热情。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才是生活中最简单的一部分

以上是工做一年以来的沉淀的一部分,算得上是工做总结吧,待续......

关注公众号《不同的前端》,以不同的视角学习前端,快速成长,一块儿把玩最新的技术、探索各类黑科技

相关文章
相关标签/搜索