在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选项的值。