热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Vue中UI组件库之Vuex与虚拟服务器初识

这篇文章主要介绍了Vue中UI组件库之Vuex与虚拟服务器初识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

一、日历组件

new Date()的月份是从0开始的。

下面表达式是:2018年6月1日
new Date(2018, 5, 1);

下面表达式是:2018年5月1日
new Date(2018, 4, 1);
或
new Date(2018, 5-1, 1);

下面表达式是:2018年5月31日(得到上个月的最后一天)
new Date(2018, 5 , 0);
日的参数可以是0,也可以是负数,表示上个月底的那一天。

下面表达式是:2018年7月01日
new Date(2018, 5, 31);

lApp.vue父组件:


lMonthView.vue子组件


l显示在页面:


l显示农历,安装插件:

npm install solarlunar --save


下面做“换月换年”业务:

App.vue父组件


MonthChooser.vue切换年月组件


切换年代视图组件:

lApp.vue父组件


lDecadeView.vue子组件



【以下开始完善整个项目】:

切换视图:App.vue父组件


DecadeChooser.vue年视图按钮组件:


MonthChooser.vue月视图按钮组件:


DecadeView.vue年份视图组件:


MonthView.vue月视图早已完善。

二、UI组件库

饿了么UI:http://element-cn.eleme.io/

iviewUI :https://www.iviewui.com/

2.1饿了么UI

以饿了么UI为例

安装依赖:

npm install --save element-ui

在main.js中配置eleUI组件:

在引入 Element 时,可以传入一个全局配置对象。该对象目前仅支持 size 字段,用于改变组件的默认尺寸。按照引入 Element 的方式,具体操作如下:

import Vue from "vue";
import App from "./App.vue";
import ElementUI from 'element-ui';
//import 'element-ui/lib/theme-chalk/index.css'; //样式在index.html页面引入
// Vue.use(ElementUI);
Vue.use(ElementUI, { size: 'small' });

new Vue({
 el : "#app" ,
 render: (h) => h(App)
});

然后就可以在.vue组件中直接使用了。

2.2 iviewui

npm install iview --save

三、传统数据管理的问题

以下是一个表示“单向数据流”理念的极简示意:

当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

多个视图依赖于同一状态。

来自不同视图的行为需要变更同一状态。

对于问题1:传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。

对于问题2:我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。

这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux、和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

之前我们做的日历组件将数据放到了App.vue最大父组件上,并且还有:year、:month、:setYear、:setMonth等一系列的参数传递操作。

但是vuex就是将数据放到了全局store中。

不用vuex也能做项目,只不过数据管理起来很不方便。

四、Vuex4.1 Vuex配置

官网:https://vuex.vuejs.org/zh-cn/

安装vuex:

npm install --save vuex

什么是vuex?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

简单说:vuex就是一个状态管理容器,说白了就是将数据单独存放出去。

4.2 state(全局仓库)

什么是“状态管理模式”?

每个Vuex应用的核心就是store(仓库)。store就是一个容器,它包含着你项目中大部分的状态 (state)。

Vuex 和单纯的全局对象有以下两点不同:

Vuex的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新变化。

你不能直接改变store中的状态。改变store中的状态的唯一途径就是通过commit提交mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

在main.js创建一个全局仓库(store),让我们从一个简单的Vue计数器开始:

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
Vue.use(Vuex); //将vuex安装到vue中

//创建一个仓库,并且存放一些全局数据(存放四大天王选项)
const store = new Vuex.Store({
 state : {
 a : 100
 }
})

new Vue({
 el : "#app",
 store, //将store注入到全局中
 render : (h)=> h(App)
})

Vuex通过store选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需要调用Vue.use(Vuex))

通过在根实例中注册store选项,该store实例会注入到根组件下的所有子组件中,并且组件能通过this.$store访问。

在App.vue中的生命周期中输出this,能看到vue实例化对象的全局有$store这个对象


之前做的日历组件将数据放到了App.vue最大父组件上,并且还有:year、:month、:setYear、:setMonth等一系列的参数传递操作。

但是Vuex就是将数据放到了全局store中,注意:

不管项目有多大,store只有一个

只要配置正确,组件内部可以使用$store即可访问store的全局数据

改变 store 中的状态(数据)的唯一途径就是通过commit()函数提交 mutation。

以下的描述来自于官方:https://vuex.vuejs.org/zh/guide/

再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.a,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。

由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。

4.3 mutations

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex中的mutation非常类似于事件:每个 mutation都有一个字符串的 事件类型(type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

你不能直接调用一个mutation handler。这个选项更像是事件注册:当触发一个type类型为JIA的 mutation时,调用此函数。要唤醒一个mutation函数,你需要以相应的type调用store.commit()方法:

main.js

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
Vue.use(Vuex); //将vuex安装到vue中

//创建一个仓库,并且存放一些全局数据(存放四大选项)
const store = new Vuex.Store({
 state : {
 a : 100
 },
 mutations : {
 // 这里的函数只能通过commit()触发,其他操作是无法影响的
 JIA(state){
  state.a++
 },
 MINUS(state){
  state.a--
 }
 }
})

App.vue


>[/code]

Vuex自带一个日志插件(vue-logger)用于调试:

import createLogger from 'vuex/dist/logger'

const store = new Vuex.Store({
 plugins: [createLogger()]
})

总结:

只有mutations中可以改变state,其他任何方式都不能改state的值

组件想要改变store中的state,只能通过commit()发出一条命令。

提交载荷(Payload)

你可以向 store.commit() 传入额外的参数,即 mutation 的 载荷(payload),在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:

main.js

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
import createLogger from 'vuex/dist/logger';

Vue.use(Vuex); //将vuex安装到vue中

//创建一个仓库,并且存放一些全局数据(存放四大选项)
const store = new Vuex.Store({
 state : {
 a : 100
 },
 mutations : {
 // 这里的函数只能通过commit()触发,其他操作是无法影响的
 JIA(state,payload){
  state.a += payload.n
 },
 MINUS(state, payload){
  state.a -= payload.n
 }
 },
 plugins: [createLogger()]
})

App.vue


记住一条重要的原则:mutations必须是同步函数

4.4 actions

上面说过mutation 中不能写异步语句,为了处理异步操作,我们来看一看action

action 类似于 mutation,不同在于:

action 提交的是 mutation,而不是直接变更状态。

action 可以包含任意异步操作。

action 要通过 store.dispatch() 方法触发

注意:涉及到异步Ajax请求数据,案例必须运行在服务器端(127.0.0.1)

新建一个data文件夹,创建txt文件。使用ajax 异步读取文本文件中数据:

App.vue父组件:


main.js

const store = new Vuex.Store({
 state : {
 a : 100
 },
 mutations : {
 JIA(state,payload){
  console.log("只有commit命令能触发我")
  state.a += payload.n
 }
 },
 actions : {
 async JIA({commit}){
  // console.log("只有dispatch命令能触发我,这里可以写异步语句")
  var data = await fetch('../data/1.txt').then(data=>data.json())
  //action提交的是mutation,而不是直接更改状态,
  //请求成功返回的数据需要通过commit命令mutations去修改state中的数据
       // context.commit("JIA", {n: data})
  // this.commit("JIA", {n: data})
  commit("JIA", {n:data})
 }
 },
 plugins: [createLogger()]
})

actions中的函数,天生自带默认参数

一些概念:

action 函数接受一个与store实例具有相同方法和属性context对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。

实践中,会经常用到ES2015的 参数解构 来简化代码(特别是我们需要调用commit很多次的时候):

actions:{
 async JIA({commit}){
 var data = await fetch("../data/1.txt").then(data=>data.json());
 commit("JIA",data)
 }
},

action 通过 store.dispatch() 方法触发:

methods:{
 add(){
 this.$store.dispatch("JIA")
 }
}

乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:

actions支持同样的载荷方式和对象方式进行分发:

vuex单向数据流动的图示:

使用 Vuex 并不意味着你需要将所有的状态放入Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

4.5 getters

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:

computed: {
 arr() {
 return this.$store.state.todos.filter(todo => todo.done).length
 }
}

在Vuex中,getter类似于组件中的computed,表示state的一些计算后的值。

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像computed计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Getter 接受 state 作为其第一个参数:

main.js

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
import createLogger from 'vuex/dist/logger'; //调试工具

Vue.use(Vuex); //将Vuex安装到全局

//创建一个仓库,并且存放以一些全局数据(存放四大选项)
const store = new Vuex.Store({
 state:{
 a:100,
 students:[
  {name:"小明",sex:"男"},
  {name:"小红",sex:"女"},
  {name:"小刚",sex:"男"},
  {name:"小花",sex:"女"},
  {name:"小黑",sex:"男"}
 ]
 },
 getters:{
 //得到所有男生
 nan(state){
  return state.students.filter((item)=>{
  return item.sex == '男';
  })
 },
 //得到所有女生
 nv(state){
  return state.students.filter((item)=>{
  return item.sex == '女';
  })
 },
 //得到男生和女生的个数,getter也可以接受getter作为第二个参数
 nanCount(state,getters){
  return getters.nan.length;
 },
 nvCount(state,getters){
  return getters.nv.length;
 }
 },
 plugins: [createLogger()]
})

new Vue({
 el:"#app",
 store, //将store注入到全局
 render:(h)=> h(App)
})

App.vue父组件:


在介绍state中我们了解到,在Store仓库里,state就是用来存放数据,若是对数据进行处理输出,比如数据要过滤,一般我们可以写到computed中。但是如果很多组件都使用这个过滤后的数据,比如饼状图组件和曲线图组件,我们是否可以把这个数据抽提出来共享?这就是getters存在的意义。官网说的很清楚,getters是store的计算属性。

getters 可以对State进行计算操作。虽然在组件内也可以做,但是getters可以在多组件之间复用如果一个状态只在一个组件内使用,是可以不用getters

getters上简单来说就是存放一些公共函数供组件调用。getters 会暴露为 $store.getters 对象,也就是说可以通过 $store.getters[属性]来进行相应的调用。

4.6vuex的命名空间

目录结构:

│ package.json
│ webpack.config.js
│
└─www
 │ index.html
 │
 └─app
 │ App.vue
 │ main.js
 │
 ├─components
 └─store
  │ index.js
  │
  ├─counter
  │ store.js
  │
  └─taobao
   store.js

./package.json:

{
 "name": "vue_study",
 "version": "1.0.0",
 "description": "",
 "main": "webpack.config.js",
 "scripts": {
 "test": "echo \"Error: no test specified\" && exit 1",
 "dev": "webpack-dev-server --content-base ./www --port 8080"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
 "vue": "^2.5.17",
 "vuex": "^3.0.1"
 },
 "devDependencies": {
 "css-loader": "^1.0.1",
 "style-loader": "^0.23.1",
 "vue-loader": "^15.4.2",
 "vue-style-loader": "^4.1.2",
 "vue-template-compiler": "^2.5.17",
 "webpack": "^4.9.1",
 "webpack-cli": "^3.1.2"
 }
}

./webpack.config.js:

const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
 //程序的入口文件
 entry: "./www/app/main.js",

 //程序的出口(打包的文件)
 output: {
  //打包文件输出的路径
  path: path.resolve(__dirname, "./www/dist"),
  //打包文件的名称
  filename: 'all.js',
  publicPath: "/public" //这是对webpack-dev-server的配置,配置虚拟路径
 },
 //监听文件的变化(自动打包)
 watch: true,
 mode: "development",
 //配置webpack模块插件
 module: {
  //关于模块的配置规则
  rules: [{
    // 模块规则(配置 loader、解析器等选项)
    test: /\.js?$/, //解析的时候匹配js文件
    //翻译什么文件夹中的文件
    include: [path.resolve(__dirname, "www/app")],
    //不翻译什么文件夹中的文件
    exclude: [path.resolve(__dirname, "node_modules")],
    // loader:"babel-loader",
    //配置翻译语法
    // options:{
    //  presets:["es2015","es2016"]
    // }
   },
   {
    test: /\.vue$/,
    loader: 'vue-loader',
    include: [path.resolve(__dirname, "www/app")],
    exclude: [path.resolve(__dirname, "node_modules")],
    options: {
     loaders: {
      js: 'babel-loader!eslint-loader'
     }
    }
   },
   {
    test: /\.css$/,
    include: [path.resolve(__dirname, "www/app")],
    exclude: [path.resolve(__dirname, "node_modules")],
    use: ['vue-style-loader', 'css-loader'],
   },
   {
    test: /\.styl(us)?$/,
    use: [
    'vue-style-loader',
    'css-loader',
    'stylus-loader'
    ]
   }
  ]
 },
 resolve: {
  alias: {
   'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'
  }
 },
 //最新版webpack需要引入此插件
 plugins: [
  new VueLoaderPlugin()
 ],
 //webpack设置代理跨越
 devServer: {
  proxy: {
   '/api': {
    target: 'http://127.0.0.1:3000', //设置你调用的接口域名和端口
    //这里理解成/api代理target中的地址,后面组件中调用接口要使用/api代替
    pathRewrite: { '^/api': '' }
   }
  }
 }
}

./www/index.html:




 
 


 

./www/app/App.vue:


./www/app/main.js:[

import Vue from "vue";
import App from "./App.vue";
import store from "./store";

new Vue({
 el: "#app",
 store,//引入store文件夹中的index.js
 render: (h) => h(App)
})

./www/app/store/index.js:

import Vue from "vue";
import Vuex from "vuex";
import counterStore from "./counter/store.js";//引入counter的store
import taobaoStore from "./taobao/store.js";//引入taobao的store
import createLogger from 'vuex/dist/logger';

Vue.use(Vuex);

export default new Vuex.Store({
 modules: {//放入modules
  counterStore,
  taobaoStore
 },
 plugins : [createLogger()]
})

./www/app/store/counter/store.js:

export default {
 namespaced: true, //命名空间
 state : {
  a:100
 },
 mutations : {
  ADD(state,payload){
   state.a++//上面的state
  }
 },
 actions : {
  ADD({commit}){
   commit("ADD")//调用上面mutations的ADD
  }
 }
}

./www/app/store/taobao/store.js:

export default {
 namespaced: true,//开启命名空间跟上面一样的用法
 state : {
  a:200
 },
 mutations : {},
 actions : {}
}

五、配置虚拟服务器

一般情况下,一个应用的数据都需要等待后端接口人员开发完对应的接口才可以获取到,这样子的效率有点低。最好是我们可以自己模拟接口数据,进行页面的数据填充,打通所有关节,之后等接口开发好了,改下接口地址就好了。

所以,作为前端和客户端开发人员,在后端还没有给出对应的api接口时,我们无法做测试。

这时,我们可以使用json-server快速搭建一个测试的api接口,能在几十秒之内搭建好。

json-server 它能模拟“数据库”,提供了一套简单的API(RESTFUL)接口。

在开发过程中,前后端不论是否分离,接口多半是滞后于页面开发的。所以建立一个RESTFUL风格的API接口,给前端页面提供虚拟的数据,是非常有必要的。

因为它足够简单,写少量数据,即可使用。

也因为它足够强大,支持CORS和JSONP跨域请求,支持GET, POST, PUT, PATCH 和 DELETE 方法,更提供了一系列的查询方法,如limit,order等。下面将详细介绍 json-server 的使用。

https://github.com/typicode/json-server

总结

以上所述是小编给大家介绍的Vue中UI组件库之Vuex与虚拟服务器初识 ,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!


推荐阅读
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • 本文介绍了DataTables插件的官方网站以及其基本特点和使用方法,包括分页处理、数据过滤、数据排序、数据类型检测、列宽度自动适应、CSS定制样式、隐藏列等功能。同时还介绍了其易用性、可扩展性和灵活性,以及国际化和动态创建表格的功能。此外,还提供了参数初始化和延迟加载的示例代码。 ... [详细]
  • 工作经验谈之-让百度地图API调用数据库内容 及详解
    这段时间,所在项目中要用到的一个模块,就是让数据库中的内容在百度地图上展现出来,如经纬度。主要实现以下几点功能:1.读取数据库中的经纬度值在百度上标注出来。2.点击标注弹出对应信息。3 ... [详细]
  • 前言:关于跨域CORS1.没有跨域时,ajax默认是带cookie的2.跨域时,两种解决方案:1)服务器端在filter中配置详情:http:blog.csdn.netwzl002 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了一种图片处理应用,通过固定容器来实现缩略图的功能。该方法可以实现等比例缩略、扩容填充和裁剪等操作。详细的实现步骤和代码示例在正文中给出。 ... [详细]
author-avatar
kenson4930
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有