本人亲历: React - UmiJS - Ant Design 从入门到放弃(终结)

作为一个不称职的后端程序员,由于一些原因,我被迫要学一些前端技术。入门React让我很头疼,虽说结构与后端基本一致,但写法与后端差距很大。我将个人学习路线和感想罗列于此,希望给后人以参考。如果认为和我的智商类似,可以跟着一块学。略过我走过的坑,会顺畅很多。

当前阶段:已入门,本文停止更新。

本人的js能力先交代一下,我知道jquery可以这么用 $("#id").onClick({ alert('ok');}),我还知道getElementbyId(),然后就不知道啥了。

2020-3-26 提示:时隔几个月,由于umi版本已经更新,根据我这个学习路径已经不行了。仅供参考。

混乱期:一个月

这个阶段大约持续好几周,先后看过各种文档。尤其让我困惑的两个点,一个是react大量大量的库,每一种库好像都要学,疯了吧。另外一个问题,js语法里面真是把符号用到了极致{{()=>}}这种写法我是服了。对于没有系统学过js基础的我,真是混乱啊。箭头函数是什么,js数组到底是用[]还是{},为啥函数还能简化,到底谁是对象,const不是常量吗,怎么都是变量还小写呢,三个点是什么鬼,export个鸟啊,bind你大爷,this你妹啊。

后来跟着做了一遍阮一峰的React 入门教程,还是摸不到头脑,头大的很。直到我找到了一个能听懂的视频。

懵懂期:一个月

重要视频:《React开发简书项目 从零基础入门到实战》

本人看的盗版视频,在此之前,我也看过一些入门教程,但是感觉只有这个视频能让我听懂。这个视频我认为是真正的循序渐进,从基础的react思路,进到redux,再加入其它中间件,到最后一个小型的项目实战。虽说中间也有一些听不懂的坑,但是填补这些坑也算是学习的一种很好的方式。语速有点慢,1.6倍速看是完美的。

由于我没有系统的学过什么es6的语法,我只了解一点js的语法,我决定直接根据视频来写,先练起来再说。看不懂的写法先不急弄明白,先写熟练了再研究。

我没有跟着作者做简书的项目,而是一直持续做经典的todo list。

  • 46集,停顿一次。练习了多次redux+react-redux+antd简单布局实现todolist的方法。没有加css、redux-saga或redux-thunk。

(react react-redux antd)

  • 56集,停顿一次,练习了多次在加入immutable+actionCreator+constants+axios+redux-saga的写法。

(react react-redux antd immutable redux-immutable axios redux-saga)

  • 加入 styled-components+pureComponent+react-router-dom(Link)+reducer中函数拆分+react-loadable+withRouter

做到这里,我发现2个问题:

1. 之前的todolist由于规划问题,后面加了很多组件后目录有点乱,所以后面要重新规划一下。

2. 视频中的React-router-dom已经升级,原视频写法已经不支持。

所以我决定重新规划一下目录结构,加入所有已经学到的库。做一个稍微正规一点的小程序,然后我把这个练习三遍,搞清流程和基本写法。

(react react-redux antd immutable redux-immutable axios redux-saga styled-components react-router-dom react-loadable)

其中一个大坑在于,antd 的 Table组件,里面的dataSource要传入数组,但是我immutable过来的貌似是个对象。我研究了好久写了一个转换函数才搞定。这个位置我还是有点疑惑,不过当下暂时这么处理吧。其余的问题,就是功能上还有点小瑕疵,也暂时忽略。

我也是懒+笨,实际应该练习五遍八遍的,结果我每次练一遍就要一上午或者一下午。

入门期:进行中

至此,原视频中的大部分知识点都覆盖掉了。我认为我对react的流程已经有了一个基本的了解。那么实际上我还有一些基础知识的坑要补。我想停一下,看一些以前看不懂的文档资料。再继续练习。

我觉得疑惑的点:

1. 为什么要用react,或者他的基本发展史。

2. es6 的基础知识

3. 常用的库有哪些

