js之DOM详解

导语:之前说过BOM是游览器对象模型,提供了很多操作游览器、操作窗口的属性和方法。那从今天开始讲述有关DOM的知识点。

# DOM的概念

DOM英文全称是Document Object Model,简称DOM,含义是文档对象模型。这个是核心的js语法所没有的,是对核心js的扩展。

每个游览器窗口都显示一个HTML文档,那每个窗口就有一个Window对象,而它的属性document就是引用的Document对象。

DOM的发展也是一步步来的,从最开始的Netscape和ie指定的一些DOM操作属性和方法;到1998年10月,W3C组织制定了1级DOM标准,到2000年发布了2级DOM标准,DOM的功能也在不断的扩展补充,从最开始的静态显示到后面的动态读写,逐步完善起来。

注意:这里指的文档是HTML文档,HTML文档被人们习惯性的叫做网页。

# 0级DOM(旧规范)

# 属性

  • title,文档的标题;位于<title></title>之间的文本;
  • cookie,一个特殊属性,允许js可以读写HTTP Cookie;
  • domain,可以使在同一域中的相互信任的服务器之间进行网页交互避免同源策略安全性的限制;
  • lastModified,文档的修改日期;
  • referrer,文档的URL,包含游览器带到当前文档的链接;
  • URL,等同于Window对象的属性location.href;
  • location,等价于属性URL,(现已废弃);
  • bgColor,文档的背景颜色,(现已废弃);

例如:

console.log('文档的标题:',document.title); // 文档的标题: Document
console.log('cookie值:',document.cookie); // cookie值:
console.log('文档的域:',document.domain); // 文档的域: localhost
console.log('文档的最新修改日期:',document.lastModified); // 文档的最新修改日期: 01/27/2019 09:02:46
console.log('文档的链接:',document.referrer); // 文档的链接: http://localhost:2009/test.html
console.log('文档的背景颜色:',document.bgColor = '#f808080'); // 文档的背景颜色: #f00
console.log('文档的当前链接:',document.location); // 文档的当前链接: Location {replace: ƒ, assign: ƒ, href: "http://localhost:2009/test.html"…}
console.log('文档的链接地址:',document.URL); // 文档的链接地址: http://localhost:2009/test.html
1
2
3
4
5
6
7
8

# 案例

防止外链,当游览者在网站的其他页面,可以跳转到当前主页,再去访问其他页。

// 检测是否本网站域名,跳转到本网站主页
if (document.referrer == '' || document.referrer.indexOf("example.com") == -1) {
  document.location = 'http://www.example.com';
}
1
2
3
4

# 方法

这个方法就是向当前文档中插入一个文本,它有时会覆盖到已存在的一些文本,所以最好在文档解析完成后再调用此方法。

和open()以及close()方法一起组合会更加好。

例如:

function hello() {
  var d = window.document;
  d.open();
  d.write('<h2>Hello,world!</h2>');
  d.close();
}
hello();
1
2
3
4
5
6
7

# 文档对象集合

这个也是0级DOM中遗留下来的一些属性和方法,不过它们的值都是数组,可以访问文档的 某些元素。

  • anchors[],

Anchors对象的一个数组,该对象代表文档中的锚,就是<a>的name属性。

  • links[],

Links对象的一个数组,该对象代表文档中的超文本链接,就是<a>href属性,也包括通过<area>标记创建的热点链接属性。

  • images[],

Images对象的一个数组,该对象代表文档中的<img>元素,Images对象的src属性是可以赋值的。可以实现图像翻滚和简单动画。

  • forms[],

Forms对象的一个数组,该对象代表文档中的<form>表单元素,每个Forms对象都有自己的一个名为elements[]的集合属性。

# 案例展示

  • 获取表单元素

文档中有一个表单名称叫login,有一个表单元素叫username,那么就可以使用命名Document对象的方法,直接用元素的name属性来命名。

<form name="login">
  <input type="text" name="username">
</form>
1
2
3
// 第一个就是表示这个表单,表单的可以用forms[按照文档的顺序第几个表单索引],表单的元素用elements[这个表单的按照表单里面的顺序第几个元素]。
var login = document.forms[0] || document.login;
var username = document.forms[0].elements[0] || document.login.username;
console.log(login,username);
1
2
3
4

注意:如果文档中有两个表单name一样,它会返回一个数组,所以要尽量保持name属性在文档中的唯一性。

  • 获取文档中所有的链接
<a href="#1">链接1</a>
<a href="#2">链接2</a>
<a href="#3">链接3</a>
1
2
3
var links = document.links;
console.log('所有的链接:',links); // 所有的链接: HTMLCollection(7) [a, a, a, a, a, a, a]
1
2

# 1级DOM(W3C标准)

之前的DOM都是被一些公司操作着定义着用法内容,是老早的约定俗成的东西,但是缺乏规范和标准。W3C组织在1998年10月发布了DOM的标准,被后人成为1级DOM标准,它规定了DOM的一些属性和方法,包括DOM树,节点操作,属性操作,以及对html文档元素进行的创建、获取、修改、删除以及添加和插入等操作方法。

# DOM树

html是嵌套的有层级关系的一种超文本标记语言,在DOM就表示对象的一棵树。这个树从树根开始,到下面的元素节点,文本节点,组成了一颗树。

例如:这是最简单的一颗DOM树。

<html>
<head>
  <title>DOM树</title>
</head>
<body>
  <h2>DOM树</h2>
  <p>你好,DOM树!</p>
</body>
</html>
1
2
3
4
5
6
7
8
9

# DOM节点

每一个元素都是一个节点,有元素节点,文本节点等,每个节点都由Node对象定义类型,用nodeType属性来表示。

节点列表:

序号|接口|nodeType常量|nodeType值 1|Element|Node.ELEMENT_NODE|1 2|Text|Node.TEXT_NODE|3 3|Document|Node.DOCUMENT_NODE|9 4|Comment|Node.COMMENT_NODE|8 5|DocumentFragment|Node.DOCUMENTFRAGMENT_NODE|11 6|Attr|Node.ATTRIBUTE_NODE|2

DOM树根部的Node是一个Document对象,这个对象的documentElement属性引用了一个Element对象,代表了文档的根元素。那以此类推,代表body这个节点的就是body。

补充:DOM的节点总的来说分为四个大的板块。

  • 第一个是Document代表文档节点,就是HTMLDocument;
  • 第二个是CharacterData代表就是文本和注释节点,TextComment;
  • 第三个就Element代表着元素节点HTMLElement,比如像head、body、title等。
  • 第四个是属性节点Attr,包括元素的各个属性。

节点还有父节点、子节点、兄弟节点等关系的节点,这个关系的节点都有对应的元素。

  • 父节点和元素,parentNodeparentElement
  • 子节点和元素,childNodes,children;
  • 当前节点的第一个子节点和元素,firstChild,firstElementChild; 当前节点的最后一个子节点和元素,lastChild,lastElementChild; 上一个兄弟节点和元素,previousSibling,previousElementSibling; 下一个兄弟节点和元素,nextSibling,nextElementSibling;

# 节点案例

获取一个元素的各类节点以及元素

这个案例就是如何表示一个元素的各类节点。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>DOM树</title>
</head>
<body>
  <h2>DOM树</h2>
  <h3>兄弟</h3>
  <p>
    <span>你好,DOM树!</span>
    <span>你好,DOM节点!</span>
    <span>你好,DOM节点对应的元素!</span>
  </p>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var helem = document.getElementsByTagName('h3')[0];
var pelem = document.getElementsByTagName('p')[0];
//1.helem的父节点,爷爷节点以及元素。
var parent = helem.parentNode;
var grandParent = parent.parentNode;
var parentElem = helem.parentElement;
var grandParentElem = parentElem.parentElement;
console.log(parent,grandParent,parentElem,grandParentElem);

//2.helem的子节点,第一个和最后一个子节点以及元素。
var childs = pelem.childNodes;
var firstChild = pelem.firstChild;
var lastChild = pelem.lastChild;
var childsElem = pelem.children;
var firstChildElem = pelem.firstElementChild;
var lastChildElem = pelem.lastElementChild;
console.log(childs,firstChild,lastChild,childsElem,firstChildElem,lastChildElem);  

