blog icon indicating copy to clipboard operation
blog copied to clipboard

使用 Webpack 和 ES6 进行 React 开发

Open huangtengfei opened this issue 8 years ago • 20 comments

工欲善其事必先利其器,在进行 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傻瓜式指南

huangtengfei avatar May 13 '16 10:05 huangtengfei

非常好的入门资料,谢谢

goldingking avatar Aug 26 '16 09:08 goldingking

代码并不能实时更新啊

Shiyanping avatar Sep 05 '16 09:09 Shiyanping

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

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

huangtengfei avatar Sep 07 '16 02:09 huangtengfei

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

Shiyanping avatar Sep 07 '16 02:09 Shiyanping

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

img

huangtengfei avatar Sep 07 '16 03:09 huangtengfei

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

Shiyanping avatar Sep 07 '16 03:09 Shiyanping

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

Shiyanping avatar Sep 07 '16 04:09 Shiyanping

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

huangtengfei avatar Sep 07 '16 06:09 huangtengfei

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

Shiyanping avatar Sep 07 '16 09:09 Shiyanping

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

huangtengfei avatar Sep 08 '16 06:09 huangtengfei

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

zhangwei900808 avatar Nov 05 '16 09:11 zhangwei900808

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

zhangwei900808 avatar Nov 05 '16 09:11 zhangwei900808

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

lwbweb avatar Dec 12 '16 06:12 lwbweb

没有导入成功less 可以手动一下 npm install less --save-dev

在2016年12月12 14时21分, "Wenbo Lu"[email protected]写道:

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

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

yurizhang avatar Dec 12 '16 08:12 yurizhang

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

lwbweb avatar Dec 12 '16 08:12 lwbweb

不客气,我公司最近的项目正在用这个,一起学习·

在2016年12月12 16时52分, "Wenbo Lu"[email protected]写道:

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

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

yurizhang avatar Dec 12 '16 14:12 yurizhang

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

beaplat-61f avatar Dec 16 '16 03:12 beaplat-61f

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

shawn2016 avatar Mar 16 '17 06:03 shawn2016

不错

qqqzhch avatar Jun 29 '17 01:06 qqqzhch

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

htmlcssscript avatar Jul 06 '17 14:07 htmlcssscript