在Vue CLI3搭建組件庫並用npm發佈實戰操做中介紹了單個組件的組件庫怎麼開發,本文將一步一步教你們怎麼開發多個組件集成的組件庫,怎麼實現組件庫按需引入。以爲有用點個贊支持一下。css
本文將略過安裝Vue CLI三、搭建組件庫的項目、清潔組件庫項目的步驟,不懂的地方能夠看Vue CLI3搭建組件庫並用npm發佈實戰操做html
在Vue CLI3中,項目的webpack配置是要在根目錄下新建vue.config.js來配置。vue
由於多入口組件庫中開發環境和生成環境的配置是不一樣,全部要區分開來。 經過process.env.NODE_ENV
變量來判斷,生產環境時process.env.NODE_ENV
爲development
。node
//開發環境配置
const devConfig = {
//...
}
const buildConfig = {
//...
}
module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig;
複製代碼
在Vue組件庫項目中原來src文件夾的內容是demo展現的內容,因此文件名改爲examples,比較形象。webpack
在vue.config.js文件中配置內容以下:git
const devConfig={
pages: {
index: {
entry: 'examples/main.js',
template: 'public/index.html',
filename: 'index.html',
},
},
}
複製代碼
文件別名會在寫demo中用到。在vue.config.js文件中配置內容以下:github
const path = require('path');
function resolve(dir) {
return path.resolve(__dirname, dir)
}
const devConfig = {
configureWebpack: {
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('packages'),
'assets': resolve('examples/assets'),
'views': resolve('examples/views'),
}
},
},
}
複製代碼
在vue.config.js文件中配置內容以下:web
const devConfig = {
devServer:{
port: 8091,//固定端口
hot: true,//開啓熱更新
open: 'Google Chrome'//固定打開瀏覽器
}
}
複製代碼
組件的代碼在packages文件夾中開發npm
在vue.config.js文件中配置內容以下:json
const devConfig = {
chainWebpack: config => {
config.module
.rule('js')
.include
.add('/packages')
.end()
.use('babel')
.loader('babel-loader')
.tap(options => {
return options
})
},
}
複製代碼
以上是開發環境的配置,下面來寫一下生產環境的配置
const buildConfig = {
chainWebpack: config => {
config.module
.rule('js')
.include
.add('/packages')
.end()
.use('babel')
.loader('babel-loader')
.tap(options => {
return options
})
},
}
複製代碼
咱們將組件庫打包編譯後放在lib文件夾中,在vue.config.js文件中配置內容以下:
const buildConfig = {
outputDir: 'lib',
}
複製代碼
關閉source map有兩個好處
在vue.config.js文件中配置內容以下:
const buildConfig = {
productionSourceMap: false,
}
複製代碼
在Vue CLI3搭建的項目中藉助
babel-plugin-import
這個webpack插件而且配置babel.config.js,來實現組件庫的按需引入的前提是組件庫是多入口文件頁面打包的。
在Vue CLI3中是在configureWebpack
選項的entry
屬性上配置項目多入口,在本文案例中,配置以下
const buildConfig = {
configureWebpack: {
entry: {
index: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\index.js',
testA: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testA\\index.js',
testB: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testB\\index.js'
},
},
}
複製代碼
可是以上每一個入口都是寫死的,因此咱們要利用nodejs實現自動化配置。
首先咱們引入nodejs中path模塊來處理文件路徑。
const path = require('path');
const join = path.join;//拼接路徑
複製代碼
寫一個把目標路徑按當前文件路徑轉成絕對路徑的方法
function resolve(dir) {
return path.resolve(__dirname, dir)
}
複製代碼
其中__dirname是當前文件所在目錄的完整絕對路徑,例
還要引入nodejs中fs模塊在處理文件信息
const fs = require('fs');
複製代碼
咱們建一個函數getEntries(path)
,其中path是組件代碼所在的文件夾名稱,返回一個對象entries,key爲每一個組件文件夾的名稱,值爲每一個組件文件夾中入口文件index.js的絕對路徑。
首先使用fs.readdirSync(resolve(path))
獲取到組件代碼所在的文件夾目錄下全部文件名稱,存在files變量中。
而後用數組reduce()
方法循環files,先將每一個文件名(item)利用join(path, item)
轉成路徑存到itemPath變量。
用fs.statSync(itemPath).isDirectory()
對每一個文件進行判斷是否是文件夾。
若是是文件夾,先把itemPath和入口文件index.js拼接成一個地址,再轉成絕對路徑,將item做爲key,賦值到返回對象上
entries[item] = resolve(join(itemPath, 'index.js'))。
複製代碼
若是不是文件夾,直接把itemPath轉成絕對路徑,將item去除後綴做爲key,賦值到返回對象上
const [name] = item.split('.')
entries[name] = resolve(`${itemPath}`)
複製代碼
下面是完整代碼
function getEntries(path) {
let files = fs.readdirSync(resolve(path));
const entries = files.reduce((ret, item) => {
const itemPath = join(path, item)
const isDir = fs.statSync(itemPath).isDirectory();
if (isDir) {
ret[item] = resolve(join(itemPath, 'index.js'))
} else {
const [name] = item.split('.')
ret[name] = resolve(`${itemPath}`)
}
return ret
}, {})
return entries
}
複製代碼
好比在本文案例中,執行getEntries('packages')
將會獲得一個對象
{
index: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\index.js',
testA: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testA\\index.js',
testB: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testB\\index.js'
},
複製代碼
利用對象展開運算符,配置entry
const buildConfig = {
configureWebpack: {
entry: {
...getEntries('packages'),
},
},
}
複製代碼
[name].index.js
,爲何配置這個名稱後面會解釋。commonjs2
,入口文件的返回值將分配給module.exports對象,使其組件庫在webpack構建的環境下使用,這個是關鍵。const buildConfig = {
configureWebpack: {
output: {
filename: '[name]/index.js',
libraryTarget: 'commonjs2',
}
},
}
複製代碼
在css.extract.filename上配置樣式打包路徑和文件名稱
const buildConfig = {
css: {
sourceMap: true,
extract: {
filename: 'style/[name].css'//在lib文件夾中創建style文件夾中,生成對應的css文件。
}
},
}
複製代碼
const buildConfig = {
chainWebpack: config => {
config.optimization.delete('splitChunks')
config.plugins.delete('copy')
config.plugins.delete('html')
config.plugins.delete('preload')
config.plugins.delete('prefetch')
config.plugins.delete('hmr')
config.entryPoints.delete('app')
},
}
複製代碼
const buildConfig = {
chainWebpack: config => {
config.module
.rule('fonts')
.use('url-loader')
.tap(option => {
option.fallback.options.name = 'static/fonts/[name].[hash:8].[ext]'
return option
})
},
}
複製代碼
本案例中簡單寫了testA和testB兩個組件來供測試
主要講一下組件入口文件怎麼編寫,其它跟平時開發同樣。
以組件testA爲例,對外暴露一個對象test,並提供 install 方法,外部就能夠經過Vue.use()
來調用這個組件。
import test from './src/index.vue';
test.install = function(Vue) {
Vue.component(test.name, test);
};
export default test;
複製代碼
import testA from './testA'
import testB from './testB'
export default {
install(Vue) {
Vue.use(testA);
Vue.use(testB)
},
}
複製代碼
執行npm run build
,打包編譯後,在項目中會獲得一個lib文件夾,內容如圖所示。
在package.json文件中寫入:
"main": "lib/tree-select.umd.min.js",
複製代碼
發佈好後(若是不會發布請看Vue CLI3搭建組件庫並用npm發佈實戰操做),在引用組件庫demo中,執行npm install map-lib-test@0.6.0 --save
安裝組件庫。
在執行npm install babel-plugin-import --save-dev
安裝babel-plugin-import插件,利用它實現組件按需引入。
在根目錄下babel.config.js文件中按以下配置
module.exports = {
"presets": ["@vue/app"],
"plugins": [
[
"import",
{
"libraryName": "map-lib-test",//組件庫名稱
"camel2DashComponentName": false,//關閉駝峯自動轉鏈式
"camel2UnderlineComponentName": false//關閉蛇形自動轉鏈式
}
],
]
}
複製代碼
在main.js寫入
import { testA, testB } from 'map-lib-test';
Vue.use(testA);
Vue.use(testB);
複製代碼
在src/views/demo.vue引用
<template>
<div>
<testA></testA>
<testB></testB>
</div>
</template>
<script>
export default {
data() {
return {
}
},
}
</script>
複製代碼
瀏覽器展現
這裏只知道引入全局樣式。配置按需引入樣式時,總是提示樣式沒法找到,若是有人知道怎麼配置,懇請在評論留言並完善。
從node_modules/_map-lib-test@0.6.0@map-lib-test文件中能夠看到組件庫的結構
import "map-lib-test/lib/style/index.css";
複製代碼
瀏覽器展現
import{testA,testB}
而不是import{testC,testD}
若是是`import {testC ,testD }`,會發生以下報錯
複製代碼
import { testA, testB } from 'map-lib-test';
複製代碼
至關
import testA from "map-lib-test/lib/testA";
import testB from "map-lib-test/lib/testB";
複製代碼
import { testC, testD } from 'map-lib-test';
複製代碼
至關
import testC from "map-lib-test/lib/testC";
import testB from "map-lib-test/lib/testD";
複製代碼
map-lib-test/lib中沒有testC、testD這個文件,因此就報以上錯誤。
那爲何是import{testA , testB}
,還記得在多入口文件頁面打包配置中配置entry時,entry對象每一個key是每一個組件代碼所在的文件夾名,而output.filename是'[name]/index.js'
。每一個組件是獨立打包,打包生成文件夾名稱是原來每一個組件代碼所在文件夾名稱。
這樣是否是很清楚,爲何要import{testA , testB}
,是由於testA和testB兩組件打包生成文件夾名分別是testA和testB。
以上還能夠說明爲何output.filename是'[name]/index.js'
而不是'[name]/main.js'
或者而不是'[name]/out.js'
由於import testA from "map-lib-test/lib/testA";
默認至關import testA from "map-lib-test/lib/testA/index.js";
因此在寫組件使用文檔時,必定要寫明按需引入時,是import{testA , testB}
的。
爲何是<testA></testA>
,而不是<testD></testD>
。 看一下testA組件的入口文件中有寫這麼一段代碼
import test from './src/index.vue';
test.install = function(Vue) {
Vue.component(test.name, test);
};
複製代碼
在Vue中註冊以test.name爲名稱的組件,而test.name就是testA組件中name選項的值。