前端高频面试题

1.什么是 Window 对象? 什么是 Document 对象?

window:代表浏览器中一个打开的窗口
1)对象属性
① window.self 引用本窗口window==window.self
② window.name 为窗口名字
③ window.defaultStatus 窗户状态栏信息
④ window.location URL地址,设置该属性可打开新的页面
2)对象方法
① window.alert("text") 提示信息会话框
② window.confirm("text") 确认会话框
③ window.prompt("text") 键盘输入会话框
④ window.setIntervel(func, time) 每隔指定时间(毫秒)执行一次操作
⑤ window.clearInterval() 清除时间间隔
⑥ window.setTimeout(action,time) 等待指定时间(毫秒)后再执行操作
⑦ window.open() 打开新的窗口
⑧ window.close() 关闭窗口
3)成员对象
① window.event
② window.document
③ Window.history
a. window.history.length 浏览过的页面数
b. window.history.back() 后退
c. window.history.forward() 前进
d. window.history.go(i) 前进或后退i个页面(i>0前进,i<0后退)
④ Window.screen
a. window.screen.width 屏幕宽度
b. window.screen.height 屏幕高度
c. window.screen.colorDepth 屏幕色深
d. window.screen.availWidth 屏幕可用宽度
e. window.screen.availHeight 屏幕可用高度(除去任务栏的高度)
⑤ Window.navigator
a. window.navigator.appCodeName 浏览器代码名
b. window.navigator.appName 浏览器名
c. window.navigator.platform 运行浏览器的操作系统平台
d. window.navigator.appVersion 浏览器的平台和版本
e. window.navigator.userAgent 由客户机发送服务器的user-agent 头部的值
f. window.navigator.cookieEnabled 浏览器是否启用cookie
g. window.navigator.appMinorVersion 浏览器补丁版本
h. window.navigator.cpuClass cpu类型
i. Window.navigator.plugins 插件标识
j. window.navigator.userProfile 用户的个人信息
k. window.navigator.systemLanguage 客户体系语言
l. window.navigator.userLanguage 用户语言
m. window.navigator.onLine 用户是否在线
n. window.navigator.mimeTypes MIME类型(数组)

document对象:代表整个HTML 文档,可用来访问页面中的所有元素。
1)对象属性
① document.title 文档标题,等价于HTML的标签
② document.bgColor 页面背景色
③ document.fgColor 前景色(文本颜色)
④ document.linkColor 未点击过的链接颜色
⑤ document.alinkColor 激活链接(焦点在此链接上)的颜色
⑥ document.vlinkColor 已点击过的链接颜色
⑦ document.URL 在同一窗口打开另一网页
⑧ document.fileCreatedDate 文件建立日期,只读属性
⑨ document.fileModifiedDate 文件修改日期,只读属性
⑩ document.fileSize 大小,只读属性
⑪ document.cookie 设置和读出cookie
⑫ document.charset 字符集
2)对象方法
① document.write() 动态向页面写入内容
② document.createElement(tag) 创建指定标签的元素
③ document.getElementById(id) 获得指定id值的元素
④ document.getElementsByName(name) 获得指定Name值的元素
3)body对象
① document.body 文档主体开始和结束,等价于
② document.body.bgColor 背景颜色
③ document.body.link 未点击过的链接颜色
④ document.body.alink 激活链接(焦点在此链接上)的颜色
⑤ document.body.vlink 已点击过的链接颜色
⑥ document.body.text 文本色
⑦ document.body.innerText ...之间的文本
⑧ document.body.innerHTML ...之间的HTML代码
⑨ document.body.topMargin 页面上边距
⑩ document.body.leftMargin 页面左边距
⑪ document.body.rightMargin 页面右边距
⑫ document.body.bottomMargin 页面下边距
⑬ document.body.background 背景
⑭ document.body.appendChild(oTag) 添加DOM对象
⑮ document.body.onclick="func()" 鼠标指针单击对象是触发
⑯ document.body.onmouseover="func()" 鼠标指针移到对象时触发
⑰ document.body.onmouseout="func()" 鼠标指针移出对象时触发
4)location-位置子对象
① document.location.hash #号后的部分
② document.location.host 域名+端口号
③ document.location.hostname 域名
④ document.location.href 完整URL
⑤ document.location.pathname 目录部分
⑥ document.location.port 端口号
⑦ document.location.protocol 网络协议
⑧ document.location.search ?号后的部分
5)通过集合引用(以images集合为例,forms集合等类似)
① document.images 标签
② document.images.length 标签的个数
③ document.images[0] 第1个标签
④ document.images[i] 第i-1个标签

