Skip to content

探索组件在线预览和调试

不同于js库的打包搭建 组件库还需要打包出css,因此单独讲

组件库对外是编译后(外部应用配置loader不编译)还是编译前(跟外部应用一起编译)

如果是编译后的组件,可以脱离技术栈吗?

我们先来大概看一下市面上组件库搭建的方式

umd-组件库打包产物是纯js

  1. 打包组件库不抽离css,通过js动态插入css,组件库产物会是js 组件库入口文件
js
//  src/index.js
import Hello from "./components/Hello.vue"

// 外部的Vue.use(MyLib)会执行该方法,完成组件的全局注册。
function install(Vue){
  Vue.component(Hello.name, Hello)
}

// 通过`script`标签引入的情况,在组件内部完成组件注册。
if(window && window.Vue) {
  Vue.use(install)
}

export default install

👆 打包成umd后 支持

  1. webpack 工程 importvue.use()
  2. html直接 <script src="">

👆 这种只打包js的形式其实和打包普通js工具库一样,不过是打包配置需要支持css和vue

webpack.config.js

js
//webpack.config.js
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
  mode: 'none',
  entry: './src/index.js',
  output: {
    path: path.join(__dirname,"/dist"),
    filename: 'my-ui.js',  
    libraryTarget: 'umd',  //用到的模块定义规范
    library: 'myUi',   //库的名字
    libraryExport: 'default'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use:  ['vue-loader']
      },
      {
        test: /\.css$/,
        use:  ['style-loader','css-loader','postcss-loader']
      },
      {
          test: /\.s[ac]ss$/i,
          use:  ['style-loader','css-loader','postcss-loader','sass-loader']
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ],
  externals : {
    // 只有当libraryTarget: 'umd'时,才可以配置如上那样的包含 { root, amd, commonjs,... } 的对象,其它的libraryTarget的值不能这样配置。
    vue: {
      root: "Vue",   //通过 script 标签引入,此时全局变量中可以访问的是 Vue
      commonjs: "vue",  //可以将vue作为一个 CommonJS 模块访问
      commonjs2: "vue",  //和上面的类似,但导出的是 module.exports.default
      amd: "vue"   //类似于 commonjs,但使用 AMD 模块系统
    }
  }

}

组件库和引用方共同的外部资源库,由引用方提供,组件库不打包进去,如vue、lodash

打包结果会是umd的结构,参考从0到1构建符合标准的公共库

js
//官方示例。 'MyUi'就是library中定义的库名称, 对应我们自己demo中的myUi。

(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
  // Commonjs
    module.exports = factory();    
  else if(typeof define === 'function' && define.amd)
  // AMD
    define([], factory);           
  else if(typeof exports === 'object')
  // ESM???
    exports['MyUi'] = factory();  
  else
  // window
    root['MyUi'] = factory();  
})(typeof self !== 'undefined' ? self : this, function() {
  return _entry_return_; // 此模块返回值,是入口 chunk 返回的值
});

umd-手动按需加载

导出文件也是一个立即执行函数,一点都不符合es6 模块化方式。按照上面那样的配置是没有办法按需加载的。

有很多库为了按需引用,把打包脚本做成多入口打包的形式,实现单个组件一份js,然后通过文件路径进行引用:

js
import 'echarts/lib/chart/pie'
import 'echarts/lib/component/title'

这样写不能一次引入多个组件,也不够优雅。element-ui就专门开发了babel插件babel-plugin-component,使我们能像下面这样按需引入:

js
import { Button, Select } from 'element-ui'
Vue.use(Button)
Vue.use(Select)
js
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
  mode: 'none',
  entry: {
   'hello': './src/components/Hello/index.js',
   'test': './src/components/Test/index.js',
   'my-ui': './src/index.js'
  },
   output: {
    path: path.join(__dirname,"/lib"),
    filename: '[name].js',
    libraryTarget: 'umd',
    library: '[name]',
    libraryExport: 'default'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use:  ['vue-loader']
      },
      {
        test: /\.css$/,
        use:  ['style-loader','css-loader','postcss-loader']
      },
      {
          test: /\.s[ac]ss$/i,
          use:  ['style-loader','css-loader','postcss-loader','sass-loader']
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ],
  externals : {
    // 只有当libraryTarget: 'umd'时,才可以配置如上那样的包含 { root, amd, commonjs,... } 的对象,其它的libraryTarget的值不能这样配置。
    vue: {
      root: "Vue",   //通过 script 标签引入,此时全局变量中可以访问的是 Vue
      commonjs: "vue",  //可以将vue作为一个 CommonJS 模块访问
      commonjs2: "vue",  //和上面的类似,但导出的是 module.exports.default
      amd: "vue"   //类似于 commonjs,但使用 AMD 模块系统
    }
  }
}

webpack抽离css style-loader 替换为 mini-css-extract-pluginwebpack抽离css

配置示例工程的babel

js
{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "my-ui",
        "styleLibrary": {
          "name": "lib-style", // same with styleLibraryName
          "base": false  // if theme package has a base.css
        }
      }
    ]
  ]
}

ESM-组件库打包纯js

使用rollup rollup从入门到打包一个按需加载的组件库

rollup.config.js

js
import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import vue from 'rollup-plugin-vue'
import autoprefixer from 'autoprefixer'
export default {
  input: "./src/index.js",
  // 一个入口打包出多种产物
  output: [
    // umd产物
    {
      file: './dist/my-lib-umd.js',
      format: 'umd',
      name: 'myLib'
    },
    // esm产物
    {
      file: './dist/my-lib-es.js',
      format: 'es'
    },
    // cjs产物
    {
      file: './dist/my-lib-cjs.js',
      format: 'cjs'
    }
  ],
  plugins:[
    babel({
        exclude: 'node_modules/**'
    }),
    // vue编译配置
    vue({
      style: {
        postcssPlugins: [
          autoprefixer()
        ]
      }
    }),
    // TODO: css抽离配置
    // rollup自身不支持cjs语法(自己写的代码尽量不使用cjs,外部库则没办法),需要插件支持
    commonjs()
  ],
  external:[  //外部库, 使用'umd'文件时需要先引入这个外部库
    'vue'
  ]
}

package.json

json
{
  "main": "dist/my-lib-cjs.js",
  "module": "dist/my-lib-es.js",
}

excemple

写示例调试页面时,可以通过npm link来直接引入资源,而不需要先写成./dist/xx

首先在组件库 package.json(配置好main指向打包后路径) 目录下初始化link npm link

再再excemle工程下 npm link my-ui package.json中定义的"name": "my-lib"

热更新问题,引入的都是打包后的库,修改源码不会实时编译,因此没有热更新效果