4. 重复代码貌似有点多,比如constants和actionCreator我认为可能还有更进一步的方案。

发现蚂蚁金服的一位react大神写过一系列文章,有点良心,那就开始吧:

《支付宝前端应用架构的发展和选择》 从Redux章节开始看,这里面引入了dva

《dvajs介绍、快速上手》    先写起来。

我一直以为yarn add 可以代替所有的npm install,所以自作聪明的把教程里面涉及安装的都用了yarn命令,结果运行失败了。那么后来我用npm install总报一个“npm ERR! Maximum call stack size exceeded”错误,后来把命令改成了cnpm,结果成功。

做完快速上手以后,浅显的理解感觉index.js像是一个流程配置,router.js就是分配访问地址和对应的页面,routes里面就是页面,model里面就是要动仓库数据了,components就是拆分的组件。OK继续往下看:

《davjs概念》   结合快速上手的例子,详细看了一遍。其中提到generator函数,所以继续:

《Generator 函数的含义与用法》 虽然有些还是看不懂,但是大概意思明白了,就是协程。OK回到dva,继续看文档:

《dvajs入门课》  最后的课堂实战是延迟一秒删除,好像就是在快速上手的例子基础上个加一个effects吧,研究研究怎么写。

我卡在了最后延迟的函数上,delay这个函数不会写,试了setTimeOut结果不好使。最后我谷歌了一下,找到了dva第一版的一个教程,原来这个延迟函数很特殊是什么Promise的。暂时不研究它了,直接拿过来用。这个练习就完成了。只能说,刚刚了解了一点dva的结构,还不算很熟。不看文档的话默写是不行的。劳逸结合,随后看点文档资料:

《dvajs知识地图》 这个里面好多知识点,又看到了很多新库,还有增删改查的方法。估计短时间内消化不了,收藏先,有个印象,后面遇到了再研究。

下一步我决定直接进军umijs,因为之前的很多资料实际也提到了umi,我浅显的认为,umi在dva基础上又做一层规划和封装。所以我估计它们结合起来用的几率很大很大,那么由于后续看到一些比较好的umi教程,所以直接走进umi的怀抱,又需要再回头来看。

《Hello!umi》  了解umi的初心,再看新版的发布说明

《发布 umi 2.0,可插拔的企业级 react 应用框架》 好,感觉高端大气上档次,那就开始吧

《UmiJS指南》 这个指南看完以后晕头转向的,有点乱,不深入研究了,等做实例的时候再回头来查吧。下一步看看umi和dva结合的基本方法

《Use umi with dva》 看到model的结构挺有意思,再看一点:

《使用 umi 改进 dva 项目开发》 很短,看起来umi在dva的基础上做了一些部署,可能后面不用看dva文档了。那么看了这么多文本资料,很累了,写起来吧:

《umi@2 + dva,完成用户管理的 CURD 应用》后来发现这应该是复制粘贴的过程,不需要手动写,因为写了也看不懂,代码太多。

注意:这个文章是新版,因为目前umi是v2,有一个针对umi v1的老版。我们下面会分别成为“原版”、“新版”。

坑1:不要选择安装TypeScript!!!..否则你会跟我一样做到一半然后泪奔重来…

坑2:新建users.js以后,访问失败,原来还需要把umirc里面原有的路由删掉才行。

坑3:Step 5中src/pages/users/model.js实际应该是src/pages/users/models/users.js

我做完Step 8分页以后,停一下,因为有点乱。这个教程只有代码,讲解很少。我必须捋捋,这些文件都是啥。首先要知道,没有做的工作有:删除、编辑、创建。

pages下面,页面即路由,这个懂。pages/users/下面分别建立了components/models/services。

pages/users/index是users的总渲染文件,统筹了。

pages/users/components/Users 这个大写了,看起来是组件。这个组件里面connect了,说明要和仓库交互。这是页面做的事情我尝试归纳一下:

1. 它构建了这个组件的结构,然后读取了传来的列表数据。