//3.helem的兄弟节点,下一个兄弟节点,上一个兄弟节点以及元素。
var nextSibling = helem.nextSibling;
var previousSibling = helem.previousSibling;
var nextSiblingElem = helem.nextElementSibling;
var previousSiblingElem = helem.previousElementSibling;
console.log(nextSibling,previousSibling,nextSiblingElem,previousSiblingElem);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 属性操作

元素的属性操作方法:

  • 获取属性getAttribute();
  • 设置属性setAttribute();
  • 移除属性removeAttribute();

例如:一个链接元素的属性操作。由于还没有涉及到元素的一些操作,这里还是以0级DOM的方法来做。

<a href="">属性操作</a>
1
//获取元素
var linka = document.links[0];

//设置属性
linka.setAttribute('title','属性提示语');
linka.setAttribute('class','属性类');
console.log(linka); // <a href="" title="属性提示语" class="attr">属性操作</a>

//获取属性
linka.getAttribute('class');
console.log(cls); // attr

//移除属性
linka.removeAttribute('title');
console.log(linka); // <a href="" class="attr">属性操作</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 元素的操作

下面就是对html文档元素进行的创建、获取、修改、删除以及添加和插入等操作方法。

# 获取元素

获取元素有以下几种方法,都是Document对象下面的方法。分别从不同的方面进行获取元素,如通过id获取元素;通过标签名称获取元素,这个获取到就是类数组,会返回一个类似于数组的结果。

  • 通过元素的id名称来获取:document.getElementById(<id名称>);
  • 通过元素的标签名称来获取:document.getElementsByTagName(<标签名称>);
  • 通过元素的name属性名称来获取:document.getElementsByName(<name属性名称>);
  • 通过元素的class名称来获取:document.getElementsByClassName(<class名称>);

注意:除了id以外,其他的都是类数组,可以用下标来表示某个元素。

例如:获取链接和输入框。

<a id="food" class="myfood" href="#1">香蕉</a>
<a class="myfood" href="#2">苹果</a>
<a class="myfood" href="#3">葡萄</a>
<input type="text" name="username">
1
2
3
4
//通过id
var banana = document.getElementById('food');
console.log(banana); // <a id="food" class="myfood" href="#1">香蕉</a>

//通过标签名称
var links = document.getElementsByTagName('a');
console.log(links); // HTMLCollection(4) [a.attr, a#food.myfood, a.myfood, a.myfood, food: a#food.myfood]

// 通过name属性名称
var username = document.getElementsByName('username');
console.log(username); // NodeList [input]

// 通过class名称
var myfood = document.getElementsByClassName('myfood');
console.log(myfood); // HTMLCollection(3) [a#food.myfood, a.myfood, a.myfood, food: a#food.myfood]

// 获取类数组的第一个
var myfoodOne = document.getElementsByClassName('myfood')[0];
console.log(myfoodOne); // <a id="food" class="myfood" href="#1">香蕉</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 创建元素

这个就是用document的方法来创建一个标签元素或者一个文本元素。

方法就是:

标签元素:document.createElement('<元素名称>'); 文本元素:document.createTextNode('<文本内容>');

例如:创建一个a标签,还有一个内容为你好,元素的文本。

// 创建链接元素
var linka = document.createElement('a');
console.log('linka:',linka); // linka: <a>​</a>​

// 创建打招呼文本
var txta = document.createTextNode('你好,元素');
console.log('txta:',txta); // txta: "你好,元素"
1
2
3
4
5
6
7

# 添加元素

这个就是把一个元素或者文本添加到另一个元素的内容里面去。

用法:<另一个元素>.appendChild(<要添加的元素>)

例如:把一个文本添加到一个链接中。

// 创建链接元素
var linka = document.createElement('a');

// 创建打招呼文本
var txta = document.createTextNode('你好,元素');

//添加元素
linka.appendChild(txta);
console.log('linka:',linka); // linka: <a>​你好,元素​</a>​
1
2
3
4
5
6
7
8
9

# 修改元素

修改元素的属性可以使用上面讲到的属性操作方法,这里主要讲修改元素的内容。

修改元素的内容有以下几种方法:

  • 往元素里面添加新的文本内容:<要修改的元素>.innerText = '<新的内容>'; +往元素里面添加新的html元素和内容:<要修改的元素>.innerHTML = '<新的内容>';

注意:它们两个的区别就是innerText只是修改元素的内容文本,相当于创建了一个文本节点,然后添加到这个元素中,改变的只有元素的内容;而innerHTML,不仅修改元素的内容文本,还可以添加嵌套的元素和元素自己的属性和内容,相当于创建了一个元素和文本节点,并且赋予元素属性和属性值,然后添加到这个要修改的元素中。

例如:

修改段落标签的内容,往段落标签里面添加一个链接,链接的class叫linka,href属性值叫#1,内容叫我是新链接。

<p id="con"></p>
<a id="linkb" href="#1"></a>
1
2
//只添加文本
var linkb = document.getElementById('linkb');
linkb.innerText = '你好,我的朋友!';
console.log(linkb); // <a id="linkb" href="#1">你好,我的朋友!</a>
//添加元素和文本
var con = document.getElementById('con');
con.innerHTML = '<a class="linka" href="#1">我是新链接</a>';
console.log(con); // <p id="con"><a class="linka" href="#1">我是新链接</a></p>
1
2
3
4
5
6
7
8

其实也可以使用上面的方法来实现,不过比较麻烦一点。

var con = document.getElementById('con');
var linkb = document.createElement('a');
linkb.setAttribute('href','#1');
linkb.setAttribute('class','linka');
var txtb = document.createTextNode('我是新链接');
linkb.appendChild(txtb);
con.appendChild(linkb);
1
2
3
4
5
6
7

# 插入元素

插入元素就是把一个子元素插入到父元素中,方法就是<父元素>.insertBefore(<子元素>,<插入子元素的位置>)

例如:往一个列表中插入一个子元素。

<ul id="list">
  <li>苹果</li>
  <li>香蕉</li>
  <li>梨子</li>
</ul>
1
2
3
4
5
var list = document.getElementById('list');
var lia = document.createElement('li');
var txtc = document.createTextNode('葡萄');
var lastElem = list.lastElementChild;
lia.appendChild(txtc);
list.insertBefore(lia,lastElem);
console.log(list); // <ul id="list"><li>苹果</li><li>香蕉</li><li>葡萄</li><li>梨子</li></ul>
1
2
3
4
5
6
7

# 替换元素

替换元素就是把当前元素的子元素给替换成另外一个子元素,方法就是<父元素>.removeChild(<要替换的子元素>)

例如:从列表中把苹果替换成葡萄。

<ul id="list">
  <li>苹果</li>
  <li>香蕉</li>
  <li>梨子</li>
</ul>
1
2
3
4
5
var list = document.getElementById('list');
var apple = list.firstElementChild;
var putao = document.createElement('li');
var txta = document.createTextNode('葡萄');
putao.appendChild(txta);
list.replaceChild(putao,apple);
console.log(list); // <ul id="list"><li>葡萄</li><li>香蕉</li><li>梨子</li></ul>
1
2
3
4
5
6
7

# 克隆元素

克隆元素其实就是复制一个元素,分为以下两种:

  • 深克隆,又叫深拷贝。就是既克隆元素本身,又克隆元素的子节点和节点的内容。
  • 浅克隆,又叫浅拷贝。只是克隆节点本身,不包括节点的内容。

方法就是:<要克隆的节点>.cloneNode(<deep: boolean>),这个参数是接收一个布尔值,true就是深拷贝,false就是浅拷贝。

例如:克隆一个苹果。

<ul id="list">
  <li>苹果</li>
  <li>香蕉</li>
  <li>梨子</li>
</ul>
1
2
3
4
5
var list = document.getElementById('list');
var apple = list.firstElementChild;
//浅拷贝
var appleOne = apple.cloneNode(false);
list.appendChild(appleOne);
console.log(list); //<ul id="list"><li>苹果</li><li>香蕉</li><li>梨子</li><li></li></ul>

