React+ES6+Webpack環境配置


轉自http://www.cnblogs.com/chenziyu-blog/p/5675086.html

參考http://www.tuicool.com/articles/BrAVv2y

React已成為前端當下最熱門的前端框架之一 , 其虛擬DOM和組件化開發讓前端開發更富靈活性,而Webpack憑借它異步加載和可分離打包等優秀的特性,更為React的開發提供了便利。其優秀的特性不再贅述。本文將詳細的記錄react babel webpack的環境搭建,以及搭建的過程中遇到的一些坑。

一、新建React項目

1、如圖為新建react項目結構,其中 entry.js放置react入口代碼,index.js放置react組件代碼,assets文件是webpack打包之后生成文件的存放地址 由於react代碼需要jsx來寫,所以我們修改編輯器里的配置。我使用的是mac版的webstorm,在如下圖地方修改jsx提示配置

首先你得安裝npm 同時命令行輸入,新建node_modules

npm install
npm init

2、輸入如下命令安裝react模塊

npm i --save react

由於react升級后把reactDOM獨立出來 ,所以我們還需要安裝 react-dom

npm install react-dom --save-dev

具體react代碼在安裝完webpack后介紹

 

二、安裝webpack

1、什么是Webpack?

事實上它是一個打包工具,而不是像RequireJS或SeaJS這樣的模塊加載器,通過使用Webpack,能夠像Node.js一樣處理依賴關系,然后解析出模塊之間的依賴,將代碼打包

2、安裝webpack

全局安裝

sudo npm install -g webpack

 

本項目安裝

npm install webpack --save-dev

3、webpack配置

每個項目下都必須配置有一個 webpack.config.js ,它的作用如同常規的 gulpfile.js/Gruntfile.js ,就是一個配置項,告訴 webpack 它需要做什么。 在根目錄新建js文件 webpack.config.js 文件內容大概如下

復制代碼
/**
 * Created by mac on 16/7/14.
 */
var webpack = require('webpack');
var path = require('path');

module.exports = {
    //頁面入口文件配置
    entry: {
        index: [
            'webpack-dev-server/client?http://localhost:5000',
            'webpack/hot/only-dev-server',
            './js/entry.js'
        ]
    },
    //入口文件輸出配置
    output: {
        path: __dirname + '/assets/',
        filename: 'bundle.js'
    },
    module: {
        //加載器配置
        loaders: [
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },

            {
                test: /\.js$/,
                loader: 'jsx-loader?harmony'
            },
            {
                test: /\.(png|jpg)$/,
                loader: 'url-loader?limit=8192'
            },
            {
                test: /\.js|jsx$/,
                loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react,presets[]=stage-0'],
                include: path.join(__dirname, 'js')
            }
        ]
    },
    //其它解決方案配置
    resolve: {
        extensions: ['', '.js', '.json', '.scss']
    },
    //插件項
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
    ]
};
復制代碼

這里對Webpack的打包行為做了配置,主要分為幾個部分:

    entry:指定打包的入口文件,每有一個鍵值對,就是一個入口文件 output:配置打包結果,path定義了輸出的文件夾,filename則定義了打包結果文件的名稱

    resolve:定義了解析模塊路徑時的配置,常用的就是extensions,可以用來指定模塊的后綴,這樣在引入模塊時就不需要寫后綴了,會自動補全

    module:定義了對模塊的處理邏輯,這里可以用loaders定義了一系列的加載器,以及一些正則。當需要加載的文件匹配test的正則時,就會調用后面的loader對文件進行處理,這正是webpack強大的原因。比如這里定義了凡是 .js 結尾的文件都是用 babel-loader 做處理,而 .jsx 結尾的文件會先經過 jsx-loader 處理,然后經過 babel-loader 處理。當然這些loader也需要通過 npm install 安裝

    plugins: 這里定義了需要使用的插件,這里使用了react-hot會在之后介紹

安裝loader示例

npm install jsx-loader --save-dev

在我的webpack.config文件中,文件入口為 js/entry.js,entry中的index數組為之后使用的react-hot配置,若不使用插件則為

entry: {
        index: './js/entry.js'
    },

打包后的出口為assets/bundle.js

三、安裝babel

1、為什么babel

能夠實現 ES6 到 ES5 的代碼轉換多虧了 Babel (以前叫 6to5) 以及 Traceur 之類的項目。這些轉換器 (更准確地說是源代碼到源代碼的編譯器) 可以把你寫的符合 ECMAScript 6 標准的代碼完美地轉換為 ECMAScript 5 標准的代碼,並且可以確保良好地運行在所有主流 JavaScript 引擎中。

