本文主要介绍React+Webpack+Router搭建React基础工程的简单方式。
React 起源于 Facebook 的内部项目,因为该公司对市场上所有的前端MVC框架都不满意,就决定自己写一套,用来构建Instagram。
React主要用于构建UI。你可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件。
特点:
1.声明式设计:React采用声明范式,可以轻松描述应用。
2.React通过对DOM的模拟,最大限度地减少与DOM的交互。
3.灵活:React可以与已知的库或框架很好地配合。
如果你想对本文中的React、webpack、router有更加深入的了解,请访问以下网站进行深入的学习:
1.React官方中文文档:http://reactjs.cn/react/docs/getting-started-zh-CN.html(大多数文章后缀名加入zh-CN会变为中文)
2.webpack中文指南 :http://webpackdoc.com/loader.html
3.react-router中文文档:http://www.uprogrammer.cn/react-router-cn/
好了,废话不多说,下面介绍运用React+Webpack+Router搭建React基础工程,目录结构如图所示:(bundle.js和server.bundle.js为构建生成的文件)
package.json文件配置:
{
"name": "webpack-react",
"version": "1.0.0",
"description": "",
"main": "./justice/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
"start:dev": "webpack-dev-server --inline --content-base build --history-api-fallback",
"start:prod": "npm run build && node server.bundle.js",
"build:client": "webpack",
"build:server": "webpack --config webpack.server.config.js",
"build": "npm run build:client && npm run build:server",
"dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build --history-api-fallback"
},
"keywords": [
"webpack"
],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"babel-plugin-react-transform": "^2.0.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.16.0",
"body-parser": "^1.4.3",
"css-loader": "^0.21.0",
"express": "^4.4.5",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"jquery": "^3.1",
"less": "^2.7.2",
"less-loader": "^2.2.3",
"react-dom": "^15.3.2",
"react-redux": "^4.4.6",
"react-router": "^3.0.0",
"redux-devtools": "^3.3.1",
"style-loader": "^0.13.0",
"url-loader": "^0.5.7",
"webpack": "^1.13.3",
"webpack-dev-server": "^1.16.2"
},
"dependencies": {
"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"babel-plugin-react-transform": "^2.0.0",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-0": "^6.16.0",
"body-parser": "^1.4.3",
"compression": "^1.6.2",
"css-loader": "^0.21.0",
"express": "^4.14.0",
"file-loader": "^0.9.0",
"if-env": "^1.0.0",
"jquery": "^3.1",
"react": "^15.3.2",
"react-dom": "^15.3.2",
"react-redux": "^4.4.6",
"react-router": "^3.0.0",
"redux-devtools": "^3.3.1",
"style-loader": "^0.13.0",
"url-loader": "^0.5.7",
"webpack": "^1.13.3",
"webpack-dev-server": "^1.16.2"
}
}
webpack.config.js构建客户端
var path = require('path'); var webpack = require('webpack'); var ExtractTextPlugin = require("extract-text-webpack-plugin"); var config = { //构建入口 entry:['webpack/hot/dev-server', path.resolve('', './justice/index.js')], //构建出口 path:打包文件存放的绝对路径 filename:打包后的文件名 publicPath:运行时的访问路径 output: { path: path.resolve('', 'build'), filename: 'bundle.js', publicPath: '/' }, module: { loaders: [ //es6 es7 react加载器 { test: /\.js?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ["es2015",'stage-0',"react"] } }, { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ["es2015",'stage-0',"react"] } }, //css解析器 { test: /\.css$/, loader: 'style!css' }, //style解析器 { test: /\.less$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 1 } }, 'less-loader' ] }, //图片处理 小于8K按base64处理 { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } ] }, babel: { presets: ['es2015','stage-0','react'] }, resolve:{ //自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名 extensions:['','.js','.json'] }, //插件配置 ExtractTextPlugin:提取样式插件 plugins: process.env.NODE_ENV === 'production' ? [ new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurrenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin(), new ExtractTextPlugin("styles.css") ] : [ new webpack.HotModuleReplacementPlugin(), new ExtractTextPlugin("styles.css") ] }; module.exports = config;
server.js 服务端配置
var express=require('express'); var path=require('path'); var compression=require('compression'); var react=require('react'); var match=require('react-router'); var RouterContext=require('react-router'); var renderToString=require('react-dom/server'); var app=express(); //must be first! 文件压缩 app.use(compression()); app.use(express.static(path.join(process.cwd(), 'build'))); app.get('*',function(req,res){ res.sendFile(path.join(process.cwd(),'build','index.html')); }); function renderPage(appHtml) { return `
${appHtml}` } var PORT = process.env.PORT || 8080 app.listen(PORT, function(){ console.log('Production Express server running at localhost:' + PORT); });
webpack.server.config.js 构建服务端:
var fs = require('fs') var path = require('path') module.exports = { entry: path.resolve(__dirname, 'server.js'), output: { filename: 'server.bundle.js' }, target: 'node', // keep node_module paths out of the bundle externals: fs.readdirSync(path.resolve(__dirname, 'node_modules')).concat([ 'react-dom/server', 'react/addons', ]).reduce(function (ext, mod) { ext[mod] = 'commonjs ' + mod return ext }, {}), node: { __filename: true, __dirname: true }, module: { loaders: [ { test: /\.js?$/, exclude: /(node_modules|bower_components)/, loader: 'babel', query: { presets: ['es2015', 'react','stage-0'] } }, { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ["es2015",'stage-0',"react"] } }, { test: /\.css$/, loader: 'style!css' }, { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } ] } }
webpack部分到此为止,接下来我们继续写react和router部分:
index.html
从上面的webpack.config.js中可知,我们构建的入口为index.js,index.js也必为路由的入口,配置如下:
index.js
var React=require('react'); var ReactDOM=require('react-dom'); var Router=require('react-router').Router; //hashHistory url中带有#号,browserHistory不带有#号 var hashHistory=require('react-router').hashHistory; var browserHistory=require('react-router').browserHistory; var routes=require('./routes.js').routes //url上的userName 和 repoName 会传到this.props.params中 //IndexRoute 当没有其他明确url时会首先render ReactDOM.render((
), document.getElementById('content'))
我们发现index.js引用了routes即路由的集合,这个集合存在于routes.js
routes.js的内容如下:
var React=require('react'); var Route= require('react-router').Route; var IndexRoute=require('react-router').IndexRoute; var Home=require('./home').Home; var Welcome=require('./page/welcome').Welcome; var Tree=require('./page/tree.jsx').Tree; var Silder=require('./page/silder.jsx').Silder; var Calendar=require('./page/calendar.jsx').Calendar; var Dialog=require('./page/dialog.jsx').Dialog; var Table=require('./page/table.jsx').Table; var Datepicker=require('./page/datepicker').Datepicker; var $=require('jquery'); //即进入路由时触发的函数 var enterFun=function(nextState,replace){ if(nextState.location.pathname=='/'){ $('#home').addClass('active'); } if(nextState.location.pathname!='/'){ $('#home').removeClass('active'); } } //component代表使用的组件 IndexRoute为首页路由 path为路径 路由按层级进入,一开始进入Home组件 然后组合首页路由同时进入Welcome组件 var routes=(
); exports.routes=routes;
首页路由welcome.js的内容如下
var React=require('react'); var Link= require('react-router').Link; var IndexLink=require('react-router').IndexLink; var Welcome = React.createClass({ render : function(){ return (
欢迎来到React); } }) exports.Welcome=Welcome;
构建方式为React+webpack+router
Home.js的内容如下接下来是menu.js 为访问路由的方式: to='xxx'中的路径与route中的path对应var React=require('react'); var Link= require('react-router').Link; var IndexLink=require('react-router').IndexLink; var Header=require('./header.js').Header; var Footer=require('./footer.js').Footer; var Navigation=require('./nav.js').Navigation; var Menu=require('./menu.js').Menu; var Welcome=require('./page/welcome.js').Welcome; var Home = React.createClass({ render : function(){ return ( ); } }) exports.Home=Home;
var React=require('react'); var Link= require('react-router').Link; var IndexLink=require('react-router').IndexLink; //React SilderBar var Menu = React.createClass({ render : function(){ return (
); } }) exports.Menu=Menu; Home React Tree React Table React Calendar React Dialog
header.js(footer.js结构与之形式相似,这里不赘述)
var React=require('react'); var Header = React.createClass({ render : function(){ return (
); } }) exports.Header=Header; {this.props.name}
table.jsx(其他组件与之结构相似,这里不赘述)
var React=require('react'); var PropTypes=require('react').PropTypes; var Link= require('react-router').Link; var IndexLink=require('react-router').IndexLink; var myTree = React.createClass({ render() { return (
tree); } }); exports.Tree=myTree;
至此,一个简单的react+webpack+router的基础工程搭建完毕
第一步:执行npm install 安装node modules
第二步:执行npm start 或npm run start:dev 运行dev模式
或
执行npm run start:prod 运行生产模式