[toc]
這篇文章主要記錄了從零發佈一個vue的npm包(包含一個簡單的指令和一個vue組件)的實踐過程及些許心得。
初始化項目
這裏我們通過@vue/cli拉取簡單配置的模板來初始化一個2.X的項目,不瞭解的同學可以看下vueCli3官方文檔
vue init webpack-simple vue-directive-kit
初始化的項目目錄如下
├── README.md
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ └── main.js
└── webpack.config.js
接下來做一些改動。src
目錄改爲examples
用於本地開發及案例展示,新增一個packages
目錄用於存放組件源碼及導出文件。
├── README.md
├── examples
│ ├── App.vue
│ └── main.js
├── index.html
├── package.json
├── packages
│ ├── componentName // 單個組件
│ │ ├── index.js
│ │ └── src
│ │ └── componentName.vue
│ └── index.js // 導出文件
└── webpack.config.js
由於文件名稱做了改動,webpack的打包配置也要做對應修改。默認webpack.config.js
會導出一個對象,這裏我們改爲導出一個函數,然後由函數把配置對象導出,這樣可以接受一個通過package.json
傳過來的參數env
,同時修改entry
及output
:
module.exports = env => {
return {
entry: env.lib ? "./packages/index.js" : "./examples/main.js",
output: {
// 打包文件的生成路徑
path: path.resolve(__dirname, env.lib ? "./lib" : "./dist"),
publicPath: env.lib ? "/lib/" : "/dist/",
// 打包後生成的文件名
filename: env.lib ? "vue-directive-kit.js" : "build.js",
/**
* library指定的就是你使用require時引入的模塊名
* 這裏便是require(“vue-directive-kit”)
*/
library: env.lib ? "vue-directive-kit" : "",
/**
* libraryTarget可以指定打包文件中代碼的模塊化方式,默認爲var,常見有如下幾種:
* commonjs/commonjs2: 將你的library暴露爲CommonJS模塊
* amd: 將你的library暴露爲amd模塊
* umd: 將你的library暴露爲所有的模塊定義下都可運行的方式
* 其中AMD和UMD需要指定library,如果不聲明組件庫則不能正常運行,
* 這是爲了在瀏覽器上通過script標籤加載時,用AMD模塊方式輸出的組件庫可以有明確的模塊名
*/
libraryTarget: env.lib ? "umd" : "var",
/**
* 當使用了 libraryTarget: "umd",
* 設置umNamedDefine爲true時,
* 會對 UMD 的構建過程中的 AMD 模塊進行命名。否則就使用匿名的 define。
*/
umdNamedDefine: env.lib ? true : false,
},
};
};
上面的配置可以知道,我們需要通過env來控制打包類型。env
需要在package.json
中傳入
"scripts": {
// 本地開發運行npm run dev
"dev": "cross-env NODE_ENV=development webpack-dev-server --env --open --hot",
"build": "cross-env NODE_ENV=production webpack --env --progress --hide-modules",
// 需要發佈的時候執行npm run lib打包
"lib": "cross-env NODE_ENV=production webpack --env.lib --progress --hide-modules"
},
完善內容
編寫組件
在packages/componentName/src/componentName.vue
文件中寫如下你內容。當然這個是爲了演示內容很簡單,其實這個文件就是真實的組件了。
<template>
<div>
<h1>我是一個組件</h1>
</div>
</template>
<script>
export default {
name: 'componentName',
data () {
return { }
}
}
</script>
在packages/componentName/src/index.js
中註冊並導出單個組件
// 引入組件
import componentName from './componentName/src'
componentName.install = Vue => Vue.component(componentName.name, componentName);
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(componentName);
}
export default componentName;
編寫指令
我們增加一個名稱爲testDirective
的指令。
創建'packages/testDirective/src/testDirective.js'文件:
export default {
bind: () => {
console.log(`directive bind`);
},
inserted: (el, binding) => {
console.log(`el:`, el);
},
}
創建packages/testDirective/index.js
文件:
// 引入組件
import testDirective from './src/testDirective'
const install = Vue => {
Vue.directive('testDirective', testDirective);
};
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use({install});
}
export default {
install
};
統一導出
編輯出口文件packages/index.js
,將packages
目錄下所有的指令及組件統一註冊導出:
// 導入顏色選擇器組件
import componentName from './componentName/src/componentName.vue'
import testDirective from './testDirective/src/testDirective'
// 存儲組件列表
const components = [
componentName,
]
// 存儲指令映射
export const directives = {
testDirective,
}
// 定義 install 方法,接收 Vue 作爲參數。如果使用 use 註冊插件,則所有的組件都將被註冊
const install = function (Vue) {
// 遍歷註冊全局組件
components.map(component => Vue.component(component.name, component))
// 遍歷註冊指令
Reflect.ownKeys(directives).map(name => Vue.directive(name, directives[name]))
}
// 判斷是否是直接引入文件
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export default {
// 導出的對象必須具有 install,才能被 Vue.use() 方法安裝
install,
// 以下是具體的組件列表
componentName,
...directives,
}
到這裏,我們的包裏就包含了一個名爲componentName
的組件和一個名爲testDirective
的指令,本地測試一下,先在examples/main.js
中引入
// 單獨引入指令文件
// import pkgName from '../packages/test-directive/index'
// 整體引入包
import pkgName from '../packages/index'
Vue.use(pkgName)
在examples/App.vue
中使用
<template>
<div id="app">
<h1 v-test-directive>Test Directive</h1>
<component-name></component-name>
</div>
</template>
然後運行yarn dev
本地查看效果
看起來本地測試已經沒有問題,可以打包發佈了。
不過在打包發佈之前,需要先做一些準備工作。
發佈前準備
generator-standard-readme
一個標準的npm包或者開源項目都會一個有完善且好看的README幫用戶快速瞭解你的項目。通過generator-standard-readme
可以快速生成一個README模板
npm install --global yo generator-standard-readme
yo standard-readme
完善package.json
文件
爲package.json
增加一些發佈npm包所需要的基本字段:
/**
* npm包名,要符合幾個規則:
* 1. name的長度必須小於等於214個字符。
* 2. name不能以"."(點)或者"_"(下劃線)開頭。
* 3. name中不能包含大寫字母。
* 4. name最終將被用作URL的一部分、命令行的參數和文件夾名。因此,name不能含有非URL安全的字符。
*/
"name": "vue-directive-kit",
"description": "A collection of vue directives.",
"version": "1.0.1",
"author": "slevin <[email protected]>",
"license": "MIT",
// 是否私有,默認爲true,改爲false
"private": false,
// 是一個字符串的數組。它可以幫助人們在使用npm search時找到這個包
"keywords": [
"vue",
"vue-directive-kit",
"vue-directive"
],
/**
* files字段是一個被項目包含的文件名數組
* 如果你在裏面放一個文件夾名,那麼這個文件夾中的所有文件都會被包含進項目中(除非是那些在其他規則中被忽略的文件)。
* 你還可以在包的根目錄或子目錄下提供一個".npmignore"文件來忽略項目包含文件,即使這些文件被包含在files字段中
* 某些文件總是被包含的,不論是否在規則中指定了它們:
* package.json
* README (and its variants)
* CHANGELOG (and its variants)
* LICENSE / LICENCE
*/
"files": [
"lib/vue-directive-kit.js",
"package.json",
"README.md"
],
/**
* main字段用來指定入口文件
*
*/
"main": "lib/vue-directive-kit.js",
/**
* 指明你的代碼被託管在何處,也就是遠程倉庫的地址
*/
"repository": {
"type": "git",
"url": "[email protected]:slevin57/vue-directive-kit.git"
}
本地包測試
隨便找一個項目,安裝這個包進行測試。這裏我們就在根目錄下再初始化一個vue基本項目用來測試包的使用,然後把npm包文件放到測試項目根路徑執行安裝,同時安裝項目依賴。
vue init webpack-simple test
mv xxx.tgz ./test
cd test
npm i xxx.tgz && npm i
在main.js
中像正常引入第三方包那樣操作就可以。測試完成後記得刪掉test目錄。
準備操作及測試都做完後就可以打包發佈了。
發佈到npm
先打包
npm run lib
登錄npm,輸入npm註冊的用戶名、密碼及郵箱
npm login
發佈
npm publish
不出意外的話,登錄npm就可以看到你發佈的包了。發佈的包在72小時內是可以刪除的,過了72小時就永遠無法刪除了,所以記得不要隨意發一些沒有意義的包。
如果需要卸載,在發佈後72小時內執行:
npm unpublish <pkg>[@<version>]