javascript
view如何接受json_如何将你的 ThinkJS 项目部署到 ZEIT 上
編者按:本文作者奇舞團前端開發工程師李喆明。
什么是 ZEIT
ZEIT(https://zeit.co) 是免費的云平臺,支持部署靜態網站以及 Serverless 函數。Serverless 是近幾年比較火的概念,簡單去理解就是你只需要去實現具體的業務邏輯,而與最終服務相關的服務器、HTTP 服務等則由第三方管理。Serverless 又被稱為 FaaS(函數即服務),由于業務粒度非常細,所以非常方便做動態擴容等自動化運維任務。
//一個最簡單的基于 Node.js 的 Serverless 函數
module.exports?=?function(req,?res)?{
??const?{?name?=?'World'?}?=?req.query
??res.send(`Hello?${name}!`)
}
通過 ZEIT 提供的 CLI 工具 now,我們可以一條命令將 Node.js, Golang, Python, Ruby, PHP, Rust 等語言的應用部署到 ZEIT 上。如果你想了解更多關于 ZEIT 這個公司的知識也可以看這篇知乎回答(https://www.zhihu.com/question/59278159/answer/163585410)了解更多。
如何使用 ZEIT
注冊非常方便,打開 https://zeit.co 點擊右上角的 "Join Free",使用 Github 或者 Gitlab 賬號登錄后會自動注冊。當然你也可以使用郵箱注冊,會發送一封確認郵件到你的郵箱。登錄后會讓你填寫昵稱、頭像和唯一 ID等配置。
選擇 Continue 之后如果是通過郵箱登錄進來的會問你是否需要綁定 Github 賬號,可以讓 Github 與 ZEIT 之間的持續集成更加方便,當然你也可以選擇 SKIP 跳過。最后一步則會指導你如何創建項目,它提供了很多快速創建的模板,例如 Next.js, React, Vuepress, Gatsby, Docz, Nuxt.js, Svelte, Angular。
按照示例使用 npm install -g now 安裝 CLI 工具,初始化項目后直接使用 now 命令即可發布到 ZEIT 上,整體流程非常簡單。
部署 Koa.js 服務
通過剛才的示例我們可以了解到其實它的本質就是將 HTTP 請求的 request和response傳入方法中,處理后再返回給 HTTP,所以它除了 Serverless 函數之外也是完全支持 Koa.js 以及基于 Koa.js 的 ThinkJS 服務部署的。我們先來看看如果要部署一個 Koa.js 服務應該怎么做。
Fork 快速部署
由于 ZEIT 官方主推 Serverless 服務,所以把 Node.js 的腳手架模板去除掉了,所以我們只能自己創建項目了,為了方便我提供了一個 DEMO 倉庫 https://github.com/lizheming/now-koa-demo。如果在剛才的注冊流程中你綁定了 Github 的賬號的話你可以選擇直接 Fork 該倉庫,等一小會兒之后就會收到 ZEIT 的 Github 通知告訴你網站已經部署成功,并在 commit 中提供部署后的地址。
命令行部署
如果沒有綁定 Github 賬戶也沒關系,我們可以通過命令行部署服務。將 DEMO 倉庫克隆下來后直接使用 now 命令就可以了。部署成功后 ZEIT 會給我們返回一個當前提交版本的唯一地址,比如說 https://now-koa-demo-pac7dbxrf.now.sh/ 打開之后就會見到 Hello from koa.js! 的返回信息。
注意事項
index.js 文件內容與正常的 Koa.js 項目代碼無異,唯一的區別是最終項目沒有直接調 app.listen() 方法進行監聽,而是使用 module.exports = app.callback() 將最終的 callback 方法進行了返回。我們知道 app.callback() 方法返回的是接受 request 和 response 對象作為參數的函數,這就回到了文章最開始的示例了。
我們再來看看 now.json 的內容。該 JSON 文件用于告訴 now 服務 index.js 文件需要使用 @now/node 運行時執行,而所有的請求需要轉發到 index.js 文件上。聽起來是不是非常像 Nginx 上的內容?
{
??"version":?2,
??"builds":?[
????{?"src":?"index.js",?"use":?"@now/node"?}
??],
??"routes":?[
????{?"src":?"/(.*)",?"dest":?"/index.js"?}
??]
}
部署 ThinkJS 服務
成功部署 Koa.js 服務之后,下面我們就來看看怎么給你的 ThinkJS 服務找一個免費空間部署上去吧!為了方便我也提供了一個 DEMO 倉庫 https://github.com/lizheming/now-thinkjs-demo,Fork 該倉庫可快速體驗 Now 部署 ThinkJS 服務。Fork 成功后過一會就會收到部署成功后的提示,同時告知你部署后的唯一地址,例如 https://now-thinkjs-demo-hrmqxxv2p.now.sh/。
然而這只是我折騰成功后的結果,基于 ThinkJS 的服務直接部署并沒有部署 Koa.js 服務那么簡單,這主要是由 ThinkJS 框架本身的特性決定的。下面我將其中需要注意的點一一道來,方便其它已有服務的遷移。我們先來看看針對 ZEIT 平臺的 ThinkJS 啟動文件有哪些內容。然后我們基于該文件主要講述下碰到的問題以及為什么需要這么做。
const?path?=?require('path');
const?Application?=?require('thinkjs');
const?Loader?=?require('thinkjs/lib/loader');
class?NowLoader?extends?Loader?{
??writeConfig()?{}
}
const?app?=?new?Application({
??ROOT_PATH:?__dirname,
??APP_PATH:?path.join(__dirname,?'src'),
??VIEW_PATH:?path.join(__dirname,?'view'),
??proxy:?true,?// use proxy
??env:?'now',
??external:?{
????log4js:?{
??????stdout:?path.join(__dirname,?'node_modules/log4js/lib/appenders/stdout.js'),
??????console:?path.join(__dirname,?'node_modules/log4js/lib/appenders/console.js')
????},
????static:?{
??????www:?path.join(__dirname,?'www')
????}
??}
});
const?loader?=?new?NowLoader(app.options);
loader.loadAll('worker');
module.exports?=?function?(req,?res)?{
??return?think.beforeStartServer().catch(err?=>?{
????think.logger.error(err);
??}).then(()?=>?{
????const?callback?=?think.app.callback();
????return?callback(req,?res);
??}).then(()?=>?{
????think.app.emit('appReady');
??});
};
服務啟動問題
剛才部署 Koa.js 的時候我們知道了,ZEIT 運行時接受的文件需要返回一個函數。在 Koa.js 中是 app.callback(),而在 ThinkJS 中則是 think.app.callback() 。不過我們卻不能直接這么返回,因為從源碼中我們可以了解到 ThinkJS 服務啟動做了以下幾件事情:
初始化 Loader 實例,在對應的進程上加載需要的文件,包括 config, middleware, controller, logic, model, service 等。
執行 beforeStartServer() 啟動前鉤子
啟動服務
啟動后向全局發送 appReady 事件
目前 ThinkJS 服務中并沒有純粹的非啟動方法包含這些內容,所以我選擇了在啟動腳本中模擬正常的啟動流程自定義啟動過程的方式。由于多進程邏輯稍微復雜點,所以我直接按照單進程模式模擬。
實例化 Loader,使用 loader.loadAll('worker') 加載所有的依賴文件
在回調中執行 beforeStartServer() 啟動前鉤子
執行 callback() 啟動服務
啟動后向全局發送 appReady 事件
文件引用問題
項目文件相對引用
我們知道 ThinkJS 的本質是文件夾即路由的模式,Controller, Model, View 等文件按照一定的文件夾規則放置,通過動態讀取文件的形式找到對應的文件并加載執行。這在正常的項目中本來不存在什么問題,但是 ZEIT Now 平臺為了節省空間,會對在入口文件中沒有顯示依賴的文件進行忽略。
我們正常的啟動文件中只會定義 APP_PATH ,而 VIEW_PATH 甚至是靜態資源目錄是在 src/config/adapter.js 以及 src/config/middleware.js 中定義的。而這兩個文件又是動態讀取文件引入的,導致在上傳的時候由于沒有顯式依賴該文件而不上傳該文件。所以為了解決這個問題,我選擇了在啟動文件中再次顯示聲明一下需要加載的文件。當然這些配置對 ThinkJS 來說是沒有用的。
依賴文件相對引用
可以看到,除了正常的項目文件的引用之外,我還寫了兩個 log4js 文件的引用,這又是為什么呢?
主要還是因為 ZEIT 為了節省體積,除了會限制只上傳需要的文件之外,還會針對入口文件使用 webpack 進行打包。使用 webpack 打包后所有的依賴都在入口文件中了,這樣就不用上傳碩大的 node_modules 文件夾,可以極大的減小體積。ZEIT 將該針對 Node.js 項目打包成單文件的打包工具開源出來了 https://github.com/zeit/ncc 如果項目中有需要打包成單文件減小體積的需求也可以使用。
而 log4js 非常早期的版本中是通過 require(./${type}) 的形式將對應的日志輸出器加載進來的。由于打包后目錄結構發生變化,打包后當前文件夾并沒有對應的文件,所以會導致執行的時候報文件找不到的錯誤。所以為了解決這個問題則同樣需要在入口文件中顯式的聲明這些文件的依賴。
去年2月份就有用戶針對這個問題提了 Commit 將所有的加載器顯式依賴后再進行選擇解決了這個問題。所以在新版 log4js 的中已經不存在這個問題了,不過我還是在這里說明一下,是因為可能項目中引用的其它依賴會有這個問題,還是需要注意一下的。
寫入權限問題
除了上面的問題之外,部署的時候我還碰到了文件寫入無權限的問題。由于 ZEIT Now 提供無狀態服務,所以寫入文件等副作用操作在 ZEIT 中被禁止了。如果你有文件寫入操作的話會在控制臺中提示寫入失敗并報錯。
而在 ThinkJS 中由于各種配置文件比較多,為了方便問題排查,會在配置文件加載完成后調用 writeConfig() 方法寫一份最終合并后的配置在 runtime 目錄中,例如 runtime/config/production.json 文件。這樣的話在 ZEIT 平臺就會報錯導致服務無法正常啟動了。
不過目前 ThinkJS 并沒有提供一個配置能夠取消這個配置文件寫入的操作。所以我提供的解決方法則是通過繼承將 writeConfig() 方法復寫掉來組織文件寫入的操作。
當然這是 ThinkJS 本身的文件寫入操作,如果說你的項目中還有其它文件寫入操作的話,也需要做對應的操作。例如 logger 日志的配置可以輸出到控制臺,文件上傳等必須寫入文件的則可以寫到系統臨時目錄 /tmp 中。不同的系統臨時目錄可能不太一樣,Node.js 中建議通過 require('os').tmpdir() 來獲取。
后記
通過 ZEIT 平臺,極大的降低了部署 Node.js 服務的成本,不僅是機器成本,維護成本也極大的降低了。其實正常的 Node.js 項目部署起來還是非常方便的,主要還是 ThinkJS 的依賴引用并非顯式的,導致了在打包上的一些困難,其它的都還是很方便的。如果有什么其它的問題,也歡迎大家多多交流。
參考資料:
如何透過 ZEIT 方便快捷地部署免費的 Node.js 項目?
關于奇舞周刊
《奇舞周刊》是360公司專業前端團隊「奇舞團」運營的前端技術社區。關注公眾號后,直接發送鏈接到后臺即可給我們投稿。
總結
以上是生活随笔為你收集整理的view如何接受json_如何将你的 ThinkJS 项目部署到 ZEIT 上的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何将交通卡转移到新 iPhone 上
- 下一篇: js 加总数组中某一列_JS数组求和的常