如何提高你的CSS水平

在今天的文章中,我将要聊聊我在一年半的实践中,总结出来的css经验。

首先让我提醒你,css是极其简单的一门语言,简单到可以用三个词概括:选择器,属性以及属性的值。这也是一些人不喜欢css的原因:他们觉得写css像孩子玩乐高玩具一样简单。

是这样的。。如果你给一个9岁的孩子介绍css的基本原理,他就会搭建一个网站。不过这个网站不会很复杂,也就是一些包含头部,链接,内容图片的页面而已。

事实上,css是一门简单的语言,并不意味着每个人的水平一样。有些人写起css来就跟个大猩猩在玩棍子,有些人可以掌握css,而有些人可以使用魔法般的使用css。

不管怎样,我还是要给你分享一些我这几个月来理解的东西,这并不是一些代码片段或者是css小技巧,而更像是一些通用的规则或者是最佳实践之类的东西,如下:

  1. 不要让你的代码脱离你的掌控,尽量简洁
  2. 掌握基础,学习CSS技巧
  3. 保持代码的可复用性
  4. 面向对象的css
  5. Css3 了解他能做的以及你可以使用的部分
  6. 渐进增强与优雅降级
  7. Css预处理工具
  8. 与时俱进
  9. 取长补短
  10. 熟能生巧

你想说什么呢?你准备好了?那我们继续往下吧。

1.不要让你的代码脱离你的掌控,尽量简洁

别让你的代码脱离你的掌控

这是编程的一种通用建议,不仅仅适合css. 当你开始写代码的时候,先思考,思考以下几个问题:

  1. 我怎样实现?
  2. 有其他方法实现么?
  3. 怎样优化(简洁,可维护,等等)?

急于编码会浪费时间,你有可能花了一个小时也没实现,不得不从头再来,这不应该发生。

如果你花了好几个小时写一个css幻灯片,最终没办法用了个js幻灯片插件或者别的,这是很蛋疼的,并不是说你没成功,而是无谓的浪费了很多时间。如果项目有deadline的话,你就要悲剧了。

保持他的简单性

Css很简单,但如果你想,同样可以变得很复杂。在很多场景里,最简单的即是最好的。当你要实现什么效果的时候,问问自己:还有更简单的方法实现么? 答案经常是肯定的。

举个例子,简单水平导航栏,你可以有多种方法实现:

  1. 列表元素浮动;
  2. 改变列表元素display属性为inline;
  3. 改变列表元素display属性为inline-block。

最简单的-设置display:inline,不需要清除浮动,不需要清除inline-block造成的空白,只需要一些padding即可,结束。

2. 掌握基础,学习CSS技巧

不仅是掌握css,掌握任何东西都要从基础做起。一个对最基本的东西都理解不透彻的人是不可能成为大师的。

CSS基础

什么是css基础呢?你可能会听到不同的声音,这也是一种无法用言语表达的东西。但我认为,css的基础是以下两部分:

  1. 盒模型:每个元素在css中都是一个盒子模型(块级或者内联),包含宽高及各种padding margin border.这是最重要的,详细可以阅读这里
  2. 权重:了解哪个属性权重最高,在调试css中非常重要。更多相关信息可以阅读Assigning property values, Cascading, and Inheritance一文。

CSS技巧

当你了解了以上原理,才算踏上了康庄大道。那么现在你可能会处理一些特殊情况,下面展示了一些:

忘了在父元素设置position

.child {
  position: absolute;
  top: 0;
  left: 0;
}
 
/* 这是忘了的*/
/*
.parent {
  position: relative; // Or anything else than static
}
*/

这时候你会想:坑爹啊,怎么东西跑到页面左上角了???你忘了给父元素设置 position:absolute或relative了。。

添加下下层技巧

.parent {
  z-index: 1;
  opacity: 0.5;
  transform: rotate(5deg);
}
 
.child {
  z-index: -1;
}

每个搞前端的都被层叠上下文折磨过。这个例子告诉我们,你不能对一个已经触发层叠上下文的元素(可能是z-index,transform或者是透明度)的子元素来应用z-index属性。 这没有变通方案,一旦你遇到过,绝对会记忆犹新。。

忘了清除浮动的技巧

你的布局乱套了,容器乱飞。你泪流满面:这是肿摸了。。请检查下浮动,可能忘了清除浮动了。

记住,一个容器如果仅仅包含浮动元素,他会塌陷,除非你清除浮动或者设置height或者overflow属性。

这种例子比比皆是,如果继续写那就没完没了了,这不是我这篇文章的目的。

我只是想指出 每天,人们都会不断的发现,css有很多的特性跟特殊情况。 你可能这一次踩个坑,下次就知道怎么对付了。

3. 保持代码的可复用性

Dry代表 不要重复自己。这并不是css特有的,这适合任何语言。

它的核心思想是,能重用的代码就不要写第n遍。在别的语言意味着封装成function,在css里,常常代表你需要利用一个可复用的类来代替重复应用的一个属性。这将在后面的面向对象css中进一步讲解。其实对于重构来说这是很简单的,我来解释一下:

当你在你的样式表里发现一段代码多次使用的时候,你需要重构一下,最终变成只出现一次(可复用)。例子:

.navigation li {
  color: #333;
}
 
.navigation li a {
  color: #333;
}
 
/* Refactoring */
 
.navigation li,
.navigation li a {
  color: #333;
}

明白么?你肯定会纳闷,这俩有啥区别啊?有两点需要考虑: 性能跟可维护性。

关于性能: 更少的行数意味着浏览器解析css会更快。按照下面这样写,浏览器会同时给两个选择器应用颜色,而不会解析两次。

关于可维护性,如果你需要修改类似的颜色,这样只需要修改一行,这里可能仅仅是两行,如果50行,100行呢。

扩展阅读

  1. DRY CSS, don’t repeat your CSS
  2. DRY CSS: a don’t-repeat-yourself methodology for creating efficient, unified and scalable style sheets

4. 面向对象的css

这是什么东东?

Oocss意思是面向对象的css,经常在其他面向对象的语言中出现这个名词。意思是利用一个“对象”,通常为一个类的实例(包含一些属性和方法)。你可能会问:这跟css有啥关系啊。

首先我要说明,oocss更像一个概念,css是不能真的“面向对象编程”的,因为没有命名空间,函数,方法,类,条件语句等。因此你要是谈oocss可能会被人嘲讽。

事实上我也是这么认为的,但我们可以利用这样的思想来促进css的书写,让网站性能提升,提高可维护性。

我们应该怎么做?

很简单,使用类名,各种类名。可以把你的网站想象成一个带有很多“方法”跟“组件”的地方,尝试找出这些重复的部分,把他们作为“对象”(类名),从而重用。

为了让你的对象划分的更精确,有两点需要注意:

  1. 分离结构与表现
  2. 分离容器与内容

单独的结构和样式

分离结构与表现是很重要的,这样你就可以单独操作负责显示的部分,让这部分在网站中多次出现,并且作用于不同的元素上。看看下面的代码,它可以作用到一个盒子,一个图片,或者一个按钮上:

#my-button,
.my-box
.my-box img {
  border: 1px solid #444;
  border-radius: 5px;
  box-shadow: 0 0 5px rgba(0,0,0,0.1);
}

另外我们可以用一个叫做.skin的类名代替,然后应用到所要使用的元素上

.skin {
  border: 1px solid #444;
  border-radius: 5px;
  box-shadow: 0 0 5px rgba(0,0,0,0.1);
}

这样使用,会让css样式表更加容易让人理解,更容易维护,解析更快。

单独的容器(结构)和内容

我认为这是oocss最重要的一点:每种组件拥有独立的代码,而不是在你某次写页面时配合页面的特定部分来定义。在你的站点中,类似的组件应该重用,就像下面代码一样:

#main h2 {
  color: #343434;
  font-size: 25px;
  line-height: 20px;
  border-bottom: 1px solid rgba(0,0,0,0.2);
  box-shadow: 0 1px rgba(255,255,255,0.4);
}

这时候不管是我从页面底部再次使用h2,或者我用同样的思想使用h3,都可以类似上面所写的,创建一个类,并给类设置样式,而不是给元素单独设置样式。

关于从不使用ID的一说?

