從零搭建、開發和發佈一個 npm 包(react + webpack + typescript + less)

近幾日開發了一個習題渲染器(支持提交答案),內容好寫,從0️建環境開發發佈頗爲不易😭,因此事後整理了一篇文章作個筆記。css

本文記錄了項目的搭建、開發和發佈過程,項目源碼地址:githubhtml

目前有不少可優化的地方,好比添加 eslint、測試、npm publish hooks 等等,時間有限先發文章,後期(認真臉)會逐步完善 : )node

開發組件

因爲組件比較簡單,文章的順序是先假設已經寫好了簡單的組件,而後須要什麼就添加什麼,一步步完成各類拓展。並非一開始就搭建環境接入各類拓展,萬事俱備以後再寫組件。感受本文的敘述對各類拓展的使用有更深入的理解。react

構建開發環境

建立項目目錄並進入執行命令:webpack

$ yarn init
複製代碼

填完幾個選項後,會生成一個 package.json ,包含項目的基本信息。git

安裝 React

$ yarn add react react-dom
複製代碼

開發組件代碼

├─src  // 用來存放組件源碼
|  ├─index.ts   // 入口文件,用來暴露組件
|  ├─utils.ts
|  ├─types   // TS 聲明文件
|  |   ├─externals.d.ts   // 因爲項目中使用了 less,需額外聲明才能使用模塊化導入 less 文件
|  |   └index.ts
|  ├─Renderer
|  |    ├─index.less
|  |    └index.tsx
複製代碼

關於組件的源碼,因爲很簡單而且不是本文的重點,因此就不展開了。可參閱項目組件部分源碼github

安裝配置 TS

因爲使用了 TS,寫好組件以後下一步要對 TS 文件進行編譯。web

# 因爲 TS 只會在開發環境使用,因此安裝在 devDependencies
$ yarn add -D typescript
複製代碼

須要在項目的根 / 目錄新建一個 tsconfig.json 的配置文件,這樣不用每次編譯時輸入重複複雜的命令。typescript

// tsconfig.json
{
  "compilerOptions": {
    "outDir": "./dist",	// 輸出的目錄
    "module": "CommonJS",	// 指定生成哪一個模塊系統代碼: "None", "CommonJS", "AMD", "System", "UMD", "ES6"或 "ES2015"
    "target": "ES2015",	// 指定 ES 目標版本,默認 ES3
    "jsx": "react",	// 在 .tsx 文件裏支持 jsx
    "declaration": true,	// 生成相應的 .d.ts 文件
    "removeComments": true,	// 刪除全部註釋,除了以 /!* 開頭的版權信息。
  },
  "include": [
    "src/**/*",	 // 須要編譯的文件
  ],
  "exclude": [
    "node_modules",
  ],
  "files": []
}
複製代碼

使用 include 引入的文件可使用 exclude 屬性過濾。 然而,經過 files 屬性明確指定的文件卻老是會被包含在內,無論 exclude 如何設置。 若是沒有特殊指定, exclude 默認狀況下會排除 node_modulesbower_componentsjspm_packages<outDir> 目錄。shell

關於 tsconfig.json 更多的配置說明,可查看:www.tslang.cn/docs/handbo…

開發示例 example

爲了方便開發, 咱們能夠在當前項目中建立一個 example(demo),開發時無需先將組件編譯打包而後引用編譯後的代碼,而是能夠直接引用該渲染器的源碼,這樣每次修改後保存即可以自動更新到頁面,極大地提升了開發效率。當開發調試完成後,即可以使用生產模式,這樣打包編譯壓縮後的渲染器代碼即可直接使用。

使用方式:在 example 中直接 import /src/.. 中的組件便可。

本習題渲染器 example 相關的代碼目錄結構以下所示:

├─example	// 示例代碼
|    ├─src
|    |  ├─index.html // 用於掛載組件到頁面
|    |  ├─index.tsx
|    |  └mock.ts	// mock 數據
複製代碼

代碼可參閱:example

引入 webpack

使用 webpack 完成對項目的打包編譯並打開 example。

# webpack-dev-server 用來開啓本地服務器打開 example
$ yarn add -D webpack webpack-cli webpack-dev-server
 # 各類 loader
$ yarn add -D ts-loader less-loader style-loader css-loader
複製代碼

在項目根目錄 / 中建立配置文件 webpack.config.js 用於打包編譯。

// webpack.config.js
// TODO 只是開發環境的設置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const base = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  devtool: 'cheap-module-source-map',
  resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: ['.ts', '.tsx', '.js', '.json'],
  },
  module: {
    rules: [
      // ts-loader 用於加載解析 ts 文件
      {
        test: /\.(ts|tsx)?$/,
        loader: 'ts-loader',
        exclude: /node_modules/
      },
      // 用於加載解析 less 文件
      {
        test: /\.less$/,
        use: [
          { loader: 'style-loader', },
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[hash:base64:6]',
              },
            }
          },
          { loader: 'less-loader', },
        ]
      },

    ],
  },
  optimization: {
    minimize: true,	// 開啓代碼壓縮
  },
};

if (process.env.NODE_ENV === 'development') {
  tempConfig = {
    ...base,
    entry: path.join(__dirname, 'example/src/index.tsx'),
    output: {
      path: path.join(__dirname, 'example/dist'),
      filename: 'bundle.js',
      library: 'laputarenderer',
      libraryTarget: 'umd',
    },
    plugins: [
      // 自動注入編譯打包好的代碼至 html
      new HtmlWebpackPlugin({
        template: path.join(__dirname, './example/src/index.html'),
        filename: 'index.html',
      }),
    ],
    devServer: {
      // port: 8008, // example 的啓動端口,選填
    },
  };
}

