本文首发于:github.com/bigo-fronte… 欢迎关注、转载。
一、整体认知
1. 团队协作
在家或者在学校的时候,一直都是自己独立地开发项目,前后端都是自己一个人梭哈,怎么写,任凭自己主宰,在这种独自一人开发的模式中,对于团队协作模式可谓是一无所知。 后来,实习期间,经过几次版本的迭代之后,对于团队协作开发模式已经有了整体上的认知。例如一个项目的需求周期是怎样的:
- 需求评审
- 需求进入排期表
- 进入开发
- 前后端自测联调
- 自测无误后发起提测
- 测试提bug(最好没有bug)
- 要是有bug就修复
- 发布上线
在这些过程中,前端主要发挥什么作用,也有了大致上的了解。
2. 工程化的理解
在实习之前,我对于工程化的概念比较模糊,更多的是局限于 组件化,模块化 等。虽然之前平时逛有一些技术博客网站的时候,会看到一些类似 《xx走进工程化xx》《xx自动化xx》,我也迫不及待地点进去了,但对于我来说,确实只是玄学,于是我会马上关闭,然后深呼吸,接着,选择《js的基本类型》《css 选择器的顺序》等文章 津津有味地读起来。
实习期间,进过几次发版之后,了解到,原来上线,是不需要手动把资源上传到服务器的。用名为jenkins 的持续集成/持续发布的工具实现起来要方便许多。
慢慢的刷新了我对工程化的认知,工程化远不止组件与模块,原来它还包括 规范化、 持续集成、自动化构建、自动化部署、自动化测试等等。
工程化是一个很大的话题,希望随着实践经验的积累,能够慢慢地掌握精髓,后面也需要找些书来看看。
3. Git操作
除了写代码,我想我最大的一个难题就是git操作了
在实习前,我会的git命令是这样的
git pull
git add
git commit
git push
我的分支是这样的
master
在创建分支的过程中,踩了不少次坑,我终于摸出了一条正确的道路,比如 在分支的管理上,我的经历是这样的:
-
刚开始:一个项目对应一个文件一个分支(所有需求都在一个分支里完成)
-
后来:一个需求对应一个文件一个分支(导致分支间的关系不明确)
-
最后:同个文件夹,一个分支对应一个需求(目前来说,还没发现什么问题♪(^∇^*))
除此之外,由于有时是另一个需求还没开发完,就要开发其他需求,所以是好几个分支在同时开发的,这时难就发生了意想不到的事情,例如下面我所遇到的:
更改已经被 push ,但是不是需要的
在分支上开发的过程中,添加了一些错误的文件,或者错误的修改之后,把本地的修改给add,commit,并且push上去了,但是这些修改是不需要的。怎么回退呢?
- git本地回退到指定版本后,按以往的提交顺序进行提交时会报错
- 这是因为gitlab已经在你提交历史前面了,你无法把push过的再次push进行覆盖,这个时候加个参数–force就行
- 加完参数后发现gitlab不允许强行push到保护的分支上,解决方法是让项目的管理员暂时在gitlab上把你要提交的分支设置为不受保护的分支即可
除此之外也了解到有git revert
这个方法
正在发开当前分支,但需要切到其他分支
开开心心地在新的分支上开发新的功能,这时候,产品经理说 其他分支有bug或者修改需求,需要赶紧处理下,这样的话,就需要切到目标分支了,那在当前的分支上所作的修改该怎么办呢?
我想到的是commit,但其实还有更好的方法
- 使用
git stash push –m”message”
保存当前的修改 - 切到目标分支修改bug,修改提交后切回原分支
- 使用
git stash pop
还原
这里需要注意的就是保存当前修改的时候,最好是添加上message,而不是简单的git stash,因为git stash 一旦多了之后,就会记不清是做了什么修改
在错误的分支开发了新功能,新功能还没有在本地进行commit(提交)
-
使用
git stash push –m”message”
保存当前的修改 -
切换到需要开发的分支
-
使用
git stash apply
应用修改
在错误的分支开发了新功能,新功能已经在本地提交了,但是还没有push到远程仓库
git log --oneline
先获取本次commit的hashgit cherry-pick <commit hash>
切到目标分支后将本次commit的修改merge到目标分支git reset <commit hash>
切回错误分支,回退到之前版本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'},
])
当然,有时候,你可能不想输出那么多列,比如,你只想输出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'])
实际上,除了console.table。还有其他的方法:
- console.info :与console.log 的作用是几乎完全一样的,也是在控制台中打印信息,只不过打印时的样式可能与 console.log 略有区别
- console.error:同样和console.log的作用几乎一样,不过会将打印的内容通过显目的红色标注出来并前面带一个 ×
- console.warn:道理同上,会通过黄色感叹号来高亮打印信息。
- console.time 和 console.timeEnd:两个方法是结合在一起使用的,他们接受一个相同的参数,输出两句表达式中间的代码的执行时间。
- console.count:会打印当前的打印内容,并在后面跟上该内容的打印次数。
说了这么多,除了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核心原理 :juejin.cn/post/689829…
5. React Hook 的学习
由于在项目的迭代中,新增的组件尽量是需要使用hooks来实现的,所以, 对react hook的学习也是必不可少的。
其中,学到了例如useMemo
,useCallback
等性能优化方法。同样,为了更好地使用,也通过看了些视频和资料,自己尝试着写了hooks中 常用的 hook的实现原理。
手写React Hook核心原理
结尾
学无止境,发现还有很多东西需要去学习,例如react fiber,react-saga,next.js,react 360等,在接下来的日子里,也需要努力学习啊!
欢迎大家留言讨论,祝工作顺利、生活愉快!
我是bigo前端,下期见。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!