有沒有遇到這樣一種狀況,你花了很大精力在業務項目中寫了一個組件,你以爲這個組件很通用,除了當前的業務場景還應該有其餘的應用場景,因此你想開源這個組件,但又不知道從何入手。這篇文章就來聊聊如何開源一個前端組件,不管是業務中已有的組件仍是新的組件。css
能夠在 Github 上新建一個項目,這裏取名 "component-example",而後使用 git clone
命令克隆到本地進行開發。也能夠先在本地建好文件,而後使用 git remote add
命令將本地項目與遠程項目進行綁定。前端
進入項目根目錄,執行 npm init
命令初始化項目,這裏沒什麼注意點,一路默認便可,命令執行結束後會獲得一個 "package.json" 文件。node
在項目根目錄新增 "src" 文件夾,進入 "src",新建 "index.js"、"index.css"、"App.js" 和 "App.css" 4 個文件。react
"index.js"webpack
import React from 'react'; import ReactDOM from 'react-dom'; import styles from './index.css'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root'));
"APP.js"git
import React from 'react'; import styles from './App.css'; export default class APP extends React.Component { render() { return <div>Hello, React Component!</div> } }
安裝 react 和 react-dom:web
npm install --save react react-dom
這裏使用 webpack 做爲打包構建工具。執行下面命令安裝 webpack:npm
npm install --save webpack webpack-cli
新建 "webpack.common.js"、"webpack.dev.js" 和 "webpack.prod.js" 3 個文件,用於配置 webpack。由於開發環境和生產環境的須要不同,好比開發環境須要 source map 以便快速定位問題,而生產環境須要文件儘可能小以減小網絡加載時間,因此須要維護兩套不一樣的構建配置。雖然是兩套配置,但仍然有不少配置是相同的,因此將相同的配置抽成 "webpack.common.js" 文件,開發環境特有的配置放在 "webpack.dev.js",生產環境特有的配置放在 "webpack.prod.js" 中。json
"webpack.common.js"bash
const path = require("path"); module.exports = { entry: "./src/index.js", output: { path: path.resolve(__dirname, "dist"), filename: "component-example.js", libraryTarget: "umd" }, module: { rules: [ { test: /\.css$/, loader: "style-loader!css-loader?modules" }, { test: /\.(jpg|png)$/, loader: "url-loader?limit=25000" } ] }, resolve: { extensions: [".jsx", ".js"] }, plugins: [] };
"webpack.dev.js"
const merge = require("webpack-merge"); const common = require("./webpack.common.js"); module.exports = merge(common, { mode: "development", devtool: "inline-source-map", watch: true });
"webpack.prod.js"
const merge = require("webpack-merge"); const common = require("./webpack.common.js"); module.exports = merge(common, { mode: "production", externals: ["react", "react-dom"] });
上面的配置中用到了 style-loader
、css-loader
、url-loader
和 webpack-merge
,也須要安裝一下:
npm install --save-dev css-loader style-loader url-loader webpack-merge
"webpack.common.js" 中,output 的 path 和 filename 指定構建結果存在 /dist/component-example.js 中;libraryTarget 的值爲 umd,這條配置必不可少,代表將 component-example.js 文件暴露爲全部模塊定義下都能運行的方式,便可以被當作 CommonJS 模塊、 AMD 模塊等不一樣類型的模塊,從而能夠在不一樣環境下運行。
另外,在 "webpack.common.js" 中還引入了 3 個 loader: style-loader
、css-loader
和 url-loader
,前二者幫助 webpack 預處理樣式文件,這裏指 CSS 文件;url-loader
用於將本地文件處理成 base64,通常用於引用背景圖時,若是沒有須要,也能夠刪除。
"webpack.dev.js" 中有 3 個額外的配置。
development
和 production
兩個值可選,不一樣的值會觸發 webpack 不一樣的 Plugin,好比 development
會觸發 NamedChunksPlugin,能夠將 chunk id 變成字符串標識符,而 production
則會觸發 UglifyJsPlugin,能夠 uglify 代碼;"webpack.prod.js" 中除了 mode,還有 externals 配置項,表示構建的 bundle 中排除對 react 和 react-dom 的依賴,由於是 React 組件,用到該組件的地方確定同時也引用了 react 和 react-dom,因此不必再在組件的 bundle 中引入,這樣又能夠大大減小構建完的文件大小。
Babel 可讓咱們使用 ES6 寫 JS,因此咱們也須要爲項目添加 Babel 配置。
新建 ".babelrc" 文件,輸入配置:
{ "presets": ["@babel/preset-env", "@babel/preset-react"] }
"webpack.common.js" 中添加 babel 相關的 配置:
rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ]
須要安裝:
npm install --save-dev @babel/core babel-loader @babel/preset-env @babel/preset-react
在 "package.json" 文件中根據 webpack 的 output 配置指定項目的入口文件,另外添加兩條命令,分別是開發時啓動命令和生產構建命令。
"main": "./dist/component-example.js", "scripts": { "start": "webpack --config webpack.dev.js --progress --colors", "build": "webpack --config webpack.prod.js", }
既然是組件,那便須要放入到一個完整的項目中去調試,這裏使用 npm link
。
首先須要進入剛剛的組件根目錄,執行 pwd
,獲取組件的絕對路徑,而後進入一個完整的目標項目的根目錄,執行 npm link $path
,"$path" 指的是剛剛執行 pwd
後獲取到的路徑 ,這樣在目標項目的 "node_modules" 中建立了一個指向組件的軟連接,至關於在目標文件中執行了 npm install --save component-example
,只不過任何在 "component-example" 中的修改都會當即反饋到目標項目中。
npm link
還有一種方式,即在 "component-example" 根目錄直接執行 npm link
,那麼會在全局的 node_modules
中建立名爲 component-example
的 NPM 包,而後在目標項目中執行 npm link component-example
以引用組件文件。但這種方式會污染全局,通常不建議這麼作。
引入組件後,能夠像正常 NPM 包同樣 import 和使用:
import ComponentExample from 'component-example'; export default class FullProject extends React.Component { render() { return <ComponentExample />; } }
在組件項目中啓動 npm start
,任何更改能夠自動構建,變化也會隨時反饋到目標項目中。
開發結束後,記得在目標項目中解除 link:npm unlink component-example
。
既然是前端組件,那 CSS 必不可少,如何引入 CSS 文件呢?這裏使用 CSS Modules。任何一個 CSS 樣式規則都是全局的,CSS Modules 的思路就是產生一個惟一的哈希字符串表示當前的規則,從而避免全局污染的狀況。
那怎麼使用呢?webpack 的 css-loader
即可以支持 CSS Modules,而且配置和使用起來都很方便。若是留意的話,你可能會發現 "webpack.common.js" 中引入 css-loader
時在後面添加了個參數: modules
,這樣即可以打開 CSS Modules 功能。
"App.css"
.title { color: red; }
"App.js"
import React from 'react'; import styles from './App.css'; export default class APP extends React.Component { render() { return <div className={styles.title}>Hello, React Component!</div> } }
JS 文件引入 CSS 模塊,並命名爲 styles, JSX 經過 className={styles.title}
聲明類名。
通過一番編碼調試,組件終於能夠發佈了,使用 npm run build
命令對項目進行構建,若是打開 "./dist/component-example.js" 的話,你會發現代碼與開發時有很大差異。再執行 npm publish
將組件發佈到 NPM 上。
使用 npm publish
時有幾個注意點,首先你須要註冊 NPM 帳號,沒有的話須要執行 npm adduser
,註冊完畢後登陸 npm login
,執行 npm who am i
驗證是否已經成功登陸,而後就能夠愉快地 npm publish
了。另一個注意點是每次發佈時須要更新 "package.json" 中的 version,執行 npm version *.*.*
指定當前發佈的版本,必定要大於上一次。
上面講了如何從零開源一個 React 組件,只是從技術角度出發,開源過程當中還會涉及其餘的方方面面,這裏再也不詳述。