由于云函數(shù)與Nodejs息息相關(guān),需要我們對云函數(shù)與Node的模塊以及Nodejs的一些基本知識有一些基本的了解。下面只介紹一些基礎(chǔ)的概念,如果你想詳細深入了解,建議去翻閱一下Nodejs的官方技術(shù)文檔:
技術(shù)文檔:Nodejs API 中文技術(shù)文檔
在前面我們已經(jīng)接觸過Nodejs的fs模塊、path模塊,這些我們稱之為Nodejs的內(nèi)置模塊,內(nèi)置模塊不需要我們使用npm install下載,就可以直接使用require引入:
const fs = require('fs')
const path = require('path')
const url = require('url')
Nodejs的常用內(nèi)置模塊以及功能如下所示,這些模塊都是可以在云函數(shù)里直接使用的:
和JavaScript的全局對象(Global Object)類似,Nodejs也有一個全局對象global,它以及它的所有屬性(一些全局變量都是global對象的屬性)都可以在程序的任何地方訪問。下面就來介紹一下Nodejs在云函數(shù)里比較常用的全局變量。
dirname是獲得當前執(zhí)行文件所在目錄的完整目錄名,node還有另外一個常用變量filename,它是獲得當前執(zhí)行文件的帶有完整絕對路徑的文件名。我們可以新建一個云函數(shù)比如nodefile,然后在nodefile云函數(shù)的index.js里輸入以下代碼:
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
console.log('當前執(zhí)行文件的文件名', __filename );
console.log('當前執(zhí)行文件的目錄名', __dirname );
}
將云函數(shù)部署上傳之后,通過小程序端調(diào)用、本地調(diào)試或云端測試就可以執(zhí)行云函數(shù),得到如下的打印結(jié)果(還記得云函數(shù)的打印日志可以在哪里查看么?):
當前執(zhí)行文件的文件名 /var/user/index.js
當前執(zhí)行文件的目錄名 /var/user
由此可見云函數(shù)在云端Linux環(huán)境就放置在/var/user
文件夾里面。
還有一些變量比如module,module.exports,exports等實際上是模塊內(nèi)部的局部變量,它們指向的對象根據(jù)模塊的不同而有所不同,但是由于它們通用于所有模塊,也可以當成全局變量。
以/
為前綴的模塊是文件的絕對路徑,放到云函數(shù)里require('/var/user/config/config.js')
會加載云函數(shù)目錄里的config文件夾里的config.js,這里require('/var/user/config/config.js')
在云函數(shù)的路徑里等同于相對路徑的require('./config/config.js')
。當沒有以 '/'、'./' 或 '../' 開頭來表示文件時,這個模塊必須是一個核心模塊或加載自node_modules 目錄。
在nodefile云函數(shù)的目錄下面新建一個config文件夾,在config文件夾里創(chuàng)建一個config.js,云函數(shù)的目錄結(jié)構(gòu)如下圖所示:
nodefile // 云函數(shù)目錄
├── config //config文件夾
│ └── config.js //config.js文件
└── index.js
└── config.json
└── package.json
然后再在config.js里輸入以下代碼,通常我們用這樣的方式申明一些比較敏感的信息,或者比較通用的模塊:
module.exports = {
AppID: 'wxda99ae45313257046', //可以是其他變量,這里只是參考
AppKey: 'josgjwoijgowjgjsogjo',
}
然后在nodefile云函數(shù)的index.js里輸入以下代碼(下面并非實際代碼,大家看著添加):
//下面兩句放在exports.main函數(shù)的前面
const config = require('./config/config.js')
const {AppID,AppKey} = config
//省略了部分代碼
exports.main = async (event, context) => {
console.log({AppID,AppKey})
}
將云函數(shù)的所有文件都部署上傳到云端之后,再來執(zhí)行云函數(shù),我們就可以看到config/config.js里面的變量就被傳遞到了index.js里了,這同時也說明在云函數(shù)目錄之下不僅可以創(chuàng)建文件(前面創(chuàng)建過圖片),還可以創(chuàng)建模塊,通過module.exports和require來達到創(chuàng)建并引入的效果。
process 對象提供有關(guān)當前 Node.js 進程的信息并對其進行控制,它有一個比較重要的屬性process.env,返回包含用戶環(huán)境的對象。
比如上面的nodefile云函數(shù),打開云開發(fā)控制臺,在云函數(shù)列表里找到nodefile,然后點擊配置在彈窗的環(huán)境變量里添加一些環(huán)境變量,比如NODE_ENV、ENV_ID、name(因為是常量,建議用大寫字母),它的值為字符串,然后我們將nodefile云函數(shù)的index.js代碼改為如下:
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
return process.env //process可以不必使用require就可以直接用
}
右鍵云函數(shù)增量上傳之后,調(diào)用該云函數(shù),然后在云函數(shù)的返回的對象里就可以看到除了有我們設(shè)置的變量以外,還有一些關(guān)于云函數(shù)環(huán)境的信息。因此我們可以把一些需要手動可以修改或者比較私密的變量添加到配置里,然后在云函數(shù)里調(diào)用,比如我們想在小程序上線之后修改小程序的云開發(fā)環(huán)境,可以添加ENV_ID字段,值到時根據(jù)情況來修改:
const cloud = require('wx-server-sdk')
const {ENV_ID} = process.env
cloud.init({
env: ENV_ID
})
再來回顧一下wx-server-sdk這個第三方模塊,它也是云開發(fā)必備的核心依賴,云開發(fā)的諸多API都是基于此。我們可以在給云函數(shù)安裝了wx-server-sdk之后(也就是右鍵云函數(shù),在終端執(zhí)行了 npm install),在電腦上打開云函數(shù)的node modules文件夾,可以看到雖然只安裝了一個wx-server-sdk,但是卻下載了很多個模塊,這些模塊都是通過三個核心依賴@cloudbase/node-sdk(原tcb-admin-node)、protobuf、jstslib來安裝的。
要想對wx-server-sdk有一個深入了解,我們可以研究一下最核心的@cloudbase/node-sdk(原tcb-admin-node),具體可以參考@cloudbase/node-sdk的Github官網(wǎng),同時由于wx-server-sdk順帶下載了很多依賴,比如@cloudbase/node-sdk、xml2js、request等,這些依賴可以在云函數(shù)里直接引入。
const request = require('request')
request模塊雖然是第三方模塊,但是已經(jīng)通過wx-server-sdk下載了,在云函數(shù)里直接通過require就可以引入。由于wx-server-sdk模塊是每個云函數(shù)都會下載安裝的,我們完全可以把它當成云函數(shù)的內(nèi)置模塊來處理,而通過wx-server-sdk順帶下載的N多個依賴,我們也可以直接引入,不必再來下載,而在使用npm install
安裝完成之后的package-lock.json里查看這些依賴的版本信息。
Nodejs生態(tài)所擁有的第三方模塊是所有編程語言里最多了,比Python、PHP、Java還要多,借助于這些開源的模塊,可以大大節(jié)省我們的開發(fā)成本,這些模塊在npm官網(wǎng)地址都可以搜索到,不過npm官網(wǎng)的第三方模塊大而全,哪些才是Nodejs開發(fā)人員最常用最優(yōu)秀的模塊呢?我們可以在Github上面找到awesome Nodejs,這里有非常全面的推薦。
在awesome-nodejs里,這些優(yōu)秀的模塊被分為了近50個不同的類別,而其中大多數(shù)都是可以用于云函數(shù)的,可見云函數(shù)的強大遠不只停留在云開發(fā)的技術(shù)文檔上,我們接下來會在這一章會選取一些比較有代表性的模塊來結(jié)合云函數(shù)進行講解。
當我們要在云函數(shù)里引入第三方模塊時,需要先在該云函數(shù)package.json里的dependencies里添加該模塊并附上版本號"第三方模塊名": "版本號"
,版本號的表示方法有很多,npm install 會下載相應(yīng)的版本(只列舉一些比較常見的):
latest
,會下載最新版的模塊;1.2.x
,等同于1.2,會下載>=1.2.0<3.0.0的版本;~1.2.4
,會下載>=1.2.4 <1.3.0的版本;^1.2.4
,會下載>=1.2.3 <2.0.0的版本
比如我們要在云函數(shù)里引入lodash的最新版,就可以去該云函數(shù)package.json里添加"lodash": "latest"
,注意是添加到dependencies屬性里面,而且package.json的寫法也要符合配置文件的格式要求,尤其要注意最后一項不能有逗號,
,以及不能在json配置文件里寫注釋:
"dependencies": {
"lodash": "latest"
}
在 npm install
時候生成一份package-lock.json文件,用來記錄當前狀態(tài)下實際安裝的各個npm package的具體來源和版本號。不同的版本號可能對運行的結(jié)果造成不一樣的影響,所以為了保證版一致會有package-lock.json,通常我們用最新的即可。
云函數(shù)運行在服務(wù)端Linux的環(huán)境中,一個云函數(shù)在處理并發(fā)請求的時候會創(chuàng)建多個云函數(shù)實例,每個云函數(shù)實例之間相互隔離,沒有公用的內(nèi)存或硬盤空間,因此每個云函數(shù)的依賴也是相互隔離的,所以每個云函數(shù)我們都要下載各自的依賴,無法做到復(fù)用。
云函數(shù)實例的創(chuàng)建、管理、銷毀等操作由平臺自動完成。每個云函數(shù)實例都在 /tmp 目錄下(這里是服務(wù)端的絕對路徑/tmp,不是云函數(shù)目錄下的./tmp)提供了一塊 512MB 的臨時磁盤空間用于處理單次云函數(shù)執(zhí)行過程中的臨時文件讀寫需求,需特別注意的是,這塊臨時磁盤空間在函數(shù)執(zhí)行完畢后可能被銷毀,不應(yīng)依賴和假設(shè)在磁盤空間存儲的臨時文件會一直存在。如果要持久化的存儲,最好是使用云存儲。
云函數(shù)應(yīng)是無狀態(tài)的,也就是一次云函數(shù)的執(zhí)行不依賴上一次云函數(shù)執(zhí)行過程中在運行環(huán)境中殘留的信息。為了保證負載均衡,云函數(shù)平臺會根據(jù)當前負載情況控制云函數(shù)實例的數(shù)量,并且會在一些情況下重用云函數(shù)實例,這使得連續(xù)兩次云函數(shù)調(diào)用如果都由同一個云函數(shù)實例運行,那么兩者會共享同一個臨時磁盤空間,但因為云函數(shù)實例隨時可能被銷毀,并且連續(xù)的請求不一定會落在同一個實例(因為同時會創(chuàng)建多個實例),因此云函數(shù)不應(yīng)依賴之前云函數(shù)調(diào)用中在臨時磁盤空間遺留的數(shù)據(jù)??偟脑瓌t即是云函數(shù)代碼應(yīng)是無狀態(tài)的。
return
返回之后就會停止運行, 和普通 node 本地運行的行為有些差異,這個要注意一下;/tmp
里,云函數(shù)的目錄是沒有寫權(quán)限的;await
,以免任務(wù)沒有執(zhí)行完,云函數(shù)就終止了;
更多建議: