当前位置: 首页 > news >正文

Vue3数据交互axios

我是南城余!阿里云开发者平台专家博士证书获得者!

欢迎关注我的博客!一同成长!

一名从事运维开发的worker,记录分享学习。

专注于AI,运维开发,windows Linux 系统领域的分享!

本章节对应知识库
南城余的知识库


在这里插入图片描述

九、Vue3数据交互axios

9.0 预讲知识-promise

9.0.1 普通函数和回调函数

普通函数: 正常调用的函数,一般函数执行完毕后才会继续执行下一行代码

<script>let fun1 = () =>{console.log("fun1 invoked")}// 调用函数fun1()// 函数执行完毕,继续执行后续代码console.log("other code processon")
</script>

回调函数: 一些特殊的函数,表示未来才会执行的一些功能,后续代码不会等待该函数执行完毕就开始执行了

<script>// 设置一个2000毫秒后会执行一次的定时任务setTimeout(function (){console.log("setTimeout invoked")},2000)console.log("other code processon")
</script>
9.0.2 Promise 简介

前端中的异步编程技术,类似Java中的多线程+线程结果回调!

  • Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

  • 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点。

(1)Promise对象代表一个异步操作,有三种状态:`Pending`(进行中)、`Resolved`(已完成,又称 Fulfilled)和`Rejected`(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是`Promise`这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从`Pending`变为`Resolved`和从`Pending`变为`Rejected`。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
9.0.3 Promise 基本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例。

    <script>/*  1.实例化promise对象,并且执行(类似Java创建线程对象,并且start)参数: resolve,reject随意命名,但是一般这么叫!参数: resolve,reject分别处理成功和失败的两个函数! 成功resolve(结果)  失败reject(结果)参数: 在function中调用这里两个方法,那么promise会处于两个不同的状态状态: promise有三个状态pending   正在运行resolved  内部调用了resolve方法rejected  内部调用了reject方法参数: 在第二步回调函数中就可以获取对应的结果 */let promise =new Promise(function(resolve,reject){console.log("promise do some code ... ...")//resolve("promise success")reject("promise fail")})console.log('other code1111 invoked')//2.获取回调函数结果  then在这里会等待promise中的运行结果,但是不会阻塞代码继续运行promise.then(function(value){console.log(`promise中执行了resolve:${value}`)},function(error){console.log(`promise中执行了reject:${error}`)})// 3 其他代码执行   console.log('other code2222 invoked')</script>
9.0.4 Promise catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

<script>let promise =new Promise(function(resolve,reject){console.log("promise do some code ... ...")// 故意响应一个异常对象throw new Error("error message")})console.log('other code1111 invoked')/* then中的reject()的对应方法可以在产生异常时执行,接收到的就是异常中的提示信息then中可以只留一个resolve()的对应方法,reject()方法可以用后续的catch替换then中的reject对应的回调函数被后续的catch替换后,catch中接收的数据是一个异常对象*/promise.then(function(resolveValue){console.log(`promise中执行了resolve:${resolveValue}`)}//,//function(rejectValue){console.log(`promise中执行了reject:${rejectValue}`)}).catch(function(error){console.log(error)} )console.log('other code2222 invoked')
</script>
9.0.5 async和await的使用

async和await是ES6中用于处理异步操作的新特性。通常,异步操作会涉及到Promise对象,而async/await则是在Promise基础上提供了更加直观和易于使用的语法。

async 用于标识函数的

  1. async标识函数后,async函数的返回值会变成一个promise对象

  2. 如果函数内部返回的数据是一个非promise对象,async函数的结果会返回一个成功状态 promise对象

  3. 如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由该对象决定

  4. 如果函数内部抛出的是一个异常,则async函数返回的是一个失败的promise对象

<script>/* async 用于标识函数的1. async标识函数后,async函数的返回值会变成一个promise对象2. 如果函数内部返回的数据是一个非promise对象,async函数的结果会返回一个成功状态 promise对象3. 如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由该对象决定4. 如果函数内部抛出的是一个异常,则async函数返回的是一个失败的promise对象*/async function fun1(){//return 10//throw new Error("something wrong")let promise = Promise.reject("heihei")return promise}let promise =fun1()promise.then(function(value){console.log("success:"+value)}).catch(function(value){console.log("fail:"+value)})
</script>

await

  1. await右侧的表达式一般为一个promise对象,但是也可以是一个其他值
  2. 如果表达式是promise对象,await返回的是promise成功的值
  3. await会等右边的promise对象执行结束,然后再获取结果,后续代码也会等待await的执行
  4. 如果表达式是其他值,则直接返回该值
  5. await必须在async函数中,但是async函数中可以没有await
  6. 如果await右边的promise失败了,就会抛出异常,需要通过 try … catch捕获处理
<script>/* 1. await右侧的表达式一般为一个promise对象,但是也可以是一个其他值2. 如果表达式是promise对象,await返回的是promise成功的值3. await会等右边的promise对象执行结束,然后再获取结果,后续代码也会等待await的执行4. 如果表达式是其他值,则直接返回该值5. await必须在async函数中,但是async函数中可以没有await6. 如果await右边的promise失败了,就会抛出异常,可以通过 try ... catch捕获处理*/async function fun1(){return 10}async function fun2(){try{let res = await fun1()//let res = await Promise.reject("something wrong")}catch(e){console.log("catch got:"+e)   }console.log("await got:"+res)}fun2()
</script>

9.1 Axios介绍

ajax

  • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

  • AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

  • AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

  • AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。

  • XMLHttpRequest 只是实现 Ajax 的一种方式。

ajax工作原理:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原生javascript方式进行ajax(了解):

<script>function loadXMLDoc(){var xmlhttp;if (window.XMLHttpRequest){//  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码xmlhttp=new XMLHttpRequest();}else{// IE6, IE5 浏览器执行代码xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}// 设置回调函数处理响应结果xmlhttp.onreadystatechange=function(){if (xmlhttp.readyState==4 && xmlhttp.status==200){document.getElementById("myDiv").innerHTML=xmlhttp.responseText;}}// 设置请求方式和请求的资源路径xmlhttp.open("GET","/try/ajax/ajax_info.txt",true);// 发送请求xmlhttp.send();}
</script>   

什么是axios 官网介绍:https://axios-http.com/zh/docs/intro

  • Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。它有如下特性
    • 从浏览器创建 XMLHttpRequests
    • 从 node.js 创建 http 请求
    • 支持 Promise API
    • 拦截请求和响应
    • 转换请求和响应数据
    • 取消请求
    • 自动转换JSON数据
    • 客户端支持防御XSRF

9.2 Axios 入门案例

1 案例需求:请求后台获取随机土味情话

  • 请求的url
https://api.uomg.com/api/rand.qinghua?format=json    或者使用  http://forum.atguigu.cn/api/rand.qinghua?format=json
  • 请求的方式
GET/POST
  • 数据返回的格式
{"code":1,"content":"我努力不是为了你而是因为你。"}

2 准备项目

npm create vite
npm install 
/*npm install vue-router@4 --save
npm install pinia */

3 安装axios

npm install axios

4 设计页面(App.Vue)

<script setup type="module">import axios from 'axios'import { onMounted,reactive } from 'vue';let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})let getLoveMessage =()=>{axios({method:"post", // 请求方式url:"https://api.uomg.com/api/rand.qinghua?format=json",  // 请求的urldata:{ // 当请求方式为post时,data下的数据以JSON串放入请求体,否则以key=value形式放url后username:"123456"}}).then( function (response){//响应成功时要执行的函数console.log(response)Object.assign(jsonData,response.data)}).catch(function (error){// 响应失败时要执行的函数console.log(error)})}/* 通过onMounted生命周期,自动加载一次 */onMounted(()=>{getLoveMessage()})
</script><template><div><h1>今日土味情话:{{jsonData.content}}</h1><button  @click="getLoveMessage">获取今日土味情话</button></div>
</template><style scoped>
</style>

5 启动测试

npm run dev

异步响应的数据结构

  • 响应的数据是经过包装返回的!一个请求的响应包含以下信息。
{// `data` 由服务器提供的响应data: {},// `status` 来自服务器响应的 HTTP 状态码status: 200,// `statusText` 来自服务器响应的 HTTP 状态信息statusText: 'OK',// `headers` 是服务器响应头// 所有的 header 名称都是小写,而且可以使用方括号语法访问// 例如: `response.headers['content-type']`headers: {},// `config` 是 `axios` 请求的配置信息config: {},// `request` 是生成此响应的请求// 在node.js中它是最后一个ClientRequest实例 (in redirects),// 在浏览器中则是 XMLHttpRequest 实例request: {}
}
  • then取值
then(function (response) {console.log(response.data);console.log(response.status);console.log(response.statusText);console.log(response.headers);console.log(response.config);
});

6 通过async和await处理异步请求

<script setup type="module">import axios from 'axios'import { onMounted,reactive } from 'vue';let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})let getLoveWords = async ()=>{return await axios({method:"post",url:"https://api.uomg.com/api/rand.qinghua?format=json",data:{username:"123456"}})}let getLoveMessage =()=>{let {data}  = await getLoveWords()Object.assign(message,data)}/* 通过onMounted生命周期,自动加载一次 */onMounted(()=>{getLoveMessage()})
</script><template><div><h1>今日土味情话:{{jsonData.content}}</h1><button  @click="getLoveMessage">获取今日土味情话</button></div>
</template><style scoped>
</style>

axios在发送异步请求时的可选配置:

{// `url` 是用于请求的服务器 URLurl: '/user',// `method` 是创建请求时使用的方法method: 'get', // 默认值// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URLbaseURL: 'https://some-domain.com/api/',// `transformRequest` 允许在向服务器发送前,修改请求数据// 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream// 你可以修改请求头。transformRequest: [function (data, headers) {// 对发送的 data 进行任意转换处理return data;}],// `transformResponse` 在传递给 then/catch 前,允许修改响应数据transformResponse: [function (data) {// 对接收的 data 进行任意转换处理return data;}],// 自定义请求头headers: {'X-Requested-With': 'XMLHttpRequest'},// `params` 是与请求一起发送的 URL 参数// 必须是一个简单对象或 URLSearchParams 对象params: {ID: 12345},// `paramsSerializer`是可选方法,主要用于序列化`params`// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)paramsSerializer: function (params) {return Qs.stringify(params, {arrayFormat: 'brackets'})},// `data` 是作为请求体被发送的数据// 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法// 在没有设置 `transformRequest` 时,则必须是以下类型之一:// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams// - 浏览器专属: FormData, File, Blob// - Node 专属: Stream, Bufferdata: {firstName: 'Fred'},// 发送请求体数据的可选语法// 请求方式 post// 只有 value 会被发送,key 则不会data: 'Country=Brasil&City=Belo Horizonte',// `timeout` 指定请求超时的毫秒数。// 如果请求时间超过 `timeout` 的值,则请求会被中断timeout: 1000, // 默认值是 `0` (永不超时)// `withCredentials` 表示跨域请求时是否需要使用凭证withCredentials: false, // default// `adapter` 允许自定义处理请求,这使测试更加容易。// 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。adapter: function (config) {/* ... */},// `auth` HTTP Basic Authauth: {username: 'janedoe',password: 's00pers3cret'},// `responseType` 表示浏览器将要响应的数据类型// 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'// 浏览器专属:'blob'responseType: 'json', // 默认值// `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)// 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求// Note: Ignored for `responseType` of 'stream' or client-side requestsresponseEncoding: 'utf8', // 默认值// `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称xsrfCookieName: 'XSRF-TOKEN', // 默认值// `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值// `onUploadProgress` 允许为上传处理进度事件// 浏览器专属onUploadProgress: function (progressEvent) {// 处理原生进度事件},// `onDownloadProgress` 允许为下载处理进度事件// 浏览器专属onDownloadProgress: function (progressEvent) {// 处理原生进度事件},// `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数maxContentLength: 2000,// `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数maxBodyLength: 2000,// `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。// 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),// 则promise 将会 resolved,否则是 rejected。validateStatus: function (status) {return status >= 200 && status < 300; // 默认值},// `maxRedirects` 定义了在node.js中要遵循的最大重定向数。// 如果设置为0,则不会进行重定向maxRedirects: 5, // 默认值// `socketPath` 定义了在node.js中使用的UNIX套接字。// e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。// 只能指定 `socketPath` 或 `proxy` 。// 若都指定,这使用 `socketPath` 。socketPath: null, // default// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http// and https requests, respectively, in node.js. This allows options to be added like// `keepAlive` that are not enabled by default.httpAgent: new http.Agent({ keepAlive: true }),httpsAgent: new https.Agent({ keepAlive: true }),// `proxy` 定义了代理服务器的主机名,端口和协议。// 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。// 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。// `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。// 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。// 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`proxy: {protocol: 'https',host: '127.0.0.1',port: 9000,auth: {username: 'mikeymike',password: 'rapunz3l'}},// see https://axios-http.com/zh/docs/cancellationcancelToken: new CancelToken(function (cancel) {}),// `decompress` indicates whether or not the response body should be decompressed // automatically. If set to `true` will also remove the 'content-encoding' header // from the responses objects of all decompressed responses// - Node only (XHR cannot turn off decompression)decompress: true // 默认值
}

9.3 Axios get和post方法

配置添加语法

axios.get(url[, config])axios.get(url,{上面指定配置key:配置值,上面指定配置key:配置值
})axios.post(url[, data[, config]])axios.post(url,{key:value //此位置数据,没有空对象即可{}},{上面指定配置key:配置值,上面指定配置key:配置值
})

测试get参数

<script setup type="module">import axios from 'axios'import { onMounted,ref,reactive,toRaw } from 'vue';let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})let getLoveWords= async ()=>{try{return await axios.get('https://api.uomg.com/api/rand.qinghua',{params:{// 向url后添加的键值对参数format:'json',username:'zhangsan',password:'123456'},headers:{// 设置请求头'Accept' : 'application/json, text/plain, text/html,*/*'}})}catch (e){return await e}}let getLoveMessage =()=>{let {data}  = await getLoveWords()Object.assign(message,data)}/* 通过onMounted生命周期,自动加载一次 */onMounted(()=>{getLoveMessage()})
</script><template><div><h1>今日土味情话:{{jsonData.content}}</h1><button  @click="getLoveMessage">获取今日土味情话</button></div>
</template><style scoped>
</style>

测试post参数

<script setup type="module">import axios from 'axios'import { onMounted,ref,reactive,toRaw } from 'vue';let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})let getLoveWords= async ()=>{try{return await axios.post('https://api.uomg.com/api/rand.qinghua',{//请求体中的JSON数据username:'zhangsan',password:'123456'},{// 其他参数params:{// url上拼接的键值对参数format:'json',},headers:{// 请求头'Accept' : 'application/json, text/plain, text/html,*/*','X-Requested-With': 'XMLHttpRequest'}})}catch (e){return await e}}let getLoveMessage =()=>{let {data}  = await getLoveWords()Object.assign(message,data)}/* 通过onMounted生命周期,自动加载一次 */onMounted(()=>{getLoveMessage()})
</script><template><div><h1>今日土味情话:{{jsonData.content}}</h1><button  @click="getLoveMessage">获取今日土味情话</button></div>
</template><style scoped>
</style>

9.4 Axios 拦截器

如果想在axios发送请求之前,或者是数据响应回来在执行then方法之前做一些额外的工作,可以通过拦截器完成

// 添加请求拦截器 请求发送之前
axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);}
);// 添加响应拦截器 数据响应回来
axios.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么return response;}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error);}
);
  • 定义src/axios.js提取拦截器和配置语法
import axios from 'axios'//  创建instance实例
const instance = axios.create({baseURL:'https://api.uomg.com',timeout:10000
})//  添加请求拦截
instance.interceptors.request.use(// 设置请求头配置信息config=>{//处理指定的请求头console.log("before request")config.headers.Accept = 'application/json, text/plain, text/html,*/*'return config},// 设置请求错误处理函数error=>{console.log("request error")return Promise.reject(error)}
)
// 添加响应拦截器
instance.interceptors.response.use(// 设置响应正确时的处理函数response=>{console.log("after success response")console.log(response)return response},// 设置响应异常时的处理函数error=>{console.log("after fail response")console.log(error)return Promise.reject(error)}
)
// 默认导出
export default instance
  • App.vue
<script setup type="module">// 导入我们自己定义的axios.js文件,而不是导入axios依赖  import axios from './axios.js'import { onMounted,ref,reactive,toRaw } from 'vue';let jsonData =reactive({code:1,content:'我努力不是为了你而是因为你'})let getLoveWords= async ()=>{try{return await axios.post('api/rand.qinghua',{username:'zhangsan',password:'123456'},//请求体中的JSON数据{params:{format:'json',}}// 其他键值对参数)}catch (e){return await e}}let getLoveMessage =()=>{// 这里返回的是一个fullfilled状态的promisegetLoveWords().then((response) =>{console.log("after getloveWords")console.log(response)Object.assign(jsonData,response.data)})}/* 通过onMounted生命周期,自动加载一次 */onMounted(()=>{getLoveMessage()})
</script><template><div><h1>今日土味情话:{{jsonData.content}}</h1><button  @click="getLoveMessage">获取今日土味情话</button></div></template><style scoped>
</style>

相关文章:

  • 润和软件HopeStage与亚信安全云主机深度安全防护系统完成产品兼容性互认证
  • Flink 客户端操作命令及可视化工具
  • 6. 行为模式 - 观察者模式
  • springboot集成websocket全全全!!!
  • 面向船舶结构健康监测的数据采集与处理系统(一)系统架构
  • 深度学习(Deep Learning) 简介
  • 【C++】STL 容器 - stack 堆栈容器 ① ( stack 堆栈容器特点 | stack 堆栈容器与 deque 双端数组容器对比 | 简单示例 )
  • 【C->Cpp】深度解析#由C迈向Cpp(2)
  • 装箱和拆箱(js的问题)
  • 在 Laravel 中,清空缓存大全
  • 神经网络:深度学习优化方法
  • 制造行业定制软件解决方案——工业信息采集平台
  • Kafka怎么保证消息发送不丢失
  • 那些年的随笔
  • 第一次记录QPSK,BSPK,MPSK,QAM—MATLAB实现
  • ----------
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【Amaple教程】5. 插件
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • Angular6错误 Service: No provider for Renderer2
  • C++11: atomic 头文件
  • Docker容器管理
  • eclipse(luna)创建web工程
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Laravel 菜鸟晋级之路
  • Material Design
  • Swoft 源码剖析 - 代码自动更新机制
  • 包装类对象
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 二维平面内的碰撞检测【一】
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 目录与文件属性:编写ls
  • 深入浅出Node.js
  • 详解移动APP与web APP的区别
  • 应用生命周期终极 DevOps 工具包
  • 最近的计划
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (2)MFC+openGL单文档框架glFrame
  • (二)构建dubbo分布式平台-平台功能导图
  • (二)换源+apt-get基础配置+搜狗拼音
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (四)模仿学习-完成后台管理页面查询
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)项目管理杂谈-我所期望的新人
  • (转)一些感悟
  • (转载)Linux 多线程条件变量同步
  • (轉)JSON.stringify 语法实例讲解
  • **PHP分步表单提交思路(分页表单提交)
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • *上位机的定义
  • .bat批处理(七):PC端从手机内复制文件到本地