项目目录:
运行命令:
npm run serve:运行实例工程
npm run new:component 组件名 组件描述:创建组件模板
通过运行该指令生成初始化组件模板(components\xxx\index.vue)、示例模板(views\xxx\demo.vue)和配置参数
npm run build:打包组件
注意:当前打包自动遍历src\components下index.vue组件
npm run build:component:打包单个组件
注意:打包的组件需要在build\components.json文件配置,初始化组件指令会自动添加上去
打包配置:
webpack.base.js
通用的webpack配置,减少重复代码
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
mode: 'production',
externals: {
vue: { //将vue依赖 "外部化",不打包进组件库
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
output: {
comments: false
}
}
})
]
},
module: {
rules: [
{
test: /\.js$/,
include: process.cwd(),
use:{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
},
exclude : /node_modules/
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{ test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"] },
]
},
plugins: [
new ProgressBarPlugin(),
new VueLoaderPlugin()
]
};
webpack.config.js
继承自webpack.base.js配置,打包全部组件配置
const path = require('path');
const merge = require('webpack-merge');
const webpackBaseConfig = require('./webpack.base.js');
const basePath = path.resolve(__dirname, '../')
module.exports = merge(webpackBaseConfig,{
entry: path.join(basePath, 'src/components', 'index.js'),
output: {
path: path.resolve(process.cwd(), './lib'),
publicPath: '/dist/',
filename: 'components.mini.js',
chunkFilename: '[id].js',
libraryTarget: 'umd',
libraryExport: 'default',
library: 'components',
umdNamedDefine: true,
globalObject: 'typeof self !== \'undefined\' ? self : this'
}
})
webpack.component.js
继承自webpack.base.js配置,打包单个组件配置
const path = require('path');
const merge = require('webpack-merge');
const webpackBaseConfig = require('./webpack.base.js');
const basePath = path.resolve(__dirname, '../')
const components = require('./components.json')
let entries = {}
Object.keys(components).forEach(key => {
entries[key] = path.join(basePath, 'src/components', components[key])
})
module.exports = merge(webpackBaseConfig,{
entry: entries,
output: {
path: path.resolve(process.cwd(), './lib'),
publicPath: '/dist/',
filename: '[name].js',
chunkFilename: '[id].js',
libraryTarget: 'umd',
libraryExport: 'default',
library: '[name]',
umdNamedDefine: true,
globalObject: 'typeof self !== \'undefined\' ? self : this'
}
})
components.json:组件列表(key:组件名,value:相对路径)
{
"BaseTable": "BaseTable/index.vue",
"Test": "Test/index.vue",
"BaseTooltip": "BaseTooltip/index.vue"
}
babel-plugin-import
安装babel-plugin-import插件,对路径做一个转换
babel.config.js:
plugins:[
'@babel/plugin-transform-modules-umd',
['import', {
libraryName: 'componentLibrary',
libraryDirectory: 'lib',
}]
]
批量引入单个组件:
import { Test } from 'componentLibrary'
组件、示例:
组件入口:components/index.js
//动态加载组件
const importFn = require.context('./', true, /index\.vue$/)
let components = importFn.keys().map(key=>importFn(key).default)
const install = function(Vue, opts = {}) {
importFn.keys().forEach(key => {
const component = importFn(key).default
Vue.component(component.name, component)
});
};
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
export default {
install,
...components.reduce((acc,com)=>{
if(acc[com.name]) console.log('存在重复组件名'+com.name)
return {...acc,[com.name]:com}
},{})
}
动态引入components文件夹下的所有组件
示例组件入口:views/index.js(对应组件的使用demo,方便调试)
//动态加载组件
const importFn = require.context('./', true, /demo\.vue$/)
let components = importFn.keys().map(key=>importFn(key).default)
export default {
...components.reduce((acc,com)=>{
if(acc[com.name]) console.log('存在重复组件名'+com.name)
return {...acc,[com.name]:com}
},{})
}
动态引入views文件夹下的所有示例组件
<template>
<div id="app" ref="app">
<!-- 组件调试 -->
<component :is="componentName"></component>
</div>
</template>
<script>
import components from './views/index.js'
export default {
name: 'App',
components,
data(){
return{
componentName:'BaseTooltipDemo' //根据示例组件名显示组件
}
}
}
</script>
App.vue引入index.js,替换componentName为要显示组件的name,页面就会加载对应name的示例组件
创建组件模板:
template.js:组件和示例组件自定义模板
// 组件初始化模板
module.exports = {
vueTemplate: (compoenntName,componentTip='') => {
return `<!-- ${componentTip} -->
<template>
<div class="${compoenntName}"></div>
</template>
<script>
export default {
name: '${compoenntName}',
components: {},
data() {
return {}
},
computed: {},
watch: {},
created() {},
mounted() {},
destroyed() {},
methods: {}
}
</script>
<style lang="scss" scoped>
.${compoenntName}{}
</style>
`
},
demoTemplate: (compoenntName,componentTip='') => {
return `<!-- ${componentTip}示例 -->
<template>
<div class="${compoenntName}Demo">
<${compoenntName}></${compoenntName}>
</div>
</template>
<script>
export default {
name: '${compoenntName}Demo',
components: {},
data() {
return {}
},
computed: {},
watch: {},
created() {},
mounted() {},
destroyed() {},
methods: {}
}
</script>
<style lang="scss" scoped>
.${compoenntName}Demo{}
</style>
`
}
}
createComponent.js:自动生成组件和示例组件、配置components.json组件列表
const path = require('path')
const fs = require('fs')
const basePath = path.resolve(__dirname, '../')
const log = message => console.log(message)
// 导入模板
const {vueTemplate,demoTemplate} = require('./template')
const componentName = process.argv[2]
const componentTip = process.argv[3]
// 递归目录
function dotExistDirectoryCreate(directory) {
return new Promise((resolve) => {
fs.mkdirSync(directory)
resolve(true)
})
}
//创建文件
function generateFile(path, data){
return new Promise((resolve, reject) => {
fs.writeFile(path, data, 'utf8', err => {
if (err) {
log(err.message)
reject(false)
} else {
resolve(true)
}
})
})
}
//开始创建组件
async function startCreate(componentName,module) {
const componentPath = path.resolve(basePath,`src/${module}/${componentName}`)
const fileName = module==='components'?'index':'demo'
const vueFile = path.join(componentPath,'',`${fileName}.vue`)
if(fs.existsSync(componentPath)) return log(`${componentName}组件已存在,请重新输入`)
await dotExistDirectoryCreate(componentPath)
let res = await generateFile(vueFile, module==='components'?vueTemplate(componentName,componentTip):demoTemplate(componentName,componentTip))
if(res&&module==='components'){
//创建说明文档
generateFile(path.join(componentPath,'',`${fileName}.md`),'')
//添加新组件信息到components.json
let config = require('./components.json');
if(!config[componentName]) config[componentName] = `${componentName}/${fileName}.vue`
//将修改后的内容写入文件
fs.writeFile('./build/components.json', JSON.stringify(config),'utf8', errJson => {
if (errJson) console.error(errJson);
});
}
}
// 生成文件
startCreate(componentName,'components')
startCreate(componentName,'views')