上手 Webpack,搭建 React 开发环境

作者: 刘星
日期: 2018年7月18日

webpack

随着互联网的蓬勃发展,web 应用越来越复杂,一个前端 web 项目有着一大堆依赖包和复杂的 JavaScript 代码。然而在 web 端,模块化的支持来的很缓慢,CSS 很容易发生冲突,JavaScript 语法较为松散……存在着种种急需解决的问题,为了简化开发的复杂度,蓬勃发展的前端社区涌现出了很多好的实践方法及工具:各种模块化规范、CSS 预处理、CSS 后处理器、TypeScript、Grunt / Gulp 等构件工具以及 webpack、browserify、rollup、parcel 等模块打包工具等等。

在前端构建中,代码模块打包几乎就是最重要的一部分,而 webpack 是模块打包工具中最流行最具代表性的一个。webpack 是一个模块打包器。它的主要目标是将 JavaScript 文件打包在一起,打包后的文件用于在浏览器中使用,但它也能够胜任转换、打包或包裹任何资源。

上手 webpack

本文将通过实例讲解 webpack,最后完成一个 react 开发环境集成 babel、eslint、sass。

新建一个 webpack-react 初始化 npm 并安装 webpack

mkdir webpack-react && cd webpack-react
npm init -y
npm install webpack webpack-cli --save-dev

进入webpack-react 新建三个文件 index.html index.js hello.js

  webpack-react
  |- package.json
+ |- dist
+   |- index.html
+ |- /src
+   |- index.js
+   |- hello.js

src/hello.js

// hello.js
export default function () {
  const element= document.createElement('div')
  element.innerHTML = '欢迎关注公众号JavaScript之禅'
  return element
}

src/index.js

// index.js
import hello from "./hello.js"
document.body.appendChild(hello())

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Getting Started</title>
</head>
<body>
  <script src="./main.js"></script>
</body>
</html>

在命令行执行 npx webpack 试试,运行该命令会将资源打包到 dist 目录下的 main.js。 webpack 4.x 可以零配置构建。此时你会看见一个警告:未设置 mode,webpack 4.x 提供了 mode 配置来使用相应的内置优化,有生产模式(production)、开发模式(development)和不设置(none)三种。但是为了更好的使用我们往往需要自己写配置文件。

先来看看 webpack 的常用配置项, 详细参考 https://webpack.docschina.org/configuration/

module.exports = {
    mode: 'production',      // 模式
    entry: '',               // 入口文件
    output: {},              // 出口文件
    module: {},              // 处理对应模块
    plugins: [],             // 对应的插件
}

按照项目,我们从 0 写个最简单的配置 webpack.config.js 它的实现的功能就像零配置一样。

// webpack.config.js
const path = require('path')

module.exports = {
    entry: './src/index.js',        // 入口文件
    output: {
        filename: 'main.js',        // 打包后的文件名称
        path: path.resolve('dist')  // 打包后的目录,必须是绝对路径
    }
}

启动 devServer

现在我们想要看到效果都需要,运行命令打包后再在浏览器中查看,现在我们使用 webpack-dev-server 来免去这些繁琐的步骤。webpack-dev-server 为我们提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)

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

修改配置文件,告诉开发服务器(dev server),在哪里查找文件、在哪个端口启动:

// webpack.config.js
const path = require('path')

module.exports = {
    entry: './src/index.js',        // 入口文件
    output: {
        filename: 'main.js',        // 打包后的文件名称
        path: path.resolve('dist')  // 打包后的目录,必须是绝对路径
    },
    devServer: {
        contentBase: './dist',
        open: true,
        port: 3000
    }
}

执行 npx webpack-dev-server 就可以将这个服务起起来,我们将它加到 npm script 中去

...
"scripts": {
    "dev": "webpack-dev-server",
    "build": "webpack"
},
...

开发时直接运行npm run dev 即可,打包则运行npm run build

使用 source map

当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置,为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。当然如果你设置了 mode 为 development,他是默认开启的。

module.exports = {
    ...
	devtool: 'inline-source-map'
    ...
}