2.怎么给jQuery扩展插件

给jQuery扩展插件有下面两种方式:
通过$.extend()来扩展jQuery。
通过$.fn 向jQuery添加新的方法

3.document load和document ready有什么区别?

1)load是当页面所有资源全部加载完成后(包括DOM文档树,css文件,js文件,图片资源等),执行一个函数
问题:如果图片资源较多,加载时间较长,onload后等待执行的函数需要等待较长时间,所以一些效果可能受到影响
2)$(document).ready()是当DOM文档树加载完成后执行一个函数 (不包含图片,css等),所以会比load较快执行
在原生的jS中不包括ready()这个方法,只有load方法就是onload事件。

4.对JS异步请求方法的理解

“同步模式" 就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

“异步模式" 非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,"异步模式"甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。

5.如何编写高性能的JavaScript

1.加载运行
1)javascript在浏览器中的性能,大概是开发者面对最重要的问题。因为javascript的阻塞特征,也就是在javascipt运行的过程中,其他的事情并不能被浏览器处理。所以javascript运行的越长,浏览器等待的时间就越长。所以应该把脚本放在底部(通常就是标签的前面)。
2)由于每次http请求都会有性能负担,加载一个100KB的脚本要比加载4个25KB的快。所以把多个脚本合成 一个脚本,通过打包工具来实现。
3)用defer属性,不过只支持internet explorer 和firefox3.5以上版本。
4)动态创建script标签,用它下载并执行代码。

2.数据
1)每一种数据都有读写的负担,全局变量,局部变量,数组,对象访问的代价都不一样。如果关心运行速度,尽量使用局部变量,限制对象和数组的使用(原型链越深,搜索的速度越慢)。因为作用域链搜索是从里往外的,在一个函数内部,首先是找局部变量,找不到,再去外面一层寻找变量。所以在函数的内部用全局变量,可以把值赋给局部变量。
2)不要使用with,with会改变作用域链,影响性能。
3)try-catch的catch表达式也是和with的效果一样,但是try-catch是一个非常有用的东西,所以在用try-catch的时候确保你了解可能发生的错误。
3)闭包,通常在一个函数激活对象和运行上下文一同销毁,但是闭包却不会。所以闭包意味着更多的内存开销。在大型网页应用的时候,需要注意这个问题。
4)对于一些常用的对象,数组项存入局部变量中,访问局部变量的速度会大大高于那些原始的变量。

3.DOM
1)在网页中,DOM操作的代价昂贵,通常是一个性能瓶颈。对于javascript和DOM之间的关系,我们可以理解为它们各自是一个房间,在DOM的房间的门外有一个守门人,每次从javascript到DOM需要缴纳过门费,所以操作DOM的次数越多,代价也就越昂贵,所以我们要尽量减少过门的次数,尽量在javascript门这边完成。即减少dom访问,在Javascript端做尽可能的事。
2)对重复访问的dom对象使用局部变量保存。
3)注意页面重绘和重排,把dom操作批量化(不要分步修改DOM,用一次性全部修改)
4)使用事件委托

4.选择算法
1)在javasctip中有很多种循环方法,while、do-whlie、for、for-in,forEach并且还有很多条件语句,if-else、switch,因为Javascript的资源有限,所以选择算法的时候优先选择最优的。
2)while、do-whlie、for 循环的性能相似,无所谓选择谁。
3)相对性能来说for-in是最浪费性能的一种(因为每次迭代都需要搜索原型的属性),除非你要遍历一个属性不知道的对象,否则不要用。
4)switch比if-else要快
5)forEach是基于函数的迭代,虽然很便利,但是基于函数的迭代是普通基于循环的八倍

5.定时器和Ajax
当javascript和用户操作一起运行的时候,用户界面是不能响应操作的,原因是当脚本执行的时候,UI不会随着用户的交互而更新,此时的用户交互被放进队列里面,当原先的脚本执行完之后才会执行这个交互。
定时器可以安排代码推迟去执行,可以把大的脚本分解成一个个小的任务,所以合理的使用定时器,可以提高用户的体验。
AJAX请求的数据尽量用json数据。
减少页面AJAX的请求次数。

6.描述浏览器的渲染过程,DOM树和渲染树的区别

在DOM树构建的同时,浏览器会构建渲染树(render tree)。渲染树的节点(渲染器),在Gecko中称为frame,而在webkit中称为renderer。渲染器是在文档解析和创建DOM节点后创建的,会计算DOM节点的样式信息。

7.什么是预解析

JS代码在执行之前,会对代码进行预解析,寻找作用域中的var 和 function ,然后对其进行事先声明,在从上到下执行代码。这就是一个预解析的过程。

8.对JavaScript中垃圾回收机制的理解

JavaScript有自动垃圾回收机制,也就是说执行环境会负责管理代码执行过程中使用的内存,在开发过程中就无需考虑内存分配及无用内存的回收问题了。
JavaScript垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。

9.事件委托机制?这样做有什么好处?

事件委托,就是某个事件本来该自己干的,但是自己不干,交给别人来干。就叫事件委 托。
事件委托原理:事件冒泡机制。
优点:
1)可以大量节省内存占用,减少事件注册。比如ul上代理所有li的click事件就很不错,从而提高性能。
2)新添加的元素还会有之前的事件,可以实现当新增子对象时,无需再对其进行事件绑定,对于动态内容部分尤为合适
缺点:
事件代理的常用应用应该仅限于上述需求,如果把所有事件都用事件代理,可能会出现事件误判。即本不该被触发的事件被绑定上了事件。

10.对this的理解

this的指向
this表示当前对象,this的指向是根据调用的上下文来决定的,默认指向window对象,指向window对象时可以省略不写,例如:
this.alert() <=> window.alert()<=> alert();
调用的上下文环境包括全局和局部;
全局环境
全局环境就是在里面,这里的this始终指向的是window对象
局部环境
1)在全局作用域下直接调用函数,this指向window
2)对象函数调用,哪个对象调用就指向哪个对象
3)使用 new 实例化对象,在构造函数中的this指向实例化对象
4)使用call或apply改变this的指向

11.bind、call、apply三者之间的区别?

call、apply、bind的作用是改变函数运行时this的指向。
call:call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。
apply:apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
bind:和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。

12.some、every、reduce、filter、map、forEach有什么区别?

arr.some()、every()— —判断数组
some()
1)不创建新数组
2)不改变原数组
3)输出的是判断为true则马上跳出循环并return成true
4)回调函数参数,item(数组元素)、index(序列)、arr(数组本身)
5)使用return操作输出,会循环数组每一项,并在回调函数中操作
every()— —与some相反
1)不创建新数组
2)不改变原数组
3)输出的是判断为false则马上跳出循环并return成false
4)回调函数参数,item(数组元素)、index(序列)、arr(数组本身)
5)使用return操作输出,会循环数组每一项,并在回调函数中操作

reduce()— —叠加数组
不一定在数学意义上的叠加计算,这里叠加指:可以利用前遍历操作的结果到下一次遍历使用,重复叠加使用下去
1)创建新数组
2)不改变原数组
3)输出的是return叠加什么就输出什么 新数组
4)回调函数参数
pre(第一次为数组第一项,之后为上一操作的结果)
next(数组的下一项)
index(next项的序列)
arr(数组本身)
回调函数后的改变第一项参数。(不影响原数组)
5)使用return操作输出,会循环数组每一项,并在回调函数中操作

arr.filter()
1)创建新数组
2)不改变原数组
3)输出的是判断为true的数组元素形成的新数组
4)回调函数参数,item(数组元素)、index(序列)、arr(数组本身)
5)使用return操作输出,会循环数组每一项,并在回调函数中操作

arr.map()— —更新数组
1)创建新数组
2)不改变原数组
3)输出的是return什么就输出什么新数组
4)回调函数参数,item(数组元素)、index(序列)、arr(数组本身)
5)使用return操作输出,会循环数组每一项,并在回调函数中操作

arr.forEach()
遍历数组全部元素,利用回调函数对数组进行操作,自动遍历数组.length次数,且无法break中途跳出循环,因此不可控。不支持return操作输出,return只用于控制循环是否跳出当前循环。

