文章目錄
近幾日開發了一個習題渲染器(支持提交答案),內容好寫,從0⃣️建環境開發發佈頗爲不易😭,所以過後整理了一篇文章做個筆記。
本文記錄了項目的搭建、開發和發佈過程,項目源碼地址:github。
目前有很多可優化的地方,比如添加 eslint、測試、npm publish hooks 等等,時間有限先發文章,後期(認真臉)會逐步完善 : )
開發組件
由於組件比較簡單,文章的順序是先假設已經寫好了簡單的組件,然後需要什麼就添加什麼,一步步完成各種拓展。並不是一開始就搭建環境接入各種拓展,萬事俱備之後再寫組件。感覺本文的敘述對各種拓展的使用有更深刻的理解。
構建開發環境
創建項目目錄並進入執行命令:
$ yarn init
填完幾個選項後,會生成一個 package.json
,包含項目的基本信息。
安裝 React
$ yarn add react react-dom
開發組件代碼
├─src // 用來存放組件源碼
| ├─index.ts // 入口文件,用來暴露組件
| ├─utils.ts
| ├─types // TS 聲明文件
| | ├─externals.d.ts // 由於項目中使用了 less,需額外聲明才能使用模塊化導入 less 文件
| | └index.ts
| ├─Renderer
| | ├─index.less
| | └index.tsx
關於組件的源碼,由於很簡單並且不是本文的重點,所以就不展開了。可參閱項目組件部分源碼。
安裝配置 TS
由於使用了 TS,寫好組件之後下一步要對 TS 文件進行編譯。
# 由於 TS 只會在開發環境使用,所以安裝在 devDependencies
$ yarn add -D typescript
需要在項目的根 /
目錄新建一個 tsconfig.json
的配置文件,這樣不用每次編譯時輸入重複複雜的命令。
// tsconfig.json
{
"compilerOptions": {
"outDir": "./dist", // 輸出的目錄
"module": "CommonJS", // 指定生成哪個模塊系統代碼: "None", "CommonJS", "AMD", "System", "UMD", "ES6"或 "ES2015"
"target": "ES2015", // 指定 ES 目標版本,默認 ES3
"jsx": "react", // 在 .tsx 文件裏支持 jsx
"declaration": true, // 生成相應的 .d.ts 文件
"removeComments": true, // 刪除所有註釋,除了以 /!* 開頭的版權信息。
},
"include": [
"src/**/*", // 需要編譯的文件
],
"exclude": [
"node_modules",
],
"files": []
}
使用 include
引入的文件可以使用 exclude
屬性過濾。 然而,通過 files
屬性明確指定的文件卻總是會被包含在內,不管 exclude
如何設置。 如果沒有特殊指定, exclude
默認情況下會排除 node_modules
,bower_components
,jspm_packages
和 <outDir>
目錄。
關於
tsconfig.json
更多的配置說明,可查看:https://www.tslang.cn/docs/handbook/tsconfig-json.html
開發示例 example
爲了方便開發, 我們可以在當前項目中創建一個 example(demo),開發時無需先將組件編譯打包然後引用編譯後的代碼,而是可以直接引用該渲染器的源碼,這樣每次修改後保存便可以自動更新到頁面,極大地提高了開發效率。當開發調試完成後,便可以使用生產模式,這樣打包編譯壓縮後的渲染器代碼便可直接使用。
使用方式:在 example 中直接 import /src/..
中的組件即可。
本習題渲染器 example 相關的代碼目錄結構如下所示:
├─example // 示例代碼
| ├─src
| | ├─index.html // 用於掛載組件到頁面
| | ├─index.tsx
| | └mock.ts // mock 數據
代碼可參閱:example
引入 webpack
使用 webpack 完成對項目的打包編譯並打開 example。
# webpack-dev-server 用來開啓本地服務器打開 example
$ yarn add -D webpack webpack-cli webpack-dev-server
# 各種 loader
$ yarn add -D ts-loader less-loader style-loader css-loader
在項目根目錄 /
中創建配置文件 webpack.config.js
用於打包編譯。
// webpack.config.js
// TODO 只是開發環境的設置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const base = {
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
devtool: 'cheap-module-source-map',
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: ['.ts', '.tsx', '.js', '.json'],
},
module: {
rules: [
// ts-loader 用於加載解析 ts 文件
{
test: /\.(ts|tsx)?$/,
loader: 'ts-loader',
exclude: /node_modules/
},
// 用於加載解析 less 文件
{
test: /\.less$/,
use: [
{ loader: 'style-loader', },
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[hash:base64:6]',
},
}
},
{ loader: 'less-loader', },
]
},
],
},
optimization: {
minimize: true, // 開啓代碼壓縮
},
};
if (process.env.NODE_ENV === 'development') {
tempConfig = {
...base,
entry: path.join(__dirname, 'example/src/index.tsx'),
output: {
path: path.join(__dirname, 'example/dist'),
filename: 'bundle.js',
library: 'laputarenderer',
libraryTarget: 'umd',
},
plugins: [
// 自動注入編譯打包好的代碼至 html
new HtmlWebpackPlugin({
template: path.join(__dirname, './example/src/index.html'),
filename: 'index.html',
}),
],
devServer: {
// port: 8008, // example 的啓動端口,選填
},
};
}
module.exports = tempConfig;
添加開發模式的 script 命令
添加命令之前,我們需要指定 node 執行環境,方便告知 webpack 現在是生產還是開發環境,決定應該如何打包。安裝如下依賴,用來設置執行環境:
$ yarn add -D cross-env
現在我們需要添加一些 npm 執行命令,用來編譯運行項目的 example。
// package.json
{
// ...
"scripts": {
"start": "cross-env NODE_ENV=development webpack-dev-server --open"
},
// ...
}
ok,到現在爲止,我們已經搭建好了這個習題渲染器的開發環境,接下來執行 yarn start
,編譯完成之後瀏覽器會自動打開 example ,🉑️以開始爲所欲爲了~
開發調試完成之後,我們還需要在最終發佈 npm 包之前使用生產模式編譯壓縮該渲染器。
搭建生產環境
修改 webpack 配置
首先安裝一個刪除文件依賴,方便每次打包時提前自動刪除上一次編譯打包後的文件。
# 用於打包編譯之前清空 /dist
$ yarn add -D clean-webpack-plugin
修改 webpack.config.js
// webpack.config.js
// ...
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// ...
if (process.env.NODE_ENV === 'development') {
// ...
} else {
tempConfig = {
...base,
entry: './src/index.ts',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
library: 'laputarenderer',
library: 'umd'
},
devtool: 'none',
// When importing a module whose path matches one of the following, just
// assume a corresponding global variable exists and use that instead.
// This is important because it allows us to avoid bundling all of our
// dependencies, which allows browsers to cache those libraries between builds.
// 我們想要避免把所有的React都放到一個文件裏,因爲會增加編譯時間並且瀏覽器還能夠緩存沒有發生改變的庫文件。
// 理想情況下,我們只需要在瀏覽器裏引入React模塊,但是大部分瀏覽器還沒有支持模塊。
// 因此大部分代碼庫會把自己包裹在一個單獨的全局變量內,比如:jQuery或_。 這叫做“命名空間”模式,
// webpack 也允許我們繼續使用通過這種方式寫的代碼庫。
// 通過我們的設置"react": "React",webpack會神奇地將所有對"react"的導入轉換成從React全局變量中加載
// 詳情🔎請參閱本文末尾的參考文檔:《React與webpack》
externals: {
'react': 'react',
'react-dom': 'react-dom'
},
plugins: [
new CleanWebpackPlugin(), // 編譯之前清空 /dist
],
};
}
module.exports = tempConfig;
添加生產模式的 script 命令執行編譯打包📦
修改 package.json
// package.json
{
// ...
"scripts": {
"build": "cross-env NODE_ENV=production npx webpack"
},
// ...
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
}
}
⚠️注意:package.json
添加了 peerDependencies
字段,用來告訴其它想安裝該庫的項目:如果想使用我這個插件,就必須同級安裝我指定的這些依賴。
在本項目中,指定了 react 和 react-dom,如果另外一個項目 A 想要安裝此渲染器,就必須同時安裝 react 和 react-dom。
npm1 和 npm2 版本可以安裝渲染器的同時自動安裝 peerDependencies 中的依賴,但是之後的版本需要手動安裝,否則將會收到警告。
更多內容請閱讀:Peer Dependencies。
自此,生產模式的所有準備工作都已經完成,執行 yarn build
之後會發現項目的根目錄新增了 dist
文件夾,即最後要發佈到 npm 上的文件。
接下來就要開始準備發佈到 npm~
npm 發佈準備
修改 package.json
配置
// package.json
{
"main": 'dist/index.js', // 該包的入口文件
"types": "dist/index.d.ts", // 指明聲明文件的入口
// ...
"files": ["dist"], // npm 發佈白名單
// ...
}
files
字段規定了:只有 dist 文件夾會出現在發佈的包裏(README.md
和 package.json
會被默認添加)。
npm 發佈
發佈之前必須在 npmjs.com 有賬號,如果沒有,先註冊:www.npmjs.com/signup。如果已經有了賬號,需要在本地終端執行 npm login
登陸。輸入用戶名密碼郵箱後即可登錄成功,npmjs.com 的賬戶裏會自動保存當前 pc 的唯一 token。
⚠️ 由於時間的關係,目前沒有做好自動化。可以在
package.json
中添加一些 script 命令即 npm publish hook,用於在發佈之前進行比如preversion
、version
和postversion
。
發佈
發佈之前先要確保已經在生產模式下打包編譯了該項目(yarn build),做了自動化的當我沒說 : P
$ npm publish
等待發布成功之後便可以在 npmjs.com 的個人主頁查看新發布的包啦 🎉🎉🎉
更新版本
修改項目之後更新版本:
# 升級補丁版本號 1.0.0 -> 1.0.1
$ npm version patch
# 升級次版本號 1.0.0 -> 1.1.0
$ npm version minor
# 升級主版本號 1.0.0 -> 2.0.0
$ npm version major