若何申请小法式账号,若何斥地本身第一个小法式,若何发布,这一系列hello world把持官方文档都有手把手教授。小法式斥地的每个步伐,供应的能力文档里都有,小我感觉,做小法式斥地,有事没事都看下文档,因为小法式更新对照快速,同时一些藐小的能力我们或者会漏掉,所以多看文档。
1.1 简洁说下目录构造和app.json
文件目录构造很天真
[原创文章:www.11jj.com]
先来看看小法式项目的文件目录构造
文件目录构造
除了app.json必需位于根目录下,其他文件随意,而且都能够删。而且页面文件能够放到若何位置,只要在app.json中的pages中设置了就能够。能够说是很天真。你还能够多个页面放在同个文件夹下(我相信你不会如许做的,何须摧残本身呢)。
接下来简洁介绍下各个文件:
全局设置文件app.json
对于一个小法式项目而言,最主要的文件是app.json,它也是斥地对象识别一个文件夹是否为小法式项目的标识。当使用斥地者对象建立一个项目是,若是选择的是空文件夹,它会建立一个新的项目。若是是一个有文件的文件夹,它会看该文件夹中是否有app.jon文件,若是有,则它会认为是一个小法式项目,则会打开该项目,若是文件夹中没有app.json文件,则提醒无法建立项目。
app.json必需放置于项目的根目录下,它是小法式项目的全局设置文件。在小法式代码包预备完成进行启动后(下文会具体介绍小法式从用户点击打开小法式到小法式销毁的整个过程),会先读取app.json文件,进行小法式的初试化,好比初始化整个小法式外框样式,获取首页页面地址等。
其实小法式就是微信供应的一个容器,各个页面就在这个容器里加载运行销毁
下面介绍下小法式的全局设置选项:
注重:
"pages": [ "pages/index/index", "pages/log/log" ]
在app.json中,pages选项是必需设置的。该设置项..了小法式所有页面的地址,个中每一项都是页面的 路径+文件名 。设置的字符串其实就是每个页面wxml路径,去掉.wxml后缀。因为框架会主动去寻找路径下.json、.js、.wxml、.wxss四个文件进行整合。也就意味着.json、.js、.wxss这三个文件的文件名必需要和.wxml的一致,不然不生效。所以一个页面至少必需得有.wxml文件。
总结:
"window":{ "enablePullDownRefresh": ture, "navigationStyle": "custom" }
该设置项用于设置小法式的全局外观样式,具体请查阅文档。这里重点提一下两个对照实用的
//去掉默认的导航栏,轻松实现周全屏 "navigationStyle": "custom" , //开启自带的下拉刷新,削减本身写样式 "enablePullDownRefresh": ture,
该选项能够让我们轻松实现导航栏tab结果,不外有个不足就是跳转可把持性非常低。就是每个tab只能跳当前小法式页面,分歧跳到其他小法式。若是需要跳到其他小法式,还需本身封装个组件。
这是收集恳求超时时间,能够设置分歧类型恳求的超时时间,好比wx.request、wx.uploadFile等。其实好多时候我们都邑忽略这个选项,小法式默认是60s超时,但我们应该手动设置更低的值,因为我们的接口一样都邑在10s内完成恳求(若是跨越10s,那你是时候优化了),所以若是收集或许办事器出问题了,那么会让用户等60s,最后照样失败,这对用户很不友好,还不如提前敷陈用户,如今出问题了,请稍后再试。
前段时间因为公司办事器网关显现了点小问题,导致有些恳求保持不上,显现大量保持超时。经由之前添加的错误信息收集插件(这个是机能优化,下文有讲到)看到了好多接口返回time-out 60s。让用户等了60s照样失败,这不友好。所以这个超时时间一样设置15s-30s对照好。
是否开启debug功能,开启后查察更多的调试信息,轻易定位问题,斥地阶段能够考虑开启
这个是连系插件使用的,因为微信小法式插件有很大限制,插件里供应的api很有限,wx.login 和 wx.requestPayment 在插件中不克使用,若是需要获取用户信息和进行支出,就必需经由插件供应的功能也实现。当你的小法式下的插件启用了插件功能也时,必需设置该选项为true
小法式插件必需挂载在一个微信小法式中,一个小法式也只能开通一个插件。当你小法式开通的插件启用了插件功能也时,必需设置该选项为true
"plugins": { "myPlugin": { "version": "1.0.0", "provider": "wxidxxxxxxxxxxxxxxxx" } }
当小法式使用了插件就必需在这里声明引入。小法式自身开通的小法式不克在自己应用
"navigateToMiniProgramAppIdList": [ "wxe5f52902cf4de896" ]
之前小法式之间只如果关系了经由公家号就能够互相跳转,现在微信做出了限制,要这个这里设置好需要跳转的小法式,上限为10个,还必需写死,不支撑设置。所以当小法式有跳转到其他小法式,必然要配好这个,不然无法跳转。
"usingComponents": { "hello-component": "plugin://myPlugin/hello-component" }
使用自界说组件或许插件供应的组件前,必需先在这里声明
1.2 小法式启动与生命周期
下面来说说小法式从用户点击打开到销毁的整个过程。用图说话更清楚,特意画了个流程图:
小法式启动会有两种情形,一种是「冷启动」,一种是「热启动」。 假如用户已经打开过某小法式,然后在必然时间内再次打开该小法式,此时无需从新启动,只需将后台态的小法式切换到前台,这个过程就是热启动;冷启动指的是用户首次打开或小法式被微信自动销毁后再次打开的情形,此时小法式需要从新加载启动。
上面的流程图包含了所有内容,但究竟文字有限,接下来具体说下几个点。
if (wx.canIUse('getUpdateManager')) { //检测是否有版本更新 var updateManager = wx.getUpdateManager() updateManager.onCheckForUpdate(function (res) { // 恳求完新版本信息的回调,有更新 if (res.hasUpdate) { wx.showLoading({ title: '检测到新版本', }) } }) updateManager.onUpdateReady(function () { wx.hideLoading(); wx.showModal({ title: '更新提醒', content: '新版本已经预备好,是否重启应用?', success: function (res) { if (res.confirm) { //清楚内陆缓存 try { wx.clearStorageSync() } catch (e) { // Do something when catch error } // 新的版本已经下载好,挪用 applyUpdate 应用新版本并重启 updateManager.applyUpdate() } } }) }) updateManager.onUpdateFailed(function () { // 新的版本下载失败 console.log('新版本下载失败'); }) }
1.3 斥地对象
对于小法式斥地对象,还没有一款闪开发者写意的对象,至少我不写意,哈哈哈!微信供应的微信斥地者对象。除了编译器不成外,其他都还行。但因为斥地对象、ios、android三个..运行小法式的内核分歧。所以有时会显现斥地对象上没问题,真机有问题的情形,稀奇是样式,能够经由在斥地对象中设置上传代码时样式主动补全来解决大多数问题。此外微信斥地者对象供应了真机调试功能,该功能对真机调试非常轻易
还有就是能够自界说编译前提
能够模拟随意场景值、设置页面参数、模拟更新等。根基知足了所有的调试。不外还有一些结果,斥地对象和真机或者会分歧,所以照样需要在真机上确认。
1.4 测试-审核-上线的那些事
办事器域名request正当域名每个月只能点窜5次。所以不该该每次恳求一个新域名就添加一次。在斥地阶段,在微信斥地者对象上勾上不校验正当域名,真机上需要开启调试模式,就能够先不设置正当域名的情形下恳求任何域名甚至ip地址。待斥地完成了,再一次性设置所有正当域名,在微信斥地者对象上作废不校验正当域名,真机上封闭调试模式,然后起头测试。
使用体验版+线上情况的接口,这就是和线上情况一模一般的,所以在发布前,使用体验版+线上情况过一遍。若是没问题,发布今后也就没问题了。
小法式..只要发布了线上版本挪用生成小法式..接谈锋能成功返回..。并且..识别是线上版本,所以还未发布的小法式是无法生成..的。
线上版本有个版本回退功能,这里有个坑,就是版本回退今后,退回的版本需要从新审核才能发布
还有设置体验版时能够设置指定路径和参数,如许很轻易测试
2 重点介绍几个组件
接下来说说使用频率对照多,功能壮大,但又有对照多坑的几个组件
2.1 web-view
web-view的显现,让小法式和H5网页之前的跳转成为了或者。经由把H5页面放置到web-view中,能够让H5页面在小法式内运行。同时在H5页面中也能够跳转回小法式页面。能够说是带来了很大的便当,但同时因为web-view的诸多限制,用起来也不是很舒服。
因为web-view是默认铺满全屏的,也就是web-view宽高和屏幕宽高一般。然后H5页面这是高度100%,这是相对web-view的高度,也是屏幕高度。然则要害问题:web-view里H5页面是从导航栏下起头衬着的。这就导致了H5页面溢出了屏幕,无法达到全屏结果。
解决方式
这个问题我在前段时间的实际项目碰着过,我们要做个H5游戏,要求是全屏,刚起头我也是设置高度100%。后来发现底部一块不见了。我的解决方式对照粗鲁,若是有更好的解决方式,迎接谈论交流。
我的解决方式是:经由拼接宽高参数在H5页面url上,这个宽高是在web-view外层较量好的。H5页面直接读取url上的宽高,动态设置页面的宽高。页面高度的较量,凭据上图,很显然就是屏幕高度减去导航栏高度。宽度都是一般的,直接是屏幕宽度。
但问题又来了,貌似没有途径获取导航栏高度。并且对于分歧机型的手机,导航栏高度分歧。经由了对多个机型导航栏跟屏幕高度的对照。发现了一个纪律,导航栏高度与屏幕高度、屏幕宽高比有必然的关系。所以凭据多个机型就较量出了这个比例。这解决了95%以上手机的适配问题,只有少数机型适配不是很好。到根基实现了全屏结果。具体代码如下:
onLoad (options) { //同步获取屏幕信息,如今用到的是屏幕宽高 var res = wx.getSystemInfoSync(); if (res) { var widHeight = res.screenHeight; //对于大多数手机,屏幕高度/屏幕宽度 = 1.78。此时导航栏占屏幕高度比为0.875 var raito = 0.875; if (res.screenHeight / res.screenWidth > 1.95) { //对于全屏手机,这个占比会更高些 raito = 0.885; } else if (res.screenHeight / res.screenWidth > 1.885) { raito = 0.88; } //做兼容处理,只有微信版本库高于6.7.2,有导航栏才去兼容,不然能够直接使用高度100%。res.statusBarHeight是手机顶部状况栏高度 //若是微信版本号大于6.7.2,有导航栏 if (util.compareVersion(res.version, "6.7.2") > 0) { widHeight = Math.round(widHeight * raito) + (res.statusBarHeight || 0); } this.setDate({ //将H5页面宽高拼接在url上,赋值给web-view的src即可加载出H5页面 webview_src: util.joinParams(h5_src, { "height": widHeight, "width": res.screenWidth }) }) } }
2.2 scroll-view
当我们要实现一个区域内滑动结果时,在H5页面中我们设置overflow-y: scroll即可。但在小法式中,没有该属性。需要用到scroll-view标签。具体把持实现我们能够查察文件scroll-view。
锚点定位在前端斥地中会经常用到,在H5页面中,我们会在url后背加上#来实现锚点定位结果。然则在小法式中如许是不起感化的,因为小法式内衬着页面的轻易不是一个浏览器,无法实时监听Hash值得转变。然则使用scroll-view,我们能够实现锚点点位结果。首要是使用scroll-into-vie属性具体实现我们直接上代码
scroll-into-view | String | 值应为某子元素id(id不克以数字开首)。设置哪个偏向可滚动,则在哪个偏向滚动到该元素
wxml文件
<!--toView的值动态转变,当toView为luckydraw时,会定位到id为luckydraw的view 需要注重的是,这里需要设置高度为屏幕高度--> <scroll-view scroll-y scroll-into-view="{{toView}}" scroll-with-animation = "true" style="height: 100%; white-space:nowrap"> <view id="top"></view> <view id="luckydraw"></view> <view id="secskill"></view> <scroll-view>
2.3 canvas
画布标签,它是原生组件,所以它必需位于屏幕最上边,并且是不克隐藏的。所以若是想要使用canvas动态生成分享照片。那你要设置她的宽高和屏幕一般。要不导出为照片时就会失真。因为这个原因,所以生成分享照片照样有办事端实现吧,照片失真太严重了。
3 formid收集
给用户发送新闻对一个小法式是非常主要的,它能够理睬回用户,导量结果非常显着。我们能够经由模板新闻想小法式用户发送新闻,但前提是我们得获取到openid和formid。用户登录我们即可即可获取到用户openid。而只要用户有点击行为,我们即可获取到formid获取formid。所以说formid是很主要的。我们能够提前收集好formid,在需要的时候给用户推送新闻。我们能够个每个button都包上form标签,只要有效户点击行为都能够收集到formid.
<form bindsubmit="formSubmit" report-submit='true'> <button formType="submit">点击</button> </form>
我们实现一个formid收集系统,为了尽量削减冗余代码和削减对买卖的影响,我们的设计是如许的
wxml文件
<!--在整个页面的最外层包裹form标签,如许就分歧对每个button都包裹一个form标签,代码简练--> <form bindsubmit="formSubmit" report-submit='true'> <view>页面内容</view> <view>页面内容</view> <button formType="submit">点击</button> <view>页面内容</view> <view> <button formType="submit">点击</button> </view> </form>
page.js文件
//每次用户有点击,都将formid添加到全局数组中 formSubmit(e) { //需要实时发送的,不添加 if(e.target.dataset.sendMsg){ formid = e.detail.formId; return; } app.appData.formIdArr.push(e.detail.formId); }
app.js
onHide: function () { //小法式切到后台时上传formid this.submitFormId(); },
4 机能优化相关
从用户打开小法式到小法式销毁,我们能够想想有哪些处所是能够优化的。首先是打开速度。小法式打开速度直接影响了用户留存。在小法式后台,运维中心-监指控警下有个加载机能监控数据,我们能够看到小法式启动总耗时、下载耗时、首次衬着耗等加载相关的数据。而这里的打开速度其实就是小法式的启动总耗时。它包罗了代码包下载、首次衬着,微信内情况初始化等步凑。在这一步,我们能做的就是若何加速代码包下载速度和削减首次衬着时间
在小法式呈现给用户之后,接下来若何提高用户体验,增加小法式坚固性的问题了。每个法式都有bug。只是我们没发现罢了,尽管在测试阶段,我们进行了详尽的测试。然则在实际生产情况,分歧的用户情况,分歧的把持路径,随时会触发一些隐藏的bug。这时若是用户没有向我们申报,我们是无法获知的。所以有需要给我们的小法式增加错误信息收集,js剧本错误,意味着整个法式挂掉了,无法响应用户把持。所以对于运行时的剧本错误,我们应该上报。对显现的bug实时修复,增加法式坚固性,供应用户体验。
每个法式都有大量的前后端数据交互,这是经由http恳求进行的。是以,还有一个错误信息收集就是接口错误信息收集。对那些恳求状况码非2XX、3XX的,或许恳求接口成功了,然则数据不是我们预期的,都能够进行信息采集。
经由对小法式运行时剧本和http恳求进行监控,我们就能够实时认识我们线上小法式的运行状况,有什么问题能够实时发现,实时修复,极高地提高了用户体验性。
4.1 让小法式更快
让小法式快,首要身分有两个,代码包下载和首屏衬着。
我们来看一个数据:
前面状况小法式代码巨细是650Kb摆布,这是下载耗时(固然跟用户收集有关,但这个是悉数用户平均时间)是1.3s摆布。然则经由优化,将代码包降低至200kb摆布时。下载耗时只有0.6s摆布。所以说,代码包削减500kb,下载耗时能削减0.5s。这个数据照样非常显着和。所以说,在不影响买卖逻辑的情形下,我们小法式代码包应该尽或者地小。那么若何降低代码包巨细呢?以下有几点能够参考
接下来是首屏衬着,从上图的小法式生命周期能够看出,从加载首页代码带首页完成衬着,这段时间就是白屏时间,也就是首次衬着时间。而小法式在这段时间内,首要工作是:加载首页代码、建立View和AppService层、初试数据传输、页面衬着。在这四个步伐中,加载首页代码,前面已经说过;建立View和AppService层,是微信完成的,跟用户手机有关,这不是我们可控的。我们能做的就是削减初试数据传输时间和页面衬着时间。
Page({ //与页面衬着有关的数据放这里 data: { goods_list:[] }, //与页面衬着无关的数据放这里 _data: { timer: null } })
4.2 让小法式更强
接下来就是给小法式增加错误信息收集,包罗js剧本错误信息收集和http恳求错误信息收集。前段时间,在时间工作斥地中,为了更好的复用和治理,我把这个错误信息收集功能做成了插件。然而做成插件并没有想象中的那么美妙,下面再具说。
剧本错误收集
对于剧本错误收集,这个相对对照简洁,因为在app.js中供应了监听错误的onError函数
只不外错误信息是包罗客栈等对照具体的错误信息,然后当上传时我们并不需要这么信息,第一虚耗宽带,第二看着累又无用。我们需要的信息是:错误类型、错误信息描述、错误位置。
thirdScriptError aa is not defined;at pages/index/index page test function ReferenceError: aa is not defined at e.test (http://127.0.0.1:62641/appservice/pages/index/index.js:17:3) at e.<anonymous> (http://127.0.0.1:62641/appservice/__dev__/WAService.js:16:31500) at e.a (http://127.0.0.1:62641/appservice/__dev__/WAService.js:16:26386) at J (http://127.0.0.1:62641/appservice/__dev__/WAService.js:16:20800) at Function.<anonymous> (http://127.0.0.1:62641/appservice/__dev__/WAService.js:16:22389) at http://127.0.0.1:62641/appservice/__dev__/WAService.js:16:27889 at http://127.0.0.1:62641/appservice/__dev__/WAService.js:6:16777 at e.(anonymous function) (http://127.0.0.1:62641/appservice/__dev__/WAService.js:4:3403) at e (http://127.0.0.1:62641/appservice/appservice?t=1543326089806:1080:20291) at r.registerCallback.t (http://127.0.0.1:62641/appservice/appservice?t=1543326089806:1080:20476)
这是错误信息字符串,接下来我们对它进行截取只需要拿我们想要的信息即可。我们发现这个字符串是有划定的。第一行是错误类型,第二行是错误详情和发生的位置,而且是";"分好分隔。所以我们照样很轻易就能够拿到我们想要的信息。
//花样化错误信息 function formateErroMsg(errorMsg){ //包一层try catch 不要让信息收集影响了买卖 try{ var detailMsg = ''; var detailPosition= ''; var arr = errorMsg.split('\n') if (arr.length > 1) { //错误详情和错误位置在第二行并用分好离隔 var detailArr = arr[1].split(';') detailMsg = detailArr.length > 0 ? detailArr[0] : ''; if (detailArr.length > 1) { detailArr.shift() detailPosition = detailArr.join(';') } } var obj = { //错误类型就是第一行 error_type: arr.length > 0 ? arr[0] : '', error_msg: detailMsg, error_position: detailPosition }; return obj }catch(e){} }
获取到我们想要的信息,就能够发送到我们办事后台,进行数据整顿和显露,这个需要办事端合营,就不深入讲了,我们拿到了数据,其他都不是事。
http恳求错误信息收集
对于http恳求错误信息收集体式,我们尽量不要暴力埋点,每个恳求发送前发送后加上我们的埋点。如许工作量太大,也不易维护。是以,我们能够从底层出发,阻挡wx.request恳求。使用Object.definePropert对wx对象的request进行从新界说。具体实现如下
function rewriteRequest(){ try { const originRequest = wx.request; Object.defineProperty(wx, 'request', { configurable:true, enumerable: true, writable: true, value: function(){ let options = arguments[0] || {}; //对于发送错误信息的接口不收集,防止死轮回 var regexp = new RegExp("https://xxxx/error","g"); if (regexp.test(options.url)) { //这里要执行本来的方式 return originRequest.call(this, options) } //这里阻挡恳求成功或失败接口,拿到恳求后的数据 ["success", "fail"].forEach((methodName) => { let defineMethod = options[methodName]; options[methodName] = function(){ try{ //在从新界说函数中执行原先的函数,不影响正常逻辑 defineMethod && defineMethod.apply(this, arguments); //起头信息收集 let statusCode, result, msg; //恳求失败 if (methodName == 'fail') { statusCode = 0; result = 'fail'; msg = ( arguments[0] && arguments[0].errMsg ) || "" } //恳求成功, //收集划定为: // 1、 statusCode非2xx,3xx // 2、 statusCode是2xx,3xx,但接口返回result不为ok if (methodName == 'success') { let data = arguments[0] || {}; statusCode = data.statusCode || ""; if (data.statusCode && Number(data.statusCode) >= 200 && Number(data.statusCode) < 400 ) { let resData = data.data ? (typeof data.data == 'object' ? data.data : JSON.parse(data.data)) : {}; //恳求成功,不收集 if (resData.result == 'ok') { return; } result = resData.result || ""; msg = resData.msg || ""; }else{ result = ""; msg = data.data || ""; } } //过滤掉header中的敏感信息 if (options.header) { options.header.userid && (delete options.header.userid) } //过滤掉data中的敏感信息 if (options.data) { options.data.userid && (delete options.data.userid) } var collectInfo = { "url": options.url || '', //恳求地址 "method": options.method || "GET", //恳求方式 "request_header": JSON.stringify(options.header || {}), //恳求头部信息 "request_data": JSON.stringify(options.data || {}), //恳求参数 "resp_code": statusCode + '', //恳求状况码 "resp_result": result, //恳求返回究竟 "resp_msg": msg, //恳求返回描述信息 } //提交参数与上一次分歧,或许参数沟通,隔了1s if (JSON.stringify(collectInfo) != lastParams.paramStr || (new Date().getTime() - lastParams.timestamp > 1000)) { //上传错误信息 Post.post_error(_miniapp, 'http', collectInfo) lastParams.paramStr = JSON.stringify(collectInfo); lastParams.timestamp = new Date().getTime() } }catch(e){ //console.log(e); } }; }) return originRequest.call(this, options) } }) } catch (e) { // Do something when catch error } }
在不使用插件的小法式中,我们能够在使用wx.request方式执行上面的代码,对wx.request进行阻挡,然后其他无需加任何代码就能够收集http恳求了。
上面说了,当我们封装成到插件时,这个就不管用了,因为当使用插件时,小法式不许可我们点窜全局变量。所以执行上面代码时会报错。这时,我们退而求其次,只能是在插件中本身封装个方式,这个方式其实就是wx.request发送恳求,然则在插件中我们就有能够阻挡wx.request了。具体实现如下:
function my_request(){ //只要执行一次阻挡代码即可 !_isInit && rewriteRequest(); return wx.request(options) }
接下来我们看下后台数据
持续监控,会帮我们找出好多隐藏的bug
4 总结
洋洋洒洒写了这么多,或许有些处所说的不太清楚,慢慢磨炼吧。然后后背几点只是挑了主要的讲,我相信有过小法式斥地经验的同伙应该没问题。然后有时间再增补和优化了。先到此,有缘看到的同伙,迎接留言交流。
根基微信小法式斥地自己也不是一件简洁的事情,如今说的也不敷具体,想要斥地出一款成功的小法式照样要支付好多的精神,当然也能够选择小法式斥地公司,如许是会省去好多麻烦。这里安利一个钰威软件,一家拥有成熟不乱手艺的团队,今朝已经和好多企业杀青合作,斥地了好多的APP、小法式的产物,无论是淘宝电商、餐饮娱乐、本土企业都能够找到适合本身的软件APP,打造一款适合本身的线上手机端,真正实现赶上智能贸易措施的途径。
大家好,小娟今天来为大家解答victory纯音乐百度云以下问题,victory纯音乐百度网盘下载很多人还不知道,现在让我们一起来看看吧!1、歌曲名:V
山东天色瞻望估计4月8—9日山东大部区域天色晴间多云北部沿海区域天色阴局部有雷雨或阵雨10日全省天色多云转阴鲁南和半岛区域局部有细雨济南天
皮肤究竟有多灾?除了受粉刺、痘痘、斑的侵扰,有时还会冒出一些米粒巨细的“肉疙瘩”,也就是「疣」。其实,疣的风险水平严重与否,首要看
大家好,小伟今天来为大家解答冬季金鱼如何饲养以下问题,冬季金鱼如何饲养很多人还不知道,现在让我们一起来看看吧!1、对于条件较好的养鱼
大家好,小乐今天来为大家解答qy152以下问题,qy152很多人还不知道,现在让我们一起来看看吧!1、我也是找了好久了,然后找不到,这个网站怎么这
点击蓝字,存眷我们1.问:2024年上半年中小学教师资格测验(面试)报名时间若何放置?答:(1)网上报名时间:2024年4月12日10:00至15日17:00。(2)
大家好,小美今天来为大家解答亲情号码怎么查看号码以下问题,如何查亲情号全号很多人还不知道,现在让我们一起来看看吧!1、官方网站查询:
大家好,小伟今天来为大家解答复印件和扫描件的区别以下问题,复印件和扫描件统称为复印件吗很多人还不知道,现在让我们一起来看看吧!1、一
Copyright 2024.依依自媒体,让大家了解更多图文资讯!