在这里插入图片描述

变化

文件变化:

以前是.html文件,现在是.vue文件

网络模型变化:

以前网页大多是b/s,服务端代码混合在页面里;
现在是c/s,前后端分离,通过js api(类似ajax的uni.request)获取json数据,把数据绑定在界面上渲染。

文件内代码架构变化:

以前一个html大节点,里面有script和style节点;
现在template是一级节点,用于写tag组件,script和style是并列的一级节点,也就是有3个一级节点。vue单文件组件规范

<template>  
    <view>  
    必须有一个view,且只能有一个根view。写所有内容。  
    </view>  
</template>  
<script>  
    export default {  
    data(){
    
    }
 }  
</script>  

<style>  
</style>

外部文件引用方式变化:

以前通过script src、link href引入外部的js和css;
现是es6的写法,import引入外部的js模块(注意不是文件)或css

js要require进来,变成了对象。
在hello uni-app的common目录有一个工具类util.js,可以在hello uni-app中搜索这个例子查看。

<script>  
var util = require('../../../common/util.js');  //require这个js模块  
var formatedPlayTime = util.formatTime(playTime); //调用js模块的方法  
</script>

而在这个util.js里,要把之前的function封装为对象的方法

function formatTime(time) {  
    return time;//这里没写逻辑  
}  

module.exports = {  
    formatTime: formatTime  
}

还有一些高级的用法

var dateUtils = require('../../../common/util.js').dateUtils; //直接使用js模块的属性。在hello uni-app有示例  
import * as echarts from '/components/echarts/echarts.simple.min.js'; //将js导入并重命名为echarts,然后使用echarts.来继续执行方法。在hello uni-app有示例

css外部文件导入

<style>  
    @import "./common/uni.css";  
</style>

全局样式,在根目录下的app.vue里写入,每个页面都会加载app.vue里的样式。

另外,vue支持组件导入,可以更方便的封装一个包括界面、js、样式的库。
如下是导入一个角标的组件库,在页面上显示一个abc并且右上角有个数字角标1

<template>  
    <view>  
        <uni-badge text="abc" :inverted="true"></uni-badge><!--3.使用组件-->  
    </view>  
</template>  
<script>  
    import uniBadge from "../../../components/uni-badge.vue";//1.导入组件  
    export default {  
        data() {  
            return {  

            }  
        },  
        components: {  
            uniBadge //2.注册组件  
        }  
    }  
</script>

如需要全局导入vue组件,每个页面都可以直接使用而不用引用和注册,在项目根目录下的main.js里处理。如下是hello uni-app里的例子。

//main.js  
import pageHead from './components/page-head.vue' //导入  
Vue.component('page-head', pageHead) //注册。注册后在每个vue的page页面里可以直接使用<page-head></page-head>组件。

组件/标签的变化:

除了改动外,新增了一批移动端常用的新组件

cover-view需要多强调几句,uni-app的非h5端的video、map、canvas、textarea是原生组件,层级高于其他组件。如需覆盖原生组件,比如在map上加个遮罩,则需要使用cover-view组件

除了内置组件,还有很多开源的扩展组件,把常用操作都进行封装,DCloud建立了插件市场收录这些扩展组件,详见插件市场

js的变化:
js的变化,分为运行环境变化、数据绑定模式变化、api变化3部分。

标准js语法和api都支持,比如if、for、settimeout、indexOf等。

但浏览器专用的window、document、navigator、location对象,包括cookie等存储,只有在浏览器中才有,app和小程序都不支持。

可能有些人以为js等于浏览器里的js。其实js是ECMAScript组织管理的,浏览器中的js是w3c组织基于js规范补充了window、document、navigator、location等专用对象。

在uni-app的各个端中,除了h5端,其他端的js都运行在一个独立的v8引擎下,不是在浏览器中,所以浏览器的对象无法使用。如果你做过小程序开发,对此应当很了解。

这意味着依赖document的很多HTML的库,比如jqurey无法使用。

当然app和小程序支持web-view组件,里面可以加载标准HTML,这种页面仍然支持浏览器专用对象window、document、navigator、location。

  • 以前的dom操作,改成vue的MVVM模式

现在前端趋势是去dom化,改用mvvm模式,更简洁的写法,大幅减少代码行数,同时差量渲染性能更好。
uni-app使用vue的数据绑定方式解决js和dom界面交互的问题。

如果你想改变某个dom元素的显示内容,比如一个view的显示文字:

以前是给view设id,然后js里通过选择器获取dom元素,进一步通过js进行赋值操作,修改dom元素的属性或值。

如下演示了一段代码,页面中有个显示的文字区和一个按钮,点击按钮后会修改文字区的值

<template>  
    <view>  
        <text>{{textvalue}}</text><!-- 这里演示了组件值的绑定 -->  
        <button :type="buttontype" @click="changetextvalue()">修改为789</button><!-- 这里演示了属性和事件的绑定 -->  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                textvalue:"123",  
                buttontype:"primary"  
            };  
        },  
        onLoad() {  
            this.textvalue="456"//这里修改textvalue的值,其实123都来不及显示就变成了456  
        },  
        methods: {  
            changetextvalue() {  
                this.textvalue="789"//这里修改textvalue的值,页面自动刷新为789  
            }  
        }  
    }  
</script>

如果你学过小程序的数据绑定,但不了解vue,要注意:

  • 小程序的数据绑定参考了vue,但自己修改了一些。在uni-app中只支持标准的vue,不支持小程序的数据绑定语法
  • 小程序里的setData在uni-app里并不存在,因为vue是自动双向数据绑定的。直接通过赋值方式修改数据,如果数据绑定到界面上,界面会自动更新渲染
  • js api的变化

因为uni-app的api是参考小程序的,所以和浏览器的js api有很多不同,如

  • alert,confirm 改成 uni.showmodel
  • ajax 改成 uni.request
  • cookie、session 没有了,local.storage 改成 uni.storage

uni-app的js api还有很多,但基本就是小程序的api,把wx.xxx改为uni.xxx即可。详见
uni-app在不同的端,支持条件编译,无限制的使用各端独有的api,条件编译

css的变化:
标准的css基本都是支持的。

选择器有2个变化:*选择器不支持;元素选择器里没有body,改为了page。微信小程序即是如此。

单位方面,px无法动态适应不同宽度的屏幕,rem无法用于nvue/weex。如果想使用根据屏幕宽度自适应的单位,推荐使用rpx,全端支持。

uni-app推荐使用flex布局,并默认就是flex布局,这个布局思路和传统流式布局有点区别。但flex的有趣在于,不管是什么技术都支持这种排版,web、小程序/快应用、weex/rn、原生的iOS、Android开发,全都支持flex。它是通吃所有端的新一代布局方案。相关教程请自行百度学习。

uni-app的vue文件里支持所有web排版方式,不管是流式还是flex。但nvue里,只支持flex,因为它在app端是使用原生排版引擎渲染的。

注意背景图和字体文件尽量不要大于40k。会影响性能。如果非要大于40k,需放到服务器侧远程引用或base64后引入,不能放到本地作为独立文件引用。在小程序里,其实小于40k的文件在css里也无法引用,uni-app编译器在编译时自动做了处理,把小于40k的文件编译为base64方式了。

工程结构和页面管理

uni-app的工程结构有单独的要求,详见

每个可显示的页面,都必须在 pages.json中注册。如果你开发过小程序,那么pages.json类似app.json。如果你熟悉vue,这里没有vue的路由,都是在pages.json里管理。

vue工程的首页一般是index.html或default.html,是在web server里配的。而uni-app的首页,是在pages.json里配的,page节点下第一个页面就是首页。一般在/pages/xx的目录下。

app和小程序中,为了提升体验,页面提供了原生的导航栏和底部tabbar,注意这些配置是在pages.json中做,而不是在vue页面里创建,但点击事件的监听在显示的vue页面中做。

在vue中,以前的js事件监听概念改为了生命周期概念。uni-app生命周期

如果你熟悉小程序开发的话,对比变化如下

  • 原来app.json被一拆为二。页面管理,被挪入了uni-app的pages.json;非页面管理,挪入manifest.json
  • 原来的app.js和app.wxss被合并到了app.vue中

开发规范

为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了如下开发规范:

目录结构

一个uni-app工程,默认包含如下目录及文件:

┌─components uni-app组件目录

│ └─comp-a.vue 可复用的a组件

├─hybrid 存放本地网页的目录,

├─platforms 存放各平台专用页面的目录,

├─pages 业务页面文件存放的目录

│ ├─index

│ │ └─index.vue index页面

│ └─list

│ └─list.vue list页面

├─static 存放应用引用静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此

├─wxcomponents 存放小程序组件的目录,

├─main.js Vue初始化入口文件

├─App.vue 应用配置,用来配置App全局样式以及监听

├─manifest.json 配置应用名称、appid、logo、版本等打包信息

└─pages.json 配置页面路由、导航条、选项卡等页面类信息

Tips:

  • static 下的 js 文件不会被编译,如果里有 es6 的代码,不经过转换直接运行,手机设备上会报错。
  • css、less/scss 等资源同样不要放在 static 目录下,建议这些公用的资源放在 common 目录下。 HbuilderX1.9.0+ 支持在根目录创建 ext.json sitemap.json 文件。

资源路径:
++模板内引入静态资源:
template内引入静态资源,如image、video等标签的src属性时,可以使用相对路径或者绝对路径
++js文件引入:
js文件或script标签内(包括renderjs等)引入js文件时,可以使用相对路径和绝对路径
++css引入静态资源:
css文件或style标签内引入css文件时(scss、less文件同理),只能使用相对路径
注意:自HBuilderX 2.6.6-alpha起支持绝对路径引入静态资源,旧版本不支持此方式

路由:
uni-app页面路由为框架统一管理,开发者需要在pages.json里配置每个路由页面的路径及页面样式。类似小程序在app.json中配置页面路由一样。所以 uni-app 的路由用法与 Vue Router 不同,如仍希望采用 Vue Router 方式管理路由,可在插件市场搜索 Vue-Router。

路由跳转:
uni-app 有两种页面路由跳转方式:使用navigator组件跳转、调用API跳转。

运行环境判断:
开发环境和生产环境:
uni-app 可通过 process.env.NODE_ENV 判断当前环境是开发环境还是生产环境。一般用于连接测试服务器或生产服务器的动态切换。

  • 在HBuilderX 中,点击“运行”编译出来的代码是开发环境,点击“发行”编译出来的代码是生产环境
  • cli模式下,是通行的编译环境处理方式。
if(process.env.NODE_ENV === 'development'){
    console.log('开发环境')
}else{
    console.log('生产环境')
}

判断平台:
平台判断有2种场景,一种是在编译期判断,一种是在运行期判断。
编译期判断 编译期判断,即条件编译,不同平台在编译出包后已经是不同的代码。

// #ifdef H5
    alert("只有h5平台才有alert方法")
// #endif
  • 如上代码只会编译到H5的发行包里,其他平台的包不会包含如上代码。
  • 运行期判断指代码已经打包,仍然需在运行期判断平台,可使用 uni.getSystemInfoSync().platform 判断客户端环境是 Android、iOS 还是小程序开发工具(在百度小程序开发工具、微信小程序开发工具、支付宝小程序开发工具中使用uni.getSystemInfoSync().platform 返回值均为 devtools)
switch(uni.getSystemInfoSync().platform){
    case 'android':
       console.log('运行Android上')
       break;
    case 'ios':
       console.log('运行iOS上')
       break;
    default:
       console.log('运行在开发者工具上')
       break;
}

如有必要,也可以在条件编译里自己定义一个变量,赋不同值。在后续运行代码中动态判断环境。

页面样式与布局:
尺寸单位:uni-app 支持的通用 css 单位包括 px、rpx
vue页面支持普通H5单位,但在nvue里不支持:

  • rem 默认根字体大小为 屏幕宽度/20(微信小程序、字节跳动小程序、App、H5)
  • vh viewpoint height,视窗高度,1vh等于视窗高度的1%
  • vw viewpoint width,视窗宽度,1vw等于视窗宽度的1%

nvue还不支持百分比单位。

App端,在 pages.json 里的 titleNView 或页面里写的 plus api 中涉及的单位,只支持 px。此时不支持 rpx