2. 它接收了dispatch,要派发一些action执行数据的改变。

3. mapStateToProps有点意思,首先在state.users里面拿出来三个值,文档里写的很清楚:users 为 model 里面的 namespace 名称。

再看一点写法上的细节:

1. {list:dataSource}的意思是给list弄了一个别名叫dataSource,然后再用的时候就用dataSource。这样做的意义可能是为了更清晰的表述吧。

2. pageChangeHandler里面dispatch的action是routerRedux.push(),这个明显是不走reducer的,因为它没有对应reducer里面的名称。但是它的意思倒是理解,看起来是跳转到pathname并且传query里面的东西传过去。后来发现这个有点特殊,它是从dva/Router里面拿出来的,那么实际上是一个叫react-router-redux里面的写法,文档在这里 https://github.com/reactjs/react-router-redux,文档中找到这样的解释“ push: Pushes a new location to history, becoming the current location.” 。这个 用法记住就可以,push并且有query。然后延伸的看一下,在/pages/users/models/users中,通过subscriptions监听到了页面网址变化,发现网址是/users的时候,派发一个名为fetch的action,并且把query传过去。但是写法是 { payload: query },这是为何呢,这不是别名的意思么,payload的别名是query,所以实际上传的是一个东东吧,就是同一个对象。然后这个fetch函数到了effects那里,就是原来的saga,它接收了payload这个对象,并且设置了默认值的page=1,同时接收了另外的call, put对象。

之前只用过一次saga里面的call,还是不太懂,查查。貌似结构是call( function, …arguments ),第一个参数接收一个函数,可以是普通函数,也可以是generator函数,第二个就是传的值了。这里面const { data, headers } = yield call(usersService.fetch, { page }); 那就是说:召唤userServer下面的fetch函数,传page过去->等待返回结果后->抽出data和headers过来。这一串骚气的小操作。顺线追踪,看/pages/services/users。在后端services一般是指对外提供调用的接口层,我估计这里的services应该也是类似的意思。这里导出了一个fetch函数,默认值page=1,然后request一个翻页地址。然后request还是从/src/utils/request里面拿过来的。从原版文章中获知,这个request.js应该是umi v1中自带的,可是新的umi v2里面是没有的。原版是在其基础上,加了一个header叫x-total-count来获取记录总数,也就是在翻页的两个页面间传递记录总数。这个request函数的返回结果有两部分,data是请求那个url的返回结果的json数据,另一个header是头部

回到model的effects,继续这个fetch函数,从之前的service层搞出data和header以后,put了一个action,名叫save,值可多了,有data数据,有记录总数,还有当前页码。这个save到了reducer里面,把payload直接展开,把data别名叫list。但是最后的return我没看懂。return { …state, list, total, page }; 这个…state是之前的所有数据,再来个逗号然后list。按照我的想法应该是return { list, total, page } 就可以了哇,为啥要…state呢,不懂。这个问题先暂时搁置,另外这个type叫save也有点奇怪,或许跟后面的添加有关,暂时搁置。

model彻底看完了,捋捋的话,基本所有文件都看了一遍。组件触发handler,导致页面跳转。subscription监听到了页面变化,告诉effects去接口异步取数据,然后去service取数据回来,再派发给reducer返回数据。

然后我还是疑惑的,流程看清了,但是数据从哪儿取回来的,我没弄明白。当page=1的时候,没看见读数据库哇!我可能有病,后来想通了,之前配置过接口,service层里调用的地址是/api/users实际直接取到远程数据了,而且分页取到不同数据也是人家远程api定义好的。我还在琢磨数据从哪儿来的,服了。

然后我准备思考以下model的结构,有reducer,有effects,有subscription,互相配合,各司其职。关键问题是,它们都是什么工作。有点乱,我准备用大白话总结一下,哪怕不太准确。

subscription:监听派发,用于初始化数据源。

effects:异步派发,用于通过call接口把数据传回来然后转发。

reducer:处理返回,用于把传过来的各类数据各种处理,然后返回。它是state的最后一步。