2、安裝babel

npm install -g babel

完成后可用命令`sudo babel --help`查看是否安裝成功

 

Babel提供babel-cli工具,用於命令行轉碼。

npm install babel-cli --save-dev

如果某些代碼需要調用Babel的API進行轉碼,就要使用babel-core模塊。

npm install babel-core --save-dev

Babel的配置文件是.babelrc,存放在項目的根目錄下。使用Babel的第一步,就是配置這個文件。 首先新建.babelrc文件 文件默認如下

{
  "presets": [],
  "plugins": []
}

presets字段設定轉碼規則,官方提供以下的規則集,你可以根據需要安裝。

復制代碼
# ES2015轉碼規則
$ npm install --save-dev babel-preset-es2015

# react轉碼規則
$ npm install --save-dev babel-preset-react

# ES7不同階段語法提案的轉碼規則(共有4個階段),選裝一個
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3
復制代碼

之后在.babelrc文件中加入配置

復制代碼
{
    "presets": [
      "es2015",
      "react",
      "stage-0"
    ],
    "plugins": []
}
復制代碼

同時安裝babel-loader

npm install babel-loader --save-dev

這時在webpack.config文件中loader中加入配置,讓打包時加載babel

       {
             test: /\.js|jsx$/, loaders: ['babel?presets[]=es2015,presets[]=react,presets[]=stage-0']
       },

 

注意!這里presets的順序要和.babelrc的順序一致

自此 模塊的安裝到此結束。

 

四、react代碼

接下來,我們來看看具體的demo代碼

1、index.html

復制代碼
<!DOCTYPE html>

<head>
    <script src="build/jquery-2.1.4.js"></script>
    <script src="bootstrap-3.3.5-dist/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="css/css.css">
    <script src="build/JSXTransformer.js"></script>
    <link rel="stylesheet" href="bootstrap-3.3.5-dist/css/bootstrap.min.css">

</head>
<body>

<div id="react"></div>

<script type="text/jsx" src="assets/bundle.js"></script>

</body>
</html>
復制代碼

我們引入最基本的jquery,bootstrap,其中注意的是需要引入JSXTransformer.js用來把jsx代碼代碼轉換成瀏覽器讀得懂的js代碼。

之后我們需要引入的是webpack打包完成之后的bundle.js文件。

2、index.js

復制代碼
/**
 * Created by mac on 16/7/15.
 */

//組件類的第一個字母必須大寫,否則會報錯,比如HelloMessage不能寫成helloMessage。另外,組件類只能包含一個頂層標簽,否則也會報錯。例如return <h1 className="mt10 ml10">chen</h1> <p>wang</p>

import React from 'react'

class RefExa extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value: 'Hello!',
            foo: '',
            bar: '',
            opacity: 1
        }
    }

    handleClick() {
        this.refs.myInputEl.focus();
        return true
    }

    handleChange() {
        this.setState({value: event.target.value});
        console.log(this.state.value);
    }
    componentWillMount() {
        console.log('WillMount')
    }
    componentDidMount() {
        console.log('DidMount');
        var that = this;
        this.setState({
            foo: 'Hello',
            bar: 'React'
        });

        this.timer = setInterval(function() {
            var opacity = this.state.opacity;
            opacity -= 0.1;
            if(opacity < 0) {
                clearInterval(that.timer);
            }
            this.setState({
                opacity: opacity
            })
        }.bind(this),1000)
    }

    stopTimer() {
        clearInterval(this.timer);
        this.setState({
            opacity: 1
        })
    }

    componentDidUpdate(props,states) {
        console.log(props);
        console.log(states);
    }

    render() {
        console.log('render');
        var value = this.state.value;
        return (
            <form name="form" noValidate>
        <div className="form-group">
            <label className="control-label col-sm-2">operate virtualDom</label>
        <div className="col-sm-9">
            <input placeholder="focus" type="text" name="myInputEl" onChange={this.handleChange.bind(this)} value={value} ref="myInputEl" />
        </div>
        </div>

        <footer className="col-sm-12">
        <button onClick={this.handleClick.bind(this)} className="btn btn-success">focus {this.props.from}</button>
        </footer>
        <h2 style={{opacity: this.state.opacity}} className="col-sm-1">{this.state.foo}</h2>
        <h2 className="col-sm-11">{this.state.bar}</h2>
        </form>
      )
    }
}

class ParentNode extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            foo: 'hello'
        }
    }

    handleClick() {
        this.refs.childNodeRef.stopTimer();
    }

    render() {
        var fooArr = ['foo','bar','baz'];
        return (
            <div className="col-sm-12">
            <RefExa from="input" ref="childNodeRef"></RefExa>
                        <button className="btn btn-default" onClick={this.handleClick.bind(this)}>stop childNode timer</button>
            {
                fooArr.map(function(v,i) {
                return <button key={i} className="ml10 btn btn-danger">{v}{i}</button>
            })
            }

            </div>
            )
    }
}


export default ParentNode


//子節點通過props獲取父節點數據 父節點通過ref控制子節點
復制代碼

 

這里是用es6寫法的react代碼,首先import React 引入加載好的react模塊,具體代碼邏輯不再贅述,其中值得注意的是

1、使用map循環渲染時需要加上key={i},使react能夠按照index渲染

2、 es6寫法中 需要在綁定事件時在之后加上bind(this) ,例如

onChange = {this.handleChange.bind(this)}

3、最后 需要export default <component Name>用於暴露組件

 

3、entry.js

復制代碼
import React from 'react';
import ReactDom from 'react-dom';
import ParentNode from './index.js';


ReactDom.render(
    <ParentNode></ParentNode>,
    document.getElementById('react')
);
復制代碼

引入React ReactDom 和寫好的組件ParentNode

之后 ReactDom.render 渲染組件

 

最后在命令行輸入 

webpack --display-error-details

打包項目,--display-error-details用於查看報錯詳情

最后我們在瀏覽器中打開項目

 

五、超贊的react插件 react-hot

    Webpack本身具有運行時模塊替換功能,稱之為Hot Module Replacement (HMR)。當某個模塊代碼發生變化時,Webpack實時打包將其推送到頁面並進行替換,從而無需刷新頁面就實現代碼替換。這個過程相對比較復雜,需要進行多方面考慮和配置。而現在針對React出現了一個第三方react-hot-loader加載器,使用這個加載器就可以輕松實現React組件的熱替換,非常方便。其實正是因為React的每一次更新都是全局刷新的虛擬DOM機制,讓React組件的熱替換可以成為通用的加載器,從而極大提高開發效率。

1、安裝react-hot 和 webpack-dev-server

npm install --save-dev webpack-dev-server react-hot-loader

 

通過這種方式指定資源熱啟動對應的服務器,然后需要配置 react-hot-loader到loaders的配置當中,比如我的所有組件代碼全部放在scripts文件夾下:

{
                test: /\.js|jsx$/,
                loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react,presets[]=stage-0'],
                include: path.join(__dirname, 'js')
            }

並且需要引入path

var path = require('path');

同時引入插件

plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
    ]

入口配置應為

復制代碼
entry: {
        index: [
            'webpack-dev-server/client?http://localhost:5000',
            'webpack/hot/only-dev-server',
            './js/entry.js'
        ]
    },
復制代碼

 

這時完整的webpack.config.js文件如下

復制代碼
/**
 * Created by mac on 16/7/14.
 */
var webpack = require('webpack');
var path = require('path');

module.exports = {
    //頁面入口文件配置
    entry: {
        index: [
            'webpack-dev-server/client?http://localhost:5000',
            'webpack/hot/only-dev-server',
            './js/entry.js'
        ]
    },
    //入口文件輸出配置
    output: {
        path: __dirname + '/assets/',
        filename: 'bundle.js'
    },
    module: {
        //加載器配置
        loaders: [
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },

            {
                test: /\.js$/,
                loader: 'jsx-loader?harmony'
            },

            //{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
            {
                test: /\.(png|jpg)$/,
                loader: 'url-loader?limit=8192'
            },

            {
                test: /\.js|jsx$/, loaders: ['babel?presets[]=es2015,presets[]=react,presets[]=stage-0']
            },

            {
                test: /\.js|jsx$/,
                loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react,presets[]=stage-0'],
                include: path.join(__dirname, 'js')
            }
        ]
    },
    //其它解決方案配置
    resolve: {
        extensions: ['', '.js', '.json', '.scss']
    },
    //插件項
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
    ]
};
復制代碼

 


2根目錄新建server.js文件
復制代碼
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
new WebpackDevServer(webpack(config), {
    publicPath: config.output.publicPath,
    hot: true,
    noInfo: false,
    historyApiFallback: true
}).listen(5000, '127.0.0.1', function (err, result) {
        if (err) {
            console.log(err);
        }
        console.log('Listening at localhost:5000');
    });
復制代碼

此時端口號應與webpack.config.js中entry的端口號一致

 

大功告成后 命令行執行

webpack --display-error-details
node server.js

之后在瀏覽器中輸入http://localhost:5001/index.html

即可查看項目,這時修改代碼,不需要刷新就可以時時重新渲染,是不是非常便利的功能!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM