這並非本身第一次發npm包, 因此這裏沒有多少入門的知識。在此以前已經有一篇前端腳手架,聽起來玄乎,實際呢?,但這一次的npm包和上一次的不是一個概念,前者只是一個腳本工具,而這個npm包是平常開發中方法和組件的集合, 是一個庫。
在讀本文前,假定你已經對npm包有必定概念,熟悉Babel編譯和webpack打包的常規用法,知道一些前端工程化的知識。假如你也想本身發佈一個npm倉庫,但對這一塊瞭解的不是不少,推薦webpack官方的建立一個 librarycss
在前端平常開發中,引入npm庫,執行webpack構建已是一件不能再日常的事情。但大多數時候,咱們不關心這個npm庫是怎樣構成的,咱們只須要知道怎麼使用,像antd;在工程化成熟的公司,也不關心webpack的配置究竟是怎樣的,只須要npm run start或npm run build去啓動一次熱加載或打包。可是若是你是編寫一個npm倉庫,這些東西你都須要知道。從那時的無知提及,起初,我用公司的構建工具(相似於roadhog)去打包個人庫,沒有坎坷,構建出一個2M多的包併成功發佈。前端
在測試項目中引入,構建成功。node
import { EnhanceTable, WithSearch } from 'antd-doddle'; // 引入倉庫
在瀏覽器中打開,打擊開始到來:
提示要引入的對象沒有正確導出,就是沒有作module.export,因此這是一個打包模式的問題,output.libraryTarget須要瞭解一下。react
webpack的output.libraryTarget決定了打包時對外暴露出來的對象是那種模式,默認是var,用於script標籤引入,該模式也是咱們平常開發構建最經常使用的模式,除了這一種,還支持的經常使用選項有:webpack
因此在這裏咱們須要設置兩個屬性來明確打包模式git
library: 'antd-doddle', libraryTarget: 'umd',
上圖是用roadhog打包出來的結果,其顯示的是開啓gzip後能夠壓縮到的大小,第一次打包的實際大小大概在2M(antd+moment+react+css),後面仔細一想,公司的組件庫也才300kb啊,本身是否是哪裏搞錯了,因此接着就有了下面的探尋之路。es6
["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css", }]
externals: { react: { commonjs: 'react', commonjs2: 'react', amd: 'react', }, antd: { commonjs: 'antd', commonjs2: 'antd', amd: 'antd', }, moment: { commonjs: 'moment', commonjs2: 'moment', amd: 'moment', }, }
打包大小優化至此就搞定了,但後面發現用roadhog打包庫有一些很難解決的難題,爲了解決還得去了解他源碼邏輯,因此後面仍是本身寫了一個webpack,很是簡單的配置。github
在寫這個庫以前,我曾想到在咱們平常構建時有下面這樣一段配置:web
rules: [{ test: /\.js$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader', query: { presets: ['@babel/preset-env', '@babel/preset-react'] } }
這段配置是告訴webpack,node_modules中引用的代碼不須要再由babel編譯一次,但這些代碼仍是會被打包進dist文件的。在如今前端的主流開發套路中,被引用的庫更但願是一個只編譯而沒有被打包過的,支持按需加載的庫。因此dist中被編譯打包過的代碼再次被打包, 這樣就會有沒必要要的代碼出現。因此這樣來看,咱們只須要將代碼編譯。babel,沒錯,就是它,可是多文件編譯,仍是找個第三方構建工具比較好,我選擇了gulp,直接上代碼:npm
// 發佈打包 gulp.task('lib', gulp.series('clean', () => { return gulp.src('./src/**/*.js') .pipe(babel()) .pipe(gulp.dest('./lib')); }, 'lessToLib')); // lessToLib用於將less文件拷貝貸lib文件夾
編譯事後大概是下面這樣的,確實只編譯沒打包,函數基本原樣:
本覺得到這就結束了,可是這纔開始。只編譯不打包消除沒必要要的代碼只是很小的緣由,重要的東西我以爲換一行說比較好。
不顧語文老師的責罵換行,那什麼纔是是最重要的:按需打包(tree shaking),對於這種組件和方法庫,做爲使用者,咱們但願他能支持按需打包,像lodash和antd這樣。因此懷着好奇的心理我去看了他們的package.json,而後發現了這樣的配置:
"main": "lib/index.js", "module": "es/index.js", "name": "antd",
除了認知中的main入口定義,還多了一個module入口.爲何須要這樣呢,和我同樣無知的,能夠先讀webpack官方的tree shaking,若是不夠直觀,能夠再看一位大佬寫的一篇相關文章聊聊 package.json 文件中的 module 字段。看下面代碼:
// es6 模塊寫法 fun.js export function square(x) { return x * x; } export function cube(x) { return x * x * x; } // commonJs寫法 fun.js exports.square = function(x) { return x * x; } exports.cube = function(x) { return x * x * x; } // index.js引入 import { square } from './fun.js'; const res = square(10); console.log('res:', res);
簡單來講,就是index.js打包編譯時,引入commonJs寫法的fun.js,打包會將square與cube兩個函數同時打進來。而引入es6寫法的fun.js,只會將square打包。這樣的操做,對於如今的主流趨勢,就是必須的優化,特別對於lodash和antd這種龐大的庫。而要使咱們的庫支持這樣的操做,咱們須要編譯時,禁止babel將es6的module引入方式編譯,其實只須要在前面的基礎上多配置一個參數:
"@babel/preset-react" // lib的打包方式 ["@babel/preset-env", { "modules": false }] // 保留es6模塊引入的方式
獲得的是下面這樣的結果:
和上面的lib對比,感受更接近原始代碼。至此,編譯已結束,可是咱們還須要在package.json中加上相應的配置:
"description": "antd後臺項目前端組件封裝和方法庫", "main": "lib/index.js", "module": "es/index.js", "scripts": { "build": "webpack --config webpack.config.js", "lib": "gulp lib", "es": "gulp es", "prepublish": "gulp && webpack --config webpack.config.js" }, "files": [ "es", "dist", "lib", "utils" ],
由於個人庫主要包括組件和方法,我把方法放到一塊兒,經過utils做爲默認輸出。而後項目中引入是這樣的:
import { EnhanceTable, WithSearch }, utils from 'antd-doddle'; // 要用裏面的方法須要再分解一次或經過utils.xxx const { DATE_FORMAT, idCodeValid } = utils;
雖然感受上不復雜,可是總感受彆扭,若是你用過dva,就見過下面這樣的引入:
import { routerRedux } from 'dva/router'; import dva from 'dva';
因此我去學習了一下,發現要這樣實現也不難
分三步,分目錄打包,增長一個輸出,並增長內部私有映射,package.json增長一個這個映射目錄的輸出。具體可查看項目源碼。實現後,項目引入是這樣的:
import { EnhanceTable, WithSearch }, utils from 'antd-doddle'; import { DATE_FORMAT, idCodeValid } from ‘antd-doddle/utils’; // 一步到位
之前咱們不少命令如webpack,gulp命令只有在全局安裝(npm install xxx -g)才能夠在命令行中直接運行或在項目中安裝,經過script定義執行,但在npm5.2之後,咱們能夠只項目中安裝,而後經過新增的npx執行。好比上面scripts中定義的lib打包("lib": "gulp"),咱們能夠直接在命令行中用:
npx gulp
有可能你和我同樣,在處處都是牆的世界,須要在npm,cnpm,公司的npm registry三者之間來回切換,每次都須要這樣:
npm set registry 'https://registry.npm.taobao.org/'
麻煩有沒有? 幸虧,這世界有不少牛逼的人,nrm registry是個很好用的工具,下面這樣:
// 安裝 npm install -g nrm // 設置入口npm,cnpm,company nrm add npm 'http://registry.npmjs.org' nrm add cnpm 'https://registry.npm.taobao.org' nrm add vnpm 'http://npm.company.com' // 切換入口到淘寶入口 nrm use cnpm
一個春節本身斷斷續續就在倒騰這個,收穫仍是挺大的。後面本身會慢慢去學習怎麼加入demo‘,加入單元測試,去建造一個完整的npm庫。
源碼庫:github
npm倉庫地址:npm