NPM中將函數當作一個包發布的方式是否合理?

NPM left-pad: Have We Forgotten How To Program?

相關問題:

為什麼 npm 要為每個項目單獨安裝一遍 node_modules? - Node.js


我是來找噴的。

首先介紹一下我自己吧。非前端,也不會前端;以前寫 C++,寫過遊戲(客戶端和服務端),寫過很淺顯的 PHP;接觸過 Python,不會 Ruby,不會 Java;現在是 Node.js 後端研發,後端研發,研發。

首先不知道為什麼,很多不明真相的觀眾都喜歡把 Node.js 歸類於前端。我很想引用朴大的一句話。

我只說一句,你才是前端,你全家都是前端,你們小區都是做前端的。

很多人印象裡面寫 Javascript 的都是些小菜鳥,所以就該把 NPM 搞得亂七八糟烏煙瘴氣的,所以 NPM 就是個亂七八糟的地方——自己沒到那個水準然後就評頭論足了,然後各其它語言的優越感都出來了。

我承認,包括我自己在內,JS 的菜鳥有很多,而且寫 Node.js 很多人是由前端轉過來的——因為都是寫 ES 的,但不能否認它的優點。

缺點本答案就不說了,反正上面 NPM 已經被噴得一無是處了,有好多答案我也贊同,的確是有缺陷的,不好的。

接下來我就說幾個合理之處,以及我自己的想法。

這 node 社區也太水了吧,有你找到像 left pad 這個 package 的時間,都能自己實現好幾遍這個功能了。

首先,如果是我肯定不會因為有這個需求而去找 `left-pad`,而是以前聽說過,然後剛好有需求才會來用。

其次,對於這個函數的位置,你們覺得放哪好呢?弄一個 `lib` 或者 `helper` 文件夾,然後寫一個 `helper.js` 或者 `util.js`,裡面就放上這麼一個 `left-pad` 函數?或者就把這個函數放在要用的那個文件本身裡面?

不管你們怎麼想,我覺得都不夠優雅。當然,如果一個大的工程的話,專門拎一個 `util.js` 文件出來也是非常不錯的選擇——但是怎麼想都不覺得 gulp / grunt 這些是很大的工程。

那麼把函數放要用的那個文件本身?反正我覺得不可取。

那麼下一種方法,就是可以捨棄 `left-pad` 而引入一個 `lodash` 之類的包,這種做法是比較可取的。不過每個人有每個人的想法,也有自己的潔癖。引入的包能盡量小就盡量小,雖然我不是這類人,但是也能表示理解。

很多人就是沒這種代碼潔癖——放到版本庫裡面的幾個項目裡面有好多充斥著一樣代碼拷來拷去的文件,我知道這裡又有人會噴我了,儘管來吧。反正我是很排斥這種做法的。

用到 `left-pad` 好,自己寫一個接到 `util.js` 裡面去。然後下一個項目又用到了,直接把這段代碼拷過來放到新項目裡面去。

有人可能會說再寫一遍啊——我認為還不如拷一遍呢。

然後喜歡造輪子的人(比如說我),最後會把這些函數抽出來搞成自己的庫,不放到各項目的版本庫裡面去了,那樣實在太髒了,而是統一放到 NPM 上——就跟 `lodash` 一樣。

那麼不喜歡造輪子的人怎麼辦呢?引入一個 `lodash`。那麼,想就用這一個功能的人怎麼辦呢?結合上述答案——函數沒地方放,也不想各種重複寫,以後重複用,那麼直接寫個 `left-pad` 放 NPM。

所以說有什麼問題呢?!為什麼那麼多人要來噴呢?!

之所以包這麼多,很大程度上正是因為 Node.js 平台標準庫嚴重不足

Node.js 源碼一直在迭代,有用的標準庫一直在更新,只不過你們不關注罷了。但是更新也得考慮到歷史包袱,Node 5 的代碼放到 Node 4 裡面很多還是可以跑的,並不是 Py3 和 Py2 的關係。

至於前端那就更是這樣了。

我雖然不會前端,但是耳濡目染,也知道一些。

至於什麼「瀏覽器端代碼為了下載速度模塊越小越好」的話,真不知道他們怎麼說出來的, jQuery 用了這麼多年,jQuery 插件各種重複功能的代碼,也沒見他們怎麼哀嚎過體積大的問題啊。對了,新浪這類的網站曾經一個網頁上同時並存多個版本 jQuery 的哦!

你知道有手機瀏覽器這種東西么?你知道國內還是有一大片人用的 2G 網路么?反正我是不敢在手機端頁面用 jQuery。引入一個三方庫都要思忖再三。而且,「新浪這類的網站曾經一個網頁上同時並存多個版本 jQuery 的哦」,請不要拿這種壞榜樣舉例,謝謝。

用 `webpack` 之類的的確能打包,並且我們就是 `gulp` + `webpack`,但是在國內大環境下面,從人力資源成本考慮——你不能強制要求所有人都會。當然你要說連這點水平都沒有還是不要來寫代碼了的話,那我無話可說。

可是我記得人家Python社區里的lib都是些要麼很繁瑣(比如一個功能實現起來好幾百行代碼),要麼很全面(一個包裡面一堆小func)的lib呢。

NPM 上不缺乏這類庫,只不過每個人有自己的習慣,喜歡引入一個大而全的還是小而精的。

接下去,NPM 這種方式的包管理方案的確是我最喜歡的一種。

我說的是方案,並沒有從安全性等各方面去探討。NPM 的這個網站的確有很多不足的地方,這一次就暴露出來了。但是並不能因此去否認它的思想。

而且這一次也只是敲響了警鐘,作為負責的開發者來說,並不會去隨意 `unpublish` 這些包——所以這是 NPM 不完善的機制和開發者主觀意願造成的事故,並不是它的包管理思想造成的。

舉個例子,A 依賴 B 包的 1.0.0 版本,C 依賴 B 的 2.0.0 版本。你的項目依賴 A 和 C。

有多少語言的包管理能做到?Python?Ruby?

當然我也不把話說絕對,不過這種依賴形式的包管理的確不多。這正是我看中 NPM 的原因之一。

而且很多人說 NPM 上面的包連文檔都沒有。

難道 Python Ruby 上面所有包都有么?我學 Python 的時候往上面發的包就沒文檔。

但是我在 NPM 上面發的包文檔測試都非常齊全。

每個社區都有新手,都有不是那麼遵循規範的人,只不過多少而已。怎麼就能扯到這個提問的答案呢?

最後,結論:不是他們忘了怎麼編程,而是他們從來就沒學會過編程!

Node.js 裡面,或者前端裡面,新手的確很多,比如說我。但是不缺乏有佈道者、大神級別的存在。

這跟 #一個函數作為一個包# 並沒有必然聯繫,哪個社區都有新手。

只不過 Node.js 社區思想如此,原因我也在上面作了我自己想法的回答。而且我支持這種思想,同時也比較期望 NPM 能作更多規範化上面的改進——#一個函數作為一個包# 和規範化是兩碼事。


HackerNews 上有相關討論:

NPM and Left-Pad: Have We Forgotten How to Program?

下面的回復我簡直搞不清楚是認真的還是來搞笑的了:

1樓:天哪! is-positive-integer/index.js at v1.0.0 · tjmehta/is-positive-integer · GitHub 只有兩三行代碼卻依賴了三個包!JavaScript 比 Java 還要企業級啦!他們已經達到一個全新的瘋狂境界了!

var passAll = require("101/pass-all")
var isPositive = require("is-positive")
var isInteger = require("is-integer")

module.exports = passAll(isPositive, isInteger)

2樓:Balabala,小的模塊更容易理解,更容易測試……

3樓:簡直荒謬,照你這麼說,我C語言中不應該 #include &,而是應該分別#include &、#include &、#include & 嘍?

4樓:小的模塊文件體積更小,瀏覽器中下載速度更快

5樓:原來如此,我忘了JavaScript連個能用的 Linker 都沒有

6樓:4樓是豬隊友,Webpack2和Google Closure Compiler 都能將沒用到的函數移除掉

7樓:「小的模塊更容易理解」不是擋箭牌,照你這麼說,有了 is-positive-integer ,接下來該寫些什麼模塊?is-bigger-than-5? word-starts-with-capital-letter? add-1-to-a-number?

8樓:如果你打算寫 is-bigger-than-5 ,我推薦你require five.js 這個包作為依賴,GitHub - jackdcrawford/five: Gives you five。

9樓:five.js 還缺少 leftPad 功能,我已經提交了 pull request,Add function to add left padding to five. by brentburg · Pull Request #234 · jackdcrawford/five · GitHub 。

10樓:你們這些算什麼,將「小而美」堅持到極致,一行流來了,100%的測試覆蓋率哦:

noop3/index.js at master · sindresorhus/noop3 · GitHub

看到這裡我已經笑岔氣了,NPM快被玩壞了。left-pad已經成為一種行為藝術,像病毒一樣在其它語言陣營傳播開來了:https://pypi.python.org/pypi/left-pad/ 、NuGet Gallery 、GitHub - stettberger/ispositive: Haskell Module: Integer.IsPositive

————

下面談正經的。先看看NPM的繁榮,根據這裡的統計信息,NPM上包的數量排名第一:Modulecounts 。Don"t Just See!Observe!不要只看到繁榮的表象。

來看一下 npm Most depended-upon packages:

有沒有發現什麼問題?lodash、underscore,兩個幾乎相同的包用來彌補JS標準庫的不足;q、async、bluebird,用來填Callback hell的坑,Promise 應列入標準庫我想沒人反對吧?對於這些,我們尚能以JS歷史包袱為借口,它們總會慢慢加到標準庫中的。但,mkdirp 這貨是怎麼回事?我知道肯定會有一些 JS 程序員跳出來說這叫「小而美」,這叫「自由軟體」,這沒有對錯之分只是「哲學」上的差異。我現在發現跟這些人沒有辦法交流,只好任他們瘋下去直到他們自己也受不鳥了為止:

fs-extra 簡介最是搞笑:

Why?

I got tired of including mkdirp, rimraf, and cp -r in most of my projects.

由於Node.js標準庫實在太小,隨便一個簡單的功能都需要require一個包,想知道有多少人被這給逼瘋嗎?Search results for fs extra 。 沒辦法,只好任他們自己瘋夠。

(事實上他們喜歡往相反的方向走,lodash一個函數發一個包: lodash-modularized npm ,這是我最不能理解的地方 ,一個文件只放一個函數我還能接受,但至於每個函數都分別publish一個包么?這就明顯不是工具是否支持優化的問題了。完全找不到這樣做的理由

對了,Five.js (Five by jackdcrawford),瞧它那Logo,我覺得「Gives you five」的正確翻譯應該是:「給你一巴掌,抽醒了沒有?」

你可以拿包的數量來衡量社區的活躍度,但我更想知道的是 Node.js 生態的健康度和成熟度,包的數量代表不了什麼,就像你不能用代碼行數來衡量程序員的水平一樣。

之所以包這麼多,很大程度上正是因為Node.js平台標準庫嚴重不足,與那些Battery inside平台相比,自然要多出很多輪子。像 fs.watch recursive ,就有很多重複的包來實現。可能有人說這是社區指導思想不同,Node.js 讓社區多個實現自由競爭,不管你怎麼胡扯,這功能終歸要合併到標準庫當中的吧,這總不能是新常態吧?

再看看其它的包:util-merge 是什麼? util._extend 竟然標準庫中不開放、qs與 querystring。fs-readdir-recursive、graceful-readlink、finalhandler 這些竟然還要單獨一個包實現。

關於標準庫,我說了你們不聽,TJ說了你們就聽了是吧?

https://twitter.com/tjholowaychuk/status/665308077420183553

我看人家Maven、RubyGems是統計的包的數目,NPM分明是統計的函數的數目,隨便一個簡單的函數都會提交一個包。人家要是把一框架全拆成幾百個小的包的話也能假裝無比繁榮。

總之,可以這樣說, 雖然活躍,但卻沒有凝聚力

再看看NPM上包的質量,我覺得 npm shrinkwrap 得是強制選項。沒有辦法保證這些包的作者遵守 Semantic Version。left-pad 作者即使不unpublish,它只需要改個API名稱結構 :

module.exports = { leftPad:function() {/*.....*/} }

但只升級小版本號,照樣能把一堆依賴它的包幹掉。不要說這種事情概率太低,就算你不要版本號精確匹配,在 package.json 裡面至少要謹慎點寫 "~1.0.0",像 Babel.js 的 babel/package.json at v6.7.4 · babel/babel · GitHub 都寫成 "^1.0.0",這樣別人更改了API方法名稱,即使升級成 "1.9.0",你還是被坑!自己用 npm semantic version calculator 看看。對,我說的就是 Babel.js 他自己!Babel.js 自己一聲不吱將 generateUidBasedOnNode 改名成 generateUidIdentifierBasedOnNode 就只升了小版本號啊:Work with Babel &>= 5.5.0 by JexCheng · Pull Request #2 · codemix/babel-plugin-closure-elimination · GitHub

其實一開始他們讓我把包發到NPM上我是拒絕的,因為NPM上查看JS的API文檔只有簡單的README Markdown,說社區繁榮,連個JavaDoc那樣結構化的像樣的文檔都沒人寫,PHP的社區文檔都比Node.js不知道高到哪裡去了。看看,Grunt依賴了一個叫 getobject 的包,但特么的這些包壓根沒有文檔:GitHub - cowboy/node-getobject: get.and.set.deep.objects.easily = true;

至於什麼「瀏覽器端代碼為了下載速度模塊越小越好」的話,真不知道他們怎麼說出來的, jQuery 用了這麼多年,jQuery插件各種重複功能的代碼,也沒見他們怎麼哀嚎過體積大的問題啊。什麼時候前端開始對文件體積這麼敏感了? browserify 不是前端用的嗎?寫個 require("url") 生成的文件有多大?明明這種問題應該盡量交給Closure Compiler這樣的工具去優化,非要人肉去拆分。再說了,CDN呢?underscore.js 整個從公用CDN載入,和僅將用到的函數編譯合併到應用代碼里,哪個更好?好吧,這道理還講不通的話,只好隨他們瘋去吧。

還有,一些人就不要像個鸚鵡一樣跟我講一堆你們道聽途說來的軟體工程理論吧,什麼模塊化DRY,以後引用這些名詞也要加量詞了,請告訴我需要往項目中加幾毫升的模塊化?無力反駁。

最後,結論:不是他們忘了怎麼編程,而是他們從來就沒學會過編程!

Doing a for loop is not equal to re-inventing the wheel. Words and phrases have...

忘了怎麼編程系例:

Nodejs里能否用Event emitter取代Callback function? - Jex Cheng 的回答

Methods — bootstrap-datepicker documentation


每個社區都有自己的內涵和思想,被你們說的一無是處,也沒見其他語言的包管理工具多麼健壯易用。


歸根結底還是 JS 內置的庫功能太少,好好和 TC39 扯扯吧。

當然,有些「簡單」函數很值得發布成庫,比如 Full-unicode 的大小寫轉換……


我只說left-pad.

用現成的包,就是為了省事,有好的性能,不用考慮edge-case,不用考慮奇怪的輸入.

但是left-pad顯然沒有做的這些.

Talk is cheap. Show you the code.

left-pad/index.js at master · azer/left-pad · GitHub

function leftpad (str, len, ch) {
str = String(str);
var i = -1;
if (!ch ch !== 0) ch = " ";
len = len - str.length;
while (++i &< len) { str = ch + str; } return str; }

最明顯的錯誤就是,如果ch是個長度大於1的字元串怎麼辦?

leftpad("a", 2, "----") 輸出 "----a",不彆扭嗎?

當然js本身就比較鬆散,看一下String.prototype.repeat的規範,允許重複次數count不是整數,中間還各種cast...但是人家還是檢查了count是不是負數和無限大.


寫測試用例的包都值得考慮下。

我想你們不會為了一個簡單又不起眼的函數寫足夠的測試用例吧!


這是要貫徹 dependent type 的思想嗎?


容我先問一個問題, 大家寫代碼時是怎麼知道有像leftPad這樣的現成的小型模塊的存在的?


永遠不要從工程和技術角度來思考前端世界,否則你會 lost in huawangye。

老闆喜歡,吹牛資本,技術證明,太多太多理由了。

否則就只能寫一個 string 點 js 了不是,多少人將無包可寫呀


不管合不合理

當下最好的方法就是這樣了

而且會存在很長一段時間


說明JS語言內嵌特性不足,而這些缺失的能力又沒有通過node.js標準庫補上。

所以我還是用Perl吧。Perl滿賽!!!


這次的 left-pad 事件讓我這種 npm 用戶擔心。雖然類似的問題在之前 npm 安裝,依賴包升級中已經顯露。

回到題主這個問題,從npm用戶的角度來看,很難去判斷合理不合理吧?

一個函數,但是解決很麻煩的問題呢?

一個問題,對於別人很麻煩對於你很簡單呢?

實際使用過程中,應該是我直接依賴的包,於我就是合理。

但是讓我擔心的是

默認的 ^version 略坑, 如此簡單的 unpublish + re-publish 深坑。導致下一次 執行 npm install 並不可靠,直接依賴包也變得不太可信了。


大開眼界,這不就是我剛學編程的時候,為了看起來牛逼而喜歡乾的事情嗎。我以為只有未成年人會這麼干。


這已經演化出一場行為藝術了……

於是有了 left-pad.io

他們是這麼說的:

left-pad被unpublish給nodejs社區帶來了巨大的災難和大量財富的損失。於是,我們及時推出了 left-pad.io:一個微服務解決方案

還有這裡:

## Are there any limits?

Padding and the input string are limited to 1024 characters in the free
version, because we have to monetize to have enough runway to launch
`right-pad.io` in Q3 2017.

## Can I buy an enterprise license?

Yes. Email root@left-pad.io with your account and ABA routing numbers.

## Who?

笑死了哈哈「who?」


關於瀏覽器端,其實完全可以一個package包含多個文件來解決,使用時只require其中一個文件(函數)就行。現在把包當函數來用實在有點過了,npm上搜個pad出來一大堆包(函數),看著就有點頭大。而且,粒度分得那麼細,一個項目依賴的包那麼多,每個包又是由不同人維護,怎麼保證這些作者都能信得過?說不定其中哪個某天心血來潮像left-pad那樣unpublish一下,又有多少項目要撲街啊。。


一個module只做一件事,並且把這件事做好,這是unix哲學啊

比如我就把我寫的一個很簡單的插值函數發到npm上去了,可惜沒星星,沒issue,沒pr,不開心。

嗯歡迎大家來參觀……

https://www.npmjs.com/package/node-idw


一個關於npm的視頻講解INTA課程主頁


裡面提到的leftpad作者,前兩天來加我Linkedin...嚇我一大跳,竟然是我大澳村的


因為有webpack


npm上的包多的數不勝數,使用時也會很麻煩,因為你需要查看使用量來決定用哪個。此外,除了google沒什麼好途徑告訴你哪個包更好些。

哦,對了。有時候你為了看一個實現,你還會發現居然在up upstream里哦~

一個小功能需要一堆依賴,然後再用工具把這堆東西打成小文件。

這的確是npm現實存在的問題。

webpack拯救世界!(斜眼)


推薦閱讀:

如何使用正則表達式得到一個 URL 中的主域名,不用正則還有什麼方法?
如何理解 "面向對象構建,函數式運行才是軟體開發的王道"?
ES next中async/await proposal實現原理是什麼?

TAG:JavaScript | Nodejs | npm |