65.9K
CodeProject 正在变化。 阅读更多。
Home

从 jQuery 到 React:前端开发思维的转变

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (8投票s)

2017 年 9 月 25 日

CPOL

7分钟阅读

viewsIcon

18976

前端开发中的函数式编程

引言

近年来,Web 前端一直令人兴奋:每年都有新的框架和工具出现。如今,前端有三大框架:Angular、React 和 Vue,其中最受欢迎的可能就是 React。React 代表了当前前端开发的思想和趋势:Web 组件。此外,这也是当前 Web 开发架构的一部分:前后端分离。对于传统的后端 MVC 框架(比如众多 PHP 框架),这些框架中的视图部分已经不再重要(你还记得那些“模板引擎”吗?)。如今,后端变成了 Restful API 或微服务(控制器和模型仍然存在,输出的只是供前端和移动应用使用的 JSON 数据)。

React

多年来,我们一直将 jQuery 与后端 MVC 框架一起用于前端开发,它仍然方便且出色,但在今天前后端分离的架构中,它已不再适用。是时候切换到 React 了。

React 不仅仅是一个 JavaScript 库,它是一个完整的技术栈(我们需要使用 Babel 转译 ES2015;需要使用 Webpack 打包模块;需要使用 Gulp 进行自动化任务等)。更重要的是,React 和 Redux(及其中间件)代表了一种不同的编程方法——前端应用中的函数式编程。

React 将网页分成各种组件,每个组件都封装了

  • 展示:HTML/JSX
  • 样式:CSS
  • 行为:JavaScript

并且每个组件都可以重用。所有组件共同构成一个应用程序。这听起来与 MVC 推广了十多年的理念——将代码与 HTML 分离——大相径庭。让我们首先创建一个 React 组件来看看它长什么样。如果你手头没有 React 项目脚手架,可以使用 Facebook 的 Create React App 工具。

它非常易于使用,只需运行

npm install --global create-react-ap

即可全局安装此 npm 包。你也可以使用 Yarn (https://yarn.npmjs.net.cn/en/),这是另一个更新的 NodeJS 包管理器。然后,你可以使用此命令创建 React 项目

create-react-app react-test

然后进入项目目录

cd react-test/

现在,如果你查看 package.json 文件的内容

{
// .....
"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test --env=jsdom",
  "eject": "react-scripts eject"
}
}

在“scripts”成员中有一个“start”命令。要启动应用程序,请运行

npm start

npm run start

(完整的命令实际上是“npm run + xxx”,但对于 start 命令,可以缩短为“npm start”)。然后,在你的浏览器中(强烈推荐 Chrome,迄今为止 React 最佳选择),打开 https://:3000/ 即可查看我们刚刚创建的应用程序。

你可以使用你喜欢的编辑器或 IDE 打开项目(推荐 VS Code 作为编辑器,或 WebStorm/PHPStorm 作为 IDE)。项目的入口文件是 ./src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

registerServiceWorker();

你在浏览器中看到的页面来自此组件

import App from './App';

在这个组件中,它还包含它的 CSS 文件

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';  // Import CSS file for this App component

class App extends Component {
render() {
  return (
    <div className="App">
      <div className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h2>Welcome to React</h2>
      </div>
      <p className="App-intro">
        To get started, edit <code>src/App.js</code> and save to reload.
      </p>
    </div>
  );
}
}

export default App;

所以这个 App 组件封装了 CSS,但它没有 JavaScript 代码,让我们添加一个按钮并为其分配一个事件处理程序

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';  // Import CSS file for this App component

// Add css style for the new button

const buttonStyle = {
  margin: '10px',
  width: '150px',
  backgroundColor: '#F58423',
  borderRadius: '3px'
};

class App extends Component {
  onButtonClick() {
    alert("Clicked the button!");
}

render() {
  return (
    <div className="App">
      <div className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h2>Welcome to React</h2>
      </div>
      <p className="App-intro">
        To get started, edit <code>src/App.js</code> and save to reload.
      </p>
      <div>
          <button style={buttonStyle} onClick={this.onButtonClick.bind(this)}>Click Me</button>
      </div>
    </div>
  );
}
}

export default App;

请注意,CSS 属性“background-color”在 JSX 中需要更改为 backgroundColor。为什么?原因是所有 JSX 实际上都在 JavaScript 运行时下运行,它们实际上是 JavaScript 代码,而“background-color”在 JavaScript 中不是一个有效的变量名。

现在我们需要一个按钮的事件处理程序。在 React 中,我们可以使用 onClick 为元素添加事件处理函数,但这个 onClick 与 HTML 的 onclick 属性完全不同。多年前,开发人员开始避免在 HTML 中嵌入 onclick,因为这会使代码混乱。相反,我们一直使用 jQuery 的方式添加事件处理函数。我们会这样做

      <div>
          <button id=”testButton”>Click Me</button>
      </div>

