React+Webpack+Router搭建React基礎工程

       本文主要介紹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 `
    <!doctype html public="storage">
    <html>
    <meta charset=utf-8/>
    <div id=app>${appHtml}</div>
    <script src="/bundle.js"></script>
   `
}
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
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="renderer" content="webkit">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0,user-scalable=no" />
    <meta content="telephone=no" name="format-detection" />
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="#035c9b">
    <!--不構建壓縮css,爲了開發中的調試,直接引用-->
    <link rel="stylesheet" href="/css/bootstrap.css"  />
    <link rel="stylesheet" href="/css/bootstrap-theme.css"  />
    <link rel="stylesheet" href="/css/theme.css"  />
    <link rel="stylesheet" href="/css/fixed-data-table.css"  />
    <link rel="stylesheet" href="/css/rc-tree/index.css"  />
    <link rel="stylesheet" href="/css/rc-dialog/index.css"  />
    <link rel="stylesheet" href="/css/rc-calendar/index.css"  />
    <link rel="stylesheet" href="/css/rc-time-picker/index.css"  />
</head>
<body>
<div id="content">

</div>
</body>
<!--iframe模式 因爲在package.json中配置了inline模式,此行註釋 -->
<!--<script src="http://localhost:8080/webpack-dev-server.js"></script>-->
<!--如果使用browserHistory 這裏請用/bundle.js -->
<!-- 其他也要用 /index.css 這種形式 -->
<script src="/bundle.js"></script>
</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((
    <Router routes={routes} history={browserHistory}></Router>
), 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=(
    <Route onEnter={enterFun} path="/" component={Home} >
        <IndexRoute onEnter={enterFun} component={Welcome}></IndexRoute>
        <Route onEnter={enterFun} path="/tree"  component={Tree}></Route>
        <Route onEnter={enterFun} path="/table" component={Table}></Route>
        <Route onEnter={enterFun} path="/calendar"  component={Calendar}></Route>
        <Route onEnter={enterFun} path="/dialog"  component={Dialog}></Route>
        <Route onEnter={enterFun} path="/silder"  component={Silder}></Route>
        <Route onEnter={enterFun} path="/datepicker"  component={Datepicker}></Route>
    </Route>
);


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 (
            <div>
               歡迎來到React<br/>
                構建方式爲React+webpack+router
            </div>
        );
    }
})

exports.Welcome=Welcome;
Home.js的內容如下
   
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 (
            <div className="pagewrap">
                <Header name='React門戶'/>
                <Navigation />
                <div className="layout-l-r">
                    <Menu/>
                    <section className="main">
                        <ol className="breadcrumb">
                            <li><a href="#">主頁</a></li>
                            <li><a href="#">數據分析</a></li>
                            <li className="active">表格</li>
                        </ol>
                        <div className="main-view col-xs-12">
                            {this.props.children||<Welcome/>}
                        </div>
                    </section>
                </div>
                <Footer />
            </div>
        );
    }
})

exports.Home=Home;
接下來是menu.js 爲訪問路由的方式: to='xxx'中的路徑與route中的path對應





var React=require('react');
var Link= require('react-router').Link;
var IndexLink=require('react-router').IndexLink;

//<Link to='silder'     activeClassName="active"  className="list-group-item">React SilderBar</Link>
var Menu = React.createClass({
    render : function(){

        return (
            <section className="leftside">
                <div className="list-group navfun">
                    <Link to='/' id='home'   className="list-group-item">Home</Link>
                    <Link to='tree'       activeClassName="active"  className="list-group-item">React Tree</Link>
                    <Link to='table'      activeClassName="active"  className="list-group-item">React Table</Link>
                    <Link to='calendar'   activeClassName="active"  className="list-group-item">React Calendar</Link>
                    <Link to='dialog'     activeClassName="active"  className="list-group-item">React Dialog</Link>
                </div>
            </section>
        );
    }
})

exports.Menu=Menu;
 header.js(footer.js結構與之形式相似,這裏不贅述)

var React=require('react');

var Header = React.createClass({
    render : function(){
        return (
            <header>
                <h1>{this.props.name}</h1>
            </header>
        );
    }
})

exports.Header=Header;

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 (<div style={{ margin: '0 20px' }}>
           tree
        </div>);
    }
});

exports.Tree=myTree;

至此,一個簡單的react+webpack+router的基礎工程搭建完畢
第一步:執行npm install 安裝node modules
第二步:執行npm start 或npm run start:dev 運行dev模式
	     
             執行npm run start:prod 運行生產模式
 















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章