卡林塔:学习随笔-待整理

371 阅读26分钟

计算机书籍分享

问题记录/to do

  1. 不能连续定义并执行2个匿名函数,也就是说以下的代码是会报错的:
(function() {})()
(function() {})()

问题记录 - 已解决

  1. es class部分重新学习后 - 实现下继承/静态变量等;
  2. webpack: 用css-loader开启css-module后,使用mini-css-extract-plugin进行css的split,结果打包出来的文件index.html在浏览器中没有c s s引入的效果?
    • 调试

      1. 先检查js文件中有没有引入scss/css文件;
      2. 然后去浏览器chrome调试面板中:检查元素,查看对应的dom元素上有无对应的class以及对应的style栏中有无样式文件中的样式;
      3. 去chrome中的source中去看打包生成的源码:去分离打包后的css文件中,找下对应的class名,嗯嗯:是一个由css-module机制生成的随机字符串作为类名(该随机类名在打包后的js文件有与源文件class名的对应关系)
        1. 此时我们修改该随机类名为index.html中的类名,样式文件生效了;
    • 解决方案
      1. 搞清楚问题,解决起来就容易了:
      2. 方案一:在我们的jsx文件中,以css-module要求的形式使用cla ss名
      3. 方案二:关闭css-loader开启的css-module模式(默认就是关闭的):
      // css-loader的options中
      modules: false // 删除css-module
      
    • 延展
      1. 关于class名重复问题,除了css-module,其实还有css-in-js的方案:styled-component,也是不错的,更符合组件的设计理念(js + html + css杂糅在一起)
  3. 前后台通过json通信:
    • json本质上是字符串,就是js对象字面量的字符串表示方法
    • json中本质上可以是数字,也就是不一定是字符串,可以是其他任何类型
  4. for of / for in - 对象的遍历方法
    • for in 会遍历原型中的属性,例如这里:
    (function() {
      var arr = [1, 2]
      Array.prototype.pm = 456;
      for (let i in arr) { console.log(`[${i}]: ${arr[i]}`); }  
      for (let v of arr ) { console.log(`${v}`); }
    })()
    
  5. 循环引用 - 模型是怎样的 图??
    • 描述:
    var a = { b: 1 };
    a.b = a;
    console.log(a);
    
    • 解决方法:我们在深拷贝中如果遇见了循环引用而未作专门的处理,就会无限递归造成内存溢出,那么处理的思路呢?应该是:
      • 利用缓存的思想,用一个数组缓存已经遍历过的引用,每次引用时只要查找缓存中是否登记过;如果已经登记过,则就不需要再递归了;
      • 循环引用 多次引用的地址归为一个即可,不需要递归挖掘:因为对于循环引用的处理就是:避免递归,只在第一次递归引用,后面就不再递归了

技术选型

  1. xlsx - 处理表格相关的nodejs模块

js

  1. url编码-阮一峰
    1. 也就是在利用url进行参数传递时 我们最好作一下encode的处理,主要是参数中可能有一些非英文、数字等字符,这些字符如果js不同意编码,就会被各个浏览器按照自己的标准进行解析。用js统一编码处理,则后台使用对应的解码方式解码即可,只对接js一家统一的编码方式,而不用考虑各个浏览器自己的实现情况了。

函数式

  1. 函数式中的高阶函数也就是可以return一个函数的函数),一般我们的柯里化,AOP编程也是基于return一个函数,那么,本质是什么呢,其实就是函数替换,用return的这个经过加工的函数给一个变量,我们用高阶函数生产的这个新函数,实现一些被加工函数作为参数传给高阶函数)所没有的功能。

感慨&顿悟

摘录

  1. 组件化:抽象粒度重构:取得平衡:既要考虑不能暴露太多的细节给最顶层页面控制,也要考虑组件嵌套的深度不宜过深:通信也是一种开发成本:顶级的父组件更多的是容器顶层样式控制分发信号量(一般通过props传递下去)
  2. vertical-align和line-height
如果快速掌握和理解这些行为表现呢?
就我个人而言,从两方面入手:1.情感化认知;2. 具象化思维。

例如,我称vertical-align和line-height为好基友
(包括以前称浮动和绝对定位是兄弟),就是“情感化认知”;
而这里的「幽灵空白节点」就是“具象化思维”。
  • 有时候感觉,之所以能复用,就是因为有名字啊:命名其实对很多事物发展很有帮助

关于学习方法

关于读书

  • 应该要对知识点以Blog的形式进行输出,尤其是经典知识点
  • 也就是反馈式学习,有没有读者不要紧,一定要尽可能输出
  • 那些手册中能查到的,或者记忆性的,就不要复述了,API随时会变,要去深入理解背后的设计机制,实现原理这样子的东西
  • 以不变应万变,重视原理,要不然只会疲于奔命
  • 关于阅读,有时候可以出小声读可以有助于我们集中注意力,效果会好一些

关于算法

  • 学习算法的本质是什么??是一种训练。
  • 设计一个好的数据结构,再加一些算法,就好了
    1. 所以任何算法,都应先设计好数据结构(数据结构我们可以从基础/元数据结构加工/改造/组合而来),让数据结构本身描述问题模型,然后再组合使用相应的算法或者设计更优的算法(如果写算法,连我们要操作怎样的数据都不清楚的话,算法从何谈起);
    2. 好的算法和好的软件一样,是迭代而来的;
  • 一定要学会思考,去分析问题+条件+目的,然后尝试给出一步接一步的方案
  • 另外,不要思考不通就一下子把答案看完,可以看一部分,提示性的,剩下的自己继续思考,最后再对比自己的方案和主流方案,有利于弥补思维缺陷和提醒思维定势
    1. 再到后面效率越高的排序,要有“辅助函数”的意识
  • 递归

    • 递归终止条件 - 就是在满足某一条件的情况下 不再调用自身 而是返回一个‘正常值’(我的理解就是:可用于计算的值)
    • 递归的本质是什么??就是栈,递归的过程就是入栈(父函数到子函数) -> 出栈执行(子函数返回值到父函数),每一次出栈执行,我们都把结果用临时变量记录作为栈中下一个要出栈执行的函数的参数;

关于设计模式

订阅模式

  • 事件队列,只是订阅模式的一种应用;
  • 订阅的本质:就是把给定的一个回调添加到某一频道的队列中去;后面的触发以及管理,都通过该频道;

状态模式

  • 状态模式 一般而言我们需要和策略模式作横向对比去理解
  • 状态模式中,一般而言,状态的转化是内置的,也就是:
state-A -> state-B -> state-C -> state-A
  • 状态模式 我们可以提供一些修改状态的函数,避免直接操作可能会出现的失误,例如把状态编织到某个状态链

前端js手写

  • 节流和防抖的应用场景区别
    • 防抖和节流都是限制某一时间段代码执行的频次,但是实现原理不太一样
    • 防抖,可以保证在delay内不会执行2次代码,也就是某一段时间内只执行一次,而且是最后一次的代码会被执行;节流,保证在一个时间间隔内只执行一次代码,而且是该段间隔中的第一次的代码;
    • 防抖适用场景:

      1. 提交,只提交最后一次;
      2. 输入框联想,避免频繁联想,而是在停顿输入后一个delay内去请求联想;
      3. 例如:你正在适用掘金的编辑器,在你输入后的一个delay内,会右上角会显示自动保存为草稿,这里必然用的是防抖,否则频繁请求,会让服务器的压力很大;
    • 节流适用场景:

      1. resize、动画、滑动条事件(me:因为是间隔执行,就可以认为是‘均匀’的,对于动画等同时强调流畅性的场景)
  • 深克隆
    • 克隆中用的很多的就是类型判断,Object.prototype.toString.call(变量),就可以输出每种类型特定的字符串,用以判断类型
      1. 例如:
          let arr = [];
          Object.prototype.toString.call(arr) 
      

关于原理

js原理

  • js运行机制

关于源码阅读

  • 对于比较长的代码,我们可以分块阅读;例如:
    // 特殊值校验
    // 主体程序
    // 辅助函数
    // 返回值

style

CSS

左侧固定,右侧自适应布局

  1. 布局的核心就是width:
    1. block元素(未设置宽度!!,不要设置宽度,会破坏block元素自身自适应的特性),其特性是会填充整个容器
    2. 固定width的侧边,用float + 固定的width, 脱离文档流,使得自适应的block可以在同一水平排列;
    3. 自适应的block部分,两边的side通过margin空余出来给脱离文档流的side使用;
    4. [left-side: windth: 200px; float:left;] + [content: margin-left: 200px; margin-right: 160px;] + [right-side: width: 160px; float: right]
  2. 利用了block水平元素宽度可以随父容器调节流动的特性
  3. 参考

BFC

  • BFC特性
    1. 包裹浮动元素
    2. 解决margin叠加
  • BFC启用
    1. overflow 设置为非visible的值
    2. position:absolute / fixed
    3. display: flow-root(支持有限)
    4. 创建table-cell表格相关
    5. column-span:all 创建多列布局

Flex

  1. 定义为Flex布局的元素如果不给明确尺寸,会自动伸展到和容器同一宽度

SCSS/SASS

HTTP

连接的本质

  1. 连接的本质,其实就是在通信的2端保存了对方的地址等信息,这样下次消息过来的时候,服务端依然知道要讲该消息的应答发送给谁,这就是连接。这个词有些误导,不是网络链路上的什么保持,而是服务器内存中对应答地址的保存。肤浅的理解,没有特别探究本质。

http 鉴权

  1. http 中 网关工作在网络层,也就是是说http的连接是在不同层,网络节点/应用因为其处理的网络协议和层次不同,工作在不同层,也就是网络有时看起来是平面化拓扑,但其实是有层次 立体化的,不同节点处理不同层次的内容
  2. 401 是用户身份认证:是否为通过认证的用户
  3. 403 是当前用户权限认证:当前用户是否拥有某权限

缓存

  • 缓存在网络结构中,是一个节点,就类似于代理那样(当然,浏览器也可以具有缓存能力,相当于给自己的通信增设了一个缓存节点);
  • 相比于代理,缓存具有将服务器端资源缓存到自己本地的能力,并通过一系列Http指令/机制(例如“新鲜度检测”)来确定是否给客户端提供本地的缓存资源;
  • 本质上是一种网络效率/资源优化机制;
  • 缓存的核心:新鲜度校验;
  • 新鲜度校验一般基于两种机制:
    1. 缓存资源的时间属性(上次修改时间等)
      • Expires
      • cache-control: Max-Age
    2. 缓存资源本身实体的属性(ETag(Entity Tag)等)
      • Etag
  • 新鲜度校验一般发生在缓存节点和服务器节点之间,通信使用的是http 1.0 和 http 1.1为缓存提供的一些首部属性;
  • 缓存的控制:主要由服务端(开发者可以通过服务器配置文件)控制,当然浏览器也可以通过一些操作(Refresh)去发送一些带有cache-control的请求指令,另外文档本身也可以设置一些缓存指令:例如在html文档中,HTTP 2.0 提供的来分配http首部信息(但是这个方案性能和支持度都不好,不建议使用)
  • 浏览器缓存:其实是浏览器本身扮演了一个缓存节点来提供自身和服务器之间的缓存服务;
  • 缓存:缓存资源 + 缓存标识
  • 我们可以将多个缓存指令配合使用
  • 浏览器缓存策略:
    • 强缓存
      1. Expires
        • HTTP 1.0
        • 一种兼容写法,本身是被淘汰的机制;
        • 优先级低
      2. Cache-Control
        • HTTP 1.1
        • 响应头 + 请求头 都可以用
        • 优先级高
    • 协商缓存
      1. last-Modified
        • 响应头: Last-Modified
        • 请求头: If-Modified-Since
        • 优先级低
        • 弊端
      2. ETag
        • 响应头: ETag
        • 请求头: If-None-Match
        • 优先级高
  • 浏览器2种缓存策略比较:
    • 性能:强缓存好
    • 精确度: 协商缓存高
    • 优先级: 协商缓存高
    • 另外:强缓存由服务器通过强缓存指令对缓存进行设置,所以是一个单向的,协商缓存则是由一组:响应头指令 + 请求头指令 配合使用的;
  • 关于浏览器缓存