13.js中json对象与json字符串互转的方法?

1)JSON字符串转换为JSON对象
var obj = str.parseJSON(); //由JSON字符串转换为JSON对象
或者
var obj = JSON.parse(str); //由JSON字符串转换为JSON对象

2)使用toJSONString()或者全局方法JSON.stringify()将JSON对象转化为JSON字符串
var last=obj.toJSONString(); //将JSON对象转化为JSON字符
或者
var last=JSON.stringify(obj); //将JSON对象转化为JSON字符

14.get请求与post请求的区别,什么是跨域?

  1. get请求与post请求的区别
    ◆ 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
    ◆ 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
    也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。
  2. 什么是跨域?
    跨域:浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议有任一不同,都是跨域 。

15.简述一次完整的http请求过程

一次完成的http请求过程:
域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

16.http状态码的含义:200 204 301 304 403 404 500

200:OK,服务器成功处理了请求
301:重定向,请求的URL已移走
304:未修改,客户端的缓存资源是最新的 ,要客户端使用缓存
403:forbidden,请求被服务器拒绝了
404:Not Found,未找到资源
500:Internal Server Error,内部服务器错误,服务器遇到了一个错误,使其无法为请求提供服务

17.如何解决跨域问题?

1)跨域出现的原因:同源策略。
什么是同源策略:同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
同源三要素:协议、端口、域名。当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。请求就不被允许。

2)解决的跨域的方式有哪些?
反向代理跨域
CORS跨域
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
带cookie跨域请求:前后端都需要进行设置

18.你使用过哪些方法,来提高微信小程序的应用速度?

1)用户行为预测
在某些场景下,用户的行为可以预测,我们可以在用户还没点击的时候就预加载下个页面的数据。让下个页面秒开,进一步提升体验的流畅性。
2)减少默认data的大小
页面打开一个新页面时微信会深拷贝一个page对象,因此,应该尽量减少默认data的大小,以及减少对象内的自定义属性。以一个100个属性的data对象为例,在iphone6上,该页面的创建时间会因此增加150ms。
3)提高页面加载速度
从页面响应用户点击行为,开始跳转,到新页面onload事件触发,存在一个延迟,这个延迟大概在100-300ms之间(安卓响应比ios慢些)。
这个延迟说短不短,我们可以利用这段时间,预先发起新页面所需要的网络请求。这样一来,就节省了100-300ms(或者一个网络请求的时间)。

19.小程序与H5的区别是什么

1)开发工具不同。
2)区别于H5的开发工具+浏览器Device Mode预览的模式,小程序的开发基于自己的开发者工具,可以实现同步本地文件+开发调试+编译+预览+上传+发布等一整套流程。
3)开发语言不同。这点是非常重要的,直接否定客户所说的复制粘贴。
4)小程序自己开发了一套WXML标签语言和WXSS样式语言,并非直接使用标准的HTML5+CSS3。
5)组件封装不同。
6)小程序独立出来了很多原生APP的组件,在HTML5需要模拟才能实现的功能,小程序里可以直接调用组件。

20.小程序可以使用第三方组件吗?

第一步:
登录小程序后台管理>设置>第三方服务>添加插件
找到你需要的插件后添加
这时候点击插件的详情,我们可以看到AppID
拷贝AppID,我们等等会用到它
第二步:
微信小程序开发工具打开你的项目的app.json

"plugins": {
      "WechatSI": {
          "version": "0.0.7",
          "provider": "wx069ba97219f66d99"
      }
  }

21.小程序里事件的使用方式?

事件分为冒泡事件和非冒泡事件:
冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
ouchstart 手指触摸
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如弹窗和来电提醒
touchend 手指触摸动作结束
tap 手指触摸后离开
longtap 手指触摸后后,超过350ms离开

22.Vue组件之间的传值如何实现?

1)父组件向子组件传递数据
在 Vue 中,可以使用 props 向子组件传递数据。
2)子组件向父组件传递数据
子组件主要通过事件传递数据给父组件
3)子组件向子组件传递数据
Vue 没有直接子对子传参的方法,建议将需要传递数据的子组件,都合并为一个组件。如果一定需要子对子传参,可以先从传到父组件,再传到子组件。
为了便于开发,Vue 推出了一个状态管理工具 Vuex,可以很方便实现组件之间的参数传递
父子组件的话还可使用以下两种方式:
父传子props在子组件进行接收
子传父亲:在子组件中用$emit()进行数据传递