module.exports = tempConfig;

複製代碼

添加開發模式的 script 命令

添加命令以前,咱們須要指定 node 執行環境,方便告知 webpack 如今是生產仍是開發環境,決定應該如何打包。安裝以下依賴,用來設置執行環境:

$ yarn add -D cross-env
複製代碼

如今咱們須要添加一些 npm 執行命令,用來編譯運行項目的 example。

// package.json
{
  // ...
  "scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --open"
  },
  // ...
}
複製代碼

ok,到如今爲止,咱們已經搭建好了這個習題渲染器的開發環境,接下來執行 yarn start ,編譯完成以後瀏覽器會自動打開 example ,🉑️以開始隨心所欲了~

開發調試完成以後,咱們還須要在最終發佈 npm 包以前使用生產模式編譯壓縮該渲染器。

搭建生產環境

修改 webpack 配置

首先安裝一個刪除文件依賴,方便每次打包時提早自動刪除上一次編譯打包後的文件。

# 用於打包編譯以前清空 /dist
$ yarn add -D clean-webpack-plugin
複製代碼

修改 webpack.config.js

// webpack.config.js

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

// ...
if (process.env.NODE_ENV === 'development') {
  // ...
} else {
  tempConfig = {
    ...base,
    entry: './src/index.ts',
    output: {
      filename: 'index.js',
      path: path.resolve(__dirname, 'dist'),
      library: 'laputarenderer',
      library: 'umd'
    },
    devtool: 'none',
    // When importing a module whose path matches one of the following, just
    // assume a corresponding global variable exists and use that instead.
    // This is important because it allows us to avoid bundling all of our
    // dependencies, which allows browsers to cache those libraries between builds.
    // 咱們想要避免把全部的React都放到一個文件裏,由於會增長編譯時間而且瀏覽器還可以緩存沒有發生改變的庫文件。
    // 理想狀況下,咱們只須要在瀏覽器裏引入React模塊,可是大部分瀏覽器尚未支持模塊。
    // 所以大部分代碼庫會把本身包裹在一個單獨的全局變量內,好比:jQuery或_。 這叫作「命名空間」模式,
    // webpack 也容許咱們繼續使用經過這種方式寫的代碼庫。
    // 經過咱們的設置"react": "React",webpack會神奇地將全部對"react"的導入轉換成從React全局變量中加載
    
    // 詳情🔎請參閱本文末尾的參考文檔:《React與webpack》
    externals: {
      'react': 'react',
      'react-dom': 'react-dom'
    },
    plugins: [
      new CleanWebpackPlugin(),	// 編譯以前清空 /dist
    ],
  };
}

module.exports = tempConfig;

複製代碼

添加生產模式的 script 命令執行編譯打包📦

修改 package.json

// package.json
{
  // ...
  "scripts": {
    "build": "cross-env NODE_ENV=production npx webpack"
  },
  // ...
  "peerDependencies": {
    "react": ">=16.9.0",
    "react-dom": ">=16.9.0"
  }
}
複製代碼

⚠️注意package.json 添加了 peerDependencies 字段,用來告訴其它想安裝該庫的項目:若是想使用我這個插件,就必須同級安裝我指定的這些依賴。

在本項目中,指定了 react 和 react-dom,若是另一個項目 A 想要安裝此渲染器,就必須同時安裝 react 和 react-dom。

npm1 和 npm2 版本能夠安裝渲染器的同時自動安裝 peerDependencies 中的依賴,可是以後的版本須要手動安裝,不然將會收到警告。

更多內容請閱讀:Peer Dependencies

自此,生產模式的全部準備工做都已經完成,執行 yarn build 以後會發現項目的根目錄新增了 dist 文件夾,即最後要發佈到 npm 上的文件。

接下來就要開始準備發佈到 npm~

npm 發佈準備

修改 package.json 配置

// package.json
{
  "main": 'dist/index.js',	// 該包的入口文件
  "types": "dist/index.d.ts",		// 指明聲明文件的入口
  // ...
  "files": ["dist"],	// npm 發佈白名單
  // ...
}
複製代碼

files 字段規定了:只有 dist 文件夾會出如今發佈的包裏(README.mdpackage.json 會被默認添加)。

npm 發佈

發佈以前必須在 npmjs.com 有帳號,若是沒有,先註冊:www.npmjs.com/signup。若是已經有了帳號,須要在本地終端執行 npm login 登錄。輸入用戶名密碼郵箱後便可登陸成功,npmjs.com 的帳戶裏會自動保存當前 pc 的惟一 token。

⚠️ 因爲時間的關係,目前沒有作好自動化。能夠在 package.json 中添加一些 script 命令即 npm publish hook,用於在發佈以前進行好比 preversionversionpostversion

發佈

發佈以前先要確保已經在生產模式下打包編譯了該項目(yarn build),作了自動化的當我沒說 : P

$ npm publish
複製代碼

等待發布成功以後即可以在 npmjs.com 的我的主頁查看新發布的包啦 🎉🎉🎉

更新版本

修改項目以後更新版本:

# 升級補丁版本號 1.0.0 -> 1.0.1
$ npm version patch
 # 升級次版本號 1.0.0 -> 1.1.0
$ npm version minor
 # 升級主版本號 1.0.0 -> 2.0.0
$ npm version major
複製代碼

參考文檔

tsconfig.json

React與webpack

相關文章
相關標籤/搜索