原文连接javascript
本文使用starter-kit:steamer-react react分支。此分支已集成react与preact。html
最近接手了互动视频的项目,作了一个月的运营活动。跟基础功能不一样,运营活动更为轻量。所以许多同事并不想用那么“重”的React。但同时,你们因为以前度过React的上手痛苦期后,开始体会到React的许多好处,裸写运营活动的时候,又开始对React的好处念念不忘记:良好的组件化、解放js能力的jsx等。
所以,寻找轻量化的类React解决方案便提上日程。java
选型的时候,首先有几个考量:node
基本上以上几点,Preact都可以很好的知足,所以最终选定为团队的类React轻量化框架进行使用和研究。react
相比起react-lite,Deku, Virtual-DOM,Preact虽然不是最多的star,但也能排第2,也具有测试用例,且做者开通了gitter chat跟开发者保持联系,某天在上面留言,做者也是回复得很迅速。git
性能方面,Preact也不俗。加载性能方面,因为自己的bundle在gzip后大概只有3kb,跟React相比小太多,自由就有优点。渲染性能方面,参考了一篇JS WEB FRAMEWORKS BENCHMARK系列测评文章,发现Preact在建立、更新、删除节点等方面,都有良好的表现。github
第一次性能测试:web
第二次性能测试:npm
包大小:redux
framework | version | minimized size |
---|---|---|
React | 0.14.3 | 136.0kb |
React-lite | 0.15.6 | 25kb |
Preact | 5.6.0 | 10kb |
Deku | 2.0.0-rc16 | 51.2kb |
Virtual Dom | 2.1.1 | 50.5kb |
除了性能的良好表现,此框架的浏览器兼容性也不错,能兼容目前的主流浏览器,而且在添加polyfill的状况下,可以兼容在国内还有很多份额的IE8,确实是很多还须要兼容IE8开发者的福音。
Preact的经常使用api基本跟React一致,这使得对React熟悉的开发者,彻底没有上手的难度,Preact做者单独开辟了一个文档Differences to React,介绍React与Preact的异同。Preact主要缺乏的React Api有PropType,Children, 和 Synthetic Events(合成事件)。做者解释道,PropType其实许多人都不使用,并不影响开发; Children实际上是数组,因此也并非必须的;而合成事件,因为不须要过分考虑不一样浏览器对事件处理的异同,因此也并无作过分封装。若是真的想使用以上这些缺失的React Api,做者也提供了preact-compat,使用的时候,在Webpack上的external这样替换即可:
{
// ...
resolve: {
alias: {
'react': 'preact-compat',
'react-dom': 'preact-compat'
}
}
// ...
}复制代码
对于React开发者来讲,最经常使用的就是redux, router这些周边的插件。而Preact也有提供preact-redux和preact-router,甚至还有帮助Preact作同构直出的preact-render-to-string。
Preact项目的框架小而美,合并成的dist文件也只有500行左右,比较容易学习和维护。若团队选择此框架做为React的轻量解决方案的话,咱们最好能具有维护和开发此框架的能力,这可以在遇到bug的时候第一时间修复,并且可以很好地开发一些组件,提高框架的开发效率。
做者在Getting Started里有比较好的介绍。其实不外乎就2点差别:
引入preact与引入react的差别。
引入preact的时候,大概是这样的:
import preact, { h, render, Component } from 'preact';复制代码
而引入react的时候,大概是这样的:
import React, { Component, PropTypes } from 'react';
import { render } from 'react-dom';复制代码
编译所需的插件差别。
preact的jsx编译,主要借助babel-plugin-transform-react-jsx,而react则是借助babel-preset-react。
若是你想在一个构建里面同时使用React和Preact(有的页面使用React,有的用Preact),你能够经过Webapck的loader include或者exclude,而后凭路径区分。而我在steamer-react的react-preact分支里的处理是直接用文件名后缀。若是是有React相关引入的,则用.js
后缀,而有Preact相关引入的,则用.jsx
后缀。
粗略看了一下Preact的实现,简单介绍一下。
Virtual Dom算是类React框架的最大卖点。Preac做者写了一篇WTF is JSX。主要就是借助babel-plugin-transform-react-jsx的能力,里面有个pragma参数,用于设定用什么函数来作virtual dom的转换。此处定义的是preact.h
["transform-react-jsx", { "pragma":"preact.h" }]复制代码
因此,你会看到编译后,有相似的代码:
_preact2.default.h(
'p',
{ className: 'info-content' },
item.des
)复制代码
查看源码,preac定义了h
的函数,用于将传入的值转换成virtual dom:
function h(nodeName, attributes, firstChild) {
// some code here
}复制代码
因此,若是传入上面的p和对应属性,则会转换成下面的对象:
VNode {nodeName: "p", attributes: {class:"info-content"}, children: undefined, key: undefined}复制代码
但virtual dom须要转换成真实的dom,还须要一个函数进行转换。在Preact中,大致是经过这个流程,而后最终转换成真实dom:
render (相似于react-dom里的render,主入口,触发渲染) => diff => idiff (看起来应该是作dom diff) => createNode (生成真实dom)复制代码
组件化也是类React框架的一大特点。Preact的组件化,主要是经过Component
这一方法来实现的。主要包括,setState,render以及一众生命周期。主要的渲染,生命周期的触发,也主要定义在renderComponent
和setComponentProps
方法内。用户的自定义组件只须要继承Component
就能够自由使用Preact组件化的能力。
Preact并无像React那样本身实现了一套事件机制,主要仍是用浏览器自带的能力。所以,在给生成真实dom并经过setAccessor
给dom插入属性的时候,有这么一段代码:
else if ('o' === name[0] && 'n' === name[1]) {
var l = node._listeners || (node._listeners = {});
name = toLowerCase(name.substring(2));
if (value) {
if (!l[name]) node.addEventListener(name, eventProxy);
} else if (l[name]) node.removeEventListener(name, eventProxy);
l[name] = value;
}复制代码
判断属性中是否含有o
和n
,也就是在看,有没有on
开头的属性(通常就是事件)。而后就进行addEventListener或者removeEventListener。看起来跟咱们写原生js的事件绑定没有什么区别。
若有错误,恳请斧正。