New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用 Webpack 和 ES6 进行 React 开发 #17

Open
huangtengfei opened this Issue May 13, 2016 · 20 comments

Comments

Projects
None yet
10 participants
@huangtengfei
Owner

huangtengfei commented May 13, 2016

工欲善其事必先利其器,在进行 React 的开发时,通常会结合 Webpack 和 ES6 一起进行。本文讲的正是如何使用 React + Webpack + ES6 进行组合开发。关于 React 的入门,后面另开一文。

本文最终的输出是一个 Hello World 。

本文所涉及的代码在 这里 ,可以直接作为一个种子项目使用:

git clone https://github.com/huangtengfei/react-webpack-es6.git
cd react-webpack-es6
npm install
npm run dev

如果你使用 sublime 开发,可以安装 sublime-react 插件。

安装依赖项

在项目目录下 ,初始化一个 package.json 文件,执行:

npm init

安装 reactreact-dom 依赖:

npm install react react-dom --save

安装 webpackwebpack-dev-server 依赖:

npm install webpack webpack-dev-server --save-dev 

安装 babel 依赖:

npm install babel-loader babel-core babel-preset-react babel-preset-es2015 --save-dev

代码编写

本文示例最终的目录结构是这样的:

--your project
  |--components(组件目录)
    |--Hello(组件1)
      |--imgs
        |--bg.png
      |--index.jsx
      |--index.less
    |--World(组件2)
      |--index.jsx
      |--index.less
    |--index.js(入口文件)
  |--build(输出目录)
    |--index.html
    |--bundle.js(输出文件,由 webpack 打包后生成的)
  |--package.json
  |--webpack.config.js

创建 React 组件

按照前面的目录结构,新建一个 components 文件夹,存放所有组件。

在 components 下建一个 Hello 文件夹,用来存放 Hello 组件的逻辑和样式。

Hello/index.js 的代码如下:

import React from 'react';
import ReactDOM from 'react-dom';

class Hello extends React.Component {
  render() {
    return <h1>Hello</h1>
  }
}

ReactDOM.render(<Hello />, document.getElementById('hello'));

World 组件的开发同上,略去。

创建页面

在 build 目录下创建一个 HTML 页面,来使用前面创建的两个组件。

index.html 的代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World</title>
  </head>
  <body>
    <div id="hello"></div>
    <div id="world"></div>
  </body>
</html>

使用 Webpack 打包

Webpack 是一个前端模块加载兼打包工具,可以对 JS、CSS 和 图片 等都作为模块来使用和处理。对不同类型的需要编译的文件,需要使用相应的加载器(比如用 babel 转译 ES6)。

另外,由于 Webpack 需要一个入口文件来进行分析和处理,需要先将 React 组件引入到一个主文件。

从前文的目录结构可以看出,components/index.js 就是这个主文件,它的代码如下:

import Hello from './Hello/index.jsx';
import World from './World/index.jsx';

下面就是 Webpack 配置文件 webpack.config.js 的编写,代码如下:

var path = require('path');
var webpack = require('webpack');

var ROOT_PATH = path.resolve(__dirname);
var APP_PATH = path.resolve(__dirname, './components/index.js');
var BUILD_PATH = path.resolve(__dirname, './build');

module.exports = {
  entry: APP_PATH,
  output: {
    path: BUILD_PATH,
    filename: 'bundle.js'
  },
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loaders: ['babel-loader?presets[]=es2015,presets[]=react']
    }]
  }
}

最后,需要将编译打包后的文件 bundle.js 引入到 index.html 中:

<body>
  ...
  <script src="./bundle.js"></script>
</body>

构建和启动

构建

前面的开发完成后,需要执行 webpack.config.js 中的构建任务,生成 bundle.js,这个操作可以在 package.json 中配置:

"scripts": {
  "build": "webpack"
}

执行 npm run build 完成构建,此时打开 index.html ,即可看到效果。

