HTML5+CSS3

1. SEO

  • SEO优化最重要三个要素
    • 标题
    • 关键词
    • 描述
  • 关键词挖掘
    • 头脑风暴、利用搜索引擎相关搜索(利用百度指数)、工具、长尾关键词
  • 网页结构布局优化
    • 控制首页链接数量、平扁化的目录层次、导航栏SEP优化、使用面包屑导航、页面不要加载太慢(尽量不要超过100k)、重要内容不要用js输出、减少使用iframe框架、谨慎使用display:none
  • 网页代码优化
    • title 标题:强调重点,尽量把重要关键字放在里面,关键字不要重复出现,做到每个页面title中不要设置相同内容
    • keyword标签:关键字,列出几个页面的重要关键字,切记过分堆砌
    • description标签:网页描述,需要高度概括网页内容,切记不要过长,过分堆砌关键字,每个页面也要有所不同。
    • body中的标签:尽量让代码语义化,在适当的位置使用适当的标签。比如h1-h6用于标题,nav用于导航。
    • a标签:页内链接,要加title说明,链接其他网站要加el="nofollow",放置蜘蛛爬外部链接就不回来了。
    • 正文标题:h1副标题用h2
    • 表格用caption标题标签
    • img应使用alt属性说明
    • Strong、em标签:需要强调时使用
    • 文本缩进不要使用 &nbsp 应css进行设置,版权符号不要使用 &copy可以直接使用输入法,打出banquan选择5 ©
    • 代码去冗余(代码精简、模块化)语义化、自动化工具(grunt/gulp/webpack)
  • HTTP状态码
    • 200(2开头表示页面响应成功)
    • 300(302、304重定向)
    • 404(页面不存在)
    • 500(5、6开头表示服务器错误)
  • 站群推广:淘宝客、镜像网站、站群模式、百度推广、百度指数、CNZZ(友盟+)、站长工具、爱站网

2. META标准

提供给页面一些元信息(名称/键值),有利于SEO

  • 属性值:
    • name 用于网站描述
    • http-equiv 没有name时可以采用这个属性的值
    • content 名称/值,可以是任何有效的字符串
    • Scheme 用于指定要翻译的属性值的方案

3. 盒子模型

盒子模型是由 content + padding + border + margin 组成

盒子模型有两种

  • IE盒子(怪异盒子)

    • 块级宽度 = width + margin
  • W3C盒(默认标准盒子)

    • 块级宽度 = width + padding + border + margin

4. CSS选择器

  • 基础
    • 通配符选择器 *
    • 标签选择器 div
    • 类选择器 class
    • id选择器 id
  • 关系
    • 相邻选择器 +
    • 兄弟选择器 ~
    • 子代选择器 >
    • 后代选择器 空格
    • 交集选择器 .
    • 并集选择器 ,
    • 属性选择器 []
  • 辅助
    • 伪类选择器 :
    • 伪元素选择器 ::

4个基础、7个关系、2个辅助

样式继承:字体样式、大小、颜色

不继承:宽高、内外边距、边框

5. CSS权重计算

  • 各选择器全值总览

    • 内联style,权值:1000
    • ID选择器,权值:0100
    • 类选择器,权值:0010
    • 标签选择器 & 伪元素选择器,权值:0001
    • 通配符、子选择器、相邻选择器等,权值:0000
    • 继承样式没有权值

    如果层级相同,则向后比较,层级不同时,层级高的权重大

6. 垂直水平居中

1. 已知宽度

<div style="position: relative">
    <div style="position: absolute; top: 50%; left: 50%; width:50px; height: 50px; margin-left: -25px; margin-top:-25px;">
        通过定位+偏移
    </div>
</div>
<hr>
<div style="position: relative">
    <div style="position: absolute; top: 0;bottom: 0;left: 0;right: 0; margin: auto; width:50px; height: 50px; ">
        通过定位+外边距自适应
    </div>
</div>

2. 未知宽度

<div style="display: flex; justify-content: center; align-items: center">
    万能的 flex
</div>
<hr>
<div style="position: relative">
    <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%)">
        通过定位+偏移
    </div>
</div>

7. 定位

  • relative 相对定位,相对于自身位置进行定位。
  • absolute 绝对定位,相对于父元素的位置进行定位,static除外
  • fixed 固定定位,相对于浏览器进行定位
  • inherit 继承,继承父元素position的属性值
  • static 默认值,没有定位,正常文档流

8. 清浮动浮动

<div>
    <p style="float: left">标签清浮动</p>
    <div style="clear: both;"></div>
</div>

<div style="overflow: hidden">
    <p style="float: left">触发BFC清浮动</p>
</div>

<style>
  .parent::after{
    content: '';
    display: table;
    height: 0;
    visibility: hidden;
    clear: both;
  }
</style>

<div class="parent">
    <p style="float: left">伪元素清浮动,伪元素只能设置为 after,设置为 before 会失效。</p>
</div>

注意:设置为浮动元素后,该元素的 display 会变成 block

9. 弹性布局flex

  • 容器属性
    • flex-derection 主轴row 交叉轴 column
    • flex-wrap 不换行nowrap 换行wrap
    • flex-flow 切换轴 换行
    • justify-content 主轴对齐方式
    • align-items 交叉轴对齐方式
    • align-content 多根轴线对齐方式,只有一条则不起作用
  • 项目属性
    • order 排序,数值越小越靠前,默认0
    • flex-grow 项目放大比,默认0(存在剩余空间,就不放大)
    • flex-shrink 项目缩小比,默认1(空间不足,项目将缩小)
    • flex-basis 分配空间前,先占据主轴空间。默认auto
    • flex 项目放大grow 项目缩小shrink 分配空间basis
    • align-self 单个项目与其他不一样对齐方式

10. 三栏布局(圣杯)

Flex 三栏布局
<div style="display: flex; text-align: center">
    <div style="flex-basis: 200px"></div>
    <div style="flex: 1; background-color: red;"></div>
    <div style="flex-basis: 200px"></div>
</div>
<hr>
Position 三栏布局
<div style="position: relative; text-align: center">
    <div style="width: 200px; position: absolute; left: 0;top: 0;"></div>
    <div style="margin: 0 200px; background-color: red;"></div>
    <div style="width: 200px; position: absolute; right: 0;top: 0;"></div>
</div>
<hr>
Float 三栏布局
<div style="overflow: hidden; text-align: center">
    <div style="width: 200px; float: left;"></div>
    <div style="width: 200px; float: right;"></div>
    <div style="margin: 0 200px; background-color: red;"></div>
</div>

11. 自适应填补

<!-- position 实现填补100px 下面的空白-->
<div style="height: 500px; background-color:pink; position: relative;">
    <div style="height: 100px; background-color:red;"></div>
    <div style="position:absolute; background-color:blue; top: 100px;left: 0;bottom: 0;">1</div>
</div>
<hr>

<!-- flex 实现填补100px 下面的空白-->
<div style="height: 500px; background-color:pink; display: flex; flex-direction: column;">
    <div style="height: 100px; background-color:red;"></div>
    <div style=" background-color:blue; flex:1;"></div>
</div>

12. 常见浏览器内核和理解

  • 常见内核
    • Webkit 内核 chrome
    • gecko 内核 fixfox
    • Opera 内核 opera
    • trident 内核 ie
  • 内核分两部分
    • 渲染引擎,将代码转换为页面
    • JS引擎,解析和执行 js 代码实现页面动态效果

13. 瀑布流

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .waterfall {
            width: 80%;
            column-count: 4;
            column-gap: 10px;
            margin: 0 auto;
        }

        .item {
            padding: 10px;
            margin-bottom: 10px;
            break-inside: avoid;
            border: 1px solid #ddd;
        }

        .item img {
            width: 100%;
        }

        .item p {
            line-height: 30px;
        }

        @media (min-width: 992px) and (max-width: 1300px) {
            .waterfall {
                column-count: 3;
            }

            p {
                color: red;
            }
        }

        @media (min-width: 768px) and (max-width: 991px) {
            .waterfall {
                column-count: 2;
            }

            p {
                color: orange;
            }
        }

        @media (max-width: 767px) {
            .waterfall {
                column-count: 1;
            }
        }
    </style>
