- 像后端一样做微信云开发
接触云开发两个多星期了,总结出一套类似后端的MVC结构。 首先我们需要用到的是官方提供的tcb-router。 云函数入口没什么特别的,正常使用,无非是在原有基础上进行了模块化。 首先创建三个文件夹:entity、service、dao; 熟悉后端开发的同学都知道:entity主要是存放一些实体类。我们看下具体有什么吧 [图片] 上图是一个通用返回的实体对象,这样一来,在返回客户端数据的时候,直接实例化出来一个CommonResponse对象 [图片] [图片] 讲完实体类,接下来要说的是dao层 这层是数据层,主要做一些数据库的操作: 查询参数通过service层传入,这样数据库操作层被独立出来,增加了复用性 [图片] 紧接着要登场的是service层 service层里面主要是一些业务逻辑的处理,拿到dao层返回的数据以后,对数据进行处理后返回给客户端 [图片] 在来看看index的入口 [图片] 这样 逻辑是不是清晰了很多? 不管是service层还是dao层都可以完美地被复用,而且便于维护
2019-11-06 - 微信小程序云开发云函数路由,担心云函数不够用的可以看下这里
一 首先我们看下云函数结构[图片] 分为 controllers 控制器 service 服务逻辑 utils 工具类 二 修改入口文件根据不同路由进入不同的文件 // 云函数入口文件 const cloud = require('wx-server-sdk') const response = require('./utils/response') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() //这个是重点,小程序传过来的时候要带 controller 控制器和 action 函数名过来 if (!event.controller || !event.action) { return response.error() } try { //根据不同的控制器,引入不同的控制器文件 const server = require('./controllers/' + event.controller) //调用控制器里面的方法 return server.getInstance({ router: event.controller + '/' + event.action, event: event })[event.action]() } catch (e) { return response.error('系统错误') } } 重点是前端得传 controller 和 action 过来,然后根据不同的控制器和函数名调用不同的文件 里面出现的response文件,我也先给大家看下,避免不知道是什么意思 这个返回信息的封装 response.js module.exports = { success: function (msg = '成功', result = {}, status = 0) { return { msg: msg, result: result, status: status } }, error:function (msg = '失败', result = {}, status = -1) { return { msg: msg, result: result, status: status } } } 三 首先创建一个基础控制器以后一些挂载函数可以在里头挂载 登录的控制器 Base.js const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() const response = require('../utils/response'); class Base { //构造函数方法 constructor(option={}) { this.response = response this.request = option.event this.router = option.router } static getInstance(option={}) { if (!this.instance) { this.instance = new this(option); } return this.instance; } } module.exports = Base; 四 可以开始写控制器方法了比如写个获取商品列表的控制器Goods.js const Base = require('./Base') class Goods extends Base { //构造函数方法 constructor(option={}) { super(option); } async list() { //商品列表 let list = []; return this.response.success('成功',{list:list }); } async add() { //添加商品 return this.response.success('成功'); } } module.exports = Goods; 这样一个获取商品列表的链接就做好了, 如果要写多其他的功能,就在controller里建js控制器,然后继承base,就可以了 五 小程序端的请求我用的是Uniapp开发 目录结构如下 [图片] 首先我封装一个请求工具 request.js export async function request(url, data = {}) { const url_list = url.split('/') data.controller = url_list[0] data.action = url_list[1] return new Promise((r, s) => { wx.cloud.callFunction({ // 云函数名称 name: 'request', // 传给云函数的参数 data: data, success: function(res) { let res_data = res.result r(res_data); }, fail: function(res) { s(res); } }) }) } 然后把所有要请求的接口写在api.js文件里 import {request} from "./request"; //商品列表 export function goodsList(data){ return request('Goods/list',data); } //添加商品 export function goodsList(data){ return request('Goods/add',data); } 然后就可以调用了 import {goodsList} from "@/api/api.js"; //就可以拿到商品列表了 let list = await goodsList(); 封装好后,小程序的请求,大家有没有觉得开发跟以前跟后端对接的形式一样 这是用云开发开发的小程序,可以参考一下 [图片]
2020-12-02 - 如何只使用一个云函数搞定一个庞大而复杂的系统
吐槽 翻遍社区的文章,关于云开发的干货,少之又少,大部分都还是官方文档的搬来搬去,没啥营养,是时候放出一点技术"干货"了(有经验的开发者都能想到的方案)! 正题 小程序云开发的云函数的最大限制是 [代码]50[代码] 个,假设每个接口都使用 [代码]1[代码] 个云函数的话,有 [代码]10[代码] 张表,每张表都有 [代码]增删改查[代码] 四个接口,那么就会有 [代码]40[代码] 个接口,再加上一些其他接口,差不多刚刚好够用,那如果有 [代码]20[代码] 张表,甚至更多的表、更多的接口呢?对于中小型的小程序来说足够使用,那如果一个非常庞大而复杂的系统该怎么办呢? 而且每一个云函数的运行环境是独立的,想要共享一些数据也不是特别方便,那么有没有什么办法突破这样的限制呢? 其实解决方案很简单,只需要一点点的 [代码]OOP[代码] 思想和利用 [代码]JavaScript[代码] 的特性,一个云函数就可以搞定所有的接口。 具体的实现请往下看。 思路 云函数的运行环境是 [代码]Nodejs[代码] , 那么使用的语言就是 [代码]JavaScript[代码] ,可以充分的利用 [代码]JavaScript[代码] 的特性。 [代码]JavaScript[代码] 中的 [代码]属性访问表达式[代码] 有两种语法 [代码]expression . identifier expression [ expression ] [代码] 第一种写法是一个表达式后跟随一个句点 [代码].[代码] 和一个标识符。表达式指定对象,标识符则指定需要访问的属性的名称。 第二种写法是使用方括号 [代码][][代码],方括号内是另一个表达式(这种方法适用于对象和数组)。第二个表达式指定要访问的属性的名称或者代表要访问数组元素的索引。 不管使用哪种形式的属性访问表达式,在 [代码].[代码] 和 [代码][][代码] 之前的表达式总是会首先计算。 虽然 [代码].[代码] 的写法更加简单,但这种方式只适用于要访问的属性名称的合法标识符,并需要准确知道访问的属性的名字,如果属性的名称是一个保留字或者包含空格和标点符号,或者是一个数字(对于数组来说),则必须使用方括号 [代码][][代码] 的写法。当属性名是通过运算得出的值而不是固定值的时候,这时也必须使用方括号 [代码][][代码] 写法。 感谢社区大神 @卢霄霄 提供参考资料,详见 [代码]JavaScript权威指南[代码] (犀牛书)4.4章节。 可以使用 [代码][][代码] 的形式来完成动态的属性访问。具体实现请往下看。 实现 上面说了太多废话了,下面直接开干吧。 新建云函数 在云开发目录中新建一个云函数,我这里命名为 [代码]cloud[代码]。 打开 [代码]index.js[代码] 文件你会看到下面这段代码 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') // 初始化 cloud cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() return { event, openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID, } } [代码] 这个云函数仅作为入口使用,上面提到了云函数的运行环境是 [代码]Nodejs[代码] 那么 [代码]Nodejs[代码] 的特性也是可以使用的,这里主要用到的是全局对象 [代码]global[代码],详见文档 在文件中,写入一些必要的全局变量,主要还是云数据库方面的,方便后面使用。 在初始化后面插入代码 [代码]global.cloud = cloud global.db = cloud.database() global._ = db.command global.$ = _.aggregate [代码] 这样就可以在同一个云函数环境中直接访问这些全局变量。 创建公共类 然后新建一个文件夹,我这里命名为 [代码]controllers[代码] ,这个文件夹用于存放所有的接口。 在 [代码]controllers[代码] 中新建一个 [代码]base-controller.js[代码] 文件,创建一个叫做 [代码]BaseController[代码] 的类,用于提供一些公用的方法。 内容如下: [代码]class BaseController { /** * 调用成功 */ success (data) { return { code: 0, data } } /** * 调用失败 */ fail (erroCode = 0, msg = '') { return { erroCode, msg, code: -1 } } } module.exports = BaseController [代码] 看到这里大家可能有点没看懂在做什么,那么请继续往下看。 创建接口 假设创建一些要操作用户相关的的接口,可以在 [代码]controllers[代码] 文件夹中新建一个 [代码]user-controller.js[代码] 的文件,创建一个名为 [代码]UserController[代码] 的类,并继承上面的 [代码]BaseController[代码] 类,内容如下: [代码]const BaseController = require('./base-controller.js') class UserController extends BaseController { // ... } module.exports = UserController [代码] 可以在这个类中编写所有关于 [代码]user[代码] 的接口方法。 编写接口 假设要分页查询用户信息,可以在 [代码]UserController[代码] 类中创建一个 [代码]list[代码] 方法。 代码如下: [代码]async list (data) { const { pageIndex, pageSize } = data let result = await db.collection('users') .skip((pageIndex - 1) * pageSize) .limit(pageSize) .get() .then(result => this.success(result.data)) .catch(() => this.fail([])) return result } [代码] 由于上面已经定义了全局变量 [代码]db[代码] 所以在 [代码]UserController[代码] 中无需引入 [代码]wx-server-sdk[代码] 引入接口类 写到这里接口已经完成了,还需要再引入这些接口类才可以进行访问。在 [代码]index.js[代码] 中引入 [代码]user-controller.js[代码] [代码]const User = require('./controllers/user-controller.js') [代码] 然后创建一个 [代码]api[代码] 变量,[代码]new[代码] 一个 [代码]User[代码] 实例 [代码]const api = { user: new User() } [代码] 在 [代码]main[代码] 方法中调用 [代码]UserController[代码] 中的方法。 [代码]exports.main = async (event, context) => { const { data } = event let result = await api['user']['list'](data) return result } [代码] 写到这里基本已经完成了接口的调用,但想要一个云函数动态调用所有接口还需要做一些改动。 动态调用接口 刚开始的时候介绍了 [代码]属性访问表达式[代码],限制稍微改动一下 [代码]main[代码] 方法 [代码]exports.main = async (event, context) => { const { controller, action, data } = event const result = await api[controller][action](data) return result } [代码] 在小程序调用云函数时,需要传入 [代码]controller[代码]、[代码]action[代码] 和 [代码]data[代码] 参数即可 [代码]const result = await wx.cloud.callFunction({ name: 'cloud', data: { controller, action, data } }) [代码] 完整 [代码]index.js[代码] 文件的代码 [代码]// 云函数入口文件 const cloud = require('wx-server-sdk') const User = require('./controllers/user-controller.js') const api = { user: new User() } // 初始化 cloud cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) global.cloud = cloud global.db = cloud.database() global._ = db.command global.$ = _.aggregate // 云函数入口 exports.main = async (event, context) => { exports.main = async (event, context) => { const { controller, action, data } = event const result = await api[controller][action](data) return result } } [代码] 其他实现 云开发官方团队打造的轮子 tcb-router
2020-05-26 - 小程序app.onLaunch与page.onLoad异步问题的最佳实践
场景: 在小程序中大家应该都有这样的场景,在onLaunch里用wx.login静默登录拿到code,再用code去发送请求获取token、用户信息等,整个过程都是异步的,然后我们在业务页面里onLoad去用的时候异步请求还没回来,导致没拿到想要的数据,以往要么监听是否拿到,要么自己封装一套回调,总之都挺麻烦,每个页面都要写一堆无关当前页面的逻辑。 直接上终极解决方案,公司内部已接入两年很稳定: 1.可完美解决异步问题 2.不污染原生生命周期,与onLoad等钩子共存 3.使用方便 4.可灵活定制异步钩子 5.采用监听模式实现,接入无需修改以前相关逻辑 6.支持各种小程序和vue架构 。。。 //为了简洁明了的展示使用场景,以下有部分是伪代码,请勿直接粘贴使用,具体使用代码看Github文档 //app.js //globalData提出来声明 let globalData = { // 是否已拿到token token: '', // 用户信息 userInfo: { userId: '', head: '' } } //注册自定义钩子 import CustomHook from 'spa-custom-hooks'; CustomHook.install({ 'Login':{ name:'Login', watchKey: 'token', onUpdate(token){ //有token则触发此钩子 return !!token; } }, 'User':{ name:'User', watchKey: 'userInfo', onUpdate(user){ //获取到userinfo里的userId则触发此钩子 return !!user.userId; } } }, globalData) // 正常走初始化逻辑 App({ globalData, onLaunch() { //发起异步登录拿token login((token)=>{ this.globalData.token = token //使用token拿用户信息 getUser((user)=>{ this.globalData.user = user }) }) } }) //关键点来了 //Page.js,业务页面使用 Page({ onLoadLogin() { //拿到token啦,可以使用token发起请求了 const token = getApp().globalData.token }, onLoadUser() { //拿到用户信息啦 const userInfo = getApp().globalData.userInfo }, onReadyUser() { //页面初次渲染完毕 && 拿到用户信息,可以把头像渲染在canvas上面啦 const userInfo = getApp().globalData.userInfo // 获取canvas上下文 const ctx = getCanvasContext2d() ctx.drawImage(userInfo.head,0,0,100,100) }, onShowUser() { //页面每次显示 && 拿到用户信息,我要在页面每次显示的时候根据userInfo走不同的逻辑 const userInfo = getApp().globalData.userInfo switch(userInfo.sex){ case 0: // 走女生逻辑 break case 1: // 走男生逻辑 break } } }) 具体文档和Demo见↓ Github:https://github.com/1977474741/spa-custom-hooks 祝大家用的愉快,记得star哦
2023-04-23 - 小程序中使用three.js
小程序中使用three.js 目前小程序支持了webgl, 同时项目中有相关3D展示的需求,所以考虑将three.js移植到小程序中。 但是小程序里面没有浏览器相关的运行环境如 window,document等。要想在小程序中使用three.js需要使用相应的移植版本。https://github.com/yannliao/three.js实现了一个在 three.js 的基本移植版, 目前测试支持了 包含 BoxBufferGeometry, CircleBufferGeometry, ConeBufferGeometry, CylinderBufferGeometry, DodecahedronBufferGeometry 等基本模型,OrbitControl, GTLFLoader, OBJLoader等。 使用 下载https://github.com/yannliao/three.js项目中build目录下的three.weapp.min.js到小程序相应目录,如: [图片] 在index.wxml中加入canvas组件, 其中需要手动绑定相应的事件,用于手势控制。 [代码]<view style="height: 100%; width: 100%;" bindtouchstart="documentTouchStart" bindtouchmove="documentTouchMove" bindtouchend="documentTouchEnd" > <canvas type="webgl" id="c" style="width: 100%; height:100%;" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" bindtouchcancel="touchCancel" bindlongtap="longTap" bindtap="tap"></canvas> </view> [代码] 在页面中引用three.js 和相应的Loader: [代码]import * as THREE from '../../libs/three.weapp.min.js' import { OrbitControls } from '../../jsm/loaders/OrbitControls' [代码] 在onLoad中获取canvas对象并注册到[代码]THREE.global[代码]中,[代码]THREE.global.registerCanvas[代码]可以传入id, 用于通过[代码]THREE.global.document.getElementById[代码]找到, 如果不传id默认使用canvas对象中的_canvasID, registerCanvas同时也会将该canvas选为当前使用canvas对象. 同时请在onUnload回调中注销canvas对象. 注意: THREE.global 中最多同时注册 5 个[代码]canvas[代码]对象, 并可以通过id找到. 注册的canvas对象, 会长驻内存, 如果不及时清理可能造成内存问题. [代码]THREE.global[代码]为[代码]three.js[代码] 的运行环境, 类似于浏览器中的window. [代码]Page({ data: { canvasId: '' }, onLoad: function () { wx.createSelectorQuery() .select('#c') .node() .exec((res) => { const canvas = THREE.global.registerCanvas(res[0].node) this.setData({ canvasId: canvas._canvasId }) // const canvas = THREE.global.registerCanvas('id_123', res[0].node) // canvas代码 }) }, onUnload: function () { THREE.global.unregisterCanvas(this.data.canvasId) // THREE.global.unregisterCanvas(res[0].node) // THREE.global.clearCanvas() }, [代码] 注册相关touch事件. 由于小程序架构原因, 需要手动绑定事件到THREE.global.canvas或者THREE.global.document上. 可以使用[代码]THREE.global.touchEventHandlerFactory('canvas', 'touchstart')[代码] 生成小程序的事件回调函数,触发默认canvas对象上的touch事件. [代码] { touchStart(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchstart')(e) }, touchMove(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchmove')(e) }, touchEnd(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchend')(e) }, } [代码] 编写three.js代码, 小程序运行环境中没有requestAnimationFrame, 目前可以使用canvas.requestAnimationFrame之后会将requestAnimationFrame注入到THREE.global中. [代码]const camera = new THREE.PerspectiveCamera(70, canvas.width / canvas.height, 1, 1000); camera.position.z = 500; const scene = new THREE.Scene(); scene.background = new THREE.Color(0xAAAAAA); const renderer = new THREE.WebGLRenderer({ antialias: true }); const controls = new OrbitControls(camera, renderer.domElement); // controls.enableDamping = true; // controls.dampingFactor = 0.25; // controls.enableZoom = false; camera.position.set(200, 200, 500); controls.update(); const geometry = new THREE.BoxBufferGeometry(200, 200, 200); const texture = new THREE.TextureLoader().load('./pikachu.png'); const material = new THREE.MeshBasicMaterial({ map: texture }); // const material = new THREE.MeshBasicMaterial({ color: 0x44aa88 }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // renderer.setPixelRatio(wx.getSystemInfoSync().pixelRatio); // renderer.setSize(canvas.width, canvas.height); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(canvas.width, canvas.height); } function render() { canvas.requestAnimationFrame(render); // mesh.rotation.x += 0.005; // mesh.rotation.y += 0.01; controls.update(); renderer.render(scene, camera); } render() [代码] 完整示例: index.js [代码]import * as THREE from '../../libs/three.weapp.min.js' import { OrbitControls } from '../../jsm/loaders/OrbitControls' Page({ data: {}, onLoad: function () { wx.createSelectorQuery() .select('#c') .node() .exec((res) => { const canvas = THREE.global.registerCanvas(res[0].node) this.setData({ canvasId: canvas._canvasId }) const camera = new THREE.PerspectiveCamera(70, canvas.width / canvas.height, 1, 1000); camera.position.z = 500; const scene = new THREE.Scene(); scene.background = new THREE.Color(0xAAAAAA); const renderer = new THREE.WebGLRenderer({ antialias: true }); const controls = new OrbitControls(camera, renderer.domElement); // controls.enableDamping = true; // controls.dampingFactor = 0.25; // controls.enableZoom = false; camera.position.set(200, 200, 500); controls.update(); const geometry = new THREE.BoxBufferGeometry(200, 200, 200); const texture = new THREE.TextureLoader().load('./pikachu.png'); const material = new THREE.MeshBasicMaterial({ map: texture }); // const material = new THREE.MeshBasicMaterial({ color: 0x44aa88 }); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // renderer.setPixelRatio(wx.getSystemInfoSync().pixelRatio); // renderer.setSize(canvas.width, canvas.height); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(canvas.width, canvas.height); } function render() { canvas.requestAnimationFrame(render); // mesh.rotation.x += 0.005; // mesh.rotation.y += 0.01; controls.update(); renderer.render(scene, camera); } render() }) }, onUnload: function () { THREE.global.unregisterCanvas(this.data.canvasId) }, touchStart(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchstart')(e) }, touchMove(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchmove')(e) }, touchEnd(e) { console.log('canvas', e) THREE.global.touchEventHandlerFactory('canvas', 'touchend')(e) }, touchCancel(e) { // console.log('canvas', e) }, longTap(e) { // console.log('canvas', e) }, tap(e) { // console.log('canvas', e) }, documentTouchStart(e) { // console.log('document',e) }, documentTouchMove(e) { // console.log('document',e) }, documentTouchEnd(e) { // console.log('document',e) }, }) [代码] index.wxml [代码]<view style="height: 100%; width: 100%;" bindtouchstart="documentTouchStart" bindtouchmove="documentTouchMove" bindtouchend="documentTouchEnd" > <canvas type="webgl" id="c" style="width: 100%; height:100%;" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" bindtouchcancel="touchCancel" bindlongtap="longTap" bindtap="tap"></canvas> </view> [代码] 其他 全部示例在 https://github.com/yannliao/threejs-example three.js 库 https://github.com/yannliao/three.js loader 组件在 threejs-example 中的 jsm 目录中 欢迎提交PR和issue
2019-10-20 - 【云开发·云函数】分层(layer)实现代码复用,云函数之间共用底层文件
导读 当我们有很多云函数时,其中可能很多 [代码]中间件[代码] 、 [代码]工具[代码] 是共用的。 把这些 [代码]中间件[代码] 、 [代码]工具[代码] 复制到每个云函数下,是一件很麻烦的事情,而且如果发生了更新,还要重新复制。最重要的是,这种方式,不符合科学的设计模式。 云函数里的 层(layer) 就可以很好的解决我们这个问题。它是把我们共通需要的 [代码]中间件[代码] 、 [代码]工具[代码] 等文件放在一个公共的路径下,使用过程中只需要引用进来即可。 注意: 层暂时还没出现在小程序官方文档,但是可以在 [代码]腾讯云 - 云函数[代码]找到文档。 实操 这里我们使用 [代码]Nodejs8.9[代码] 来做示范 1. 先确认小程序已经开通了云开发 2. 登录腾讯云 打开登录界面 选择【其他登录方式 - 微信公众号 - 扫码选择对应的小程序(不是公众号)】. [图片] 注意:小程序云开发和腾讯云的账户体系其实是有关联的,可以把腾讯云面板看作更高级的云开发面板 3. 编写层代码 编写以下代码,并保存为 [代码]demo.js[代码] [代码]exports.main = async (event) => { return "hello,layer." } [代码] 把 [代码]demo.js[代码] 打包成 [代码]demo.zip压缩包[代码] 4. 创建分层 打开云函数面板. 点击左侧菜单的『层』 地域选择『上海』 点击『新建按钮』 层名称:demo 提交方法:本地上传zip包 层代码:选择上一步打包的 [代码]demo.zip[代码] 压缩包 运行环境:Nodejs8.9 [图片] 点击提交 注意: 有些压缩软件压缩后,会多一层文件夹,需要注意下,压缩包内打开应该直接就是文件 [代码]demo.js[代码] 没有多一层文件夹。 5. 小程序·云开发面板创建云函数 创建云函数 [图片] 修改 [代码]云函数 demo 下的 index.js[代码] 代码并上传 [代码]var hello = require("demo.js") exports.main = (event, context, callback) => { return hello.main() }; [代码] 6. 云函数绑定层 回到 腾讯云 云函数面板,选择左侧菜单 [代码]函数服务[代码] 注意左上角的 命名空间 选择小程序云开发的命名空间,地域:上海 选择刚创建的 [代码]demo[代码] 云函数 点击 [代码]层管理[代码] 绑定刚创建的 [代码]demo[代码] 层 7. 调试 还是 [代码]demo[代码] 的云函数里,点击 [代码]函数代码[代码] 项 点击测试,返回结果 [代码]"hello,layer."[代码] 既成功 [图片] Q&A 问:为什么云函数里可以直接[代码]require("demo.js")[代码] 引入 [代码]demo.js[代码] ? 答:绑定层后,会根据顺序把层文件放在系统目录 [代码]/opt[代码] 下,然后该目录在 Node.js 环境下又在环境变量里。当引入 [代码]demo.js[代码] 在当前目录找不到时,会尝试在环境变量内查找。所以云函数可以找到该文件。 再问:如果层的文件名和云函数列表的文件名冲突,岂不是会引入错误? 答:是的,这时候可以使用绝对路径确保引入的是层文件 [代码]require("/opt/demo.js")[代码] 再再问:如果云函数绑定了多个层,每个层里都有 [代码]demo.js[代码] 会怎么样? 答:会根据层的顺序,逐个覆盖文件,最终 [代码]/opt/demo.js[代码] 文件是最后一层的文件。 再再再问:环境变量除了这个路径,还有哪些路径? 答:这里有一份各语言的环境变量路径,可以参考下,具体查看文档 关环境变量 路径 THONPATH [代码]/var/user:/opt[代码] ASSPATH [代码]/var/runtime/java8:/var/runtime/java8/lib/*:/opt[代码] DE_PATH [代码]/var/user:/var/user/node_modules:/var/lang/node6/lib/node_modules:/opt:/opt/node_modules[代码] 问:云函数只支持 [代码]Nodejs8.9[代码] 版本吗? 答:在小程序云开发面板里,确实只能创建。但是可以通过服务端 SDK 创建以下语言支持。 Python 3.6 Python 2.7 Nodejs 10.15 Nodejs 8.9 Nodejs 6.10 Php 7.2 Php 5.6 Java 8 Golang 1 持续更新… 参考资料 腾讯云·云函数文档
2020-05-26