配置 Html 模板

现在项目的 index.html 是手动创建的,如果更改打包后的文件的名称就又要去手动更改一次。我们使用 html-webpack-plugin 插件来搞定,生成一个自动引用你打包后的 JS 文件的新 index.html

$ npm install --save-dev html-webpack-plugin

修改配置文件

const HtmlWebpackPlugin  = require('html-webpack-plugin')

module.exports = {
    ...
    plugins: [
      new HtmlWebpackPlugin()
    ]
}

使用 sass

Loaderswebpack 提供的最激动人心的功能之一了。通过使用不同的loader实现对不同格式的文件的处理。如 sass 转 css,jsx 转 js。

这一步我们先来配置 CSS,为了从 JavaScript 模块中 import 一个 CSS 文件,你需要在 module 配置中安装并添加 style-loader 和 css-loader。这儿我还用了 sass,所以多下载了sass-loadernode-sass

$ npm install  --save-dev css-loader style-loader sass-loader node-sass

webpack.config.js

// 使用sass
...
module: {
    rules: [
        {
            test: /\.scss$/,
            use: [
                'style-loader',
                'css-loader'
                'sass-loader'
            ]
        }
    ]
}
...

// ============
// 如果你不需要sass 配置css即可
// ============
...
module: {
    rules: [
        {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader'
            ]
        }
    ]
}
...

新建一个hello.scss, 在 hello.js 中引用

hello.scss

.hello {
  color: red;
}

hello.js

// hello.js
import './hello.scss'

export default function () {
    const element= document.createElement('div')
    element.innerHTML = '欢迎关注公众号JavaScript之禅'
    element.classList.add('hello')
    return element
}

执行npm run build 在浏览器中打开 index.html就能看见样式生效了

使用 autoprefixer 添加 CSS3 前缀

由于 CSS3 很多属性还需要添加各个浏览器的前缀,使用 postcss 的 autoprefixer 可以帮助我们方便的处理这个问题

$ npm i -D postcss-loader autoprefixer

新建一个 postcss.config.js 文件配置下 postcss

module.exports = {
  plugins: [
      require('autoprefixer')
  ]
}

在 webpack 配置中添加 postcss-loader

module: {
    rules: [
      {
        test: /\.js$/,
        include: /src/,
        exclude: /node_modules/,
        use: [
          'babel-loader'
        ]
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader', // 添加 postcss-loader
          'sass-loader'
        ]
      }
    ]
  },

分离 CSS

在多数情况下打包时都会进行 CSS 分离,以便在生产环境中节省加载时间。在 4.x 以前的版本中,我们使用 extract-text-webpack-plugin 来拆分 CSS。在 4.x 版本中 webpack 推荐使用 mini-css-extract-plugin 替代。

$ npm install --save-dev mini-css-extract-plugin

在配置文件中配置下这个插件

...
{
    test: /\.scss$/,
    use: [
        // 'style-loader',
        MiniCssExtractPlugin.loader,
        'css-loader',
        'postcss-loader',
        'sass-loader'
    ]
}
...
plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: "static/css/[name].css",
      chunkFilename: "static/css/[id].css"
    })
  ]

运行npm run build,webpack 会将 CSS 单独打包出来。

如果重复执行npm run build dist目录下会有上次打包后的文件,这儿使用 clean-webpack-plugin 这个插件来清理

$ npm i -D clean-webpack-plugin

在配置文件中配置我们的需要清理的文件夹

...
plugins: [
    ...
    new CleanWebpackPlugin(['dist'])
]
...

使用 babel 编译 react

前面说了,我们的目标是搭建 react 开发环境,现在就配置 babel-loader 来转码 react。

$ npm install --save-dev babel-loader babel-core babel-preset-env babel-preset-react
$ npm install --save react react-dom

新增一个.babelrc文件配置 babel

{
  "presets": [
    "env", 
    "react"
  ]
}

修改 webpack.config.js,增加一个处理 react 的 loader

