本文含有以下章节:

  1. vue组件库开发
  2. 环境搭建-本地预览开发环境
  3. 环境搭建-组件打包环境
  4. 环境搭建-单独css打包配置

vue组件库开发

组件库开发的目的:现在公司小项目可能会比较多,虽然用的element ui 但是还有一部分会根据自身需要进行封装增强或则针对业务化公用的封装,这样一来就会存在以下几个问题:

  1. 每个项目中都有部分通用公用的组件,要么都copy一次
  2. 维护困难,如果有bug或则新增功能,所有项目都需要重新copy一次

如果不分那么细致的话,可以连同工具类一起发布到npm中,只要所有项目依赖该组件库,就能解决上面的维护问题。

依照够用原则,放弃了element ui的构建方式,原因是学习成本比较高,后来发现 iview 组件库的方式和vue-cli生成的配置差不多,就依此为参考进行开发环境的搭建;

环境搭建思路

使用vue-cli为基础,安装好后,再修改配置文件;
百度看了好多讲解打包组件的方式,但是都是很简单配置和vue-cli脱节的的开发方式,可能学习成本来说会让人摸不着头脑。 参考了一些开源框架,最后终于大概弄明白了一些概念:

  • 开发环境和vue-cli默认生成的配置类似
  • 不同的是 组件的打包,要打包成js文件 和 css文件。
  • 发布到npm的内容是.npmignore 和 package.json决定的

以上几点明白了。就可以尝试搭建了;

环境搭建-本地预览开发环境

本地预览环境用于开发组件的时候 实时预览和调试;
目录如下:

|- rootxx                   # 项目根目录(项目名)
    |- examples             # 用于本地预览开发
        |- assets
        |- components
        |- router
        |- App.vue
        |- index.html
        |- main.js
    |- src                  # 用于组件开发   
  • examples目录: 看上面的目录结构,其实就是把原先在src下生成的目录拷贝过来了,我们的思路就是 换了一个文件夹而已。用来写测试开发。

  • src目录:用来开发通用组件的代码

修改配置文件

由于我们只是把文件目录更换了,尝试模仿原生的配置来写一下;
config/index.js

# 直接拷贝dev的代码
module.exports = {
    build:{...},  // vue-cli默认配置
    dev:{...},  // vue-cli默认配置
    // 我们模仿dev来写一套配置,直接copydev的配置过来
    examplesDev:{
        env: require('./examples.dev.env'),   // 这里环境变量配置更改为我们新增的
        port: 8080,
        autoOpenBrowser: true,
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {},
        // CSS Sourcemaps off by default because relative paths are "buggy"
        // with this option, according to the CSS-Loader README
        // (https://github.com/webpack/css-loader#sourcemaps)
        // In our experience, they generally work as expected,
        // just be aware of this issue when enabling this option.
        cssSourceMap: false
    } 
}

config/examples.dev.env.js

module.exports = {
  NODE_ENV: '"examples"'
}

最简单的配置我们模仿好了,接下来从运行命令入口进行修改
package.json中配置有启动命令的入口文件,如下

 "scripts": {
    "dev": "node build/dev-server.js",
    // 我们也需要模仿上面的定义一个文件
    "dev:examples": "node build/dev-examples-server.js",

build/dev-examples-server.js

同样的,由于我们是增加了一个目录,相当于更改了配置文件的指向路径,我们把原来的build/dev-server.js复制一份进行修改

require('./check-versions')()

var config = require('../config')
let nodeenv = process.env.NODE_ENV
if (!nodeenv) {
  // 这里需要修改成我们自己定义好的环境变量
  process.env.NODE_ENV = JSON.parse(config.examplesDev.env.NODE_ENV)
}

var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')

// 这里需要重新按照新增的环境变量判定下,然后赋值
// 为了和验证我们一开始的想法,我们只是修改了影响的部分代码,而不是直接删除以前的代码
var webpackConfig = ''
var customConfig = null
switch (nodeenv) {
  case 'development':
    webpackConfig = require('./webpack.dev.conf')
    customConfig = config.dev
    break
  case 'production':
    webpackConfig = require('./webpack.prod.conf')
    customConfig = config.build
    break
  default:
    // 这里又用到了另外一个配置文件,同样我们需要复制一份./webpack.dev.conf文件进行修改
    webpackConfig = require('./webpack.examples.dev.conf')
    customConfig = config.examplesDev
}

// 下面用到的端口代理配置什么的 都要改成新的配置对象
// default port where dev server listens for incoming traffic
var port = process.env.PORT || customConfig.port
// automatically open browser, if not set will be false
var autoOpenBrowser = !!customConfig.autoOpenBrowser
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = customConfig.proxyTable

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  quiet: true
})

