bigo-frontend / blog

👨🏻‍💻👩🏻‍💻 bigo前端技术博客
https://juejin.cn/user/4450420286057022/posts
MIT License
129 stars 9 forks source link

【bigo】在bigo前端实习三个月的总结 #9

Open Sunny-lucking opened 4 years ago

Sunny-lucking commented 4 years ago

file

本文首发于:https://github.com/bigo-frontend/blog/ 欢迎关注、转载。

一、整体认知

1. 团队协作

在家或者在学校的时候,一直都是自己独立地开发项目,前后端都是自己一个人梭哈,怎么写,任凭自己主宰,在这种独自一人开发的模式中,对于团队协作模式可谓是一无所知。 后来,实习期间,经过几次版本的迭代之后,对于团队协作开发模式已经有了整体上的认知。例如一个项目的需求周期是怎样的:

  1. 需求评审
  2. 需求进入排期表
  3. 进入开发
  4. 前后端自测联调
  5. 自测无误后发起提测
  6. 测试提bug(最好没有bug)
  7. 要是有bug就修复
  8. 发布上线

    在这些过程中,前端主要发挥什么作用,也有了大致上的了解。

    2. 工程化的理解

    在实习之前,我对于工程化的概念比较模糊,更多的是局限于 组件化模块化 等。虽然之前平时逛有一些技术博客网站的时候,会看到一些类似 《xx走进工程化xx》《xx自动化xx》,我也迫不及待地点进去了,但对于我来说,确实只是玄学,于是我会马上关闭,然后深呼吸,接着,选择《js的基本类型》《css 选择器的顺序》等文章 津津有味地读起来。

    实习期间,进过几次发版之后,了解到,原来上线,是不需要手动把资源上传到服务器的。用名为jenkins 的持续集成/持续发布的工具实现起来要方便许多。

    慢慢的刷新了我对工程化的认知,工程化远不止组件与模块,原来它还包括 规范化、 持续集成、自动化构建、自动化部署、自动化测试等等。

    工程化是一个很大的话题,希望随着实践经验的积累,能够慢慢地掌握精髓,后面也需要找些书来看看。

3. Git操作

除了写代码,我想我最大的一个难题就是git操作了

在实习前,我会的git命令是这样的

除此之外,由于有时是另一个需求还没开发完,就要开发其他需求,所以是好几个分支在同时开发的,这时难就发生了意想不到的事情,例如下面我所遇到的:

更改已经被 push ,但是不是需要的

在分支上开发的过程中,添加了一些错误的文件,或者错误的修改之后,把本地的修改给add,commit,并且push上去了,但是这些修改是不需要的。怎么回退呢?

  1. git本地回退到指定版本后,按以往的提交顺序进行提交时会报错
  2. 这是因为gitlab已经在你提交历史前面了,你无法把push过的再次push进行覆盖,这个时候加个参数–force就行
  3. 加完参数后发现gitlab不允许强行push到保护的分支上,解决方法是让项目的管理员暂时在gitlab上把你要提交的分支设置为不受保护的分支即可

    除此之外也了解到有git revert这个方法

正在发开当前分支,但需要切到其他分支

开开心心地在新的分支上开发新的功能,这时候,产品经理说 其他分支有bug或者修改需求,需要赶紧处理下,这样的话,就需要切到目标分支了,那在当前的分支上所作的修改该怎么办呢?

我想到的是commit,但其实还有更好的方法

  1. 使用git stash push –m”message” 保存当前的修改
  2. 切到目标分支修改bug,修改提交后切回原分支
  3. 使用git stash pop 还原

这里需要注意的就是保存当前修改的时候,最好是添加上message,而不是简单的git stash,因为git stash 一旦多了之后,就会记不清是做了什么修改

在错误的分支开发了新功能,新功能还没有在本地进行commit(提交)

  1. 使用git stash push –m”message”保存当前的修改

  2. 切换到需要开发的分支

  3. 使用git stash apply 应用修改

在错误的分支开发了新功能,新功能已经在本地提交了,但是还没有push到远程仓库

  1. git log --oneline 先获取本次commit的hash
  2. git cherry-pick <commit hash> 切到目标分支后将本次commit的修改merge到目标分支
  3. git reset <commit hash> 切回错误分支,回退到之前版本
  4. git checkout -- . 清空修改

二、调试技巧

1. console.table展示数据

在以往打印某个变量,基本都是使用console.log,但是其实还有更好的方法。

在控制台上展示数组或对象,使用console.table比console.log更加直观明了。

