• React+ES6+Webpack深入浅出


        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

    即可查看项目,这时修改代码,不需要刷新就可以时时重新渲染,是不是非常便利的功能!

  • 相关阅读:
    财务报表分析(张新民教授)-第六章 企业报表战略分析
    zend studio 10破解/汉化
    使用jQuery动态加载js脚本文件的方法
    手机端的META你有多了解?
    js添加事件、移除事件、阻止冒泡、阻止浏览器默认行为等写法(兼容IE/FF/CHROME)
    理解CSS Clip属性及用法
    js touch触屏原理分析
    解决xp共享的批处理文件
    jQuery Tools:Web开发必备的 jQuery UI 库
    使用CSS3线性渐变实现图片闪光划过效果
  • 原文地址:https://www.cnblogs.com/susanws/p/6694604.html
Copyright © 2020-2023  润新知