背景說明
最近公司要將項(xiàng)目用的組件整合成一個(gè)npm庫,像使用mint-ui一樣引用,于是借這個(gè)機(jī)會(huì)學(xué)習(xí)了一下,實(shí)現(xiàn)按需引用自己的vue組件庫。
參考鏈接:https://segmentfault.com/a/1190000015884948#articleHeader1
目標(biāo)
可以像mint-ui一樣按需引用組件,方便團(tuán)隊(duì)統(tǒng)一管理組件。
難點(diǎn)
webpack的入口、出口配置,以及組件的按需加載
原理
利用 babel-plugin-component 插件,來實(shí)現(xiàn)按需加載。該插件需要的是 這樣一個(gè)文件結(jié)構(gòu)

實(shí)現(xiàn)過程
-
vue-cli 搭建基礎(chǔ)項(xiàng)目。
vue init webpack adms-plugin
因?yàn)楣窘M件依賴mint-ui,因此還需要install mint-ui以及使用iconfont。
npm i mint-ui --save

項(xiàng)目是用vue-cli搭建的項(xiàng)目:
1、build中是webpack相關(guān)配置
2、example目錄是開發(fā)時(shí)調(diào)試用
3、src目錄中components中是各個(gè)功能模塊,各功能模塊的入口中同element一樣,對(duì)組件進(jìn)行擴(kuò)展,增加install方法,將組件進(jìn)行全局注冊(cè),index.js是入口,其中引入所有功能模塊,同時(shí)導(dǎo)出模塊和install方法,將所有模塊進(jìn)行注冊(cè)。
4、lib中是最終打包的目標(biāo)目錄,記錄將要打包的功能模塊名稱和路徑
5、增加components.json文件
{
"Datepopup":"./src/components/Datepopup",
"index":"./src/index"
}
index文件為所有文件入口,可用于全局引用。
-
webpack相關(guān)配置更改
規(guī)劃中,需要將各個(gè)模塊打包至lib,一個(gè)功能模塊為一個(gè).js文件,并且在theme目錄中存在一個(gè)同名的.css文件,這樣使用時(shí)借助babel-plugin-component插件就可實(shí)現(xiàn)按需引入。還需要一個(gè)總的index.js包含所有的功能模塊,和一個(gè)index.css包含所有的樣式。
這里采用多入口(entry)配置,實(shí)現(xiàn)各個(gè)功能模塊分別打包成一個(gè).js文件,并使用extract-text-webpack-plugin將樣式進(jìn)行抽離后,按入口chunk進(jìn)行打包為對(duì)應(yīng)的.css文件。
npm run build進(jìn)行將各個(gè)模塊打包為各自的.js文件,樣式文件也同時(shí)抽離處理為同名的.css
下面是webpeck相關(guān)具體修改:
E:\npm\adms-plugin\config\index.js

assetsRoot路徑改為lib
-
下面詳細(xì)介紹webpack.prod.conf
E:\npm\adms-plugin\build\webpack.prod.conf.js
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
// 整理入口
const components = require('../components.json')
const entrys = {}
Object.keys(components).forEach(item => {
entrys[item] = components[item]
})
const webpackConfig = merge(baseWebpackConfig, {
entry: entrys,
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: '[name].js',
library: 'VueDropUpload',
libraryTarget: 'umd'
},
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: '/theme/[name].css'
})
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
1、刪除HtmlWebpackPlugin相關(guān)配置,這里只需打包為js文件和css文件,不涉及html
2、引入components.json,根據(jù)配置的模塊及路徑配置入口entrys,配置完后,將入口寫入entry配置
3、ExtractTextPlugin插件是進(jìn)行css抽離的,這里不用處理
4、ouput中,出口.js文件的名稱不能寫死
5、在plugins插件配置處,ExtractTextPlugin相關(guān)配置,filename不能寫死,需要根據(jù)chunk名稱自動(dòng)生成對(duì)應(yīng)名稱。這里設(shè)置為'/theme/[name].css'指定目錄lib/theme存放
到此,webpack配置就差不多完成了。
npm run build打包完成后的目錄結(jié)構(gòu):

-
配置package.json
E:\npm\adms-plugin\package.json

private設(shè)置項(xiàng)目為公開,main設(shè)置項(xiàng)目入口地址
打包發(fā)布
npm login //登錄npm
npm publish //發(fā)布包
截止到目前為止,vue組件成功發(fā)布到npm。
此處需要將 將淘寶鏡像 還原。否則 發(fā)布、安裝 都出問題。
// 查詢當(dāng)前配置的鏡像 npm get registry //https://registry.npmjs.org/
// 設(shè)置成淘寶鏡像 npm config set registry http://registry.npm.taobao.org/
// 換成原來的 npm config set registry https://registry.npmjs.org/
另外,每次發(fā)布 需要 修改 package.json 里的 version。
還需要注意是否包名(package.json 里的 name)沖突,npm 平臺(tái)已存在的話 不讓上傳。
引入使用
接下來大家就可以開心的使用自己的npm庫了,
import {Datepopup} from 'adms-plugin'
Vue.use(Datepopup)
這里需要使用babel-plugin-component插件,并且配置
npm i babel-plugin-component -D
E:\npm\hello-world\demo.babelrc文件
"plugins": ["transform-vue-jsx", "transform-runtime",[
"component",{
"libraryName": "adms-plugin",
"styleLibrary": {
"name": "theme",
"base":true
}
}
]],
其中l(wèi)ibraryName為自己的庫名,name為樣式文件,base為是否使用引用base.css文件。
遇到的難點(diǎn)
- 多個(gè)組件引用base.css文件
使用babel-plugin-component會(huì)引用base.css,難點(diǎn)主要是 base.css 文件的生成,由于 webpack 無法單獨(dú)生成css文件(要依賴js)。這里 我新建了一個(gè) base.js 文件,也作為了一個(gè) webpack 的入口文件
因此我新建了一個(gè)base.js
import './assets/css/base.css'
import './assets/font/iconfont.css'
并且修改components.json文件:
{
"Datepopup":"./src/components/Datepopup",
"index":"./src/index",
"base":"./src/base"
}
這樣就可以生成base.css,同時(shí)依賴的iconfont也加載了進(jìn)來
- 打包時(shí)需要注意名稱命名需要使用kebab-case (短橫線分隔命名)格式,例如:button-model,在components.json配置中,需要使用短橫線分隔命名。
總結(jié)
構(gòu)建vue項(xiàng)目庫難點(diǎn)主要?jiǎng)?chuàng)建webpack入口,以及base.css文件的引入,同時(shí)使用時(shí)候記得配合babel-plugin-component插件按需加載,希望這篇文章能幫助有需要的同學(xué)們。