启动服务器

但这种方式显得略 low,一是它是双击以文件的形式打开 HTML 页面,二是每次有更改都要手动执行 npm run build 重新打包。

一种更好的方式是启动一个静态资源服务器,监听文件内容修改并自动打包。在这里用的是前面安装好的 webpack-dev-server ,在 package.json 中配置:

"scripts": {
  "dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build"
}

简单解释一下 dev 中各个参数的含义:webpack-dev-serverlocalhost:8080 建立一个 Web 服务器;--devtool eval 映射编译好的源码,用于调试;--progress 显示代码打包进度;--colors 表示在命令行中显示颜色; --content-base 来指定 server 启动后的内容目录。

执行 npm run dev 启动 server,此时打开 http://localhost:8080 ,即可看到效果。修改一下 Hello 或者 World 组件中的内容,刷新页面,你会发现浏览器中内容也相应改变了。

自动刷新

前面实现了对文件修改的监听和自动打包,但浏览器还需要手动刷新。其实可以在 Webpack 的配置文件中增加一个入口点,实现自动刷新。

···
    entry: [
    'webpack/hot/dev-server',
      'webpack-dev-server/client?http://localhost:8080',
      APP_PATH
  ],
···

这样,应用在修改后,浏览器就会自动刷新了。

更完整的种子

其实讲到这里本文已经可以结束了,不过为了让种子更完整,并体现 Webpack 的强大,再加上点样式和图片吧。

处理样式

不管什么类型的资源,Webpack 都需要相应的 loader 来处理。对于普通的 css,需要 css-loaderstyle-loader 两种 loader,前者会遍历所有 css 并对 url() 处理,后者将样式插入到页面的 style 标签中。对于预编译 css 语言,还需要额外的 loader,比如 less-loadersass-loader,这里用 less 举例。

首先,安装 loader :

npm install less-loader css-loader style-loader --save-dev

然后,在 webpack.config.js 中配置 loader,注意 loader 的处理顺序是从右到左的:

...
{
    test: /\.less$/,
    loader: 'style!css!less'
}
...

配置完之后要重新运行一下 npm run dev

接着,在 Hello 和 World 组件中各添加一个样式文件,这里用 Hello/index.less 举例:

@lightgrey: #ccc;

h1 {
  background: @lightgrey;
}

最后,在 components/index.js 中引入 less 文件:

import './Hello/index.less';
import './World/index.less';

切换到浏览器,看一下是不是有变化了呢~

处理图片

为了减轻网络请求的压力,有时候会有将小图片转成 BASE64 字符串的需求,这个可以通过 url-loader 来实现。

同处理样式中的操作,首先安装 url-loader

npm install url-loader --save-dev

然后配置 webpack.config.js

...
{
    test: /\.(png|jpg)$/,
    loader: 'url?limit=50000'
}
...

重新重新运行一下 npm run dev

接着,在 Hello 组件下建一个 imgs 文件夹,并放入一张小于 50K 的 bg.png 做背景图片,然后修改 Hello/index.less 文件:

h1 {
  background: url('./img/bg.png');
}

切换到浏览器,审查元素,看一下图片是不是转成 BASE64 字符串了呢~

到此为止,这已经是一个较为完整的种子了。想对 React 、Webpack 和 ES6 各自做深入了解的,请自行查阅资料~

补充

loader 的写法

在使用 Webpack 打包小节 webpack.config.js 的编写中,babel-loader 最初我是这样写的:

loaders: [{
    test: /\.jsx?$/,
    loader: 'babel-loader',
    query: {
        presets: ['es2015', 'react']
    }
}]

后来为了使用热加载,引入了 react-hot ,loader 要改为 loaders,即:

...
    loaders: ['babel-loader', 'babel-loader'],
    query: {
        presets: ['es2015', 'react']
    }
...    