//深拷贝
var appleOne = apple.cloneNode(true);
list.appendChild(appleOne);
console.log(list); //<ul id="list"><li>苹果</li><li>香蕉</li><li>梨子</li><li>苹果</li></ul>
1
2
3
4
5
6
7
8
9
10
11

# 删除元素

删除元素就是把一个子元素从到父元素中删去,方法就是<父元素>.removeChild(<要删除的子元素>)

例如:从列表中删除最后一个子元素。

<ul id="list">
  <li>苹果</li>
  <li>香蕉</li>
  <li>梨子</li>
</ul>
1
2
3
4
5
var list = document.getElementById('list');
var lastElem = list.lastElementChild;
list.removeChild(lastElem);
console.log(list); // <ul id="list"><li>苹果</li><li>香蕉</li></ul>
1
2
3
4

温馨提示:上面这些操作可以写成函数封装起来,日后使用比较方便了。

比如说获取元素是经常用到的,就写一个函数就可以了。

//获取元素
function getElem(name) {
  var result;
  var $d = document;
  if (name.indexOf('#') != -1) {
    name = name.split('#')[1];
    result = $d.getElementById(name);
  } else if (name.indexOf('.') != -1) {
    name = name.split('.')[1];
    result = $d.getElementsByClassName(name);
  } else if (name.indexOf('%') != -1) {
    name = name.split('%')[1];
    result = $d.getElementsByTagName(name);
  } else if (name.indexOf('&') != -1) {
    name = name.split('&')[1];
    result = $d.getElementsByName(name);
  }
  return result;
}

//例如:获取p标签
var w1 = getElem('#con');
var w2 = getElem('.cons');
var w3 = getElem('%p');
//获取输入框
var w4 = getElem('&user');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<p id="con" class="cons">CSS样式脚本化的内容</p>
<input type="text" name="user">
1
2

# CSSDOM

CSSDOM就是DOM对于CSS样式进行脚本化,可以很轻松的创建动态的html文档,之前因为这个原因还出现了DHTML,文档不再是静态的文本和媒体等内容了,而是可以动态的获取和操作。

CSSDOM的操作有以下几种:

  • Document对象的styleSheets来表示;
  • 通过style这个属性来控制元素的样式变化的,
  • 通过className可以给元素添加类名。

# styleSheets[]数组

通过Document对象引用的CSSStyleSheet对象属性和方法。可以用这个document.styleSheets[]就可以获取到所有的样式。

如果这个文档中引用了3个样式表,那么就会返回这3个样式表所定义的样式规则内容。

第一个样式表就可以用这样来表示document.styleSheets[0]

属性:每个样式表都有以下几个属性

  • cssRules和rules,这里面记录着每一条规则的内容;
  • disabled,这个属性表示这个样式表是否禁用掉,默认是false;
  • href,这个属性表示这个样式表的url地址;
  • type,这个样式表的类型。

例如:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>CSS样式脚本化</title>
  <link rel="stylesheet" href="jbh.css">
</head>
<body>
  <h2>CSS样式脚本化</h2>
  <p>CSS样式脚本化的内容</p>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
body {
  margin: 0;
  padding: 0;
}
h2 {
  color: #f00;
  font-size: 20px;
}
p {
  padding: 5px 10px;
  color: #eee;
}
1
2
3
4
5
6
7
8
9
10
11
12
var allstyle = document.styleSheets[0];
var cssRules = allstyle.cssRules;
var disabled = allstyle.disabled;
var href = allstyle.href;
var type = allstyle.type;
console.log('规则:',cssRules); // CSSRuleList {0: CSSStyleRule, 1: CSSStyleRule, 2: CSSStyleRule, length: 3}
console.log('是否禁用:',disabled); // false
console.log('url地址:',href); // url地址: http://localhost:2009/day/jbh.css
console.log('类型:',type); // 类型: text/css
1
2
3
4
5
6
7
8
9

# style属性控制

这个就是通过html的style标签来控制元素的样式,适用于内联样式和内嵌样式。优先级比外部样式表要大,就是可以把外部样式表的样式给覆盖掉。

注意:这个style只是可以获取和改变文档内部的样式规则,对于外链的样式表,无法获取和改变样式的属性。