nvue中,uni-app 模式(nvue 不同编译模式介绍)可以使用 px 、rpx,表现与 vue 中一致。weex 模式目前遵循weex的单位,它的单位比较特殊:

  • px:,以750宽的屏幕为基准动态计算的长度单位,与 vue 页面中的 rpx 理念相同。(一定要注意 weex 模式的 px,和 vue 里的 px 逻辑不一样。)
  • wx:与设备屏幕宽度无关的长度单位,与 vue 页面中的 px 理念相同

下面对 rpx 详细说明:

  • 微信小程序设计了 rpx 解决这个问题,uni-app 在 App 端、H5 端都支持了 rpx。
  • rpx 是相对于基准宽度的单位,可以根据屏幕宽度进行自适应。uni-app 规定屏幕基准宽度 750rpx
  • 开发者可以通过设计稿基准宽度计算页面元素 rpx 值,设计稿 1px 与框架样式 1rpx 转换公式如下:

设计稿 1px / 设计稿基准宽度 = 框架样式 1rpx / 750rpx

换言之,页面元素宽度在 uni-app 中的宽度计算公式:

750 * 元素在设计稿中的宽度 / 设计稿基准宽度

举例说明:

  1. 若设计稿宽度为 750px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app 里面的宽度应该设为:750 *100 / 750,结果为:100rpx。
  2. 若设计稿宽度为 640px,元素 A 在设计稿上的宽度为 100px,那么元素 A 在 uni-app 里面的宽度应该设为:750 *100 / 640,结果为:117rpx。
  3. 若设计稿宽度为 375px,元素 B 在设计稿上的宽度为 200px,那么元素 B 在 uni-app 里面的宽度应该设为:750 *200 / 375,结果为:400rpx。

跨端兼容

uni-app 已将常用的组件、JS API 封装到框架中,开发者按照 uni-app 规范开发即可保证多平台兼容,大部分业务均可直接满足。

但每个平台有自己的一些特性,因此会存在一些无法跨平台的情况。

  • 大量写 if else,会造成代码执行性能低下和管理混乱。
  • 编译到不同的工程后二次修改,会让后续升级变的很麻烦。

uni-app 提供了条件编译手段,在一个工程里优雅的完成了平台个性化实现。

条件编译:
条件编译是用特殊的注释作为标记,编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。

写法:以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。

  • #ifdef:if defined 仅在某平台存在
  • #ifndef:if not defined 除了某平台均存在
  • %PLATFORM%:平台名称
    在这里插入图片描述

%PLATFORM% 可取值如下:

平台/规范 文档
APP-PLUSApp / H5+规范
APP-PLUS-NVUEApp nvue / wexx+规范
H5H5 / H5+规范
MMP-WEIXIN微信小程序 / 微信小程序+规范
MP-ALIPAY支付宝小程序 / 支付宝小程序+规范
MP-BAIDU百度小程序 / 支付宝小程序+规范
MP-TOUTIAO字节跳动小程序 / 字节跳动小程序+规范
MP-QQQQ小程序 /(目前仅cli版支持)
MP微信小程序/支付宝小程序/百度小程序/字节跳动小程序/QQ小程序

注意: 条件编译是利用注释实现的,在不同语法里注释写法不一样,js使用 // 注释、css 使用 /* 注释 */、vue/nvue 模板里使用 <!–注释 – >

API 的条件编译:
如下代码仅在 App 下出现:

 // #ifdef APP-PLUS
function loginout(){
	var app = getApp()
	ajax({
		url: "/system/loginOut.do",
		success: function(res) {
			app.globalData.user = null
			app.globalData.header.Cookie = ""
			wx.setStorageSync('useruuid', "")
			initSession()
			uni.showToast({
				content: '操作成功'
			})
			uni.switchTab({
			    url: '/pages/tabBar/index/index'
			});
		}
	})
}
  // #endif

如下代码不会在 H5 平台上出现

// #ifndef H5
		defaultAddress: function() {
			if (this.defaultAdd == false) {
				this.defaultAdd = true
			} else {
				this.defaultAdd = false
			}
		},
// #endif

如下代码会在 App 和 H5 平台上出现:

// #ifdef APP-NVUE || H5
		shengChange: function(e) {
			this.shengindex = e.target.value
			this.selectCity(this.shengarray[this.shengindex].id)
		},