这时在执行构建的时候,会报错 Error: Cannot define 'query' and multiple loaders in loaders list,于是改成这种写法。不过后来直接用 webpack-dev-server 的 --hot 选项,也没引入 react-hot 了,但此处写法仍保留,便于后面扩展 loader 。

安装 less-loader 报错

如果在安装样式的几个 loader 的时候,报以下错误:

UNMET PEER DEPENDENCY less@^2.3.1

不妨试试用 cnpm 单独安装 less :

tnpm i less@^2.3.1

参考

React Webpack 小书

Setting up React for ES6 with Webpack and Babel

Webpack傻瓜式指南

@goldingking

This comment has been minimized.

Show comment
Hide comment
@goldingking

goldingking Aug 26, 2016

非常好的入门资料,谢谢

goldingking commented Aug 26, 2016

非常好的入门资料,谢谢

@Shiyanping

This comment has been minimized.

Show comment
Hide comment
@Shiyanping

Shiyanping Sep 5, 2016

代码并不能实时更新啊

Shiyanping commented Sep 5, 2016

代码并不能实时更新啊

@huangtengfei

This comment has been minimized.

Show comment
Hide comment
@huangtengfei

huangtengfei Sep 7, 2016

Owner

@Shiyanping 如果你是按本文开头的直接 clone 代码,是已经实现了更新后浏览器自动刷新的,如果是从开头一步步自己写的,在 webpack 配置文件别忘了加:

···
entry: [
  'webpack/hot/dev-server',
  'webpack-dev-server/client?http://localhost:8080',
  APP_PATH
],
···
Owner

huangtengfei commented Sep 7, 2016

@Shiyanping 如果你是按本文开头的直接 clone 代码,是已经实现了更新后浏览器自动刷新的,如果是从开头一步步自己写的,在 webpack 配置文件别忘了加:

···
entry: [
  'webpack/hot/dev-server',
  'webpack-dev-server/client?http://localhost:8080',
  APP_PATH
],
···
@Shiyanping

This comment has been minimized.

Show comment
Hide comment
@Shiyanping

Shiyanping Sep 7, 2016

@huangtengfei 我是直接clone的代码,可以正常运行,修改jsx的内容不能实现自动刷新,但是修改less内容开始可以,修改两次就不可以了

Shiyanping commented Sep 7, 2016

@huangtengfei 我是直接clone的代码,可以正常运行,修改jsx的内容不能实现自动刷新,但是修改less内容开始可以,修改两次就不可以了

@huangtengfei

This comment has been minimized.

Show comment
Hide comment
@huangtengfei

huangtengfei Sep 7, 2016

Owner

@Shiyanping 不会啊,我刚才把代码更新下来试了一下,没有你说的问题:

img

Owner

huangtengfei commented Sep 7, 2016

@Shiyanping 不会啊,我刚才把代码更新下来试了一下,没有你说的问题:

img

@Shiyanping

This comment has been minimized.

Show comment
Hide comment
@Shiyanping

Shiyanping Sep 7, 2016

@huangtengfei 方便给个QQ或者什么联系方式吗?我的是真有问题,求大神给说说

Shiyanping commented Sep 7, 2016

@huangtengfei 方便给个QQ或者什么联系方式吗?我的是真有问题,求大神给说说

@Shiyanping

This comment has been minimized.

Show comment
Hide comment
@Shiyanping

Shiyanping Sep 7, 2016

文件clone下来放的目录有要求吗?如果方便希望您加我QQ,帮我解答下,我QQ594388036。

Shiyanping commented Sep 7, 2016

文件clone下来放的目录有要求吗?如果方便希望您加我QQ,帮我解答下,我QQ594388036。

@huangtengfei

This comment has been minimized.

Show comment
Hide comment
@huangtengfei

huangtengfei Sep 7, 2016

Owner

@Shiyanping clone下来的目录没要求,加qq的话我晚上下班回去后加你吧

Owner

