设计理念与生态体系
React 设计理念:专注于视图层的组件库
- ES6 React
- Virtual DOM:虚拟
DOM - Component-Driven Development:组件驱动开发
- Immutability:不变性
- Top-down Rendering:自上而下的渲染
- 渲染路径与优化
- 打包工具、构建请求、调试、路由等
- Isomorphic Application:同构应用
Here are a few reasons why React has become so popular so quickly:
- Working with the DOM API is hard. React basically gives developers the ability to work with a virtual browser that is faster and more friendly than the real browser. React’s virtual browser acts like an agent between the developer and the real browser.
- React enables developers to declaratively describe their User Interfaces and model the state of those interfaces. This means instead of coming up with steps to describe transactions on interfaces, developers just describe the interfaces in terms of a final state (like a function). When transactions happen to that state, React takes care of updating the User Interfaces based on that.
- React is just JavaScript, there is a very small API to learn, just a few functions and how to use them. After that, your JavaScript skills are what make you a better React developer. There are no barriers to entry. A JavaScript developer can become a productive React developer in an hour or so.
But there’s a lot more to it than just that. Let’s attempt to cover all the reasons behind React’s rising popularity. One reason is its Virtual DOM (React’s reconciliation algorithm). We’ll work through an example to show the actual practical value of having such an algorithm at your command.
小而美的视图层
声明式组件
在
命令式编程与声明式编程
命令式编程
const doubleMap = (numbers) => {
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}
return doubled;
};
console.log(doubleMap([2, 3, 4])); // [4, 6, 8]
而使用声明式编程来实现相同的操作时,我们首先会将变换的过程抽象出来,以更清晰的方式描述这个过程:
const doubleMap = (numbers) => numbers.map((n) => n * 2);
console.log(doubleMap([2, 3, 4])); // [4, 6, 8]
命令式编程中我们会频繁地使用声明语句for
、if
、switch
、throw
这些。而函数式编程更加依赖于表达式
2 * 2;
doubleMap([2, 3, 4]);
Math.max(4, 3, 2);
jQuery
<i>
节点的方式将数据插入到
- 现代浏览器的发展与逐步统一的原生
API :由于浏览器的历史原因,曾经的前端开发为了兼容不同浏览器怪癖,需要增加很多成本。jQuery 由于提供了非常易用的API ,屏蔽了浏览器差异,极大地提高了开发效率。这也导致很多前端只懂jQuery 。其实这几年浏览器更新很快,也借鉴了很多jQuery 的API ,如querySelector
,querySelectorAll
和jQuery 选择器同样好用,而且性能更优。 - 前端由以
DOM 为中心到以数据/ 状态为中心:jQuery 代表着传统的以DOM 为中心的开发模式,但现在复杂页面开发流行的是以React 为代表的以数据/ 状态为中心的开发模式。应用复杂后,直接操作DOM 意味着手动维护状态,当状态复杂后,变得不可控。React 以状态为中心,自动帮我们渲染出DOM ,同时通过高效的DOM Diff 算法,也能保证性能。 - 不支持同构渲染与跨平台渲染:
React Native 中不支持jQuery 。同构就是前后端运行同一份代码,后端也可以渲染出页面,这对SEO 要求高的场景非常合适。由于React 等流行框架天然支持,已经具有可行性。当我们在尝试把现有应用改成同构时,因为代码要运行在服务器端,但服务器端没有DOM ,所以引用jQuery 就会报错。这也是要移除jQuery 的迫切原因。同时不但要移除jQuery ,在很多场合也要避免直接操作DOM 。 - 性能缺陷:
jQuery 的性能已经不止一次被诟病了,在移动端兴起的初期,就出现了Zepto 这样的轻量级框架,Angular 1 也内置了jqlite 这样的小工具。前端开发一般不需要考虑性能问题,但你想在性能上追求极致的话,一定要知道jQuery 性能很差。原生API 选择器相比jQuery 丰富很多,如document.getElementsByClassName
性能是$(classSelector)
的50 多倍!
对于很多初学
声明式组件
我们在上文中提到过,声明式编程的核心理念在于描述做什么,通过声明式的方式我们能够以链式方法调用的形式对于输入的数据流进行一系列的变换处理,本部分我们还是以
jQuery(function ($) {
var username = "";
var password = ""; // Disable the button at start
$("#signup-button").attr("disabled", true); // Email field
$("#email-field").on("blur", function () {
username = $(this).val();
if (username == "") {
$("#email-error").html("Please enter email address");
$("#signup-button").attr("disabled", true);
} else {
checkValues();
}
}); // Password field
$("#password-field").on("blur", function () {
password = $(this).val();
if (password == "") {
$("#password-error").html("Please enter password");
$("#signup-button").attr("disabled", true);
} else {
checkValues();
}
}); // Both fields
function checkValues() {
if (username != "" && password != "") {
$("#email-error").html("");
$("#password-error").html("");
$("#signup-button").attr("disabled", false);
}
}
});
上述代码中我们以符合平时思维逻辑的、声明语句形式的方式描述了整个业务逻辑,这就是典型的命令式编程思想。不过这种方式也显而易见的存在很多的代码冗余,导致整体的可读性与重构性降低,譬如邮箱与密码这两个输入域都会在失去焦点时进行验证,并且判断是否设置按钮失效。而在声明式编程中,我们可以将公用的部分业务逻辑代码,即是偏向于计算的、表达式形式的代码剥离出来,可以得到如下的封装:
jQuery(function ($) {
function checkIfEmpty(e) {
return !e.target.value;
}
function checkIfBothEmpty(noEmail, noPass) {
return noEmail || noPass;
}
function getEmailMessage(noEmail) {
return noEmail ? "Please enter email address." : "";
}
function getPasswordMessage(noPassword) {
return noPassword ? "Please enter password." : "";
} // Email field
var email = $("#email-field").asEventStream("blur").map(checkIfEmpty);
email.map(getEmailMessage).assign($("#email-error"), "html"); // Password field
var password = $("#password-field").asEventStream("blur").map(checkIfEmpty);
password.map(getPasswordMessage).assign($("#password-error"), "html"); // Both fields
Bacon.combineWith(checkIfBothEmpty, email, password).assign(
$("#signup-button"),
"attr",
"disabled"
);
});
代码更加清晰易懂,并且对于空判断这些公共逻辑代码的提出也方便了我们进行重构或者对于业务逻辑的变化进行快速响应。未来如果我们需要添加
<select value={this.state.value} onChange={this.handleChange}>
{somearray.map((element) => (
<option value={element.value}>{element.text}</option>
))}
</select>
我们并不需要手动地使用for
循环来控制元素生成,这也就是声明式的精髓。
JSX
很多人第一次学习React.createElement
函数的抽象,而该函数主要的作用是将朴素的
在现代浏览器中,对于
Virtual DOM
如我们所知,在浏览器渲染网页的过程中,加载到data-reactid
属性,这有助于