console.table([
  {name:"Sunny",age:18,country:'China',job:'engineer'},
  {name:"Luffy",age:16,country:'Japan',job:'Pirate'},
  {name:"Kin",age:36,country:'Italy',job:'doctor'},
])

image.png

当然,有时候,你可能不想输出那么多列,比如,你只想输出name和job,那么你只需要在后面加个依赖数组,表明要输出哪些字段

console.table([
  {name:"Sunny",age:18,country:'China',job:'engineer'},
  {name:"Luffy",age:16,country:'Japan',job:'Pirate'},
  {name:"Kin",age:36,country:'Italy',job:'doctor'},
],['name','job'])

image.png

实际上,除了console.table。还有其他的方法:

说了这么多,除了table,其实更多还是使用debugger!

2. copy复制数据

使用copy方法 可以复制控制台 输出的值 到粘贴板,比手动复制要方便一些 需要注意的是,只能在谷歌浏览器上哦

3. 滚动元素到视图

在调试DOM元素的时候,我们已经聚焦到相关的DOM结构上了,但是对应的元素并没有在可视窗口上展示,那么我们可以将其快速滚动到可视窗口。

控制面板 => Elements => 右击选中的DOM节点 => Scroll into view

4. 捕获快照

有时候,需要把实现好的页面交付给产品看一下,这时需要截图,但是如果网页太长,就很不方便了,难道要截很多张吗?当然,可以下载长截图的工具,可以这样,但是没必要,其实有一个长截图的命令:

控制面板 => 审查元素 => command + shift + p => capture full size screenshot

这时你就能看到一张长截图啦

5. 捕获局部快照

当然,有时候,并不想截取那么长,只想截取某个部分,其实也是有对应的命令的

控制面板 => 审查元素 => command + shift + p => capture node screens

三、代码风格

由于实习过程中,是在一个现在的项目上进行迭代,添加新的功能,所以可以从前辈们的代码中学习到一些比较好的代码风格,以下就列出来一些印象比较深刻的。

1. 注重命名

命名一个事件,总是有些困难,因为它很重要,我们希望可以直截了当地从方法名就看出方法的作用。比如将两个数组合并成一个新的数组,并且返回的数组不存在重复的值。

我们会怎样命名,才能体现出它的功能呢?

也许可以这样:

mergeListsIntoUniqueList(arr1,arr2){}

但是,实际上,一个方法只做一件事是最好的,这样耦合性会低一些,方法的复用性也就好一点

我们把这两个方法拆成两个方法,一个负责合并,一个负责去重

mergeList(arr1,arr2)
unique(arr)

2. IF语句简化

看一下下面的代码:

多个if嵌套

if(name === "sunny" || name === "Luffy" || === "Kin"){

}

有个比较优美的写法,就是把这些值写进一个数组来,然后判断name是否在数组里即可

const nameArr = ["sunny","Luffy","Kin"]
if(nameArr.includes(name)){

}

3. && 代替 if

看一下下面的代码:

  function hello(){
    console.log("hi")
  }
  let enableSpeak = false
  if( enableSpeak){
    hello()
  }

其实有个更加优美的写法

  function hello(){
    console.log("hi")
  }
  let enableSpeak = false

  enableSpeak && hello()

4. 多个if嵌套

当我们在接收后端返回来的数据的时候,接受到的是一个多层嵌套的对象,而我们要拿到深层处的一个对象属性的值,为了防止报错,我们可能需要利用多层if嵌套,如下代码所示:

if(result.data){
  if(result.data.obj){
    if(result.list.obj.name){
      if(result.list.obj.name.firstname){

      }
    }
  }
}

这样写起来,始终是有些麻烦的。

有个可选链操作符( ?. ),它允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效

if(result.data.obj.name.firstname){

}

5. 尽早返回

有下面的代碼:

function handleEvent(event){
  if(event){
    //...
    if(event.target){
      // do some thing real
    }
  }
}

尽早返回使得我们的代码更加易读

function handleEvent(event){
  if(!event || !event.target){
   return;
  }
}

6. 对象参数

有下面所示代码

function createObj(name,sex,age,hobby,job,address){}

当函数的参数比较多时,我们可以将同一类的参数使用对象进行合并,然后将合并后的对象作为参数传入,这样在调用该函数时能够很清楚地理解每个参数的含义,也不用去记住每个参数的位置

function createObj({name,sex,age,hobby,job,address}){}

四、新技术

在实习前,由于专攻vue,对于react属于零基础,由于组里的项目是react,所以就开始走上react的踩坑之路

在写react的时候,会出现一个报错,因此就会引发一些思考

1. 为什么要引入React

在写 React 的时候,你可能会写类似这样的代码:

import React from 'react'
function A(){
  return <h1>前端sunny</h1>
}

要是不写

import React from 'react'

就会报错,很奇怪,下面代码中明明没有使用到react的方法或属性,为什么一定要引入react呢?

后来查资料原来是 babel 会把jsx代码转化成

function A(){
  return React.createElement('h1',null,"前端sunny")
}

2. 为什么要引入super(),能不能不调用?

JavaScript 对this使用的限制,是有原因的。假设有如下的继承:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class Geek extends Person {
  constructor(name) {
    this.sayHello;
    super(name);
  }
  sayHello() {
    alert('Good morning xdm!');
  }
}

如果 JavaScript 允许在调用super之前使用 this,一个月之后,我们需要修改sayHello方法,方法中使用了 name 属性:

  sayHello() {
    alert('Good morning xdm! I am '+ this.name);
  }

就会出现 name 为 undefined的情况了,是不是细思极恐!

3. 为什么调用方法要 bind this

class Geek extends Person {

  sayHello() {
    alert('Good morning xdm!');
  }
  render(){
    return (
      <button onClick={this.handleClick}>Click</button>
    )
  }
}

会发现 this是 undefined,第一次遇到这个问题,始终会觉得奇怪,因为在以前写vue的时候,是没啥问题的

vue代码:

<button @click="handleClick">Click</button>

或者

<button @click="this.handleClick">Click</button>

之所以react和vue事件的使用方式有所差别,原因还是内部的实现机制的不同

react将事件通过addEventListener统一注册到 document上,然后会有一个事件池存储了所有的事件,当事件触发的时候,通过dispatchEvent进行事件分发,可以简单的理解为,最终this.handleClick会做为一个回调函数调用

作为回调函数调用通常会出现this丢失的情况,就像下面的代码,最常见不过:

function delaySayHello(){
  let _this = this;
  setTimeout(function(){
    // 使用_this
  })
}

那有哪些方法来处理这个this呢?

1. 直接bind this

写起来顺手,一气呵成。性能不太好,每次 render 都会进行 bind,而且如果有两个元素的事件处理函数式同一个,也还是要进行 bind。

class Geek extends Person {

  sayHello() {
    alert('Good morning xdm!');
  }
  render(){
    return (
      <button onClick={this.sayHello.bind(this)}>Click</button>
    )
  }
}

2.constructor里手动bind

因为构造函数只执行一次,那么只会 bind 一次,如果有多个元素都需要调用这个函数,也不需要重复 bind。

没有明显缺点,可能就是代码多了?

class Geek extends Person {
  constructor(props){
    super(props)
    this.sayHello = this.sayHello.bind(this)
  }
  sayHello() {
    alert('Good morning xdm!');
  }
  render(){
    return (
      <button onClick={this.sayHello.bind(this)}>Click</button>
    )
  }
}

3.使用箭头函数

顺手,好看。每次 render 都会重复创建函数,性能会差一点。

class Geek extends Person {
  sayHello() {
    alert('Good morning xdm!');
  }
  render(){
    return (
      <button onClick={(e)=>this.sayHello(e)}>Click</button>
    )
  }
}

4.public class field

顺手,好看。处于试验阶段,如果不愿冒险,最好是在构造函数中绑定 this

class Geek extends Person {
  sayHello = () => {
    alert('Good morning xdm!');
  }
  render(){
    return (
      <button onClick={}>Click</button>
    )
  }
}

4. 手写简单的react

为了更好地熟悉react的实现原理,看一些教学视频和资料,自己尝试着写了一个简单的react

手写简单的react核心原理 :https://juejin.cn/post/6898292945867571207

5. React Hook 的学习

由于在项目的迭代中,新增的组件尽量是需要使用hooks来实现的,所以, 对react hook的学习也是必不可少的。

其中,学到了例如useMemouseCallback等性能优化方法。同样,为了更好地使用,也通过看了些视频和资料,自己尝试着写了hooks中 常用的 hook的实现原理。

手写React Hook核心原理

结尾

学无止境,发现还有很多东西需要去学习,例如react fiber,react-saga,next.js,react 360等,在接下来的日子里,也需要努力学习啊!

欢迎大家留言讨论,祝工作顺利、生活愉快!

我是bigo前端,下期见。

masongzhi commented 3 years ago

有个可选链操作符( ?. ),

这是因为项目安装了@babel/plugin-proposal-optional-chaining,不是原生支持的特性.

Sunny-lucking commented 3 years ago

有个可选链操作符( ?. ),

这是因为项目安装了@babel/plugin-proposal-optional-chaining,不是原生支持的特性.

多谢提醒!