huangtengfei commented Sep 7, 2016

@Shiyanping clone下来的目录没要求,加qq的话我晚上下班回去后加你吧

@Shiyanping

This comment has been minimized.

Show comment
Hide comment
@Shiyanping

Shiyanping Sep 7, 2016

@huangtengfei 我找到原因了,我用的webstorm,会自动保存,有时候刷新,有时候不刷,我换成sublime手动保存就完全可以了。谢谢咯

Shiyanping commented Sep 7, 2016

@huangtengfei 我找到原因了,我用的webstorm,会自动保存,有时候刷新,有时候不刷,我换成sublime手动保存就完全可以了。谢谢咯

@huangtengfei

This comment has been minimized.

Show comment
Hide comment
@huangtengfei

huangtengfei Sep 8, 2016

Owner

@Shiyanping OK,解决就行,昨晚加班回去太晚,忘了加q了

Owner

huangtengfei commented Sep 8, 2016

@Shiyanping OK,解决就行,昨晚加班回去太晚,忘了加q了

@zhangwei900808

This comment has been minimized.

Show comment
Hide comment
@zhangwei900808

zhangwei900808 Nov 5, 2016

非常好的入门教程,最好发表出来,让更多人知道

zhangwei900808 commented Nov 5, 2016

非常好的入门教程,最好发表出来,让更多人知道

1 similar comment
@zhangwei900808

This comment has been minimized.

Show comment
Hide comment
@zhangwei900808

zhangwei900808 Nov 5, 2016

非常好的入门教程,最好发表出来,让更多人知道

zhangwei900808 commented Nov 5, 2016

非常好的入门教程,最好发表出来,让更多人知道

@lwbweb

This comment has been minimized.

Show comment
Hide comment
@lwbweb

lwbweb Dec 12, 2016

为什么直接clone的会报错client:75 Cannot find module 'less'、?根据步骤创建的也会报类似的错 Cannot resolve module 'Hello/index.less' ,新人求赐教、

lwbweb commented Dec 12, 2016

为什么直接clone的会报错client:75 Cannot find module 'less'、?根据步骤创建的也会报类似的错 Cannot resolve module 'Hello/index.less' ,新人求赐教、

@yurizhang

This comment has been minimized.

Show comment
Hide comment
@yurizhang

yurizhang Dec 12, 2016

yurizhang commented Dec 12, 2016

@lwbweb

This comment has been minimized.

Show comment
Hide comment
@lwbweb

lwbweb Dec 12, 2016

非常感谢能得到您的回复,确实是这个问题,超级感谢。

lwbweb commented Dec 12, 2016

非常感谢能得到您的回复,确实是这个问题,超级感谢。

@yurizhang

This comment has been minimized.

Show comment
Hide comment
@yurizhang

yurizhang Dec 12, 2016

yurizhang commented Dec 12, 2016

@beaplat-61f

This comment has been minimized.

Show comment
Hide comment
@beaplat-61f

beaplat-61f Dec 16, 2016

react入门的文章太多了,捣鼓了半天,终于靠这篇文章部署好环境了

beaplat-61f commented Dec 16, 2016

react入门的文章太多了,捣鼓了半天,终于靠这篇文章部署好环境了

@shawn2016

This comment has been minimized.

Show comment
Hide comment
@shawn2016

shawn2016 Mar 16, 2017

很不错,很好的入门资料。

shawn2016 commented Mar 16, 2017

很不错,很好的入门资料。

@qqqzhch

This comment has been minimized.

Show comment
Hide comment
@qqqzhch

qqqzhch commented Jun 29, 2017

不错

@htmlcssscript

This comment has been minimized.

Show comment
Hide comment
@htmlcssscript

htmlcssscript Jul 6, 2017

这篇现在看不来了 , 老铁还更吗

htmlcssscript commented Jul 6, 2017

这篇现在看不来了 , 老铁还更吗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment