webpack组件库打包

项目目录:

 

运行命令:

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')

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值