那么再思考一下删除,删除应该只用reducer就行了,组件里面派发一个action,传id过去,然后在reducer删除就可以,是不是,再想想,应该是。想自己尝试写一下删除,然后和教程做对比。但是,刚做就发现问题了,怎么在组件里面派发action呢?完全不知道。所以还是直接看教程吧。

做完以后发现,噢,原来所有和数据有关的工作,都要走接口,所以必须走一遍effects来异步派发,然后services来处理调用数据。另外一个问题是,effects里面用了select,而select就是获取state中的数据,一般使用方法就是const id = yield select(state => state.id); ,ok了。

下面是编辑,编辑的话如果按照我的智商来想,就是比删除多传了几个字段。继续看教程吧,估计肯定还有自己想不到的点。

天呀,这个独立的modal组件,这代码好复杂。这么个简单逻辑写这么多代码也是醉了。不研究,继续看创建,估计差不多了。

由于复用了modal组件,创建代码相当少。我的天啊,我此刻感觉到智商不够用。人家作者这个原文的标题可是“12步30分钟”,我光粘贴代码和想其中的流程就花了3天时间。而且关键是3天以后的我,还是根本默写不出这样的程序。

新的一天,我洗了洗头,仔细一想,其实一共就十个文件。增删改查这四个重要功能都有了,死活也要把这个例子弄明白,然后自己必须能背写出来。那么我决定余下三天,就只啃这个例子了。那么就加油吧!

啃了5天以后,这个流程基本弄懂了。但是原代码中,我认为有一个位置不好理解,自行改一下。就是UserModal组件中传入的onOk,我把它改为okAction,用以区别Modal组件中中的onOk。另外request.js我没有背,然后头部模板我没有写,total传值也没有仔细研究。

接下来,我想回头重新看一遍dvajs的知识地图。https://dvajs.com/knowledgemap/,不错,有些地方看明白了。我决定用umi做一次todolist的案例。

做完发现和之前的增删改查基本类似,没啥大提升,建议忽略。往下走。

到此为此呢,我想umi/dva基础的东西大概明白了,至少流程清楚了。下面我想直接跳跃到ant design pro里面,看看其中的结构。但是我知道,antd pro好像是基于typescript的,而且根据云谦对前端未来的展望,typescript将成为主流。所以我必须要先学一下typescript。然后发现要学typescript要先学es6,好吧,所以我又找到一个es6的视频教程,这个我觉得也很不错,推荐!

《开课吧-石川-ECMA Script6.0》2017年的视频,同样我是看的盗版。后来惊奇的发现在B站有分享,传送门>>

我本人是看的在其它途径上找到的盗版视频,跟B站的不太一样,B站的似乎还有直播的录像,我这个没有,我这个只有18节课程,最后一节的名字叫“18 ES7预览.mp4”。

看完以后,我发现石川又成立了自己的品牌叫智能社,然后在腾讯课堂出了新视频,so,我建议如果后来者看这个新的:传送门>>

在发现这个新视频以后,我又加速看了一遍新课程。其中增加了babel的命令行添加,打包。增加了async/await,然后也简单介绍了一下ES7/8。

看完ES6以后,我决定学习Typescript,之前看过的两个视频的作者讲的我感觉不错。选选吧!慕课网的Dell他的TS视频是226块钱,14小时课程。智能社的Blue呢,腾讯课堂1毛钱2小时课程。然后我又看了其它几个课程的介绍,感觉优点崩溃。 这么复杂么,感觉像是一门新语言!竟然要十几个小时学习。初步打算先花1毛钱让在石川那入个门,然后再学一下《极客时间-TypeScript开发实战》。

实际执行路线是这样的,首先我看了石川的第一集,发现确实有点复杂。然后我直接看的《极客时间-TypeScript开发实战》,看了前三个视频,发现讲课这大哥是个学术派人才,我相信课程相当好,但是太枯燥了。后来一咬牙还是准备学慕课网Dell的《TypeScript -系统入门到项目实战》,虽然课时有点长,但是我认为Dell会带我循序渐进。

