配置开发环境(webpack)以支持开发React项目

(本文介绍手动配置前端开发环境——构建工具链的核心部分,通常这部分我们都是通过前端框架官方脚手架自动搭建的,例如React 的 create-react-app 和 Vue的 Vue CLI。如果你想了解这些脚手架为我们做了什么,本文对你有用。)

当我们手动为了一支现代的「Web 应用」 准备开发环境,这个过程对于新手来说,不是特别的简单,尤其是当前端使用了像React框架的情况,因为社区生态(ecosystem)存在错综复杂的关系。这需要一些 分析,学习,和记录具体过程。

着手准备开发环境的思想准备是说,「全栈web 应用」 开发环境的「标准」到底是怎样的——怎样的生产效果是我们想要的,需要的,高效舒适的开发环境。还有,在技术上是一个怎样的处理过程。

什么是开发环境

我们可随意在本地文件系统中创建一个数据目录,并用npm初化一个 node 包($npm -init),并将VS CODE项目运行在这个目录里($code .),已经算是准备开发环境了。当然,很明显这种环境过于原始。

首先,我们必须明白,即使安装和配置高级的代码编辑器,也提供一定的开发舒适性,像智能提示,自动完成,甚至是提供了调试器,但是我们web 项目是 编辑器无关的,它不是这里所讲的主要的 开发环境配置

另外,我们可以通用npm 为项目添加「项目源码依赖」($npm install),然而安装依赖包也不是在配置开发环境,因为源码依赖是项目功能的客观需要,不是在为开发操作提供舒适度。

通过以上二的对比,我们比较清楚认识到,在技术上,所谓配置开发环境,主要是指 通过npm 为项目添加「项目开发依赖」($npm install --save-dev)。至于,安装什么样的开发工具包,又怎样配置使用它们,社区则存在太多的观点和选项了。

「Reac 应用」 开发环境的「标准」

效果上,开发舒适[注]是配置开发环境的标准。技术上,由于前端和后端的环境非常不同,配置的任务也非常不同,例如后端node不受浏览器影响,可以直接使用现代JS功能。

后端由于是“一”,不受用户浏览器的“多”影响,天然具备开发舒适性,所以后端项目鲜有安装大量「项目开发依赖」,最常的一项是nodemon包,提供热加载,和调试功能;前端,则主要配置webpack 这个包

注:舒适性受主观影响,受开发者智力,水平,价值风格影响

前端最基础的开发环境满足以下几点:

  1. 支持最新的ESM模块,ES6语法
  2. 支持web 模块,例如css ,png,font等 可以import
  3. Router, sass 等不是必须的
  4. ui库也是不必须的
  5. 支持 JSX, SFC等基本 方便创建 V组件
  6. 语法帮助 也是不是必须的
  7. 支持热加载的开发服务器

webpack 配置过程

以下记录了为React项目配置舒适开发环境的过程。全程假定你已经使用npm init创建一个项目目录,并且只列出相应的内容,自行创建源文件。

另外 webpack基础认识,请看我之前的这篇文章(《webpack与项目构建再认识》)。

1 安装 项目开发依赖(webpack webpack-cli)

webpack 和一般开发依赖相似,以D安装:

$ npm install webpack webpack-cli --save-dev

2 了解默认配置,以及打包概念

21 开发目录(src)和发布目录(dist)

开发目录:我们的前端开发,全程在一个“虚拟的”环境(./src) 上开发,可以使用ESM,ES6+ JSX等;

发布目录:它是构建(build)的目标所在,是可被拷走,用作部署到生产服务器的数据

22 配置文件 webpack.config.js

const path = require('path');
module.exports = {
  entry: './src/index.js', // 依赖入口
  output: {
    filename: 'main.js', // 最终打包好的文件
    path: path.resolve(\_\_dirname, 'dist'), // 发布目录
  },
};

23 APP页面 dist/index.html

这个是直接在输出目录安排的(手动静态),会有需要动态生成的前端页面,看下面

Getting Started

24 使用npm script执行 打包任务

package.json

{
  "scripts": {
  "build": "webpack"
},
"devDependencies": {
  "webpack": "^5.4.0",
  "webpack-cli": "^4.2.0"
}
}

3 APP页面的“打包” 插件:html-webpack-plugin

上面的 23 中 index.html 是手动的,在大一点项目中,index.html 也会作为“源码模块”,需要动态打包

31 安装 html-webpack-plugin

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

32 在源码目录创建 index “模块”,并配置webpack

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "src", "index.html")
    })
 ]
};

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
  <meta charset="utf-8">
  <title>React with Webpack</title>
  </head>
<body>
  <div id="root"></div>
</body>
</html>

4 安装 webpack专用开发服务器

webpack-dev-server 是基于内存启动的,运行在内存中模拟出发布目录,这样可高效支持热加载。

41 webpack-dev-server

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

42 使用npm script 启动服务器

更新package.json,增加一个dev script块:

"dev": "webpack serve --mode development"

Error: ENOSPC: System limit for number of file watchers reached, watch'所在文件路径' 解决方案

在终端按顺序执行下面两个命令即可解决问题

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p sudo sysctl --system

5 配置 清理任务

出口目录(output)不会被webpack跟踪的,所以随项目进展,可能会有“垃圾”,所以最好在每一次构建前先清理目录

51安装清理插件

npm install clean-webpack-plugin --save-dev

52 配置

webpack.config.js:

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

...

plugins: [
...
new CleanWebpackPlugin()
],

6 配置 支持 ES6+

开始使用 loader,对「web 模块进行预处理」,这里第一个,当然是生出 ESM 模块——将ES6+的模块转译为ES5模块。

loader 与 plugins认识

这里补充一下loader和 plugins的知识,因为babel作一个 webpack loader 自己也因为功能丰富引入plugins,这个容易和webpack本身的插件机制混淆。

webpack 本身只支持ES5 JS的import 和bundling,webapp其它类型web模块 是通过 loader 实现的。例如 bable loader 支持 ES6 module import ;css loader 支持  css module import 。

61 安装 babel 核心以及转译插件

$ npm i @babel/core babel-loader @babel/preset-env --save-dev

62 配置babel支持ES6+ 转译

configure babel by creating a new file, babel.config.json

{
  "presets": [
  "@babel/preset-env"
  ]
}

63 配置 webpack 使用 babel loader

configure webpack to use the loader

module.exports = {
  module: {
    rules: [
    {
      test: /\\.js$/, //
      exclude: /node_modules/,
      use: ["babel-loader"]
    }
    ]
 },
 plugins: []
};

7 配置支持 JSX

支持 JSX 和支持ES6 的性质很类似(另一个babel 插件),JSX可以看和ES6一样,是一种高级语法,需要转换。只是转换 React 特定的ES5模块。

71 安装 babel 插件

babel 核心前面已经安装了,这里只需安装 react 插件

$ npm i @babel/preset-react --save-dev

72 配置babel支持JSX 转译

configure babel to use the React preset in babel.config.json:

{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

8 配置支持 CSS

将 JS以外的web 资源进行模块化需要一点点解释。样式,和图片等“web 模块”,与JS模块处理是不同,所以使用独立的loader (JS模块预处理使用babel);另外,这些模块的预处理,和打包操作也是JS模块不同。例如:CSS模块使用 css-loader预处理为web模块,再用 style-loader进行“打包”。

css-loader parses the CSS into JavaScript and resolves any dependencies

style-loader outputs our CSS into a