JS 世界变化很快,现在有一些新的工具出现在了我们的视野中。今天,我们将探讨 Browserify、Gulp 和 React,看看它们是否适合我们的项目。您可能听说过它们,但还没有时间去尝试。因此,我们将了解使用 Browserify、使用 Gulp 和使用 React 的优缺点。因为了解我们的选择肯定没有坏处。
Browserify:为浏览器打包 Node 模块
Browserify 是一款开发工具,它允许我们在浏览器中编写 Node 风格的模块,或者包含来自 npm 的实际 Node 模块。模块以单独的文件编写,可以进行 `export` 操作,并且模块可以通过 `require` 引入其他模块。Browserify 然后可以解析我们的主 JS 模块,构建依赖关系树,并将所有内容打包在一起。
一个很棒的事情是,现在 NPM 上的数万个模块都可以用于我们的项目。依赖项在 `package.json` 中定义,如果我们的项目 `requires` 它们,Browserify 将把这些依赖项与我们的 JS 一起打包。例如,请看这个 `package.json`
/* package.json */
{
"name": "hipApp",
"description": "Showing off hip stuff",
"dependencies": {
"browserify": "~3.44.x",
"gulp": "3.8.x",
"react": "0.11.x",
"underscore": "*"
}
}
一旦我们运行 `npm install`,我们就可以在项目中使用 React 和 Underscore 等模块。现在我们只需在项目中 `require` 它们即可
/* app.js */
var React = require('react');
var myModule = require('./myModule');
// ...
然后我们调用 Browserify
browserify --debug app.js > bundle.js
Browserify 将为我们包含来自 npm 的 React。请注意,它甚至会找出要包含哪些本地模块。我们包含了 `./myModule`,它是与 `app.js` 相同文件夹中的另一个模块。
让我们将这种依赖项加载风格与 AMD 等技术进行比较,AMD 主要由 RequireJS 实现。它们都是 JS 模块定义 API,但实现方式不同。Browserify 遵循 CommonJS,CommonJS 适用于服务器端,而 RequireJS 遵循 AMD,AMD 适用于浏览器端。但是,任何一种都可以在任何环境中使用。
Browserify 的一个很棒之处在于,所有 NPM 模块都可用于我们的项目,而且数量还在不断增加(已超过 86K)。它的模块也不需要包装在 `define` 调用中。
尽管 Browserify 需要预先加载所有模块,这意味着需要构建步骤。AMD 是异步的,因此可以延迟加载模块,只需要刷新页面即可。尽管我们可以使用 Gulp 自动化 Browserify 的构建步骤。
Gulp:流式构建系统
Gulp 是一种 JS 构建系统,类似于 Grunt,它利用“流”或管道,并专注于代码而非配置。构建系统通常设置为监视项目的变化,然后自动处理常见的构建步骤,例如打包、预编译或压缩。Gulp 和 Grunt 都有大量的插件来帮助完成这些事情。Browserify 就是其中一个插件。
让我们来看一个 Gulpfile 的示例。它包含了一些 React JSX 文件的功能,我们还没有讨论过,但稍后会派上用场。请阅读 Gulpfile 中的注释以了解详情
/* gulpfile.js */
// Load some modules which are installed through NPM.
var gulp = require('gulp');
var browserify = require('browserify'); // Bundles JS.
var del = require('del'); // Deletes files.
var reactify = require('reactify'); // Transforms React JSX to JS.
var source = require('vinyl-source-stream');
var stylus = require('gulp-stylus'); // To compile Stylus CSS.
// Define some paths.
var paths = {
css: ['src/css/**/*.styl'],
app_js: ['./src/js/app.jsx'],
js: ['src/js/*.js'],
};
// An example of a dependency task, it will be run before the css/js tasks.
// Dependency tasks should call the callback to tell the parent task that
// they're done.
gulp.task('clean', function(done) {
del(['build'], done);
});
// Our CSS task. It finds all our Stylus files and compiles them.
gulp.task('css', ['clean'], function() {
return gulp.src(paths.css)
.pipe(stylus())
.pipe(gulp.dest('./src/css'));
});
// Our JS task. It will Browserify our code and compile React JSX files.
gulp.task('js', ['clean'], function() {
// Browserify/bundle the JS.
browserify(paths.app_js)
.transform(reactify)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('./src/'));
});
// Rerun tasks whenever a file changes.
gulp.task('watch', function() {
gulp.watch(paths.css, ['css']);
gulp.watch(paths.js, ['js']);
});
// The default task (called when we run `gulp` from cli)
gulp.task('default', ['watch', 'css', 'js']);
只需安装 NPM 依赖项,运行 `./node_modules/.bin/gulp`,它就会在后台为我们处理所有事情。我们的文件由 `gulp.watch` 监视,任务会自动运行,并且通过流和管道以干净的方式完成。每当我们修改任何 JS/CSS 时,我们都可以像使用 AMD 一样刷新浏览器。
使用 Grunt 还是 Gulp 取决于个人喜好。两者都有大量的模块可用,不过 Gulp 比较新。Grunt 主要通过配置完成,而 Gulp 主要通过代码和流完成。不过,Gulp 可能会更快一些,因为它不需要中间文件来完成任务。因此,我们的构建系统就位后,让我们进入重头戏:React。
React:声明式和响应式组件
React 是 Facebook 用于构建可重用 Web 组件的 JS 库。它不是像 AngularJS 这样的完整 MVC 框架;React 专注于组件的视图渲染,不依赖任何框架,并且可以平滑地集成到大多数项目中。
Facebook 表示 React 的目的是构建随时间变化的数据的大型应用程序。Facebook 需要一个不会接管整个应用程序的东西。他们可以混合使用可以与遗留组件集成的组件。如果您想了解更多信息,React 的作者之一 Pete Hunt 在 Quora 上写了一些关于 React 的论点。
React 没有像传统应用程序中那样使用命令式单向数据绑定,也没有像 Angular 中那样使用双向数据绑定,而是实现了单向响应式数据流。React 的组件是声明式定义的,并且在数据更改时会自动重新渲染,而无需手动注册监听器和处理程序来更新 DOM,或者设置链接函数和数据绑定。就像一个函数一样,数据输入,组件输出。
为了方便起见,让我们来看一个基于 React 主页的示例,它只显示一个姓名
/** @jsx React.DOM */
var React = require('react'); // Browserify!
var HelloMessage = React.createClass({ // Create a component, HelloMessage.
render: function() {
return Hello {this.props.name}; // Display a property.
}
});
React.renderComponent( // Render HelloMessage component at #name.
,
document.getElementById('name'));
您可能已经注意到,我们的 Javascript 中有一些标记。React 有一个名为 JSX 的语法糖。它需要编译成 JS,这将通过我们之前使用的 Gulpfile 中的 Reactify 插件自动完成。不过,如果需要,React 也有一个 JSX 编译器。请注意,JSX 不是必需的;React 有普通的 JS API,但那样就没那么有趣了。
组件使用 `createClass` 创建。与函数类似,组件可以在渲染过程中以 `props` 的形式接收参数。在上面的示例中,`name="John"` 传递给组件,然后通过 `this.props.name` 引用。请注意,组件只能由一个节点组成。如果我们希望有多个 DOM 节点,则必须将它们都包装在一个根节点下。
除了通过 `props` 获取输入数据之外,组件还可以拥有一个内部的可变状态,可以通过 `this.state` 访问。这是另一个示例,这次是一个计时器,基于 React 的主页
/** @jsx React.DOM */
var React = require('react');
var Timer = React.createClass({
getInitialState: function() { // Like an initial constructor.
return {
seconds: 0
};
},
incrementTimer: function() { // A helper method for our Timer.
this.setState({ // Use setState to modify state.
seconds: this.state.seconds + 1 // Never modify state directly!
});
},
componentDidMount: function() { // A method run on initial rendering.
setInterval(this.incrementTimer, 1000);
},
render: function() {
return (
Seconds Elapsed: {this.state.seconds}
);
}
});
React.renderComponent(, document.getElementById('timer'));
我们有一个 `setInterval` 修改组件的状态,每 1000 毫秒触发一次刷新。不过在更实际的应用中,状态更有可能通过用户输入或通过 XHR 获取的数据来修改,而不是通过简单的间隔来修改。
以上是 React 的一些基础知识。如果可重用声明式组件和响应式渲染恰好是您项目中所需要的,您可以访问 React 入门指南。祝您的开发顺利。无论您是否决定使用这些工具,了解您的选择总是很有利的。
关于 Kevin Ngo
Kevin 是 Mozilla 的虚拟现实开发者,也是 A-Frame(一个开源 WebVR 框架)的核心开发者。他在 Twitter 上的用户名是 @ngokevin_。
关于 Robert Nyman [荣誉编辑]
Mozilla Hacks 的技术布道师和编辑。发表演讲和博客文章,内容涉及 HTML5、JavaScript 和开放网络。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年以来一直从事 Web 前端开发工作,在瑞典和纽约市都有工作经历。他还在 http://robertnyman.com 定期发表博客文章,喜欢旅行和结识新朋友。
9 条评论