</head>
<body>
<div class="waterfall">
<!--    <div class="item">-->
<!--        <img src="" alt="">-->
<!--        <p></p>-->
<!--    </div>--> 
 </div>
<script src="https://cdn.bootcss.com/Mock.js/1.0.1-beta3/mock-min.js"></script>
<script>
    // 使用mock模拟数据
    var mockList = Mock.mock({
        "waterfall|200": [
            {
                "img": '@image',
                "context": '@cparagraph(5,10)'
            }
        ]
    })

    mockList.waterfall.forEach(item => {
        var div = document.createElement('div')
        div.setAttribute('class', 'item')
        var img = document.createElement('img')
        img.setAttribute('src', item.img)
        var p = document.createElement('p')
        p.innerText = item.context
        div.appendChild(img)
        div.appendChild(p)
        document.querySelector('.waterfall').appendChild(div)
    })

</script>
</body>
</html>

14. H5 新特性

  • Html5

    新增特性:

    • canvas
    • audio/video
    • 对本地离线存储更好的执行
    • localStorage长期存储数据,浏览器关闭后数据也不丢失(只能手动删除)
    • sessionStorage在浏览器关闭后自动删除
    • 语义化更好的标签
    • 新的表单控件
    • 新的通信技术(WebWorker,WebSocket)
  • Html5移除了纯表现型的元素,和对可用性具有负面影响的元素

由于IE6/7/8都支持document.createElement来创建元素,因此可以通过这个方法来让浏览器兼容Html5新标签

15. Css3 新特性

  • 各种css选择器
  • 圆角border-radius
  • 多列布局 column
  • 文本效果 text-shadow
  • 线性渐变linear-gradients
  • 转换 transform
  • 过渡transition
  • 动画animation
  • 弹性flex布局
  • 媒体查询 @media

JavaScript

1. 数据类型

简单数据类型:Number、String、Boolean、Null、Undefined、Symbol

复杂数据类型:Object

2. this指向

// 谁调用它,this就指向谁
// 1.全局调用
function foo() {
    console.log(this.a)
}
var a = 2;
foo();

// 2.隐式调用
var obj1 = {
    a:1,
    foo
}
var obj2 = {
    a:2,
    foo
}
obj1.foo() // 1
obj2.foo() // 2

// 3.显示调用
function foo2() {
    console.log(this.a)
    bar.apply({a:2},arguments)
}
function bar(b) {
    console.log(this.a+b)
}
var a = 1;
foo2(3)

// call/apply/bind区别
// call 区别在于接受参数不同,call是依次接受参数,apply接受数组
// bind 创建一个新的包装函数,它不会立即执行

3. 闭包

// 闭包是指有权访问函数内部作用域的变量的函数。
// 三个特性
// 1.闭包可以访问当前函数以外的变量
function getOuter() {
    var data = 'outer'
    function getData(str){
        console.log(str + data) // 访问外部变量 data
    }
    return getData('I am ')
}
getOuter()
// 2.即使函数返回,闭包仍然能访问外部函数定义的变量
function getOuter2() {
    var data = '815'
    return function (str) {
        console.log(str + data)
    }
}
var day = getOuter2()
day('今天')
day('明天')
// 3.闭包可更新外部变量的值
function updateCount() {
    var data = '815'
    return function (count) {
        data = count;
        console.log( data)
    }
}
var count = updateCount()
count(911)
count(123)

4. 浅拷贝、深拷贝

