精华内容
下载资源
问答
  • 差分吸收光谱法(DOAS)是利用气体分子窄带吸收特征来测量气体浓度的一种光谱测量技术。本文介绍了DOAS的基本原理,利用MATLAB开发了一套苯、甲苯二甲苯(BTX)DOAS数据处理程序,并将BTX浓度假设值与反演值进行对比分析...
  • 基本拓扑电路上一般没有吸收缓冲电路,实际电路上一般有吸收缓冲电路,吸收与缓冲是工程需要,不是拓扑需要。...● 降低di/dtdv/dt,降低振铃,改善EMI品质 ● 提高效率(提高效率是可能的,但弄不好也...

    基本拓扑电路上一般没有吸收缓冲电路,实际电路上一般有吸收缓冲电路,吸收与缓冲是工程需要,不是拓扑需要。

     

    吸收与缓冲的功效:

     

    ● 防止器件损坏,吸收防止电压击穿,缓冲防止电流击穿

    ● 使功率器件远离危险工作区,从而提高可靠性

    ● 降低(开关)器件损耗,或者实现某种程度的关软开

    ● 降低di/dt和dv/dt,降低振铃,改善EMI品质

    ● 提高效率(提高效率是可能的,但弄不好也可能降低效率)

    也就是说,防止器件损坏只是吸收与缓冲的功效之一,其他功效也是很有价值的。

     

    吸收

     

    吸收是对电压尖峰而言。

     

    电压尖峰的成因:

     

    ● 电压尖峰是电感续流引起的。

    ● 引起电压尖峰的电感可能是:变压器漏感、线路分布电感、器件等效模型中的感性成分等。

    ● 引起电压尖峰的电流可能是:拓扑电流、二极管反向恢复电流、不恰当的谐振电流等。

     

    减少电压尖峰的主要措施是:

     

    ● 减少可能引起电压尖峰的电感,比如漏感、布线电感等

    ● 减少可能引起电压尖峰的电流,比如二极管反向恢复电流等

    ● 如果可能的话,将上述电感能量转移到别处。

    ● 采取上述措施后电压尖峰仍然不能接受,最后才考虑吸收。吸收是不得已的技术措施

     

    拓扑吸

     

    将开关管Q1、拓扑续流二极管D1和一个无损的拓扑电容C2组成一个在布线上尽可能简短的吸收回路。

     

    拓扑吸收的特点:

     

    ● 同时将Q1、D1的电压尖峰、振铃减少到最低程度。

    ● 拓扑吸收是无损吸收,效率较高。

    ● 吸收电容C2可以在大范围内取值。

    ● 拓扑吸收是硬开关,因为拓扑是硬开关。

     

    体二极管反向恢复吸收

    开关器件的体二极管的反向恢复特性,在关断电压的上升沿发挥作用,有降低电压尖峰的吸收效应。

     

    RC 吸收

     

     

     

    ● RC吸收的本质是阻尼吸收。

    ● 有人认为R 是限流作用,C是吸收。实际情况刚好相反。

    ● 电阻R 的最重要作用是产生阻尼,吸收电压尖峰的谐振能量,是功率器件。

    ● 电容C的作用也并不是电压吸收,而是为R阻尼提供能量通道。

    ● RC吸收并联于谐振回路上,C提供谐振能量通道,C 的大小决定吸收程度,最终目的是使R形成功率吸收。

    ● 对应一个特定的吸收环境和一个特定大小的电容C,有一个最合适大小的电阻R,形成最大的阻尼、获得最低的电压尖峰。

    ● RC吸收是无方向吸收,因此RC吸收既可以用于单向电路的吸收,也可用于双向或者对称电路的吸收。

     

    RC 吸收设计

     

     

     

    ● RC吸收的设计方法的难点在于:吸收与太多因素有关,比如漏感、绕组结构、分布电感电容、器件等效电感电容、电流、电压、功率等级、di/dt、dv/dt、频率、二极管反向恢复特性等等。而且其中某些因素是很难获得准确的设计参数的。

    ● 比如对二极管反压的吸收,即使其他情况完全相同,使用不同的二极管型号需要的RC吸收参数就可能有很大差距。很难推导出一个通用的计算公式出来。

    ● R 的损耗功率可大致按下式估算:

    Ps = FCU2

    其中U为吸收回路拓扑反射电压。

    ● 工程上一般应该在通过计算或者仿真获得初步参数后,还必须根据实际布线在板调试,才能获得最终设计参数。

     

    RCD 吸收

     

     

    特点

     

    ● RCD吸收不是阻尼吸收,而是靠非线性开关D 直接破坏形成电压尖峰的谐振条件,把电压尖峰控制在任何需要的水平。

     

    ● C 的大小决定吸收效果(电压尖峰),同时决定了吸收功率(即R的热功率)。

     

    ● R 的作用只是把吸收能量以热的形式消耗掉。其电阻的最小值应该满足开关管的电流限制,最大值应该满足PWM逆程RC放电周期需要,在此范围内取值对吸收效果影响甚微。

     

    ● RCD吸收会在被保护的开关器件上实现某种程度的软关断,这是因为关断瞬间开关器件上的电压即吸收电容C上的电压等于0,关断动作会在C 上形成一个充电过程,延缓电压恢复,降低dv/dt,实现软关断。

     

    不适应性

     

    ● RCD吸收一般不适合反激拓扑的吸收,这是因为RCD吸收可能与反激拓扑相冲突。

    ● RCD吸收一般不适合对二极管反压尖峰的吸收,因为RCD吸收动作有可能加剧二极管反向恢复电流。

     

    钳位吸收

     

    RCD 钳位

     

    ● 尽管RCD钳位与RCD吸收电路可以完全相同,但元件参数和工况完全不同。RCD吸收RC时间常数远小于PWM周期,而RCD钳位的RC时间常数远大于PWM周期。

     

    ● 与RCD吸收电容的全充全放工况不同,RCD钳位的电容可以看成是电压源,其RC充放电幅度的谷值应不小于拓扑反射电压,峰值即钳位电压。

    ● 由于RCD钳位在PWM电压的上升沿和下降沿都不会动作,只在电压尖峰出现时动作,因此RCD钳位是高效率的吸收。

     

    齐纳钳位

     

    ● 齐纳钳位的几种形式。

    ● 齐纳钳位也是在电压尖峰才起作用,也是高效率吸收。

    ● 某些场合,齐纳钳位需要考虑齐纳二极管的反向恢复特性对电路的影响。

    ● 齐纳吸收需注意吸收功率匹配,必要时可用有源功率器件组成大功率等效电路

     

    无损吸收

     

     

    无损吸收的条件

     

    ● 吸收网络不得使用电阻。

    ● 不得形成LD电流回路。

    ● 吸收回路不得成为拓扑电流路径。

    ● 吸收能量必须转移到输入侧或者输出侧。

    ● 尽量减少吸收回路二极管反向恢复电流的影响。

    无损吸收是强力吸收,不仅能够吸收电压尖峰,甚至能够吸收拓扑反射电压,

     

    比如:

     

    缓冲

     

    缓冲是对冲击尖峰电流而言

    ● 引起电流尖峰第一种情况是二极管(包括体二极管)反向恢复电流。

    ● 引起电流尖峰第二种情况是对电容的充放电电流。这些电容可能是:电路分布电容、变压器绕组等效分布电容、设计不恰当的吸收电容、设计不恰当的谐振电容、器件的等效模型中的电容成分等等。

     

    缓冲的基本方法:

     

    在冲击电流尖峰的路径上串入某种类型的电感,可以是以下类型:

     

    缓冲的特性:

     

    ● 由于缓冲电感的串入会显著增加吸收的工作量,因此缓冲电路一般需要与吸收电路配合使用。

    ● 缓冲电路延缓了导通电流冲击,可实现某种程度的软开通(ZIS)。

    ● 变压器漏感也可以充当缓冲电感。

     

    LD 缓冲

     

     

     

    特点:

    ● 可不需要吸收电路配合。

    ● 缓冲释能二极管与拓扑续流二极管电流应力相当甚至更大。

    ● 缓冲释能二极管的损耗可以简单理解为开关管减少的损耗。

    ● 适当的缓冲电感(L3)参数可以大幅度减少开关管损耗,实现高效率。

     

    LR 缓冲

     

     

    特点:

    ● 需要吸收电路配合以转移电感剩余能量。

    ● 缓冲释能电阻R的损耗较大,可简单理解为是从开关管转移出来的损耗。

    ● R、L参数必须实现最佳配合,参数设计调试比较难以掌握。

    ● 只要参数适当仍然能够实现高效率。

     

    饱和电感缓冲

     

     

    ● 饱和电感的电气性能表现为对di/dt敏感。

     

    ● 在一个冲击电流的上升沿,开始呈现较大的阻抗,随着电流的升高逐渐进入饱和,从而延缓和削弱了冲击电流尖峰,即实现软开通。

     

    ● 在电流达到一定程度后,饱和电感因为饱和而呈现很低的阻抗,这有利于高效率地传输功率。

     

    ● 在电流关断时,电感逐渐退出饱和状态,一方面,由于之前的饱和状态的饱和电感量非常小,即储能和需要的释能较小。另一方面,退出时电感量的恢复可以减缓电压的上升速度,有利于实现软关断。

     

    ● 以Ls2为例,5u表示磁路截面积5mm2,大致相当于1颗PC40材质4*4*2的小磁芯。

     

    饱和电感特性

     

    ● 热特性

     

    饱和电感是功率器件,通过进入和退出饱和过程的磁滞损耗(而不是涡流损耗或者铜损)吸收电流尖峰能量,主要热功率来自于磁芯。

     

    这一方面要求磁芯应该是高频材料,另一方面要求磁芯温度在任何情况下不得超过居里温度。这意味着饱和电感的磁芯应该具有最有利的散热特性和结构,即:更高的居里温度、更高的导热系数、更大的散热面积、更短的热传导路径。

     

    ● 饱和特性

     

    显然饱和电感一般不必考虑使用气隙或者不易饱和的低导磁率材料。

     

    ● 初始电感等效特性

     

    在其他条件相同情况下,较低导磁率的磁芯配合较多匝数、与较高导磁率的磁芯配合较少匝数的饱和电感初始电感相当,缓冲效果大致相当。

     

    这意味着直接采用1 匝的穿心电感总是可能的,因为任何多匝的电感总可以找到更高导磁率的磁芯配合1 匝等效之。这还意味着磁芯最高导磁率受到限制,如果一个适合的磁芯配合1 匝的饱和电感,将没有使用更高导磁率的磁芯配合更少匝数的可能。

     

    ● 磁芯体积等效特性

     

    在其他条件相同情况下,相同体积的磁芯的饱和电感缓冲效果大致相当。既然如此,磁芯可以按照最有利于散热的磁路进行设计。比如细长的管状磁芯比环状磁芯、多个小磁芯比集中一个大磁芯、穿心电感比多匝电感显然具有更大的散热表面积。

     

    ● 组合特性

     

    有时候,单一材质的磁芯并不能达到工程上需要的缓冲效果,采用多种材质的磁芯相互配合或许才能能够满足工程需要。

     

     

    无源无损缓冲吸收

     

     

     

    ● 如果缓冲电感本身是无损的(非饱和电感),而其电感储能又是经过无损吸收的方式处理的,即构成无源无损缓冲吸收电路,实际上这也是无源软开关电路。

     

    ● 缓冲电感的存在延迟和削弱的开通冲击电流,实现了一定程度的软开通。

     

    ● 无损吸收电路的存在延迟和降低了关断电压的dv/dt,实现了一定程度的软关断。

     

    ● 实现无源软开关的条件与无损吸收大致相同。并不是所有拓扑都能够搭建出一个无源软开关电路。因此除了经典的电路外,很多无源软开关电路都是被专利的热门。

     

    ● 无源无损软开关电路效率明显高于其他缓冲吸收方式,与有源软开关电路效率相差无几。因此只要能够实现无源软开关的电路,可不必采用有源软开关。

    吸收缓冲电路性能对

     

     

    滤波缓

     

     

     

    ● 电路中的电解电容一般具有较大的ESR(典型值是百毫欧姆数量级),这引起两方面问题:一是滤波效果大打折扣;二是纹波电流在ESR上产生较大损耗,这不仅降低效率,而且由于电解电容发热直接导致的可靠性和寿命问题。

     

    ● 一般方法是在电解电容上并联高频无损电容,而事实上,这一方法并不能使上述问题获得根本的改变,这是由于高频无损电容在开关电源常用频率范围内仍然存在较大的阻抗的缘故。

     

    ● 提出的办法是:用电感将电解和CBB分开,CBB位于高频纹波电流侧,电解位于直流(工频)侧,各自承担对应的滤波任务。

     

    ● 设计原则:Π形滤波网络的谐振频率Fn应该错开PWM频率Fp。可取Fp=(1.5~2)Fn 。

     

    ● 这一设计思想可以延伸到直流母线滤波的双向缓冲,或者其他有较大滤波应力的电路结构。

     

    振铃

     

     

     

    振铃的危害:

     

    ● MEI测试在振铃频率容易超标。

    ● 振铃将引起振铃回路的损耗,造成器件发热和降低效率。

    ● 振铃电压幅度超过临界值将引起振铃电流,破环电路正常工况,效率大幅度降低。

     

    振铃的成因:

     

    ● 振铃多半是由结电容和某个等效电感的谐振产生的。对于一个特定频率的振铃,总可以找到原因。电容和电感可以确定一个频率,而频率可以观察获得。电容多半是某个器件的结电容,电感则可能是漏感。

     

    ● 振铃最容易在无损(无电阻的)回路发生。比如:副边二极管结电容与副边漏感的谐振、杂散电感与器件结电容的谐振、吸收回路电感与器件结电容的谐振等等。

     

    振铃的抑制:

     

    ● 磁珠吸收,只要磁珠在振铃频率表现为电阻,即可大幅度吸收振铃能量,但是不恰当的磁珠也可能增加振铃。

     

    ● RC 吸收,其中C可与振铃(结)电容大致相当,R 按RC吸收原则选取。

     

    ● 改变谐振频率,比如:只要将振铃频率降低到PWM频率相近,即可消除PWM上的振铃。

     

    ● 特别地,输入输出滤波回路设计不当也可能产生谐振,也需要调整谐振频率或者其他措施予以规避。

     

    吸收缓冲能量再利用

     

    RCD吸收能量回收电路

     

    只要将吸收电路的正程和逆程回路分开,形成相对0 电位的正负电流通道,就能够获得正负电压输出。其设计要点为:

     

    RCD吸收电路参数应主要满足主电路吸收需要,不建议采用增加吸收功率的方式增加直流输出功率。

    展开全文
  • 在合理简化被动调Q速率方程组的基础上,得到了能反映激发态吸收效应的慢恢复饱和吸收体被动调Q的判据,以及调Q脉冲的峰值功率、脉冲能量、脉冲宽度能量利用率等调Q重要参量的解析表达式。结果具有一定的普遍性,有...
  • (即要限制主MOS管反峰,又要RCD吸收回路功耗)  MOS管是金属(metal)-氧化物(oxid)-半导体(semiconductor)场效应晶体管,或者称是金属-绝缘体(insulator)-半导体。MOS管的sourcedrain是可以对调的,他们...
  • 采用Drude-Lorentz模型描述红外微纳结构材料吸收性能的色散关系,根据有效电子数浓度理论等效电路理论,以矩形结构吸收材料为代表,建立了红外微纳结构吸收材料的几何结构参数与Drude-Lorentz模型参数之间的函数...
  • (即要限制主MOS管最大反峰,又要RCD吸收回路功耗最小)  MOS管是金属(metal)-氧化物(oxid)-半导体(semiconductor)场效应晶体管,或者称是金属-绝缘体(insulator)-半导体。MOS管的sourcedrain是可以对调...
  • 我们解析地确定了低频极限下的相移,并表明散射和吸收截面得到了修正。 特别地,我们表明在低频极限中,当我们增加非交换性参数的值时,散射/吸收截面的值会减小。 另外,我们已经通过数值求解任意频率的问题来证实...
  • 研究了飞秒激光诱导单个空气等离子体通道的吸收和辐射特性,给出了等离子体通道吸收系数的解析表达式,并理论推导出了辐射能流率的表达式。与TOPS不透明度数据库查表方法比较,两种方法计算结果符合得很好。分析了吸收...
  • 本文对激光等离子体细丝的场分布共振吸收进行了理论研究.在冷等离子体的条件下,首先求得场方程以及电场的径向分量轴向分量的解析表示式;然后,通过数值计算发现:在等离子体细丝的径向场存在一个隧道效应,即在径向...
  • 磁珠用来吸收超高频信号,象一些 RF 电路,PLL,振荡电路都需要在电源输入部分加磁珠,而电感是一种储能元件,用在 LC 振荡电路、中低频的滤波电路等,其应用频率范围很少超过 50MHz.  1.片式电感:  在电子设备...
  • 吸收电路

    2020-12-22 20:20:20
    开关电源中的全部缓冲吸收电路解析,收藏了! 图片 基本拓扑电路上一般没有吸收缓冲电路,实际电路上一般有吸收缓冲电路,吸收与缓冲是工程需要,不是拓扑需要。 吸收与缓冲的功效: ● 防止器件损坏,吸收防止电压...

    开关电源中的全部缓冲吸收电路解析,收藏了!

    图片

    基本拓扑电路上一般没有吸收缓冲电路,实际电路上一般有吸收缓冲电路,吸收与缓冲是工程需要,不是拓扑需要。

    吸收与缓冲的功效:

    ● 防止器件损坏,吸收防止电压击穿,缓冲防止电流击穿
    ● 使功率器件远离危险工作区,从而提高可靠性
    ● 降低(开关)器件损耗,或者实现某种程度的关软开
    ● 降低di/dt和dv/dt,降低振铃,改善EMI品质
    ● 提高效率(提高效率是可能的,但弄不好也可能降低效率)
    也就是说,防止器件损坏只是吸收与缓冲的功效之一,其他功效也是很有价值的。

    吸收

    吸收是对电压尖峰而言。

    电压尖峰的成因:

    ● 电压尖峰是电感续流引起的。
    ● 引起电压尖峰的电感可能是:变压器漏感、线路分布电感、器件等效模型中的感性成分等。
    ● 引起电压尖峰的电流可能是:拓扑电流、二极管反向恢复电流、不恰当的谐振电流等。

    减少电压尖峰的主要措施是:

    ● 减少可能引起电压尖峰的电感,比如漏感、布线电感等
    ● 减少可能引起电压尖峰的电流,比如二极管反向恢复电流等
    ● 如果可能的话,将上述电感能量转移到别处。
    ● 采取上述措施后电压尖峰仍然不能接受,最后才考虑吸收。吸收是不得已的技术措施

    拓扑吸
    图片

    图片
    将开关管Q1、拓扑续流二极管D1和一个无损的拓扑电容C2组成一个在布线上尽可能简短的吸收回路。

    拓扑吸收的特点:

    ● 同时将Q1、D1的电压尖峰、振铃减少到最低程度。
    ● 拓扑吸收是无损吸收,效率较高。
    ● 吸收电容C2可以在大范围内取值。
    ● 拓扑吸收是硬开关,因为拓扑是硬开关。

    体二极管反向恢复吸收
    开关器件的体二极管的反向恢复特性,在关断电压的上升沿发挥作用,有降低电压尖峰的吸收效应。

    RC 吸收

    图片

    图片

    ● RC吸收的本质是阻尼吸收。
    ● 有人认为R 是限流作用,C是吸收。实际情况刚好相反。
    ● 电阻R 的最重要作用是产生阻尼,吸收电压尖峰的谐振能量,是功率器件。
    ● 电容C的作用也并不是电压吸收,而是为R阻尼提供能量通道。
    ● RC吸收并联于谐振回路上,C提供谐振能量通道,C 的大小决定吸收程度,最终目的是使R形成功率吸收。
    ● 对应一个特定的吸收环境和一个特定大小的电容C,有一个最合适大小的电阻R,形成最大的阻尼、获得最低的电压尖峰。
    ● RC吸收是无方向吸收,因此RC吸收既可以用于单向电路的吸收,也可用于双向或者对称电路的吸收。

    RC 吸收设计

    图片

    图片

    ● RC吸收的设计方法的难点在于:吸收与太多因素有关,比如漏感、绕组结构、分布电感电容、器件等效电感电容、电流、电压、功率等级、di/dt、dv/dt、频率、二极管反向恢复特性等等。而且其中某些因素是很难获得准确的设计参数的。
    ● 比如对二极管反压的吸收,即使其他情况完全相同,使用不同的二极管型号需要的RC吸收参数就可能有很大差距。很难推导出一个通用的计算公式出来。
    ● R 的损耗功率可大致按下式估算:
    Ps = FCU2
    其中U为吸收回路拓扑反射电压。
    ● 工程上一般应该在通过计算或者仿真获得初步参数后,还必须根据实际布线在板调试,才能获得最终设计参数。

    RCD 吸收
    图片

    图片

    特点

    ● RCD吸收不是阻尼吸收,而是靠非线性开关D 直接破坏形成电压尖峰的谐振条件,把电压尖峰控制在任何需要的水平。

    ● C 的大小决定吸收效果(电压尖峰),同时决定了吸收功率(即R的热功率)。

    ● R 的作用只是把吸收能量以热的形式消耗掉。其电阻的最小值应该满足开关管的电流限制,最大值应该满足PWM逆程RC放电周期需要,在此范围内取值对吸收效果影响甚微。

    ● RCD吸收会在被保护的开关器件上实现某种程度的软关断,这是因为关断瞬间开关器件上的电压即吸收电容C上的电压等于0,关断动作会在C 上形成一个充电过程,延缓电压恢复,降低dv/dt,实现软关断。

    不适应性

    ● RCD吸收一般不适合反激拓扑的吸收,这是因为RCD吸收可能与反激拓扑相冲突。
    ● RCD吸收一般不适合对二极管反压尖峰的吸收,因为RCD吸收动作有可能加剧二极管反向恢复电流。

    钳位吸收

    RCD 钳位

    ● 尽管RCD钳位与RCD吸收电路可以完全相同,但元件参数和工况完全不同。RCD吸收RC时间常数远小于PWM周期,而RCD钳位的RC时间常数远大于PWM周期。

    ● 与RCD吸收电容的全充全放工况不同,RCD钳位的电容可以看成是电压源,其RC充放电幅度的谷值应不小于拓扑反射电压,峰值即钳位电压。
    ● 由于RCD钳位在PWM电压的上升沿和下降沿都不会动作,只在电压尖峰出现时动作,因此RCD钳位是高效率的吸收。

    齐纳钳位

    ● 齐纳钳位的几种形式。
    ● 齐纳钳位也是在电压尖峰才起作用,也是高效率吸收。
    ● 某些场合,齐纳钳位需要考虑齐纳二极管的反向恢复特性对电路的影响。
    ● 齐纳吸收需注意吸收功率匹配,必要时可用有源功率器件组成大功率等效电路

    无损吸收

    图片

    无损吸收的条件

    ● 吸收网络不得使用电阻。
    ● 不得形成LD电流回路。
    ● 吸收回路不得成为拓扑电流路径。
    ● 吸收能量必须转移到输入侧或者输出侧。
    ● 尽量减少吸收回路二极管反向恢复电流的影响。
    无损吸收是强力吸收,不仅能够吸收电压尖峰,甚至能够吸收拓扑反射电压,

    比如:
    图片

    缓冲

    缓冲是对冲击尖峰电流而言
    ● 引起电流尖峰第一种情况是二极管(包括体二极管)反向恢复电流。
    ● 引起电流尖峰第二种情况是对电容的充放电电流。这些电容可能是:电路分布电容、变压器绕组等效分布电容、设计不恰当的吸收电容、设计不恰当的谐振电容、器件的等效模型中的电容成分等等。

    缓冲的基本方法:

    在冲击电流尖峰的路径上串入某种类型的电感,可以是以下类型:

    图片
    缓冲的特性:

    ● 由于缓冲电感的串入会显著增加吸收的工作量,因此缓冲电路一般需要与吸收电路配合使用。
    ● 缓冲电路延缓了导通电流冲击,可实现某种程度的软开通(ZIS)。
    ● 变压器漏感也可以充当缓冲电感。

    LD 缓冲

    图片

    特点:
    ● 可不需要吸收电路配合。
    ● 缓冲释能二极管与拓扑续流二极管电流应力相当甚至更大。
    ● 缓冲释能二极管的损耗可以简单理解为开关管减少的损耗。
    ● 适当的缓冲电感(L3)参数可以大幅度减少开关管损耗,实现高效率。

    LR 缓冲

    特点:
    ● 需要吸收电路配合以转移电感剩余能量。
    ● 缓冲释能电阻R的损耗较大,可简单理解为是从开关管转移出来的损耗。
    ● R、L参数必须实现最佳配合,参数设计调试比较难以掌握。
    ● 只要参数适当仍然能够实现高效率。

    饱和电感缓冲

    ● 饱和电感的电气性能表现为对di/dt敏感。

    ● 在一个冲击电流的上升沿,开始呈现较大的阻抗,随着电流的升高逐渐进入饱和,从而延缓和削弱了冲击电流尖峰,即实现软开通。

    ● 在电流达到一定程度后,饱和电感因为饱和而呈现很低的阻抗,这有利于高效率地传输功率。

    ● 在电流关断时,电感逐渐退出饱和状态,一方面,由于之前的饱和状态的饱和电感量非常小,即储能和需要的释能较小。另一方面,退出时电感量的恢复可以减缓电压的上升速度,有利于实现软关断。

    ● 以Ls2为例,5u表示磁路截面积5mm2,大致相当于1颗PC40材质442的小磁芯。

    饱和电感特性

    ● 热特性

    饱和电感是功率器件,通过进入和退出饱和过程的磁滞损耗(而不是涡流损耗或者铜损)吸收电流尖峰能量,主要热功率来自于磁芯。

    这一方面要求磁芯应该是高频材料,另一方面要求磁芯温度在任何情况下不得超过居里温度。这意味着饱和电感的磁芯应该具有最有利的散热特性和结构,即:更高的居里温度、更高的导热系数、更大的散热面积、更短的热传导路径。

    ● 饱和特性

    显然饱和电感一般不必考虑使用气隙或者不易饱和的低导磁率材料。

    ● 初始电感等效特性

    在其他条件相同情况下,较低导磁率的磁芯配合较多匝数、与较高导磁率的磁芯配合较少匝数的饱和电感初始电感相当,缓冲效果大致相当。

    这意味着直接采用1 匝的穿心电感总是可能的,因为任何多匝的电感总可以找到更高导磁率的磁芯配合1 匝等效之。这还意味着磁芯最高导磁率受到限制,如果一个适合的磁芯配合1 匝的饱和电感,将没有使用更高导磁率的磁芯配合更少匝数的可能。

    ● 磁芯体积等效特性

    在其他条件相同情况下,相同体积的磁芯的饱和电感缓冲效果大致相当。既然如此,磁芯可以按照最有利于散热的磁路进行设计。比如细长的管状磁芯比环状磁芯、多个小磁芯比集中一个大磁芯、穿心电感比多匝电感显然具有更大的散热表面积。

    ● 组合特性

    有时候,单一材质的磁芯并不能达到工程上需要的缓冲效果,采用多种材质的磁芯相互配合或许才能能够满足工程需要。

    图片

    无源无损缓冲吸收

    图片

    图片

    ● 如果缓冲电感本身是无损的(非饱和电感),而其电感储能又是经过无损吸收的方式处理的,即构成无源无损缓冲吸收电路,实际上这也是无源软开关电路。

    ● 缓冲电感的存在延迟和削弱的开通冲击电流,实现了一定程度的软开通。

    ● 无损吸收电路的存在延迟和降低了关断电压的dv/dt,实现了一定程度的软关断。

    ● 实现无源软开关的条件与无损吸收大致相同。并不是所有拓扑都能够搭建出一个无源软开关电路。因此除了经典的电路外,很多无源软开关电路都是被专利的热门。

    ● 无源无损软开关电路效率明显高于其他缓冲吸收方式,与有源软开关电路效率相差无几。因此只要能够实现无源软开关的电路,可不必采用有源软开关。
    吸收缓冲电路性能对

    图片

    滤波缓

    图片

    图片

    ● 电路中的电解电容一般具有较大的ESR(典型值是百毫欧姆数量级),这引起两方面问题:一是滤波效果大打折扣;二是纹波电流在ESR上产生较大损耗,这不仅降低效率,而且由于电解电容发热直接导致的可靠性和寿命问题。

    ● 一般方法是在电解电容上并联高频无损电容,而事实上,这一方法并不能使上述问题获得根本的改变,这是由于高频无损电容在开关电源常用频率范围内仍然存在较大的阻抗的缘故。

    ● 提出的办法是:用电感将电解和CBB分开,CBB位于高频纹波电流侧,电解位于直流(工频)侧,各自承担对应的滤波任务。

    ● 设计原则:Π形滤波网络的谐振频率Fn应该错开PWM频率Fp。可取Fp=(1.5~2)Fn 。

    ● 这一设计思想可以延伸到直流母线滤波的双向缓冲,或者其他有较大滤波应力的电路结构。

    振铃

    振铃的危害:

    ● MEI测试在振铃频率容易超标。
    ● 振铃将引起振铃回路的损耗,造成器件发热和降低效率。
    ● 振铃电压幅度超过临界值将引起振铃电流,破环电路正常工况,效率大幅度降低。

    振铃的成因:

    ● 振铃多半是由结电容和某个等效电感的谐振产生的。对于一个特定频率的振铃,总可以找到原因。电容和电感可以确定一个频率,而频率可以观察获得。电容多半是某个器件的结电容,电感则可能是漏感。

    ● 振铃最容易在无损(无电阻的)回路发生。比如:副边二极管结电容与副边漏感的谐振、杂散电感与器件结电容的谐振、吸收回路电感与器件结电容的谐振等等。

    振铃的抑制:

    ● 磁珠吸收,只要磁珠在振铃频率表现为电阻,即可大幅度吸收振铃能量,但是不恰当的磁珠也可能增加振铃。

    ● RC 吸收,其中C可与振铃(结)电容大致相当,R 按RC吸收原则选取。

    ● 改变谐振频率,比如:只要将振铃频率降低到PWM频率相近,即可消除PWM上的振铃。

    ● 特别地,输入输出滤波回路设计不当也可能产生谐振,也需要调整谐振频率或者其他措施予以规避。

    吸收缓冲能量再利用
    图片

    RCD吸收能量回收电路

    只要将吸收电路的正程和逆程回路分开,形成相对0 电位的正负电流通道,就能够获得正负电压输出。其设计要点为:

    RCD吸收电路参数应主要满足主电路吸收需要,不建议采用增加吸收功率的方式增加直流输出功率。

    展开全文
  • 今天写文,主要是把我的研究成果的精华内容整理落地,另外通过再次梳理希望发现更多优化的地方,也希望可以让更多的人少踩一些坑,让跟多的人理解掌握这个技术。相信看过本文(前提是能对你的胃口,也能较好的消化...

    写在前面

    前段时间一直在研究react ssr技术,然后写了一个完整的ssr开发骨架。今天写文,主要是把我的研究成果的精华内容整理落地,另外通过再次梳理希望发现更多优化的地方,也希望可以让更多的人少踩一些坑,让跟多的人理解和掌握这个技术。

    相信看过本文(前提是能对你的胃口,也能较好的消化吸收)你一定会对 react ssr 服务端渲染技术有一个深入的理解,可以打造自己的脚手架,更可以用来改造自己的实际项目,当然这不仅限于 react ,其他框架都一样,毕竟原理都是相似的。

    为什么要服务端渲染(ssr)

    至于为什么要服务端渲染,我相信大家都有所闻,而且每个人都能说出几点来。

    首屏等待

    在 SPA 模式下,所有的数据请求和 Dom 渲染都在浏览器端完成,所以当我们第一次访问页面的时候很可能会存在“白屏”等待,而服务端渲染所有数据请求和 html内容已在服务端处理完成,浏览器收到的是完整的 html 内容,可以更快的看到渲染内容,在服务端完成数据请求肯定是要比在浏览器端效率要高的多。

    没考虑SEO的感受

    有些网站的流量来源主要还是靠搜索引擎,所以网站的 SEO 还是很重要的,而 SPA 模式对搜索引擎不够友好,要想彻底解决这个问题只能采用服务端直出。改变不了别人(搜索yinqing),只能改变自己。

    SSR + SPA 体验升级

    只实现 SSR 其实没啥意义,技术上没有任何发展和进步,否则 SPA 技术就不会出现。

    但是单纯的 SPA又不够完美,所以最好的方案就是这两种体验和技术的结合,第一次访问页面是服务端渲染,基于第一次访问后续的交互就是 SPA 的效果和体验,还不影响SEO 效果,这就有点完美了。

    单纯实现 ssr 很简单,毕竟这是传统技术,也不分语言,随便用 php 、jsp、asp、node 等都可以实现。

    但是要实现两种技术的结合,同时可以最大限度的重用代码(同构),减少开发维护成本,那就需要采用 react 或者 vue 等前端框架相结合 node (ssr) 来实现。

    本文主要说 React SSR 技术 ,当然 vue 也一样,只是技术栈不同而已。

    核心原理

    整体来说 react 服务端渲染原理不复杂,其中最核心的内容就是同构。

    node server 接收客户端请求,得到当前的req url path,然后在已有的路由表内查找到对应的组件,拿到需要请求的数据,将数据作为 props 、context或者store 形式传入组件,然后基于 react 内置的服务端渲染api renderToString() or renderToNodeStream() 把组件渲染为 html字符串或者 stream 流, 在把最终的 html 进行输出前需要将数据注入到浏览器端(注水),server 输出(response)后浏览器端可以得到数据(脱水),浏览器开始进行渲染和节点对比,然后执行组件的componentDidMount 完成组件内事件绑定和一些交互,浏览器重用了服务端输出的 html 节点,整个流程结束。

    技术点确实不少,但更多的是架构和工程层面的,需要把各个知识点进行链接和整合。

    这里放一个架构图

    5442b7c8343128752f738ed6f3122c98.png

    react ssr

    从 ejs 开始

    实现 ssr 很简单,先看一个 node ejs的栗子。

    // index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>react ssr <%= title %></title>
    </head>
    <body>
     <%= data %>
    </body>
    </html>
    复制代码
     //node ssr
     const ejs = require('ejs');
     const http = require('http');
    http.createServer((req, res) => {
     if (req.url === '/') {
     res.writeHead(200, {
     'Content-Type': 'text/html' 
     });
     // 渲染文件 index.ejs
     ejs.renderFile('./views/index.ejs', {
     title: 'react ssr', 
     data: '首页'}, 
     (err, data) => {
     if (err ) {
     console.log(err);
     } else {
     res.end(data);
     }
     })
     }
    }).listen(8080);
    复制代码

    jsx 到字符串

    上面我们结合 ejs模板引擎 ,实现了一个服务端渲染的输出,html 和 数据直接输出到客户端。

    参考以上,我们结合 react组件 来实现服务端渲染直出,使用 jsx 来代替 ejs,之前是在 html 里使用 ejs 来绑定数据,现在改写成使用jsx 来绑定数据,使用 react 内置 api 来把组件渲染为 html 字符串,其他没有差别。

    为什么react 组件可以被转换为 html字符串呢?

    简单的说我们写的 jsx 看上去就像在写 html(其实写的是对象) 标签,其实经过编译后都会转换成React.createElement方法,最终会被转换成一个对象(虚拟DOM),而且和平台无关,有了这个对象,想转换成什么那就看心情了。

    const React = require('react');
    const { renderToString} = require( 'react-dom/server');
    const http = require('http');
    //组件
    class Index extends React.Component{
     constructor(props){
     super(props);
     }
     render(){
     return <h1>{this.props.data.title}</h1>
     }
    }
     
    //模拟数据的获取
    const fetch = function () {
     return {
     title:'react ssr',
     data:[]
     }
    }
    //服务
    http.createServer((req, res) => {
     if (req.url === '/') {
     res.writeHead(200, {
     'Content-Type': 'text/html'
     });
     const data = fetch();
     const html = renderToString(<Index data={data}/>);
     res.end(html);
     }
    }).listen(8080);
    复制代码

    ps:以上代码不能直接运行,需要结合babel 使用 @babel/preset-react 进行转换

    npx babel script.js --out-file script-compiled.js --presets=@babel/preset-react
     
    复制代码

    引出问题

    在上面非常简单的就是实现了 react ssr ,把jsx作为模板引擎,不要小看上面的一小段代码,他可以帮我们引出一系列的问题,这也是完整实现 react ssr 的基石。

    • 双端路由如何维护?

    首先我们会发现我在 server 端定义了路由 '/',但是在 react SPA 模式下我们需要使用react-router来定义路由。那是不是就需要维护两套路由呢?

    • 获取数据的方法和逻辑写在哪里?

    发现数据获取的fetch 写的独立的方法,和组件没有任何关联,我们更希望的是每个路由都有自己的 fetch 方法。

    • 服务端 html 节点无法重用

    虽然组件在服务端得到了数据,也能渲染到浏览器内,但是当浏览器端进行组件渲染的时候直出的内容会一闪而过消失。

    好了,问题有了,接下来我们就一步一步的来解决这些问题。

    同构才是核心

    react ssr 的核心就是同构,没有同构的 ssr 是没有意义的。

    所谓同构就是采用一套代码,构建双端(server 和 client)逻辑,最大限度的重用代码,不用维护两套代码。而传统的服务端渲染是无法做到的,react 的出现打破了这个瓶颈,并且现在已经得到了比较广泛的应用。

    路由同构

    双端使用同一套路由规则,node server 通过req url path 进行组件的查找,得到需要渲染的组件。

    //组件和路由配置 ,供双端使用 routes-config.js

    class Detail extends React.Component{
     render(){
     return <div>detail</div>
     }
    }
    class Index extends React.Component {
     render() {
     return <div>index</div>
     }
    }
    const routes = [
     
     {
     path: "/",
     exact: true,
     component: Home
     },
     {
     path: '/detail', exact: true,
     component:Detail,
     },
     {
     path: '/detail/:a/:b', exact: true,
     component: Detail
     }
     
    ];
    //导出路由表
    export default routes;
    复制代码

    //客户端 路由组件

    import routes from './routes-config.js';
    function App(){
     return (
     <Layout>
     <Switch>
     {
     routes.map((item,index)=>{
     return <Route path={item.path} key={index} exact={item.exact} render={item.component}></Route>
     })
     }
     </Switch>
     </Layout>
     );
    }
    export default App;
    复制代码

    node server 进行组件查找

    路由匹配其实就是对 组件path 规则的匹配,如果规则不复杂可以自己写,如果情况很多种还是使用官方提供的库来完成。

    matchRoutes(routes, pathname)

    //引入官方库
    import { matchRoutes } from "react-router-config";
    import routes from './routes-config.js';
    const path = req.path;
    const branch = matchRoutes(routes, path);
    //得到要渲染的组件
    const Component = branch[0].route.component;
     
    //node server 
    http.createServer((req, res) => {
     
     const url = req.url;
     //简单容错,排除图片等资源文件的请求
     if(url.indexOf('.')>-1) { res.end(''); return false;}
     res.writeHead(200, {
     'Content-Type': 'text/html'
     });
     const data = fetch();
     //查找组件
     const branch = matchRoutes(routes,url);
     
     //得到组件
     const Component = branch[0].route.component;
     //将组件渲染为 html 字符串
     const html = renderToString(<Component data={data}/>);
     res.end(html);
     
     }).listen(8080);
    复制代码

    可以看下matchRoutes方法的返回值,其中route.component 就是 要渲染的组件

    [
     { 
     
     route:
     { path: '/detail', exact: true, component: [Function: Detail] },
     match:
     { path: '/detail', url: '/detail', isExact: true, params: {} } 
     
     }
     ]
    复制代码

    react-router-config 这个库由react 官方维护,功能是实现嵌套路由的查找,代码没有多少,有兴趣可以看看。

    文章走到这里,相信你已经知道了路由同构,所以上面的第一个问题 : 【双端路由如何维护?】 解决了。

    数据同构(预取同构)

    这里开始解决我们最开始发现的第二个问题 - 【获取数据的方法和逻辑写在哪里?】

    数据预取同构,解决双端如何使用同一套数据请求方法来进行数据请求。

    先说下流程,在查找到要渲染的组件后,需要预先得到此组件所需要的数据,然后将数据传递给组件后,再进行组件的渲染。

    我们可以通过给组件定义静态方法来处理,组件内定义异步数据请求的方法也合情合理,同时声明为静态(static),在 server 端和组件内都也可以直接通过组件(function) 来进行访问。

    比如 Index.getInitialProps

    //组件
    class Index extends React.Component{
     constructor(props){
     super(props);
     }
     //数据预取方法 静态 异步 方法
     static async getInitialProps(opt) {
     const fetch1 =await fetch('/xxx.com/a');
     const fetch2 = await fetch('/xxx.com/b');
     return {
     res:[fetch1,fetch2]
     }
     }
     render(){
     return <h1>{this.props.data.title}</h1>
     }
    }
    //node server 
    http.createServer((req, res) => {
     
     const url = req.url;
     if(url.indexOf('.')>-1) { res.end(''); return false;}
     res.writeHead(200, {
     'Content-Type': 'text/html'
     });
     
     //组件查找
     const branch = matchRoutes(routes,url);
     
     //得到组件
     const Component = branch[0].route.component;
     
     //数据预取
     const data = Component.getInitialProps(branch[0].match.params);
     
     //传入数据,渲染组件为 html 字符串
     const html = renderToString(<Component data={data}/>);
     res.end(html);
     }).listen(8080);
    复制代码

    另外还有在声明路由的时候把数据请求方法关联到路由中,比如定一个 loadData 方法,然后在查找到路由后就可以判断是否存在loadData这个方法。

    看下参考代码

    const loadBranchData = (location) => {
     const branch = matchRoutes(routes, location.pathname)
     const promises = branch.map(({ route, match }) => {
     return route.loadData
     ? route.loadData(match)
     : Promise.resolve(null)
     })
     return Promise.all(promises)
    }
    复制代码

    上面这种方式实现上没什么问题,但从职责划分的角度来说有些不够清晰,我还是比较喜欢直接通过组件来得到异步方法。

    好了,到这里我们的第二个问题 - 【获取数据的方法和逻辑写在哪里?】 解决了。

    渲染同构

    假设我们现在基于上面已经实现的代码,同时我们也使用 webpack 进行了配置,对代码进行了转换和打包,整个服务可以跑起来。

    路由能够正确匹配,数据预取正常,服务端可以直出组件的 html ,浏览器加载 js 代码正常,查看网页源代码能看到 html 内容,好像我们的整个流程已经走完。

    但是当浏览器端的 js 执行完成后,发现数据重新请求了,组件的重新渲染导致页面看上去有些闪烁。

    这是因为在浏览器端,双端节点对比失败,导致组件重新渲染,也就是只有当服务端和浏览器端渲染的组件具有相同的props 和 DOM 结构的时候,组件才能只渲染一次。

    刚刚我们实现了双端的数据预取同构,但是数据也仅仅是服务端有,浏览器端是没有这个数据,当客户端进行首次组件渲染的时候没有初始化的数据,渲染出的节点肯定和服务端直出的节点不同,导致组件重新渲染。

    数据注水

    在服务端将预取的数据注入到浏览器,使浏览器端可以访问到,客户端进行渲染前将数据传入对应的组件即可,这样就保证了props的一致。

    //node server 参考代码
    http.createServer((req, res) => {
     
     const url = req.url;
     if(url.indexOf('.')>-1) { res.end(''); return false;}
     res.writeHead(200, {
     'Content-Type': 'text/html'
     });
     console.log(url);
     
     //查找组件
     const branch = matchRoutes(routes,url);
     //得到组件
     const Component = branch[0].route.component;
     //数据预取
     const data = Component.getInitialProps(branch[0].match.params);
     //组件渲染为 html
     const html = renderToString(<Component data={data}/>);
     //数据注水
     const propsData = `<textarea style="display:none" id="krs-server-render-data-BOX">${JSON.stringify(data)}</textarea>`;
     // 通过 ejs 模板引擎将数据注入到页面
     ejs.renderFile('./index.html', {
     htmlContent: html, 
     propsData
     }, // 渲染的数据key: 对应到了ejs中的index
     (err, data) => {
     if (err) {
     console.log(err);
     } else {
     console.log(data);
     res.end(data);
     }
     })
     }).listen(8080);
     
     //node ejs html
     
     <!DOCTYPE html>
    <html lang="en">
    <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
    </head>
    <body>
     <div id="rootEle">
     <%- htmlContent %> //组件 html内容
     </div>
     
     <%- propsData %> //组件 init state ,现在是个字符串
    </body>
    </html>
    </body>
    复制代码

    需要借助 ejs 模板,将数据绑定到页面上,为了防止 XSS攻击,这里我把数据写到了 textarea 标签里。

    下图中,我看着明文数据难受,对数据做了base64编码 ,用之前需要转码,看个人需要。

    9b735200ed3f963ed4d45f60d9223208.png

    数据脱水

    上一步数据已经注入到了浏览器端,这一步要在客户端组件渲染前先拿到数据,并且传入组件就可以了。

    客户端可以直接使用id=krs-server-render-data-BOX 进行数据获取。

    第一个方法简单粗暴,可直接在组件内的constructor 构造函数 内进行获取,如果怕代码重复,可以写一个高阶组件。

    第二个方法可以通过 context 传递,只需要在入口处传入,在组件中声明 static contextType 即可。

    我是采用context 传递,为了后面方便集成 redux 状态管理 。

    // 定义 context 生产者 组件
    import React,{createContext} from 'react';
    import RootContext from './route-context';
    export default class Index extends React.Component {
     constructor(props,context) {
     super(props);
     }
     render() {
     return <RootContext.Provider value={this.props.initialData||{}}>
     {this.props.children}
     </RootContext.Provider>
     }
    }
    //入口 app.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { BrowserRouter } from 'react-router-dom';
    import Routes from '../';
    import Provider from './provider';
    //渲染入口 接收脱水数据
    function renderUI(initialData) {
     ReactDOM.hydrate(<BrowserRouter><Provider initialData={initialData}>
     <Routes />
     </Provider>
     </BrowserRouter>, document.getElementById('rootEle'), (e) => {
     });
    }
    //函数执行入口
    function entryIndex() {
     let APP_INIT_DATA = {};
     let state = true;
     //取得数据
     let stateText = document.getElementById('krs-server-render-data-BOX');
     if (stateText) {
     APP_INIT_DATA = JSON.parse(stateText.value || '{}');
     }
     if (APP_INIT_DATA) {//客户端渲染
     
     renderUI(APP_INIT_DATA);
     }
    }
    //入口执行
    entryIndex();
    复制代码

    行文至此,核心的内容已经基本说完,剩下的就是组件内如何使用脱水的数据。

    下面通过 context 拿到数据 , 代码仅供参考,可根据自己的需求来进行封装和调整。

    import React from 'react';
    import './css/index.scss';
    export default class Index extends React.Component {
     constructor(props, context) {
     super(props, context);
     //将context 存储到 state 
     this.state = {
     ... context
     }
     }
     //设置此参数 才能拿到 context 数据
     static contextType = RootContext;
     //数据预取方法
     static async getInitialProps(krsOpt) {
     if (__SERVER__) {
     //如果是服务端渲染的话 可以做的处理,node 端设置的全局变量
     }
     const fetch1 = fetch.postForm('/fe_api/filed-manager/get-detail-of-type', {
     data: { ofTypeId: 4000 }
     });
     const fecth2 = fetch.postForm('/fe_api/filed-manager/get-detail-of-type', {
     data: { ofTypeId: 2000 }
     });
     const resArr = await fetch.multipleFetch(fetch1, fecth2);
     //返回所有数据
     return {
     page: {},
     fetchData: resArr
     }
     }
     componentDidMount() {
     if (!this.isSSR) { //非服务端渲染需要自身进行数据获取
     Index.getInitialProps(this.props.krsOpt).then(data => {
     this.setState({
     ...data
     }, () => {
     //可有的一些操作
     });
     });
     }
     }
     render() {
     //得到 state 内的数据,进行逻辑判断和容错,然后渲染
     const { page, fetchData } = this.state;
     const [res] = fetchData || [];
     return <div className="detailBox">
     {
     res && res.data.map(item => {
     return <div key={item.id}>{item.keyId}:{item.keyName}---{item.setContent}</div>
     })
     }
     </div>
     }
    }
    复制代码

    到此我们的第三个问题:【服务端 html 节点无法重用 】已经解决,但人不够完美,请继续看。

    css 过滤

    我们在写组件的时候大部分都会导入相关的 css 文件。

    import './css/index.scss';//导入css
    //组件
    class Index extends React.Component{
     constructor(props){
     super(props);
     }
     static async getInitialProps() {
     const fetch1 =await fetch('/xxx.com/a');
     const fetch2 = await fetch('/xxx.com/b');
     return {
     res:[fetch1,fetch2]
     }
     }
     render(){
     return <h1>{this.props.data.title}</h1>
     }
    }
    复制代码

    但是这个 css 文件在服务端无法执行,其实想想在服务端本来就不需要渲染 css 。为什么不直接干掉? 所以为了方便,我这里写了一个babel 插件,在编译的时候干掉 css 的导入代码。

    /**
     * 删除 css 的引入
     * 可能社区已经有现成的插件但是不想费劲儿找了,还是自己写一个吧。 
     */
    module.exports = function ({ types: babelTypes }) {
     return {
     name: "no-require-css",
     visitor: {
     ImportDeclaration(path, state) {
     let importFile = path.node.source.value;
     if(importFile.indexOf('.scss')>-1){
     // 干掉css 导入
     path.remove();
     }
     }
     }
     };
    };
    //.babelrc 中使用
     "plugins": [
     "./webpack/babel/plugin/no-require-css" //引入 
     ]
    复制代码

    动态路由的 SSR

    现在要说一个更加核心的内容,也是本文的一个压轴亮点,可以说是全网唯一,我之前也看过很多文章和资料都没有细说这一块儿的实现。

    不知道你有没有发现,上面我们已经一步一步的实现了 React SSR 同构 的完整流程,但是总感觉少点什么东西。

    SPA模式下大部分都会实现组件分包和按需加载,防止所有代码打包在一个文件过大影响页面的加载和渲染,影响用户体验。

    那么基于 SSR 的组件按需加载如何实现呢?

    当然我们所限定按需的粒度是路由级别的,请求不同的路由动态加载对应的组件。

    如何实现组件的按需加载?

    在 webpack2 时期主要使用require.ensure方法来实现按需加载,他会单独打包指定的文件,在当下 webpack4,有了更加规范的的方式实现按需加载,那就是动态导入 import('./xx.js'),当然实现的效果和 require.ensure是相同的。

    咱们这里只说如何借助这个规范实现按需加载的路由,关于动态导入的实现原理先按下不表。

    我们都知道 import 方法传入一个js文件地址,返回值是一个 promise 对象,然后在 then 方法内回调得到按需的组件。他的原理其实就是通过 jsonp 的方式,动态请求脚本,然后在回调内得到组件。

    import('../index').then(res=>{
     //xxxx
    });
    复制代码

    那现在我们已经得到了几个比较有用的信息。

    • 如何加载脚本 - import 结合 webpack 自动完成
    • 脚本是否加载完成 - 通过在 then 方法回调进行处理
    • 获取异步按组件 - 通过在 then 方法回调内获取

    我们可以试着把上面的逻辑抽象成为一个组件,然后在路由配置的地方进行导入后,那么是不是就完成了组件的按需加载呢?

    先看下按需加载组件, 目的是在 import 完成的时候得到按需的组件,然后更改容器组件的 state,将这个异步组件进行渲染。

    /**
     * 按需加载的容器组件
     * @class Bundle
     * @extends {Component}
     */
    export default class Async extends React.Component {
     constructor(props) {
     super(props);
     this.state = {
     COMPT: null
     };
     }
     UNSAFE_componentWillMount() {
     //执行组件加载
     if (!this.state.COMPT) {
     this.load(this.props);
     }
     }
     load(props) {
     this.setState({
     COMPT: null
     });
     //注意这里,返回Promise对象; C.default 指向按需组件
     props.load().then((C) => {
     this.setState({
     COMPT: C.default ? C.default : COMPT
     });
     });
     }
     render() {
     return this.state.COMPT ? this.props.children(this.state.COMPT) : <span>正在加载......</span>;
     }
    }
    复制代码

    Async 容器组件接收一个 props 传过来的 load 方法,返回值是 Promise类型,用来动态导入组件。

    在生命周期 UNSAFE_componentWillMount 得到按需的组件,并将组件存储到 state.COMPT内,同时在 render 方法中判断这个状态的可用性,然后调用this.props.children 方法进行渲染。

    //调用
    const LazyPageCom = (props) => (
     <Async load={() => import('../index')}>
     {(C) => <C {...props} />}//返回函数组件
     </Async>
    );
    复制代码

    当然这只是其中一种方法,也有很多是通过 react-loadable 库来进行实现,但是实现思路基本相同,有兴趣的可以看下源码。

    //参考代码
    import React from 'react';
    import Loadable from 'react-loadable';
    //loading 组件
    const Loading =()=>{
     return (
     <div>loading</div>
     ) 
    }
    //导出组件
    export default Loadable({
     loader:import('../index'),
     loading:Loading
    });
    复制代码

    到这里我们已经实现了组件的按需加载,剩下就是配置到路由。

    看下伪代码

    //index.js
    class Index extends React.Component {
     render() {
     return <div>detail</div>
     }
    }
    //detail.js
    class Detail extends React.Component {
     render() {
     return <div>detail</div>
     }
    }
    //routes.js
    //按需加载 index 组件
    const AyncIndex = (props) => (
     <Async load={() => import('../index')}>
     {(C) => <C {...props} />}
     </Async>
    );
    //按需加载 detai 组件
    const AyncDetail = (props) => (
     <Async load={() => import('../index')}>
     {(C) => <C {...props} />}
     </Async>
    );
    const routes = [
     {
     path: "/",
     exact: true,
     component: AyncIndex
     },
     {
     path: '/detail', exact: true,
     component: AyncDetail,
     }
    ];
    复制代码

    结合路由的按需加载已经配置完成,先不管 server端 是否需要进行调整,此时的代码是可以运行的,按需也是 ok 的。

    但是ssr无效了,查看网页源代码无内容。

    动态路由 SSR 双端配置

    ssr无效了,这是什么原因呢?

    上面我们在做路由同构的时候,双端使用的是同一个 route配置文件routes-config.js,现在组件改成了按需加载,所以在路由查找后得到的组件发生改变了 - AyncDetail,AyncIndex,根本无法转换出组件内容。

    ssr 模式下 server 端如何处理路由按需加载

    其实很简单,也是参考客户端的处理方式,对路由配置进行二次处理。server 端在进行组件查找前,强制执行 import 方法,得到一个全新的静态路由表,再去进行组件的查找。

    //获得静态路由
    import routes from 'routes-config.js';//得到动态路由的配置
    export async function getStaticRoutes() {
     const staticRoutes = [];//存放新路由
     for (; i < len; i++) {
     let item = routes[i];
     
     //存放静态路由
     staticRoutes.push({
     ...item,
     ...{
     component: (await item.component().props.load()).default
     }
     });
     
     }
     return staticRoutes; //返回静态路由
    }
    复制代码

    如今我们离目标更近了一步,server端已兼容了按需路由的查找。但是还没完!

    我们这个时候访问页面的话,ssr 生效了,查看网页源代码可以看到对应的 html 内容。

    但是页面上会显示直出的内容,然后显示<span>正在加载......</span> ,瞬间又变成直出的内容。

    ssr 模式下 client 端如何处理路由按需加载

    这个是为什么呢?

    是不是看的有点累了,再坚持一下就成功了。

    其实有问题才是最好的学习方式,问题解决了,路就通了。

    首先我们知道浏览器端会对已有的节点进行双端对比,如果对比失败就会重新渲染,这很明显就是个问题。

    咱分析一下,首先服务端直出了 html 内容,而此时浏览器端js执行完后需要做按需加载,在按需加载前的组件默认的内容就是<span>正在加载......</span> 这个缺省内容和服务端直出的 html 内容完全不同,所以对比失败,页面会渲染成 <span>正在加载......</span>,然后按需加载完成后组件再次渲染,此时渲染的就是真正的组件了。

    如何解决呢?

    其实也并不复杂,只是不确定是否可行,试过就知道。

    既然客户端需要处理按需,那么我们等这个按需组件加载完后再进行渲染是不是就可以了呢?

    答案是:可以的!

    **如何按需呢? **

    向“服务端同学”学习,找到对应的组件并强制 执行import按需,只是这里不是转换为静态路由,只找到按需的组件完成动态加载即可。

    既然有了思路,那就撸起代码。

    import React,{createContext} from 'react';
    import RootContext from './route-context';
    export default class Index extends React.Component {
     constructor(props,context) {
     super(props);
     }
     render() {
     return <RootContext.Provider value={this.props.initialData||{}}>
     {this.props.children}
     </RootContext.Provider>
     }
    }
    //入口 app.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { BrowserRouter } from 'react-router-dom';
    import Routes from '../';
    import Provider from './provider';
    //渲染入口
    function renderUI(initialData) {
     ReactDOM.hydrate(<BrowserRouter><Provider initialData={initialData}>
     <Routes />
     </Provider>
     </BrowserRouter>, document.getElementById('rootEle'), (e) => {
     });
    }
    function entryIndex() {
     let APP_INIT_DATA = {};
     let state = true;
     //取得数据
     let stateText = document.getElementById('krs-server-render-data-BOX');
     
     //数据脱水
     if (stateText) {
     APP_INIT_DATA = JSON.parse(stateText.value || '{}');
     }
     if (APP_INIT_DATA) {//客户端渲染
     
     - renderUI(true, APP_INIT_DATA);
     //查找组件
     + matchComponent(document.location.pathname, routesConfig()).then(res => {
     renderUI(true, APP_INIT_DATA);
     });
     }
    }
    //执行入口
    entryIndex();
    复制代码

    matchComponent 是我封装的一个组件查找的方法,在文章开始已经介绍过类似的实现,代码就不贴了。

    核心亮点说完,整个流程基本结束,剩下的都是些有的没的了,我打算要收工了。

    其他

    SEO 支持

    页面的 SEO 效果取决于页面的主体内容和页面的 TDK(标题 title,描述 description,关键词 keyword)以及关键词的分布和密度,现在我们实现了 ssr所以页面的主体内容有了,那如何设置页面的标题并且让每个页面(路由)的标题都不同呢?

    只要我们每请求一个路由的时候返回不同的 tdk 就可以了。

    这里我在所对应组件数据预取的方法内加了约定,返回的数据为固定格式,必须包含 page 对象,page 对象内包含 tdk 的信息。

    看代码瞬间就明白。

    import './css/index.scss';
    //组件
    class Index extends React.Component{
     constructor(props){
     super(props);
     }
     static async getInitialProps() {
     const fetch1 =await fetch('/xxx.com/a');
     const fetch2 = await fetch('/xxx.com/b');
     return {
     page:{
     tdk:{
     title:'标题',
     keyword:'关键词',
     description:'描述'
     }
     }
     res:[fetch1,fetch2]
     }
     }
     render(){
     return <h1>{this.props.data.title}</h1>
     }
    }
    复制代码

    这样你的 tdk 可以根据你的需要设置成静态还是从接口拿到的。然后可以在 esj 模板里进行绑定,也可以在 componentDidMount通过 js document.title=this.state.page.tdk.title设置页面的标题。

    <!DOCTYPE html>
    <html lang="en">
    <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <meta name="keywords" content="<%=page.tdk.keyword%>" />
     <meta name="description" content="content="<%=page.tdk.description%>" />
     <title><%=page.tdk.title%></title>
    </head>
    <body>
     <div id="rootEle">
     <%- htmlContent %>
     </div>
     <%- propsData %>
    </body>
    </html>
    </body>
    <%page.staticSource.js.forEach(function(item){%>
    复制代码

    fetch 同构

    可以使用isomorphic-fetch、axios或者whatwg-fetch + node-fetch 等库来实现支持双端的 fetch 数据请求,这里推荐使用axios 主要是比较方便。

    TODO 和 思考

    没有介绍结合 redux 状态管理的 ssr 实现,其实也不复杂,关键还是看业务中是否需要使用redux,因为文中已经实现了使用 context 传递数据,直接改成按store 传递也很容易,但是更多的还是对 react-redux 的应用。

    //渲染入口 代码仅供参考 
    function renderUI(initialData) {
     ReactDOM.hydrate(<BrowserRouter><Provider store={initialData}>
     <Routes />
     </Provider>
     </BrowserRouter>, document.getElementById('rootEle'), (e) => {
     });
    }
    复制代码

    服务端同构渲染虽然可以提升首屏的出现时间,利于 SEO,对低端用户友好,但是开发复杂度有所提高,代码需要兼容双端运行(runtime),还有一些库只能在浏览器端运行,在服务端加载会直接报错,这种情况就需要进行做一些特殊处理。

    同时也会大大的增加服务端负载,当然这都容易解决,可以改用renderToNodeStream() 方法通过流式输出来提升服务端渲染性能,可以进行监控和扩容,所以是否需要 ssr 模式,还要看具体的产品线和用户定位。

    最后

    本文最初从 react ssr 的整体实现原理上进行说明,然后逐步的抛出问题,循序渐进的逐步解决,最终完成了整个React SSR 所需要处理的技术点,同时对每个技术点和问题做了详细的说明。

    但实现方式并不唯一,还有很多其他的方式, 比如 next.js, umi.js,但是原理相似,具体差异我会接下来进行对比后输出。

    源码参考

    由于上面文中的代码较为零散,恐怕不能直接运行。为了方便大家的参考和学习,我把涉及到代码进行整理、完善和修改,增加了一些基础配置和工程化处理,目前已形成一个完整的开发骨架,可以直接运行看效果,所有的代码都在这个骨架里,欢迎star 欢迎 下载,交流学习。

    项目代码地址: http://github.com/Bigerfe/koa

    说点感想

    很多东西都可以基于你现有的知识创造出来。

    只要明白了其中的原理,然后梳理出实现的思路,剩下的就是撸代码了,期间会大量的自动或被动的从你现有的知识库里进行调取,一步一步的,只要不怕麻烦,都能搞得定。

    另外如果想完整的学习react ssr,可以看我的小册,从0到1带你搭建一个完整的开发框架。

    React SSR 服务端渲染原理解析与同构实践

    最后一件事

    推荐大家关注我的公众号《前端技术江湖》,获得更多原创精选文章,另外在业余时间我做了一个面试题库网站,用来收集前端面试题,目的是节省时间,提高效率,碎片时间刷刷题。推荐大家多多关注。

    a3a03af01a43c9e96027648a691f26f7.png
    展开全文
  • 磁珠用来吸收超高频信号,象一些 RF 电路,PLL,振荡电路都需要在电源输入部分加磁珠,而电感是一种储能元件,用在 LC 振荡电路、中低频的滤波电路等,其应用频率范围很少超过 50MHz.  1.片式电感:  在电子设备...
  • 采用0.004AgNO3-0.996NaNO3混合熔融盐,用离子交换技术在德国B270光学玻璃上制备了费米折射率分布渐变波导,理论分析了费米折射率分布渐变波导的包层吸收模谱特性,导波损耗测试实验验证了解析结果。在此基础上,提出了...
  • 11_触摸反馈的机制原理全解析、手写触摸反馈算法自定义 View3-1 触摸反馈总结: 自定义 View3-1 触摸反馈 休息了几个月,HenCoder 又回来了。 这期的内容是之前说过的,自定义 View 的最后一部分:触摸反馈。触摸...

    11_触摸反馈的机制和原理全解析、手写触摸反馈算法

    自定义 View3-1 触摸反馈

    休息了几个月,HenCoder 又回来了。

    这期的内容是之前说过的,自定义 View 的最后一部分:触摸反馈。触摸反馈的概念简单,但是内部逻辑比较复杂,往往把开发者难倒、让人总也学不会的也是因为逻辑太多绕不过来,所以我这次又做了一个长长的视频来讲解原理,把最本质的东西拆解开来讲,希望能让你比较舒服地吸收。视频的制作花了 5 天时间,一共 12 分钟多,全部是讲的触摸反馈的一些最核心的逻辑和原理。

    细节上反而没有讲太多,因为讲这方面细节的文章,网上已经一大堆了,而且不少都写得很好。

    闲话说完,放视频:这个视频上传不了,总是提示视频类型错误。

    如果在页面中看不到视频,可以点 这里 去看原视频。

    总结:

    自定义触摸反馈的关键:

    1. 重写 onTouchEvent(),在里面写上你的触摸反馈算法,并返回 true(关键是 ACTION_DOWN 事件时返回 true)
    2. 如果是会发生触摸冲突的 ViewGroup,还需要重写 onInterceptTouchEvent(),在事件流开始时返回 false,并在确认接管事件流时返回一次 true,以实现对事件的拦截
    3. 当子 View 临时需要阻止父 View 拦截事件流时,可以调用父 View 的 requestDisallowInterceptTouchEvent() ,通知父 View 在当前事件流中不再尝试通过 onInterceptTouchEvent() 来拦截
    展开全文
  • 大家都知道java最大的优点是它的完全OO化和它在多年的发展过程中吸收和总结了许多先进的框架与模式,其中工厂模式就是最常用的模式之一。下面我想将我在学习和实践过程中对工厂模式的认识与了解介绍给大家。由于笔者...
  • DNS域名解析

    2018-12-23 15:12:42
    前面写过一篇关于一次完整的HTTP请求,文章内容相对浅显,又是一年跳槽季,为了各位攻城狮们来年可以找到满意的工作心仪的对象,几经思考,...如图所示,DNS域名解析是一个递归查询迭代查询的过程,下面将对每...
  • HashMap源码解析

    2019-03-10 18:12:18
    本文的所有图片以及源码解析内容都来自于微信...本文是针对jdk1.8的HashMap源码进行解析,jdk1.7jdk1.8的HashMap有很大的不同。 jdk1.8的HashMap较jdk1.7增加了红黑树的数据结构,当链表中的Node节点数超过8...
  • 重复数据删除是一个python库,使用机器学习对结构化数据快速执行模糊匹配,重复数据删除实体解析。 重复数据删除将帮助您: 从名称地址的电子表格中删除重复的条目 将具有客户信息的列表链接到具有订单历史...
  • Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大简单易用两个特征。那么什么是Java呢?Java是一种解释性的,基于对象的脚本语言...
  • E语言解析

    热门讨论 2014-02-22 17:51:19
    为满足电力系统数据模型的高效描述大量在线数据的高效交换,研发了数据模型描述语言E,它采用面向对象技术,兼容面向关系技术,吸收了XML的优点;在描述100节点CIM模型时,E语言的效率比XML高13.6倍;E语言已在电力调度...
  • 煤炭是最受欢迎的能源之一。 但是,由于高湿度,它的能量效率相对较低,燃烧过程中有害物质的释放量更大。... 本文研究了一种加热平板煤的近似解析非线性数学模型,假设吸收的微波能量同时通过热辐射对流去除。
  • 作为Bee系列解析的最后一个部分,本文分享一下Bee的UI开发。...感觉吸收了QTJS,CSS各自UI开发的长处(布局,事件,CSS标签)。 BeeUI的内部还是通过UIView的层级frame的设定实现自动布局,支持原生
  • 详解配置 Apache 服务器支持 PHP 文件的解析这里有新鲜出炉的...语法吸收了C语言、JavaPerl的特点,入门门槛较低,易于学习,使用广泛,主要适用于web开发领域。PHP的文件后缀名为php。这篇文章主要介绍了详解配置...
  • 所谓太阳能制冷,就是利用太阳集热器为吸收式制冷机提供其发生器所需要的热媒水。热媒水的温度越高,则制冷机的性能... 太阳能吸收式空调系统主要由太阳集热器和吸收式制冷机两部分构成。  1.1 吸收式制冷工作原理
  • FDDI工作原理解析

    2021-01-20 06:50:06
    一旦识别出有用的令牌,该站便将其吸收,随后便可发送一帧或多帧。这时环上没有令牌环上后,便在环上插入一新的令牌,不必像802.5令牌环那样,只有收到自己发送的帧后才能释放令牌。因此,任一时刻环上可能会有来自多...
  • 滚轮罐耳是煤矿竖井提升运输过程中的重要辅助设备,其安装在提升容器上,能够吸收提升过程中罐道和容器...应用ANSYSADAMS软件对罐耳承受水平推力时的缓冲行程进行解析,并同试验情况相对比,得到缓冲行程变化的一般规律。
  • PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解发布于 2017-09-08 20:05:36 | 124 次阅读 | 评论: 0 | 来源: ...语法吸收了C语言、JavaPerl的特点,入门门槛较低,易于学习,使用广泛,主要适用于Web开...
  • SAP技术解析

    千次阅读 2011-10-02 17:00:08
    随着对SAP了解理解的加深,不难发现SAP除了OS几乎提供了所有软件solution, 并且不断在适应更新,不间断的吸收引入业界的新技术,改造调整自己原有的技术, 新技术名词,新概念也层出不穷,比如mySAP, NetWeaver...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 273
精华内容 109
关键字:

吸收和解析