在我们日常开发node
项目的时候,通常是直接使用node
命令来运行我们的项目,例如我就会使用nodemon
来创建开发环境来运行我的项目,在开发完成之后我会使用一些构建工具来构建我的项目,例如webpack
,rollup
等等;
当然这并不重要,关键在于打包完成了之后可能会出现一些问题,就是开发环境和生产环境的产物不一样,导致项目无法正常运行,例如我们使用fs
模块来读取文件,但是在打包之后我们的文件路径就会发生变化,这就导致了我们的项目无法正常运行;
同时我们也可能会使用一些新的语法,例如es6
,es7
等等,如果我们的生产环境下的node
版本不支持这些语法,那么我们的项目也会无法正常运行,这个时候我们就需要使用babel
来进行转码;
所以为了解决这些问题,我们需要一个开发环境和生产环境一致的环境,例如我现在使用的是webpack
来构建我的项目,那么我同样使用webpack
来构建一个开发环境,这样就可以保证我们的开发环境和生产环境一致;
接下来我们就来看看如何使用webpack
来构建一个node
的开发环境,如果你还不了解webpack
,可以先看看webpack的一些基本概念;
1. 初始化项目
首先我们需要初始化一个node
项目,这里我使用npm
来初始化一个项目,当然你也可以使用yarn
来初始化一个项目;
bash
npm init -y
然后我们安装一些依赖,例如我使用的是express
来创建一个简单的node
服务,当然你也可以使用其他的一些框架,例如koa
,hapi
等等;
bash
npm install express
写一个简单的node
服务,例如我写一个简单的express
服务;
javascript
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
})
app.listen(3000, () => {
console.log('http://localhost:3000');
});
然后我们使用node
命令来运行我们的项目,例如我使用nodemon
来运行我的项目;
bash
nodemon index.js
注意:
nodemon
是需要单独安装的,你可以使用npm install nodemon -g
来全局安装,也可以使用npm install nodemon --save-dev
来安装到项目中;
不出意外的情况下,我们的项目应该可以正常运行,现在在浏览器中输入http://localhost:3000
,我们应该可以看到Hello World!
;
现在可以随便修改一些东西,例如我修改一下Hello World!!!
,然后我们可以看到我们的项目会自动刷新,接着刷新页面,我们就可以看到我们的修改已经生效了;
目录结构如下:
2. 使用 webpack 构建开发环境
使用webpack
来构建我们的开发环境,首先我们需要安装一些依赖:webpack
,webpack-cli
这两个
bash
npm install webpack webpack-cli --save-dev
然后我们需要创建一个webpack
的配置文件,例如我创建一个webpack.config.js
文件;
javascript
const path = require("node:path");
module.exports = {
target: "node", // 指定构建的目标是node
mode: "development", // 指定构建的模式是开发模式
entry: {
index: "./index.js", // 指定入口文件
},
output: {
path: path.resolve(__dirname, "./dist"), // 指定输出的目录
filename: '[name].js', // 指定输出的文件名
clean: true, // 每次构建之前清空输出目录
},
devtool: "source-map", // 生成source-map
}
这样我们就完成了一个简单的webpack
配置,然后我们可以使用webpack
命令来构建我们的项目;
bash
npx webpack
不出意外我们可以看到根目录下多了一个dist
目录,然后我们可以看到里面有一个index.js
文件,这个文件就是我们构建之后的文件;
现在我们可以使用node
命令来运行我们的项目,例如我直接使用node
来运行我们的项目;
bash
node dist/index.js
这其实就是生产环境下的产物文件,我们可以看到生成的文件体积非常大,这是因为webpack
默认会将所有的node_modules
都打包进去,这样就导致了我们的文件体积非常大;
但是在开发环境下我们并不需要这些东西,因为这样会影响我们的开发效率,所以我们需要使用webpack-node-externals
来排除这些东西;
2.1 使用 webpack-node-externals
首先我们需要安装webpack-node-externals
这个依赖;
bash
npm install webpack-node-externals --save-dev
然后我们需要修改一下我们的webpack
配置文件;
javascript
const path = require("node:path");
const nodeExternals = require("webpack-node-externals");
module.exports = {
// ...其他配置
externals: nodeExternals(),
}
非常简单就可以完成了,然后我们再次使用webpack
命令来构建我们的项目;
bash
npx webpack
-
没有排除
node_modules
之前的文件体积 -
排除
node_modules
之后的文件体积
可以看看对比之后的文件体积,我们可以看到排除node_modules
之后的文件体积小了很多,这样就可以提高我们的开发效率;
2.2 结合 nodemon 来构建开发环境
直到现在我们都需要单独构建项目,然后重新运行我们的项目,这样会非常麻烦,所以我们需要结合nodemon
来构建我们的开发环境;
首先我们需要解决webpack
每次发生文件修改之后都需要重新构建的问题,这个时候我们可以使用webpack --watch
来监听文件的变化,然后自动构建我们的项目;
bash
npx webpack --watch
当然我们完全可以将这个指令写到package.json
的scripts
中,例如我写一个dev:build
指令;
json
{
"scripts": {
"dev:build": "webpack --watch"
}
}
这里指令就是单独用来构建开发环境的,然后我们可以使用nodemon
来运行我们的项目,我再写一个dev:start
指令;
json
{
"scripts": {
"dev:build": "webpack --watch",
"dev:start": "nodemon dist/index.js"
}
}
接下来我们将这两个指令组合起来,我们再写一个dev
指令;
json
{
"scripts": {
"dev:build": "webpack --watch",
"dev:start": "nodemon ./dist/index.js --watch ./dist/index.js",
"dev": "npm run dev:build | npm run dev:start"
}
}
这里使用|
来连接两个指令,因为这两个指令本身就是互不干扰的,我们使用|
来让这两个指令并行运行即可;
这样当webpack
检测到文件发生变化之后就会自动构建我们的项目,然后构建完成之后,nodemon
也会检测到文件发生变化,然后自动重启我们的项目,这样就可以保证我们的开发环境和生产环境一致了;
这里有一个细节,就是nodemon
只需要监听dist
目录下的文件即可,在我这个案例中,我们只需要指定./dist/index.js
即可;
现在我们可以体验一下开发环境了,我们先执行npm run dev
指令,然后我们可以修改一下我们的index.js
文件,来看看效果吧;
2.3 自定义自己的开发环境
既然我们都用上webpack
,那么webpack
的特性我们都可以使用的,例如我们可以上代码检查、babel
转码、别名等等;
当然这些现在网上都有很多的教程,这里我就不再赘述了,我们就简简单单的配置一个别名来体验一下;
我们可以直接在webpack
配置文件中配置别名,例如我配置一个@
别名;
javascript
const path = require("node:path");
module.exports = {
// ...其他配置
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
}
然后我们可以在index.js
中使用这个别名,例如我引入一个utils
文件;
javascript
import utils from "@/utils";
console.log(utils);
当然我们需要新建这个目录和文件,例如我新建一个src/utils.js
文件;
javascript
export default "Hello World!";
这里我们使用了es6
的import/export
语法,在node
环境如果要使用这个特性,需要在package.json
中配置type
字段为module
;
但是我们使用了webpack
来构建我们的项目,所以我们并不需要这个配置,webpack
会自动帮我们处理这个问题;
注:
webpack
默认会将es6
的import/export
语法转换为commonjs
的require/module.exports
语法;
2.4 调试 node 代码
在开发环境下我们经常需要调试我们的代码,例如我们需要打断点,查看变量的值等等;
而我们将原始代码构建之后,就会变得非常难以调试,这个时候我们可以使用source-map
来解决这个问题;
webpack
默认会生成source-map
,我们只需要在webpack
配置文件中配置devtool
字段即可;
javascript
module.exports = {
// ...其他配置
devtool: "source-map",
}
这个在上面的配置文件中已经配置了,这里强调一下;
然后我们还需要在node
环境中配置--inspect
参数,我们修改一下dev:start
指令;
json
{
"scripts": {
"dev:build": "webpack --watch",
"dev:start": "cross-env NODE_OPTIONS=--enable-source-maps nodemon --inspect ./dist/index.js --watch ./dist/index.js",
"dev": "npm run dev:build | npm run dev:start"
}
}
这里出现了两个新的东西,一个是cross-env
,一个是--inspect
参数,我们一个一个来看;
cross-env
:这个是一个跨平台的环境变量设置工具,我们可以使用这个工具来设置环境变量,这样就可以在windows
和linux
上都能正常运行;--inspect
:这个是node
的一个调试参数,我们可以使用这个参数来开启node
的调试模式,这样我们就可以在chrome
浏览器中调试我们的node
代码了;
cross-env
需要单独安装,你可以使用npm install cross-env --save-dev
来安装;
因为我们需要使用source-map
,而在node
中是需要开启--enable-source-maps
参数的,这样我们才能正常的使用source-map
,因为环境的问题,我们需要使用cross-env
来设置环境变量;
而如果需要代码能调试就需要使用--inspect
参数,这样我们就可以调试我们的node
代码了;
使用 chrome 调试 node 代码
首先我们需要在chrome
浏览器中输入chrome://inspect
,然后我们可以看到一个Devices
的面板,如下:
因为我们没有携带任何参数,所以是默认的localhost:9229
,然后我们点击inspect
,然后我们就可以看到一个chrome
的调试面板了;
我们可以直接点击Open dedicated DevTools for Node
,然后我们就可以看到一个chrome
的调试面板了,由于连接需要时间,所以可能会有一些延迟,稍微等待一下即可;
接下来的环节就是我们非常熟悉的调试环节了,我们可以打断点,查看变量的值等等;
使用 webstorm 调试 node 代码
如果你使用的是webstorm
,那么你可以直接使用webstorm
来调试我们的node
代码,这样就会非常方便了;
新版的webstorm
直接点击右上角的当前文件
,然后选择编辑配置,就会弹出一个配置面板,如下:
然后我们可以点击+
,然后选择附加到 Node.js/Chrome
,然后我们就可以看到一个配置面板了,如下:
这些都是默认配置,你可以修改一下名称方便自己区分,其他的不需要修改,接下来也是一样的正常调试即可;
个人习惯使用
webstorm
,所以不太了解vscode
是怎么调试node
代码的,但是应该是差不多的,可以自行搜索一下;
3. 合并开发环境和生产环境
现在我们已经完成了一个开发环境,但是其实生产环境和开发环境大差不差的,由于node
代码是纯粹的js
代码,我们并不需要像web
一样有非常多的配置;
我们可以非常轻易的将开发环境和生产环境合并在一起,我这里直接使用webpack
的mode
字段来进行区分;
javascript
const path = require("node:path");
const nodeExternals = require("webpack-node-externals");
// 使用一个变量来判断当前环境是否是开发环境
const hasDev = process.env.NODE_ENV === "development";
module.exports = {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
target: "node",
mode: process.env.NODE_ENV || "development", // webpack的 mode 字段,该字段会确认构建的模式,已确认是否开启一些优化
entry: {
index: "./index.js"
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: '[name].js',
clean: true
},
stats: hasDev ? "errors-only" : "normal", // 控制台输出的信息,在开发环境下只输出错误信息
devtool: hasDev ? "inline-source-map" : "source-map", // 开发环境下使用 inline-source-map,生产环境下使用 source-map
externals: hasDev ? [nodeExternals()] : [], // 开发环境下排除 node_modules,生产环境下不排除
}
这里我们使用了process.env.NODE_ENV
来区分开发环境和生产环境,这样我们就可以使用webpack
的mode
字段来进行区分了;
我们还需要修改一下package.json
中的dev
指令,例如我这样修改;
json
{
"scripts": {
"dev:build": "cross-env NODE_ENV=development webpack --mode development --watch",
"dev:start": "cross-env NODE_OPTIONS=--enable-source-maps nodemon --inspect ./dist/index.js --watch ./dist/index.js",
"dev": "npm run dev:build | npm run dev:start",
"build": "cross-env NODE_ENV=production webpack --mode production"
}
}
这样我们就可以使用npm run build
来构建我们的生产环境了,然后使用npm run dev
来开发我们的开发环境了;
4. 总结
本章使用webpack
来开发node
项目,本质上webpack
就是一个打包工具,而且webpack
也是支持构建node
项目的,只是并没有提供开发环境的配置,所以我们需要自己来配置一下;
而实现node
开发环境的配置也并没有使用很多高级技巧,都是一些基本的配置,例如source-map
,mode
,externals
等等;
只要我们明白开发环境和生产环境的区别,我们就可以很轻易的配置我们的开发环境了;