摘要:為何有查閱了的文檔,并沒有找到字段的定義,直到才知道它是中最早就提出的概念。況且目前大部分仍是采用,所以便使用了另一個字段。所以目前主流的打包工具都是支持的,鑒于其優點,字段很有可能加入的規范之中。
引入
最近團隊的一個同學在搞 npm library 源碼的調試插件,因為內部的一個組件庫含有大量的邏輯,在某個項目中不經意就出現一個磨人的 bug,但是組件庫發布都是打包編譯后的代碼,而且沒有 publish src 代碼,不方便調試,每次還要 down 一下包的源碼,再改下 webpack 的配置(比如 rule 中 exclude 去掉組件庫, 改下 resolve ,在 dll 中去掉組件庫)。被他們耳語目染了好幾天,我就想,記得 npm 包是可以直接引源碼的,大概改下 webpack 配置就可以了。然后便找到了 package.json 中 module 字段,并查漏 js 中 tree shaking 的知識,所以我并沒有去研究怎么搞那樣的一個插件?,而是由 package 中的 module 字段延伸出的一些知識。
為何有 module查閱了 package.json 的文檔,并沒有找到 module 字段的定義,直到 google 才知道它是 rollup 中最早就提出的概念 --- pkg.module。大概就是最早的 npm 包都是基于 CommonJS 規范的,package.json 形如:
"name": "package1", "version": "1.0.0", "main": "lib/index.js"
當 require("package1") 的時候,就會根據 main 字段去查找入口文件。
而 es2015 后,js 擁有了 ES Module,相較于之前的模塊化方案更爽滑,更優雅,并且 ES 模塊也是官方標準(JS 規范),而 CommonJS 模塊是一種特殊的傳統格式,在 ES 模塊被提出之前做為暫時的解決方案。所以 rollup 去利用 ES Module 構建,就可以利用 ES Module 的很多特性,從而提高打包的性能,其中提升一個便是 tree shaking,這個我們后面去介紹。在這個構建思想的基礎上,開發、產出的 npm 包同樣使用 es6 的 module,即可同樣受益于 tree shaking 等特性。
而 CommonJS 規范的包都是以 main 字段表示入口文件了,如果使用 ES Module 的也用 main 字段,就會對使用者造成困擾,假如他的項目不支持打包構建,比如大多數 node 項目(盡管 node9+ 支持 ES Module)。這就是庫開發者的模塊系統跟項目構建的模塊系統的沖突,更像是一種規范上的問題。況且目前大部分仍是采用 CommonJS,所以 rollup 便使用了另一個字段:module。
像這樣:
"name": "package1", "version": "1.0.0", "main": "lib/index.js", "module": "es/index.js"
webpack 從版本 2 開始也可以識別 pkg.module 字段。打包工具遇到 package1 的時候,如果存在 module 字段,會優先使用,如果沒找到對應的文件,則會使用 main 字段,并按照 CommonJS 規范打包。所以目前主流的打包工具(webpack, rollup)都是支持 pkg.module 的,鑒于其優點,module 字段很有可能加入 package.json 的規范之中。另外,越來越多的 npm 包已經同時支持兩種模塊,使用者可以根據情況自行選擇,并且實現也比較簡單,只是模塊導出的方式。
注意:雖然打包工具支持了 ES Module,但是并不意味著其他的 es6 代碼可以正常使用,因為使用者可能并不會對你的 npm 包做編譯處理,比如 webpack rules 中 exclude: /node_modules/,所以如果不是事先約定好后編譯或者沒有兼容性的需求,你仍需要用 babel 處理,從而產出兼容性更好的 npm 包。還好 rollup 在這方面做的不錯,對于 library 開發者更友好一些。
同時支持的效果類似這樣:
Tree-shaking
tree-shaking 是近兩年才在 JS 中出現的,之前沒有的,而模塊化的概念是一直都有方案的,只不過直到 ES Module 才有統一的標準趨勢。
前面提到 rollup 采用 ES Module,帶來的一個優點便是 tree shaking,那什么是 tree-shaking 呢。
有一個圖片很形象的解釋了它的功能。
tree-shaking 的功能就是把我們 JS 中無用的代碼,給去掉,如果把打包工具通過入口文件,產生的依賴樹比作 tree,tree-shaking 就是把依賴樹中用不到的代碼 shaking 掉。
我們通過代碼了解下,webpack3.x 下打包驗證 tree-shaking。
// 入口文件 index.js import { func1 } from "./export1"; func1();
// export1 文件 export function func1() { console.log("func1"); } export function func2() { console.log("func2"); }
func2 方法雖然導出了,但是在 index.js 中是沒有用到的,func2 就是無用代碼,最終打包生成的 build 是應該去掉的。
使用最簡單的 webpack 配置,不使用 babel,產出 build.js,export1 是這樣的:
/* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (immutable) */ __webpack_exports__["a"] = func1; /* unused harmony export func2 */ function func1() { console.log("func1"); } function func2() { console.log("func2"); } /***/ })
我們發現有兩行注釋,/* harmony export (immutable) 表明代碼是有用的,unused harmony export func2表明 func2 是無用代碼,說明 webpack 已經識別。不過 webpack 僅僅是做了“標記”,去掉這些代碼的能力,是通過插件實現的,常用的便是 unglify。在 plugins 用啟用 UglifyJsPlugin 后,查看下 build。
// webpack.config.js const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); module.exports = { ... plugins: [ new UglifyJsPlugin(), ] }
上圖即編譯后 export1 模塊的截圖,可以看到 func2 已經被去掉了。不過在我開啟 babel-loader 以后,babel 配置就是一個簡單的 "presets: ["env"]",卻發現 func2 又回來了,如下:
這是為什么呢。因為 tree-shaking 是依賴 ES Module 的靜態加載,而 babel-presets-env 中是包含 ES2015 modules to CommonJS transform 的 plugin,也就是轉化成 CommonJS,所以無法識別哪些代碼是未引用的,也就是無法 tree-shaking,所以 babel transform 的時候應該保留 ES Module。
通過 presets 的 option 選擇,設置 modules 為 false 即可。
另外,tree-shaking 并不是完美無缺的,有些情況還無法識別。比如你導入了一個模塊,但是這個變量代碼中未使用,是不會去掉的,細節可以看這篇文章
為什么是 ES ModuleES Module 之前的 JS 實現模塊化,都是基于第三方依賴管理實現的,比如 requirejs,seajs,這都是在代碼執行的時候,才知道依賴了哪些模塊,常用的 node 中的 commonjs,也是如此
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! });
所以,當 ES Module 在代碼不執行的時候,就可以知道模塊的依賴關系,就不難理解是為什么了。
思考我的本意是,可否利用 module 字段的特性,讓我的 npm 包支持引入源碼,從而可以實現源碼調試、并且后編譯的效果,不過從目前的規范看來,內部還是可以試一下的,開源的包最好不要這樣做,除非你有自己的一套規范以及后編譯生態。雖然沒有達到目的,不過也后知后覺的了解到 module 的用意,以及 rollup 在開發包時候的妙用,以及 tree-shaking 并不是自己了解的那么美好。
相關推薦你的Tree-Shaking并沒什么卵用
【譯】如何在 Webpack 2 中使用 tree-shaking
手把手帶你走進下一代的ES6模塊打包工具—Rollup
原文:https://github.com/configu/bl...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107674.html
摘要:字段由腳本命令組成的字典,這些命令運行在包的各個生命周期中。在打包過程中,如果遇到字段會優先使用字段表示的路徑下的文件,如果不存在,則用字段表示的作為入口,并按照的規范打包。其中還分析了文件中字段和字段的不同以及和兩個字段的區別。 所有用npm下載的包或者要上傳至npm的模塊都會有一個package.json文件,這個文件總是存在于模塊(或者包)的根目錄下,這個文件到底是干嘛的,現在就...
摘要:但是,面對辣么多的配置文件,還是從開始自己來吧,畢竟我只想打包一個組件。這里想一下我們的需求,我們想要打包一個組件,使用,根據上面的說明,不難想到還應該需要一個可以用來識別并轉換文件,一句話,就是把按下面格式的編寫的組件轉換為模塊。 對于vue單文件組件的使用,我們知道使用vue-cli可以快速生成項目結構,進行.vue單文件組件的編寫,使用 npm run build命令會從main...
摘要:但是,面對辣么多的配置文件,還是從開始自己來吧,畢竟我只想打包一個組件。這里想一下我們的需求,我們想要打包一個組件,使用,根據上面的說明,不難想到還應該需要一個可以用來識別并轉換文件,一句話,就是把按下面格式的編寫的組件轉換為模塊。 對于vue單文件組件的使用,我們知道使用vue-cli可以快速生成項目結構,進行.vue單文件組件的編寫,使用 npm run build命令會從main...
摘要:但是,面對辣么多的配置文件,還是從開始自己來吧,畢竟我只想打包一個組件。這里想一下我們的需求,我們想要打包一個組件,使用,根據上面的說明,不難想到還應該需要一個可以用來識別并轉換文件,一句話,就是把按下面格式的編寫的組件轉換為模塊。 對于vue單文件組件的使用,我們知道使用vue-cli可以快速生成項目結構,進行.vue單文件組件的編寫,使用 npm run build命令會從main...
摘要:同時虛擬主機管理系統還包含了財務管理功能功能。隨著互聯網的發展和技術的不斷更新,虛擬主機管理系統的開發也越來越完善,功能也越來越強大,尤其值得一說的是虛擬主機管理系統更加智能化。點開網頁出現虛擬主機已開通是什么意思?虛擬主機是把一臺服務器分成很多虛擬的服務器,每一個虛擬主機都具有獨立的域名和完整的Internet服務器(支持WWW、FTP、E-mail等)功能。缺點只有一個id,一旦有別的網...
閱讀 1240·2021-11-22 13:54
閱讀 1425·2021-11-22 09:34
閱讀 2698·2021-11-22 09:34
閱讀 4007·2021-10-13 09:39
閱讀 3341·2019-08-26 11:52
閱讀 3361·2019-08-26 11:50
閱讀 1529·2019-08-26 10:56
閱讀 1913·2019-08-26 10:44