吾輩的博客原文地址: https://blog.rxliuli.com/p/c3...
吾輩已經寫了一個 TypeScript/JavaScript Cli 工具 liuli-cli,若有須要可使用這個 Cli 直接生成一個開箱即用 SDK 項目,而後就能夠直接開始寫本身的代碼,不須要太過關心下面的內容了 -- 由於,它們都已然集成了。
若是咱們想要寫一個 JavaScript SDK
,那麼就不太可能將全部的代碼都寫到同一個 js 文件中。固然了,想作的話的確能夠作到,但隨着 JavaScript SDK
內容的增長,一個 js 文件容易形成開發衝突,以及測試上的困難,這也是現代前端基本上都依賴於打包工具的緣由。前端
現今最流行的打包工具是 webpack,然而事實上對於單純的打包 JavaScript SDK 而言 webpack 顯得有些過重了。webpack 終究是用來整合多種類型的資源而產生的(ReactJS/VueJS/Babel/TypeScript/Stylus
),對於純 JavaScript 庫而言其實並無必要使用如此 強大 的工具。而 rollup 就顯得小巧精緻,少量配置就能馬上打包了。node
該記錄的代碼被吾輩放到了 GitHub,有須要的話能夠看下。
開始以前,咱們必需要對如下內容有所瞭解jquery
// src/wait.js /** * 等待指定的時間/等待指定表達式成立 * @param {Number|Function} param 等待時間/等待條件 * @returns {Promise} Promise 對象 */ function wait(param) { return new Promise(resolve => { if (typeof param === 'number') { setTimeout(resolve, param) } else if (typeof param === 'function') { var timer = setInterval(() => { if (param()) { clearInterval(timer) resolve() } }, 100) } else { resolve() } }) } export default wait // src/fetchTimeout.js /** * 爲 fetch 請求添加超時選項 * 注:超時選項並不是真正意義上的超時即取消請求,請求依舊正常執行完成,但會提早返回 reject 結果 * @param {Promise} fetchPromise fetch 請求的 Promise * @param {Number} timeout 超時時間 * @returns {Promise} 若是超時就提早返回 reject, 不然正常返回 fetch 結果 */ function fetchTimeout(fetchPromise, timeout) { var abortFn = null //這是一個能夠被 reject 的 Promise var abortPromise = new Promise(function(resolve, reject) { abortFn = function() { reject('abort promise') } }) // 有一個 Promise 完成就馬上結束 var abortablePromise = Promise.race([fetchPromise, abortPromise]) setTimeout(function() { abortFn() }, timeout) return abortablePromise } export default fetchTimeout // src/main.js import wait from './wait' import fetchTimeout from './fetchTimeout' /** * 限制併發請求數量的 fetch 封裝 */ class FetchLimiting { constructor({ timeout = 10000, limit = 10 }) { this.timeout = timeout this.limit = limit this.execCount = 0 // 等待隊列 this.waitArr = [] } /** * 執行一個請求 * 若是到達最大併發限制時就進行等待 * 注:該方法的請求順序是無序的,與代碼裏的順序無關 * @param {RequestInfo} url 請求 url 信息 * @param {RequestInit} init 請求的其餘可選項 * @returns {Promise} 若是超時就提早返回 reject, 不然正常返回 fetch 結果 */ async _fetch(url, init) { const _innerFetch = async () => { console.log( `執行 execCount: ${this.execCount}, waitArr length: ${ this.waitArr.length }, index: ${JSON.stringify(this.waitArr[0])}`, ) this.execCount++ const args = this.waitArr.shift(0) try { return await fetchTimeout(fetch(...args), this.timeout) } finally { this.execCount-- } } this.waitArr.push(arguments) await wait(() => this.execCount < this.limit) // 嘗試啓動等待隊列 return _innerFetch() } } export default FetchLimiting
安裝 rollup
webpack
npm i rollup -D
在根目錄建立一個 rollup.config.js
配置文件git
export default { // 入口文件 input: 'src/main.js', output: { // 打包名稱 name: 'bundlea', // 打包的文件 file: 'dist/bundle.js', // 打包的格式,umd 支持 commonjs/amd/life 三種方式 format: 'umd', }, }
添加一個 npm script
github
"scripts": { "build": "rollup -c" }
而後運行 npm run build
測試打包,能夠看到 dist 目錄下已經有 bundle.js
文件了web
好了,到此爲止咱們已經簡單使用 rollup 打包 js 了,下面的內容都是可選項,若是須要能夠分節選讀。
然而,咱們雖然已經將 main.js 打包了,然而實際上咱們的代碼沒有發生什麼變化。即:本來是 ES6 的代碼仍然會是 ES6,而若是咱們想要儘量地支持更多的瀏覽器,目前而言仍是須要兼容到 ES5 才行。npm
因此,咱們須要 babel
,它可以幫咱們把 ES6 的代碼編譯成 ES5。json
附:babel 被稱爲現代前端的 jquery。
首先,安裝 babel 須要的包promise
npm i -D rollup-plugin-babel @babel/core @babel/plugin-external-helpers @babel/preset-env
在 rollup.config.js
中添加 plugins
import babel from 'rollup-plugin-babel' export default { plugins: [ // 引入 babel 插件 babel({ exclude: 'node_modules/**', }), ], }
添加 babel 的配置文件 .babelrc
{ "presets": [ [ "@babel/preset-env", { "modules": false } ] ], "plugins": ["@babel/plugin-external-helpers"] }
再從新運行 npm run build
,能夠看到 bundle.js
中的代碼已經被編譯成 ES5 了。
那麼,生產中的代碼還須要作什麼呢?是的,壓縮,減少 js 代碼的體積是必要的。接下來,咱們還須要使用 uglify
壓縮咱們打包後的 bundle.js
代碼。
首先仍然是安裝 uglify
相關的包
npm i -D rollup-plugin-uglify
而後在 rollup.config.js
中引入插件就行了
// 注意,這裏引入須要使用 { uglify } 而非 uglify,由於 uglify 導出自身時使用的是 exports.uglify import { uglify } from 'rollup-plugin-uglify' export default { plugins: [ // js 壓縮插件,須要在最後引入 uglify(), ], }
若是咱們想要須要多人協做統一代碼風格,那麼可使用 ESLint 來強制規範。
首先,全局安裝 eslint
npm i eslint -g
而後使用 eslint cli
初始化
eslint --init
下面的三項問題選擇
Use a popular style guide
Standard (https://github.com/standard/standard)
JavaScript
y
而後,咱們發現項目根目錄下多出了 .eslintrc.js
,這是 eslit 的配置文件。然而,咱們須要對其稍微修改一下,否則若是咱們的代碼中出現了瀏覽器中的對象,例如 document
,eslint 就會傻傻的認爲那是個錯誤!
修改後的 .eslintrc.js
配置
module.exports = { extends: 'standard', // 添加了運行環境設定,設置 browser 爲 true env: { browser: true, }, }
當咱們查看打包後的 bundle.js
時發現 eslint 給咱們報了一堆錯誤,因此咱們須要排除掉 dist 文件夾
添加 .eslintignore
文件
dist
添加 rollup-plugin-eslint
插件,在打包以前進行格式校驗
npm i -D rollup-plugin-eslint
而後引入它
import { eslint } from 'rollup-plugin-eslint' export default { plugins: [ // 引入 eslint 插件 eslint(), ], }
這個時候,當你運行 npm run build
的時候,eslint 可能提示你一堆代碼格式錯誤,難道咱們還要一個個的去修復麼?不,eslint 早已考慮到了這一點,咱們能夠添加一個 npm 腳本用於全局修復格式錯誤。
"scripts": { "lint": "eslint --fix src" }
而後運行 npm run lint
,eslint 會盡量修復格式錯誤,若是不能修復,會在控制檯打印異常文件的路徑,而後咱們手動修復就好啦
其實很簡單,只要在 rollup.config.js
啓用一個配置就行了
export default { output: { // 啓用代碼映射,便於調試之用 sourcemap: true, }, }
首先移除掉根目錄下的 rollup.config.js
配置文件,而後建立 build 目錄並添加下面四個文件
// build/util.js import path from 'path' /** * 根據相對路徑計算真是的路徑 * 從當前類的文件夾開始計算,這裏是 /build * @param {String} relaPath 相對路徑 * @returns {String} 絕對路徑 */ export function calcPath(relaPath) { return path.resolve(__dirname, relaPath) }
// build/rollup.config.dev.js import { eslint } from 'rollup-plugin-eslint' import { calcPath } from './util' import { name } from '../package.json' export default { // 入口文件 input: calcPath('../src/main.js'), output: { // 打包名稱 name, // 打包的文件 file: calcPath(`../dist/${name}.js`), // 打包的格式,umd 支持 commonjs/amd/life 三種方式 format: 'umd', // 啓用代碼映射,便於調試之用 sourcemap: true, }, plugins: [ // 引入 eslint 插件,必須在 babel 以前引入,由於 babel 編譯以後的代碼未必符合 eslint 規範,eslint 僅針對咱們 [本來] 的代碼 eslint(), ], }
// build/rollup.config.prod.js import babel from 'rollup-plugin-babel' // 注意,這裏引入須要使用 { uglify } 而非 uglify,由於 uglify 導出自身時使用的是 exports.uglify import { uglify } from 'rollup-plugin-uglify' import { eslint } from 'rollup-plugin-eslint' import { calcPath } from './util' import dev from './rollup.config.dev' import { name } from '../package.json' export default [ dev, { // 入口文件 input: calcPath('../src/main.js'), output: { // 打包名稱 name, // 打包的文件 file: calcPath(`../dist/${name}.min.js`), // 打包的格式,umd 支持 commonjs/amd/life 三種方式 format: 'umd', }, plugins: [ // 引入 eslint 插件,必須在 babel 以前引入,由於 babel 編譯以後的代碼未必符合 eslint 規範,eslint 僅針對咱們 [本來] 的代碼 eslint(), // 引入 babel 插件 babel({ exclude: calcPath('../node_modules/**'), }), // js 壓縮插件,須要在最後引入 uglify(), ], }, ]
// build/rollup.config.js import dev from './rollup.config.dev' import prod from './rollup.config.prod' // 若是當前環境時 production,則使用 prod 配置,不然使用 dev 配置 export default process.env.NODE_ENV === 'production' ? prod : dev
修改 npm 腳本
"scripts": { "build:dev": "rollup -c build/rollup.config.js --environment NODE_ENV:development", "build:prod": "rollup -c build/rollup.config.js --environment NODE_ENV:production", "build": "npm run build:dev && npm run build:prod", }
那麼,關於使用 rollup 打包 JavaScript 的內容就先到這裏了,有須要的話後續吾輩還會繼續更新的!