// #endif

组件的条件编译:
如下组件仅会在微信小程序中出现:

<!-- #ifdef MP-WEIXIN -->
   <uni-calendar-item></uni-calendar-item>
<!-- #endif -->

样式的条件编译:
样式的条件编译,无论是 css 还是 sass/scss/less/stylus 等预编译语言中,必须使用 /注释/ 的写法。

/* #ifdef APP-PLUS */
.row-wrap{
    line-height: 88rpx;
    padding: 0 40rpx;
    border-bottom: 1rpx solid #eee;
    display: flex;
    display: -webkit-flex;
    font-size: 24rpx;
    position: relative;
}
/* #endif */

pages.json 的条件编译:
下面的页面,只有运行至 App 时才会编译进去

/* #ifdef APP-PLUS */
{
			"path": "pages/integral/integralJiesuan",
			"style": {
				"navigationBarTitleText": "积分结算"
			}
		}
/* #endif */

不同平台下的特有功能,以及小程序平台的分包,都可以通过 pages.json 的条件编译来更好地实现。这样,就不会在其它平台产生多余的资源,进而减小包体积。

json的条件编译,如不同平台的key名称相同,cli项目下开发者自己安装的校验器会报错,需自行关闭这些校验器对json相同key的校验规则。如果使用HBuilderX的校验器,无需在意此问题,HBuilderX的语法校验器为此优化过。

static 目录的条件编译:
在不同平台,引用的静态资源可能也存在差异,通过 static 的的条件编译可以解决此问题,static 目录下新建不同平台的专有目录(目录名称同 %PLATFORM% 值域,但字母均为小写),专有目录下的静态资源只有在特定平台才会编译进去。

如以下目录结构,a.png 只有在微信小程序平台才会编译进去,b.png 在所有平台都会被编译。
┌─static

│ ├─mp-weixin

│ │ └─a.png

│ └─b.png

├─main.js

├─App.vue

├─manifest.json

└─pages.json

整体目录条件编译:
如果想把各平台的页面文件更彻底的分开,也可以在uni-app项目根目录创建platforms目录,然后在下面进一步创建APP-PLUS、MP-WEIXIN等子目录,存放不同平台的文件。

