react的路很长,这将是一个开始。
开发之前,你需要首先了解一下几点:
webpack:
Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。
webpack-dev-server:
在webpack中有一个在开发时很好用的功能,热加载(Hot-replace),在react开发中,结合上react-hot-loader插件,开发工作将会变得更棒:更改代码之后,你甚至都不需要去刷新你的浏览器,界面就会自动刷新。但是,要使用这一功能的话,我们就需要使用webpack-dev-server。webpack-dev-server是一个小型的node.js Express服务器,它使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供Web服务。它还有一个通过Socket.IO连接着webpack-dev-server服务器的小型运行时程序。webpack-dev-server发送关于编译状态的消息到客户端,客户端根据消息作出响应。
react-router:
在react开发中,react-router是由官方维护的,唯一可选的路由库。它通过管理 URL,实现组件的切换和状态的变化,开发复杂的应用几乎肯定会用到。
babel:
Babel是一个JavaScript编译器。默认情况下,babel自带了一组ES2015语法转化器。这些转化器可以让你现在就使用最新的JavaScript语法,而不需要等待浏览器提供支持。本文要讲的开发例子中,就使用到了ES6以及部分ES7所提出的一些新规范,所以需要使用babel来进行转化。
一、配置package.json文件
首先建立项目文件夹,在该项目文件夹目录下执行npm init命令,然后一直回车到底,生成package.json文件,接下来再进行详细的配置。
package.json
{
"name": "webpack-react",
"version": "1.0.0",
"description": " webpack-react ",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --hot --progress --colors",
"build": "webpack --progress --colors"
},
"author": "CainZK",
"license": "ISC",
"devDependencies": {
"babel": "^6.5.2",
"babel-core": "^6.7.2",
"babel-loader": "^6.2.4",
"babel-plugin-react-transform": "^2.0.2",
"babel-plugin-transform-class-properties": "^6.16.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-2": "^6.17.0",
"classnames": "^2.2.5",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
"html-webpack-plugin": "^2.10.0",
"react": "^0.14.8",
"react-dom": "^15.3.2",
"react-hot-loader": "^1.3.0",
"react-router": "^2.8.1",
"react-slick": "^0.14.5",
"react-transform-hmr": "^1.0.4",
"rmc-list-view": "^0.6.2",
"sass-loader": "^3.2.0",
"style-loader": "^0.13.0",
"url-loader": "^0.5.7",
"webpack": "^1.12.14",
"webpack-dev-server": "^1.14.1"
}
}
这里需要说一下scripts字段以及devDependencies字段。在scripts字段下第一个命令"start": "webpack-dev-server --hot --progress --colors"表示执行npm start命令时,会执行后面的命令,即用webpack-dev-server启动服务, --hot表示热替换配置,--progress表示显示进度,--colors表示配置输出的颜色。第二个命令build表示执行npm run build的时候,会使用webpack进行打包操作。
二、配置webpack.config.js文件
上一步打包的时候,所有的配置工作都是在webpack.config.js文件中进行的。所以接下来我们来完善webpack的相关配置。
webpack.config.js
var path = require('path');
var webpack = require('webpack');
var ROOT_PATH = path.resolve(__dirname);
var APP_PATH = path.resolve(ROOT_PATH, 'app');
var BUILD_PATH = path.resolve(ROOT_PATH, 'build');
// var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
a:[
'webpack/hot/only-dev-server',
"./app/index.js"
],
b:[
'webpack/hot/only-dev-server',
"./app/index2.js"
]
},
output: {
path: BUILD_PATH,
publicPath: "/build/",
filename: "[name].bundle.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
query:
{
presets:['react','es2015','stage-2'] // babel配置:添加这三个presets用来处理js和jsx
}
}, {
test: /\.css$/,
loaders: ['style', 'css', 'sass'], //.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理
include: APP_PATH
}, {
test: /\.(png|jpg)$/,
loader: 'url?limit=40000' //图片文件使用 url-loader 来处理,小于40000字节的直接转为base64
}
]
},
resolve:{
extensions:['','.js','.json'] //自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
},
devServer: {
hot: true, //热加载模式
inline: true //inline模式(将webpack-dev-sever的客户端入口添加到包(bundle)中)
},
plugins: [
new webpack.NoErrorsPlugin(),//用来跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误
new webpack.HotModuleReplacementPlugin()//全局开启代码热替换
]
};
entry:入口配置,这里我进行的是多页面入口文件的配置,如果是单页面的话可以这样来进行配置:
entry: {
'webpack/hot/only-dev-server',
"./app/index.js"
},
path是webpack输出地址,取绝对路径;
publicPath是webpack-dev-server启动服务时在内存中文件生成的地址。
filename:这里多页面入口文件配置需要加上[name]。
loaders:使用loader对一些文件进行相应的处理。
test:使用正则匹配要处理的文件。
exclude:排除不处理的目录。
loader:要使用的loader。
...(其余相关配置已写在代码注释中)
三、完善入口页面
在项目根目录下简历index.html以及index1.html表示两个入口页面。
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" type="text/css" href="./app/css/base.css">
<title>React App</title>
</head>
<body>
<div id="content"></div>
<script src="./build/b.bundle.js"></script>
</body>
</html>
index1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="content"></div>
<script src="./build/a.bundle.js"></script>
</body>
</html>
在根目录下新建app目录,新建index.js以及index1.js文件。
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import myBt from './example/myBt';
import Carousel from './example/myCarousel';
import ActionSheet from './example/myActionSheet';
import { Router, Route, IndexRoute, hashHistory} from 'react-router';
import rt from './example/route';
class App extends React.Component {
render () {
console.log(window.screen.height);
return (
<div>
{this.props.children}
</div>
);
}
}
ReactDOM.render((
<Router history={hashHistory}>
<Route path="/" component={App} >
<IndexRoute component={rt}/>
<Route path="/carousel" component={Carousel}/>
<Route path="/button" component={myBt}/>
<Route path="/actionSheet" component={ActionSheet}/>
</Route>
</Router>),
document.getElementById('content')
);
index1.js
import React from 'react';
import ReactDOM from 'react-dom';
import ListView from 'rmc-list-view';
var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';
class App extends React.Component {
constructor(props) {
super(props);
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds,
loaded:false,
};
}
componentDidMount() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
loaded: true,
});
})
}
renderLoadingView() {
return (
<div>
Loading ...
</div>
);
}
render() {
if (!this.state.loaded) {
return this.renderLoadingView();
}
var rowStyle = {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
}
var imgStyle = {
height:120,
width:80
}
return(
<ListView
style={{ height: 667, backgroundColor:'#FAFAFA' }}
dataSource={this.state.dataSource}
renderRow={(rowData) => (
<div style={rowStyle}>
<img src={rowData.posters.thumbnail} style={imgStyle}/>
<a style={{flex: 1,marginTop:15,marginLeft:10,fontSize:18,height:36,position:'absolute'}}>
{rowData.title}
</a>
</div>
)}
/>
);
}
};
ReactDOM.render(<App/>, document.getElementById('content'));
这里我们在index.js文件中使用了react-router,简单介绍一下它的一些配置及简单的用法。
导入路由:
import { Router, Route, IndexRoute, hashHistory} from 'react-router';
配置路由信息:
<Router history={hashHistory}>
<Route path="/" component={App} >
<IndexRoute component={rt}/>
<Route path="/carousel" component={Carousel}/>
<Route path="/button" component={myBt}/>
<Route path="/actionSheet" component={ActionSheet}/>
</Route>
</Router>
路由跳转页面设置:
import React from 'react';
export default class Button extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div style={{padding:20}}>
<a href="#carousel" style={{marginLeft:20}}>
carousel
</a>
<a href="#button" style={{marginLeft:20}}>
button
</a>
<a href="#actionSheet" style={{marginLeft:20}}>
actionSheet
</a>
</div>
);
}
};
path是跳转路径,component中的是跳转路径过后要展示出来的react组件。具体的详细用法可以参考npm官网的介绍,这里就不多说了。
到这里框架搭建基本完成,缺的只有文中所引用的第三方组件了,如果您感兴趣的话,可以参考github,将项目down下来,再进行研究,我也会继续完善这个简单的框架,添加更多的组件进去,方便开发。
成功搭建后的演示: