Vue多組件倉庫開發與發佈

在開發組件時,咱們可能會指望一類組件放在同一個代碼倉庫下,就像element那樣,咱們可使用element提供的腳手架,也可使用vue cli 3建立一個更‘新’的項目。javascript

項目建立

經過vue cli 3建立項目,建立文件夾packages用於存放組件。html

單個組件目錄

packages下就是每個組件,每一個組件和單獨項目同樣,會有package.jsonREADME.mdsrcdist等文件及目錄。vue

如何演示/調試組件

在組件開發過稱中,咱們須要對組件進行展現,因此建立了examples文件夾,用於存放每一個組件示例。
經過一個列表展現出全部的組件,點擊選擇當前開發的組件,進入對應的example
路由的根就是一個導航列表,而後每一個組件對應一個路由,經過一個配置文件的components.js來生成這個路由。java

// 路由
import Navigation from "./Navigation";
import components from "./components";

let routes = components.map(component => ({
  path: `/${component.name}`,
  component: () => import(`../examples/${component.name}`)
}));

routes.unshift({
  path: "",
  component: Navigation
});

export default routes;
複製代碼

自動化腳本

建立/編譯/發佈

建立新的組件,須要修改components.js配置文件,在examplespackages下建立對應目錄。
編譯/發佈組件,由於倉庫下會有多個組件,若是一次發佈多個,就須要進入每一個文件夾下執行命令。
上面過程實現自動化,有不少種方式,好比能夠經過npm run <script>,能夠直接經過node命令等。這裏我參考element,採用了Makefile。node

建立script文件夾,其中包括建立腳本new.js和構建腳本build.jsnpm

建立腳本

建立腳本主要就是目錄的建立與文件的寫入,其中可能須要注意的可能就是格式問題。
一種方式是在``之間,按照規範格式去完成寫入內容,這樣作比較麻煩,並且可能面臨格式化要求修改問題。
另外一種方式是在腳本中引入eslint,腳本中的eslint.CLIEngine能夠根據配置文件(好比.eslintrc.js)格式化文件。須要注意的是須要比命令行中配置須要多添加fix: true配置, 以下json

const CLIEngine = eslint.CLIEngine;
const cli = new CLIEngine({ ...require("../.eslintrc.js"), fix: true });
複製代碼

eslint在腳本中的使用方法,更具體的能夠參考eslint文檔中Node.js API部分api

// scripts/new.js部分
...

components.push({
  label: newName,
  name: newName
})

const updateConfig = function(path, components) {
  writeFile(path, `module.exports = ${JSON.stringify(components)}`).then(() => {
    console.log("完成components.js")
    // 格式化
    CLIEngine.outputFixes(cli.executeOnFiles([configPath]))
  })
}

const createPackages = function(componentName) {
  try {
    const dir = path.resolve(__dirname, `../packages/${componentName}/`)
    // 建立文件夾
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir)
      console.log(`完成建立packages/${componentName}文件夾`)
    }
    // 寫入README
    if (!fs.existsSync(`${dir}/README.md`)) {
      writeFile(
        `${dir}/README.md`,
        `## ${componentName} ### 使用說明 `
      ).then(() => {
        console.log("完成建立README")
      })
    }
    // 寫入package.json
    if (!fs.existsSync(`${dir}/package.json`)) {
      writeFile(
        `${dir}/package.json`,
        `{ "name": "@hy/${componentName}", "version": "1.0.0", "description": "${componentName}", "main": "./dist/hy-${componentName}.umd.min.js", "keywords": [ "${componentName}", "vue" ], "author": "", "license": "ISC" } `
      ).then(() => {
        console.log("完成建立package.json")
      })
    }
    // 建立index.js
    if (!fs.existsSync(`${dir}/index.js`)) {
      writeFile(`${dir}/index.js`, `export {}`).then(() => {
        console.log("完成建立index.js")
        CLIEngine.outputFixes(cli.executeOnFiles([`${dir}/index.js`]))
      })
    }
  } catch (err) {
    console.error(err)
  }
}

const createExample = function(componentName) {
  try {
    const dir = path.resolve(__dirname, `../examples/${componentName}/`)
    // 建立文件夾
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir)
      console.log(`完成建立examples/${componentName}文件夾`)
    }
    // 寫入index.vue
    if (!fs.existsSync(`${dir}/index.vue`)) {
      writeFile(
        `${dir}/index.vue`,
        `<template> </template> <script> import { } from '../../packages/${componentName}/index' export default { components: {} } </script> `
      ).then(() => {
        console.log(`完成建立examples/${componentName}/index.vue文件`)
        // 格式化index.vue
        CLIEngine.outputFixes(cli.executeOnFiles([`${dir}/index.vue`]))
      })
    }
  } catch (err) {
    console.error(err)
  }
}

...
複製代碼

構建腳本

// build.js
...

async function build() {
  for (let i = 0, len = components.length; i < len; i++) {
    const name = components[i].name
    await buildService.run(
      "build",
      {
        _: ["build", `${root}/packages/${name}/src/index.js`],
        target: "lib",
        name: `hy-${name}`,
        dest: `${root}/packages/${name}/dist`,
        // 生成格式: umd格式會同時成功demo.html commonjs,umd,umd-min
        formats: "commonjs,umd-min"
        // clean: false
      },
      ["--target=all", `./packages/${name}/src/index.js`]
    )
  }
}

...
複製代碼

Lerna

lerna是一個多包倉庫管理的工具,能夠幫助建立、管理、發佈多包倉庫中的包。
關於lerna我也沒有太深刻得使用,只是用到了發佈。首先在項目下執行init初始化了項目,在每次commit以後,能夠執行publishlerna會對應代碼庫打tag,併發布到npm倉庫。併發

項目版本問題

0.0.1爲不規範版本號,最小應該從1.0.0開始。npm publish沒法發佈,可是lerna publish能夠發佈。
致使結果安裝爲固定版本號,而不是以^開頭的版本號範圍。outdate能夠檢測到有更新,沒法經過update升級。async

組件開發

組件開發主要是在packages/<component name>/src目錄下進行,在example/<component name>/目錄下能夠引入該組件src下的源文件,用一些數據來進行開發測試。組件開發和項目中的組件開發基本相同。
做爲組件庫中的組件,須要更多的考慮其通用性和易用性。不能爲了通用而加入不少的屬性,而使其失去易用性;一樣也不能爲了易用,而使其過於簡單,使用範圍過於侷限。
對於每個屬性、每一個拋出去的方法,都須要認真考慮其必要性。

惟一不一樣的地方可能須要注意的是導出的方式。
一種是直接導出組件,這種形式在使用時須要引入,而且在components中聲明,也就是局部註冊。
另外一種是添加install方法後導出。這種形式須要調用vue.use方法,至關於全局註冊。

相關文章
相關標籤/搜索