這篇文章主要介紹了淺談Vue CLI 3結合Lerna進行UI框架設計,在此之前先簡單介紹一下Element的構建流程,以便對比新的UI框架設計。小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
當前大部分UI框架設計的Webpack配置都相對複雜,例如 Element 、 Ant Design Vue 和Muse-UI等Vue組件庫。例如Element,爲了實現業務層面的兩種引入形式( 完整引入 和 按需引入 ),以及拋出一些可供業務層面通用的 utils
、 i18n
等,Webpack配置變得非常複雜。爲了簡化UI框架的設計難度,這裏介紹一種簡單的UI框架設計,在此之前先簡單介紹一下 Element 的構建流程,以便對比新的UI框架設計。
一般組件庫的設計者將引入形式設計成 完整引入 和 按需引入 兩種形式: 完整引入 的開發相對便利,針對一些大型業務或者對於打包體積不是特別注重的業務, 按需引入 開發的顆粒度相對精細,可以減少業務的打包體積。
設計的UI框架實踐項目的github地址是ziyi2/vue-cli3-lerna-ui,包括了preset.json、自己設計的Vue CLI插件以及自己設計的一系列UI組件(和生成的UI框架示例稍有不同),如果覺得整體結構有不合理的或者考慮不夠全面的地方,歡迎大家提issue,這樣我也可以對它進行完善。如果大家感興趣,希望大家能夠Star一下,這裏拜謝大家了!
Element
首先了解 Element
的構建流程,查看 Element 2.7.0
版本 package.json
的npm 腳本 :
// 其中的`node build/bin/build-entry.js` 生成Webpack構建入口 "build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js", // 構建css樣式 "build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk", // 構建commonjs規範的`utils` "build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js", // 構建umd模塊的語言包 "build:umd": "node build/bin/build-locale.js", // 清除構建文件夾`lib` "clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage", // 總體構建 "dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme", // 執行eslint校驗 "lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet"
這裏重點關注Element的構建腳本,忽略 測試、發佈、啓動開發態調試頁面、構建演示頁面 等腳本。
npm run dist
與 Element
構建相關的npm腳本繁多,但是 總體構建腳本 是 dist
:
"dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme"
&&
是繼發執行,只有當前任務成功,才能執行下一個任務。
總體構建腳本包含了以下按順序執行的腳本命令
- npm run clean - 清除構建文件夾lib
- npm run build:file - 其中的node build/bin/build-entry.js 生成Webpack構建入口
- npm run lint - 執行eslint校驗
- webpack --config build/webpack.conf.js - 構建umd總文件
- webpack --config build/webpack.common.js - 構建commonjs2總文件
- webpack --config build/webpack.component.js - 構建commonjs2組件(提供按需引入)
- npm run build:utils - 構建commonjs的utils(供commonjs2總文件、commonjs2組件以及業務使用)
- npm run build:umd - 構建umd語言包
- npm run build:theme - 構建css樣式
如果對於對於 umd
、 commonjs2
、 amd
等模塊定義不是特別清晰,可參考Webpack文檔模塊定義系統。
執行 npm run dist
後會在當前根目錄生成新的 lib
文件夾,包含以下構建內容:
lib ├── directives # commonjs指令(這裏歸爲utils) ├── locale # commonjs國際化(commonjs語言包和API) ├── mixins # commonjs mixins(這裏歸爲utils) ├── theme-chalk # css 樣式文件 ├── transitions # commonjs transitions(這裏歸爲utils) ├── umd # umd語言包 ├── utils ├── alert.js # commonjs組件 ├── aside.js ├── ... ├── element-ui.common.js # commonjs2總文件 ├── ... ├── index.js # umd總文件 ├── ...
從Element官方文檔的使用指南結合 lib
可以看出, Element
爲我們提供了以下能力:
1、CDN引入(umd 總文件)
2、npm包完整引入(拋出commonjs2總文件)
3、按需引入(拋出commonjs2的所有UI組件)
4、支持國際化
5、提供utils方法(官方文檔沒有說明,但事實上業務可以使用)
CDN引入的umd總文件一般是全量構建的,不會有依賴問題,但是commonjs2模塊的文件需要在業務層面再次使用Webpack構建。例如需要在業務層面支持 國際化 和 提供utils 的功能,那麼就不能將 國際化 和 提供utils 的代碼 bundle 到 commonjs2總文件 或 commonjs2的所有UI組件 中(每一個組件都 bundle utils
的方法或者國際化API顯然是不合理的),如果需要在業務層面支持 按需引入 的功能,那麼不建議將 所有UI組件 的源碼 bundle 到 commonjs2總文件 中,這樣便可以實現層層引用,對外拋出功能的同時在業務層面可以防止Webpack二次打包,從而導致引入兩遍甚至多遍相同的代碼的問題。
在組件庫中開發時,爲了構建commonjs2模塊的文件,需要對各個 utils
、組件等引入的路徑做出強約定,這樣不僅產生的Webpack配置會變得很難維護,對於開發者的開發也需要做出一定的規範限制。
接下來分析一下各個腳本的構建功能。
npm run build:file
build:file
腳本是自動生成一些源碼文件的腳本:
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
其中與構建相關的腳本是 node build/bin/build-entry.js
,主要用於生成Webpack構建的入口源文件 src/index.js
:
// 註釋說明該文件由build-entry.js腳本自動生成 /* Automatically generated by './build/bin/build-entry.js' */ import Pagination from '../packages/pagination/index.js'; // ... 這裏省略大部分組件引入 import TimelineItem from '../packages/timeline-item/index.js'; import locale from 'element-ui/src/locale'; import CollapseTransition from 'element-ui/src/transitions/collapse-transition'; const components = [ Pagination, // ... 這裏省略大部分組件 TimelineItem, CollapseTransition ]; const install = function(Vue, opts = {}) { locale.use(opts.locale); locale.i18n(opts.i18n); components.forEach(component => { Vue.component(component.name, component); }); Vue.use(Loading.directive); Vue.prototype.$ELEMENT = { size: opts.size || '', zIndex: opts.zIndex || 2000 }; Vue.prototype.$loading = Loading.service; // ... }; /* istanbul ignore if */ if (typeof window !== 'undefined' && window.Vue) { install(window.Vue); } export default { version: '2.7.0', locale: locale.use, i18n: locale.i18n, install, CollapseTransition, Loading, Pagination, // ... 這裏省略大部分組件 TimelineItem };
在組件的開發過程中如果組件較多,建議使用腳本自動生成構建入口文件。
npm run lint
構建之前使用 lint
腳本對構建的源碼文件進行 eslint
校驗:
"lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",
Element
對 eslint
做了嚴格控制,一旦 eslint
報錯那麼 dist
總體構建腳本 執行停止,整體構建失敗。這裏的 eslint
校驗可以使用eslint-loader 進行處理(如果你希望 eslint
校驗失敗也可以進行構建可以查看Errors and Warning )。
webpack --config build/webpack.conf.js
webpack --config build/webpack.conf.js
腳本用於構建umd總文件,執行該腳本最終會在 lib
下生成 index.js
文件:
lib ├── index.js # umd 總文件
webpack.conf.js
配置如下:
// build/webpack.conf.js // ...忽略 module.exports = { mode: 'production', // 指定入口文件src/index.js,該入口文件由`build:file`腳本自動生成 entry: { app: ['./src/index.js'] }, output: { // 在lib文件中生成 path: path.resolve(process.cwd(), './lib'), // 生成lib/index.js filename: 'index.js', // 生成umd模塊 libraryTarget: 'umd', // src/index.js文件採用export default語法拋出,因此需要設置libraryExport // 否則引入的UI組件庫需要使用.default才能引用到拋出的對象 // if your entry has a default export of `MyDefaultModule` // var MyDefaultModule = _entry_return_.default; // 這裏踩過坑,所以說明一下,不配置的話遇到的問題是引入的UI組件庫沒法解構 libraryExport: 'default', }, resolve: { extensions: ['.js', '.vue', '.json'], // 'element-ui': path.resolve(__dirname, '../') // alias中的'element-ui'作爲npm包拋出後指向了業務項目node_modules所在的npm包路徑 alias: config.alias }, externals: { // 構建只排除vue // umd模塊通過CDN形式引入,因此將所有的組件、utils、i18n等構建在內 // umd模塊沒有按需引入功能 vue: config.vue }, // ...忽略 };
構建文件 lib/index.js
主要的功能是用於CDN形式引入項目,並且無法做到按需加載,產生的體積非常大,對於簡單的應用可能不適用。
webpack --config build/webpack.common.js
webpack --config build/webpack.common.js
腳本用於構建commonjs2總文件,執行該腳本最終會在 lib
下生成 element-ui.common.js
文件:
lib ├── element-ui.common.js # commonjs2 總文件
由於該文件需要在業務層面再次使用Webpack構建,因此考量的方面較多。在分析Webpack配置之前,再次回顧一下 Element
能爲我們做什麼:
1、完整引入(拋出commonjs2總文件)
2、按需引入(拋出commonjs2的所有UI組件)
3、支持國際化(commonjs2)
4、提供utils方法(commonjs2,當然官方沒有對外說明)
webpack --config build/webpack.common.js
腳本主要用於構建完整引入功能,同時爲了可以在業務層面拋出 按需引入、支持國際化 等功能,構建 element-ui.common.js
時需要將 UI組件、支持國際化、utils方法 的源代碼排除。
webpack.common.js
配置如下:
// build/webpack.common.js // ...忽略 module.exports = { mode: 'production', entry: { app: ['./src/index.js'] }, output: { path: path.resolve(process.cwd(), './lib'), publicPath: '/dist/', filename: 'element-ui.common.js', chunkFilename: '[id].js', libraryExport: 'default', library: 'ELEMENT', // 生成commonjs2模塊 libraryTarget: 'commonjs2' }, resolve: { extensions: ['.js', '.vue', '.json'], // 'element-ui': path.resolve(__dirname, '../') alias: config.alias, modules: ['node_modules'] }, // 這裏用於排除UI組件、支持國際化、utils方法的源代碼,這些源代碼需要額外的腳本進行構建 externals: config.externals, optimization: { // commonjs2無須壓縮處理 minimize: false }, // ...忽略 };
重點需要關注一下 config.externals
屬性,打印輸出該變量的值:
[{ vue: 'vue', // 排除所有UI組件的源代碼 'element-ui/packages/option':'element-ui/lib/option', // ... // 排除國際化的源代碼 'element-ui/src/locale': 'element-ui/lib/locale', // 排除utils方法的源代碼 'element-ui/src/utils/vue-popper': 'element-ui/lib/utils/vue-popper', 'element-ui/src/mixins/emitter': 'element-ui/lib/mixins/emitter', 'element-ui/src/transitions/collapse-transition': 'element-ui/lib/transitions/collapse-transition' // ... }, // var nodeExternals = require('webpack-node-externals'); // nodeExternals() [Function] ];
externals屬性可以將一些特定的依賴從輸出的bundle中排除,例如在開發態中組件之間有依賴關係, element-ui/packages/pagination
中引入 element-ui/packages/option
組件:
pagecages/pagination/src/pagination.js
// pagination組件中需要用到option組件 import ElOption from 'element-ui/packages/option'; // ...
Webpack構建後,可以發現在 element-ui.common.js
中並沒有將 element-ui/packages/option
組件打包在內,而只是更改了它的引入路徑 element-ui/lib/option
(在實現 按需引入 功能時會用 webpack --config build/webpack.component.js
腳本構建出該文件)。
// lib/element-ui.common.js module.exports = require("element-ui/lib/option");
因此以上列出的 config.externals
屬性的 key
和 value
可以排除 UI組件、支持國際化、utils方法 功能的代碼。
config.externals
屬性的最後一個值是 [Function]
,是由webpack-node-externals 生成的。這裏解釋一下 webpack-node-externals
的作用:
Webpack allows you to define externals - modules that should not be bundled. When bundling with Webpack for the backend - you usually don't want to bundle its node_modules dependencies. This library creates an externals function that ignores node_modules when bundling in Webpack.
例如在 Elment
組件庫開發中需要依賴 deepmerge
,那麼Webpack構建的時候不需要將該依賴bundle到 element-ui.common.js
中,而是將其添加到 Element
組件庫(作爲npm包發佈)的 dependencies
,這樣通過npm安裝 Element
的同時也會安裝它的依賴 deepmerge
,從而使得 element-ui.common.js
通過 require("deepmerge")
的形式引入該依賴不會報錯。
這裏列出 element-ui.common.js
排除的一些代碼:
// 排除utils源碼(utils源碼會通過`npm run build:utils`腳本構建) module.exports = require("element-ui/lib/utils/dom"); // 排除vue module.exports = require("vue"); // 排除國際化源碼(國際化源碼會通過`npm run build:utils`腳本構建) module.exports = require("element-ui/lib/locale"); // 需要注意和Vue相關的JSX依賴(Vue CLI3系統構建的包也會有一個該功能的依賴) module.exports = require("babel-helper-vue-jsx-merge-props"); // 排除一些Elment組件使用的其他依賴 module.exports = require("throttle-debounce/throttle"); // 排除UI組件源碼(UI組件源碼會通過`webpack --config build/webpack.component.js`腳本構建) module.exports = require("element-ui/lib/option");
需要注意 Element
發佈的npm包入口文件就是 element-ui.common.js
,可以通過package.json中的 main
字段信息查看。
webpack --config build/webpack.component.js
webpack --config build/webpack.component.js
腳本用於構建commonjs2的UI組件(提供按需引入功能),執行該腳本最終會在 lib
下生成所有 Element
支持的UI組件(同時這些文件也會被 element-ui.common.js
總入口文件引用):
lib ├── alert.js # commonjs 組件 ├── aside.js ├── button.js ├── ...
查看 build/webpack.component.js
配置:
// ...忽略 const Components = require('../components.json'); // Components是所有組件的構建入口列表 // { // "pagination": "./packages/pagination/index.js", // ... // "timeline-item": "./packages/timeline-item/index.js" // } const webpackConfig = { mode: 'production', // 多入口 entry: Components, output: { path: path.resolve(process.cwd(), './lib'), publicPath: '/dist/', filename: '[name].js', chunkFilename: '[id].js', libraryTarget: 'commonjs2' }, resolve: { extensions: ['.js', '.vue', '.json'], alias: config.alias, modules: ['node_modules'] }, // 排除其他UI組件、支持國際化、utils的源碼,這些源碼會額外構建 externals: config.externals, }, // ...忽略 };
構建單個組件和構建總體入口文件 element-ui.common.js
的Webpack配置類似,需要將 utils
、 locale
以及其他一些依賴排除。
npm run build:utils
build:utils
腳本主要用於構建commonjs的 utils
(提供國際化以及 utils
功能):
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
可以發現該命令並不是通過Webpack進行多文件構建,而是通過Babel直接進行轉義處理(Webpack構建會產生額外的Webpack代碼,並且配置繁瑣,Babel轉義處理構建的代碼非常乾淨),將 src
目錄下除了Webpack構建入口文件 src/index.js
以外的所有其他文件進行轉義處理。執行該腳本最終會在 lib
下生成所有的 utils
文件:
lib ├── directives # commonjs 指令 ├── locale # commonjs 國際化API和語言包 ├── mixins # commonjs 混入 ├── transitions # commonjs 過度動畫 ├── utils # commonjs 工具方法
生成的這些工具方法會被 lib
下的 element-ui.common.js
和各個組件引用,同時在業務層面也可以引用這些工具方法。查看 .babelrc
文件的配置信息:
{ "presets": [ [ "env", { "loose": true, "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } } ], "stage-2" ], "plugins": ["transform-vue-jsx"], "env": { // cross-env BABEL_ENV=utils "utils": { "presets": [ [ "env", { // 鬆散模式,更像人手寫的ES5代碼 "loose": true, // es6轉成commonjs "modules": "commonjs", "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } } ], ], "plugins": [ ["module-resolver", { "root": ["element-ui"], "alias": { // 類似於Webpack的externals功能 // 將源代碼的引入路徑更改成目標代碼的引入路徑 "element-ui/src": "element-ui/lib" } }] ] }, "test": { "plugins": ["istanbul"] } } }
utils
文件源代碼之間互相引用的路徑是 element-ui/src
,轉義成目標代碼後互相之間的引用路徑是 element-ui/lib
,因此需要有類似於Webpack的 externals
的功能去更改目標代碼的引用路徑,進行Babel轉義時插件babel-plugin-module-resolver 可以實現該功能。
npm run build:theme
build:theme
腳本主要用於構建UI組件的css樣式:
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
這裏主要關注 gulp build --gulpfile packages/theme-chalk/gulpfile.js
腳本,該腳本使用Gulp構建工具構建css樣式文件,Glup構建多文件樣式會非常簡單。最終將當前構建的 packages/theme-chalk/lib
目錄下的內容拷貝到 lib/theme-chalk
目錄下供外部業務使用:
lib ├── theme-chalk # css 樣式文件 │ ├── fonts # icons │ ├── alert.css # 按需引入的組件樣式 │ ├── ... # 按需引入的組件樣式 │ └── index.css # 完整引入樣式
查看 gulpfile.js
文件:
'use strict'; const { series, src, dest } = require('gulp'); const sass = require('gulp-sass'); const autoprefixer = require('gulp-autoprefixer'); const cssmin = require('gulp-cssmin'); function compile() { return src('./src/*.scss') // sass轉化成css .pipe(sass.sync()) // Parse CSS and add vendor prefixes to rules by Can I Use // css瀏覽器兼容處理 .pipe(autoprefixer({ browsers: ['ie > 9', 'last 2 versions'], cascade: false })) // 壓縮css .pipe(cssmin()) .pipe(dest('./lib')); } function copyfont() { return src('./src/fonts/**') .pipe(cssmin()) .pipe(dest('./lib/fonts')); } exports.build = series(compile, copyfont);
Vue CLI 3 & Lerna
構建整個 Element
組件庫的腳本繁多,構建的代碼之間互相還有引用關係,對於開發的引用路徑也會產生一定的約束。因此設計類似於 Element
的UI框架相對開發者而言需要一定的開發門檻。
這裏基於Vue CLI 3的 開發/構建目標/庫 能力以及 Lerna 工具設計了一個UI框架,這個UI框架集成了以下特點:
1、 結構特點 :每個UI組件都是一個npm包, 多語言、工具和樣式 都是自成體系的npm包,可被業務或UI組件靈活引用,同時天然按需加載。
2、 配置特點 :如果需要進行構建處理,那麼每個npm包可單獨進行構建配置,配置變得更加簡單。結合Vue CLI3的 構件庫 能力,對於簡單UI組件的構建幾乎可以做到webpack零配置,當然需要特殊的webpack loader除外。
3、 發佈特點 :組件庫的版本迭代可以更快,不需要進行整體構建,每個組件可單獨快速發佈 PATCH
或 MINOR
版本。
這裏設定業務層面需要進行webpack構建處理,因此可以對UI框架的組件不進行構建處理,當然如果UI組件的設計需要特殊的webpack loader處理除外,否則業務層面需要做額外的webpack配置。當然不構建處理是相對於一定的使用場景的,不構建處理可能也會產生額外的一些問題。
這個UI框架的設計也會有一些缺陷:
1、沒有完整引入功能(也可以進行整體構建,但是這裏不推薦)
2、不提供UMD模塊
3、業務層面引入繁瑣(可以出額外的引入工具,簡化業務中的UI組件引入)
Vue CLI 3
構建庫
爲了簡化UI框架的webpack配置,這裏將Vue CLI 3作爲開發的容器引入,借用Vue CLI 3的構建庫功能( 構建web-components-組件 功能應該更合適,這裏沒有進行驗證),幾乎可以做到UI組件構建的零配置。通過審查項目的-webpack-配置 能力,可以查看Vue CLI 3爲我們預先設置的通用webpack配置(幾乎可以滿足大部分的UI組件構建)。
插件體系
這裏使用Vue CLI 3的插件和Preset功能開發了幾個插件,以便於快速構建起步的UI設計框架,具體的 preset.json 配置如下:
{ "useConfigFiles": true, "router": true, "routerHistoryMode": true, "vuex": false, "cssPreprocessor": "less", // MAC OS X下生效,Windows下不生效,具體未深入研究 "packageManager": "yarn", "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "lintOn": ["save", "commit"] }, "@ziyi2/vue-cli-plugin-ui-base": {}, "@ziyi2/vue-cli-plugin-ui-cz": {}, "@ziyi2/vue-cli-plugin-ui-lint": {} } }
這裏採用了官方設計的 @vue/cli-plugin-babel 和 @vue/cli-plugin-eslint 插件,同時自己設計了額外的三個插件來支持整個新的UI框架的起步:
@ziyi2/vue-cli-plugin-ui-base
:UI框架基礎插件,生成Monorepo結構的源碼目錄(加入Lerna管理工具),生成基礎通用的webpack配置(在VUE CLI 3的webpack配置上進行再配置,VUE CLI3提供了 vue.config.js
文件供開發者進行webpack再配置),提供了幾個基礎UI組件的示例(僅參考價值)。
@ziyi2/vue-cli-plugin-ui-cz
: UI框架的 cz 適配器插件,加入了 cz-customizable 、 commitlint 、 conventional-changelog ,用於生成Angular規範的Git提交說明、檢測提交說明是否符合規範以及自動生成UI框架的升級日誌等。
@ziyi2/vue-cli-plugin-ui-lint
:UI框架的lint-staged 插件,代碼提交前會執行Eslint校驗,校驗不通過則不允許提交辣雞代碼。
這三個插件已經發布在npm的倉庫裏,如果是已有的Vue CLI 3項目,可直接通過 vue add @ziyi2/ui-cz
等命令進行安裝使用,插件源碼地址ziyi2/vue-cli3-lerna-ui/plugins ,如果想學習設計Vue CLI 3插件,可參考插件開發指南,不過官方文檔可能不夠詳細,建議參考官方設計的插件或者別人設計的優秀插件。
Lerna
Lerna是一個Monorepo管理工具,使所有的組件(npm包)設計都集成在一個git倉庫裏,同時可以利用yarn的workspace特性,模擬發佈的組件環境,從而使組件的開發和測試變得簡單,不需要多次進行組件的發佈測試(這裏用Lerna進行Vue CLI插件開發也非常方便)。
同時Lerna還集成了版本發佈工具,可以快速的對UI框架進行版本發佈。
UI組件各自修復問題可以各自快速發佈 patch
版本,如果UI組件整體有非兼容性更新,可以利用Lerna進行 MAJOR
版本發佈,更多關於版本發佈規範可查看語義化版本。
UI框架實踐
利用Vue CLI 3的遠程Preset,這裏將自己設計的UI框架分享給大家進行實踐使用:
// 可能獲取會有點慢,大家耐心等待 vue create --preset ziyi2/vue-cli3-lerna-ui my-project
如果報錯 unable to get local issuer certificate
,可以設置 git config --global http.sslVerify false
。
如果遠程確實獲取preset.json失敗,可以採用本地的方式,將preset.json 配置複製下來,放入新建的 preset.json
文件,執行以下命令生成UI框架:
vue create --preset preset.json my-project
執行後的生成過程如下:
腳本命令
// 啓動開發服務 "serve": "vue-cli-service serve", // 生成靜態資源 "build": "vue-cli-service build", // Eslint校驗 "lint": "vue-cli-service lint", // 安裝和鏈接Lerna repo的依賴 "bootstrap": "lerna bootstrap", // 更新升級日誌 "cz:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md", // 構建 "lib": "lerna run lib"
如果需要利用GitHub Pages發佈靜態資源,可以新增命令 "deploy": "npm run build && gh-pages -d dist"
,需要安裝 gh-page
依賴。
啓動
進入項目目錄,使用 yarn serve
命令啓動開發態視圖,如果是Windows系統,可能會報以下錯誤:
在Windows下 vue create
可能會採用npm進行依賴安裝,MAC OS X下無此問題,此時需要額外使用yarn進行再一次安裝操作(這裏使用了yarn的workspace特性,因此安裝依賴建議都使用yarn進行操作):
lerna bootstrap
執行 yarn serve
:
這裏給出了國際化、選擇器、警告以及按鈕等UI設計示例。
構建
執行 lerna run lib
後(構建可以配合 npm run lint
校驗,校驗不通過則構建失敗),Lerna工具會對每一個npm包執行 lib
腳本:
這裏分別對 utils
、 btn
、 theme
包進行了構建處理,其中 btn
採用了Vue CLI 3默認的構建庫配置。
Monorepo結構
UI框架生成並構建後的Monorepo結構如下:
. ├── packages # workspaces │ ├── alert # 警告(不構建) │ │ ├── alert.vue # 組件源碼 │ │ ├── index.js # npm包入口文件 │ │ └── package.json # npm包描述文件 │ ├── btn # 按鈕 │ │ ├── lib # 目標文件 │ │ │ └── lib.common.js # npm包入口文件 │ │ ├── btn.vue # 組件源碼 │ │ ├── index.js # 構建入口文件 │ │ ├── package.json # npm包描述文件(需要vue cli的開發態依賴) │ │ └── vue.config.js # 構建配置文件 │ ├── locale # 國際化 │ │ ├── lang # 語言包 │ │ │ ├── enjs # 英文 │ │ │ └── zh_CN.js # 中文 │ │ ├── mixins # 各個組件調用的國際化API │ │ ├── src # 源碼 │ │ ├── index.js # npm包入口文件 │ │ ├── alert.vue # 組件源碼 │ │ ├── index.js # npm包入口文件 │ │ └── package.json # npm包描述文件 │ ├── select # 選擇器(類似於alert) │ ├── theme # 樣式 │ │ ├── lib # 目標文件 │ │ │ ├── alert.css # 警告樣式 │ │ │ ├── btn.css # 按鈕樣式 │ │ │ ├── index.css # 總體樣式 │ │ │ └── select.css # 選擇器樣式 │ │ ├── src # 源文件 │ │ │ ├── utils # 通用方法和變量 │ │ │ ├── alert.less # 警告樣式 │ │ │ ├── btn.less # 按鈕樣式 │ │ │ ├── index.less # 總體樣式 │ │ │ └── select.less # 選擇器樣式 │ │ ├── gulpfile.js # 構建配置文件 │ │ └── package.json # npm包描述文件 │ └── utils # 工具方法 │ ├── lib # 目標文件(這裏也可以採用lodash的方式,去掉lib文件夾這一層) │ ├── src # 源文件 │ ├── babel.config.js # 構建配置文件 │ └── package.json # npm包描述文件 ├── public # 公共資源目錄 ├── src # 開發態目錄 ├── .browserslistrc # UI框架目標瀏覽器配置 ├── .cz-config.js # cz定製化提交說明配置 ├── .gitignore # git忽略配置 ├── .lintstagedrc # lint-staged配置 ├── babel.config.js # vue cli的babel配置 ├── lerna.json # lerna配置 ├── package.json # vue cli容器描述文件(容器不是npm包) ├── postcss.config.js # postcss配置 ├── README.md # 說明 └── vue.common.js # 通用的組件構建配置文件
這裏重點說明 src
文件, src
文件可以根據開發需要自行選定方案:
1、使用默認的CLI服務進行開發和UI框架Demo演示,這裏UI框架採用原生的 .vue
文件形式進行Demo演示,如果想使用 .md
文件進行演示,可以採用vue-markdown-loader 。
2、使用Vue 驅動的靜態網站生成器VuePress,這個目前不是很穩定。 發佈
發佈
完全可以按照語義化版本進行:
1、各自npm包可以使用 npm publish
快速發佈 MINOR
和 PATCH
版本。
2、如果某個npm包有非兼容性更新,那麼可以使用 lerna publish
發佈 MAJOR
版本。
使用Lerna工具發佈的npm包建議採用scope的形式發佈,UI框架示例沒有給出Demo,如果想採用scope形式發佈可以查看 ziyi2/vue-cli3-lerna-ui ,需要在每個npm包的 package.json
中做額外的配置,具體可查看 vue-cli3-lerna-ui/plugins/vue-cli-plugin-ui-base/package.json 的 publishConfig
字段信息。
總結
對比Element的UI框架設計,採用Vue CLI 3 & Lerna的形式可以簡化UI框架的配置,使各個UI組件的構建配置互相獨立,對於簡單的UI組件可以利用Vue CLI 3的默認webpack配置。同時採用Monorepo的設計結構( Why is Babel a monorepo? ),配合Lerna工具,可以使得UI框架修復問題和發佈新功能的響應能力變得更快。
生成UI框架實踐項目的github地址是 ziyi2/vue-cli3-lerna-ui ,包括了 preset.json
、自己設計的Vue CLI插件以及自己設計的一系列UI組件(和生成的UI框架示例稍有不同),如果覺得整體結構有不合理的或者考慮不夠全面的地方,歡迎大家提issue,這樣我也可以對它進行完善。如果大家感興趣,希望大家能夠Star一下,這裏拜謝大家了!
參考鏈接
- Element - github
- npm scripts 使用指南 - 阮一峯
- umd - github
- Element官方文檔
- eslint - eslint文檔
- Module Definition Systems - Webpack文檔
- eslint-loader - github
- webpack-node-externals - github
- Externals - Webpack文檔
- babel-plugin-module-resolver - github
- Gulp - Gulp文檔
- Vue CLI 3/開發/構建目標/庫 - Vue CLI文檔
- Vue CLI 3/開發/webpack相關/審查項目的webpack配置 - Vue CLI文檔
- Vue CLI 3/基礎/插件和Preset - Vue CLI文檔
- @vue/cli-plugin-babel - Vue CLI Plugin
- @vue/cli-plugin-eslint - Vue CLI Plugin
- cz - Git提交說明工具
- cz-customizable - Cz適配器,自定義說明
- commitlint - Cz適配器,提交說明檢測
- conventional-changelog - Cz適配器,生成日誌
- lint-staged - 代碼提交審覈工具
- 插件開發指南 - Vue CLI文檔
- 語義化版本 - 版本發佈規範
- Vue CLI3/基礎/CLI服務 - Vue CLI文檔
- Why is Babel a monorepo?
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持神馬文庫。