跨域

jsonp

  • jsonp的原理就是向后台通过url参数(一般约定为callback)传递一个回调函数的句柄(函数名),后台在通过请求信息(参数)计算处理后在响应中传回一个拼接的js(callback(data))

React

关于React的diff算法

  • diff的概念:比对两棵树以比较有效率实现ui同步。
  • 算法实现

    • 原来的算法为对树(render方法会生成由React自定义数据结构(核心属性有type,props,children)的virtrualDomTree)的递归比较,一层一层比较,先比较父节点,再比较子节点
    • 每一层中的比较中,对于当前比较节点,按类型分为:React元素或者原生DOM节点
      • 对于Dom节点比较,先比较类型type
        1. type不一样,则直接从该节点为顶点销毁子树(并销毁其状态),然后用新生成VirtrualDomTree替换这一部分;
        2. 如果type一致,则比较其props/属性,只更旧树上的变化的属性;
      • 对于React元素的比较:也是先比较type,
        1. type不一致的和Dom节点的处理一样,也是子该节点以下销毁旧树,把新树替换上去;
        2. type一致的,保留旧virtrualDomTree的实例,然后先把新树的props通过比对更新到旧树上,然后对新旧两个树的children列表进行比对(这里就是有无key的影响体现了,当子节点列表有差异时,会比对产生一个mutation),然后递归去render children(子节点)这一层,diff规则和上一层,一样;
    • 那么,基于以上,我们就可以理解当列表中两个元素相互交换位置时会发生什么??key在算法中发挥作用主要是当前层/节点的children如果是列表的话,需要首先对这个列表过一遍,以保证列表的增删改/次序位置/同节点属性等保持一致:
      • 这个当然要分有无key的情况来讨论
        1. 有key的情况下,就会用key来进行当前节点唯一性的标记,在列表比对时,用key来代表节点进行children的列表比对,key相同的保持旧节点实例不变,只比对变更属性
        2. 无key的情况下,就是普通节点按次序比较了,例如2个节点仅仅是调换了一下位置就可能会把旧节点销毁了
      • 另外关于index不适合作key的情况为什么(其实key本质上是列表节点的一种身份标记,可以告诉react的diff算法,用来标志新旧两棵树上同一个列表节点,通过比对key,用key可以区分,那些是旧树就有的节点,哪些是新树新增加或者删除的节点,key相同的情况下,首先是保留旧节点实例的
  • key的来源(原则:保持列表内唯一即可)
    1. 来自模型(一般源于数据库)的id值
    2. 可以用值的一部分利用hash映射算法生成,当然hash 要足够优
    3. 在不牵扯顺序调整的情况下,数组index也可以胜任
    4. 对了,说到对唯一性的标识,怎么能少了es6的symbol呢

React-router

React 小细节

  • 刚用react那会儿对es6也不算熟,当时就感觉,为什么
<Todo onClick={(e) => onClick(e)} />
// 在不指定传参的情况 与下面语句基本相等 ??
<Todo onClick={onClick} />

今天盯着看了会儿,这不就是一个函数表达式吗??眼拙啊

onClick={(e) => onClick(e)}
// 等价于
onClick={function() { return onClick(e)}}

Redux

  • 关于react + redux中组件的划分:尽量把视图容器分开来设计组件,之间可以通过props来传递信息:
    1. 容器组件,凡是直接与Redux的store打交道的都是容器组件,一般都是经过HOC connect包装后的组件
    2. 展示组件,纯展示性的组件了,一般就是函数组件
  • 在面对具体业务时,思路:
    1. 我们的首要任务是根据任务模型设计state模型,就是设计reducer,最好的方法就是我们先写一个defaultState作为默认值,也会对该view/功能对应的state又一个比较直观的认识;
    2. 然后思考我们需要哪些操作(action.type),当然这些共用型的常量肯定要用大写的const常量,并且在一个文件中集中配置管理
    3. 然后就是设计action了
    4. 最后把我们的reducer 通过combineReducers来组合起来,然后交给createStore即可;
    5. 然后,进入我们业务设计部分,这部分就分为容器组件展示组件的划分了
    6. 考虑:数据的交互其实是用户view都和store进行交互,读写数据,这是设计的一个思考前提,也就是我们要了解这一功能,数据需要从哪里来,到哪里去,触发什么;
  • 话说,Redux完成一个功能所需要的模版文件太多了,而且不直观,业界有mobx这样的替代解决方案,但也有其一点点局限性,嗯嗯,给Redux做个辅助库,这一块找时间值得思考尝试下

使用

  • Switch会只渲染第一个path属性和loaction匹配的

React-router 源码阅读感悟

  • Just Component(一切皆组件)
  • 组件封装了path和component之间的映射关系,而path,是标准URL中的一部分;
  • 传递给组件的props.history.action属性,用法举例:
    • 翻页动画:我们可以根据action的值来判断是POP 或 PUSH 从而采取不通的翻页动画

Vue

关于组件化的认识(摘自Vue官网

一个重要的事情值得注意,关注点分离不等于文件类型分离。在现代 UI
开发中,我们已经发现相比于把代码库分离成三个大的层次并将其相互交织起来,
把它们划分为松散耦合的组件再将其组合起来更合理一些。
在一个组件里,其模板、逻辑和样式是内部耦合的,
并且把他们搭配在一起实际上使得组件更加内聚且更可维护。

Vue组件

  1. Vue组件本质上是一个有名称的Vue实例,特点是可以通过名称进行复用
  2. 任何Vue应用,都有一个根实例、

H5 app

高清图显示方案

  • 提供高清图:位图像素得和物理像素的数量一致,那么,当dp r=2时,css像素(200 * 300)的img,实际上是由(400 * 600)的设备像素提供的显示的,那么,基于位图像素物理像素数量一致才不会发生就近取色的算法(会导致模糊),所以,我们要针对dpr = 2 && css(200 * 300)的img 提供(400*600)的高清图
  • 所谓的高清适配,就是让图片尺寸尽量接近设备的物理像素值
  • 其实 一套高清图就可以,但是给不同的设备准备不同的图,因为不高清的图,可以节省流量和带宽

dpr的获取:

  1. 要理解,dpr = 设备物理像素 / css像素
  • js:
    window.devicePixelRatio
    
  • css媒体查询:
    @media (-webkit-device-pixel-ratio){}
    @media (-webkit-min-device-pixel-ratio){}
    @media (-webkit-max-device-pixel-ratio){}
    

设计稿还原

  1. 核心就是等比还原
  2. rem适配
  3. 其实rem适配的道理也很简单,就是通过动态计算并设置font-size,保证用0.01rem等比描述设计稿中的1px;(之所以选1rem = 设计稿100px,主要是因为100比较好计算)
  4. 既然是等比,就一定有一个等比的公式:
    1. 先假设我们要始终报纸用0.01rem = 设计稿上的1px
    2. 假设目前目标的设计稿尺寸为1px
    3. 设设计稿为常见的750px
    求font-Size?、
    解:
    公式推导如下(核心就是等比实现,实际元素宽度/实际设备宽度 = 设计稿中元素宽度 / 设计稿宽度):
    实际设备css:            设计稿上:
    fontSize * 0.01rem        1px 
    ————————————————————  =  ————————————————————
    device-width              750px
    推得:
    fontSize = deviceSize / 7.5
    
  5. 其实等比还原还可以这样理解:
    1. 设计稿尺寸 / 设备实际css尺寸 = 设计稿上元素的尺寸 / 该元素实际的设备尺寸
  6. 上面也可以这样理解,我们把设计稿的宽度分为100小份,每一份对应在设备上就是1rem。那么 好比 设计稿中100px的元素,我们就用1rem来在css中描述;设计稿中的1px,我们用0.01rem表示
  7. 关于rem适配 - 可以读下flexible的实现
  8. 另外 webpack等工程化中也会有很多工程层面的解决方案,与业务更加解耦,项目业务代码更加纯净;

vw适配

  1. vw就更简单了,也更推荐,毕竟rem的flexible.js已经在19年停用了
  2. vm就相当于不用我们动态改变font-Size以保证1rem = 设计稿width/100了

koa中间件编写感悟

  • 中间件 本质就是一个可以生成(返回)async函数的工厂
  • 每个中间件都可以拿到ctx和next由koa传入的参数,剩余的参数我们通过工厂传入,也就是在app.use注册时传入
  • 中间件要考虑next调用的时机,另外,如果要进行respoons的相关回复,我们要考虑header和body两部分的设置
  • 注意容错
  • 对于某些中间件中依赖的部分非核心非常用库,我们可以通过语义等将其当作黑盒对待,重视其输入和输出以及作用

koa-static

  • 核心库koa-send
    • 最核心的实现就是: ctx.body = fs.createReadStream(path)
    • 当我们需要获取file时,node提供的fs和path配合使用,威力很强

koa-views

  • 核心库consolidate源码
    • 利用requires这样一个自定义对象来缓存那些已经require 过的模版引擎:例如:
      • 处理ejs:
        var engine = requires.ejs || ( requires.ejs = reuiqre('ejs') )   
        

koa-router

  • Nesting routers is supported; - 支持路由嵌套
    forums.use('/forums/:fid/posts', posts.routes(),posts.allowedMethods()); 

工程化

webpack

  • 关于modulechunkbundle的区别
    1. module
      • module对应的是我们的源码文件,一般和源码文件一一对应,也是webpack的输入
    2. chunk
      • chunk是webpack按照配置的规则对module进行拆分合并后的东西,一般和module是一对多(也可以是一对一,看具体的split设置)的关系,我们的codeSplit规则一般就作用于module到chunk这个过程
    3. bundle
      • bundle是webpack对chunk进行处理(编译压缩等)后输出到dist的文件(就是我们最终打包产生的文件),一般和chunk是一一对应的关系;
  • css split:在css文件比较大时,可以分离打包css,把css打包为单独的文件(可以避免将css打包到js中,增大js的体积),然后加快加载速度(css是与DOM并行加载的,js默认则是阻塞式加载
    1. 任何时候使用第三方库时,阅读文档都是第一步骤:在提取并分离文件的extract-text-webpack-plugin主页我们就看到提醒,webpack v4之后我们提取分离文件应该用mini-css-extract-plugin
    2. splitChunk
    3. 关于echarts等比较大的包,可以用chuncks: async配置,设置为动态加载,而不是在首次加载时就引入,避免打包到vendor,减少首屏渲染时间。

babel

  • 在webpack中配置babel时,babel-loader会读取.babelrc配置文件

HMR

  • 不使用react-hot-loader的话,我们改变React组件内容,浏览器那边是自动刷新,而不是无刷新的HMR

fetch

  • MDN关于Body对象的描述,我们可以知道为什么fetch在获取到数据后,中间还得有一层返回Body对象方法的then:
fetch('http://example.com/movies.json')
  .then(function(response) {
    // 为什么后面可以接then ?
    // 因为Body对象的方法(这里是response.json)返回的是一个Promise对象
    return response.json();
  })
  .then(function(myJson) {
    // 这里的then是上面response.json()返回的
    console.log(myJson);
  });

git

  1. 读书笔记-图解git
    1. 从上面的内容,我们知道了分支指向了一次提交。为什么分支指向一个提交的原因,其实也是git中的分支为什么这么轻量的答案。因为分支就是指向了一个 commit 的指针,当我们提交新的commit,这个分支的指向只需要跟着更新就可以了,而创建分支仅仅是创建一个指针。
    2. 我们就可以看到 rebase 的一个缺点,那就是修改了分支的历史提交。如果已经将分支推送到了远程仓库,会导致无法将修改后的分支推送上去,必须使用 -f 参数(force)强行推送。所以使用 rebase 最好不要在公共分支上进行操作。
    3. git bisect 二分法查找bug
    4. 每个文件都有一个sha1值,该sha1值就是它在tree中的key,文件有无修改,看key(sha1)就可以知道。而很多操作都不是直接操作文件,都是对树的操作。
    5. 所谓的索引就是经过设计的字符串,用来找到真正的变化。
    6. HEAD始终指向挡墙commit的引用,也就是和当前分支(本质就是索引)同值。
    7. 大量的git操作都是操作索引。
    8. HEAD还可以直接指向branch,通过branch间接指向当前commit。
    9. 在HEAD移动时,会带着branch一起移动。(移动,其实就是指向不同的commit)。其实,commit索引才是git操作的最小粒度。
    10. branch只是一个引用,删除branch只会删除这个引用,不会删除任何commit。
    11. merge的冲突:改变不同文件或者同一文件不同行都不会产生冲突。
    12. merge就是用一次新的commit把目标分支上的commit的内容应用到该新commit上。
    13. 分支之间有个概念,叫同步;git rebase和git merge本质上都是同步两个分支的提交。同步之后,两个分支还是分别存在的,不过一个分支中有了另一个分支的提交信息了。
    14. git rebase会把你分支上超出master的部分重新打上cimmit引用,再接到最新的base上;所以 它会更改你当前分支上的提交记录

优化

关于优化,可以分为用户体验优化(例如Loadingskeleton), 和性能优化,例如缩短首屏加载时间(理念一般是:通过codeSplit 和 缓存机制配合,让那些不易变动的包尽量利用长缓存机制,减少远程资源请求次数(打包出来的index.html中的bundle.js在没有缓存的情况下,都会阻塞式地远程下载)。缩小首屏加载包的体积:压缩,路由和组件级的codeSplit,尽量减少加载不必要加载的代码。这些实现一般依靠在webpack中配置以及各种懒加载组件等辅助实现)

性能优化

“我们应该忘记忽略很小的性能优化,
可以说在97%的情况下,过早的优化是万恶之源,
而我们应该关心对性能影响最关键的那另外3%的代码” -- 高德纳

也就是说:不要将优化性能的精力浪费在对整体性能影响不大的代码上,而对于性能有关键影响的部分,优化并不嫌早。

性能优化梳理

白屏 {
	loading
	== 伪服务端渲染(只渲染首屏,poppeteer)
	cache + splitCode:= 变化部分 + 不易变化部分(适合作为长期缓存,
        一般为公共依赖之类的)
	开启Http2
}
从白屏 到 FMP {
	sketon(代替无意义页面)
}
从FMP到TTI(这一阶段主要是js阻塞:js体积 + js本身执行速度) {
	TreeShaking
	pollyfill动态加载
	动态加载es6
	路由级别split code:将首屏js拆解出来
}
组件加载 {
	codeSplit - 主要依赖于懒加载
	预加载 - 原理:hover
	keep-alive:react-keep-alive
}

加载优化

首屏加载

执行优化

场景问题解决

大数据量list渲染问题

  • 分为“有分页”和“无分页”两种方案
    1. “有分页”:除了前端分页器实现,还有就是(移动端用的多)下拉到底部时触发请求,获得分页后的数据
    2. “无分页”:就需要用到“虚拟列表”的技术了

版本控制 git

  • git的版本控制:本地仓库和远程仓库配合使用,而且就存储机制和内容方面,基本一致,也就是git一视同仁;
  • git只制定了灵活度很高的规则,至于怎么玩,就由大家去想象了
  • 分布式系统,也就是基本每一个节点,都可以作为另一个节点的服务器/代码仓库,资源/代码都是管理在每一个节点上的;