当Nicole Sullivan提出oocss的概念时,最热烈的讨论是“绝对不使用id选择器么?” Nicholas C. Zakas 与 Nicole Sullivan在他们的csslint(css质量检查工具)中特别反对对id选择器的使用。

为了理解nicole的观点,我们必须认识到id选择器因为高权重会在使用中出现一些问题。如下代码(代码来自于这里):

<!-- HTML -->
<div id="header">
<p>
<a href="#">Foo</a>
<a href="#">Bar</a>
</p>
<div class="tweet">
<a href="#">Follow me on twitter</a>
</div>
</div>
<div class="tweet">
    <a href="#">Follow me on twitter</a>
</div>
/* CSS */
#header a { color: #f90;  }
.tweet     a { color: #000; }

如果让第一个twitter的链接变黑,你有两种选择:给一个id,或者利用!important选择器来机枪打蚊子。如果header是一个类名,就不会有这种问题了。

这也是Nicole Sullivan为什么讲“no ID”的原因。

我引用 harry roberts对此话题的一个讨论来作为这节结束

[…] I have decided that a blanket ban is sensible. Save yourself many potential headaches and never use IDs in your CSS files.

当然原则上id是可以使用的,而且很易生效

我对OOCSS的理解

事实上我并不熟悉oocss。因为我并不在一个拥有很多前端的的大型网站工作。Oocss对大型网站架构很有用,但不适合单页面作战。

然而虽然我并未应用oocss,但我关注前端工作中的组件重用,样式表的可维护性以及性能。这也是oocss所注重的,所以在某些方面,我的工作跟oocss并没有太大区别。

扩展阅读

  1. An introduction to OOCSS
  2. Object Oriented CSS
  3. OOCSS.org
  4. Don’t use IDs in CSS selectors?
  5. OOCSS——概念篇
  6. OOCSS——核心篇

5. Css3:了解他能做的以及你可以使用的部分

现在我们打住概念性的讨论,来点干货:css3,虽然这玩意至今没有个确切的定义。

Unlike CSS 2, which is a large single specification defining various features, CSS 3 is divided into several separate documents called “modules”. Each module adds new capabilities or extends features defined in CSS 2, over preserving backward compatibility. […] Due to the modularization, different modules have different stability and statuses.

From Cascading Style Sheets on Wikipedia

现在我们来讨论下那些已经被现代浏览器实现的一些特性。

当前,从圆角到渐变,从透明到阴影,伪类,随处可见css3。

学习你能做些什么?

我觉得,通过利用css3,你可以减少http请求(图片请求),减少标签数量,减少js的代码量。让我们展开来说:

  1. 圆角,一行代码代替了用四张小图片拼四个圆角;
  2. 透明度与alpha通道的支持: 一行代码代替了一张半透明png;
  3. 更先进的选择器:不需要js来写了;
  4. Flexbox:几行代码代替了一整个布局框架;
  5. 渐变:几行代码代替了背景平铺的图片;
  6. 多背景:不需要多个容器了;
  7. 伪类:装饰性的元素不需要额外的标签去定义了。

我可以继续讲下去,但是我相信你已经明白了我的意思:知晓你到底可以用css3来做什么。你需要做一个目录么?你可以用纯css来实现,配合CSS counters,你需要做一个漂亮的自定义边框么?你可以利用border-image属性来实现。

ss能做无数的事情,css3也一样。你需要了解css力所能及跟力所不能及的。但深入这些,没有什么捷径,只能通过不断的阅读文档,自我探索。比如,你可以用calc()属性来计算,但如果要搞六列的等高布局,你肯定会杯具。

学习你能用什么?

css最大的问题是浏览器兼容性。在css3里这种情况变得更加糟糕。当你不断试验css3属性时,这将是你萦绕不去的梦魇。

比如最耳熟能详的css3属性:border-radius,杯具的是ie8以下,跟opera mini浏览器仍然不认识这玩意。事实上chrome跟FF对圆角的渲染也不一致。

这对一名前端来说意味着啥呢?优雅降级。如果你做圆角,除了border-radius,你仍然需要利用多图片来拼圆角,努力让各大浏览器保持一致。

当我要使用css时这是我通常的思考步骤:

  1. 我要用这css干嘛?
  2. 我明白我要干嘛了,这是css2规范里的么?
    1. 必须的,结束。
    2. 不行,跳到第三步
  3. 这玩意兼容性怎么样啊
    1. 很好的兼容啊,结束
    2. 兼容性很一般,跳到第四步
  4. 这是锦上添花还是需求啊?
    1. 这是锦上添花,那么低端浏览器我就只能优雅降级了。
    2. 这是需求啊!跳到第五步
  5. 我怎么在不兼容的浏览器里对付这个需求呢?

现在以渐变为例:

  1. 这里要用渐变?
  2. 用吧。。css2里有渐变?
    1. 渐变是css3的好吧。
  3. 兼容性呢?
    1. 还好吧,一般化,ie8以下跟operamini不支持css3渐变(这里明显有误了)
  4. 这是锦上添花还是需求啊?
    1. 这只是一种更炫的手段,不是重点。不支持的我就用个单色代替吧。
    2. 需求要求这里就是用渐变啊!我得找兼容了。
  5. 我要如何让它不支持的浏览器上工作
    1. 用背景图片吧。

如果你用css的时候思考这些步骤,就有思路来处理浏览器兼容性跟优雅降级之类的了。

注意!一定要注意你产品的用户!如果你要做移动端app或者移动设备,你可以使劲用css3属性,不用担心。但如果你做个银行系统。。抱歉了,大部分用户都是ie8以下的

Providing fallbacks

根据不同情况,对低端浏览器做兼容可能会很蛋疼,也可能会很烦人。

No fallback or simple fallback

如果有些增强用户体验的特性不被支持,不会带来什么后果的话,你就不用强制兼容了,这太爽了。

也有一些情况兼容起来也比较简单。

总之你需要写两份属性来实现这个效果,作用于老版本浏览器的跟现代浏览器版的。

.my-element {
  border: 1px solid #666;
  border: 1px solid rgba(0,0,0,0.3);
  background: #708090;
  background: hsl(210, 13%, 50%);
}

以上的例子中,如果浏览器不识别rgba跟hsl颜色,他会读第一行跟第三行作为降级。那么现代浏览器就会覆盖这些属性,以新属性来渲染。

Modernizr

现在当我们聊到css3的时候,绝对会提及一个库Modernizr。 他可以对浏览器做html5跟css3的特性检测。听起来可能觉得很庞大,但这个库性能非常的好。

无论如何,有时你必须确切地知道如果浏览器支持一个特定的功能。因为,对我们说,提供另一种优雅降级版本,需要知道他的CSS属性。

/* Normal version */
.dropdown {
  opacity: 0;
  pointer-events: none;
}
 
.trigger:hover .dropdown {
  opacity: 1;
  pointer-events: auto;
}
 
/* Fallback with Modernizr */
 
.no-opacity .dropdown,
.no-pointerevents .dropdown {
  opacity: 1;
  pointer-events: auto;
  display: none;
}
 
.no-opacity .trigger:hover .dropdown,
.no-pointerevents .trigger:hover .dropdown {
  display: block;
}

如果你要提供一个降级方案的话,需要依赖modernizr提供的两个类(.no-opacity 跟.no-pointerevents)但不能同时触发两个。在使用新技术的时候,一定要记得优雅降级,这对用户体验很重要,如果不行,一定要告知用户。

扩展阅读

  1. CSS current work with table of specifications
  2. WebPlatform.org
  3. Modernizr for feature detection
  4. CanIUse for the browser support

6. 渐进增强与优雅降级

这两个概念大家肯定耳熟能详了,尤其是优雅降级。其实两者还是有点区别的。

渐进增强

渐进增强,你需要制定一个基本的需求和功能,然后根据浏览器支持度,增强用户体验。你在使用的时候,需要用一些html5属性来提醒某些部分缺失(比如在某某浏览器下可以获得最佳体验)。这是一种根据浏览器引擎增强用户体验的方式。

优雅降级

当你谈到优雅降级的时候,通常是给低端浏览器提供一个不支持特性的替代方案。他的形式通常是从高级到低级讨论的。比如说你需要在canvas标签里面注明,这样当浏览器不支持canvas的时候用户就会知道了。

<canvas>
This page uses HTML5 Canvas. Please use a recent browser to see this content. To get another browser, go to <a href="http://browsehappy.com/">BrowseHappy</a>
</canvas>

他们有何不同?

事实上没啥区别。有些特性呈现在现代浏览器上,而对此在低端浏览器上则上替代方案。这是一个过程:你可以让网页在现代浏览器中的效果很炫,也得顾忌ie,或者置之不理,这完全取决于你。事实上我想说:不同浏览器的用户体验绝对是不同的,即使你需要提供一些基本功能,也请记得利用新特性增强一下你的浏览器或者应用,这是非常好的。

扩展阅读

  1. Progressive enhancement and graceful degradation
  2. Understanding progressive enhancement

7. Css预处理工具

Css预处理器大概是本年度热门话题了。。这玩意真的好用么?用来干嘛?我需要用么?用什么样的?这是css圈子里的热门话题了。

在这个环节我将以客观的态度来对待这个问题。首先要明确一点:如果你不想用预处理器,请不要用,然后就不会纠结了。不用预处理器并不代表你不是个好前端,仅仅是你可能无法完成一些事情而已。但对待预处理器这件事上,你仍然需要有自己独立的思考。

接下来,什么是预处理器?事实上预处理器就是把一些语句段编译成别的引擎(比如浏览器)需要的语言。各种语言都有预编译,比如html可以使用markdown或者jade。Less,sass跟stylus都是css的预处理器。Coffeescript是js的预处理器,当然还有php的预处理器:cakephp。

要点是什么?

预处理器赋予了css一些基于面向对象语言的特性,比如

  1. 变量(variables)
  2. 可传参的函数(functions (with parameters))
  3. 命名空间(namespaces)
  4. 嵌套(nesting)
  5. 条件语句(conditional statements)
  6. 操作(operations)
  7. 其他(and many more)

听起来很有吸引力,对吧?也许你想要一个例子来找出发生了什么。让我们看下面的CSS导航栏。

.navigation {
  width: 800px;
  width: calc(100%-150px);
}
.navigation li {
  color: #444;
}
.navigation li a {
  text-decoration: none;
}

看一个Scss处理的版本

$main-color: #444;
 
.navigation {
  width: (100%-150px);
  li {
      color: $main-color;
      a {
          text-decoration: none;
      }
  }
}

预处理器的基本功能就是把这些表达式翻译成css,结果肯定是相同的,而处理逻辑不同。预处理器会自动处理并输出结果,而不需要calc函数的支持。

最终,你得到了一个可读性更强的(包含嵌套与变量),可维护性更强的(包含变量与函数)的样式表。上面的仅仅是举个例子,在真实的项目里你会体会到这一点。

如何选择?

现在市面上已经有一些不同的css预处理器了,他们各有千秋,同时在细微之处也有区别。选择的余地完全在你手里。这里提供几个主要的选择:

  1. Sass (written in Ruby)
  2. LESS (written in JavaScript)
  3. Stylus (written in JavaScript)
  4. Crush (written in PHP)

最好的方式是了解他们是否能最完美的满足你跟你项目的需求。这是由很多因素决定的,如果全部列出来就超出本文的范围了,但你可以找到一些相关的css预处理器的资料。

我对CSS预处理的理解?

我不敢说精通css预处理器,但我非常喜欢。他完善了一些css缺少的非常有用的特性:变量,嵌套以及条件语句。

我之前小玩了一下less,受益匪浅。利用less我完善了以前制作的 CSS Loading Animations,less应用在了循环,自动加前缀以及关键帧上。

我也尝试了sass跟compass。令我震惊的是这玩意安装很简单,在ruby上跑得也很顺畅。所以我觉得Sass + Compass碉堡了。你可以看看我的文章:为啥我抛弃了less选择了sass

不过我还是需要完全手写普通的css,这也占据了我工作的绝大部分。但最终我觉得大家都会用css预处理器。Css缺乏一些很有用的特性,而预处理器正好填补了这方面的空白。

扩展阅读

  1. Sass vs. LESS vs. Stylus
  2. Musing on preprocessing
  3. Deep Dive Into CSS Preprocessors
  4. Guidelines using OOCSS and CSS preprocessors

8. 与时俱进

语言在不断的进化,css是显著典型。Css草案没完没了,浏览器厂商也在不断的实现这些。

因此,你需要与时俱进,始终对新技术保持跟进,虽然一时半会还用不上,但可以了解chrome开发版本是不是支持了?chrome跟safari,ff,opera未来的版本会不会支持呢?你需要在css解决方案及css问题上,不断的开拓自己的眼界。

扩展阅读

  1. Cascading Style Sheets home page
  2. CanIUse.com for browser share and compatibility
  3. Mozilla Developer Network for excellent documentation
  4. HTML5 Rocks for latest, cutting-edge tutorials and articles

9. 取长补短

读源码是学编程最好的方式之一。css是客户端的,你可以用WebKit Inspector, Dragonfly, Firebug F12等工具看css。同时,互联网是很开放的,大家都乐于开源。

另外一种方法是跟着教程一步步地走,然后从头自己写一遍。遇到问题后瞄一眼答案,然后继续。

如果你觉得自己已经熟悉css了,想深入css,你需要写一些demo来测试那些试验性的css属性,或者解决一个问题的不同思路。学无止境。

前阵子Chris Coyier, Tim Sabat 和 Alex Vazquez三位建立了CodePen,他是一个在线分享前端代码的平台,可以自由的添加你需要的库,比如jQuery, jQuery UI, MooTools, YUI, Prototype, Zepto, Dojo, Ext JS, PrefixFree等等。同时也可以使用一些预处理器比如HAML, Markdown, Slim, LESS, SCSS, Sass, CoffeeScript。

学习其他人例子的网站:

  1. CodePen
  2. CSS Deck
  3. Dabblet
  4. TheCodePlayer
  5. CSS3 案例

10. 熟能生巧

实践出真知。所以最好的建议:不断的练习。熟能生巧。并不是让你不停的从头建站。你可以从dribbble上找些图用纯css实现一下。最终你可能不一定会用到,但你学到了,这就足够了。

我之前也说了,掌握基础,了解技巧。Css有很多坑,填坑是csser日常工作的一部分。而坑都是写代码的时候踩出来的,只有踩过了,解决过了,才能提高。

同时我建议你多多分享自己的代码,让大家提提意见,取百家之长方为上。你可以扔到JSFiddle里然后分享到群里,问问大家有没有可以吐槽的。

扩展阅读

本文提到很多链接与资源,全部列入在此,以供大家索引阅读:

  1. Cascading Style Sheets home page
  2. CSS current work with table of specifications
  3. Assigning property values, Cascading, and Inheritance
  4. Cascading Style Sheets on Wikipedia
  5. CSS 2.1: Box Model
  6. WebPlatform.org
  7. Modernizr
  8. CanIUse.com
  9. Z-Index And The CSS Stack: Which Element Displays First? by Steven Bradley
  10. DRY CSS, don’t repeat your CSS by Steven Bradley
  11. DRY CSS: a don’t-repeat-yourself methodology for creating efficient, unified and scalable style sheets by Jeremy Clarke
  12. Progressive enhancement and graceful degradation on DevOpera
  13. Understanding progressive enhancement by Aaron Gustafson
  14. Sass
  15. LESS
  16. Stylus
  17. Crush
  18. Sass vs. LESS vs. Stylus by Johnathan Croom
  19. Musing on preprocessing by Chris Coyier
  20. Deep Dive Into CSS Preprocessors by Johnathan Verrecchia
  21. Guidelines using OOCSS and CSS preprocessors by Chris Webb
  22. Mozilla Developer Network
  23. HTML5 Rocks
  24. CodePen
  25. CSS Deck
  26. Dabblet
  27. TheCodePlayer

非常感谢你阅读此文,希望这篇文章可以真正在你学习使用css的时候帮到你。当然有意见跟建议你也可以提出来~

译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!

如需转载烦请注明出处:

英文原文:http://tympanus.net/codrops/2012/11/20/learning-principles-for-improving-your-css

中文译文:http://www.w3cplus.com/css/learning-principles-for-improving-your-css.html


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流