• React 和 ES6 工作流之 Webpack的使用(第六部分)


    这是React和ECMAScript2015系列文章的最后一篇,我们将继续探索React 和 Webpack的使用。

    下面是所有系列文章章节的链接:

    本篇文章Github源码

    React JS

    什么是Webpack?

    就像JSPM一样,Webpack是你的前端应用的模块管理的解决方案。

    使用Webpack,你能够用一种方便的方法完全控制你的应用资源。

    为什么Webpack这么受欢迎?主要有以下几个原因:

    • Webpack使用npm作为外部模块源。如果你想添加React到你的项目中,只需要执行 npm install react即可。这是一个附加的优势,因为你已经知道如何将你喜欢的库添加到你的项目中。

    • 你几乎可以加载所有的东西,而不只是JavaScript。Webpack使用名字为loaders的装载机来完成加载。这是对应的loaders清单

    • Webpack有一个很强大的开发工具生态系统。像热更新这样的东西将戏剧性的改变你的开发流程。

    • 对于各种类型的任务有很多Webpack plugins。在大多数情况下,你可以使用已经存在的解决方案。

    • Webpack 有很漂亮的logo :)

    Getting started

    让我们开始从之前的系列文章中调整我们的应用程序。

    首先,我们将要安装初始的开发依赖。

    npm install --save-dev webpack
    npm install --save-dev babel-core
    npm install --save-dev babel-preset-es2015 babel-preset-react babel-preset-stage-0

    在上面的列表中,webpack是自解释型的。Babel是用于将ES6转换成ES5(如果你阅读了前面的React and ES6系列文章,你应该对ES6和ES5非常熟悉)。自从babel 6后你必须为每一个额外的语言特征安装独立的包。这些包叫做presets。我们安装es2015 preset,react presetstage-0 preset。对于更多关于babel 6的信息,你可以阅读这篇文章

    下一步,安装非开发依赖(react和react-dom包):

    npm install --save react react-dom

    现在在你的项目中基于Webpack最重要的一步。在你的项目根目录下面创建webpack.config.dev.js文件。这个文件将用来打包你所有的在一个bundle(或者多个bundle)里面的JavaScript(在大多数项目中不只是JavaScript),打包完就可以在用户的浏览器中正式运行。

    webpack.config.dev.js的内容如下:

    var path = require('path');
    var webpack = require('webpack');
    
    var config = {
        devtool: 'cheap-module-eval-source-map',
        entry: [
            './app.js'
        ],
        output: {
            path: path.join(__dirname, 'dist'),
            filename: 'bundle.js',
            publicPath: '/dist/'
        },
        plugins: [
            new webpack.NoEmitOnErrorsPlugin()
        ]
    };
    
    module.exports = config;

    以上代码的亮点:

    • Line 5. 在提高应用程序的各种调试策略中,我们有一个选择,你可以点击这里了解更多关于cheap-module-eval-source-map的内容。

    • Lines 6-8. 这里我们定义了app.js为应用程序的主入口。

    • Lines 9-13. 这个配置制定Webpack将打包所有的模块成文件bundle.js,并且将bundle.js文件放到dist/路径下面。

    Webpack loaders

    Webpack几乎可以加载所有的东西到你的代码中(这里是清单)。Webpack使用的名字叫做Webpack装载机。

    你可以制定文件扩展名关联到特别的装载机。

    在我们的案例中,我们将使用babel-loader来将ES2015 / ES6的代码转换成ES5.首先,我们需要安装npm依赖包。

    npm install --save-dev babel-loader

    然后,通过添加一些新的装载机关键字到出口对象中来调整webpack.config.dev.js文件的配置。

    var config = {
        ... add the below code as object key ...
        module: {
            loaders: [
                {
                    test: /.js$/,
                    loaders: ['babel-loader'],
                    exclude: /node_modules/
                }
            ]
        }
    };
    
    module.exports = config;

    这里需要重点注意的是,我们通过exclude关键字的设置禁止Webpack解析node_modules文件夹里面的文件。

    接下来我们在项目的根目录下面添加.babelrc文件。

    {
      "presets": ["react", "es2015", "stage-0"]
    }

    这个文件是配置babel以便能够使用前面我们添加的react,es2015stage-0presets。

    现在,无论什么时候Webpack遇到,比如:import CartItem from './cartItem.js',它将加载这个文件并且将ES6转换成ES5

    添加Webpack开发服务器

    为了运行这个程序,我们需要在服务器上运行这些文件。

    幸运的是,Webpack生态系统已经提供所有你需要的东西。你可以使用Webpack开发服务器或者Webpack开发中间件,比如:Express.js

    我们将使用后者。优势是在内存中处理文件时速度快。

    让我们安装npm模块:

    npm install --save-dev webpack-dev-middleware express

    下一步,在根目录下面添加server.js文件:

    var path = require('path');
    var express = require('express');
    var webpack = require('webpack');
    var config = require('./webpack.config.dev');
    
    var app = express();
    var compiler = webpack(config);
    
    var port = 3000;
    
    app.use(require('webpack-dev-middleware')(compiler, {
        noInfo: true,
        publicPath: config.output.publicPath
    }));
    
    app.use(require('webpack-hot-middleware')(compiler));
    
    app.get('*', function (req, res) {
        res.sendFile(path.join(__dirname, 'index.html'));
    });
    
    app.listen(port, function onAppListening(err) {
        if (err) {
            console.error(err);
        } else {
            console.info('==> Webpack development server listening on port');
        }
    });

    这是典型的使用Webpack Dev Middlewareexpress.js服务器。

    添加热刷新模块

    Webpack Dev Middleware已经包含了热刷新的特性。无论什么时候,你的代码发生变化,它都会立即刷新页面。

    如果想简单的看看热刷新的演示效果,可以看看Dan Abramov视频

    为了激活Hot Module Reloading,你首先得安装必须得npm包。

    npm install --save-dev webpack-hot-middleware

    然后在webpack.config.dev.js文件中设置entryplugins:

    var config = {
        entry: [
            './app.js',
            'webpack-hot-middleware/client'
        ],
    
        ...
    
        plugins: [
            new webpack.HotModuleReplacementPlugin(),
            new webpack.NoEmitOnErrorsPlugin()
        ]
    };
    
    module.exports = config;

    如果想对React 应用更进一步使用模块刷新其实有很多种方法。

    其中一个简单的方法就是安装babel-preset-react-hmre模块。

    npm install --save-dev babel-preset-react-hmre

    调整.babelrc文件的内容:

    {
      "presets": ["react", "es2015", "stage-0"],
      "env": {
        "development": {
          "presets": ["react-hmre"]
        }
      }
    }

    到这一步,这个应用就具备热刷新的功能。

    最后几步

    • 创建index.html文件

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title>React and ES6 Part 6</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.2/css/foundation.min.css">
    </head>
    <body>
    <nav class="top-bar" data-topbar role="navigation">
        <section class="top-bar-section">
            <ul class="left">
                <li class="active">
                    <a href="http://egorsmirnov.me/2016/04/11/react-and-es6-part6.html" target="_blank">
                        Blog post at egorsmirnov.me: React and ES6 - Part 6, React and ES6 Workflow with Webpack
                    </a>
                </li>
            </ul>
        </section>
    </nav>
    <div class="root"></div>
    <script src="/dist/bundle.js"></script>
    </body>
    </html>
    • 创建app.js文件

    import React from 'react';
    import ReactDOM from 'react-dom';
    import CartItem from './cartItem.js';
    
    const order = {
        title: 'Fresh fruits package',
        image: 'http://images.all-free-download.com/images/graphiclarge/citrus_fruit_184416.jpg',
        initialQty: 3,
        price: 8
    };
    
    ReactDOM.render(
        < CartItem
            title={order.title}
            image={order.image}
            initialQty={order.initialQty}
            price={order.price
            }
        />,
        document.querySelector('.root')
    )
    ;
    • 创建cartItem.js文件

    import React from 'react';
    
    export default class CartItem extends React.Component {
    
        static propTypes = {
            title: React.PropTypes.string.isRequired,
            price: React.PropTypes.number.isRequired,
            initialQty: React.PropTypes.number
        };
    
        static defaultProps = {
            title: 'Undefined Product',
            price: 100,
            initialQty: 0
        };
    
        state = {
            qty: this.props.initialQty,
            total: 0
        };
    
        constructor(props) {
            super(props);
        }
    
        componentWillMount() {
            this.recalculateTotal();
        }
    
        increaseQty() {
            this.setState({qty: this.state.qty + 1}, this.recalculateTotal);
        }
    
        decreaseQty() {
            let newQty = this.state.qty > 0 ? this.state.qty - 1 : 0;
            this.setState({qty: newQty}, this.recalculateTotal);
        }
    
        recalculateTotal() {
            this.setState({total: this.state.qty * this.props.price});
        }
    
        render() {
            return (
                <article className="row large-4">
                    <figure className="text-center">
                        <p>
                            <img src={this.props.image}/>
                        </p>
                        <figcaption>
                            <h2>{this.props.title}</h2>
                        </figcaption>
                    </figure>
                    <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p>
    
                    <p className="large-4 column">
                        <button onClick={this.increaseQty.bind(this)} className="button success">+</button>
                        <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button>
                    </p>
    
                    <p className="large-4 column"><strong>Price per item:</strong> ${this.props.price}</p>
    
                    <h3 className="large-12 column text-center">
                        Total: ${this.state.total}
                    </h3>
    
                </article>
            );
        }
    }

    修改package.json

    现在已经将前面所有碎片化的代码已经整合在一个项目中。

    我们需要在package.json文件的scripts区域添加一些脚本。

    {
      "name": "awesome-application",
      "version": "1.0.0",
      ...
      "scripts": {
        "start": "node server.js"
      },
      ...
    }

    运行项目

    • 运行npm start

    • 在浏览器中打开http://localhost:3000

    • 项目运行效果图
      cover

    Webpack生产环境配置

    现在我们能够在服务器上运行我们的应用程序并且能够通过热模块更新刷新我们的页面。

    但是如果我们想要将产品部署到生产环境?没问题,Webpack有对应的解决方案。

    创建webpack.config.prod.js文件,文件内容为:

    var path = require('path');
    var webpack = require('webpack');
    
    var config = {
        devtool: 'source-map',
        entry: [
            './app.js'
        ],
        output: {
            path: path.join(__dirname, 'dist'),
            filename: 'bundle.js'
        },
        plugins: [
            new webpack.optimize.OccurrenceOrderPlugin(),
            new webpack.DefinePlugin({
                'process.env': {
                    'NODE_ENV': JSON.stringify('production')
                }
            }),
            new webpack.optimize.UglifyJsPlugin({
                compressor: {
                    warnings: false
                }
            })
        ],
        module: {
            loaders: [
                {
                    test: /.js$/,
                    loaders: ['babel-loader'],
                    exclude: /node_modules/
                }
            ]
        }
    };
    
    module.exports = config;

    它和开发模式下的配置文件有点相似,但是有以下不同点:

    • 热刷新的功能不再有,因为在生产环境中不需要这个功能。

    • JavaScript bundle被依赖于webpack.optimize.UglifyJsPluginUglifyJs压缩。

    • 环境变量NODE_ENV被设置成production。这需要屏蔽来自React开发环境中的警告。

    下一步,更新package.json文件中的scripts

    {
      ...
      "scripts": {
         "start": "node server.js",
         "clean": "rimraf dist",
         "build:webpack": "NODE_ENV=production webpack --progress --colors --config webpack.config.prod.js",
         "build": "npm run clean && npm run build:webpack"
      },
      ...
    }

    到现在为止,如果你在控制台运行npm run build,压缩文件bundle.js将被创建并且放在dist/路径下面。这个文件准备在生产环境中使用。

    这只是刚刚开始

    我们刚才学到的东西只是Webpack的一些基础。

    Webpack是一个很容易入门的工具,但是要想精通,需要点时间好好研究研究。

    参考文档

    社群品牌:从零到壹全栈部落
    定位:寻找共好,共同学习,持续输出全栈技术社群
    业界荣誉:IT界的逻辑思维
    文化:输出是最好的学习方式
    官方公众号:全栈部落
    社群发起人:春哥(从零到壹创始人,交流微信:liyc1215)
    技术交流社区:全栈部落BBS
    全栈部落完整系列教程:全栈部落完整电子书学习笔记

    关注全栈部落官方公众号,每晚十点接收系列原创技术推送
  • 相关阅读:
    【原创】cs+html+js+css模式(一):初识新模式
    【原创】cs+html+js+css模式(三):RemoteCallHandler详解
    删除表数据
    【原创】cs+html+js+css模式(二):webconfig中的设置
    silverlight动画
    rdlc报表表达式应用(字符串和转换)
    Accordion控件制作下拉面板菜单(静态数据)
    silverlight三种布局
    Sys.UI.DomElement
    Accordion控件动态数据绑定案例
  • 原文地址:https://www.cnblogs.com/10manongit/p/12220753.html
Copyright © 2020-2023  润新知