1、概念

  • 微信小程序的云开发是腾讯云与微信团队深度合作推出的一个全新的小程序的解决方案,它提供了云函数、云数据库与云存储这三大基础能力支持,随着云开发的出现,小程序的开发者可以将服务端的部署和运营的环节进行服务端的托管,让腾讯云去管理,而不需要在运维和管理方面投入太多的精力。

2、传统小程序开发与云开发的区别

  • 小程序传统开发模式:(开发效率低、运维成本高)

    1. 对于小程序传统的开发模式而言,我们需要一个客户端(也就是前端页面),而前端页面展示的数据大多来自于数据库,因此我们还需要一个服务端,把后端的代码以及数
      据库部署上去,并且前后端联调的过程也是沟通的过程,时间成本比较高;小程序在部署的时候,我们需要购买相对应的域名以及服务器,并且还要进行备案(非常麻烦、耗时长),
      部署成功后,在运维小程序的过程中,也会遇到很多很多的问题,比如DB运维、文件存储、内容加速(CDN)、网络防护、容器服务、负载均衡以及安全加固等;虽然有的公司没有
      购买服务器,使用的是阿里云或者腾讯云上面的服务,但是依旧需要维护,因此运维成本非常高。

  • 小程序云开发模式:采用serverless(无服务)模式

    1. 对于小程序的云开发模式而言,依旧需要一个客户端,但是由于小程序提供了云开发,而云开发提供了云函数、云数据库以及云存储三大基础能力支持,我们可以直接在客户
      端调用云数据库里面的内容,当然也可以通过客户端调用云函数,在云函数里面处理一些业务逻辑,并在云函数里面调用云数据库,同时我们可以在客户端上传相应的文件到云存储当
      中,或者将云存储中的图片下载到客户端给用户去展示,由于云开发是部署在腾讯云上面的,因此我们不需要额外的运维人员,运维成本也会降低。除此之外,小程序中的云函数采用
      的是Node.js,云数据库采用的是mongoDB,而node调用mongoDB也是很方便的。

    • 三大基础能力支持:

      • 云数据库:是一个JSON数据库(文档型数据库),提供了2GB的免费存储空间,实现对数据的增删改查的操作;

        • 支持的数据类型:Number、String、Object、Array、Boolean、Date、Null以及GeoPoint(地理位置点);
        • 如何通过代码操作数据库:
          • 初始化:

            1. const db = wx.cloud.database()
          • 切换环境
            1. const testDB = wx.cloud.database({
            2. env: 'test'//环境名称
            3. })
          • 简单数据的增删改查
            • WXML

              1. <view>数据库的增删改查</view>
              2. <button size="mini" catchtap="onCatchPostHandler">增加</button>
              3. <button size="mini" catchtap="onCatchDeleteHandler">删除</button>
              4. <button size="mini" catchtap="onCatchUpdateHandler">修改</button>
              5. <button size="mini" catchtap="onCatchGetHandler">查找</button>
            • JS
              1. // 数据库的初始化
              2. const db = wx.cloud.database();
              3.  
              4. Page({
              5.  
              6. /**
              7. * 页面的初始数据
              8. */
              9. data: {
              10.  
              11. },
              12. /**
              13. * 数据库的添加
              14. */
              15. onCatchPostHandler() {
              16. // 回调函数写法
              17. /**
              18. * db.collection('user').add({
              19. data: {
              20. name: 'wxh',
              21. age: '18',
              22. job: 'IT'
              23. },
              24. success: (res) => {
              25. // 使用了ES6中的箭头函数,目的是为了改变this指向
              26. console.log(res)
              27. },
              28. fail: (error) => {
              29. console.log(error)
              30. }
              31. })
              32. */
              33. // promise的写法
              34. db.collection('user')
              35. .add({
              36. data: {
              37. name: 'jerry',
              38. age: '',
              39. job: 'teacher'
              40. }
              41. })
              42. .then((res) => {
              43. console.log(res)
              44. })
              45. .catch((error) => {
              46. console.log(error)
              47. })
              48. },
              49. /**
              50. * 数据库的删除
              51. */
              52. onCatchDeleteHandler() {
              53. // 删除一条可以通过小程序端进行控制,而删除多条我们需要小程序调用云函数来操作数据库
              54. db.collection('user')
              55. .doc("b040a67a5df1f6d1029170ef7e785160")
              56. .remove()
              57. .then((res) => {
              58. console.log(res)
              59. })
              60. .catch((error) => {
              61. console.log(error)
              62. })
              63. },
              64. /**
              65. * 数据库的修改
              66. */
              67. onCatchUpdateHandler() {
              68. db.collection('user')
              69. .doc('b040a67a5df1f6d1029170ef7e785160')
              70. .update({
              71. data: {
              72. age: ''
              73. }
              74. })
              75. .then((res) => {
              76. console.log(res)
              77. })
              78. .catch((error) => {
              79. console.log(error)
              80. })
              81. },
              82. /**
              83. * 数据库的查找
              84. */
              85. onCatchGetHandler() {
              86. db.collection('user')
              87. .where({
              88. name: 'wxh'
              89. })
              90. .get({})
              91. .then((res) => {
              92. console.log(res)
              93. })
              94. .catch((error) => {
              95. console.log(error)
              96. })
              97. },
            • 注意:如果是在数据库手动加入记录,会出现查不到的情况,这个时候我们需要设置

      • 云函数:相当于小程序在服务端的后台代码,可以非常方便的获取到当前登录用户的信息(appid、openid、生成分享图或者调用腾讯云的SDK);

        • 去Node官网(http://nodejs.cn/),安装Node环境(需要nodev8.0及以上版本);
        • 安装成功的标志:windows+r,输入cmd, 打开命令输入  node -v 显示版本号;

        • 创建云函数

          • 很有可能提醒是否需要安装wx-server-sdk    ----- >    点击确定;
          • 每次修改云函数都需右键点击“上传并部署,云端安装依赖”
          • 在调用云函数的过程中提示没有安装wx-server-sdk包,这个时候在cloudfunctions中,右键在终端打开,输入安装命令npm install --save wx-server-sdk@latest(表示安装最新版本)
        • 简单的调用一个云函数:求a+b的和;
          • sum下的index.js

            1. // 云函数入口函数
            2. exports.main = async (event, context) => {
            3. // event 中包括小程序调用云函数传过来的对象
            4. // context 指当前调用的上下文,同时也包括当前用户的一些信息
            5. return {
            6. sum: event.a + event.b
            7. }
            8. }
            1. <button catchtap="onCatchSumHandler" size="mini">调用云函数sum</button>
            2. /**
            3. * 云函数的调用
            4. */
            5. onCatchSumHandler() {
            6. wx.cloud
            7. .callFunction({
            8. name: 'sum',//当前云函数的名称
            9. data: {
            10. a: ,
            11. b:
            12. }
            13. })
            14. .then((res) => {
            15. console.log(res)
            16. })
            17. .catch(error => {
            18. console.log(error)
            19. })
            20. },
        • 获取当前用户的openid:
          • 传统的微信登录方式与小程序云开发登录方式的区别:

            • 传统的微信登录方式

              1. 首先用户端小程序通过调用wx.login,从微信服务端获取一个code,然后用户小程序端调用wx.requestcode传递给后端服务器,后端
                获得到code之后,对微信服务端发起请求,通过code获取openidsession_key,最后将小程序的唯一标识发送给小程序本地存储。

            • 小程序云开发登录方式
              1. 用户通过点击按钮,从小程序获取用户的信息,而小程序通过云函数获得用户的信息,云函数给小程序端返回用户的openid,小程序获取到
                用户的信息之后,将用户的信息存储到云数据库。

          • 云函数会自带一个login文件夹,它的作用就是获取当前用户的信息;
            1. <button catchtap="onCatchOpenIdHandler" size="mini">获取当前用户openid</button>
            2.  
            3. /**
            4. * 获取当前用户的openid
            5. */
            6. onCatchOpenIdHandler() {
            7. wx.cloud
            8. .callFunction({
            9. name: 'login',
            10. })
            11. .then((res) => {
            12. console.log(res)
            13. })
            14. .catch((error) => {
            15. console.log(error)
            16. })
            17. },
        • 批量删除
          • 定义云函数batchDelete

            1. // 云函数入口文件
            2. const cloud = require('wx-server-sdk')
            3.  
            4. cloud.init()
            5.  
            6. // 获取云数据库
            7. const db = cloud.database();
            8.  
            9. // 云函数入口函数
            10. exports.main = async (event, context) => {
            11. // 数据库是一个异步的操作,等到删除成功或者失败才可以返回数据给小程序 因此需要ES7的await
            12. try {
            13. return await db.collection('user')
            14. .where({
            15. name: event.name
            16. })
            17. .remove();
            18. }
            19. catch (error) {
            20. console.log(error)
            21. }
            22. }
          • 调用云函数
            1. <button catchtap="onCatchBatchDeleteIdHandler" size="mini">批量删除数据</button>
            2.  
            3. /**
            4. * 批量删除
            5. */
            6. onCatchBatchDeleteIdHandler() {
            7. wx.cloud.callFunction({
            8. name: 'batchDelete',
            9. data: {
            10. name: 'wxh'
            11. }
            12. }).then((res) => {
            13. console.log(res)
            14. }).catch((error) => {
            15. console.log(error)
            16. })
            17. },
      • 云存储:管理、上传、下载以及分享文件等操作;

        • 云存储能力

          • wx.cloud.uploadFile  -----   上传文件

            • 步骤示意图

            • 上传并展示的代码

              1. <view>云存储</view>
              2. <button catchtap="onCatchUploadHandler">上传图片</button>
              3. <button catchtap="onCatchShowHandler">展示图片</button>
              4. <view wx:for="{{showImgPath}}" wx:key="index">
              5. <image src="{{item.fileID}}"></image>
              6. </view>
              7.  
              8. /**
              9. * 页面的初始数据
              10. */
              11. data: {
              12. showImgPath: []
              13. },
              14. /**
              15. * 图片上传
              16. */
              17. onCatchUploadHandler() {
              18. // 1、从本地相册选择图片或使用相机拍照
              19. wx.chooseImage({
              20. count: , //最多选择的图片张数,最大值为9
              21. sizeType: ['original', 'compressed'], //所选图片的尺寸(原图、压缩)
              22. sourceType: ['album', 'camera'], //图片来源(相册、相机),电脑端只会打开相册,而手机端会有两个选项(拍照或者相册)
              23. success(res) {
              24. // tempFilePath可以作为img标签的src属性显示图片
              25. const tempFilePaths = res.tempFilePaths[];
              26. wx.cloud.uploadFile({
              27. cloudPath: new Date().getTime() + 'png', // 上传至云端的路径 可以指定文件夹以及文件名称 如果写死每次上传都会覆盖原有图片
              28. filePath: tempFilePaths, // 小程序临时文件路径
              29. success: res => {
              30. console.log("上传成功")
              31. // 返回文件 ID
              32. db.collection('imgFile')
              33. .add({
              34. data: {
              35. fileID: res.fileID
              36. },
              37. success: (res) => {
              38. console.log(res)
              39. console.log("保存成功")
              40. },
              41. fail: (error) => {
              42. console.log(error)
              43. }
              44. })
              45. },
              46. fail: console.error
              47. })
              48. }
              49. })
              50. },
              51. /**
              52. * 图片展示
              53. */
              54. onCatchShowHandler() {
              55. // 1、通过云存储查找用户本人管理的图片
              56. wx.cloud.callFunction({
              57. name: 'login',
              58. success: res => {
              59. const openid = res.result.openid;
              60. // 2、去数据库中查找
              61. db.collection('imgFile')
              62. .where({
              63. _openid: openid
              64. })
              65. .get()
              66. .then((res) => {
              67. this.setData({
              68. showImgPath: res.data
              69. })
              70. console.log(res)
              71. })
              72. .catch((error) => {
              73. console.log(error)
              74. })
              75. },
              76. fail: error => {
              77. console.log(error)
              78. }
              79. })
              80. },
            • 上传多张图片
              • WXML

                1. <!-- 评价 -->
                2. <view class="comment-container">
                3. <van-field value="{{ content }}" placeholder="写一些评价吧" bind:change="onContentChange" />
                4. <van-rate value="{{ score }}" bind:change="onScoreChange" />
                5. <view style="margin-bottom:20rpx">
                6. <van-button type="warning" bindtap="uploadImg" size="small">上传图片</van-button>
                7. </view>
                8. <view>
                9. <image class="comment-img" src="{{item}}" wx:for="{{uploadImages}}" wx:key="index"></image>
                10. </view>
                11. <van-button size="large" type="danger" bindtap="submit">提交评价</van-button>
                12. </view>
                13. </view>
              • JS
                1. // 对数据库进行初始化
                2. const db = wx.cloud.database();
                3.  
                4. Page({
                5.  
                6. /**
                7. * 页面的初始数据
                8. */
                9. data: {
                10. movieid: '', //对应电影的movieid
                11. movieDetail: {},
                12. content: '', //评价的内容
                13. score: , //电影评分
                14. uploadImages: [], //上传的图片
                15. fileIds: [], //云存储返回的图片id
                16. },
                17.  
                18. /**
                19. * 生命周期函数--监听页面加载
                20. */
                21. onLoad: function(options) {
                22. // 获取上一个页面传过来的参数
                23. this.setData({
                24. movieid: options.movieid
                25. })
                26. this.getMovieDetail(options.movieid)
                27. },
                28. /**
                29. * 获取电影详情信息
                30. */
                31. getMovieDetail(movieid) {
                32. wx.showLoading({
                33. title: '加载中',
                34. })
                35. wx.cloud.callFunction({
                36. name: 'getDetail',
                37. data: {
                38. movieid: movieid
                39. },
                40. success: res => {
                41. this.setData({
                42. movieDetail: JSON.parse(res.result)
                43. })
                44. wx.hideLoading()
                45. },
                46. fail: error => {
                47. console.log(error)
                48. }
                49. })
                50. },
                51. /**
                52. * 输入评价
                53. */
                54. onContentChange(event) {
                55. this.setData({
                56. content: event.detail //输入框的内容
                57. })
                58. },
                59. /**
                60. * 进行评分
                61. */
                62. onScoreChange(event) {
                63. this.setData({
                64. score: event.detail
                65. });
                66. },
                67. /**
                68. * 上传图片
                69. */
                70. uploadImg() {
                71. wx.chooseImage({
                72. count: ,
                73. sizeType: ['original', 'compressed'],
                74. sourceType: ['album', 'camera'],
                75. success: res => { //可以改变this指向
                76. // tempFilePath可以作为img标签的src属性显示图片
                77. const tempFilePaths = res.tempFilePaths;
                78. this.setData({
                79. uploadImages: this.data.uploadImages.concat(tempFilePaths)
                80. })
                81. }
                82. })
                83. },
                84. /**
                85. * 提交评价
                86. */
                87. submit() {
                88. wx.showLoading({
                89. title: '评价中...',
                90. })
                91. // 首先获取到评价的内容 分数 以及上传的图片
                92. // 涉及到了异步(非阻塞)的问题,由于上传时间不确定,因此需要成功上传到云存储才能将fileid存到云数据库
                93. let promiseArr = [];
                94. // 1、上传图片到云存储
                95. for (let i = ; i < this.data.uploadImages.length; i++) {
                96. promiseArr.push(new Promise((resolve, reject) => {
                97. let item = this.data.uploadImages[i];
                98. // 通过正则表达式取出文件对应的扩展名
                99. let suffix = /\.\w+$/.exec(item)[];
                100. wx.cloud.uploadFile({
                101. cloudPath: new Date().getTime() + suffix, // 上传至云端的路径
                102. filePath: item, // 小程序临时文件路径
                103. success: res => {
                104. // 返回文件 ID
                105. this.setData({
                106. fileIds: this.data.fileIds.concat(res.fileID)
                107. });
                108. resolve()
                109. },
                110. fail: console.error
                111. })
                112. }))
                113. }
                114. Promise.all(promiseArr).then((res) => {
                115. // 2、插入数据
                116. db.collection('comment').add({
                117. data: {
                118. content: this.data.content,
                119. score: this.data.score,
                120. movieid: this.data.movieid,
                121. fileIds: this.data.fileIds
                122. },
                123. success: (res) => {
                124. wx.showToast({
                125. title: '评价成功',
                126. });
                127. wx.hideLoading()
                128. },
                129. fail: (error) => {
                130. wx.hideLoading();
                131. wx.showToast({
                132. title: '评价失败',
                133. })
                134. }
                135. })
                136.  
                137. }).catch((error) => {
                138. console.log(error)
                139. })
                140. },
              • 效果图

          • wx.cloud.downloadFile  -----   下载文件

            • 步骤示意图

            • 代码

              • WXML

                1. <view>云存储</view>
                2. <button catchtap="onCatchUploadHandler">上传图片</button>
                3. <button catchtap="onCatchShowHandler">展示图片</button>
                4. <button catchtap="onCatchSettingAgainHandler">再次调起配置页</button>
                5. <view wx:for="{{showImgPath}}" wx:key="index">
                6. <image src="{{item.fileID}}"></image>
                7. <button catchtap="onCatchDownLoadHandler" data-fileID="{{item.fileID}}">下载图片</button>
                8. </view>
              • JS
                1. /**
                2. * 图片下载
                3. */
                4. onCatchDownLoadHandler(event) {
                5. const fileID = event.target.dataset.fileid;
                6. wx.cloud.downloadFile({
                7. fileID: fileID,
                8. success: res => {
                9. wx.showToast({
                10. title: '下载成功',
                11. })
                12. // 保存到手机相册
                13. wx.saveImageToPhotosAlbum({
                14. filePath: res.tempFilePath,
                15. success: (res) => {
                16. wx.showToast({
                17. title: '保存成功',
                18. })
                19. },
                20. fail: (error) => {
                21. if (error.errMsg === "saveImageToPhotosAlbum:fail:auth denied" || error.errMsg === "saveImageToPhotosAlbum:fail auth deny" || error.errMsg === "saveImageToPhotosAlbum:fail authorize no response") {
                22. wx.showToast({
                23. title: '用户拒绝了',
                24. });
                25. wx.openSetting({
                26. success: res => {
                27. console.log(res)
                28. if (res.authSetting['scope.writePhotosAlbum']) {
                29. wx.showModal({
                30. title: '提示',
                31. content: '获取权限成功,再次点击图片即可保存',
                32. showCancel: false,
                33. })
                34. } else {
                35. wx.showModal({
                36. title: '提示',
                37. content: '获取权限失败,将无法保存到相册哦~',
                38. showCancel: false,
                39. })
                40. }
                41. },
                42. fail: error => {
                43. console.log(error)
                44. }
                45. })
                46. }
                47. }
                48. })
                49. },
                50. fail: error => {
                51. console.log(error)
                52. }
                53. })
                54. },
                55. /**
                56. * 打开配置
                57. */
                58. onCatchSettingAgainHandler() {
                59. wx.openSetting({
                60. success: res => {
                61. console.log(res)
                62. },
                63. fail: error => {
                64. console.log(error)
                65. }
                66. })
                67. },
          • wx.cloud.deleteFile  -----   删除文件
          • wx.cloud.getTempFileURL  -----   获取临时链接

3、云开发的开通

  • 打开微信开发工具,点击云开发 -----> 开通 -----> 确定

  • 会出现一个环境配置,每个小程序账号可免费创建两个环境,建议是一、开发环境;二、生产环境;
    • 注意:开发工具右上角点击 “详情” -----> 本地设置(调试基础库的版本必须在2.2.3以上,才可以支持云开发)
  • 云开发提供了一个可视化的控制台,点击云开发即可出现;

4、遇到的问题

微信小程序开发(二)----- 云开发的更多相关文章

  1. 微信小程序中使用云开发获取openid

    微信小程序获取openid 新建一个微信小程序项目 注意要注册一个自己的小程序账号,并有属于自己的appid 点击云开发按钮,自行填入开发环境名称 打开app.js,找到依赖环境 修改为刚才设置的环境 ...

  2. 微信小程序--简约风博客小程序(基于云开发 - 全开源)

    微信小程序--简约风博客小程序(基于云开发 - 全开源) 项目启动纯属突发奇想,想看看博客小程序,例如wehalo博客小程序,但是感觉自建平台还要浪费自己的服务器算力,还没有访问量,省省吧. 本着白嫖 ...

  3. 微信小程序版博客——开发汇总总结(附源码)

    花了点时间陆陆续续,拼拼凑凑将我的小程序版博客搭建完了,这里做个简单的分享和总结. 整体效果 对于博客来说功能页面不是很多,且有些限制于后端服务(基于ghost博客提供的服务),相关样式可以参考截图或 ...

  4. 微信小程序购物商城系统开发系列-目录结构

    上一篇我们简单介绍了一下微信小程序的IDE(微信小程序购物商城系统开发系列-工具篇),相信大家都已经蠢蠢欲试建立一个自己的小程序,去完成一个独立的商城网站. 先别着急我们一步步来,先尝试下写一个自己的 ...

  5. 微信小程序购物商城系统开发系列-工具篇

    微信小程序开放公测以来,一夜之间在各种技术社区中就火起来啦.对于它 估计大家都不陌生了,对于它未来的价值就不再赘述,简单一句话:可以把小程序简单理解为一个新的操作系统.新的生态,未来大部分应用场景都将 ...

  6. 微信小程序购物商城系统开发系列

    微信小程序购物商城系统开发系列 微信小程序开放公测以来,一夜之间在各种技术社区中就火起来啦.对于它 估计大家都不陌生了,对于它未来的价值就不再赘述,简单一句话:可以把小程序简单理解为一个新的操作系统. ...

  7. 从微信小程序到鸿蒙js开发【11】——页面路由

    目录: 1.router.push()&wx.navigateTo() 2.router.replace()&wx.redirectTo() 3.router.back()&w ...

  8. 从微信小程序到鸿蒙js开发【12】——storage缓存&自动登录

    鸿蒙入门指南,小白速来!从萌新到高手,怎样快速掌握鸿蒙开发?[课程入口] 正文: 在应用开发时,我们常需要将一些数据缓存到本地,以提升用户体验.比如在一个电商的app中,如果希望用户登录成功后,下次打 ...

  9. 从微信小程序到鸿蒙js开发【13】——list加载更多&回到顶部

    鸿蒙入门指南,小白速来!从萌新到高手,怎样快速掌握鸿蒙开发?[课程入口] 目录: 1.list加载更多 2.list回到顶部 3.<从微信小程序到鸿蒙js开发>系列文章合集 1.list加 ...

  10. 从微信小程序到鸿蒙js开发【15】——JS调用Java

    鸿蒙入门指南,小白速来!0基础学习路线分享,高效学习方法,重点答疑解惑--->[课程入口] 目录:1.新建一个Service Ability2.完善代码逻辑3.JS端远程调用4.<从微信小 ...

随机推荐

  1. 前端要懂的nginx配置

    多服务接口nginx反向代理 ```js server { listen 80; server_name xx.xx.xx.com; // 前端部署的域名 root /med; index index ...

  2. Spring MVC 设置UTF-8编码

    按照需求选其中之一即可吧. 修改读取参数时候的编码: 在web.xml中: 添加一个过滤器(filter),注册 org.springframework.web.filter.CharacterEnc ...

  3. PHP数字处理

    援引自博文 https://blog.csdn.net/hongxiaoshuang/article/details/54598757 ceil -- 进一法取整 float ceil ( float ...

  4. 悲观锁,乐观锁以及MVCC

    在上文中,我们探讨了MySQL不同存储引擎中的各类锁,在这篇文章中我们将要讨论的是MySQL是如何实现并发控制的.并发问题有三种,分别为: 读-读,不存在任何问题 读-写,有隔离性问题,可能遇到脏读( ...

  5. linux中的selinux到底是什么

    一文彻底明白linux中的selinux到底是什么 2018年06月29日 14:17:30 yanjun821126 阅读数 58877 标签: SElinux 更多 个人分类: Linux   一 ...

  6. 如何将 GitHub 中的项目导入到 stackblitz.com 中

    如何将一个 GitHub 中的项目导入到 stackblitz.com 中,然后开始编辑和编译呢? 例如,我们有一个项目在 GitHub 中的地址为:https://github.com/cwiki- ...

  7. 详解vue的diff算法原理

    我的目标是写一个非常详细的关于diff的干货,所以本文有点长.也会用到大量的图片以及代码举例,目的让看这篇文章的朋友一定弄明白diff的边边角角. 先来了解几个点... 1. 当数据发生变化时,vue ...

  8. 「WC 2007」剪刀石头布

    题目链接 戳我 \(Solution\) 直接求很明显不太好求,于是考虑不构成剪刀石头布的情况. 我们现在假设一个人\(i\)赢了\(x\)场,那么就会有\(\frac{x*(x-1)}{2}\) 我 ...

  9. vue-cli构建一个工程

    使用前,必须要先按照 node:安装node Vue CLI官方文档:https://cli.vuejs.org/zh/ 安装node地址:https://nodejs.org/zh-cn/downl ...

  10. Spring的jsp标签库

    1,主要有两个 一个用于渲染HTML表单标签, 这些标签会绑定model中的某个属性 另一个包换了一些工具类标签 2,将表单绑定到模型上 包含了14个标签,海报一个人为用户展现错误的标签,它会将错误信 ...