在 HBuilderX 中,ctrl+alt+/ 即可生成正确注释(js:// 注释、css:/* 注释 */、vue/nvue模板: <!-- 注释 – >)。

配置

pages.json
pages.json 文件用来对 uni-app 进行全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等。

condition
启动模式配置,仅开发期间生效,用于模拟直达页面场景,如小程序转发后,用户点击所打开的页面。

属性说明:
在这里插入图片描述
list说明:
在这里插入图片描述
注意: 在 App 里真机运行可直接打开配置的页面,微信开发者工具里需要手动改变编译模式.
在这里插入图片描述

"condition": { //模式配置,仅开发期间生效
        "current": 0, //当前激活的模式(list 的索引项)
        "list": [
                {
                    "name": "index", //模式名称
                    "path" : "pages/index/index",
                    "query": "interval=4000&autoplay=false" //启动参数,在页面的onLoad函数里面得到。
                },
                {
                    "name": "about", //模式名称
                    "path" : "pages/about/about",
                    "query": "" //启动参数,在页面的onLoad函数里面得到。
                }
        ]
    }

manifest.json
manifest.json 文件是应用的配置文件,用于指定应用的名称、图标、权限等。

package.json
通过在package.json文件中增加uni-app扩展节点,可实现自定义条件编译平台(如钉钉小程序、微信服务号等平台)。

vue.config.js
vue.config.js 是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被自动加载,一般用于配置 webpack 等编译选项,具体规范参考:vue.config.js

uni.scss
uni.scss文件的用途是为了方便整体控制应用的风格。比如按钮颜色、边框风格,uni.scss文件里预置了一批scss变量预置。
uni.scss是一个特殊文件,在代码中无需 import 这个文件即可在scss代码中使用这里的样式变量。uni-app的编译器在webpack配置中特殊处理了这个uni.scss,使得每个scss文件都被注入这个uni.scss,达到全局可用的效果。如果开发者想要less、stylus的全局使用,需要在vue.config.js中自行配置webpack策略。

App.vue
App.vue是uni-app的主组件,所有页面都是在App.vue下进行切换的,是页面入口文件。但App.vue本身不是页面,这里不能编写视图元素。

这个文件的作用包括:调用应用生命周期函数、配置全局样式、配置全局的存储globalData

main.js
main.js是uni-app的入口文件,主要作用是初始化vue实例、定义全局组件、使用需要的插件如vuex。

使用代码块直接创建组件模板

为提升开发效率,HBuilderX将 uni-app 常用代码封装成了以 u 开头的代码块,如在 template 标签内输入 ulist 回车,会自动生成如下代码:

<view class="uni-list">
    <view class="uni-list-cell">
        <view class="uni-list-cell-navigate uni-navigate-right" v-for="(item,index) in list" :key="index">
            {{item.value}}
        </view>
    </view>
</view>

代码块分为Tag代码块、JS代码块,如在 script 标签内输入 uShowToast 回车,会自动生成如下代码:

uni.showToast({
    title: '',
    mask: false
    duration: 1500
});

uni-app已支持代码块见下方列表:
Tag代码块:

  • uButton
  • uCheckbox
  • uGrid:宫格,需引用uni ui
  • uList:列表,需引用uni ui
  • uListMedia
  • uRadio
  • uSwiper
    几乎各种组件不管是内置组件还是uni ui的组件,均已封装为代码块,在HBuilderX的vue代码template区域中敲u,代码助手会提示所有可见列表。也可在HBuilderX菜单工具-代码块设置-vue代码块的左侧列表查阅所有。

除组件外,其他常用代码块包括:

  • viewfor:生成一段带有v-for循环结构的视图代码块
  • vbase:生成一段基本的vue代码结构

JS代码块:
uni api代码块:

  • uRequest

  • uGetLocation

  • uShowToast

  • uShowLoading

  • uHideLoading

  • uShowModal

  • uShowActionSheet

  • uNavigateTo

  • uNavigateBack

  • uRedirectTo

  • uStartPullDownRefresh

  • uStopPullDownRefresh

  • uLogin

  • uShare

  • uPay

vue js代码块:

  • vImport:导入文件

  • ed:export default

  • vData:输出 data( ) { return { } }

  • vMethod:输出 methods:{}

  • vComponents:输出 components: {}

其他常用js代码块:

  • iff:简单if

  • forr:for循环结构体

  • fori:for循环结构体并包含i

  • funn:函数

  • funa:匿名函数

  • rt:return true

  • clog:输出:“console.log()”

  • clogvar:增强的日志输出,可同时把变量的名字打印出来

  • varcw:输出:“var currentWebview = this. m p . p a g e . mp.page. mp.page.getAppWebview()”

  • ifios:iOS的平台判断

  • ifAndroid:Android的平台判断
    预置代码块不满足需求的话,可以自定义代码块,教程参考https://ask.dcloud.net.cn/article/35924

混合开发问题总结

uni-app和原生App混合开发问题:
首先务必确认uni-app和原生代码,谁是主谁是从的问题。

  • 如你的应用是uni-app开发,需要扩展一些原生能力,请参考插件市场右上角的原生插件开发教程。
  • 如你的App是原生开发的,部分功能栏目想通过uni-app实现,有2种方式
  • 在原生App里集成uni小程序sdk,参考文档
  • 如不想集成原生sdk,那就把uni-app代码发布成H5方式,在原生App里通过webview打开。
  • 如果应用是uni-app开发为主,只是想离线打包,那么不应该使用uni小程序sdk,请在HBuilderX的发行菜单里点击离线打包。
  • 另注意离线打包无法享受插件市场的付费原生插件,如有相关需求需自己进行原生插件开发,参考插件市场右上角的原生插件开发教程。

uni-app和原生小程序混合开发问题:
uni-app编译后的wxml代码没有可维护性,不便于整合到原生小程序里。如果想复用原生小程序的代码:

方式1:把原生小程序的代码变成组件,uni-app支持使用小程序wxml组件,参考
方式2:把原生小程序转换为uni-app源码,有各种转换工具,详见

使用uniapp遇到的问题会持续更新中---------
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