官網資料
構建生產版本——庫模式
https://cn.vitejs.dev/guide/build.html#library-mode
詳細設置
https://cn.vitejs.dev/config/#build-lib
技術棧
- vite
- rollup
打包方式
vue-cli 自帶的是 webpack 的打包方式,打出的包體積有點大,而 vite 自帶的是 rollup 的打包方式,這種方式打包的體積就非常小,官網也有一些使用說明,所以學會之后還是比較很方便的。
vite 的庫項目可以分為兩類:(我自己分的)
- 一個是純js的項目,不帶HTML;
- 一個是可以帶上HTML(模板)的項目,比如UI庫。
下面分別介紹一下編寫和打包方式,其實大同小異。
純js的庫項目
使用 vite 建立項目,這里舉一個簡單的例子:
// main.js
const toTypeString = (val) => {
return Object.prototype.toString.call(val)
}
const typeName = (val) => {
return Object.prototype.toString.call(val).replace(/^\[object (\S+)\]$/,'$1').toLowerCase()
}
const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (val, key) => hasOwnProperty.call(val, key)
const isFunction = (val) => toTypeString(val) === '[object Function]'
const isAsync = (val) => toTypeString(val) === '[object AsyncFunction]'
const isObject = (val) => val !== null && typeof val === 'object'
const isArray = Array.isArray
const isString = (val) => typeof val === 'string'
const isNumber = (val) => typeof val === 'number'
const isBigInt = (val) => typeof val === 'bigint'
const isBoolean = (val) => typeof val === 'boolean'
const isRegExp = (val) => toTypeString(val) === '[object RegExp]'
const isDate = (val) => val instanceof Date
const isMap = (val) => toTypeString(val) === '[object Map]'
const isSet = (val) => toTypeString(val) === '[object Set]'
const isPromise = (val) => toTypeString(val) === '[object Promise]'
const isSymbol = (val) => typeof val === 'symbol'
const isNullOrUndefined = (val) => {
if (val === null) return true
if (typeof val === 'undefined') return true
return false
}
function log(){
if (window.__showlog) console.log(...arguments)
}
const logTime = (msg, auto = true) => {
const start = () => {
if (window.__showlog) console.time(msg)
}
const end = () => {
if (window.__showlog) console.timeEnd(msg)
}
if (auto) start() // 自動開始計時
return { start, end }
}
export {
log, // 打印調試信息
logTime, // 計時
toTypeString, // Object.prototype.toString.call(val)
typeName, // 獲取可以識別的名稱
hasOwnProperty,
hasOwn,
isFunction, // 驗證普通函數
isAsync, // 驗證 async 的函數
isPromise, // 驗證 Promise
isObject, // 驗證 Object
isArray, // 驗證數組
isString, // 驗證字符串
isNumber, // 驗證 number
isBigInt, // 驗證 BigInt
isBoolean, // 驗證 布爾
isRegExp, // 驗證正則類型
isDate, // 驗證日期
isMap, // 驗證 map
isSet, // 驗證 set
isSymbol, // 驗證 Symbol
isNullOrUndefined // null 或者 undefined 返回 true
}
代碼比較簡單,僅僅只是演示。
想要打包的話,只能有一個出口文件,所以內部的代碼結構要設置好。
帶HTML的庫項目
純js的好辦了,export 輸出就好,那么帶模板的怎么辦呢?其實也是一樣的。
用 vite 建立一個項目,建立一個測試文件:
// t-text.vue
- 模板部分:
<!--單行文本-->
<template>
<el-input
v-model="value"
:id="'c' + columnId"
:name="'c' + columnId"
:size="size"
:clearable="clearable"
:validate-event="validate_event"
:show-word-limit="show_word_limit"
@blur="run"
@change="run"
@clear="run"
@input="myinput"
@keydown="clear"
>
</el-input>
- 代碼部分
import { defineComponent } from 'vue'
// 按需索取的方式引入UI庫(非必須)
import { ElInput } from 'element-plus'
// 引入組件需要的屬性、表單子控件的管理類
import { itemProps, itemController } from 'nf-ui-controller'
export default defineComponent({
name: 'el-form-item-text',
props: {
'el-input': ElInput,
modelValue: [String, Number],
...itemProps // 基礎屬性
},
emits: ['update:modelValue'],
setup (props, context) {
const {
value,
run,
clear,
myinput
} = itemController(props, context.emit)
return {
value,
run,
clear,
myinput
}
}
})
這是基於UI庫做的二次封裝的庫,使用了兩個第三方插件:
- 一個是 element-plus,采用按需加載的方式;
- 一個是自己做的 nf-ui-controller 庫。
然后我們設置一個入口文件 main.js
import nfText from './t-text.vue'
export {
nfText
}
如果有很多組件的話,就要考慮好結構,這里只是舉個例子。
設置vite.config.js
代碼寫好之后,需要設置一下 vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path' // 主要用於alias文件路徑別名
export default defineConfig({
plugins: [vue()],
// 打包配置
build: {
lib: {
entry: resolve(__dirname, 'lib/main.js'), // 設置入口文件
name: 'nf-tool', // 起個名字,安裝、引入用
fileName: (format) => `nf-tool.${format}.js` // 打包后的文件名
},
sourcemap: true, // 輸出.map文件
rollupOptions: {
// 確保外部化處理那些你不想打包進庫的依賴
external: ['vue'],
output: {
// 在 UMD 構建模式下為這些外部化的依賴提供一個全局變量
globals: {
vue: 'Vue'
}
}
}
}
})
- entry:指定要打包的入口文件。
- name:包的名稱
- fileName:包文件的名稱,默認是umd和es兩個文件。
- sourcemap:是否生成 .map 文件,默認是不會生成的,如果需要的話需要設置為 true。
- rollupOptions:如果項目引用了第三方插件,那么需要在這里設置排除,如果不設置的話,第三方插件的源碼也會被打包進來,這樣打包文件就變大了。排除之后第三方的插件會單獨存在。
vite 會按照這里的設置進行打包,打包分為兩種模式:
- umd:打包后代碼很緊湊、體積小,但是不易讀;
- es:打包后的代碼和我們寫的代碼很像,易讀,但是體積大。
第三方插件的處理方式
如果項目使用了第三方的插件,那么需要在 external 里面做設置:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path' // 主要用於alias文件路徑別名
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
// 打包配置
build: {
lib: {
entry: resolve(__dirname, 'lib/main.js'),
name: 'nf-ui-element-plus',
fileName: (format) => `nf-ui-element-plus.${format}.js`
},
sourcemap: true,
rollupOptions: {
// 確保外部化處理那些你不想打包進庫的依賴
external: ['vue','nf-ui-controller','element-plus'], // 注意看這里
output: {
// 在 UMD 構建模式下為這些外部化的依賴提供一個全局變量
globals: {
vue: 'Vue',
'nf-ui-controller': 'nfUIController',
'element-plus': 'elementPlus'
}
}
}
}
})
設置之后,第三方插件的代碼,會以 import 的方式被引用。
如果不做設置的話,就會把第三方插件里面使用到的代碼,拿出來作為項目內部代碼一起被打包,這樣包的體積就變大了。
設置package.json
設置 package.json 主要是發布的時候做資源包的說明,需要按照 npm 的要求設置屬性:
{
"name": "nf-tool",
"version": "0.0.4",
"description": "JavaScript 的小工具,驗證 JavaScript 的數據類型,輸出調試信息等。",
"keyword": "JavaScript typeof log",
"files": ["dist"],
"main": "./dist/nf-tool.umd.js",
"module": "./dist/nf-tool.es.js",
"exports": {
".": {
"import": "./dist/nf-tool.es.js",
"require": "./dist/nf-tool.umd.js"
}
},
"private": false,
"license": "MIT",
"auther": "jin yang (jyk). Email: jyk0013@163.com",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"@element-plus/icons": "^0.0.11",
"element-plus": "^1.2.0-beta.3",
"nf-tool": "^0.0.6",
"vue": "^3.2.16"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.9.3",
"vite": "^2.6.4"
}
}
如果在安裝資源包的時候,不想自動安裝第三方插件的話,需要去掉 dependencies 和 devDependencies,否則會自動安裝里面涉及到的第三方插件。
打包
設置好之后我們可以用 yarn build
進行打包。
打包后的文件是這種風格:
- nf-tool-es.js
- nf-tool-umd.js
發布資源包到 npmjs.com
簡單介紹一下步驟:
- 先到 npmjs.com 網站注冊賬號;
- 到注冊郵箱激活賬號;
- 打開終端,使用
npm login
登錄; - 使用
npm publish
發布;
篇幅有限,細節就不介紹了。
安裝資源包
我喜歡使用 yarn 安裝資源包,因為速度更快一些。
yarn add nf-tool
package.json 里面設置的名稱就是安裝用的名稱,所以要起個好的名稱,另外不能和現有的名稱重復。
如果一個項目即是庫項目,又需要發布到網站怎么辦?
為啥會有這樣的需求?
庫項目在編寫的時候,需要一個開發環境,一邊寫代碼,一邊運行看效果。
寫好之后需要按庫項目的方式打包。
然后是不是需要一個測試環境,或者演示環境?
如果再建立一個項目寫測試和演示,那么就有點麻煩了,如果可以在一個項目搞定就方便多了。
這個時候就需要設置不同的 vite.config.js 。
之前使用注釋的方式,改來改去的比較麻煩。現在發現 vite 提供了“模式”的方式,允許我們在 vite.config.js 里面做不同的設置。
設置.env文件
按照官網的說明,我們可以建立多個 .env.* 文件,來存放不同的“模式”:
.env : 開發環境(默認)
VITE_BASEURL=./
.env.project : 測試、演示環境
VITE_BASEURL=nf-rollup-tool
.env.lib : 庫打包
VITE_BASEURL=lib
模式設置好之后我們來修改 vite.config.js
修改 vite.config.js
在 vite.config.js 里面,首先定義不同的 defineConfig 備用,然后使用 loadEnv 讀取模式值,根據模式返回對應的 defineConfig。
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path' // 主要用於alias文件路徑別名
const pathResolve = (dir) => resolve(__dirname, '.', dir)
// 發布庫的設置
const lib = defineConfig({
plugins: [vue()],
// 打包配置
build: {
lib: {
entry: resolve(__dirname, 'lib/main.js'),
name: 'nf-tool',
fileName: (format) => `nf-tool.${format}.js`
},
sourcemap: true,
rollupOptions: {
// 確保外部化處理那些你不想打包進庫的依賴
external: ['vue'],
output: {
// 在 UMD 構建模式下為這些外部化的依賴提供一個全局變量
globals: {
vue: 'Vue'
}
}
}
}
})
// 開發模式、生產模式
const project = (url) => {
return defineConfig({
plugins: [vue()],
devtools: true,
resolve: {
alias: {
'/@': resolve(__dirname, '.', 'src'),
'/nf-tool': pathResolve('lib/main.js') //
}
},
base: url,
// 打包配置
build: {
sourcemap: true,
outDir: 'distp', // 指定輸出路徑,要和庫的包區分開
assetsDir: 'static/img/', // 指定生成靜態資源的存放路徑
rollupOptions: {
output: {
chunkFileNames: 'static/js1/[name]-[hash].js',
entryFileNames: 'static/js2/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
},
brotliSize: false, // 不統計
target: 'esnext',
minify: 'esbuild' // 混淆器,terser構建后文件體積更小
}
}
})
}
// 用 loadEnv 讀取模式,然后返回對應的 defineConfig
export default ({ mode }) => {
const url = loadEnv(mode, process.cwd()).VITE_BASEURL
if (url === 'lib') {
// 打包庫文件
return lib
} else {
// 開發模式、生產模式
return project(url)
}
}
注意:需要設置不同的輸出的文件夾,否則會互相覆蓋。
修改 package.js
設置執行的命令,后面加上需要的模式。
"scripts": {
"dev": "vite",
"build": "vite build --mode project",
"lib": "vite build --mode lib",
"serve": "vite preview"
}
這樣就可以把不同的環境完全分開了,如果有其他的需求,還可以增加更多的模式。
更改后的打包命令
yarn dev // 開發環境
yarn build // 打包發布到網站,演示
yarn lib // 打包庫項目,做成資源包
這樣各司其職,互不干擾,也不用各種寫注釋了。