在 HTML 中,我们为 button 元素添加了一个 id 属性,然后在 <script> 标签中,我们将使用 jQuery

<script src="./jquery.min.js"></script>
<script>
      $(document).ready(function () {
          $('#testButton').click(function () {
              alert("Clicked the button!");
          });
      });
</script>

jQuery 的方法是

  1. 使用 CSS 规则获取按钮元素
  2. 将匿名回调函数作为事件处理程序附加
  3. 在事件回调函数中,执行此 onclick 事件所需的操作。例如,选择页面上的其他 DOM 元素,并修改/隐藏/显示它们

这种编程模式——“当事件发生时,在回调函数中选择 DOM 元素进行修改或显示/隐藏”,是最简单的方法,而且非常容易理解。这就是 jQuery 当时被发明的原因。

React 和 JSX 是在开倒车吗?不,React 正在以不同的方式做事。当我们在 HTML 中添加 onclick 事件处理程序时,这个 onclick 事件处理程序函数在全局作用域下执行,这可能导致“全局变量污染”。此外,向 DOM 元素添加许多事件处理函数可能会影响页面性能,特别是当它们数量巨大时。

但 React 没有这样的问题。当我们在 JSX 中使用 onClick 时,它不会转换为 HTML 的 onclick。React 使用“事件委托”来处理事件,因此无论我们在 JSX 中使用了多少个 onClick,最终在 DOM 树的顶部只会挂载一个事件处理函数。所有点击事件都由这个主要事件处理函数捕获,然后分配给特定的函数进行处理。

此外,React 有许多组件生命周期函数,例如

  • componentWillMount()
  • componentDidMount()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • componentDidUpdate()

它们只是“钩子函数”。多亏了这些生命周期函数,当组件被卸载时,该组件的所有事件处理函数都将被释放。顺便说一句,这些生命周期函数的命名约定借鉴了 iOS,例如

  • loadView()
  • viewDidLoad()
  • viewWillAppear()
  • viewDidAppear()

React 还带来了虚拟 DOM 及其 Diff 算法,因为直接操作 DOM 节点会降低页面性能。即使只进行一次 DOM 操作,也会导致浏览器重新布局和渲染页面,这比执行 JavaScript 语句慢得多。与 jQuery 不同,React 是数据驱动的:事件触发数据(状态)变化,然后数据(状态)变化触发渲染;在渲染过程中,虚拟 DOM 会与上次的虚拟 DOM 进行比较并找出差异。然后,在修改实际 DOM 树时,它只需要处理更改的部分。

从上面的例子可以看出,传统上 CSS 在 .css 文件中,JavaScript 在 .js 文件中,HTML 在 .html 或模板文件中。React 将这三者都放在一个 .js.jsx 文件中,因为这三者的目的是协同工作以实现一个功能。这种方式体现了高内聚的原则。如今,越来越多的开发人员意识到后端的 MVC 风格可能不最适合前端,而 React(和 Redux)及时地将函数式响应式编程引入了前端。

函数式编程中有一些重要的特性

  1. 函数是头等公民。 这在 JavaScript 中是正确的。许多命令式编程语言缺乏但 JavaScript 拥有的一项特性是在另一个函数内部声明一个函数。此外,JavaScript 函数是一个对象,可以作为参数传递。而且,一个函数的结果也可以是一个函数。
  2. 数据是不可变的。 在“纯”函数式编程中,数据是不可变的,因此没有“变量”。所有数据都不能更改,如果需要更改,唯一的方法是生成新数据。
    这一理念在 React 和 Redux 中得到了很好的体现
    • 在 React 中,它强调一个组件不应该改变传入的 props;
    • 在 Redux 中,一个 reducer 函数不能修改 store 的状态,而只是生成一个新的状态,通常使用 ES2015 的 Object.assign() 方法。
  3. 使用纯函数。 纯函数就像数学中的函数一样,没有副作用,这意味着函数的输出只由输入决定。

在 React 中,每个组件的 render() 函数都是一个纯函数。这意味着渲染结果只由组件的状态和 props 决定。React 的一个公式是

UI = f(state)

在 Redux 中,每个 reducer 也必须是一个纯函数。

 

尽管许多项目仍在使用 jQuery,但 React 和 Redux 为前端领域带来了生机。这种转变不仅仅是关于一种新语言/库,而是一种技术栈和一种编程范式

P.S. Facebook 有一篇名为“Thinking in React”的好文章,值得一读

© . All rights reserved.