23.Vuex是做什么的

Vuex可以实现Vue中的数据状态管理。
Vuex是基于Vue框架的一个状态管理库。可以管理复杂应用的数据状态,比如兄弟组件的通信、多层嵌套的组件的传值等等。
Vuex流程理解:
Vuex中包括的对象有:state、mutations、actions、getters和modules。
在Vuex流程中涉及到的对象:Components、Actions、Mutations和State。
实现过程:
用户在component中通过操作dispatch触发了一个action,action就会commit一个mutation函数,从而mutate一个新的state,Vuex就会将新的state渲染(render)到component中,从而让界面更新。
如下几种属性:
state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步
actions => 像一个装饰器,包裹mutations,使之可以异步
modules => 模块化Vuex

24.不用Vuex会带来什么问题?

与数据存储不同,Vuex 解决的主要问题是不同组件间的通信,以达到对当前页面数据状态的管理。
既然是状态,它不会是持久化的,在页面刷新或关闭后,数据自动丢失。
如果组件比较少,完全可以不用 Vuex。而且,目前有很多基于 Vuex 的插件,结合 localStorage、sessionStorage、IndexDB 等,可以达到数据持久化的目的。

25.在vue里添加音频

方法1:将音频文件放置在static目录中,然后进行调用
方法2:给项目配置mp3格式的解析器 url-loader

26.MVVM模式

Model指的是js中的数据,如对象,数组等等。
View指的是页面视图
viewModel指的是vue实例化对象

27.Vue项目中代理配置

1)vue-cli中proxyTable配置接口地址代理示例
2)Webpack-dev-server的proxy用法(同vite)

dev: {
  ...
  proxyTable: {
    '/api': {
      target: 'http://www.abc.com',  // 目标接口域名
      changeOrigin: true,  // 是否跨域
      pathRewrite: {
        '^/api': '/api'   // 重写接口
      }
    }
   }
   ...
}

module.exports = defineConfig({  
//配置代理
  devServer: {
    // open: true,// 是否自动打开浏览器
    // port: 8080,// 设置项目端口号
    // https: false,// 是否开启https
    // hotOnly: false,
    // host:"0.0.0.0",//允许所有的主机访问当前项目
    proxy: {
      // 配置跨域
      '/api': { // 正则匹配到以这个开头的时候 就用代理
        target: 'http://www.xxxx.vip',// 指向的是目标服务器
        pathRewrite: { // 路径重写  /api开头的请求会去到target下请求
          '^/api': '',// 替换/api 为空字符
        },
        ws: true,
        changOrigin: true,// 是否开启代理
      }
    }
  }
})

28.怎么定义Vue-router的动态路由?怎么获取传过来的动态参数?

在router目录下的index.js文件中,对path属性加上/:id
使用router对象的params.id

22.Vue-router实现原理

前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来。
改变浏览器地址而不向服务器发出请求有两种方式:
1)在地址中加入#以欺骗浏览器,地址的改变是因为正在进行页内导航
2)使用H5的window.history功能,使用URL的Hash来模拟一个完整的URL

22.Vue-router有哪几种导航钩子

1)全局的
2)单个路由独享的
3)组件级的
每个钩子方法接收三个参数:
1)to: Route: 即将要进入的目标 路由对象
2)from: Route: 当前导航正要离开的路由
3)next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

23.Vue中路由之间的跳转如何实现

1)router-link 【实现跳转最简单的方法】
2)this.router.push({ path:’/user’}) 3)this.router.replace{path:‘/’ }

24.vue有哪些优点?

Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API。
Vue.js是一个构建数据驱动的 web 界面的渐进式框架。
Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。
Vue两大特点:响应式编程、组件化。
Vue的优势:轻量级框架、简单易学、双向数据绑定、组件化、视图、数据和结构的分离、虚拟DOM、运行速度快。

25.对Promise的理解

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

promise是为解决异步处理回调金字塔问题而产生的:
1)Promise对象的状态不受外界影响
A)pending 初始状态
B)fulfilled 成功状态
C)rejected 失败状态
Promise 有以上三种状态,只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态。
2)Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可逆,只能由 pending变成fulfilled或者由pending变成rejected

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容