在学完前 4 个模块之后,我相信你会对微信小程序的开发有一个全新的认识。在前面 3 个模块中,俊鹏分别从微信小程序内在的运行原理,小程序工程化开发以及具体实践层面,深度讲解了微信小程序开发所必要的知识和能力。而第 4 个模块里,我带你认识了微信小程序的云端解决方案——小程序·云开发,解读了云开发的主要能力。
今天这一讲,我将带你基于云开发实现一个在线商城小程序,通过实战加深你对这门课的认知。
我们从项目最简单处做起。在小程序项目开发之前,通常会有完备的产品逻辑设计以及产品视觉设计为开发者提供产品的交互和视觉元素。根据这些条件,你才能编写代码,生成最终可运行的产品。
因为这门课的重点在于如何进行微信小程序开发,以及后端服务开发,并没有包含设计环节,所以我们直接从图中的微信小程序开发开始。
我们先在浏览器中打开基础项目,下载初始化代码(我们会在此代码的基础上开始学习)。
使用最新版本的微信开发者工具,导入此项目(选择项目的目录),并在 AppID 处填写小程序的 AppID。
导入成功后,开发者工具中展示的界面如下(左侧的模拟器能够正常运行展示出页面):
紧接着,我们操作一下左侧的模拟器,可以完成整个商城项目的页面交互逻辑:
商城交互逻辑主要有 9 点:
首页商品列表页(展示商品的列表);
点击任意商品,进入商品详情页;
点击商品详情页中加入购物车,选择商品规格后确认加入;
点击商品详情页中(或主页 Tabbar)进入购物车页,查看已经加入购物车的商品;
选择购物车中商品,点击结算按钮,进入订单确认页面;
在订单确认页面中,选择配送方式和输入备注,点击提交订单,转到订单中心页;
在订单中心页面选择商品执行付款,或者执行取消动作;
若执行商品付款则变更状态为待收货状态,可点击确认收货;
确认收货后可在已完成栏中看到,可点击删除订单。
为了能让你更容易理解,项目基本上采用了原生语言无框架开发,下面我给你解读一下项目中的一些主要逻辑。
本地化模拟
首先你要清楚,初始项目中你所做的一切产品交互体验都只是在本地流转执行的。也就是说,你的每一个点击和动作,都没有后端服务的工作,全都在小程序里模拟。而你在产品的首页中所看到的商品内容,其实是我预先写在代码里的一系列数据,请你打开项目目录下的 miniprogram/app.js,可以看到如下内容:
这里就是维持项目运转的基本商品数据,通过引入固定的资源来提供交互所必要的数据。而这就引入了我们实战中所要讲的第 1 点:现实中,微信小程序开发和其他前端开发一样,需要预先数据填充,来支撑整个小程序交互逻辑的开发。
我们不能够直接动用后端接口来做小程序页面逻辑开发,这会为后端服务造成不可控的伤害,进而影响项目进度。
虚拟化接口
那么当我们使用预置数据开发页面之后,如何高效地对接后端服务呢? 这就引入了我们讲的第 2 点——虚拟化接口。我们在页面开发时,将需要后端服务的业务操作分离出来,做成一个接口。由于这个接口并没有真正的后端接入,只是提供样子,所以我们称其为虚拟化接口。
请你打开项目目录 miniprogram/pages/index/index.js 文件,定位到 onLoad 函数:
index 页面是项目的首页,用于展示商品列表,所以页面需要商品的列表信息。由于产品最终需要从后端获取商品的列表信息,所以我们将这一动作制作成接口。接着再打开项目目录 miniprogram/app.js,定位到 55 行,如下代码:
我们通过编写一个函数,模拟后端服务将商品列表返回,从操作本身上来看,这个函数就是直接返回了我们之前预置的数据,并没有其他操作,但我们仍需要按照真实情况来模拟,因为后端服务的请求肯定会有网络延迟,所以一般我们在请求时使用回调函数,或者是 promise 来处理,所以在本地模拟时,我们也这样构建。
我们规定:如果有数据需要调用 obj 入参对象的 success 函数将数据传进去,这样处理后,我们便可以在调用方的 success 中编写获取到数据后的有关逻辑了。
因为网络延迟,所以表现在页面中应该需要体现等待加载的状态,告知用户产品正在通信(避免用户觉得产品卡死,并操作退出),所以我们在接口函数中使用 timeout 来规定 1 秒钟之后再返回数据。
这样,在页面中,我们可以编写和演示用户等待数据加载时的展示效果,来进一步开发页面。
如果你细心,就会发现 app.js 文件中包含了产品中所有需要后端交互的虚拟化接口,这样我们在与后端对接时,就可以直接操作 app.js 文件,非常方便地改写和调试,不需要改动页面逻辑代码:
小程序缓存
当然,我们通过虚拟化接口操作的不仅仅是读取数据,更多的是做数据的存储。比如,产品需要记录用户购物车里加入了哪个商品、订单结算时的收货地址、订单状态等信息。
那么在没有后端服务接入的情况下,我们如何存储这些数据呢? 这就引出了我们要讲的第 3 点——小程序缓存。
我们通过微信小程序提供的缓存接口,将产品交互时需要的数据存储起来,通过这种缓存方式保存的数据将不会随着用户退出小程序而消失,可以一直驻留(除非用户主动操作删除小程序本地数据):
请打开项目目录 miniprogram/app.js 文件,定位到 178 行,为商品付款的虚拟接口 toPayTap
在此接口函数中,我们通过 wx.getStorageSync 同步获取缓存中信息,然后经过一系列处理,将处理后的信息通过 wx.setStorageSync 存回去。
需要注意的是, 我们通过缓存能力实现数据保存的虚拟化接口,需要尽可能地与产品设计时保持一致,也就是尽可能模拟接口文档所应该呈现的信息。比如真实后端服务获取订单时有订单状态(进行中、已完成)的标志,但是在新增订单时我们并没有提供这个标志,也就是说这个标志是后端逻辑添加的,但是为了能够完整复现逻辑,我们应该在虚拟接口时主动添加:
使用虚拟化接口串联产品的整个逻辑,对微信小程序开发来说可能增加了开发负担,因为你除了要实现页面逻辑交互开发,可能还要进一步思考后端数据的操作逻辑,但你也会获得一些好处,我总结了 3 点。
得到一个完整运行的高保真运行 DEMO
大部分产品开发都会经历需求变更,主要因为产品人员模糊了产品形态,造成一些错误决定。比如微信规定在小程序内不能长按扫描二维码,但是产品同学不清楚这个形态,做了长按二维码识别的设计,导致之后为了绕过这个限制,更改了许多产品交互逻辑,用来替换长按二维码识别的功能。
但如果你通过虚拟化接口完成一个完全可交互的原型 DEMO,就非常容易发现这类设计并不合理,并在后端服务未开工之前及时规避,从而实现高效率的投入产出比。甚至你可以要求产品同学体验 DEMO 确保产品需求无误后,再进行后续的产品开发工作。
理清前后端接口,及时发现不合理的地方
由于虚拟化了所有与后端交互的接口,你会非常容易发现接口设计中不合理的地方,比如在获取订单列表这个接口中,文档并没有给出订单状态信息,但设计稿中却要求显示订单状态。这时你可以及时向产品同学和后端同学纠正,避免后端同学在架构完成之后再变更这个返回结果。(虽然加订单状态比较简单,但如果出现极端情况会影响后端进度)
具备开发后端服务的能力,只缺一个运维
既然你已经在前端通过小程序缓存完成了产品的后端数据交互,那为什么不自己实现一下后端呢?在服务器 server 后端下,搭建服务器环境、配置网络、配置项目运行环境等工作非常复杂,并且开发工作模式和前端本身开发工作模式差别巨大。你很难在短时间掌握和适应独自开发后端,必须借助运维同学的帮助。
而上述 3 点就引出了实战的高潮部分:使用云开发完整复刻前端虚拟化的接口,构建后端服务。 这样一来前端开发者便可以独立开发前后端应用服务了。
接下来,我带你通过 3 个关键点来用云开发实现后端服务(如果你未开通过云开发,请按照此指引熟悉并开通)。
首先,我们要将所必要的资源上传到服务中,比如商品的图片、视频等。在前端虚拟化实现时,我们用的是第三方商城的资源链接。而当搭设自己的后端服务时,我们要承载这些资源的分发。明确这一点之后,我们要访问浏览器,下载项目的多媒体资源,下载后解压资源如下图所示:
然后打开小程序开发者工具项目左上方的“云开发”按钮,进入云开发控制台,在“存储栏目”中点击上传文件,选择解压后的资源图片(除 data.json),将其上传至云存储:
上传完成后如下图所示,每个资源都有唯一 File ID
然后将 File ID 中除了文件名之外,前半部分根目录保存下来。以上图为例,保存的根目录为
cloud://cloud-tcb.636c-cloud-tcb-1301077292(保留好这个根目录,我们会在下一步骤用到)。
第二步要构建数据库,在微信小程序开发前,我们已经根据产品同学的设计逻辑设计好数据库的结构了,结构整体如下:
接下来,我们打开下载的多媒体资源包中的 data.json 文件,会看到预置的一些数据,然后用文本工具将 FileID 替换成存储时保存的根目录,最终效果如下:
注意一下,上面的信息里我们把存储中上传的资源和数据源文件做了结合。接下来,我们打开云开发控制台中的“数据库栏目”,创建集合,名称为 goods,如下图所示:
创建成功后,点击左侧 goods 集合,点击导入,选择修改后的 data.json 文件,确认导入:
导入后效果如下图所示:
另外,再创建一个集合名为 order,用于记录订单数据,此集合不用导入数据。这样一来,我们就构建完了服务所用的数据库以及基础商品图片。
接下来,我们开始开发后端接口(每个接口一般都会经历 4 个步骤,我们从最简单的获取商品列表的接口开始入手)。
创建云函数
第一次创建云函数时,你需要在项目目录中新建一个名为 cloudfunctions 的文件夹。
小程序开发者工具会自动为我们引入云开发环境(如果你有多个环境可以右键变更,选择之前上传存储和数据库的同一云开发环境)。
目录创建好后,就可以创建云函数了,在 cloudfunctions 右键,点击新建 Node.js 云函数:
在出现的输入框中输入 getGoodlist ,回车后便创建了一个名为 getGoodlist 的云函数,效果如下:
编写代码
接着打开 cloudfunctions/getGoodlist/index.js 文件,将文件中的所有内容删除,替换成下列代码:
//引入云开发SDK
const cloud = require('wx-server-sdk')
//初始化云开发SDK
cloud.init({
//指定当前运行环境
env: cloud.DYNAMIC_CURRENT_ENV
})
//云数据库操作对象
const db = cloud.database();
exports.main = async (event, context) => {
//定位数据库集合goods
const list = (await db.collection('goods')
.field({//指定所列字段
title:true,
price:true,
origin:true,
img:true
})
.get()).data;//执行取出所有记录的指定字段,返回值中取data
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
//返回取出的数据
return list;
- 1
- 2
}
上传并部署
然后保存更新后的代码文件,在云函数文件夹处右键,重新部署上传,如下图所示:
如果你并没有变更 package.json 中的依赖引入,可以直接选择 index.js 右键,增量更新文件:
增量更新只变更单一文件,并不会做云函数的重新部署,在频繁改动代码时推荐使用增量更新,速度更快。
小程序端更改调用
当我们在云开发中编写了 getGoodlist,获取商品列表信息的接口云函数之后,就需要变更小程序端的代码,替换之前的虚拟化接口了。
所以咱们来打开项目 miniprogram/app.js,定位 getGoodSList 函数,将其代码改成如下:
getGoodSList: function (obj) {
wx.cloud.callFunction({
name:'getGoodlist',
success:function(res){
if (obj != null && obj.success != null) {
obj.success(res.result);
}
}
});
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在新的代码中,我们使用 wx(小程序SDK)中内置的 cloud(云开发SDK)操作请求云函数(callFunction),名称为 getGoodlist。当请求成功之后,调用传入参数的 success 返回得到的数据。
经过这 4 个步骤后,我们就完成了获取商品列表接口的变更,来看一下实际效果:
通过控制台我们可以看到,小程序请求了云函数,并返回了数据,还通过云存储资源返回了需要的商品图片(在这里我没有介绍每个接口的实现方法,如果你想了解的话,可以通过浏览器访问此链接,获取云函数代码以及变更后的 app.js 代码,参照以上步骤对云函数进行逐个更改)。
如果你已经掌握了上述内容,可以直接下载云开发版的完整项目,在开发者工具中打开运行,接下来,我们将围绕此项目展开讲解。
到目前为止,通过一系列实践,我已经带你使用云开发构建了在线商城小程序,为了便于你学习和理解,项目本身并不复杂,没有使用一些依赖库。但在真实开发时,我们不可避免地要使用一些依赖库来提升开发效率。在 05 讲,俊鹏就带你了解了如何使用 WebPack 提升研发效率,接下来,我们实际感受一下。
首先,我们先来体验一下微信小程序官方 npm 的构建方法,假设我们需要使用非常有名的lodash 依赖进行开发。
打开项目目录 miniprogram/app.js,添加并使用 lodash 依赖,如下图所示:
你会发现出现这样的情况:在运行时报错,提示找不到这个依赖。所以接下来,我们需要安装这个依赖。
当我们使用官方 npm 构建方法时,我们需要执行如下步骤。
初始化NPM
在开发者工具项目目录中右键,打开内建终端(注意,定位到 miniprogram 目录)
输入以下代码,初始化 npm:
npm init --yes
- 1
安装依赖
在终端中继续输入如下代码,安装 lodash 依赖
npm i lodash
- 1
这时会在 miniprogram 目录中出现 node_module 文件夹:
构建 npm
接下来就来构建微信小程序 npm ,点击开发者工具——菜单栏中的工具,选择“构建npm”。
开发者工具将会为我们构建 npm,最终效果如下:
在与 node_module 目录同级中出现 miniprogram_npm 目录,这就是我们构建好的微信小程序 npm。构建完毕后,我们来重新运行一下小程序,发现并不能很好的运行,报如下错误:
这是因为微信小程序不支持 lodash 包中的一些属性,所以我们需要绕行,我们在示例中使用的是 lodash.add 方法,所以继续进行如下步骤。
再次打开终端,输入如下代码安装 lodash.add:
npm i lodash.add
- 1
重新使用微信 npm 构建,构建 lodash.add,效果如下:
更改项目 miniprogram/app.js 文件,将引入代码进行更改:
重新运行小程序,发现可以正常使用了:
当然,俊鹏已经详细描述了官方构建的一些问题和弊端,比如依赖代码占用体积过大、引入流程特别复杂等。的确如此,在上图控制台中我们也发现警告提示,告知 lodash 包过大,已经超过500kb了,而且在实际开发中,我们需要用到各种依赖,如此构建会加重代码包的负担。最终上传之后,效果如下:
包体积已经超过 1M,这显得非常臃肿,并且开发体验非常不好。所以我们再来体验一下WebPack 的构建方法,对比一下体会其中的优势。
首先我们先删除之前官方 npm 构建的文件夹 miniprogram_npm,以及 package-lock\package.json 文件,暂时保持 app.js 代码不变,效果如下:
接下来,我们开始从这里构建 WebPack 化的小程序。
更改项目结构
首先在项目根目录创建文件夹,名称为 src,并将 miniprogram、cloudfunctions 文件夹移到里面,最终效果如下:
安装WebPack依赖
在项目根目录启动内建终端,初始化 npm:
npm init --yes
- 1
接着安装 webpack、webpack-cli、copy-webpack-plugin、clean-webpack-plugin 4个依赖:
npm i --save-dev webpack webpack-cli copy-webpack-plugin clean-webpack-plugin
- 1
然后再次执行,安装 babel 等相关依赖:
``java
npm i --save-dev @babel/core @babel/preset-env babel-loader
最终效果如下:
处理配置文件
在项目目录中,创建 webpack.config.js 文件,并在文件中添加如下代码:
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin')
const {
CleanWebpackPlugin
} = require('clean-webpack-plugin')
const srcdir = path.resolve(__dirname, 'src')
const putdir = path.resolve(__dirname, 'dist')
module.exports = {
entry: {
'app':'./app.js',
'pages/cart/cart':'./pages/cart/cart.js',
'pages/detail/detail':'./pages/detail/detail.js',
'pages/index/index':'./pages/index/index.js',
'pages/order/order':'./pages/order/order.js',
'pages/submit/submit':'./pages/submit/submit.js',
},
output: {
filename: '[name].js',
path: path.resolve(putdir, 'miniprogram')
},
module: {
rules: [{
test: /\.js$/,
use: 'babel-loader'
}]
},
plugins: [
new CleanWebpackPlugin({
cleanStaleWebpackAssets: false,
}),
new CopyPlugin({
patterns: [{
from: path.resolve(srcdir, 'cloudfunctions'),
to: path.resolve(putdir, 'cloudfunctions')
}, {
from: path.resolve(srcdir, 'miniprogram'),
to: path.resolve(putdir, 'miniprogram'),
globOptions: {
ignore: ['**/*.js'],
}
}]
})
]
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
在项目根目录中创建文件 .babelrc ,在文件中填充如下代码:
{
"presets": ["@babel/env"]
}
- 1
- 2
- 3
上述 WebPack 配置文件主要是用来描述 WebPack 如何处理我们的项目:主要通过 babel 来处理 js 文件,其余文件类型全部使用 copyplugin 插件进行复制转移即可。
Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。我们在终端中执行如下代码:
webpack
- 1
在日志中出现一个错误,我们在之前 app.js 中仍然保持引入的 lodash 依赖,但是并没有安装这个依赖,导致错误。所以我们要对 app.js 的文件进行修改,效果如下:
同时在终端中输入以下命令,安装 lodash 依赖:
npm i lodash
- 1
完成之后重新执行 WebPack 命令,就会发现项目目录中构造出 dist 文件夹:
然后打开项目目录下 project.config.json,配置 root 根目录,如下图所示:
配置完成后,我们便能够重新在模拟器中看到小程序运行了。
新增遍历 js 插件
你要注意,在我们操作演示的过程中,我们通过 babel 处理 js 文件,然后使用 copy 插件直接复制其他的文件,不做任何更改。
那怎么让 WebPack知道小程序代码中哪些地方存在 js 文件呢?我们使用配置的方式来匹配,如下图配置:
但这样意味着我们每增加一个页面,就要特定配置一次,效果并不好,所以我们可以通过写一个遍历插件来实现自动配置。
在项目根目录创建 plugin 文件夹,在文件夹内创建 loadpath.js 文件,文件中填写如下代码:
const path = require('path')
const fs = require('fs')
const replaceExt = require('replace-ext')
var minidir = null
function _inflateEntries (entries = [], entry) {
const configFile = replaceExt(entry, '.json')
const content = fs.readFileSync(configFile, 'utf8')
const config = JSON.parse(content)
const items = config.pages
if (typeof items === 'object') {
Object.values(items).forEach(item => {
inflateEntries(entries, item)
})
}
}
function inflateEntries (entries, entry) {
entry = path.resolve(minidir, entry)
if (entry != null && !entries.includes(entry)) {
replaceExt(entry, '.js')
entries.push(entry)
_inflateEntries(entries, entry)
}
}
class loadpath {
constructor () {
this.entries = []
}
init (options) {
minidir = path.resolve('./src/miniprogram')
inflateEntries(this.entries, options.src)
const output = {}
this.entries.map(item => {
output[replaceExt(path.relative(minidir, item), '')] = item
})
return output
}
}
module.exports = loadpath
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
打开终端,安装 replace-ext 依赖,最终效果如下图:
npm i replace-ext
- 1
打开项目目录下 webpack.config.js 文件,更改如下配置:
保存后重新终端执行 webpack,效果如下:
此插件是根据 app.json 中配置的 page 内容来自动生成相应 entry 配置,达到自动遍历生成的目的。
关于 WebPack 的实战内容,我就讲这么多,关于WebPack 更多使用方法和插件用法,你可以通过 WebPack 官网学习和使用。最后,我们看一下在同样项目代码的情况下,使用 WebPack打包构建的效果:
相比于官方 npm,这大大缩小了代码包的体积,另外我们可以使用通用前端依赖使用方法来便捷的开发小程序,对我们的研发效率也有非常大的提升(关于项目最终版代码,请点击此链接下载)。
今天这一讲,我详细讲解了原生的小程序本地开发、云开发后端服务的搭建,以及工程化开发转型升级,本次实战的项目并不复杂,即使是初入前端也可以轻松掌握和学习。
当然,如果你细心的话应该会注意到,在项目中有些代码我做了一些不合理的设计,所以我这次留给你的作业是:结合前 4 个模块的内容,对这一讲项目不合理处进行完善修改,并尝试在云开发服务的执行效率上,根据第 4 模块的知识点做一些优化实践。
包体积从将近2M直接降到70K左右😱😱😱
老师,想问下propject.config.json这个文件是放在根目录吗? src 小程序里面还需要保留吗?
project.config文件是给微信开发者工具看的,用来调整各项配置,一般在打包时一并打包到dist
怎么实现热更新呢
老师,能完善下 less 的相关配置吗? 感觉配置不出来😂
可以参考less的webpack部署,网络上有很多资料
使用那种类型的AppID,供测试的不行吗? 为什么我看到的工具栏没有“云开发”选择?
测试的APPID由于没有绑定实际主体,所以不能开通云开发,也就无法使用云开发
老师,如果想配置less应该怎么配置呢
可以使用less-loader进行处理,具体参考webpack相关指南
一晃两个多月的时间就过去了,相信通过这门课程的学习,你已经掌握了微信小程序一些必要的技术原理和常见的实践方案,并且能够感受到云开发对小程序研发效率的提升。
基于这个前提,最后的结束语我不再讲解一些技术知识,而是想跟你站在一样的角度(作为一名前端开发者),一起聊一聊前端研发在云原生和 Serverless 技术背景下的演进方向:全栈。
其实,我最初加入云开发团队的直接原因就是:我认为云开发是能够令前端开发者成为全栈开发的一项革命性技术模式。而之所以有这个想法跟我的从业经历有关。
我刚接触前端还是在上大学的时候,那时,我在 SAP 上海研究院实习,主要开发基于 SVG 的Charts 库,99%的代码逻辑是将数据用 SVG 转化为可视化的图形。毕业后,我做了一名传统的 Web 前端开发者,中间还折腾过富本文编辑器。再后来,我有近一年的时间在研究效率工程,也就是所谓的前端工程化,除此之外,在加入腾讯云开发团队之前的工作是地图,技术核心是WebGL。
可以说除了音视频以外,我以往的经历基本涵盖了前端领域绝大部分的技术方向。
不论是大众的 Web 页面还是小众的 SVG,不论是宏观到 Web 整体的工程化,还是微观到像素的图形编程。表面看上去,似乎每一份新工作跟之前的工作都关联甚微,比如在使用 WebGL 期间积累的矩阵、向量、三角剖分等数学和图形学知识,基本在现阶段的工作中得不到体现。
不知道你是否和我一样,曾经对职业前景有过迷惘,比如前端的边界是什么、发展方向在哪儿、前端工程师的核心价值如何体现……而我从毕业到加入腾讯之前一直尝试着在不同的工作内容中寻找答案。
我想现在我找到了:以全栈为进化方向的前端,以及辅助其落地的云开发。
当然,如果要探讨一项技术或者一种理念是否具有革命性,必然需要一些参照物,而历史是最佳的参考。所以在讨论云开发对前端的革新之前,我们有必要回忆一下前端的演进史,用一句话可以概括这个演进过程:不断扩宽前端开发者的能力边界。
我们对照这张图来回顾前端的演进史:
前端演进史
如果以世界上第一个 Web 浏览器 WorldWideWeb 的诞生作为起点,那么 90 年代初便是前端的 0.0 时代。那时候没有前端工程师的称谓,前端的工作也很简单,主要是静态 UI 和表单验证,要么后端工程师兼顾为之,要么由独立的 UI Developer 全权负责。
其实时至今日,很多外企仍然沿袭着 UI Developer 岗位,与目前国内大众认知的前端工程师不同的是,UI Developer 的能力栈除了 HTML/JS/CSS 以外,还要具备一定的 UI 和交互设计能力。
起源于 Microsoft Outlook 的 AJAX 是引起前端第一次革命的关键技术,这是一个重要的转折点,以此为契机网站具备了动态性,前端工程师的能力模型逐渐从 UI 偏向逻辑和数据。
后来,广为人知的 Node.js 成就了 前端 2.0。Node.js 打破了前后端编程语言的壁垒,令前端开发者能够以相对较小的成本跨界服务端,但是编程语言仅仅是服务端开发最表层也是最易突破的,背后更困难的部分单纯靠 Node.js 是无法解决的(细节稍后再表)。除了服务端以外,Node.js 对前端最大的贡献是提供了工程化的土壤。
在此你不妨思考这样一个问题: 一个构建工具最基本最底层的能力是什么?答案是文件读写能力。
而在 Node.js 之前,HTML/JS/CSS 的构建只有两个途径:
借助其他编程语言的工具,比如基于 Java 的 YUI Compressor;
用IE浏览器,这不是笑话,IE 浏览器能够在早期占据霸主地位可不完全是因为 Windows 系统的背书,它自身的功能性也非常强大。早期的 IE 浏览器是 Windows 系统的一个组件,以它为入口可以调用一些系统级的底层功能,文件 IO 便在此列,实现的方式是借助ActiveX 控件的 FileSystemObject 对象(细节就不讲了,感兴趣的话,你可以自行搜索)。
另外,有一些声音认为 React 能够配得上“革命性”一词,因为它一定程度上改变了前端的编程模型,React 之前的前端是围绕 DOM,React 之后是面向数据。诚然如此,但跟 AJAX 和Node.js 相比,React 引起的变革程度达不到同等的量级,而 React 对前端组件化生态的影响也是在原有基础上的增强而不是新创。
所以,称 React 为前端 3.0 缺乏足够的说服力,不过前端 2.5 还是充分的。说到底,React 只是改变了前端领域自身,而 AJAX 和 Node.js 无一不是对前后端都有显著影响的技术。
对于前端下一步的发展方向目前有两种主流的声音:
前后端包揽的“大前端”,也就是“全栈”,关键性技术是Node.js;
以 React Native、Flutter 等跨端技术为突破点的“泛前端”,即全端。
其实,这两个方向并不冲突,比如微信小程序本质上也是一种跨端开发技术,你编写的代码不需要关注是 iOS 还是 Android 平台,而云开发实现了小程序的前后端全栈开发,所以使用云开发的小程序即可以叫作“大前端”,也可以被叫作“泛前端”。
不过,“全栈”这个词一直存在歧义:
狭义上的全栈来源于前端技术圈,指的是一个人包揽前端和 Web 服务端;
广义上的全栈应该是在前后端以外,还包括数据库并且能够精通围绕三者展开的架构和技术细节,这是几乎不可能的任务,如果真的有人能够达到广义的“全栈”,估计就是接近艾伦·图灵一般的计算机之神。
而在狭义与广义之间,云开发面向的是广义的全栈,将服务器管理、数据库优化等“脏活”交给云平台,从而前端开发者能够将交互逻辑、业务逻辑、数据全部掌控在自己手中,这才是真正意义上的全栈开发。
横向上,跨端技术抹平了端侧的差异,纵向上,云开发比 Node.js 更进一步降低了后端的开发门槛,进而实现了全栈开发。 为什么呢?我想你通过对比云开发与 BFF(Backends for Frontend,为前端服务的后端),能够得到答案。
BFF 简单来说就是在原有的一体化服务端基础上,针对不同的业务平台分别开发一层独有的、很薄的服务端。
一体化服务架构
BFF架构
其中,BFF 主要承担了后端接口聚合功能,以及一部分平台差异化的业务逻辑,而与平台无关的核心业务逻辑由后端的微服务承载。这种架构模式的优势在于实现了差异化逻辑的松耦合,进而令前后端的迭代和维护更高效和安全。
目前业内对 BFF 普遍实践模式是将 BFF 分发到负责各平台技术开发的团队,比如 App 团队负责 Mobile BFF、前端团队负责 PC web 和 H5 BFF ……那么对于前端工程师来说,这种模式是否意味着前端兼顾 BFF层?理想的场景是这样的,但现实工作中并非如此。
因为 BFF 本质上仍然是服务端,对于前端开发而言,Node.js 虽然解决编程语言的问题,但是编程语言只是服务端编程最表面也是最容易突破的一层。除了编程语言之外,一名合格的服务端开发者还需要具备一些服务端独有的领域知识以及服务治理、数据管理等,这些才是真正阻碍前端成为全栈的关键因素,而这些问题便是云开发主要解决的问题。
云开发解决的问题
总结一句话就是: 在前端向全栈进化的角度上,Node.js 实现了从 0 到 1,而云开发更进一步实现了从 1 到 99。为什么不是 100 呢?因为我们团队并没有停滞不前,仍然在不停地探索更完善的方案,未来你会看到云开发更好的一面。
讲到这儿,我们这门课才算是真正地结束了,今天我和你一起探讨了云开发可能带给前端的变革。我希望你能在学完所有课程之后,除了掌握其中讲解的技术能力之外,还能得到一些启发,比如从历史全局的角度上理解云开发这项技术带来的变革。如果这些启发能够会在你未来的职业生涯中产生一些积极的影响,我便心满意足了。
十分非常荣幸也非常高兴能够跟你分享一些技术和思想,如果你有任何关于微信小程序或云开发的问题,可以在课程留言区处留言,或者可以尝试在云开发的官网中寻找答案。如果没有找到的话,可以在我们的问答社区找到我,必有答复。 除此之外,我为你准备了一份结课问卷,希望你对本课程的内容进行评价,以便我及时优化课程内容。