點燈坊

失くすものさえない今が強くなるチャンスよ

開發前後端都能使用 Package

Sam Xiao's Avatar 2021-10-24

ECMAScript 最迷人的地方除了 First Class Function 外,可以前後端共用相同語言與 Package 也是一大特色,本文將使用 ECMAScript 2015+ 建立 ES Module,並透過 Babel 轉譯成 CommonJS Module,讓前後端都能使用相同 。

Version

Node 14.18.1

Add Package

$ mkdir my-package
$ cd my-package

建立 my-package 目錄。

$ yarn init --yes

使用 Yarn 建立 package.json

$ yarn add @babel/core @babel/cli @babel/preset-env --dev

安裝 Babel 相關 package,負責將 ES module 轉譯成 CommonJS module。

僅管 ES module 才是標準,但目前後端 Node 支援 ES module 程度仍然有限,但前端 Webpack 已經支援 CommonJS module,因此只能在開發階段使用 ES module,再透過 Babel 轉譯成現在前後端都能接受的 CommonJS module

日後等 Node 支援 ES Module 成熟,就可不必再使用 Babel,完全使用 ES Module 即可

$ yarn add ramda

安裝 package 本身所相依的 package。

本例會使用到 Ramda

own000

.babelrc

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

在 project 根目錄下建立 .babelrc,使用剛剛安裝的 @babel/preset-env 的設定將 ES6+ 轉譯。

own001

My First Package

src/index.js

export { isOdd } from './isOdd'

建立 src 目錄,package 所有的 source code 都在此目錄下。

index.js 主要用於統整所有 module。

isOdd.js 引入的 isOdd 馬上 export 出去。

src/isOdd.js

import { compose, equals, flip, modulo } from "ramda"

export let isOdd = compose (equals (1), flip (modulo) (2))

引用了 Ramda 的 function 組合出 isOdd()

package.json

{
  "name": "wink-test",
  "version": "1.0.0",
  "main": "dist/index.js",
  "files": [
    "dist"
  ],
  "license": "MIT",
  "scripts": {
    "build": "babel src --out-dir dist"
  },
  "dependencies": {
    "ramda": "^0.26.1"
  },
  "devDependencies": {
    "@babel/cli": "^7.7.0",
    "@babel/core": "^7.7.2",
    "@babel/preset-env": "^7.7.1"
  }
}

第 9 行

"scripts": {
  "build": "babel src --out-dir dist"
},

yarn build 將使用 Babel 編譯 src 目錄下所有 .jsdist 目錄。

第 4 行

"main": "dist/index.js",

Package 的起始檔案改成解譯後 dist 目錄下的 index.js

第 5 行

"files": [
  "dist"
],

dist 目錄下所有檔案將安裝到 user 的 node_modules 目錄下。

own002

$ yarn build

將 ES Module 轉譯成 CommonJS Module。

own003

dist/index.js

own004

dist/index.js 為轉譯後的 CommonJS module。

$ yarn publish

使用 yarn publish 發布 package 到 NPM。

own005

Quokka

own006

Quokka 可正常使用剛發佈 wink-testisOdd()

Node

own007

Node 也可正常使用剛發佈 wink-testisOdd()

Vue

App.vue

<template>
  <div>
    <ul>
      <li v-for="(item, index) in result" :key="index">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
import { filter } from 'ramda';
import { isOdd } from 'wink-test';

let mounted = function() {
  let data = [1, 2, 3];

  this.result = filter(isOdd)(data);
}

export default {
  name: 'app',
  data: () => ({
    result: []
  }),
  mounted
}
</script>

12 行

import { filter } from 'ramda';
import { isOdd } from 'wink-test';

let mounted = function() {
  let data = [1, 2, 3];

  this.result = filter(isOdd)(data);
}

Vue 也可正常使用剛發佈 wink-testisOdd()

own008

Conclusion

  • 若要使用 ECMAScript 開發同時 Vue 與 Node 都能使用的 package,且使用 ES Module 語法,現階段只能透過 Babel 轉譯成 CommonJS module,等日後 Node 支援 ES module 更完整,則可不使用 Babel

Reference

Mostafa Fouad, A Simple Guide to Publishing an NPM Package