从零到一搭建 React 项目

从零到一搭建 react 项目系列之(四)

2020-08-20  本文已影响0人  越前君

上一篇文章介绍了如何打包成 HTML 文件,接着我们将介绍如何搭建开发环境之热更新

使用 source-map

如果按照默认的 production 模式打包代码是,可能会很难追踪错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js, b.jsc.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js。你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。

为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你。

source map 有许多可用选项,请务必仔细阅读它们,以便可以根据需要进行配置。

对于本指南,我们将使用 eval-source-map 选项,这有助于解释说明示例意图(此配置仅用于示例,不要用于生产环境):

选择一个工具

在每次编译代码时,手动运行 yarn run build 会显得特别麻烦。

webpack 提供了几种可选方式,帮助我们在代码发生变化后自动编译代码:

webpack 可以在 watch mode(观察模式)下使用。在这种模式下,webpack 将监视您的文件,并在更改时重新编译。
webpack-dev-server 提供了一个易于部署的开发服务器,具有快速的实时重载(live reloading)功能。
如果你已经有一个开发服务器并且需要完全的灵活性,可以使用 webpack-dev-middleware 作为中间件。

webapck-dev-serverwebpack-dev-middleware 使用内存编译,这意味着 bundle 不会被保存在硬盘上。这使得编译十分迅速,并导致你的文件系统更少麻烦。

在大多数情况下你会想要使用 webpack-dev-server,因为这是最简单的开始的方式,并且提供了很多开箱即用的功能。本项目中也将会使用到它。

简单配置 webpack-dev-server

# 安装
$ yarn add webpack-dev-server@3.9.0 --dev

// 修改 webpack.config.js 配置
devServer: {
  contentBase: './dist',
  open: true
}

// 在 package.json 添加 npm script
"scripts": {
    "dev": "webpack-dev-server --config webpack.config.js --colors"
}

以上配置告知 webpack-dev-server,将 dist目录下的文件 servelocalhost:8080 下。(译注:serve,将资源作为 server 的可访问文件)。

现在,在命令行中运行 yarn run dev,我们会看到浏览器自动加载页面。如果你更改任何源文件并保存它们,web server 将在编译代码后自动重新加载。

但是同时可以观察到一个细节,每次更改文件页面会重新加载,但是这应该不是我们想要的,我们想要的是模块热替换hot module replacement)。

HMR 模块热替换配置

*首先它不适用于生产环境,仅应用于开发环境。

模块热替换功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:

// webpack.config.js
const config = {
  // ... 其他已有配置不变
  devServer: {
    contentBase: './dist',
    hot: true,
    open: true
  },
  plugins: [
    // 在 webpack 4 中其实已被弃用,取代它的是 optimization.namedModules 后续的文章会讲解到
    // new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ]
}

我们在入口文件 index.js 引入 main.js

// main.js
console.log('This is main.js')

我们来修改 index.js文件,以便当 main.js 内部发生变更时可以告诉 webpack 接受更新的模块。

// index.js
import './main.js'

console.log('Hello Webpack!')

if(module.hot) {
    module.hot.accept('./main.js', () => {
        console.log('Accept update!')
    })
}

注意,若修改了 webpack 配置文件,我们需要重新执行 yarn run dev 使其生效。

当我们修改 main.js 按下保存之后,会看到浏览器控制台会输出以下信息:

[WDS] App updated. Recompiling...
[WDS] App hot update...
[HMR] Checking for updates on the server...
This is main.js.
We modified the main.js file.
Accept update!
[HMR] Updated modules:
[HMR]  - ./src/main.js
[HMR] App is up to date.

HMR 加载样式

借助于 style-loader,使用模块热替换来加载 CSS 实际上极其简单。此 loader 在幕后使用了 module.hot.accept,在 CSS 依赖模块更新之后,会将其 patch(修补) 到 <style> 标签中。

在此之前,我们说过 webpack 只能读取 JavaScript 和 JSON 文件,其他类型的文件需要借助对应的 loader 来处理。

首先使用以下命令安装两个 loader 。

$ yarn add --dev css-loader@3.2.0 style-loader@1.0.0

更新 webpack 配置文件

// webpack.config.js
const config = {  
  module: {
    rules: [
      {
        test: /\.css/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

module.exports = config

我们在 src 目录下新增 style.css 文件,并在 index.js 中引入。

/* style.css */
body {
    font-size: 30px;
}
// index.js
import './main.js'
import './style.css'

console.log('Hello Webpack!')

if(module.hot) {
    module.hot.accept('./main.js', () => {
        console.log('Accept update!')
    })
}

然后我们试着修改 style.css 的样式,就能看到页面字体大小随之更改,而无需完全刷新。

至此

我们最简单的 HMR 已经配置好了,接着我们将会介绍接入 React 。

最后附上

// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const webpack = require('webpack')

const config = {
  mode: 'development',
  devtool: 'eval-source-map',
  entry: {
    index: path.resolve(__dirname, './src/index.js')
  },
  devServer: {
    contentBase: './dist',
    hot: true,
    open: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: '开发环境', // 模板要使用 <title><%= htmlWebpackPlugin.options.title %></title> 配置才生效
      template: './src/index.html', // 模板路径
      filename: 'index.html', // 输出 HTML 文件名称
      inject: 'body', // 插入的 script 标签位于 body 底部,true 同理
      hash: true, // 加上 hash 值
      favicon: './src/favicon.ico'
    }),
    // 新版无需再指定删除目录,默认删除 output 目录
    new CleanWebpackPlugin(),
    // 热更新
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  module: {
    rules: [
      {
        test: /\.css/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

module.exports = config
// index.js
import './main.js'
import './style.css'

console.log('Hello Webpack!')

if(module.hot) {
    module.hot.accept('./main.js', () => {
        console.log('Accept update!')
    })
}
// 当前项目目录
webpack4_demo
  | - /assets
  | - /config
  | - /dist
    | - some outputh file
  | - /src
    | - favicon.ico
    | - index.html
    | - index.js
    | - main.js
    | - styles.css
  | - .gitignore
  | - package.json
  | - README.md
  | - webpack.config.js
  | - yarn.lock
上一篇 下一篇

猜你喜欢

热点阅读