轻松入门React和Webpack
https://segmentfault.com/a/1190000002767365 最近在学习React.js,之前都是直接用最原生的方式去写React代码,发现组织起来特别麻烦,之前听人说用Webpack组织React组件得心应手,就花了点时间学习了一下,收获颇丰 说说React一个组件,有自己的结构,有自己的逻辑,有自己的样式,会依赖一些资源,会依赖某些其他组件。比如日常写一个组件,比较常规的方式:
那么在React中是什么样子呢? 结构和逻辑在React的世界里,结构和逻辑交由JSX文件组织,React将模板内嵌到逻辑内部,实现了一个JS代码和HTML混合的JSX。 结构 在JSX文件中,可以直接通过 var CustomComponent = React.creatClass({
render: function(){
return (<div className="custom-component"></div>); } });
通过这种方式可以很方便的定义一个组件,组件的结构定义在render函数中,但这并不是简单的模板引擎,我们可以通过js方便、直观的操控组件结构,比如我想给组件增加几个节点: var $nodes = ['h','e',152)">'l',152)">'o'].map(function(str){
span>{str}</span>); }); return (<"custom-component">{$nodes}</ 通过这种方式,React使得组件拥有灵活的结构。那么React又是如何处理逻辑的呢?
点击按钮应当触发相应地逻辑,一种比较直观的方式就是给button绑定一个 function getDragonKillingSword() {
//送宝刀
}
button onclick="getDragonKillingSword()">屠龙宝刀,点击就送</ 但事实上
那么如果想手动调用组件的方法,首先在ButtonComponent上设置一个 this.refs.getSwordButton.getDragonKillingSword(); 看起来屌屌哒~那么问题又来了,父组件希望自己能够按钮点击时调用的方法,那该怎么办呢? 配置参数父组件可以直接将需要执行的函数传递给子组件: <ButtonComponent clickCallback={this.getSwordButtonClickCallback}/>
然后在子组件中调用父组件方法: {this.props.clickCallback}>屠龙宝刀,点击就送</ 子组件通过
小节这里简单介绍了通过JSX来管理组件的结构和逻辑,事实上React给组件还定义了很多方法,以及组件自身的生命周期,这些都使得组件的逻辑处理更加强大 资源加载CSS文件定义了组件的样式,现在的模块加载器通常都能够加载CSS文件,如果不能一般也提供了相应的插件。事实上CSS、图片可以看做是一种资源,因为加载过来后一般不需要做什么处理。 React对这一方面并没有做特别的处理,虽然它提供了Inline Style的方式把CSS写在JSX里面,但估计没有多少人会去尝试,毕竟现在CSS样式已经不再只是简单的CSS文件了,通常都会去用Less、Sass等预处理,然后再用像postcss、myth、autoprefixer、cssmin等等后处理。资源加载一般也就简单粗暴地使用模块加载器完成了 组件依赖组件依赖的处理一般分为两个部分:组件加载和组件使用 组件加载 React没有提供相关的组件加载方法,依旧需要通过 组件使用 如果细心,就会发现其实之前已经有使用的例子了,要想在一个组件中使用另外一个组件,比如在 疑问到这里就会发现一个问题,React除了只处理了结构和逻辑,资源也不管,依赖也不管。是的,React将近两万行代码,连个模块加载器都没有提供,更与Angularjs,jQuery等不同的是,他还不带啥脚手架...没有Ajax库,没有Promise库,要啥啥没有... 虚拟DOM那它为啥这么大?因为它实现了一个虚拟DOM(Virtual DOM)。虚拟DOM是干什么的?这就要从浏览器本身讲起 如我们所知,在浏览器渲染网页的过程中,加载到HTML文档后,会将文档解析并构建DOM树,然后将其与解析CSS生成的CSSOM树一起结合产生爱的结晶——RenderObject树,然后将RenderObject树渲染成页面(当然中间可能会有一些优化,比如RenderLayer树)。这些过程都存在与渲染引擎之中,渲染引擎在浏览器中是于JavaScript引擎(JavaScriptCore也好V8也好)分离开的,但为了方便JS操作DOM结构,渲染引擎会暴露一些接口供JavaScript调用。由于这两块相互分离,通信是需要付出代价的,因此JavaScript调用DOM提供的接口性能不咋地。各种性能优化的最佳实践也都在尽可能的减少DOM操作次数。 而虚拟DOM干了什么?它直接用JavaScript实现了DOM树(大致上)。组件的HTML结构并不会直接生成DOM,而是映射生成虚拟的JavaScript DOM结构,React又通过在这个虚拟DOM上实现了一个 diff 算法找出最小变更,再把这些变更写入实际的DOM中。这个虚拟DOM以JS结构的形式存在,计算性能会比较好,而且由于减少了实际DOM操作次数,性能会有较大提升 道理我都懂,可是为什么我们没有模块加载器? 所以就需要Webpack了 说说Webpack什么是Webpack?事实上它是一个打包工具,而不是像RequireJS或SeaJS这样的模块加载器,通过使用Webpack,能够像Node.js一样处理依赖关系,然后解析出模块之间的依赖,将代码打包 安装Webpack首先得有Node.js 然后通过 配置WebpackWebpack的构建过程需要一个配置文件,一个典型的配置文件大概就是这样 var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
entry: {
entry1: './entry/entry1.js',entry2: './entry/entry2.js'
},output: {
path: __dirname,filename: '[name].entry.js'
},resolve: {
extensions: ['',152)">'.js',152)">'.jsx']
},module: {
loaders: [{
test: /.js$/,loader: 'babel-loader'
},{
test: /.jsx$/,152)">'babel-loader!jsx-loader?harmony'
}]
},plugins: [commonsPlugin]
};
这里对Webpack的打包行为做了配置,主要分为几个部分:
当然Webpack还有很多其他的配置,具体可以参照它的配置文档 执行打包 如果通过 $webpack --config webpack.config.js
这样就会读取当前目录下的webpack.config.js作为配置文件执行打包操作 如果是通过gulp插件gulp-webpack,则可以在gulpfile中写上gulp任务: var gulp = 'gulp');
'gulp-webpack');
var webpackConfig = './webpack.config');
gulp.task("webpack",function() {
return gulp
.src('./')
.pipe(webpack(webpackConfig))
.pipe(gulp.dest('./build'));
});
组件编写使用Babel提升逼格Webpack使得我们可以使用Node.js的CommonJS规范来编写模块,比如一个简单的Hello world模块,就可以这么处理: var React = 'react');
var HelloWorldComponent = React.createClass({
displayName: 'HelloWorldComponent',0)">function() {
return (
<div>Hello world</div> ); } }); module.exports = HelloWorldComponent;
等等,这和之前的写法没啥差别啊,依旧没有逼格...程序员敲码要有geek范,要逼格than逼格,这太low了。现在都ES6了,React的代码也要写ES6, var HelloWorldComponent = '!babel!jsx!./HelloWorldComponent');
那我们应当如何使用Babel提升代码的逼格呢?改造一下之前的HelloWorld代码吧: import React from 'react';
export default class HelloWorldComponent extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
div>Hello World</div> ); } }
这样在其他组件中需要引入HelloWorldComponent组件,就只要就可以了: import HelloWorldComponent from './HelloWorldComponent'
怎么样是不是更有逼格了?通过import引入模块,还可以直接定义类和类的继承关系,这里也不再需要 Babel带来的当然还不止这些,在其帮助下还能尝试很多优秀的ES6特性,比如箭头函数,箭头函数的特点就是内部的this和外部保持一致,从此可以和 ['H',152)">'o'].map((c) => {
span>{c}</span>); });
其他还有很多,具体可以参照Babel的学习文档 样式编写我是一个强烈地Less依赖患者,脱离了Less直接写CSS就会出现四肢乏力、不想干活、心情烦躁等现象,而且还不喜欢在写Less时候加前缀,平常都是gulp+less+autoprefixer直接处理的,那么在Webpack组织的React组件中要怎么写呢? 没错,依旧是使用loader 可以在 {
test: /.less$/,152)">'style-loader!css-loader!autoprefixer-loader!less-loader'
}
通过这样的配置,就可以直接在模块代码中引入Less样式了: 'react';
'./HelloWorldComponent.less');
export div> ); } }
其他Webpack的loader为React组件化提供了很多帮助,像图片也提供了相关的loader: { test: /.png$/,152)">"url-loader?mimetype=image/png" }
更多地loader可以移步webpack的wiki 在Webpack下实时调试React组件 Webpack和React结合的另一个强大的地方就是,在修改了组件源码之后,不刷新页面就能把修改同步到页面上。这里需要用到两个库 首先需要安装这两个库, 安装完成后,就要开始配置了,首先需要修改entry配置: entry: {
helloworld: [
'webpack-dev-server/client?http://localhost:3000',152)">'webpack/hot/only-dev-server',152)">'./helloworld'
]
},
通过这种方式指定资源热启动对应的服务器,然后需要配置 /.js?$/,loaders: ['react-hot',152)">'babel'],include: [path.join(__dirname,152)">'scripts')]
}
最后配置一下plugins,加上热替换的插件和防止报错的插件: plugins: [
new webpack.HotModuleReplacementPlugin(),new webpack.NoErrorsPlugin()
]
这样配置就完成了,但是现在要调试需要启动一个服务器,而且之前配置里映射到 var WebpackDevServer = 'webpack-dev-server');
var config = './webpack.config');
new WebpackDevServer(webpack(config),{
publicPath: config.output.publicPath,hot: true,historyApiFallback: true
}).listen(3000,152)">'localhost',0)">function (err,result) {
if (err) console.log(err);
console.log('Listening at localhost:3000');
});
这样就可以在本地3000端口开启调试服务器了,比如我的页面是根目录下地
结束React的组件化开发很有想法,而Webpack使得React组件编写和管理更加方便,这里只涉及到了React和Webpack得很小一部分,还有更多的最佳实践有待在学习的路上不断发掘 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |