新建一个项目

可以使用webpack自己搭建,也可以使用脚手架搭建一个项目
这里为了快速给大家演示,我直接用vuecli4搭建一个项目

  1. // 已经全局安装了vuecli4脚手架
  2. vue create common_business

自定义选择想要的插件后,我们的项目就完成了
我们进入到项目中,把不要的文件都删除掉,最后的文件分布如下图:
image.png

  • build:项目的配置文件
  • examples:事例代码,可以在开发完成后进行测试,不会被打包
  • lib:打包后的文件,使用这个组件实际就是引用了这个文件内容
  • packages:封装组件的源代码,里面一个组件就是一个文件夹
  • public:index.html文件,方便本地调试的
  • static:一些需要的静态文件,一般情况下,是不需要这个文件夹的
  • 其他:配置文件

封装组件

image.png
我们这次就封装一个alert弹窗,该弹窗是基于element-ui的el-dialog封装的,如图src文件夹下的Main.vue文件就是该组件的源码,源码部分我就不主要阐述了
跟src文件夹同级有一个index.js,这个文件主要是负责导出对应的组件的,如果你的组件库想要设置按需引入,那就需要在每一个组件里面导出自身
如下代码,其实就是在进行组件的注册

  1. import AlertDialog from './src/Main.vue';
  2. AlertDialog.install = function(Vue) {
  3. Vue.component(AlertDialog.name, AlertDialog);
  4. };
  5. export default AlertDialog;

packages文件夹下还有一个index.js,这个js主要就是到处packages文件夹下的所有组件的:
我们把组件都放入数组中,实际上是可以方便我们做一个循环,而不必一个一个写死

  1. import AlertDialog from './alert-dialog';
  2. // 存储组件列表
  3. const components = [
  4. AlertDialog
  5. ]
  6. // 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,则所有的组件都将被注册
  7. const install = function (Vue) {
  8. // 判断是否安装
  9. if (install.installed) return
  10. // 遍历注册全局组件
  11. components.map(component => Vue.component(component.name, component))
  12. }
  13. // 判断是否是直接引入文件
  14. if (typeof window !== 'undefined' && window.Vue) {
  15. install(window.Vue)
  16. }
  17. export {
  18. install,
  19. AlertDialog
  20. }
  21. // 导出的对象必须具有 install,才能被 Vue.use() 方法安装
  22. export default {
  23. install,
  24. AlertDialog
  25. }

使用vue的同学应该都知道,在vue中如果我们想要注册一个组件,我们可以使用Vue.use(xxx),那具体是为什么呢?
查看vue源码你就会发现,我们在使用Vue.use(xxx)的时候,实际是将Vue传递给xxx组件,并且调用xxx组件里面的install方法,这就是为什么我们的组件里面为什么必须要加一个install方法了
我看可以看看install方法帮我们做了什么?
其实就是在进行组件的注册

  1. const install = function (Vue) {
  2. // 判断是否安装
  3. if (install.installed) return
  4. // 遍历注册全局组件
  5. components.map(component => Vue.component(component.name, component))
  6. }

同时使用export defaultexport,是为了做既可以通过一个对象进行导出,又可以进行解构
好了,组件的封装就讲到这儿了

项目配置

如果我们想要讲组件打包成script标签可以引入的方式,我们需要设置libraryTarget,这个target设置我下次会出文章详细讲述,这里就不赘述了,其实就是需要我们设置成umd模式
新建vue.config.js

  1. const devConfig = require('./build/config.dev')
  2. const buildConfig = require('./build/config.build')
  3. module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig

这里我是区分了两个环境的,为了更清晰,所以我直接区分成两个js
注意:如果你不需要进行本地调试,那你可以不需要配置dev环境,直接打包即可

我们先看看dev环境下的配置:

  1. const pub = require('./config.common')
  2. const path = require('path')
  3. module.exports = {
  4. pages: {
  5. index: {
  6. // page 的入口
  7. entry: 'example/main.js',
  8. // 模板来源
  9. template: 'public/index.html',
  10. // 在 dist/index.html 的输出
  11. filename: 'index.html',
  12. // 当使用 title 选项时,
  13. title: '组件'
  14. },
  15. },
  16. configureWebpack: {
  17. resolve: {
  18. extensions: ['.js', '.vue', '.json'],
  19. alias: {
  20. '@': resolve('examples')
  21. }
  22. },
  23. },
  24. chainWebpack: config => {
  25. config.module
  26. .rule('js')
  27. .include.add(/packages/).end()
  28. .include.add(/examples/).end()
  29. .use('babel')
  30. .loader('babel-loader')
  31. .tap(options => {
  32. // 修改它的选项...
  33. return options
  34. })
  35. }
  36. }
  37. function resolve(dir) {
  38. return path.join(__dirname, dir)
  39. }

由于组件库的话,一般都会是比较独立的,不会引入太多依赖,引入太多依赖的组件,我们也不建议专门进行npm包的封装
所以我们这是配置了babel以及入口文件entry,上面已经写得很清楚了

下面我们来重点看看生产环境的打包配置:

  1. module.exports = {
  2. outputDir: resolve('lib'),
  3. productionSourceMap: false,
  4. publicPath: './',
  5. configureWebpack: {
  6. entry: {
  7. '/': resolve('packages/index.js')
  8. },
  9. output: {
  10. filename: '[name]/index.js',
  11. libraryTarget: 'umd',
  12. libraryExport: 'default',
  13. library: 'CommonComponents'
  14. },
  15. resolve: {
  16. extensions: ['.js', '.vue', '.json'],
  17. alias: {
  18. '@': resolve('examples')
  19. }
  20. },
  21. },
  22. css: {
  23. sourceMap: true,
  24. extract: {
  25. filename: '[name]/style.css'
  26. }
  27. },
  28. chainWebpack: config => {
  29. config.module
  30. .rule('js')
  31. .include.add(/packages/).end()
  32. .use('babel')
  33. .loader('babel-loader')
  34. .tap(options => {
  35. // 修改它的选项...
  36. return options
  37. })
  38. config.optimization.delete('splitChunks')
  39. config.plugins.delete('copy')
  40. config.plugins.delete('html')
  41. config.plugins.delete('preload')
  42. config.plugins.delete('prefetch')
  43. config.plugins.delete('hmr')
  44. config.entryPoints.delete('app')
  45. }
  46. }
  47. function resolve(dir) {
  48. // 路径可能与你的项目不同
  49. return path.join(__dirname, dir)
  50. }

首先需要配置ouput的文件夹,表示打包后的文件我们要放到lib文件夹下

  1. outputDir: resolve('lib'),

然后收最重要的配置:

  • filename:表示打包后文件的名字,我们把name写活,如果有多个组件,我们就好有多个文件夹
  • libraryTarget:表示打包的模式,常见的是_umd__commonJs__amd_
  • libraryExport:将入口的默认到处分配给libraryTarget
  • library:导出的库的名字,你的组件库暴露出去的名字
    1. output: {
    2. filename: '[name]/index.js',
    3. libraryTarget: 'umd',
    4. libraryExport: 'default',
    5. library: 'CommonComponents'
    6. },
    最后你执行npm run build,会出现一个lib文件夹,里面就会是你打包后的文件了
    image.png

    使用

    先将你的项目发布到npm上
    1. npm publish
    注意:
    每次发布之前都需要更新一下版本号
    1. npm version patch
    2. npm version minor // 1.1.0 表示新增一些小功能
    3. npm version mmajor // 2.0.0 表示大的版本或大升级
    4. npm version preminor // 1.1.0-0 后面多了个0,表示预发布
    npm上如何已经有你发布的包了,那你就可以去cdn上面找到你的包
    image.png
    地址:https://www.jsdelivr.com/
    去搜索一下你刚刚发布的包,如下图:
    image.png
    如果搜到了,你可以找到cdn地址,引入到你的项目中
    image.png
    打开想要使用组件的项目的index.html,引入上面截图的地址:
    1. // index.html
    2. <body>
    3. <noscript>
    4. <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    5. </noscript>
    6. <div id="app"></div>
    7. <script src="https://cdn.jsdelivr.net/npm/common_business@0.1.12/lib/index.min.js"></script>
    8. </body>
    在项目的入口文件进行注册:
    1. // main.js
    2. const AlertDialog = CommonComponents.AlertDialog;
    3. Vue.use(AlertDialog);
    在vue文件中使用:
    1. <template>
    2. <div>
    3. <el-button type="primary"
    4. @click="alertVisible = true">打开通过script标签引入的alert弹窗组件</el-button>
    5. <alert-dialog title="提示"
    6. :visible.sync="alertVisible"
    7. width="600px">
    8. <div class="file-name-box">我在主应用使用</div>
    9. </alert-dialog>
    10. <router-view class="main-center"
    11. ref="mainView" />
    12. </div>
    13. </template>
    注意
    如果更新了版本,cdn上面搜索不到新版本内容,可能是cdn有缓存,你可以直接在浏览器直接通过修改版本号打开你需要引入的js,如果能打开,如果是发布成功的,你可以放心在项目里面使用
    如下图:我发布了0.1.12,但是cdn上面没有这个版本,我就直接把0.1.11修改成0.1.12,如果浏览器返回正常,那就可以直接使用了
    image.png