乐趣区

使用React和Node.js制作网易云音乐App的一次总结 【实现全部基本核心功能】

一、技术选型

语言选择:

JavaScript
TypeScript
最终选择了 JavaScript 作为开发语言,一开始尝试使用 TypeScript,但是由于是移动端,antd-mobile 的库与 TS 有那么一些不兼容,官方文档上也没有像 PC 端那样推荐你使用 TS,踩过坑,于是换回了 JS。

开发环境 create-react-app 目前最好用的开发 React 环境

UI 组件库的选择:

Swiper.js 个人认为 Swiper 在对 Vue 的支持会更好,官方文档上也没有明确支持 React

antd-mobile 阿里旗下的库,PC 端的功能强大,但是更多使用场景是 TO-C,这里只做尝试使用,个人觉得 TO- B 项目不建议使用,本项目中也仅仅使用到了一点点这个组件库的功能

接口调用,网易云音乐的原生接口,Music 和 Mv 都使用它的原生接口

开发所需的包

pubsub-js 对 state 的管理的包

react-router-dom 路由

antd-mobile 官方推荐的按需加载配置

less-loader 对 less 的支持

Node.js 端

express 框架 (有考虑 KOA2 框架,但是鉴于 express 的成熟性没有选择)

puppeteer 爬虫获取数据的包

ws 模块,webSocket 的使用

request-promise-native,使用 Node.js 的服务器无跨域特性发送请求调用网易云音乐接口

版本控制工具,毫无疑问使用 Git

包管理器,这里使用的是 yarn,不是 npm

技术选型对于后期迭代非常重要,个人建议大项目上 TS 和 React。
二、项目所需要到的知识点
由于是在移动端,H5 , C3,事件处理还有性能优化考虑的问题要更多

基础知识点

主打音乐类 APP, 需要使用到的 H5 标签,<audio> <video> 等

C3 技术,canvas,Animation 等制作一些动画
对移动端的一些特殊情况,比如圆角过圆等的处理。

JSX 语法,比较基本的 JSX 语法一定是要非常熟练的

ES5/6 TypeScript, 为什么需要 TS 的知识?为了看懂源码,更好调试代码。

React 知识点

React 的事件机制,原生事件和合成事件的触发哪个优先级更高,事件代理,事件派发是什么过程?

setState 的异步同步问题,其实就是上面的事件机制,这个问题遇到的还是非常多的,如果搞不懂,那么调试起来非常困难

Reat 中追求组件化,个人喜欢组件化到极致,这样方便调试,在使用 TS 和 React 配合时,调试真的非常简单

高阶函数的使用 React 中对于大量的重复逻辑函数,使用函数柯里化给予默认参数和封装成高阶函数使用

高阶组件也是用得非常多,对于一些重复逻辑的组件,我们可以封装成高阶组件,即传入值是一个组件,返回的是一个新的组件。

React V16 版本的 Fiber 架构,跟之前的区别?新版本的执行 render() 时候,是分割成多个小任务,可取消中断的过程,配合原生 JS 的 requestAnimationFrame(高优先级别的任务处理)和 requestIdleCallback( 低优先级别的任务处理), 达到性能优化的目的。

React 的 diff 算法,三种 diff 模式:

Tree diff 是优先对比两棵树的同级别 DOM 节点,所以尽量不要将 DOM 节点彻底删除,否则会让 React 的 render() 时间变长,具体在操作样式时候这点会非常明显,需要将一个元素隐藏时候如果 display:none,如果切换显示和隐藏特别频繁,那么会出现闪屏。

Component diff 是对组件的 diff,其实我们可以通过 shouldComponentUpdate 的生命周期函数返回值控制组件是否重新渲染,它的两个参数是 (nextProps,subState), 返回值是 ture 则重新渲染组件,反之 NO。

element diff,为什么在 React 中需要元素要有一个唯一的 key 值,因为底层的 diff 算法是四根指针,例如遍历两个元素 old element: A,C,B,D;new element B,C,A,D。如果没有唯一的 key 值,那么 diff 算法生成新的虚拟 DOM 节点过程是:发现第一个不一样,那么直接插入 B,C,A,D ; 但是如果有唯一的 key 值,那么 React 的 diff 算法会发现,里面有一样的元素,那么:插入 B,C 不动,插入 A,D 不动。所以唯一的 key 值多重要?

三、移动端的不一样
滑动时页面警告
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.
解决方法

* {
-ms-touch-action: pan-y;
touch-action: pan-y;
}

事件点击穿透,禁止一个事件的默认行为,对其手指抬起事件绑定逻辑。

antd-mobile 的按需加载需要配置更多,图标和功能也更少。

touches targettouches changedtouches 的区别, 处理一些复杂逻辑会用到
在对制作 SPA 单页面应用时,频繁切换的一些元素,做性能优化处理,利用上面提到的那些 React 知识点,不然很可能出现闪屏,用户体验感差。
在 http 通信时,如果要将返回的数据 setState,那么请注意 setState 的同异步场景,准确把控渲染和设置状态时间差逻辑,特别是多个请求,可以使用 `promise.all 或者在 setState 的回调函数中发送请求,定时器把控时间。

比如下面这段代码,需要发送 10 个请求并且将返回的数据整合,再把数组中的 10 个 promise 对象的值取出,设置成状态重新渲染。
` 首先在生命周期函数 componentDidMount 中 this.createSrc() 调用 `
reqMvList=() => {
const {data, src}=this.state;
const MvList=data.map(async item => {
const result=await reqMv(item.id)
const {data: {brs}}=result;
return brs[480]
})
return MvList
}
` 调用 createSrc 函数后会先调用 reqMvList 函数,拿到结果 `
createSrc=() => {
const result=this.reqMvList()
let arr=[]
result.forEach(item => {
item.then(res => {
arr=[res, …arr]
return res
})
})
` 上面将得到的 10 个 promise 对象中的值全部取出放到数组中 `
setTimeout(() => {
this.setState({
src:arr
})
}, 1000);
}
` 如果此时不加定时器,那么会先执行 setState 的代码,再去执行 promise.then 里面的逻辑,
那么其实状态已经更新完了,但是数据是后面添加到 arr 中的,所以会出现状态里面没有值的情况,
这里需要加一个定时器解决。`
本次构建过程中涉及到的一些面试题

http 的 ajax 轮询 长轮询 keep-alive 和 webSocket 的区别
如何将一个元素从页面上隐藏 根据场景需求,配合 React 的 Fiber 和 diff 算法机制使用
高阶函数,高阶组件,函数柯里化的使用
如何在一个请求回来数据并且在设置状态成功后发送下面的请求(优雅发送请求,平铺数据)?
手写一个 promise

promise.all 的使用

pubsub-js 的使用

React 的三大属性
对于高阶组件中的修饰器的使用,例如 @withRouter

cookie 和 cors 如何配合使用?cookie 可以跨域吗?

requestAnimationFrame 和 requestIdleCallback 的区别,在 React 的 Fiber 中

Node.js 端对 request-promise-native 的使用
现在的性能优化真的只看 DOMContentLoad 和 Load 的时间吗?可以从 RASI 四个方面去看待。

由于太晚了,面试题的答案会放出来。喜欢的朋友请收藏,谢谢。感谢 @xpromise 的技术支持

退出移动版