var hotMiddleware = require('webpack-hot-middleware')(compiler, {
  log: () => {},
  heartbeat: 2000
})
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({action: 'reload'})
    cb()
  })
})

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = {target: options}
  }
  app.use(proxyMiddleware(options.filter || context, options))
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
var staticPath = path.posix.join(customConfig.assetsPublicPath, customConfig.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

var uri = 'http://localhost:' + port

var _resolve
var readyPromise = new Promise(resolve => {
  _resolve = resolve
})

console.log('> Starting dev server...')
devMiddleware.waitUntilValid(() => {
  console.log('> Listening at ' + uri + '\n')
  // when env is testing, don't need open it
  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
    opn(uri)
  }
  _resolve()
})

var server = app.listen(port)

module.exports = {
  ready: readyPromise,
  close: () => {
    server.close()
  }
}

build/webpack.examples.dev.conf.js

var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
var path = require('path');

// 原生的配置文件只考虑了 打包成web应用的开发和发布,他们是同一个目录
// 所以这里我要定义 不同的目录,就要先覆盖baseWebpackConfig里面的入口文件和输出目录
let entry = {
  app: './examples/main.js'
}
// add hot-reload related code to entry chunks
// 这里是热加载的配置
// 下面的代码作用完成之后把入口文件修改成了一个数组 app: [ './build/dev-client', './examples/main.js' ]
Object.keys(entry).forEach(function (name) {
  entry[name] = ['./build/dev-client'].concat(entry[name])
})

module.exports = merge(baseWebpackConfig, {
  entry: entry,
  // 输出
  // 覆盖基础配置里面的输出,把输出的目录定义到 examples/dist目录下
  output: {
    // 原生的配置里面是引用的 build里面的 config.build.assetsRoot
    path: path.join(__dirname, '../examples/dist'),
    filename: '[name].js',
    chunkFilename: '[name].chunk.js'
  },
  module: {
    // cssSourceMap 的配置也需要更改为我们新增的配置
    rules: utils.styleLoaders({sourceMap: config.examplesDev.cssSourceMap})
  },
  // cheap-module-eval-source-map is faster for development
  devtool: '#cheap-module-eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      // 环境定义也需要修改为我们定义的配置
      'process.env': config.examplesDev.env
    }),
    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      // 这里原生的默认配置都是 index.html 
      // 我尝试了一下,两个都更改为/examples/index.html的话,不能正常显示
      // 百度了一下:template是模版文件,指定到新的目录下的index.html 配置是没有错的
      // filename : 输出的文件,原生默认配置里面的没有看懂为什么可以,但是这里写一样的就不行
      // 可能必须写到上面output.path的输出目录中去才可以吧
      filename: path.join(__dirname, '../examples/dist/index.html'),
      template: path.join(__dirname, '../examples/index.html'),
      inject: true
    }),
    new FriendlyErrorsPlugin()
  ]
})

上面的文件中都用到了build/utils.js工具类里面也有一些基于环境变量做出的配置;

其他的配置还有和环境变量相关的,但是并不是特别的重要,就不列出来了

// 最主要修改的是 这里的路径,按照环境变量判定
exports.assetsPath = function (_path) {
  let nodeenv = process.env.NODE_ENV
  var assetsSubDirectory = ''
  switch (nodeenv){
    case 'development':
      assetsSubDirectory = config.dev.assetsSubDirectory
      break
    case 'production':
      assetsSubDirectory = config.build.assetsSubDirectory
      break
    default:
      assetsSubDirectory = config.examplesDev.assetsSubDirectory
  }
  return path.posix.join(assetsSubDirectory, _path)
}

最后:我们运行 npm run dev:examples 就能看到久违的开发环境了,热更新也有效果。

其他章节请移步本人Gitbook

https://zq99299.gitbooks.io/vue-note/content/chapter/vu_components_lib/

教程种子项目,该分支是一步一步搭建的最终教程项目

https://git.oschina.net/zhuqiang/vue-components-lib-dev-env/tree/tutorials

Logo

前往低代码交流专区

更多推荐