例如:给一个链接加上颜色和大小。

<p>CSS样式脚本化的内容</p>
1
var con = document.getElementById('con');
con.style.color = '#f00';
con.style.fontSize = '20px';
1
2
3

# className

这个className就是给元素直接添加类名。只要文档内部或者外链样式表有这个类名的css规则就可以加上去。

用法:<要加类名的元素>.className="<类名>"

例如:给一个段落加一个类名。

<p>给我加类名吧!</p>
1
.con {
  padding: 5px 10px;
  height: 300px;
  color: #eee;
}
1
2
3
4
5
var con = document.getElementsByTagName('p')[0];
con.className = 'con';
console.log(con); // <p class="con">给我加类名吧!</p>
1
2
3

# classList

这个classList就是给元素直接添加多个类名。

用法:<要加类名的元素>.classList="<类名1 类名2 类名3 ...>",类名之间要空格。

例如:给一个段落加一个类名。

<p>给我加多个类名吧!</p>
1
.con {
  padding: 5px 10px;
}
.con1 {
  height: 300px;
}
.con2 {
  color: #eee;
}
1
2
3
4
5
6
7
8
9
var con = document.getElementsByTagName('p')[0];
con.classList = 'con con1 con2';
console.log(con); // <p class="con con1 con2">给我加类名吧!</p>
1
2
3

# DOM标准支持表

由于游览器的各处林立,导致制定的标准在每个游览器的支持都不一样,下面这个表是有关如何查询游览器支持哪些DOM标准的。

Document对象的implementation引用了DOMImplementation对象,定义了一个方法用来查询是否支持。

方法用法:hasFeature(name,version);里面包含两个参数,第一个是你要查询的标准名称,你要查询的DOM级别。

在这里我封装了一个方法,可以用来查询,如果返回true说明支持,反之则不支持。

function searchDomSupport(name,version) {
  if (document.implementation &&
    document.implementation.hasFeature &&
    document.implementation.hasFeature(name,version)
  ) {
    return true;
  } else {
    return false;
  }
}
var res = searchDomSupport('html','1.0');
console.log(res); // true;
1
2
3
4
5
6
7
8
9
10
11
12

附表:

序号|特性名称|版本|说明|暗示--|---|---|---|--- 1|HTML|1.0|1级的Core和HTML接口|无 2|XML|1.0|1级的Core和XML接口|无 3|Core|2.0|2级的Core接口|无 4|HTML|2.0|2级的HTML接口|Core 5|XML|2.0|2级的XML专有接口|Core 6|Views|2.0|AbstractView接口|Core 7|StyleSheets|2.0|通用样式表遍历|Core 8|CSS|2.0|CSS样式|Core,Views 9|CSS2|2.0|CSS2Properties接口|CSS 10|Events|2.0|事件处理基础结构|Core 11|UIEvents|2.0|用户接口事件(Events+ Views)|Events,Views 12|MouseEvents|2.0|Mouse事件|UIEvents 13|HTMLEvents|2.0|HTML事件|Events

提示:有时候这个方法可能不是太准确,兼容性不是很好。

这里提供两个网站,上面有着各个游览器的的兼容性的处理方法。

这个站点提供了作者DOM和CSS标准对于游览器兼容性的进行广泛研究后的成果。

# 优缺点

一个对象的出现,既有优点,又有缺点,不过缺点大于优点。

# 优点

  • 易用性强

  • 把文档信息都存于内存中,遍历简单方便

# 缺点

  • 解析速度慢,占内存过高,非常大的文件就不适合操作;

  • 在DOM运行的过程中,创建和修改、删除了大量的对象,机制不健全,导致效率低下,消耗了大量的时间

  • DOM有风险,使用需谨慎。切记,切记。

# 写在最后

因为DOM在不断的发展,内容规则和标准在不断的扩展。所以今天讲的是0级和1级DOM的有关知识点。因为内容太多,所以有关2级DOM的内容放在以后来说。其实上面的内容已经非常多了,需要慢慢的去使用。实践出真知,理论联系实践,相信你自己一定可以成功的。

分享至:

  • qq
  • qq空间
  • 微博
  • 豆瓣
  • 贴吧