關(guān)于Vue組件庫(kù)開(kāi)發(fā)詳析
前言
2017年是Vue.js大爆發(fā)的一年,React迎來(lái)了一個(gè)強(qiáng)有力的競(jìng)爭(zhēng)對(duì)手,王者地位受到挑戰(zhàn)(撰寫(xiě)此文時(shí)github上Vue與React的star數(shù)量已逼近)。我們團(tuán)隊(duì)這一年有十多個(gè)大型項(xiàng)目采用了Vue技術(shù)棧,在開(kāi)發(fā)效率、頁(yè)面性能、可維護(hù)性等方面都有不錯(cuò)的收效。 我們希望把這些項(xiàng)目中可復(fù)用的功能組件提取出來(lái),給后續(xù)項(xiàng)目使用,以減少重復(fù)開(kāi)發(fā),提高效率,同時(shí)也為了致敬前端界“出一個(gè)框架,造一遍輪子”的行規(guī), 一個(gè)基于Vue 2的移動(dòng)端UI組件庫(kù)被提上日程。
組件庫(kù)的開(kāi)發(fā)過(guò)程總的來(lái)說(shuō)還是比較順利的,這里與大家分享一些問(wèn)題與思考。
腳手架選擇
盡管我們團(tuán)隊(duì)的這些Vue技術(shù)棧項(xiàng)目的腳手架大都使用的是webpack,在為組件庫(kù)選擇腳手架的時(shí)候我們還是在webpack與Rollup中猶豫了一下。
Rollup看起來(lái)更適合組件庫(kù)的開(kāi)發(fā),它把所有模塊構(gòu)建在一個(gè)函數(shù)內(nèi),執(zhí)行效率更高,它支持Tree Shaking,只打包需要的代碼,輸出文件更?。╳ebpack后來(lái)也支持了)。但綜合考慮之后,我們還是選擇了webpack作為打包工具。首先,按照規(guī)劃,demo演示和文檔頁(yè)面也在這個(gè)腳手架中,所以對(duì)代碼分割、熱加載等功能是有需求的,而這方面能力Rollup遠(yuǎn)不及webpack。另外,這個(gè)組件庫(kù)由多人開(kāi)發(fā)維護(hù),基于現(xiàn)有webpack腳手架開(kāi)發(fā)成本更低、效率更高。選擇webpack,讓我們可以更專(zhuān)注于造輪子。
打包
即便選擇了webpack作為打包工具,我們也并不希望這個(gè)庫(kù)的使用場(chǎng)景局限在webpack項(xiàng)目中,通過(guò)AMD/CMD方式、甚至通過(guò)script標(biāo)簽直接引用等場(chǎng)景都應(yīng)該得到支持。為了達(dá)到這個(gè)目的,我們需要在webpack配置文件中設(shè)置輸出格式,需要配置的選項(xiàng)是output.libraryTarget,有以下可選值:
“var”(默認(rèn)值)輸出為一個(gè)變量
var MyLibrary = _entry_return_ ;
“this” 輸出為this的一個(gè)屬性
this["MyLibrary"] = _entry_return_ ;
“window” 輸出為window對(duì)象的一個(gè)屬性
window["MyLibrary"] = _entry_return_ ;
“global” 輸出為global對(duì)象的一個(gè)屬性
global["MyLibrary"] = _entry_return_ ;
“commonjs” 輸出為exports 的一個(gè)屬性
exports["MyLibrary"] = _entry_return_ ;
“commonjs2” 以module.exports形式輸出
module.exports = _entry_return_ ;
“amd” 輸出為AMD模塊
“umd” 暴露給所有模塊定義,允許它和CommonJS/AMD/全局變量一起工作
很顯然,我們需要把output.libraryTarget
的值設(shè)為“umd”,以使我們的庫(kù)可以工作在各種場(chǎng)景下。
另一個(gè)與庫(kù)打包有關(guān)的設(shè)置項(xiàng)是output.umdNamedDefine
,在output.libraryTarget
設(shè)為umd,且output.library 也設(shè)置了的情況下,把此選項(xiàng)值設(shè)為true,將會(huì)為AMD模塊命名。
webpackConfig.output = { path: path.resolve(__dirname, 'dist'), publicPath:"/", filename: '[name].js', library: 'xxx', //模塊名稱(chēng) libraryTarget: 'umd', //輸出格式 umdNamedDefine: true //是否把模塊名作為AMD輸出的命名空間 };
Vue組件庫(kù)只提供組件,Vue文件自身需要組件庫(kù)使用者在項(xiàng)目中自行引入,庫(kù)中無(wú)需打包。所以我們可以把Vue加到externals中。
externals: { vue: 'vue' }
這樣Vue就不會(huì)被打包。不過(guò),有個(gè)問(wèn)題,就是用script標(biāo)簽的形式引用Vue的時(shí)候,掛在window上的變量名是“Vue”,而不是我們需要的”vue”,因此使用時(shí)會(huì)報(bào)vue未定義的錯(cuò)誤。
還好,webpack的externals配置項(xiàng)支持傳入一個(gè)對(duì)象,可以為不同導(dǎo)出形式指定不同名稱(chēng)。所以下面這種寫(xiě)法可以解決這個(gè)問(wèn)題。
組件類(lèi)型
規(guī)劃中的Vue組件庫(kù)包含組件(Component)、指令(Directive)和過(guò)濾器(Filter)三種類(lèi)型的存在。
比較特殊的是模態(tài)彈窗類(lèi)(Modal)組件,如Dialog、Toast等等。頁(yè)面中可能存在很多個(gè)Modal,而很多場(chǎng)景下用戶(hù)的行為只會(huì)觸發(fā)其中一部分,如果把所有可能彈出的Modal(特別是異步的、結(jié)構(gòu)內(nèi)容復(fù)雜的Modal)全部寫(xiě)在頁(yè)面上,是否妥當(dāng)?對(duì)于多頁(yè)面應(yīng)用,每個(gè)頁(yè)面都寫(xiě)一遍或者再封裝一層組件是否繁瑣而冗余?這個(gè)問(wèn)題在知乎上引發(fā)過(guò)討論,尤大(Vue.js作者尤雨溪)本人在參與討論時(shí)給出建議,組件多層嵌套時(shí),應(yīng)該把Modal放在根組件里,然后在子組件里通過(guò)事件觸發(fā)。在具體應(yīng)用里,應(yīng)該這么用,這符合Vue提倡的“狀態(tài)驅(qū)動(dòng)”。不過(guò)在組件庫(kù)里,我們還是希望提供一種更便捷更通用的方式來(lái)使用Modal類(lèi)型的組件。
參考了Element UI等優(yōu)秀組件庫(kù)的做法,我們把Modal類(lèi)型的組件掛到了Vue.prototype上,使之成為Vue的實(shí)例方法,一次安裝、全局調(diào)用。
this.$dialog(options);
因此,我們的組件庫(kù)組件類(lèi)型還包括“實(shí)例方法”。
組件CSS作用域
對(duì)于一個(gè)組件,我們希望它的CSS只作用于當(dāng)前組件內(nèi)的元素,所以我們給每個(gè)組件的Vue單頁(yè)面文件的style標(biāo)簽加上了scoped屬性。編譯后,HTML標(biāo)簽會(huì)被自動(dòng)添加一個(gè)隨機(jī)生成的唯一屬性 (比如 data-v-f3f3eg9) ,同時(shí)對(duì)應(yīng)的CSS選擇器也會(huì)增加同名的屬性選擇器(如.example[data-v-f3f3eg9]),這樣組件內(nèi)的 CSS 便指定了作用域。
編譯后:
通過(guò)scoped屬性的確能達(dá)到給組件樣式設(shè)置作用域的目的,基本能避免組件內(nèi)的樣式影響外部,但是它也帶來(lái)了另外一個(gè)問(wèn)題,就是給外部覆蓋內(nèi)部樣式帶來(lái)了不便。無(wú)論組件功能多么通用,接口多么靈活,只要涉及到UI,就難免無(wú)法滿(mǎn)足所有項(xiàng)目樣式需求,所以應(yīng)該允許在具體的項(xiàng)目中根據(jù)需要覆蓋組件部分甚至全部樣式。而scoped隨機(jī)生成屬性名提高了覆蓋樣式的難度。
經(jīng)過(guò)權(quán)衡,我們?cè)诮M件里移除了scoped屬性,改用class策略來(lái)避免組件內(nèi)樣式影響外部。當(dāng)然,scoped屬性也不是沒(méi)有存在的意義,它更適合在具體應(yīng)用中使用,對(duì)于復(fù)用性高的組件來(lái)說(shuō),不是最佳選擇。
按需使用與自定義構(gòu)建
隨著項(xiàng)目推進(jìn),組件庫(kù)里的組件越來(lái)越多,目前已超過(guò)40個(gè),構(gòu)建之后的文件也越來(lái)越大。如果某個(gè)應(yīng)用只用到了庫(kù)里的少數(shù)幾個(gè)組件,完全沒(méi)有必要使用完整的構(gòu)建包,所以我們需要提供一種按需使用的方式。早期,我們是讓用戶(hù)通過(guò)私有npm安裝組件庫(kù)之后,根據(jù)應(yīng)用自身需要直接引用src目錄下組件源碼的方式來(lái)實(shí)現(xiàn)按需加載。這種方式有較大局限性,因?yàn)橐玫脑创a沒(méi)有經(jīng)過(guò)編譯,需要用戶(hù)自己去處理組件的依賴(lài)關(guān)系,ES6/SCSS/Vue模板等編譯工作也需要用戶(hù)在自己的項(xiàng)目里完成,繁瑣、易出錯(cuò),也難以支持webpack外的其他場(chǎng)景。 我們?cè)O(shè)想提供一種自定義構(gòu)建的方式,來(lái)實(shí)現(xiàn)按需打包。首先讓用戶(hù)選擇需要哪些組件,然后基于這些信息生成一個(gè)個(gè)性化的配置文件,再基于這個(gè)文件進(jìn)行構(gòu)建,最終只打包編譯用戶(hù)指定的這些組件。
那么,通過(guò)哪種方式與用戶(hù)交互,收集用戶(hù)指令呢?比較友好的方式是通過(guò)web,比如在項(xiàng)目主頁(yè)中提供一個(gè)頁(yè)面,讓用戶(hù)在線(xiàn)選擇組件,然后下載構(gòu)建之后的文件。而根據(jù)我們組件庫(kù)目前的定位,推薦的使用方式是通過(guò)私有npm安裝,所以我們首先推出的是通過(guò)命令行界面(CLI)方式來(lái)完成自定義構(gòu)建。
用戶(hù)只需要在終端執(zhí)行命令“npm run custom”,即可得到全部組件的列表,通過(guò)鍵盤(pán)選擇需要的組件,然后按下回車(chē),腳手架便開(kāi)始自動(dòng)完成剩余的個(gè)性化構(gòu)建工作。
片刻之后,只包含用戶(hù)所選組件的構(gòu)建包會(huì)出現(xiàn)在dist目錄下,文件體積比完整版本小很多。
這種方式下,所選組件會(huì)經(jīng)歷組件庫(kù)腳手架完整的構(gòu)建流程,自動(dòng)處理組件依賴(lài)關(guān)系,對(duì)ES6/SCSS/Vue等語(yǔ)法也進(jìn)行了編譯,構(gòu)建出的文件也支持AMD/CMD/script標(biāo)簽直接引用等場(chǎng)景,能比較好的滿(mǎn)足按需使用的需求。
圖標(biāo)
組件庫(kù)UI組件難免會(huì)包含一些小圖標(biāo),需要尋找一種合適的方式處理這些圖標(biāo)。
在應(yīng)用開(kāi)發(fā)中有時(shí)會(huì)把一些圖片轉(zhuǎn)成Base64編碼放在代碼里,這會(huì)使數(shù)據(jù)量增大30%左右,所以這種方式不適合較大圖片。而對(duì)于小圖標(biāo)來(lái)說(shuō),增加的絕對(duì)數(shù)據(jù)量并不大,卻能減少一個(gè)http請(qǐng)求,也不失為一種優(yōu)化方案。不過(guò),組件庫(kù)較普通應(yīng)用對(duì)數(shù)據(jù)量更為敏感,這種方式不是上策。
另一種處理小圖標(biāo)的經(jīng)典方案是雪碧圖(CSS Sprite),但這種基于精準(zhǔn)位置信息的圖標(biāo)引用方式在移動(dòng)端基于rem的布局中并不是那么受歡迎,因?yàn)閞em布局自身就難以精確,如果用于組件庫(kù)也會(huì)給按需引用帶來(lái)一些不便。
對(duì)于小圖標(biāo),在移動(dòng)端更需要的是矢量方案,天然適配各種像素密度的屏幕。
在組件庫(kù)中,比較流行的是采用基于CSS3字體(@font-face)的ICON FONT方案,也就是把圖標(biāo)放在一個(gè)自定義字體文件中。有很多優(yōu)點(diǎn),比如:
- ICON在字體中是矢量存在,受移動(dòng)端歡迎
- 良好的瀏覽器兼容性,web字體并非CSS3發(fā)明,更早之前的瀏覽器(包括IE6)也都事實(shí)上支持,雖有些許差異,終歸是有辦法兼容的
- 可通過(guò)CSS控制ICON顏色和透明度等樣式,甚至可以實(shí)現(xiàn)顏色漸變效果
我們并沒(méi)有選擇ICON FONT方案,我們認(rèn)為SVG方案更適合移動(dòng)端組件庫(kù):
- SVG雖在PC端個(gè)別古董瀏覽器中兼容較差,但在移動(dòng)端兼容良好
- ICON FONT被認(rèn)為是文本,所以一些瀏覽器會(huì)對(duì)其進(jìn)行抗鋸齒處理,這可能導(dǎo)致圖標(biāo)不那么銳利,清晰度打折扣
- SVG樣式控制比ICON FONT更靈活,甚至可以控制圖標(biāo)各個(gè)部分的顏色,實(shí)現(xiàn)彩色圖標(biāo)。而這對(duì)ICON FONT來(lái)說(shuō)是不可能實(shí)現(xiàn)的
- ICON FONT通常是用偽對(duì)象或偽類(lèi)插入頁(yè)面,其展示受到“l(fā)ine-height”、“vertical-align”、“l(fā)etter-spacing”、“word-spacing”及字體相關(guān)CSS屬性影響,也受到字體字符設(shè)計(jì)本身影響。而SVG在頁(yè)面中就是一個(gè)標(biāo)簽,更方便控制,語(yǔ)義化也更好
- 結(jié)合symbol元素可以實(shí)現(xiàn)所謂“SVG Sprite”,也就是把很多SVG圖標(biāo)整合在一起,通過(guò)ID引用指定圖標(biāo),可以復(fù)用。這種方式比CSS Sprite還要方便,因?yàn)椴恍枰P(guān)心圖標(biāo)具體位置信息
SVG Sprite也不是必須手動(dòng)去組合,借助webpack的 svg-sprite-loader 可以輕松實(shí)現(xiàn)SVG Sprite的動(dòng)態(tài)生成,圖標(biāo)的按需加載不是夢(mèng)。
擴(kuò)展閱讀
- NutUI組件庫(kù) - https://nutui.jd.com
- webpack output.libraryTarget - https://webpack.js.org/configuration/output/#output-librarytarget
- umd - https://github.com/umdjs/umd
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- 淺談基于Vue.js的移動(dòng)組件庫(kù)cube-ui
- Vue.js 的移動(dòng)端組件庫(kù)mint-ui實(shí)現(xiàn)無(wú)限滾動(dòng)加載更多的方法
- Mint UI 基于 Vue.js 移動(dòng)端組件庫(kù)
- 基于Vue框架vux組件庫(kù)實(shí)現(xiàn)上拉刷新功能
- Vue cli3 庫(kù)模式搭建組件庫(kù)并發(fā)布到 npm的流程
- 在vue2.0中引用element-ui組件庫(kù)的方法
- Vue組件庫(kù)發(fā)布到npm詳解
- mpvue項(xiàng)目中使用第三方UI組件庫(kù)的方法
- 基于Vue 2.0的模塊化前端 UI 組件庫(kù)小結(jié)
- 少女風(fēng)vue組件庫(kù)的制作全過(guò)程
相關(guān)文章
使用Bootstrap + Vue.js實(shí)現(xiàn)添加刪除數(shù)據(jù)示例
本篇文章主要介紹了使用Bootstrap + Vue.js實(shí)現(xiàn) 添加刪除數(shù)據(jù)示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02vue將某個(gè)組件打包成js并在其他項(xiàng)目使用
這篇文章主要給大家介紹了關(guān)于vue將某個(gè)組件打包成js并在其他項(xiàng)目使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07Vue指令之 v-cloak、v-text、v-html實(shí)例詳解
當(dāng)用戶(hù)頻繁刷新頁(yè)面或網(wǎng)速慢時(shí),頁(yè)面未完成 Vue.js 的加載時(shí),導(dǎo)致 Vue 來(lái)不及渲染,這就會(huì)導(dǎo)致在瀏覽器中直接暴露插值(表達(dá)式),這篇文章主要介紹了Vue指令 v-cloak、v-text、v-html,需要的朋友可以參考下2019-08-08vue-router中的hash和history兩種模式的區(qū)別
大家都知道vue-router有兩種模式,hash模式和history模式,這里來(lái)談?wù)剉ue-router中的hash和history兩種模式的區(qū)別。感興趣的朋友一起看看吧2018-07-07vue3+echarts+折線(xiàn)投影(陰影)效果的實(shí)現(xiàn)
這篇文章主要介紹了vue3+echarts+折線(xiàn)投影(陰影)效果的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10詳解vue-cli項(xiàng)目開(kāi)發(fā)/生產(chǎn)環(huán)境代理實(shí)現(xiàn)跨域請(qǐng)求
這篇文章主要介紹了詳解vue-cli項(xiàng)目開(kāi)發(fā)/生產(chǎn)環(huán)境代理實(shí)現(xiàn)跨域請(qǐng)求,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07vue移動(dòng)端城市三級(jí)聯(lián)動(dòng)組件使用詳解
這篇文章主要為大家詳細(xì)介紹了vue移動(dòng)端城市三級(jí)聯(lián)動(dòng)組件的使用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07Vue如何解決子組件data從props中無(wú)法動(dòng)態(tài)更新數(shù)據(jù)問(wèn)題
這篇文章主要介紹了Vue如何解決子組件data從props中無(wú)法動(dòng)態(tài)更新數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10