<script src="https://cdn.bootcss.com/lodash.js/4.17.15/lodash.min.js"></script>
<script>
    // 浅拷贝
    // 1.Object.assign({},obj)
    // 2.展开运算符...
    // 3.concat()
    // 4.slice

    // 深拷贝
    // 1.JSON.parse(JSON.stringify) 无法拷函数
    // 2.手写递归弱深拷贝【时间、正则都不行】
    function deepCopy(obj) {
        let result = Array.isArray(obj) ? [] : {};
        if (obj && typeof obj === 'object') {
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    if (obj[key] && typeof obj[key] === "object") {
                        result[key] = arguments.callee(obj[key])
                    } else {
                        result[key] = obj[key]
                    }
                }
            }
        }
        return result;
    }

    var obj1 = {
        a: 1,
        b: [1, 2, 3, 4, 5],
        c: {name: '张三', age: 11, f: {g: 1}},
        fn: function () {
            console.log('hahaha')
        },
        d: new Date(),
        f: /aaa/
    }

    var obj2 = deepCopy(obj1)
    console.log(obj2)
    console.log(obj2.c.f === obj1.c.f)


    // 3.lodash库中的cloneDeep(),这个牛逼。
    var obj3 = _.cloneDeep(obj1)
    console.log(obj3)
    console.log(obj3.c.f === obj1.c.f)

</script>

5. 原型、构造函数、实例

原型是简单对象,用于实现对象的属性继承

构造函数可以通过new来创建一个对象

实例通过构造函数和new创建出来的,便是实例

  • 三者关系:
    • 构造函数.prototype === 原型
    • 原型.constructor === 构造函数
    • 实例._proto_ === 原型

6. 原型链

每个对象都有创建该对象的构造函数原型,将对象链接起来组成了原型链。可以实现继承和共享属性 。

  • 原型链中的机制
    • 属性查找机制:当查找对象属性时,不存在该属性,会沿着原型链往上一级查找,找到则输出,否则继续找。直到顶部 Object 还没找到,输出 undefined
    • 属性修改机制:只修改实例对象本身的属性,如果不存在则添加,如果修改原型属性,需通过Prototype属性,但这样修改会导致所有继承于这个对象原型的实例属性发生改变。
  • 原型如何实现继承
function Parent(value) {
    this.val = value
}

Parent.prototype.getValue = function () {
    console.log(this.val)
}

function Child(value) {
    Parent.call(this, value)
}

// 将父类的原型赋值给子类,并将原型的constructor修改为子类
Child.prototype = Object.create(Parent.prototype, {
    constructor: {
        value: Child,
        enumerable: false,
        writable: true,
        configurable: true
    }
})
const child = new Child(1)
console.dir(child)
child.getValue()  // 1
console.log(child instanceof Parent) // true

7. 数组API

  • 迭代
    • every() 全返回 true 则返回 true
    • filter() 每项运行函数,过滤不需要的项,组成新数组
    • forEach() 迭代,无法中途跳出循环,break/continue/return 都失效
    • map() 将每项运行函数的结果,组成数组
    • some() 每项运行函数,如果对任意一项返回 true ,则返回 true
  • 其他
    • join() 通过指定连接符生成字符串
    • push/pop 尾部推入/尾部弹出
    • shift/unshift 头部弹出/头部推入
    • sort/reverse 排序/反转
    • concat 连接数组,返回新数组(浅拷贝)
    • slice 截断数组,返回新数组(浅拷贝)
    • splice 从 start 开始,删除 num 个元素,并插入 arg,返回新数组
    • indexOf 找元素,返回下标索引
    • reduce(fn(prev, cur), defaultPrev) 归并数组,pre累计,cur当前,defaul初始值

8. 字符串API

  • concat 连接字符串
  • indexOf 检索字符串
  • match/replace/search 正则匹配/替换/索引
  • slice 截取字符串,返回被截取片段
  • substr 提取字符串中指定数目字符
  • substring 截取字符串两个指定索引号之间的字符
  • split 将字符串通过指定字符串分割为数组
  • toString 返回字符串
  • valueOf 返回某个字符串的原始值

9. 数组去重

简单去重

// 最简单数组去重法
/*
* 新建一新数组,遍历传入数组,值不在新数组就push进该新数组中
* IE8以下不支持数组的indexOf方法
* */
function uniq(array){
    var temp = []; //一个新的临时数组
    for(var i = 0; i < array.length; i++){
        if(temp.indexOf(array[i]) == -1){
            temp.push(array[i]);
        }
    }
    return temp;
}

