使用webpack4搭建一个基于Vue的组件库
組內負責的幾個項目都有一些一樣的公共組件,所以就著手搭建了個公共組件開發腳手架,第一次開發 library,所以是參考著 iview 的配置來搭建的。記錄如何使用webpack4搭建一個library的腳手架
前言
使用 webpack4,需要安裝 webpack 和 webpack-cli
yarn add webpack webpack-cli -D
然后就是書寫配置文件。
項目腳手架結構
我寫的 library 的目錄結構如下,僅供參考,主要是模仿 iview 的結構,其中部分配置參考了 vue-cli的 webpack 配置文件。
├─build │ build.js // 用于執行構建 │ check-versions.js // vue-cli 留下的,主要就是檢查npm版本和node版本 │ webpack.base.conf.js // 通用配置 │ webpack.dev.conf.js // 開發環境 │ webpack.dist.prod.conf.js // 用于生成library的代碼 -- hbf.min.js │ webpack.prod.conf.js // 用于生成example文件的打包代碼,這個其實是沒有必要的. │ ├─dist │ └─example // example生成的打包文件夾,可以通過githubPage來預覽,或者本地使用anywhere預覽 │ hbf.min.js // library 文件 │ ├─example // example目錄 │ App.vue │ index.html │ main.js │ ├─lib │ │ index.js // 全量引入公共組件,并暴露出來,包含install方法可供vue引入使用該插件 │ │ README.md │ │ │ └─components // 公共組件 │ ├─package.json // 項目包依賴更為具體信息的可以到github倉庫閱覽。
經過 webpack 編譯后的代碼
為了更好的理解,先來了解下 webpack 編譯后的代碼。
經過webpack處理過的代碼通常都是如下所示
// webpack編譯后的代碼/* * @param {Array} modules */ ;(function(modules) {function __webpack_require__(moduleId) {var module = {i: moduleId, // 模塊IDl: false,exports: {}, // 作為結果返回.}// 調用modules數組的某個元素(類型為函數)modules[moduleId].call(module.exports,module,module.exports,__webpack_require__)return module.exports}return __webpack_require__(0) })([/** 省略了代碼, 該數組的每一項代表一個模塊,實際是一個函數,接受三個參數,module對象,module.exports對象,__webpack_require__函數 **/ ])webpack 編譯后的代碼的整體結構就是一個IIFE函數,接受一個 modules: Array參數。
對于模塊處理,無論是 ES Module 的 import 還是 commonjs 的 require 都轉化為__webpack_require__ 這個函數來引入模塊。
__webpack_require__ 函數,會從 modules 數組的第一個元素開始(moduleId 為 0,也就是入口文件),執行該模塊(實為一個函數)的邏輯,利用傳入的module.exports的數據類型為引用類型Object,間接地給module.exports添加屬性。
return __webpack_require__(0)
從入口文件開始,逐個引入依賴模塊,最后返回入口模塊的 module.exports
此時這個編譯后的 js 文件,是無法被其他模塊所引用的,只在當前作用域內有效, webpack 就提供了創建 library 的方式,就是在output里定義library 和 libraryTarget。使得構建完的 js 可以供其他模塊引入使用。
設置library配置
對于作為一個 library 使用的項目來說,output 選項需要設置 library
// webpack.dist.pord.conf.jsoutput: {path: path.resolve(__dirname, '../dist'),publicPath: '/dist/',filename: 'hbf.min.js',library: 'hbf',libraryTarget: 'umd' },library 可以是字符串,也可以是對象,(對象僅限于 libraryTarget 的值為 umd 的情況下使用)
output: {library: {root:'Hbf', // 暴露給全局變量,window.Hbf進行調用commonjs: 'hbf-public-components'},libraryTarget: 'umd' }commonjs 和 commonjs2 的區別。
commonjs 規范就是定義了一個 exports 對象,而 nodejs 在實現的時候,在 commonjs 規范的前提下做了一些擴展,定義了 module.exports ,從而也叫這種為 commonjs2 規范。
我們在引用別人的庫的時候,通常都是可以通過多種方法引入的,比如 <script> 標簽引入,通過 commonJS模塊 引入,通過 ES6 Module 引入。
libraryTarget設置為umd(通用模塊規范)的話,則打包后可以通過多種模塊加載的方法加載 library,具有高兼容性。
library 的依賴問題
如果我們的 library 是基于某某庫的基礎上開發的,比如說寫一個基于vue的 UI 組件庫,在開發的這個組件庫的時候,我們需要引入vue,如果使用這個組件庫的用戶本身就已經引入了vue,那么vue就會被引入并打包兩次,所以我們在開發一個library的時候,對于一些所依賴的模塊,可以由引入library的使用者提供。所以我們需要將依賴的模塊在 library 的打包構建中去除。
externals 的作用,防止將某些 import 的包打包到 bundle 中,而是在運行時再去外部獲取這些擴展依賴。
通過設置 externals,從輸出的 bundle 中排除 vue 和 iview。
這些外部依賴可能是以下的任何一種形式。
- root 全局變量訪問
- commonjs 作為一個commonjs模塊引入
- commonjs2 與commonjs類似,不過導出的是 module.exports
- amd 使用amd模塊規范引入
另外在 package.json 加多一個peerDependencies字段,作用是約定library所依賴的庫的版本號,在使用者下載使用library的時候,如果所依賴的 iview 和 vue 的版本號不對,就會發出警告。
"peerDependencies": {"iview": ">2.0.0","vue": ">2.0.0" },對于這兩個依賴,寫到開發環境依賴中 ,不然安裝時,會在庫的目錄下安裝vue 和 iview,這也不符合讓 library 的引用者提供 library 的依賴這個想法。
vue 插件庫/ 組件庫
對于 vue 的插件庫 / 組件庫來說,如果想要全局引入的話,需要有一個install方法。install 內部邏輯就是通過參數傳進來的vue對象,注冊所有組件。然后最后將所有公共組件連同install方法組成一個新對象暴露出去。
// 引入公共組件 import publicMenu from './components/public-menu' import tablePage from './components/table-page' import sliderCustom from './components/slider-custom'const components = {publicMenu,tablePage,sliderCustom, } const Hbf = Object.assign({}, components) const install = function(Vue, opts) {if (install.installed) returnObject.keys(components).forEach(component => {Vue.component(component, component)}) }// 用于script標簽引入 if (typeof window !== 'undefined' && window.Vue) {install(window.Vue) }// 將install方法賦給Hbf對象 Hbf.install = install// 輸出default變量,用于全量引入,也可以在引入的時候選擇使用 * 來全量引入 export default Hbf // 輸出各個組件,用于按需引入 export { publicMenu, tablePage, sliderCustom }在暴露含有 install 方法的對象時,一開始使用的是 module.exports,引用 library 的時候報錯Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'。
因為我測試用的項目關閉了 babel 對 ES Module 的編譯,通常情況下,沒有手動關閉的話,babel 會將 ES6 Module 編譯轉換成 commonjs 規范。所以在關閉了module的轉換的情況下,由于庫的輸出使用的是 commonjs 規范的 module.exports,而引入庫使用的是 ES6 模塊規范的import關鍵字,所以產生了報錯,我將庫的導出寫成export關鍵字,就沒報錯了。
以后書寫某一模塊的導入導出,還是使用同一種規范的,不要混用。
了解到現在大多數庫,都是用的 commonjs 規范,由于 webpack 的 tree-shaking 只對 ES Module起作用,webpack 的 tree-shaking 實際上是由Uglylify來實現的。
所以庫的模塊規范可以使用兩種,利用 package.json的 main 和 module 字段分別定義庫的兩種模塊規范的入口文件。 main 使用的是 commonjs 規范語法書寫的文件,而 module 是使用 ES6 module 語法書寫的文件,module字段目前還是一個提案。所以采用了 ES2015 模塊語法的庫的,當我們只使用到 library的部分代碼,則可以利用webpack 進行 tree-shaking,去除未引用的代碼,減少打包文件體積。
發布 npm 包
首先就是需要注冊一個 npm 賬號。
如果之前使用的是淘寶鏡像的話,需要先切回 npm 官方源。不然是發不了包的。
打開命令行
切換官方源 npm config set registry https://registry.npmjs.org/
執行npm login,然后輸入你的賬號信息。
可以配置.npmignore 忽略一些不需要上傳的文件,寫法跟.gitignore 相同。
需要保證項目有正確的package.json文件和README.md文件
然后執行npm publish進行發包。
發完包就可以切回淘寶鏡像源
npm config set registry https://registry.npm.taobao.org
引用組件庫
Script 標簽引入
一開始是將 JS 文件放在本地測試,發現在HTML文件的第一行就報錯,Unexpected token <,StackOverflow說的原因是引入路徑不正確,所以我就把 JS 文件放到 CDN 上了,
<script src="http://osuuzm0m8.bkt.clouddn.com/hbf.min.js"></script> <script> console.log(window.Hbf) // 會看到你導出的對象 </script>
輸出
全量引用
可以像使用其他 vue 插件庫/組件庫一樣使用。
import hbf from 'hbf-public-components'// 使用use方法觸發hbf的intall方法,注冊全部組件 Vue.use(hbf)如果是沒有導出default變量,則使用另外一種方式全量引入
import * as hbf from 'hbf-public-components'按需引用
import { publicMenu } from 'hbf-public-components'按需引用,如果 library 使用的是ES2015 Module規范,則不需要安裝任何插件,webpack 會對其進行tree-shaking,去除未引用的代碼。
前面提過,webpack的tree-shaking是由Uglylify插件實現的,我在開發環境下,沒有啟用Uglylify來壓縮代碼,所以查看模塊打包圖,會發現整個庫都被引入了,雖然我只引入了一個組件。webpack4在生產環境下,才會進行tree-shaking,設置mode的值為production就會開啟生產環境下的優化。
如果是使用 commonjs 規范的 library 則需要一個插件支持,babel-plugin-import。該插件是ant官方開發的。許多 UI 組件庫的按需引入也是依賴于這個插件。
安裝
yarn add babel-plugin-import -D
修改.babelrc文件,
"plugins": [["import", {"libraryName": "hbf-public-components","libraryDirectory": "lib/components"}] ],總結
其實如果對于一個不是很穩定,需要一直迭代更新的公用組件庫來說,使用 npm 包的話,會比較不方便,經常更新的公共組件代碼可以使用Git subtree(教程)來維護,可以等到一定地步之后,公共組件庫穩定下來之后再考慮發布一個 npm 包。
而開發一個組件庫,也可以使用 rollup.js 來搭腳手架,rollup.js 默認使用的就是 ES2015 Module,可以進行靜態分析,去除未引用的代碼,tree-shaking 也是 rollup.js 先提出的。Rollup相比較于Webpack,更適合用于構建library,Vue.js就是使用 Rollup 構建的。Webpack 在代碼分割這方面比較有優勢,所以webpack 相對來說比較適合構建應用程序,不過使用 webpack 構建 library 也是可以的。
這個項目也可以用做 webpack 構建 library 的通用腳手架。下次再嘗試Rollup構建 library
有幫助的話,可以給個star的話就最好啦。有問題也可以聯系我。
項目地址: https://github.com/huya-base-...
npm 地址:https://www.npmjs.com/package...
關于模塊規范,以及 webpack,babel 是如何編譯轉換模塊的文章
來源:https://segmentfault.com/a/1190000015877664
轉載于:https://www.cnblogs.com/qixidi/p/10149589.html
總結
以上是生活随笔為你收集整理的使用webpack4搭建一个基于Vue的组件库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1060 爱丁顿数 (25 分)(思维)
- 下一篇: V-Charts中使用extend属性定