然后我用了1天时间做了Sublime Text编辑器的一些配置(个人还是建议用VS Code,我这个属于个人爱好)。然后就按部就班的看视频了。

经验1:装饰器后面不能加分号,加了就报错。

经验2:装饰器参数名不能改,我想把key改成method结果报错(课程6-9)

在看完前6章视频以后,我发现3、5章的爬虫项目完全忘记了。可能跟我中间中断了10天左右有关。这可怎么办,只好回头再去做一遍。要不第7章明显看不懂了。在此期间,umi出了v3版,全部使用typescript改写,看来学ts是正确的。硬着头皮搞吧,还有什么选择呢?

看完了第七章,并且做了两遍爬虫和登录的程序,算是入门了。实际上我做的时候,是抓取的我这个博客首页右侧的标签聚合。感觉不错。挺好。说个体验,如果不是有这个实战项目的话,以前学过的知识点真是忘得一干二净。下面继续看第八章了,是个react的界面,非常棒,非常切合我要学的东西。那么就看起来吧!

( 偶然发现有关TypeScript知识点的一篇可能不错的文章:https://segmentfault.com/a/1190000020300143,先存着,以后看 )

第八章看完了,需要有react基础,看的还真挺顺畅。但是说实话,后面代码有点复杂,有些逻辑需要想想。然后我对泛型那块感觉有点没头脑。决定回头再去看一眼第二章一些忘掉的知识点。

简单做个小笔记,掺杂了一些个人想法:

类型保护:就是一个变量进行了多个类型(number|string)第一以后,用instanceof来判断:if(name instanceof number)…

泛型的意思,实际是特殊的类型,英文generic意为非常规,非通用的。和普通类型使用一样,可以使用在函数参数,也可以使用在返回值中,但是用之前必须在函数名前面,或者类名前用尖括号进行声明。function test<T, P>(){}  class test<T, P>{}。使用的时候,可以直接调用一个普通类型test<string>(),也可以额外定义接口,然后继承接口test<T extends Item>。复杂一点还可以循环test<T extends keyof Person>。总体来说,泛型就是要做到对类型的灵活控制。如果遇到有关类型的问题,我一定要往泛型这块想想。

类型定义文件.d.ts,其中需要declare namespace xxx,然后里面是type=string这样。

我突然发现应该一边学,一边记笔记啊!不复习的话记忆力等于零。

随后我简单看了一下《极客时间-TypeScript开发实战》的目录,感觉这个老师讲的比较细节,很多知识点在dell的课程中没有。但是这个老师后面可能结合的是vue讲的,而且知识点之间没有穿插实战。我认为,这个视频也值得看,但是为了加快进度,我决定暂时搁置。

下一步,我要回到umi当中,由于学typescript时间过长,umi有点忘了,我想回头再看看,毕竟学过了es6,学过了typescript,很多东西可能有新的理解了。正好我可以利用这篇博文,了解到我之前都看了哪些文章,再从头来一遍。就从“入门期”这个为止开始。

《dvajs知识地图》 这个差不多能看懂了,好重要,肯定用的到。

UMI文档》 Umi3的文档,新的了,以前没看过,好重要。

好,光看不练肯定不行。既然umi出了3版本,那么我就准备用过umi3+dva+typescript重新做一个用户列表的增删改查。这个要是能做出来的话,我说自己已经入门umi应该没有问题。实际上由于typescript学了太长时间,dva和umi怎么用已经有点模糊了。不过不要紧,一边做,一边查。那么就开始,探索一下。

经过一个月的探索,我终于写完了2次基于umi和dva的用户CRUD的列表。我决定录制视频课程,帮助新手略坑。随即又用一个月的时间准备、录制、剪辑,在B站发布了我的第一个视频作品:

《视频课程:Umi3+Dva入门实践 完成用户管理的CRUD 应用(2020)》

至此,本文停更。

点赞