node.js系列-多种方案教你在node程序中同时使用CommonJS 和 ES Module 混合开发最佳实践

前情提要

我们平时使用的npm 第三方包一般基于这两种规范开发的,很容易遇到一个项目里既有 CommonJS 又有 ES Module 的情况,那么我们应该如何解决这种CommonJS 和 ES Module 混合开发的问题呢?

CommonJS是什么?

  • 2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志"Javascript模块化编程"正式诞生。在服务器端,需要有模块,与操作系统和其他应用程序互动。
  • CommonJS 是为了提供一个类似Python,Ruby和Java标准库,让开发者可以使用CommonJS API编写应用程序,可以运行在不同的JavaScript解释器和不同的主机环境中。
  • NodeJS,webpack 我们通常会以CommonJS的形式来书写。

CommonJS可以开发以下程序:

(1)、服务器端JavaScript应用程序
(2)、命令行工具
(3)、图形界面应用程序
(4)、混合应用程序(如,Titanium或Adobe AIR)

代码形式

通俗大白话,特点就是: 使用require关键词来引入依赖,举个例子:

const path = require('path')
const AutoLoad = require('@fastify/autoload')

以及 module.exports

module.exports = async function (fastify, opts) {
  fastify.get('/', async function (request, reply) {
    return 'this is an example'
  })
}

ES module是什么?

ES Modules(ESM)是用于处理模块的 ECMAScript 标准。 虽然 Node.js 长期使用 CommonJS 标准,但浏览器从未有过模块系统。 每个主要决策(如模块系统)必须首先由 ECMAScript 标准化,然后由浏览器实施。

ES modules(ESM) 是 JavaScript 官方的标准化模块系统。

ES 模块是如何运作的

当使用模块来开发的时候,会建立一个模块模块依赖图。不同依赖之间联系来自于你使用的任何 import 语句。

区别

  • CommonJS 按需加载,可以动态加载、条件加载、循环加载
  • ESM 静态加载,方便解析依赖,优化运行效率。 import() 目前也可以动态加载
  • CommonJS 模块输出值的拷贝,ESM输出值的引用。
  • CommonJS 运行时加载,ESM编译时输出接口。
  • CommonJS 模块的require()同步加载,ESM的import异步加载,有独立模块依赖解析阶段。
  • ESM的import会提升,变量const只读。

区别举例

在CommonJS中我们可以直接使用__dirname,就像下面这样:

fastify.register(AutoLoad, {
    dir: path.join(__dirname, 'plugins'),
    options: Object.assign({}, opts)
  })

但是在ESM中不再支持,我们需要改为

import {dirname} from 'path';
import { fileURLToPath } from 'url';

function getDirname(url) {
  const __filename = fileURLToPath(url);
  return dirname(__filename);
}

解决方案

方案1:按照 ES Module 规范,指定 “type”

单独指定某些文件使用 CommonJS 模块,或者ES Module 模块

  • 正常情况下,我们不做配置的话,项目默认是 CommonJS 规范
  • 在 package.json 文件中指定 “type”:“module” 后,就会按照 ES Module 规范
  • 强制的指定文件后缀为 .cjs后,此文件会遵守CommonJS 规范


ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and 'test/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
ReferenceError: 在 ES 模块范围中没有定义要求,您可以使用 import 代替
这个文件被视为 ES 模块,因为它有一个’。Js 的文件扩展名和‘ test/package.json’包含“ type”: “ module”。若要将其视为 CommonJS 脚本,请将其重命名为使用’。Cjs 的文件扩展名。

“type”:"module"情况下,如何解决这个报错呢?

我们可以单独给某个文件设置后缀名来使用CommonJS 脚本,强制的指定文件后缀为 .cjs

方案2:按照CommonJS 规范,不做任何配置情况下

指定需要使用ES Module 的文件的后缀名为 .mjs,那么这个文件会被强制指定使用 ES Module 规范

方案3:使用 Babel 完美配置 CommonJS 和 ES Module 随心使用(这个目前有更新的配置方案,等我忙完了写到方案4,方案3也可以用哦)

安装依赖

npm install --save-dev babel-cli babel-preset-env babel-register babel-preset-stage-0 
npm install --save babel-polyfill # babel转码时不能识别一些全局对象的API,例如Object.assign,使用它可以解决这个问题
  • babel-polyfill babel转码时不能识别一些全局对象的API
  • babel-preset-stage-0 es阶段性提案语法 stage-0 包含stage1,2,3
  • babel-register 钩子,在程序入口文件引入即可实现转码

在根目录新建 .babelrc

{
    "presets": [
      "env",
      "stage-0"
    ]
  }

配置命令入口,在根目录新建 main.js

require('babel-polyfill');
require('babel-register');
require('./app.js'); // 引入您的项目的启动文件

配置命令行

package.json中加入

"scripts": {
    "start": "node main.js",
  },

启动项目

npm start

大功告成,接下来项目内写任何类型都不会有问题

方案4:等我忙完了,继续补充,确实有更好的方式,可以使用更少的依赖

  • 今天就写到这里啦~
  • 小伙伴们,( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ我们明天再见啦~~
  • 大家要天天开心哦

欢迎大家指出文章需要改正之处~
学无止境,合作共赢

在这里插入图片描述

欢迎路过的小哥哥小姐姐们提出更好的意见哇~~

猜你喜欢

转载自blog.csdn.net/tangdou369098655/article/details/132074759