您好,登錄后才能下訂單哦!
前言
2017年是Vue.js大爆發的一年,React迎來了一個強有力的競爭對手,王者地位受到挑戰(撰寫此文時github上Vue與React的star數量已逼近)。我們團隊這一年有十多個大型項目采用了Vue技術棧,在開發效率、頁面性能、可維護性等方面都有不錯的收效。 我們希望把這些項目中可復用的功能組件提取出來,給后續項目使用,以減少重復開發,提高效率,同時也為了致敬前端界“出一個框架,造一遍輪子”的行規, 一個基于Vue 2的移動端UI組件庫被提上日程。
組件庫的開發過程總的來說還是比較順利的,這里與大家分享一些問題與思考。
腳手架選擇
盡管我們團隊的這些Vue技術棧項目的腳手架大都使用的是webpack,在為組件庫選擇腳手架的時候我們還是在webpack與Rollup中猶豫了一下。
Rollup看起來更適合組件庫的開發,它把所有模塊構建在一個函數內,執行效率更高,它支持Tree Shaking,只打包需要的代碼,輸出文件更小(webpack后來也支持了)。但綜合考慮之后,我們還是選擇了webpack作為打包工具。首先,按照規劃,demo演示和文檔頁面也在這個腳手架中,所以對代碼分割、熱加載等功能是有需求的,而這方面能力Rollup遠不及webpack。另外,這個組件庫由多人開發維護,基于現有webpack腳手架開發成本更低、效率更高。選擇webpack,讓我們可以更專注于造輪子。
打包
即便選擇了webpack作為打包工具,我們也并不希望這個庫的使用場景局限在webpack項目中,通過AMD/CMD方式、甚至通過script標簽直接引用等場景都應該得到支持。為了達到這個目的,我們需要在webpack配置文件中設置輸出格式,需要配置的選項是output.libraryTarget,有以下可選值:
“var”(默認值)輸出為一個變量
var MyLibrary = _entry_return_ ;
“this” 輸出為this的一個屬性
this["MyLibrary"] = _entry_return_ ;
“window” 輸出為window對象的一個屬性
window["MyLibrary"] = _entry_return_ ;
“global” 輸出為global對象的一個屬性
global["MyLibrary"] = _entry_return_ ;
“commonjs” 輸出為exports 的一個屬性
exports["MyLibrary"] = _entry_return_ ;
“commonjs2” 以module.exports形式輸出
module.exports = _entry_return_ ;
“amd” 輸出為AMD模塊
“umd” 暴露給所有模塊定義,允許它和CommonJS/AMD/全局變量一起工作
很顯然,我們需要把output.libraryTarget
的值設為“umd”,以使我們的庫可以工作在各種場景下。
另一個與庫打包有關的設置項是output.umdNamedDefine
,在output.libraryTarget
設為umd,且output.library 也設置了的情況下,把此選項值設為true,將會為AMD模塊命名。
webpackConfig.output = { path: path.resolve(__dirname, 'dist'), publicPath:"/", filename: '[name].js', library: 'xxx', //模塊名稱 libraryTarget: 'umd', //輸出格式 umdNamedDefine: true //是否把模塊名作為AMD輸出的命名空間 };
Vue組件庫只提供組件,Vue文件自身需要組件庫使用者在項目中自行引入,庫中無需打包。所以我們可以把Vue加到externals中。
externals: { vue: 'vue' }
這樣Vue就不會被打包。不過,有個問題,就是用script標簽的形式引用Vue的時候,掛在window上的變量名是“Vue”,而不是我們需要的”vue”,因此使用時會報vue未定義的錯誤。
還好,webpack的externals配置項支持傳入一個對象,可以為不同導出形式指定不同名稱。所以下面這種寫法可以解決這個問題。
組件類型
規劃中的Vue組件庫包含組件(Component)、指令(Directive)和過濾器(Filter)三種類型的存在。
比較特殊的是模態彈窗類(Modal)組件,如Dialog、Toast等等。頁面中可能存在很多個Modal,而很多場景下用戶的行為只會觸發其中一部分,如果把所有可能彈出的Modal(特別是異步的、結構內容復雜的Modal)全部寫在頁面上,是否妥當?對于多頁面應用,每個頁面都寫一遍或者再封裝一層組件是否繁瑣而冗余?這個問題在知乎上引發過討論,尤大(Vue.js作者尤雨溪)本人在參與討論時給出建議,組件多層嵌套時,應該把Modal放在根組件里,然后在子組件里通過事件觸發。在具體應用里,應該這么用,這符合Vue提倡的“狀態驅動”。不過在組件庫里,我們還是希望提供一種更便捷更通用的方式來使用Modal類型的組件。
參考了Element UI等優秀組件庫的做法,我們把Modal類型的組件掛到了Vue.prototype上,使之成為Vue的實例方法,一次安裝、全局調用。
this.$dialog(options);
因此,我們的組件庫組件類型還包括“實例方法”。
組件CSS作用域
對于一個組件,我們希望它的CSS只作用于當前組件內的元素,所以我們給每個組件的Vue單頁面文件的style標簽加上了scoped屬性。編譯后,HTML標簽會被自動添加一個隨機生成的唯一屬性 (比如 data-v-f3f3eg9) ,同時對應的CSS選擇器也會增加同名的屬性選擇器(如.example[data-v-f3f3eg9]),這樣組件內的 CSS 便指定了作用域。
編譯后:
通過scoped屬性的確能達到給組件樣式設置作用域的目的,基本能避免組件內的樣式影響外部,但是它也帶來了另外一個問題,就是給外部覆蓋內部樣式帶來了不便。無論組件功能多么通用,接口多么靈活,只要涉及到UI,就難免無法滿足所有項目樣式需求,所以應該允許在具體的項目中根據需要覆蓋組件部分甚至全部樣式。而scoped隨機生成屬性名提高了覆蓋樣式的難度。
經過權衡,我們在組件里移除了scoped屬性,改用class策略來避免組件內樣式影響外部。當然,scoped屬性也不是沒有存在的意義,它更適合在具體應用中使用,對于復用性高的組件來說,不是最佳選擇。
按需使用與自定義構建
隨著項目推進,組件庫里的組件越來越多,目前已超過40個,構建之后的文件也越來越大。如果某個應用只用到了庫里的少數幾個組件,完全沒有必要使用完整的構建包,所以我們需要提供一種按需使用的方式。早期,我們是讓用戶通過私有npm安裝組件庫之后,根據應用自身需要直接引用src目錄下組件源碼的方式來實現按需加載。這種方式有較大局限性,因為引用的源碼沒有經過編譯,需要用戶自己去處理組件的依賴關系,ES6/SCSS/Vue模板等編譯工作也需要用戶在自己的項目里完成,繁瑣、易出錯,也難以支持webpack外的其他場景。 我們設想提供一種自定義構建的方式,來實現按需打包。首先讓用戶選擇需要哪些組件,然后基于這些信息生成一個個性化的配置文件,再基于這個文件進行構建,最終只打包編譯用戶指定的這些組件。
那么,通過哪種方式與用戶交互,收集用戶指令呢?比較友好的方式是通過web,比如在項目主頁中提供一個頁面,讓用戶在線選擇組件,然后下載構建之后的文件。而根據我們組件庫目前的定位,推薦的使用方式是通過私有npm安裝,所以我們首先推出的是通過命令行界面(CLI)方式來完成自定義構建。
用戶只需要在終端執行命令“npm run custom”,即可得到全部組件的列表,通過鍵盤選擇需要的組件,然后按下回車,腳手架便開始自動完成剩余的個性化構建工作。
片刻之后,只包含用戶所選組件的構建包會出現在dist目錄下,文件體積比完整版本小很多。
這種方式下,所選組件會經歷組件庫腳手架完整的構建流程,自動處理組件依賴關系,對ES6/SCSS/Vue等語法也進行了編譯,構建出的文件也支持AMD/CMD/script標簽直接引用等場景,能比較好的滿足按需使用的需求。
圖標
組件庫UI組件難免會包含一些小圖標,需要尋找一種合適的方式處理這些圖標。
在應用開發中有時會把一些圖片轉成Base64編碼放在代碼里,這會使數據量增大30%左右,所以這種方式不適合較大圖片。而對于小圖標來說,增加的絕對數據量并不大,卻能減少一個http請求,也不失為一種優化方案。不過,組件庫較普通應用對數據量更為敏感,這種方式不是上策。
另一種處理小圖標的經典方案是雪碧圖(CSS Sprite),但這種基于精準位置信息的圖標引用方式在移動端基于rem的布局中并不是那么受歡迎,因為rem布局自身就難以精確,如果用于組件庫也會給按需引用帶來一些不便。
對于小圖標,在移動端更需要的是矢量方案,天然適配各種像素密度的屏幕。
在組件庫中,比較流行的是采用基于CSS3字體(@font-face)的ICON FONT方案,也就是把圖標放在一個自定義字體文件中。有很多優點,比如:
我們并沒有選擇ICON FONT方案,我們認為SVG方案更適合移動端組件庫:
SVG Sprite也不是必須手動去組合,借助webpack的 svg-sprite-loader 可以輕松實現SVG Sprite的動態生成,圖標的按需加載不是夢。
擴展閱讀
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。