var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));

Includes去重

function uniq(array){
    var temp = []; //一个新的临时数组
    for(var i = 0; i < array.length; i++){
        if(!temp.includes(array[i])){
            temp.push(array[i]);
        }
    }
    return temp;
}

ES6 Set去重

function unique (arr) {
  return Array.from(new Set(arr))
}

ES6 Set 简化去重

[...new Set(arr)] 

10. 数组排序

参考:https://www.cnblogs.com/bear-blogs/p/10808399.html

冒泡排序

// 原理:从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素

插入排序

// 原理:第一个元素默认是已排序元素,取出下一个元素和当前元素比较,如果当前元素大就交换位置。那么此时第一个元素就是当前的最小数,所以下次取出操作从第三个元素开始,向前对比,重复之前的操作。

选择排序

// 原理:遍历数组,设置最小值的索引为 0,如果取出的值比当前最小值小,就替换最小值索引,遍历完成后,将第一个元素和最小值索引上的值交换。如上操作后,第一个元素就是数组中的最小值,下次遍历就可以从索引 1 开始重复上述操作。

快速排序

// 原理:在数据集之中,找一个基准点,建立两个数组,分别存储左边和右边的数组,利用递归进行下次比较。

希尔排序

/* 原理:
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
*/

归并排序

/*
原理:
(1) 把长度为n的输入序列分成两个长度为n/2的子序列;
(2)对这两个子序列分别采用归并排序;
(3) 将两个排序好的子序列合并成一个最终的排序序列。
*/

11. 函数防抖(debounce) 与 函数节流(throttle)

如果事件处理函数(click)调用频率无限制,会加重浏览器的负担,导致用户体验非常差,那么可以采用debounce(防抖)和 throttle(节流)的方式来减少调用频率,同时又不影响实际效果。

  • 函数防抖(debounce)
    • 当持续触发事件时,一定时间段内没有再次触发事件,事件处理函数才执行一次,如果设定的事件到来之前,又触发事件,就开始重新开始延时(即设定的时间内触发事件将无效)
    • 例如:当持续触发scroll事件时,事件处理函数handle只在停止滚动1000毫秒之后才会调用一次,即持续触发滚动事件的过程中,handle一直没有执行
function debounce(handle){
    let timeout = null // 创建一个标记用来存放定时器
    return function(){
        clearTimeout(timeout) // 每当用户调用的时候把前一个定时器清空
        timeout = setTimeout(() => {
            handle.apply(this,arguments)
        },500) // 500ms后触发,期间再次调用,则重新计算延时
    }
}
function sayHi(){
    console.log('防抖成功')
}
var btn = document.getElementById('button')
btn.addEventListener('click',debounce(sayHi))
  • 函数节流(throttle)
    • 当持续触发事件时,保证一定时间段内只调用一次事件处理函数(通过判断是否达到一定条件来触发函数)
    • 第一种方式:通过时间戳来判断是否已到可执行时间,记录上一次执行的时间戳,然后每次触发事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经到达设置的时间差,如果是则执行,并更新上次执行的时间戳
    • 第二种方式:使用定时器
function throttle(handle){
    let canRun = true // 通过闭包保存一个标记,不被回收
    return function(){
        if(!canRun) return // 在函数头部判断标记是否为true,为false时不允许调用handle
        canRun = false // 设置标记为false
        setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
          handle.apply(this, arguments)
          // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
          canRun = true
        }, 500);
    }
}
function sayHi() {
  console.log('节流成功');
}
var btn = document.getElementById('button');
btn.addEventListener('click', throttle(sayHi)); // 节流

12. 阻止事件冒泡、阻止默认行为

  • 阻止事件冒泡
非IE浏览器:event.stopPropagation()
IE浏览器:window.event.cancelBubble = true

function stopBubble(e){
    // 如果提供了事件对象,则是非IE浏览器下
    if(e && e.stopPropagation){
        // 因此它支持W3C的stopPropagation()方法
        e.stopPropagation()
    }else{
        // IE浏览器下,取消事件冒泡
        window.event.cancelBubble = true
    }
}
  • 阻止默认行为
非IE浏览器:event.preventDefault()
IE浏览器:window.event.returnValue  = false

function stopDefault(e) {
  //阻止默认浏览器动作(W3C)
  if (e && e.preventDefault) e.preventDefault();
  //IE中阻止函数器默认动作的方式
  else window.event.returnValue = false;
  return false;
}

13. JS兼容各种浏览器版本的事件绑定

/**
 * 兼容低版本IE,element为需要绑定事件的元素,
 * eventName为事件名(保持addEventListener语法,去掉on),fun为事件响应函数
 */

function addEvent(element, eventName, fun){
    if(element.addEventListener){
        element.addEventListener(eventName, fun, false)
    }else{
        element.attachEvent('on' + eventName, fun)
    }
}

14. JS创建、移除、复制、查找节点

  • 创建新节点
createDocumentFragment() - 创建一个DOM片段
createElement() - 创建一个具体的元素
createTextNode() - 创建一个文本节点
复制代码
  • 添加、移除、替换、插入
appendChild() - 添加子节点
removeChild() - 移除子节点
replaceChild() - 替换子节点
insertBefore() - 插入
复制代码
  • 查找
getElementsByTagName() - 通过标签名称
getElementsByName() - 通过元素的Name属性
getElementById() - 通过元素id,具有唯一性
querySelect() - 通过指定元素 标签/class/id/属性选择器等等进行匹配
querySelectAll()

15. Js的事件循环(Event Loop)机制

JavaScript 是一门单线程语言【反正没看懂】

1. 执行栈与任务队列

由于 JS 是单线程语言,当遇到异步任务(如Ajax)时,不可能一直等到异步执行完毕后,再继续往下执行,因为这段时间浏览器处于空闲的,会导致浪费巨大的资源。

2. 执行栈

当执行某个函数、事件(指定过回调函数)时,就会进入执行栈中,等待主线程读取

3. 主线程

主线程和执行栈不同,主线程规定了现在要执行,执行栈的哪个事件

4. 主线程循环

当遇到一个异步事件后,并不会一直等异步事件执行返回结果,而是会将这个事件挂在与执行栈不同的队列中,这个队列称为任务队列 TaskQueue

当主线程将执行栈中的所有代码都执行完后,主线程会去查看任务队列中是否存在任务。如果存在,那么主线程就会依次执行那些任务队列中的回调函数。

5. Javascript异步执行的运行机制

  • 所有任务都在主线程上执行,形成一个执行栈
  • 主线程之外,还存在一个任务队列(TaskQueue。只要异步任务有了返回结果,就在任务队列之中放置一个事件
  • 当执行栈中的所有同步任务执行完毕,就会去查看任务队列,那些对应的异步任务,结束等待状态,进入执行栈并开始执行
  • 主线程会不断的重复第三点。

6. 宏任务与微任务

异步任务可以分为两类,不同类型的API注册的任务会依次进入到各自对应的队列中,然后等待事件循环(EventLoop)将它们依次压入执行栈中执行

  • 宏任务MacroTask
script(整体代码)
setTimeout
setInterval
setImmediate
UI渲染
I/O流操作
postMessage
MessageChannel
  • 微任务MicroTask
Promise
MutaionObserver
process.nextTick

1. 事件循环(EventLoop)

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:

  • 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
  • 检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
  • 更新render(每一次事件循环,浏览器都可能会去更新渲染)
  • 重复以上步骤

综上所述

宏任务 → 所有微任务 → 下一个宏任务

#Vue

16. 数组/对象【增加/删除】不响应解决

// 数组 
this.$set(this.todoList, 0, { name: "123" }); // 修改数组0的值
this.todoList.splice(0, 1, { name: "123" }); // 修改数组0的值
this.todoList.splice(this.todoList.length - 3); // 修改数组长度

// 对象
this.$set(this.todoObject, "love", "喝水"); // 添加 love 属性 
this.$delete(this.todoObject, "name"); // 删除 name 属性

留言

暂无评论

我要发表看法