赞
踩
前端工程化
是使用软件工程的方法
来单独
解决前端
的开发流程中模块化、组件化、规范化、自动化
的问题,其主要目的为了提高效率和降低成本。前端工程化实现的技术栈有很多,我们采用ES6+nodejs+npm+Vite+VUE3+router+pinia+axios+Element-plus组合来实现
由于VUE3中大量使用了ES6的语法,所以ES6成为了学习VUE3的门槛之一
。let 和var的差别
1、let 不能重复声明
2、let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问。
3、let不会预解析进行变量提升
4、let 定义的全局变量不会作为window的属性
5、let在es6中推荐优先使用
const和var的差异
1、新增const和let类似,只是const定义的变量不能修改
2、并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
1、字符串中可以出现换行符
2、可以使用 ${xxx} 形式输出变量和拼接变量
<script> // 1 多行普通字符串 let ulStr = '<ul>'+ '<li>JAVA</li>'+ '<li>html</li>'+ '<li>VUE</li>'+ '</ul>' console.log(ulStr) // 2 多行模板字符串 let ulStr2 = ` <ul> <li>JAVA</li> <li>html</li> <li>VUE</li> </ul>` console.log(ulStr2) // 3 普通字符串拼接 let name ='张小明' let infoStr =name+'被评为本年级优秀学员' console.log(infoStr) // 4 模板字符串拼接 let infoStr2 =`${name}被评为本年级优秀学员` console.log(infoStr2) </script>
{}
表示对象,方括号 []
表示数组。通过解构赋值,函数更方便进行参数接受等。数组解构赋值
let [a, b, c] = [1, 2, 3]; //新增变量名任意合法即可,本质是按照顺序进行初始化变量的值
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
let [a, b, c, d = 4] = [1, 2, 3];
console.log(d); // 4
对象解构赋值
let {a, b} = {a: 1, b: 2};
//新增变量名必须和属性名相同,本质是初始化变量的值为对象中同名属性的值
//等价于 let a = 对象.a let b = 对象.b
console.log(a); // 1
console.log(b); // 2
let {a: x, b: y} = {a: 1, b: 2};
console.log(x); // 1
console.log(y); // 2
函数参数解构赋值
function add([x, y]) {
return x + y;
}
add([1, 2]); // 3
ES6 允许使用“箭头” 义函数。语法类似Java中的Lambda表达式
<script> //ES6 允许使用“箭头”(=>)定义函数。 //1. 函数声明 let fn1 = function(){} let fn2 = ()=>{} //箭头函数,此处不需要书写function关键字 let fn3 = x =>{} //单参数可以省略(),多参数无参数不可以! let fn4 = x => console.log(x) //只有一行方法体可以省略{}; let fun5 = x => x + 1 //当函数体只有一句返回值时,可以省略花括号和 return 语句 //2. 使用特点 箭头函数this关键字 // 在 JavaScript 中,this 关键字通常用来引用函数所在的对象, // 或者在函数本身作为构造函数时,来引用新对象的实例。 // 但是在箭头函数中,this 的含义与常规函数定义中的含义不同, // 并且是由箭头函数定义时的上下文来决定的,而不是由函数调用时的上下文来决定的。 // 箭头函数没有自己的this,this指向的是外层上下文环境的this let person ={ name:"张三", showName:function (){ console.log(this) // 这里的this是person console.log(this.name) }, viewName: () =>{ console.log(this) // 这里的this是window console.log(this.name) } } person.showName() person.viewName() //this应用 function Counter() { this.count = 0; setInterval(() => { // 这里的 this 是上一层作用域中的 this,即 Counter实例化对象 this.count++; console.log(this.count); }, 1000); } let counter = new Counter(); </script>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> #xdd{ display: inline-block; width: 200px; height: 200px; background-color: red; } </style> </head> <body> <div id="xdd"></div> <script> let xdd = document.getElementById("xdd"); // 方案1 xdd.onclick = function(){ console.log(this) let _this= this; //this 是xdd //开启定时器 setTimeout(function(){ console.log(this) //变粉色 _this.style.backgroundColor = 'pink'; },2000); } // 方案2 xdd.onclick = function(){ console.log(this) //开启定时器 setTimeout(()=>{ console.log(this)// 使用setTimeout() 方法所在环境时的this对象 //变粉色 this.style.backgroundColor = 'pink'; },2000); } </script> </body> </html>
setTimeout(function(){},2000);
,该方法的调用者是window对象,所以直接在内部用this设置是是真的window对象的,而我们想要操作的是xdd对象,所以我们可以用方法一的方法获取到外部的this,然后再内部使用。或者用方法二直接就用箭头函数获取到外部的this对象。rest参数,在形参上使用 和JAVA中的可变参数几乎一样
<script>
// 1 参数列表中多个普通参数 普通函数和箭头函数中都支持
let fun1 = function (a,b,c,d=10){console.log(a,b,c,d)}
let fun2 = (a,b,c,d=10) =>{console.log(a,b,c,d)}
fun1(1,2,3)
fun2(1,2,3,4)
// 2 ...作为参数列表,称之为rest参数 普通函数和箭头函数中都支持 ,因为箭头函数中无法使用arguments,rest是一种解决方案
let fun3 = function (...args){console.log(args)}
let fun4 = (...args) =>{console.log(args)}
fun3(1,2,3)
fun4(1,2,3,4)
// rest参数在一个参数列表中的最后一个只,这也就无形之中要求一个参数列表中只能有一个rest参数
//let fun5 = (...args,...args2) =>{} // 这里报错
</script>
spread参数,在实参上使用rest
<script> let arr =[1,2,3] //let arrSpread = ...arr;// 这样不可以,...arr必须在调用方法时作为实参使用 let fun1 =(a,b,c) =>{ console.log(a,b,c) } // 调用方法时,对arr进行转换 转换为1,2,3 fun1(...arr) //应用场景1 合并数组 let arr2=[4,5,6] let arr3=[...arr,...arr2] console.log(arr3) //应用场景2 合并对象属性 let p1={name:"张三"} let p2={age:10} let p3={gender:"boy"} let person ={...p1,...p2,...p3} console.log(person) </script>
ES6中新增了对象创建的语法糖,支持了class extends constructor等关键字,让ES6的语法和面向对象的语法更加接近
class Person{ // 属性 #n; age; get name(){ return this.n; } set name(n){ this.n =n; } // 实例方法 eat(food){ console.log(this.age+"岁的"+this.n+"用筷子吃"+food) } // 静态方法 static sum(a,b){ return a+b; } // 构造器 constructor(name,age){ this.n=name; this.age = age; } } let person =new Person("张三",10); // 访问对象属性 // 调用对象方法 console.log(person.name) console.log(person.n) person.name="小明" console.log(person.age) person.eat("火锅") console.log(Person.sum(1,2)) class Student extends Person{ grade ; score ; study(){ } constructor(name,age ) { super(name,age); } } let stu =new Student("学生小李",18); stu.eat("面条")
对象的拷贝,快速获得一个和已有对象相同的对象的方式
<script>
let arr =['java','c','python']
let person ={
name:'张三',
language:arr
}
// 浅拷贝,person2和person指向相同的内存
let person2 = person;
person2.name="小黑"
console.log(person.name)
</script>
<script>
let arr =['java','c','python']
let person ={
name:'张三',
language:arr
}
// 深拷贝,通过JSON和字符串的转换形成一个新的对象
let person2 = JSON.parse(JSON.stringify(person))
person2.name="小黑"
console.log(person.name)
console.log(person2.name)
</script>
模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护、扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。前端模块化的主要优势如下:
> 目前,前端模块化有多种规范和实现,包括 CommonJS、AMD 和 ES6 模块化。ES6 模块化是 JavaScript 语言的模块标准,使用 import 和 export 关键字来实现模块的导入和导出。现在,大部分浏览器都已经原生支持 ES6 模块化,因此它成为了最为广泛使用的前端模块化标准. `
ES6中无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或者方法
//1.分别暴露 // 模块想对外导出,添加export关键字即可! // 导出一个变量 export const PI = 3.14 // 导出一个函数 export function sum(a, b) { return a + b; } // 导出一个类 export class Person { constructor(name, age) { this.name = name; this.age = age; } sayHello() { console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`); } }
/*
*代表module.js中的所有成员
m1代表所有成员所属的对象
*/
import * as m1 from './module.js'
// 使用暴露的属性
console.log(m1.PI)
// 调用暴露的方法
let result =m1.sum(10,20)
console.log(result)
// 使用暴露的Person类
let person =new m1.Person('张三',10)
person.sayHello()
<!-- 导入JS文件 添加type='module' 属性,否则不支持ES6的模块化 -->
<script src="./app.js" type="module" />
//2.统一暴露 // 模块想对外导出,export统一暴露想暴露的内容! // 定义一个常量 const PI = 3.14 // 定义一个函数 function sum(a, b) { return a + b; } // 定义一个类 class Person { constructor(name, age) { this.name = name; this.age = age; } sayHello() { console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`); } } // 统一对外导出(暴露) export { PI, sum, Person }
/* {}中导入要使用的来自于module.js中的成员 {}中导入的名称要和module.js中导出的一致,也可以在此处起别名 {}中如果定义了别名,那么在当前模块中就只能使用别名 {}中导入成员的顺序可以不是暴露的顺序 一个模块中可以同时有多个import 多个import可以导入多个不同的模块,也可以是同一个模块 */ //import {PI ,Person ,sum } from './module.js' //import {PI as pi,Person as People,sum as add} from './module.js' import {PI ,Person ,sum,PI as pi,Person as People,sum as add} from './module.js' // 使用暴露的属性 console.log(PI) console.log(pi) // 调用暴露的方法 let result1 =sum(10,20) console.log(result1) let result2 =add(10,20) console.log(result2) // 使用暴露的Person类 let person1 =new Person('张三',10) person1.sayHello() let person2 =new People('李四',11) person2.sayHello()
// 3默认和混合暴露 /* 默认暴露语法 export default sum 默认暴露相当于是在暴露的对象中增加了一个名字为default的属性 三种暴露方式可以在一个module中混合使用 */ export const PI = 3.14 // 导出一个函数 function sum(a, b) { return a + b; } // 导出一个类 class Person { constructor(name, age) { this.name = name; this.age = age; } sayHello() { console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`); } } // 导出默认 export default sum // 统一导出 export { Person }
/* *代表module.js中的所有成员 m1代表所有成员所属的对象 */ import * as m1 from './module.js' import {default as add} from './module.js' // 用的少 import add2 from './module.js' // 等效于 import {default as add2} from './module.js' // 调用暴露的方法 let result =m1.default(10,20) console.log(result) let result2 =add(10,20) console.log(result2) let result3 =add2(10,20) console.log(result3) // 引入其他方式暴露的内容 import {PI,Person} from './module.js' // 使用暴露的Person类 let person =new Person('张三',10) person.sayHello() // 使用暴露的属性 console.log(PI)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 这里也可以用浏览器打开连接,然后将获得的文本单独保存进入一个vue.js的文件,导入vue.js文件即可 --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <div id="app"> <!-- 给style属性绑定colorStyle数据 --> <!-- {{插值表达式 直接将数据放在该位置}} --> <h1 v-bind:style="colorStyle">{{headline}}</h1> <!-- v-text设置双标签中的文本 --> <p v-text="article"></p> <!-- 给type属性绑定inputType数据 --> <input v-bind:type ="inputType" value="helloVue3"> <br> <!-- 给按钮单击事件绑定函数 --> <button @click="sayHello()">hello</button> </div> <script> //组合api const app = Vue.createApp({ // 在setup内部自由声明数据和方法即可!最终返回! setup(){ //定义数据 //在VUE中实现DOM的思路是: 通过修改修数据而影响页面元素 // vue3中,数据默认不是响应式的,需要加ref或者reactive处理,后面会详细讲解 let inputType ='text' let headline ='hello vue3' let article ='vue is awesome' let colorStyle ={'color':'red'} // 定义函数 let sayHello =()=>{ alert("hello Vue") } //在setup函数中,return返回的数据和函数可以在html使用 return { inputType, headline, article, colorStyle, sayHello } } }); //挂载到视图 app.mount("#app"); </script> </body> </html>
npm run dev
开始运行:assets/
目录:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等。components/
目录:用于存放组件相关的文件。组件是代码复用的一种方式,用于抽象出一个可复用的 UI 部件,方便在不同的场景中进行重复使用。layouts/
目录:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等。pages/
目录:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下,可以创建对应的文件夹,用于存储不同的页面组件。plugins/
目录:用于存放 Vite 插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等。router/
目录:用于存放 Vue.js 的路由配置文件,负责管理视图和 URL 之间的映射关系,方便实现页面之间的跳转和数据传递。store/
目录:用于存放 Vuex 状态管理相关的文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。utils/
目录:用于存放一些通用的工具函数,如日期处理函数、字符串操作函数等。什么是VUE的组件?
什么是.vue文件?
<script> <template> <style>
工程化vue项目如何组织这些组件?
<div id ='app'></div>
是用于挂载所有组建的元素<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
src="/src/main.js"
指向了main.js文件:import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')
createApp(App).mount('#app')
这行代码是在main.js中用来挂载一个Vue应用到指定的HTML元素上。具体来说,createApp(App)
用来创建一个Vue应用实例,然后调用.mount('#app')
方法将这个实例挂载到id为app
的HTML元素上,从而让Vue应用在该元素中渲染和生效。./App.vue
文件并且为它起了一个别名叫App:<script setup>
import haha from "./components/haha.vue"
</script>
<template>
<h1 class="h1c1">hello</h1>
<haha></haha>
</template>
<style scoped>
.h1c1{
color:red;
}
</style>
<script setup>
</script>
<template>
<h1 class="h11">haha</h1>
</template>
<style scoped>
.h11{
color:blue;
}
</style>
import './style/reset.css' //书写引入的资源的相对路径即可!
import './style/reset.css'
3.Vue文件style代码引入:
@import './style/reset.css'
<script type="module"> //存储vue页面逻辑js代码 import {ref} from 'vue' export default{ setup(){ //非响应式数据: 修改后VUE不会更新DOM //响应式数据: 修改后VUE会更新DOM //VUE2中数据默认是响应式的 //VUE3中数据要经过ref或者reactive处理后才是响应式的 //ref是VUE3框架提供的一个函数,需要导入 //let counter = 1 //ref处理的响应式数据在js编码修改的时候需要通过.value操作 //ref响应式数据在绑定到html上时不需要.value let counter = ref(1) function increase(){ // 通过.value修改响应式数据 counter.value++ } function decrease(){ counter.value-- } return { counter, increase, decrease } } } </script> <template> <div> <button @click="decrease()">-</button> {{ counter }} <button @click="increase()">+</button> </div> </template> <style scoped> button{ border: 1px solid red; } </style>
<script type="module" setup> /* <script type="module" setup> 通过setup关键字 可以省略 export default {setup(){ return{}}}这些冗余的语法结构 */ import {ref} from 'vue' // 定义响应式数据 let counter = ref(1) // 定义函数 function increase(){ counter.value++ } function decrease(){ counter.value-- } </script> <template> <div> <button @click="decrease()">-</button> {{ counter }} <button @click="increase()">+</button> </div> </template> <style scoped> button{ border: 1px solid red; } </style>
插值表达式:最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 ,即双大括号{{}}
<script setup type="module"> let msg ="hello vue3" let getMsg= ()=>{ return 'hello vue3 message' } let age = 19 let bee = '蜜 蜂' // 购物车 const carts = [{name:'可乐',price:3,number:10},{name:'薯片',price:6,number:8}]; //计算购物车总金额 function compute(){ let count = 0; for(let index in carts){ count += carts[index].price*carts[index].number; } return count; } </script> <template> <div> <h1>{{ msg }}</h1> msg的值为: {{ msg }} <br> getMsg返回的值为:{{ getMsg() }} <br> 是否成年: {{ age>=18?'true':'false' }} <br> 反转: {{ bee.split(' ').reverse().join('-') }} <br> 购物车总金额: {{ compute() }} <br/> 购物车总金额: {{carts[0].price*carts[0].number + carts[1].price*carts[1].number}} <br> </div> </template> <style scoped> </style>
为了渲染双标中的文本,我们也可以选择使用
v-text
和v-html
命令
```html <script setup type="module"> let msg ='hello vue3' let getMsg= ()=>{ return msg } let age = 19 let bee = '蜜 蜂' let redMsg ='<font color=\'red\'>msg</font>' let greenMsg =`<font color=\'green\'>${msg}</font>` </script> <template> <div> <span v-text='msg'></span> <br> <span v-text='redMsg'></span> <br> <span v-text='getMsg()'></span> <br> <span v-text='age>18?"成年":"未成年"'></span> <br> <span v-text='bee.split(" ").reverse().join("-")'></span> <br> <span v-html='msg'></span> <br> <span v-html='redMsg'></span> <br> <span v-html='greenMsg'></span> <br> <span v-html="`<font color='green'>${msg}</font>`"></span> <br> </div> </template> <style scoped> </style>
想要渲染一个元素的 attribute,应该使用
v-bind
指令
<script setup type="module"> const data = { name:'尚硅谷', url:"http://www.atguigu.com", logo:"http://www.atguigu.com/images/index_new/logo.png" } </script> <template> <div> <a v-bind:href='data.url' target="_self"> <img :src="data.logo" :title="data.name"> <br> <input type="button" :value="`点击访问${data.name}`"> </a> </div> </template> <style scoped> </style>
我们可以使用
v-on
来监听 DOM 事件,并在事件触发时执行对应的 Vue的JavaScript代码。
v-on:click="handler"
或简写为 @click="handler"
on
前缀 如:onClick --> click
.once:只触发一次事件。[重点]
.prevent:阻止默认事件。[重点]
<script setup type="module"> import {ref} from 'vue' // 响应式数据 当发生变化时,会自动更新 dom树 let count=ref(0) let addCount= ()=>{ count.value++ } let incrCount= (event)=>{ count.value++ // 通过事件对象阻止组件的默认行为 event.preventDefault(); } </script> <template> <div> <h1>count的值是:{{ count }}</h1> <!-- 方法事件处理器 --> <button v-on:click="addCount()">addCount</button> <br> <!-- 内联事件处理器 --> <button @click="count++">incrCount</button> <br> <!-- 事件修饰符 once 只绑定事件一次 --> <button @click.once="count++">addOnce</button> <br> <!-- 事件修饰符 prevent 阻止组件的默认行为 --> <a href="http://www.atguigu.com" target="_blank" @click.prevent="count++">prevent</a> <br> <!-- 原生js方式阻止组件默认行为 (推荐) --> <a href="http://www.atguigu.com" target="_blank" @click="incrCount($event)">prevent</a> <br> </div> </template> <style scoped> </style>
<script type="module" setup> let counter = 0; function show(){ alert(counter); } </script> <template> <div> <button @click="counter--">-</button> {{ counter }} <button @click="counter++">+</button> <hr> <!-- 此案例,我们发现counter值,会改变,但是页面不改变! 默认Vue3的数据是非响应式的!--> <button @click="show()">显示counter值</button> </div> </template> <style scoped> </style>
<script type="module" setup> /* 从vue中引入ref方法 */ import {ref} from 'vue' let counter = ref(0); function show(){ alert(counter.value); } /* 函数中要操作ref处理过的数据,需要通过.value形式 */ let decr = () =>{ counter.value--; } let incr = () =>{ counter.value++; } </script> <template> <div> <button @click="counter--">-</button> <button @click="decr()">-</button> {{ counter }} <button @click="counter++">+</button> <button @click="incr()">+</button> <hr> <button @click="show()">显示counter值</button> </div> </template> <style scoped> </style>
<script type="module" setup> /* 从vue中引入reactive方法 */ import {ref,reactive} from 'vue' let data = reactive({ counter:0 }) function show(){ alert(data.counter); } /* 函数中要操作reactive处理过的数据,需要通过 对象名.属性名的方式 */ let decr = () =>{ data.counter--; } let incr = () =>{ data.counter++; } </script> <template> <div> <button @click="data.counter--">-</button> <button @click="decr()">-</button> {{ data.counter }} <button @click="data.counter++">+</button> <button @click="incr()">+</button> <hr> <button @click="show()">显示counter值</button> </div> </template> <style scoped> </style>
对比ref和reactive:
ref
适用于以下开发场景:ref
主要用于包装基本类型数据(如字符串、数字等),即只有一个值的数据,如果你想监听这个值的变化,用 ref
最为方便。在组件中使用时也很常见。访问方式简单:ref
对象在访问时与普通的基本类型值没有太大区别,只需要通过 .value 访问其实际值即可。reactive
适用于以下开发场景:reactive
可以将一个普通对象转化为响应式对象,这样在数据变化时会自动更新界面,特别适用于处理复杂对象或者数据结构。reactive
可以递归追踪所有响应式对象内部的变化,从而保证界面的自动更新。toRef基于reactive响应式对象上的一个属性,创建一个对应的 ref响应式数据。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。toRefs将一个响应式对象多个属性转换为一个多个ref数据,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 [toRef()]
<script type="module" setup> /* 从vue中引入reactive方法 */ import {ref,reactive,toRef,toRefs} from 'vue' let data = reactive({ counter:0, name:"test" }) // 将一个reactive响应式对象中的某个属性转换成一个ref响应式对象 let ct =toRef(data,'counter'); // 将一个reactive响应式对象中的多个属性转换成多个ref响应式对象 let {counter,name} = toRefs(data) function show(){ alert(data.counter); // 获取ref的响应对象,需要通过.value属性 alert(counter.value); alert(name.value) } /* 函数中要操作ref处理过的数据,需要通过.value形式 */ let decr = () =>{ data.counter--; } let incr = () =>{ /* ref响应式数据,要通过.value属性访问 */ counter.value++; } </script> <template> <div> <button @click="data.counter--">-</button> <button @click="decr()">-</button> {{ data.counter }} & {{ ct }} <button @click="data.counter++">+</button> <button @click="incr()">+</button> <hr> <button @click="show()">显示counter值</button> </div> </template> <style scoped> </style>
v-if='表达式'
只会在指令的表达式返回真值时才被渲染
也可以使用 v-else
为 v-if
添加一个“else 区块”。
一个 v-else
元素必须跟在一个 v-if
元素后面,否则它将不会被识别。
<script type="module" setup>
import {ref} from 'vue'
let awesome = ref(true)
</script>
<template>
<div>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/228689
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。