module: {
    rules: [
      ...
      {
        test: /\.js$/,
        include: /src/,
        exclude: /node_modules/,
        use: [
          'babel-loader'
        ]
      },
      ...
   ]
  },

删除 src 目录下的所有文件,新建一个index.jsindex.scss

// index.scss
body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
}
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.scss'

ReactDOM.render(
  <h1>欢迎关注公众号JavaScript之禅</h1>
  ,document.getElementById('root'))

我们需要修改 HtmlWebpackPlugin 的配置,在public文件夹中新建index.html 用来作为生成 html 文件的模板

<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="/manifest.json">
    <link rel="shortcut icon" href="/favicon.ico">
    <title>React App</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
  </body>
</html>

修改 webpack.config.js 中的配置

...
plugins: [
    new HtmlWebpackPlugin({
        template: path.resolve(__dirname, '../public/index.html'),
        hash: true
    })
]
...

现在就可以愉快的编写 react 了。

处理图片字体等资源

前面说过 webpack 处理各种文件就是配置各种 loader,对于图片、字体等资源我们可以使用 file-loader 来处理

$ npm install file-loader --save-dev

修改 webpack 的配置文件,让其支持图片文字等资源

...
{
  test: /\.(png|svg|jpg|gif)$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        name: 'static/images/[name].[hash:8].[ext]'
      }
    }
  ]
},
{
  test: /\.(woff|woff2|eot|ttf|otf)$/,
  use: [
    'file-loader'
  ]
}
...

使用 ESLint 检查代码

ESLint 是一个 javascript 代码检测工具,它可以用于检查常见的 JavaScript 代码错误,也可以进行代码风格检查,这样我们就可以根据自己的喜好指定一套 ESLint 配置,然后应用到所编写的项目上,从而实现辅助编码规范的执行,有效控制项目代码的质量。

在 webpack 中我们可以使用 eslint-loader 来检查代码,笔者我比较喜欢 JavaScript Standard Style ,现在就来配置下

$ npm install --save-dev eslint eslint-loader eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node  eslint-plugin-react

新建 .eslintrc 配置 eslint 规则

{
  "extends": [
    "standard",
    "plugin:react/recommended"
  ]
}

在 webpack 配置中新增 eslint-loader

...
module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        include: /src/,
        exclude: /node_modules/,
        loader: 'eslint-loader'
      }
      ...
    ]
    ...
}
...

启用 HMR

模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。我们是讲快速上手搭建 react 开发环境的,在此就不细说 HMR 了。我们直接使用 react-hot-loader 这个模块。

$ npm --save-dev install react-hot-loader

.babelrc中添加配置

{
  ...
  "plugins": ["react-hot-loader/babel"]
}

src/App.js中使用 react-hot-loader

import React from 'react'
import { hot } from 'react-hot-loader'
 
class App extends Component {}
 
export default hot(module)(App)

除此之外还需要在 webpack.config.js 中添加如下配置,并且修改下 devServer 的配置

plugins: [
    ...
	new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
],
devServer: {
    contentBase: './dist',
    port: 3000,
    open: true,
    hot: true
}

现在运行npm run dev 修改项目代码试试。如果你跟着教程一步一步走到这儿,你会发现修改 CSS 并不会生效,这是因为,我们用了 MiniCssExtractPlugin 来拆分 CSS 文件,在开发环境中我们还是使用 style-loader 即可,同时开发环境还应该启用 sourceMap 方便调试。

下一节再讲开发环境与生产环境的区分,并在此基础上做些调整优化。

文档信息

版权声明:署名-非商业性使用-禁止演绎 4.0 国际(CC BY-NC-ND 4.0)

原文链接:https://www.liuxing.io/blog/webpack-react-tutorial/

发表日期:2018年07月18日


最近更新

Curl 使用指南

Curl 是一个常用的命令行数据传输工具, 可以方便的从命令行创建网络请求

2021年5月04日

JavaScript 中的分号

JavaScript 代码到底加不加分号。本文带你了解 JavaScript 的分号自动插入机制,以及哪些情况必须加分号

2021年5月02日