摘要:年工程化協作開發棧最佳實踐我們將花半小時實戰擼一個包含,的標準的用于工程協作的包開發棧。使用腳手架,秒鐘構建可自由配置的開發棧。分別表示詢問彈窗自動執行任務執行任務后操作。
發起一個github/npm工程協作項目,門檻太高了!!
最基礎的問題,你都要花很久去研究:
如何在項目中全線使用es2017代碼? 答案是babel
如何統一所有協作者的代碼風格? 答案是eslint + prettier
如何測試驅動開發,讓項目更健壯? 答案是jest
如何持續化集成,方便更多協作者參與項目? 答案是circleci
這四樣工具的配置,是每個github項目都會用上的。另外,gitignore配置、editconfig、readme、lisence。。。也是必不可缺的。
你可能需要花數天時間去研究文檔、數天時間去做基礎配置。
這樣的時間成本,可以直接勸退大多數人。
但,假如幾秒鐘,就可以按需求配置好這一切呢?
你可以先來體驗一下“輪子工廠”,在命令行輸入:
npx lunz myapp
一路回車,然后試一試yarn lint,yarn test,yarn build命令
第一部分: 2019年github + npm工程化協作開發棧最佳實踐
第二部分: 使用腳手架,10秒鐘構建可自由配置的開發棧。
2019年github + npm工程化協作開發棧最佳實踐我們將花半小時實戰擼一個包含package.json, babel, jest, eslint, prettify, gitignore, readme, lisence的標準的用于github工程協作的npm包開發棧。
如果能實際操作,請實際操作。1. 新建文件夾如果不能實際操作,請在bash下輸入npx lunz npmdev獲得同樣的效果。
mkdir npmdev && cd npmdev2. 初始化package.json
npm init
package name: 回車 version: 回車 description: 自己瞎寫一個,不填也行 entry point: 輸入`dist/index.js` test command: 輸入`jest` git repository: 輸入你的英文名加上包名,例如`wanthering/npmdev` keywords: 自己瞎寫一個,不填也行 author: 你的英文名,例如`wanthering` license: 輸入`MIT`
在package.json中添加files字段,使npm發包時只發布dist
... "files": ["dist"], ...
之前不是創建了.editorconfig 、LICENSE、circle.yml、.gitignore、README.md嗎,這四個復制過來。
3. 初始化eslintnpx eslint --init
How would you like to use ESLint? 選第三個 What type of modules does your project use? 選第一個 Which framework does your project use? 選第三個None Where does your code run? 選第二個 Node How would you like to define a style for your project? 選第一個popular Which style guide do you want to follow? 選第一個standard What format do you want your config file to be in? 選第一個 javascript
在package.json中添加一條srcipts命令:
... "scripts": { "test": "jest", "lint": "eslint src/**/*.js test/**/*.js --fix" }, ...4. 初始化prettier
為了兼容eslint,需要安裝三個包
yarn add prettier eslint-plugin-prettier eslint-config-prettier -D
在package.json中添加prettier字段
... "prettier": { "singleQuote": true, "semi": false }, ...
在.eslintrc.js中,修改extends字段:
... "extends": ["standard","prettier","plugin:prettier/recommended"], ...5. 創建源文件
mkdir src && touch src/index.js
src/index.js中,我們用最簡單的add函數做示意
const add = (a,b)=>{ return a+b} export default add
這時命令行輸入
yarn lint
這會看到index.js自動排齊成了
const add = (a, b) => { return a + b } export default add6. 配置jest文件
所有的npm包,均采用測試驅動開發。
現在流行的框架,無非jest和ava,其它的mocha之類的框架已經死在沙灘上了。
我們安裝jest
npm i jest -D
然后根目錄下新建一個test文件夾,放置進jest/index.spec.js文件
mkdir test && touch test/index.spec.js
在index.spec.js內寫入:
import add from "../src/index.js"; test("add",()=>{ expect(add(1,2)).toBe(3)})
配置一下eslint+jest:
yarn add eslint-plugin-jest -D
在.eslintrc.js中,更新env字段,添加plugins字段:
"env": { "es6": true, "node": true, "jest/globals": true }, "plugins": ["jest"], ...
因為需要jest中使用es6語句,需要添加babel支持
yarn add babel-jest @babel/core @babel/preset-env -D
創建一下.babelrc配置,注意test字段,是專門為了轉化測試文件的:
{ "presets": [ [ "@babel/preset-env", { "targets": { "node": 6 } } ] ], "env": { "test": { "presets": [ [ "@babel/preset-env", { "targets": { "node": "current" } } ] ] } } }
好,跑一下yarn lint,以及yarn test
yarn lint yarn test構建打包
比起使用babel轉碼(安裝@babel/cli,再調用npx babel src --out-dir dist),我更傾向于使用bili進行打包。
yarn add bili -D
然后在package.json的script中添加
"scripts": { "test": "jest", "lint": "eslint src/**/*.js test/**/*.js --fix", "build": "bili" },.gitignore
創建 .gitignore,復制以下內容到文件里
node_modules .DS_Store .idea *.log dist output examples/*/yarn.lock.editorconfig
創建.editorconfig,復制以下內容到文件里
root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = falsecircle.yml
創建circle.yml,復制以下內容到文件內
version: 2 jobs: build: working_directory: ~/project docker: - image: circleci/node:latest branches: ignore: - gh-pages # list of branches to ignore - /release/.*/ # or ignore regexes steps: - checkout - restore_cache: key: dependency-cache-{{ checksum "yarn.lock" }} - run: name: install dependences command: yarn install - save_cache: key: dependency-cache-{{ checksum "yarn.lock" }} paths: - ./node_modules - run: name: test command: yarn testREADME.md
創建README.md,復制以下內容到文件內
# npm-dev > my laudable project
好了,現在我們的用于github工程協作的npm包開發棧已經完成了,相信我,你不會想再配置一次。
這個項目告一段落。
事實上,這個npm包用npm publish發布出去,人們在安裝它之后,可以作為add函數在項目里使用。
使用腳手架,10秒鐘構建可自由配置的開發棧。同樣,這一章節如果沒時間實際操作,請輸入git clone https://github.com/wanthering/lunz.git
當你開啟新項目,復制粘貼以前的配置和目錄結構,浪費時間且容易出錯。
package.json、webpack、jest、git、eslint、circleci、prettify、babel、gitigonre、editconfig、readme的強勢勸退組合,讓你無路可走。
所以有了vue-cli,非常強大的腳手架工具,但你想自定義自己的腳手架,你必須學透了vue-cli。
以及yeoman,配置賊麻煩,最智障的前端工具,誰用誰sb。
還有人求助于docker,
有幸,一位來自成都的寶藏少年egoist開發了前端工具SAO.js。
SAO背景不錯,是nuxt.js的官方腳手架。
作為vue的親弟弟nuxt,不用vue-cli反而用sao.js,你懂意思吧?
因為爽!!!!!!!!
因為,一旦你學會批量構建npm包,未來將可以把精力集中在“造輪子”上。
新建sao.js全局安裝
npm i sao -g
快速創建sao模板
sao generator sao-npm-dev
一路回車到底
ok,當前目錄下出現了一個sao-npm-dev
打開看一下:
├── .editorconfig ├── .git ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── circle.yml ├── package.json ├── saofile.js ├── template │?? ├── .editorconfig │?? ├── .gitattributes │?? ├── LICENSE │?? ├── README.md │?? └── gitignore ├── test │?? └── test.js └── yarn.lock
別管其它文件,都是用于github工程協作的文件。
有用的只有兩個:template文件夾, 和saofile.js
把template文件夾刪空,我們要放自己的文件。
生成SAO腳手架好,把npmdev整個文件夾內的內容,除了node_modules/、package-lock.json和dist/,全部拷貝到清空的sao-npm-dev/template/文件夾下
現在的sao-npm-dev/template文件夾結構如下:
├── template │?? ├── .babelrc │?? ├── .editorconfig │?? ├── .eslintrc.js │?? ├── .gitignore │?? ├── LICENSE │?? ├── README.md │?? ├── circle.yml │?? ├── package.json │?? ├── src │?? │?? └── index.js │?? ├── test │?? │?? └── index.spec.js │?? └── yarn.lock配置文件改名
模板文件中.eslint.js .babelrc .gitignore package.json,很容易造成配置沖突,我們先改名使它們失效:
mv .eslintrc.js _.eslintrc.js mv .babelrc _.babelrc mv .gitignore _gitignore mv package.json _package.json配置saofile.js
現在所見的saofile,由三部分組成: prompts, actions, completed。
分別表示: 詢問彈窗、自動執行任務、執行任務后操作。
大家可以回憶一下vue-cli的創建流程,基本上也是這三個步驟。
彈窗詢問的,即是我們用于github工程協作的npm包開發棧每次開發時的變量,有哪些呢?
我來列一張表:
字段 | 輸入方式 | 可選值 | 意義 |
---|---|---|---|
name | input | 默認為文件夾名 | 項目名稱 |
description | input | 默認為my xxx project | 項目簡介 |
author | input | 默認為gituser | 作者名 |
features | checkbox | eslint和prettier | 安裝插件 |
test | confirm | yes 和no | 是否測試 |
build | choose | babel 和 bili | 選擇打包方式 |
pm | choose | npm 和yarn | 包管理器 |
根據這張表,我們修改一下saofile.js中的prompts,并且新增一個templateData(){},用于向template中引入其它變量
prompts() { return [ { name: "name", message: "What is the name of the new project", default: this.outFolder }, { name: "description", message: "How would you descripe the new project", default: `my ${superb()} project` }, { name: "author", message: "What is your GitHub username", default: this.gitUser.username || this.gitUser.name, store: true }, { name: "features", message: "Choose features to install", type: "checkbox", choices: [ { name: "Linter / Formatter", value: "linter" }, { name: "Prettier", value: "prettier" } ], default: ["linter", "prettier"] }, { name: "test", message: "Use jest as test framework?", type: "confirm", default: true }, { name: "build", message: "How to bundle your Files?", choices: ["bili", "babel"], type: "list", default: "bili" }, { name: "pm", message: "Choose a package manager", choices: ["npm", "yarn"], type: "list", default: "yarn" } ] }, templateData() { const linter = this.answers.features.includes("linter") const prettier = this.answers.features.includes("prettier") return { linter, prettier } },
先把saofile放下,我們去修改一下template文件,使template中的文件可以應用這些變量
修改template/中的變量template下的文件,引入變量的方式是ejs方式,不熟悉的可以看一看ejs官方頁面,非常簡單的一個模板引擎
現在我們一個一個審視文件,看哪些文件需要根據變量變動。
1. src/index.js無需變動
2. test/index.spec.js如果test為false,則文件無需加載。test為true,則加載文件。
3. .editorconfig無需改動
4. _.gitignore無需改動
5. _.babelrc如果build采用的babel,或test為true,則導入文件。
并且,如果test為true,應當開啟env,如下設置文件
_.babelrc
{ "presets": [ [ "@babel/preset-env", { "targets": { "node": 6 } } ] ]<% if( test ) { %>, "env": { "test": { "presets": [ [ "@babel/preset-env", { "targets": { "node": "current" } } ] ] } }<% } %> }6. _.eslintrc.js
在打開test的情況下,加載env下的jest/globals及設置plugins下的jest。
在開啟prettier的情況下,加載extends下的prettier和plugin:prettier/recommend
所以文件應當這樣改寫
_.eslintrc.js
module.exports = { "env": { "es6": true, "node": true<% if(test) { %>, "jest/globals": true<% } %> }<% if(test) { %>, "plugins": ["jest"]<% } %>, "extends": ["standard"<% if(prettier) { %>,"prettier","plugin:prettier/recommended"<% } %>], "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" } }7. _package.json
name字段,加載name變量
description字段,加載description變量
author字段,加載author變量
bugs,homepage,url跟據author和name設置
prettier為true時,設置prettier字段,以及devDependence加載eslint-plugin-prettier、eslint-config-prettier以及prettier
eslint為true時,加載eslint下的其它依賴。
jest為true時,加載eslint-plugin-jest、babel-jest、@babel/core和@babel/preset-env,且設置scripts下的lint語句
build為bili時,設置scripts下的build字段為bili
build為babel時,設置scripts下的build字段為babel src --out-dir dist
最后實際的文件為:(注意里面的ejs判斷語句)
{ "name": "<%= name %>", "version": "1.0.0", "description": "<%= description %>", "main": "dist/index.js", "scripts": { "build": "<% if(build === "bili") { %>bili<% }else{ %>babel src --out-dir dist<% } %>"<% if(test){ %>, "test": "jest"<% } %><% if(linter){ %>, "lint": "eslint src/**/*.js<% } if(linter && test){ %> test/**/*.js<% } if(linter){ %> --fix"<% } %> }, "repository": { "type": "git", "url": "git+https://github.com/<%= author %>/<%= name %>.git" }, "author": "<%= author %>", "license": "MIT", "bugs": { "url": "https://github.com/<%= author %>/<%= name %>/issues" }<% if(prettier){ %>, "prettier": { "singleQuote": true, "semi": false }<% } %>, "homepage": "https://github.com/<%= author %>/<%= name %>#readme", "devDependencies": { <% if(build === "bili"){ %> "bili": "^4.7.4"<% } %><% if(build === "babel"){ %> "@babel/cli": "^7.4.4"<% } %><% if(build === "babel" || test){ %>, "@babel/core": "^7.4.4", "@babel/preset-env": "^7.4.4"<% } %><% if(test){ %>, "babel-jest": "^24.8.0", "jest": "^24.8.0"<% } %><% if(linter){ %>, "eslint": "^5.16.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.17.2", "eslint-plugin-node": "^9.0.1", "eslint-plugin-promise": "^4.1.1", "eslint-plugin-standard": "^4.0.0"<% } %><% if(linter && test){ %>, "eslint-plugin-jest": "^22.5.1"<% } %><% if (prettier){ %>, "prettier": "^1.17.0", "eslint-plugin-prettier": "^3.1.0", "eslint-config-prettier": "^4.2.0"<% } %> } }8. circle.yml
判斷使用的lockFile文件是yarn.lock還是package-lock.json
<% const lockFile = pm === "yarn" ? "yarn.lock" : "package-lock.json" -%> version: 2 jobs: build: working_directory: ~/project docker: - image: circleci/node:latest branches: ignore: - gh-pages # list of branches to ignore - /release/.*/ # or ignore regexes steps: - checkout - restore_cache: key: dependency-cache-{{ checksum "<%= lockFile %>" }} - run: name: install dependences command: <%= pm %> install - save_cache: key: dependency-cache-{{ checksum "<%= lockFile %>" }} paths: - ./node_modules - run: name: test command: <%= pm %> test9. README.md
# <%= name %> > <%= description %>
填入name和desc變量。
并跟據linter、test、build變量來選擇提示命令。
具體文件略。
好,文件的變量導入完成,現在回到saofile.js:
處理actions當我們通過彈窗詢問到了變量。
當我們在構建好模板文件,只等變量導入了。
現在就需要通過saofile.js中的actions進行導入。
把actions進行如下改寫:
actions() { return [{ type: "add", files: "**", filters: { "_.babelrc": this.answers.test || this.answers.build === "babel", "_.eslintrc.js": this.answers.features.includes("linter"), "test/**": this.answers.test } }, { type: "move", patterns: { "_package.json": "package.json", "_gitignore": ".gitignore", "_.eslintrc.js": ".eslintrc.js", "_.babelrc": ".babelrc" } }] },
其實很好理解! type:"add"表示將模板文件添加到目標文件夾下,files表示是所有的, filters表示以下這三個文件存在的條件。
type:"move"就是改名或移動的意思,將之前加了下劃線的四個文件,改回原來的名字。
處理competed當文件操作處理完之后,我們還需要做如下操作:
初始化git
安裝package里的依賴
輸出使用指南
async completed() { this.gitInit() await this.npmInstall({ npmClient: this.answers.pm }) this.showProjectTips() }跑通測試
SAO已經幫你寫好了測試文件,在test文件夾下。
因為我們要測試很多個選項,原來的sao.mock和snapshot要寫很多次。所以我們把它提煉成一個新的函數verifyPkg()
我們進行一下改寫,同時將package.json、.eslintrc.js打印在snapshot文件中。
import path from "path" import test from "ava" import sao from "sao" const generator = path.join(__dirname, "..") const verifyPkg = async (t, answers) => { const stream = await sao.mock({ generator }, answers) const pkg = await stream.readFile("package.json") t.snapshot(stream.fileList, "Generated files") t.snapshot(getPkgFields(pkg), "package.json") if(answers && answers.features.includes("linter")){ const lintFile = await stream.readFile(".eslintrc.js") t.snapshot(lintFile, ".eslintrc.js") } } const getPkgFields = (pkg) => { pkg = JSON.parse(pkg) delete pkg.description return pkg } test("defaults", async t => { await verifyPkg(t) }) test("only bili", async t => { await verifyPkg(t,{ features: [], test: false, build: "bili" }) }) test("only babel", async t => { await verifyPkg(t,{ features: [], test: false, build: "babel" }) }) test("launch test", async t => { await verifyPkg(t,{ features: [], test: true }) }) test("launch linter", async t => { await verifyPkg(t,{ features: ["linter"] }) }) test("launch prettier", async t => { await verifyPkg(t,{ features: ["prettier"] }) })
ok,這時候跑一下測試就跑通了
測試文件打印在snapshots/test.js.md中,你需要一項一項檢查,輸入不同變量時候,得到的文件結構和package.json 以及.eslintrc.js的內容。
這個時候,整個項目也就完成了。
我們先在npmjs.com下注冊一個帳號,登錄一下npm login登錄一下。
然后,直接npm publish成功之后,就可以使用
sao npm-dev myapp
初始化一個github工程化協作開發棧了。
進階: 本地使用sao.js,發布自定義前端工具大部分人,不會專門去安裝sao之后再調用腳手架,而更喜歡使用
npx lunz myapp
那就新添加一個cli.js文件
#!/usr/bin/env node const path = require("path") const sao = require("sao") const generator = path.resolve(__dirname, "./") const outDir = path.resolve(process.argv[2] || ".") console.log(`> Generating lunz in ${outDir}`) sao({ generator, outDir, logLevel: 2 }) .run() .catch((err) => { console.trace(err) process.exit(1) })
通過sao函數,可以輕松調用于來sao腳手架。
然后,將package.json中的name改名成你想發布npm全局工具名稱,比如我創建的是lunz
并且,加入bin字段,且修改files字段
... "bin": "cli.js", "files": [ "cli.js", "saofile.js", "template" ], ...
這時,應用一下npm link命令,就可以本地模擬出
lunz myapp
的效果了。
如果效果ok的話,就可以使用npm publish發包。
注意要先登錄,登錄不上的話可能是因為你處在淘寶源下,請切換到npm正版源。
結語:現在,你有什么想法,只需要隨時隨刻 npx lunz myapp一下,就可以得到當前最新、最標準、最現代化的github+npm工程化實踐。
把時間集中花在輪子的構建邏輯上,而不是基礎配置上。
與前端之“神”并肩,通過你的經驗,讓前端的生態更繁榮。
如果實在想研究基礎配置,不如幫助我完善這個“輪子工廠”
歡迎大家提交pull request,交最新的實踐整合到項目中
github地址: https://github.com/wanthering...
一起加入,構造更完美的最佳實佳!點擊右上角的Fork按鈕。
新建一個分支:git checkout -b my-new-feature
上報你的更新:git commit -am "Add some feature"
分支上傳云端:git push origin my-new-feature
提交 pull request
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109482.html
摘要:趨勢擴展到機器學習領域已經成為容器編排的事實標準,它的領域也在不斷擴張,未來將成為機器學習技術棧的一部分。比如,發布了開源的,通過添加到集群中,擴展了的,使得機器學習的工作負載在中成為一等公民。 2018年對于微服務來說是非常重要的一年,這一年Service Mesh開始嶄露頭角,解決服務間復雜的通信問題,這一年很多國內互聯網公司已經有了較為成熟的微服務實踐案例,網易云主辦的微服務實踐...
摘要:問題回答者黃軼,目前就職于公司擔任前端架構師,曾就職于滴滴和百度,畢業于北京科技大學。最后附上鏈接問題我目前是一名后端工程師,工作快五年了。 showImg(https://segmentfault.com/img/bVbuaiP?w=1240&h=620); 問題回答者:黃軼,目前就職于 Zoom 公司擔任前端架構師,曾就職于滴滴和百度,畢業于北京科技大學。 1. 前端開發 問題 大...
閱讀 3685·2021-11-25 09:43
閱讀 2645·2021-11-25 09:43
閱讀 3844·2021-11-24 09:38
閱讀 697·2021-11-18 10:02
閱讀 2237·2021-09-22 15:53
閱讀 2998·2019-08-30 15:44
閱讀 2774·2019-08-30 14:01
閱讀 2754·2019-08-29 15:15