阿古达木

阿古达木 查看完整档案

北京编辑中国民航大学  |  信息安全 编辑求职  |  无头衔 编辑 www.qwero.cn/index.html#/ 编辑
编辑

牛逼的工程师就是能用简单的代码和思路写出复杂的功能

个人动态

阿古达木 赞了文章 · 8月31日

SVG之Animation

<animate>元素用于实现动画效果(动画截图比较麻烦,本文中的例子最好直接写demo看效果)

基本动画

将<animate>元素嵌入到元素内,来实现该元素的动画效果。

<rect x="10" y="10" width="200" height="20" stroke="black" fill="none">
    <animate
    attributeName="width"
    attributeType="XML"
    from="200" to="20"
    begin="0s" dur="5s"
    fill="freeze" />
</rect>

以上代码会一个200*20的长方形,在5秒内渐变成一个20*20的正方形,并且在动画结束时停留在正方形的状态。

<animate>元素的基本属性:

  • attributeName

定义发生变化的元素属性名

  • attributeType

当attributeType="XML"时,attributeName被认为是XML的属性;当attributeType="CSS"时,attributeName被认为是css的属性;不指定attributeType时,默认为"auto",会先将attributeName作为css的属性,如果无效,再将attributeName作为XML的属性。

  • from & to & by

from和to分别定义发生变化的属性的初始值和终止值。from可缺省,表示初始值即为<animate>父元素相应的属性值。可用by替换to,表示变化偏移量。可以理解为to = from + by。

  • begin & dur & end

begin定义动画开始时间;dur定义动画所需时间;end定义动画终止时间。时间单位h:小时;min:分钟;s:秒;ms:毫秒。默认时间单位为s

  • fill

当fill="freeze"时,动画终止时,发生变化的元素属性值停留在动画终止时的状态;当fill="remove"时,动画终止时,发生变化的元素属性值回复到动画起始时的状态。fill属性默认值为remove。
允许在同一个元素内嵌入多个<animate>

<rect x="10" y="10" width="20" height="20" style="stroke: black; fill: #cfc;">
    <animate attributeName="width" attributeType="XML" begin="0s" dur="2s" from="20" to="120" fill="freeze"/>
    <animate attributeName="height" attributeType="XML" begin="0s" dur="2s" from="20" by="100" fill="freeze"/>
</rect>

动画时间

当begin设置为一个具体时间,比如2s,svg会在元素加载完毕后,过2秒开始执行动画。
begin还可以指定一个其他<animate>的begin或者end,比如:

<circle cx="60" cy="60" r="30" style="fill: #f9f; stroke: gray;">
    <animate id="c1" attributeName="r" attributeType="XML" begin="0s" dur="4s" from="30"  to="10" fill="freeze"/>
</circle>
<circle cx="120" cy="60" r="10" style="fill: #9f9; stroke: gray;">
    <animate attributeName="r" attributeType="XML" begin="c1.end" dur="4s" from="10" to="30" fill="freeze"/>
</circle>

第二个圆的动画执行起始时间为第一个圆动画执行完毕时间。
begin属性还可以进行简单计算:
begin="c1.end+1.5s":表示动画执行起始时间为第一个圆执行完毕后的1.5秒。

当end设置为一个具体时间,比如2s,svg会在元素加载完毕后,过2秒即停止执行动画,不管这个元素的动画是否执行完毕。如果end设置的比begin小,则动画根本不会执行。

end同样可以指定一个其他<animate>的begin或者end,同样支持计算。

重复动画

通过设置repeatDur或者repeatCount属性,让动画重复执行。

  • repeatDur

设置动画执行的总时长。在repeatDur设置的时间内,动画一直会重复执行。如果repeatDur小于dur,repeatDur的作用与end一样。

  • repeatCount

设置动画重复执行的次数。
repeatDur和repeatCount都可以通过设置为indefinit实现无限循环动画。
当repeatDur和repeatCount同时作用于同一个<animate>时,动画终止时间取两者中较小值。
repeat可作为begin和end中的参数使用:

<circle cx="60" cy="60" r="15" style="fill: none; stroke: red;">
    <animate id="circleAnim" attributeName="cx" attributeType="XML" begin="0s" dur="5s" repeatCount="3" from="60" to="260" fill="freeze"/>
</circle>
<rect x="230" y="80" width="30" height="30" style="fill: #ccf; stroke: black;">
    <animate attributeName="x" attributeType="XML" begin="circleAnim.repeat(1)+2.5s" dur="5s" from="230" to="30" fill="freeze"/>
</rect>

长方形的动画会在圆形动画执行过一遍后延迟2.5秒后开始执行。
repeat(n)中的n需大于0。

复杂属性的动画

动画还可作用于颜色、paths、d等非纯数字属性。

颜色的动画过程可以分解成r,g,b,a四个数字的渐变。比如从#f00变化到#0f0,红色部分从1渐变到0,绿色部分同时从0渐变到1。
paths和d要实现动画有一个前提,其参数个数不能变。变化前后参数一一对应,所有参数同时渐变。

<polygon points="30 30 70 30 90 70 10 70" style="fill:#fcc; stroke:black">
    <animate id="animation"
             attributeName="points"
             attributeType="XML"
             to="50 30 70 50 50 90 30 50"
             begin="0s" dur="5s" fill="freeze"/>
</polygon>
<path d="M15 50 Q 40 15, 50 50, 65 32, 100 40" style="fill:none; stroke: black" transform="translate(0,50)">
    <animate attributeName="d"
             attributeType="XML"
             to="M50 15 Q 15 40, 50 50, 32 65, 40 100"
             begin="0s" dur="5s" fill="freeze"/>
</path>

多节点动画

实现一个属性的连续变化有两种方式:

  1. 多个<animate>组合
  2. 使用value + keyTimes + calcMode属性

基础动画中提到过多<animate>组合方式;这里说下values + keyTimes + calcMode。

  • values

values属性值表示一个动画经过的节点数值,数值间以分号分割。

<circle cx="175" cy="75" r="20" fill="red">
    <animate attributeName="r"
             attributeType="XML"
             values="20;50;20"
             begin="0" dur="1" repeatCount="indefinite"
             fill="freeze"></animate>
</circle>

上例中1秒内,圆的半径由20变为50,再由50变为20。

  • keyTimes

keyTimes属性值与values属性值一一对应,第一个数值永远是0(表示起始时间点),最后一个数值永远是1(表示终止时间点),中间的数值表示变化到对应values属性值时所处时间点百分比(0~1之间)。

<circle cx="175" cy="75" r="20" fill="red">
    <animate attributeName="r"
             attributeType="XML"
             values="20;50;20"
             keyTimes="0;0.2;1"
             begin="0" dur="1" repeatCount="indefinite"
             fill="freeze"></animate>
</circle>

上例中0.2秒时圆的半径由20变为50,在之后的0.8秒又从50变为20。

  • calcMode

calcMode可以影响动画各阶段的表现。calcMode有四种属性值:paced, linear, discrete, spline

calcMode="paced"时,动画会忽略keyTimes属性,根据values数值以匀速变化。
calcMode="linear"时,动画根据values和keyTimes属性,在每个时间段内匀速变化。linear为calcMode的默认属性值。
calcMode="discrete"时,动画根据values和keyTimes属性,去掉过度动画,到了keyTimes的某个节点,属性值直接变为values对应数值。
calcMode="spline"时,需要配合keySplines属性,设置每个时间段内的三次贝塞尔变化曲线。

<circle cx="175" cy="75" r="20" fill="red">
    <animate attributeName="r"
             attributeType="XML"
             values="20;50;20"
             keyTimes="0;.15;1"
             calcMode="spline"  keySplines=".5 0 .5 1;.5 0 .5 1"
             begin="0" dur="1" repeatCount="indefinite"
             fill="freeze"></animate>
</circle>

上例中加上贝塞尔曲线后圆形的变化有点类似心跳的节奏。

<set>

<set>元素也可设置属性变化的动画,但与<animate>有明显区别:

  • 不需要from属性,起始状态即为父节点属性值。
  • 一到begin属性设置的时点,指定的attributeName属性值即改为to指定的属性值,没有过度动画。
  • attributeName可以是属性值非数字的属性,如style="visibility: hidden;"
<text text-anchor="middle" x="60" y="60" style="visibility: hidden;">
    <set attributeName="visibility" attributeType="CSS"
         to="visible" begin="1s" dur="10s" fill="freeze"></set>
    <set attributeName="x" attributeType="XML"
         to="120" begin="2s" dur="10s" fill="freeze"></set>
    All gone!
</text>

上例中1秒后显示All gone!,再过1秒后文字移动至(120,60)。

<animateTransform>

要实现transform属性改变的动画,需要使用<animateTransform>来替代<animate>。

<rect x="-10" y="-10" width="20" height="20" style="fill: #ff9; stroke: black;">
    <animateTransform id="a1" attributeName="transform" attributeType="XML" type="scale" from="1" to="4 2" additive="sum" begin="0s" dur="4s" fill="freeze"></animateTransform>
    <animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0" to="45" additive="sum" begin="a1.end" dur="4s" fill="freeze"></animateTransform>
</rect>

<animateTransform>的attributeName指定为transform(个人感觉有点多余,animateTransform本身就是应用于改变transform属性值的)。用type属性指定transform需要改变的属性(translate, scale, rotate, skewX, skewY)。

<animateTransform>还有个additive属性。上例中两个<animateTransform>同时作用于一个<rect>元素,默认情况下additive属性值为replace,表示当前<animateTransform>的初始状态与之前的<animateTransform>变化结果无关。如果additive="sum",表示当前<animateTransform>的变化基于之前的<animateTransform>变化之上。

上例结果:

clipboard.png
clipboard.png

(刚看到一个长方形使用rotate会变成一个菱形感觉哪里不对,再回想了一下translate的本质和形变的次序,就明了了)

<animateMotion>

<animateMotion>是个很强大的元素,之前所有动画功能在css里都可以用animation实现,但<animateMotion>能实现的功能是单凭css无法实现的。
<animateMotion>可以让父元素沿着指定的路径运动!
直线路径可以简单使用from + to属性来指定起点和终点:

<g>
    <rect x="0" y="0" width="50" height="30" style="fill: #ccc;"/>
    <circle cx="40" cy="30" r="10" style="fill: #cfc; stroke: green;"/>
    <circle cx="10" cy="30" r="10" style="fill: #cfc; stroke: green;"/>
    <animateMotion from="0,0" to="60,30" dur="4s" fill="freeze"/>
</g>

也可以实用path指定复杂的路径

<g>
    <rect x="0" y="0" width="50" height="30" style="fill: #ccc;"/>
    <circle cx="40" cy="30" r="10" style="fill: #cfc; stroke: green;"/>
    <circle cx="10" cy="30" r="10" style="fill: #cfc; stroke: green;"/>
    <animateMotion path="M50,125 C 100,25 150,225, 200, 125" dur="4s" fill="freeze"/>
</g>

也可以指定<path>元素作为自己的路径

<path id="cubicCurve" d="M50,125 C 100,25 150,225, 200, 125"/>
<g>
    <rect x="0" y="0" width="50" height="30" style="fill: #ccc;"/>
    <circle cx="40" cy="30" r="10" style="fill: #cfc; stroke: green;"/>
    <circle cx="10" cy="30" r="10" style="fill: #cfc; stroke: green;"/>
    <animateMotion dur="6s" fill="freeze">
        <mpath xlink:href="#cubicCurve"/>
    </animateMotion>
</g> 

<animateMotion>有个rotate属性,默认为0,元素在运动时不会旋转。当设置为auto时,元素对应的水平轴会始终与path路径保持水平,上图中加上rotate="auto"后的效果就像是车子开过山坡。

<animateMotion>节点

与多节点动画类似,<animateMotion>也有三个属性用于控制动画的节点。

  • keyPoints

第一个属性值为0(表示path路径的起始位置),最后一个属性值为1(表示path路径的终点位置),各属性值用分号分割,每个属性值与keyTimes的属性值一一对应,表示到某个时点运动到相对于路径的哪个位置。

  • keyTimes

第一个属性值为0(表示动画起始时间),最后一个属性值为1(表示动画终止时间),各属性值用分号分割,每个属性值与keyPoints的属性值一一对应,表示运动到某个位置需要的总动画时常占比。

  • calcMode

与多节点动画中的calcMode属性值及作用完全一致。

<path d="M-10,-3 L10,-3 L0,-25z" style="fill: yellow; stroke: red;" >
    <animateMotion
    path="M50,125 C 100,25 150,225, 200, 125"
    rotate="auto"
    keyPoints="0;0.2;0.8;1"
    keyTimes="0;0.33;0.66;1"
    calcMode="linear"
    dur="6s" fill="freeze"/>
</path>

CSS动画

css中有两种实现动画的方式:transition和animation。
transition用于实现较简单的两点动画,即根据属性的起始状态和终止状态,完整中间过渡动画。

transition

transition动画相关的css属性如下

  • transition-property

定义动画属性,可定义多个属性,逗号分隔

  • transition-duration

定义动画执行时间,单位(s,ms),可定义多个属性,与transition-property一一对应,逗号分隔

  • transition-timing-function

与calcMode类似,影响动画的表现。默认值为ease。可选项有:ease | linear | ease-in | ease-out | ease-in-out | step-start | step-end | steps(<integer>[, [ start | end ] ]?) | cubic-bezier(<number>, <number>, <number>, <number>)

  • transition-delay

定义动画延迟多少时间后开始执行

其中transition-timing-function中的step-start、step-end和steps有点类似calcMode中的discrete模式,状态之间切换没有中间过渡。具体来说stepts(<integer>[, [start | end]])的第一个参数必须为正整数,表示分几步变化,第二个参数设置为start时,表示在动画开始执行时就进行变化,设置为end表示在动画经过transition-duration设置的时间才开始变化,第二个参数允许省略,缺省值为end。step-start相当于steps(1, start)即属性一开始就变为最终状态;step-end相当于steps(1,end)即属性经过transition-duration设置的时间后立刻变为终止状态。

#car{
    transition-property:width, height;
    transition-duration:1s, 3s;
    transition-timing-function: ease-in, cubic-bezier(1,0,0,1);
    transition-delay:2s, 0.5s;
}

可使用transition缩写简化上例中的css,直接从W3C规范里抄了下缩写组成部分。


transition:<single-transition> [ ‘,’ <single-transition> ]*

<single-transition> = [ none | <single-transition-property> ] || <time> || <single-transition-timing-function> || <time>

第一个<time>对应transition-duration,第二个<time>对应transition-delay。
如果有一个<single-transition>里的第一个参数设置为none,则整个transition无效。也就是说none只能这么用:transition:none

#car{
    transition:width 1s ease-in 2s, height 3s cubic-bezier(1,0,0,1) .5s;
}

animation

animation可以说是transition的高级版,配合@keyframes可以实现对动画过程的多点控制。除了svg中延指定path路径运动的动画实现不了,其他都可以用animation来实现。其很多属性与svg的<animate>元素属性作用也能一一对应。
先看下animation的属性

  • animation-name

对应<animate>的attributeName属性,属性值为@keyframes名对应,后面再说@keyframes

  • animation-duration

对应<animate>的dur属性

  • animation-timing-function

对应<animate>的calcMode属性,可用三次贝塞尔曲线来设置动画运行的效果

  • animation-iteration-count

对应<animate>的repeatCount属性。默认为1。infinite表示无限重复(不同于repeatCount的indefinte)

  • animation-delay

动画延迟执行时间。有点类似<animate>的start属性的作用,用start加上一些事件和时间的计算,也可以实现延迟执行动画的效果

  • animation-fill-mode

类似<animate>的fill属性,但有区别。animation-fill-mode有四个属性值可选:none | forwards | backwards | both。

 - **forwards**
 类似fill的freeze,动画结束后保持最后一帧时的状态。
 - **backwards**
 这个功能svg里好像没有。backwards是配合animation-delay一起使用的,如果动画延迟执行,那么在开始执行之前处于延迟的这段时间内,动画对象元素的默认状态是其原始状态,而非@keyframes的起始状态。当animation-fill-mode设置成backwards时,动画对象元素在延迟的这段时间内,将处于@keyframes设置的起始状态。
 - **both**
 相当于同时设置了forwards和backwards两个属性值。
 - **none**
 none是animation-fill-mode的默认属性值,即动画开始前和动画结束后,对象元素均处于其原始状态。
  • animation-play-state

animation-play-state顾名思义控制动画状态的,两个属性值也好理解。

- **running**
表示让动画执行
- **paused**
表示暂停动画
可以通过改变这个属性来控制动画的播放和暂停,pause改为running后不会让元素重头再执行一遍动画,而是接着暂停时的状态继续变化。
  • animation-direction

这个属性用于控制动画是正向执行,或者反向执行,如果设置了animation-iteration-count重复执行次数,还能控制一次动画执行完毕后,以何种方式执行接下去的一次动画。有四个属性值normal | reverse | alternate | alternate-reverse

 - **normal**
 默认属性值,动画正向执行,重复执行动画时,每次都从起始状态开始变化。
 - **reverse**
 动画反向执行,即从指定的终止状态变化到起始状态,重复执行动画时,每次都从终止状态开始变化。其中animation-timing-function设置的运动曲线也会被颠倒,原来的ease-in会变成ease-out的效果。
 - **alternate**
 第一次执行动画时,从起始状态开始变化。重复执行动画时,单次动画结束后,下一次动画以当前状态作为起始状态开始变化,以上一次动画的起始状态作为该次动画的终止状态,相当于执行一次reverse的动画效果。
 - **alternate-reverse**
 和alternate类似,但是第一次执行动画时,从指定的结束状态开始向起始状态变化。

可以用animation来缩写以上所有属性:

animation:<single-animation> [ ‘,’ <single-animation> ]*

<single-animation> = <single-animation-name> || <time> || <single-animation-timing-function> || <time> || <single-animation-iteration-count> || <single-animation-direction> || <single-animation-fill-mode> || <single-animation-play-state>

其中第一个<time>为animation-duration,第二个<time>为animation-delay。

再来看与animation紧密相关的@keyframes
@keyframes的作用类似values + keyTimes,可精细设置动画过程中每个变化节点。
@keyframes必须包含一个动画起始状态信息,和一个动画终止状态信息。可以用from + to,也可以用0% + 100%:

@keyframes rotate1{
    from {
        left: 0;
        top: 0;
    }
    to {
        left: 100px;
        top: 100px;
    }
}
@keyframes rotate2{
    0% {
        left: 0;
        top: 0;
    }
    100%{
        left: 100px;
        top: 100px;
    }
}

以上两段代码等价,都表示起始状态的left和top属性为0,终止状态的left和top属性为100px。
对于中间动画节点,比如在动画进行到四分之一时,希望left为50px:

@keyframes rotate2{
    0% {
        left: 0;
        top: 0;
    }
    50%{
        left: 50px;
    }
    100%{
        left: 100px;
        top: 100px;
    }
}

另外,还可以对每段动画的变化曲线做单独设置animation-timing-function属性,属性需要设置在起始状态信息中:

@keyframes rotate2{
    0% {
        left: 0;
        top: 0;
        animation-timing-function: ease-out;
    }
    50%{
        left: 50px; 
        animation-timing-function: ease-in;
    }
    100%{
        left: 100px;
        top: 100px;
    }
}
查看原文

赞 19 收藏 26 评论 6

阿古达木 赞了文章 · 8月26日

前端也需要好好的精进自己的算法

算法

前端发展的再快,也不要忘记精进自己的算法,算法是灵魂和核心。我会把我刷过的算法题总结归类,不断完善。欢迎大家关注

数组和堆栈

递归

链表

二叉树和递归

递归和回溯

动态规划

贪心算法

双指针,滑动窗口

其他

查看原文

赞 445 收藏 383 评论 10

阿古达木 赞了文章 · 8月24日

关于CommonJS、AMD、CMD、UMD说明

CommonJS规范

CommonJS定义的模块分为3部分:

  1. require 模块引用

  2. exports 模块导出

  3. module 模块本身

根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的

// a.js

var a = {
    "a":a,
    "b":b
}

module.export = a //模块导出

// b.js
var b = require('./a.js') //模块引入

CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作

AMD(Asynchromous Module Definition)规范

  1. AMD 加载模块是异步

  2. define(id?, dependencies?, factory);

    id: 模块标识,可以省略。
    dependencies: 所依赖的模块,可以省略。
    factory: 模块的实现,或者一个JavaScript对象。    
    
   //a.js 只有factory
   define(function() {
       return {
           mix: function(source, target) {
               ...
            }
        };
    });
    
    //b.js 依赖a.js
    define(['a'], function(a) {
        return {
            show: function() {
               ...
            }
        }
    });
    
    //c.js 依赖a.js b.js
    define(['a', 'b'], function(a, b) {
        ....
    });
    
    //d.js 对象模块
    define({
        data1: [],
        data2: []
    });

AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:

    define(function (require, exports, module) {
        var reqModule = require("./a.js");
        requModule.mix();
        
        exports.asplode = function () {
            ...
        }
    });

CMD(Common Module Definition)规范

CMD和AMD的区别有以下几点:

  1. 对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)

  2. CMD推崇依赖就近,AMD推崇依赖前置

    //AMD写法
    define(['./a','./b'], function (a, b) {
        //依赖一开始就写好
        a.mix();
        b.show();
    });
    
    //CMD写法
    define(function (requie, exports, module) {
        //依赖可以就近书写
        var a = require('./a');
        a.mix();
        
        if (...) {
            var b = requie('./b');
            b.show();
        }
    });

UMD(Universal Module Definition)规范

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node, CommonJS-like
        module.exports = factory(require('jquery'));
    } else {
        // Browser globals (root is window)
        root.returnExports = factory(root.jQuery);
    }
}(this, function ($) {
    //    methods
    function myFunc(){};

    //    exposed public method
    return myFunc;
}));

应用UMD规范的js文件其实就是一个立即执行函数。函数有两个参数,第一个参数是当前运行时环境,第二个参数是模块的定义体。在执行UMD规范时,会优先判断是当前环境是否支持AMD环境,然后再检验是否支持CommonJS环境,否则认为当前环境为浏览器环境( window

查看原文

赞 9 收藏 33 评论 0

阿古达木 关注了问题 · 8月23日

怎么使用es6 的class 优雅地写出单例模式?

如题,目前只想到通过全局变量的模式

var instance = null;

class Cache{  
    constructor() {
        if(!instance){
              instance = this;
        }
        return instance;
      }
}

但是明显的,这个方法并不优雅,求大神指点

关注 6 回答 4

阿古达木 关注了专栏 · 8月20日

SegmentFault 行业快讯

第一时间为开发者提供行业相关的实时热点资讯

关注 24895

阿古达木 赞了文章 · 8月20日

美国如果把根域名服务器封了,中国会从网络上消失?

美国如果把根域名服务器封了,中国会从网络上消失?
作者:卫剑钒
来源:公众号微月人话


自从美国宣布“清洁网络”行动后,很多懂点网络的人,第一反应是,美国人会下手根域名服务器吗?

这种忧虑可不是一年两年了。

2014年6月24日的《人民日报》上引用专家发言:“目前美国掌握着全球互联网13台域名根服务器中的10台。理论上,只要在根服务器上屏蔽该国家域名,就能让这个国家的国家顶级域名网站在网络上瞬间“消失”。在这个意义上,美国具有全球独一无二的制网权,有能力威慑他国的网络边疆和网络主权。譬如,伊拉克战争期间,在美国政府授意下,伊拉克顶级域名“.iq”的申请和解析工作被终止,所有网址以“.iq”为后缀的网站从互联网蒸发。”_(1)_

《信息安全与通信保密》杂志2014年第10期的一篇文章写道:“2004年,由于与利比亚在顶级域名管理权问题上发生争执,美国终止了利比亚的顶级域名.LY的解析服务,导致利比亚从网络中消失3天。”_(2)_

对此,我们需要害怕吗?我们需要什么样的反制措施?

不是专家,还真回答不了这个问题。

因为这需要了解DNS的工作原理,了解根域名的管理机制。

这里先给出简要回答:不排除这种可能性,但并不是没有办法。

一句话原因:虽然根不在我们手里,但我们有镜像。

DNS傻瓜书

先了解点基本概念,懂DNS的可以直接跳过本节。

1、DNS是什么?

DNS就是将域名转换为IP的,因为我们人类的记忆力太差,根本记不住IP,而电脑通信又必须用IP,所以人类发明了域名,让我们可以记住baidu.com、taobao.com这种还算能记得住的域名。然后通过DNS,将这些域名转换为电脑需要的IP。

2、DNS是怎么工作的?

每个电脑里面都设置了本地DNS服务器(简称LDNS),需要的时候,就向LDNS发出请求,LDNS在网上问权威域名服务器(简称权威DNS),有时候问一家是不够的,要问一大圈下来,最后才能得到答案。

3、权威DNS是干什么的?

问我一个域名,我告诉你IP,如果我不知道,我告诉你谁可能知道,你再去问它。

4、什么是根域名服务器(简称根DNS)?

当LDNS啥都不知道的时候(也即没有任何缓存),就去问根DNS,根能告诉LDNS下一步该问谁。

5、全世界有多少根DNS?

13个,其中10个在美国,英国和瑞典各1个,日本1个。

6、根DNS的名字和IP都是什么?

在这个网址:

https://www.internic.net/doma...

打开可以看到,里面有13个根的名字和IP,其名字从A.root-servers.net到M.root-servers.net。

A开头那个简称A根,是主根,其他12个(B、C、D、E、F、G、H、I、J、K、L、M)是辅根。

为什么根DNS只有13台?

本节看不懂没关系(一般人都看不懂),你只需要知道,由于历史原因和技术原因,对于IPv4而言,根DNS只能有13个IP。

正宗答案是:DNS主要使用UDP数据报传送报文,不含前面的各种头部,DNS报文要求被控制在512字节之内( RFC1035 ),主要考虑是这个大小几乎可以在互联网上畅通无阻,不会因为路径中某个MTU太小( MTU 通常总会 >= 576,见 RFC791 )而导致IP分片,从而预防了各种不可预期的后果_(3)_。

而每一个根DNS在DNS报文中都要占用一定的字节数,比如根的名称、TTL、IP地址等。这样,13个根域名服务器基本上就把空间占差不多了,剩余的字节还要用于包装DNS报头以及其它协议参数,所以根域名服务器不易太多,13个算是比较合适的数目。具体可以看一下“Why 13 DNS root servers?”这篇文章。_(4 )_

真的只有13台服务器吗?

和很多人想象的完全不一样,这13个根域名服务器,并不是只有13台物理的服务器。

这13个根,只是一个逻辑上的概念,每个根DNS,背后都有多台真正的物理服务器在工作!

截至2020年8月12日,全球一共有1097个根服务器。每一个根都有若干个镜像,分布在全球不同的地方。

这个数目在不断上涨,去年10月1日新中国成立70周年阅兵的时候,我看了一下,是1015个服务器。

这13个根由12个独立的机构管理,比如A根和J根都是由Verisign公司管理,截至2020年8月12日,A根在全球各地有53个站点,J根有185个站点。L根由ICANN管理,全球有167个站点,其中北京2个,上海1个。

在root-servers网站上_(5)_,可以查到所有这些根服务器的分布,从网站展示的根镜像服务器地图上看(2020年8月12日),北京有 5 个根镜像服务器,上海 1 个,杭州 2 个,武汉1个、郑州1个、西宁1个、贵阳1个、广州1个、香港 9 个,台北 6 个。

包含港澳台部分,我国一共有28个根镜像。

我国境内发出的对根DNS的请求,其实都由镜像完成了。这一点后面会解释。

现在,为了增长知识,你该硬着头皮看一些DNS细节了。

DNS到底是怎么工作的?

对于IT从业者,希望你能理解并牢牢记住本节的内容。

因为你迟早会遇到有关DNS的困惑。

先介绍一下域名的级别:

.代表根域名, .com这种是顶级域名,也叫一级域名,baidu.com这种叫二级域名, www.baidu.com这种叫三级域名,依次类推。

注:也有其他叫法的,反正你知道这个意思就可以了。

再介绍一下最常见的两种域名服务器:

权威DNS:负责对请求作出权威的回答。权威DNS中存储着记录,最常见的3种:A记录(记录某域名和其IP的对应),NS记录(记录某域名和负责解析该域的权威DNS),CNAME记录(负责记录某域名及其别名)。权威能直接回答的,就回A记录;需要其他权威DNS回答的,就回NS记录,然后LDNS再去找其他权威DNS问;如果该记录是别名类型的,就回CNAME,LDNS就会再去解析别名。

递归DNS:通常就是LDNS,它接受终端的域名查询请求,负责在网上问一圈后,将答案返回终端。

现在举一个具体的例子:比如终端请求www.baidu.com这个域名的IP。

在没有缓存时,LDNS会从根DNS问起:

1、LDNS问根DNS说:“www.baidu.com的IP是多少啊?”。

2、根DNS说:“我哪有时间管你这么细的问题,你去问com顶级域的DNS吧,我只管到顶级域,喏,这些是com顶级域DNS的名字和IP,你去问它们吧”。(以NS记录回应)

3、LDNS又忙问com的权威DNS,com权威DNS说:“你问的这是三级域名,我不管这么多,你去问baidu.com的权威DNS吧,它的名字是ns.baidu.com,他的IP是XXX(这里可能给出多个权威DNS)”。

4、LDNS继续问baidu.com的权威DNS,这次痛快,因为www.baidu.com正是它管的,它可能直接给出A记录,也可能给出CNAME记录,如果是前者,就直接得到IP,如果是后者,就需要对别名再做查询。

5、最终,LDNS得到www.baidu.com的IP,并将其返回给终端。

美国如果把根域名服务器封了,中国会从网络上消失?

细心的人会问,在第1步中,LDNS问根DNS的时候,他是怎么知道根DNS的IP的?

这13个IP通常是预先配置在LDNS里面的。在LDNS初始化DNS缓存或者缓存失效的时候,LDNS向自己被预先配置的这些IP中的一个,发起对根的查询(也即询问.的NS记录),获得最新的根DNS的信息_(6)_。

对于DNS服务器软件而言,这13个IP,配置在根提示文件(root hints file)中,可能是named.cache或root.ca或root.hints等等之类的文件。

上面就是各种教科书中都会讲到的DNS查询过程,但实际上,没有这么麻烦,因为各个层面都是有缓存的。

实际DNS查询的过程,是这样的:

举个例子,比如用户在浏览器中输入这个域名:123.abc.qq.com.cn

1、浏览器会先看自身有没有对这个域名的缓存,如果有,就直接返回,如果没有,就去问操作系统,操作系统也会去看自己的缓存,如果有,就直接返回,如果没有,再去hosts文件看,也没有,才会去问LDNS。

2、LDNS会去先看看自己有没有123.abc.qq.com.cn的A记录,要有就直接返回,要没有,就去看有没有abc.qq.com.cn的NS记录,如果有,就去问它要答案,如果没有,就去看有无qq.com.cn的NS的记录,如果有,就去问它,没有就去看有无com.cn的DNS,还没有就去看有无cn的DNS,如果连cn的NS记录都没有,才去问根。

所以,有了缓存以后,教科书上那种从根问起的情况,实际上很少发生。

只有在各处都没有缓存的时候,我们才会问根。

根镜像起什么作用?

根镜像承担起和根一样的功能。

根DNS中,最重要的文件就是根区文件(Root Zone file)。所有顶级域名记录都存在根区文件中。

辅根从主根同步数据,根镜像从根同步数据。最终,所有根和镜像都有着同样的根区文件。

而且最有意思的是,根镜像和根有着同样的IP。

我们知道,全球有一千多个根镜像,但是大多数人不知道,它们一起共享13个IP!  对的。因为只有13个根。

这是如何做到的?答案是任播(Anycast,又译泛播)技术。

不关心技术细节的,请直接看本节的最后一句。

任播最初由RFC1546提出,主要用在DNS根服务器上。

任播是指在IP网络上通过一个IP地址标识一组提供特定服务的主机,服务访问方并不关心提供服务具体是哪一台主机提供的,访问该地址的报文可以被IP网络路由到“最近”的一个(最好也只是一个,别送到多个)服务器上。这里“最近”可以是指路由器跳数、服务器负载、服务器吞吐量、客户和服务器之间的往返时间( RTT,round trip time )、链路的可用带宽等特征值。

这样,一方面,用户可以就近访问;另一方面,即便部分根出现故障也没事。

有些同学可能联想到负载均衡,没错,大致上就是这个意思。

对于中国用户来说,对根的请求,一般不会跑到美国去,而是通过任播技术路由到中国境内的根镜像上。

根DNS是怎么管理的?

根DNS目前由12家机构管理。A根是主根,由美国公司Verisign管理。

根DNS中最重要的文件,根区文件,由ICANN管理。

ICANN(The Internet Corporation for Assigned Names and Numbers,互联网名称与数字地址分配机构)是成立于1998年的一家注册在美国的非营利性组织。

根DNS管理的历史变迁过程还是比较复杂的。这里简要说一下。

DNS最初的技术开发者与管理者是美国南加州大学的Jon Postel博士,他掌管互联网初期根DNS的管理和分配。

1988年,美国政府要求Jon Postel采取更安全和更合理的措施来保证互联网核心资源的分配和管理_(7)_。于是,大名鼎鼎的IANA(The Internet Assigned Numbers Authority,互联网数字分配机构)被组建,并在DARPA和南加州大学信息科学研究所(ISI)的合同下管理。

IANA负责互联网全局编号和编码的管理与协调,之所以需要这么个机构,是因为互联网协议的值或参数,必须是全球唯一的,否则无法互联互通,比如HTTP协议默认都在80端口等待用户请求,而404编码则一致代表"未找到页面”。IANA主要职责包括IP地址段的分配、协议代码和编号的分配(如协议号、端口号)、自治系统编号 (ASN) 分配、DNS根区管理(包括通用顶级域名gTLD以及国家和地区顶级域名ccTLD管理)等。_(8)_

1998年ICANN成立之后,美国商务部以合同形式,委托ICANN承担IANA日常运行,IANA从ISI转移到ICANN之下。

对于顶级域名的管理,ICANN的政策是,每个顶级域名(像com、cn、org这种顶级域名,目前有1000多个)都找一个托管商,该域名的所有事项都由托管商负责。

.cn域名的托管商是中国互联网络信息中心(CNNIC),它决定.cn域名的各种政策。

.com、.net 、.name、.gov这四个顶级域名都由Verisign公司托管。

Verisign和ICANN还是闹过几次不愉快的。_(9)_

2003年,Verisign 推出了一项新业务 Site Finder,用户访问没有注册过的.com或.net域名,都会被导向 Verisign 的网站。这意味着,它事实上拥有了所有没有注册过的.com和.net域名。几天之内,Verisign 就挤入了全世界的前10大网站。

ICANN 要求 Verisign 立刻停止该业务,否则将终止域名托管合同。Verisign 屈服了,停止了这项业务,但是接着就把 ICANN 告上了法庭,要求法庭厘请两者之间的合同,ICANN 到底有没有权力干涉它的业务。

2006年底,他们达成了庭外和解。ICANN 同意延长 Verisign 的顶级域名托管合同,并且同意 Verisign 向消费者收取的单个域名注册费的上限,从6美元提高到了7.85美元。这个费用标准,一直沿用到了今天,你去注册一个.com或.net域名,所交的钱有0.18美元是 ICANN 收取的管理费,7.85美元是 Verisign 收取的托管费,其余的钱就是域名零售商的费用。

虽然是ICANN运营着IANA,但毕竟是在美国政府的合同管理之下,全球各国以及民间人士颇有微词,一致认为美国政府应该彻底退出。

2014年3月14日,美国商务部国家通讯与信息管理局(NTIA)宣布愿意将IANA的管理权完全移交给ICANN,并要求ICANN制定移交计划。NTIA尤其强调,移交计划要强化多利益相关方模式,不能以政府间组织或政府领导的组织取代当前NTIA扮演的角色。

2016年3月17日,ICANN向NTIA提交了移交计划。2016年6月9日,NTIA公布审核意见,表示ICANN提交的移交计划满足了此前设定的条件。

2016年8月16日,NTIA宣布不再延期现有合同。

虽然遇到一些阻挠_(10)_,最终,2016年10月1日,ICANN和美国商务部之间关于IANA职能的合同到期且不再续约,ICANN彻底成为独立的非营利机构。IANA部门的员工和其他的相关资源都被转移到ICANN新设立的附属机构PTI(Public Technical Identifiers,公共技术标识符)中。

ICANN使用全球多利益相关方治理模型(global multistakeholder governance model)进行管理。PTI董事会共5席,3席由ICANN委派,2席由全球互联网社群代表组成提名委员会产生。2017年2月,ICANN发布PTI董事竞选公告,经半年多轮面试及背景调查,提名委员会于2017年10月26日宣布我国北龙中网的王伟与另一欧洲代表中选。又经一个半月的利益冲突审查,2017年12月13日ICANN董事会正式确认王伟当选。_(11)_

我国的根镜像由谁管理?

从目前我所找到的资料看,自2003年以来,我国在不断引进根镜像,尤其是去年,根镜像个数增速很快。

2003年,中国电信引入了国内第一个根镜像节点(F根)。

2005年,I根服务器运行机构在 CNNIC 设立了中国第二个根镜像(I根)。

2006年,中国联通(原中国网通)与美国 VeriSign 公司合作, 在国内正式开通J根镜像服务器,同时引入了全球最大的两个顶级域名 “.COM”和“.NET”镜像节点;引进这些镜像的主要目的是提高根域名和顶级域名的解析性能。

2014年,世纪互联与ICANN合作在中国增设L根域名服务器镜像。

2019年6月24日,工信部批准CNNIC设立六台域名根镜像服务器(F、I、K、L)。这六台域名根服务器编号为 JX0001F、JX0002F、JX0003I、JX0004K、JX0005L 和 JX0006L_(12)_,并批准互联网域名系统北京市工程研究中心(ZDNS)设立L根镜像服务器JX0007L_(13)_。

2019年11月6日,工信部批复同意中国信息通信研究院设立L根镜像服务器,编号分别为JX0008L、JX0009L。

2019年12月5日,工信部批复同意中国信息通信研究院设立域名根服务器(K根镜像服务器),编号为JX0010K。

2019年12月9日,工信部批复同意CNNIC设立域名根服务器(J、K根镜像服务器),编号分别为JX0011J、JX0012K。

从工信部的批文中可以了解到,相关单位负责根镜像的运行、维护和管理工作,维护国家利益和用户权益,并接受工信部的管理和监督检查。

工信部在给CNNIC的批文中写道:“你中心应严格遵守《互联网域名管理办法》《通信网络安全防护管理办法》及相关法律法规、行政规章及行业管理规定,接受我部的管理和监督检查,建立符合我部要求的信息管理系统并与我部指定的管理系统对接,保证域名根服务器安全、可靠运行,为用户提供安全、方便的域名服务,保障服务质量,保护用户个人信息安全,维护国家利益和用户权益。”

美国能对根DNS做什么手脚?

虽然ICANN是一个独立的非营利性机构,但如果美国政府动用强制力量,A根(主根)的内容仍然存在被篡改的可能。

也就是根区文件可以被篡改。

会怎么篡改?

我们先看看根区文件长什么样。

从ICANN官网上可以下载根区文件:

https://www.iana.org/domains/...

该文件保存所有顶级域名的信息,目前大小为2.2M,2万余行。

每当有顶级域名的变动时,该文件就会更新。

我们可以看到,和cn域名解析相关的记录也就那么几十行。

美国如果把根域名服务器封了,中国会从网络上消失?

如果删除和cn相关的那些行,很快,就会同步到所有的根中。

然后,在所有的缓存都过期之后,全球所有人都访问不了.cn后缀的网站。

如何应对?

因为我们维护着根镜像,所以我们控制着镜像中的内容。

而中国境内的对根的访问,通过我们的运营商,都会落到对我国根镜像的访问上。

我们可以不同步关于cn的修改。

就这么简单。

可以简单写个程序,每次同步完立刻加上cn记录。

也可以自己搭个主根,完全不和美国的根同步。(相当于另立中央了)

当然,世界各地不在我们管理之下的根和根镜像,如果不加行动,仍然会同步这些删除。

那么,除了中国自己,其他国家的人都无法访问.cn网站。

但是,这些国家很快就会有响应,凡是想访问.cn网站的国家,都会把cn记录加回去,并拒绝同步美国删去的这几行。

最终,只有美国人,访问不了.cn网站。

综上分析,我认为美国这么做的可能性不大,因为这一招过于低劣,将会让美国政府完全颜面扫地,并失去今后在互联网领域的任何话语权。而ICANN也将失去公信力,整个互联网世界,会推选使用新的机构和新的主根。

因为互联网世界的一贯准则就是:如有封禁,就绕过它。

后记

最后,我们看看本文开头所提的两个断网事件是怎么回事:

关于伊拉克域名事件,可以看看清华大学段海新教授的文章:“伊拉克域名.IQ被美国删除的背后以及早期的根域名管理”,里面把整个事件的来龙去脉说的很清楚。主要原因是.iq域名的前任管理者于2002年被关进监狱,新任管理者(NCMC)于2005年才提出申请,而IANA当时还考虑征求新旧代理双方对新授权的一致认可,所以才出现了所谓的“申请和解析工作被终止”。

关于利比亚域名事件,可以看看此文:“利比亚国家顶级域名(.LY)中止服务始末”,事实情况是参与运营.LY的两家机构因争夺归属权而内斗的结果(其中一方关闭了.LY域名服务器的解析)。经过这番变乱,2004年10月,ICANN批准将.LY授予利比亚邮电总公司,.LY事件算是尘埃落定。

本文中提到的风险和应对,主要是我个人的分析,下面看看业内专家的说法。

中国工程院院士、清华大学计算机系主任吴建平在2019年的一次访谈_(14)_中表示,DNS根域名服务器不是互联网的“核按钮”。全球互联网根域名服务器运行者,不可能同时关闭所有的根服务器,包括影子服务器。

互联网域名系统北京市工程研究中心(ZDNS)主任毛伟表示_(15)_:互联网专家一直都在不断完善域名根系统安全保障机制,就算真的断“根”了,也有应急方法来解决。在境内,可以采用根区数据备份并搭建应急根服务器来解决;在全球层面,可以用根镜像、IPv6环境下的根服务器数量扩展、根服务器运行机构备选机制等方法来解决。

现在,了解了这么多,关于根域名服务器,你是不是放心了很多。


参考文献:

  1. 从网络大国走向网络强国(http://opinion.people.com.cn/...
  2. 美国网络霸权浅析(http://www.wanfangdata.com.cn..._type=perio&id=xxaqytxbm201410030)
  3. 为什么域名根服务器只能有13台呢?(https://www.zhihu.com/questio...
  4. Why 13 DNS root servers?(https://miek.nl/2013/november...
  5. https://root-servers.org
  6. Initializing a DNS Resolver with Priming Queries(https://tools.ietf.org/html/d...
  7. 薛虹:互联网全球治理的新篇章(https://zhuanlan.zhihu.com/p/...
  8. ICANN: IANA职能(https://www.icann.org/zh/syst...
  9. 阮一峰:根域名的知识
  10. 徐培喜:IANA职能管理权移交谁是赢家
  11. 北龙中网王伟任职PTI董事 我国专家就任国际互联网治理关键岗位(http://news.sina.com.cn/c/201...
  12. 工业和信息化部关于同意中国互联网络信息中心设立域名根服务器(F、I、K、L根镜像服务器)及域名根服务器运行机构的批复(http://www.miit.gov.cn/n11462...
  13. 工业和信息化部关于同意互联网域名系统北京市工程研究中心有限公司设立域名根服务器(L根镜像服务器)及域名根服务器运行机构的批复(http://www.miit.gov.cn/n11462...
  14. 中国工程院院士吴建平:DNS根服务器不是互联网的核按钮!
  15. ZDNS毛伟:互联网根并不能让中国断网,更应重视企业域名服务风险

原文链接

segmentfault 公众号

查看原文

赞 30 收藏 9 评论 2

阿古达木 关注了专栏 · 8月20日

【前端有的玩】公众号

我是子君,我的公众号是【前端有的玩】,欢迎关注。每周我会至少分享一篇前端相关文章,希望可以帮助到致力于前端开发的你。

关注 1722

阿古达木 赞了文章 · 8月17日

【吐血整理】前端面试全攻略,为您保驾护航,🤑金三银四🤑不在话下

2020给自己一个把基础打扎实的可能

🤡🤡🤡🤡👹👹👹👹👽👽👽👽
🤕 日常积累以及深入浅出研究 🤒
😺😺😺😺🙀🙀🙀🙀😻😻😻😻

几个维度去成长自己:

  • 🍏STAR法则
    这个不多介绍,自己找。
  • 🍎刷面试题
    市面上面试题铺天盖地,数不胜数;不要迷惑,要学会归纳和总结,并且发掘面试题思路、把底层技术牢牢深挖,即使进度很慢也值得,一步一步踏实前进。
  • 🍐思维导图
  • 🍊常逛技术网站
    github、sf.gg、掘金...
    公众号、国外网站、其它...多得是
    甚至多去思考和一些代码库的实践思路和分析源码
  • 🍋 规划技术路线
    有条有理得规划自己技术路线
  • 🍉学习周边知识
    零零散散的一些知识点,产品、运营知识点,甚至UIUX
  • 🍇数据库、算法、爬虫、HTTP
  • 🍓服务器、docker、中台、微前端...
  • 🍒node.js、V8、python、go

清单一🤑🤑🤑

...

查看更多请点击:《前端金三银四》之面试清单一

清单二🤑🤑🤑

...

查看更多请点击:《前端金三银四》之面试清单二

清单三🤑🤑🤑

...

查看更多请点击:《前端金三银四》之面试清单三

更多请麻烦关注Thanks♪(・ω・)ノ

Vue3+ & Vue-CLI3+ 开发生态圈资讯【🎯Find the latest breaking √vue3 & vue-cli 3+ News.

🚀欢迎Star,后续会不断更新。
🇨🇳最后更新日期:2️⃣0️⃣2️⃣0️⃣/0️⃣3️⃣/3️⃣1️⃣

【2020】 ≡≡≡≡≡≡≡--------------------------------- 【2021】

Twitter vue3: vue 3 will be available by the end of Q2.

除了单独Vue3资讯,欢迎查看更多vue.js资讯:【【🔥Vue.js资讯📚】目前web前端开发非常火爆的框架;定时更新,欢迎 Star 一下。

屏幕快照 2020-04-04 09.49.16.png

海边看日出🌅,每一天给自己加油ヾ(◍°∇°◍)ノ゙

查看原文

赞 105 收藏 81 评论 2

阿古达木 关注了专栏 · 8月17日

凹凸实验室

凹凸实验室(Aotu.io,英文简称O2) 始建于2015年10月,是一个年轻基情的技术团队。 O2面向多终端技术体系,致力于构建沉淀与分享包括但不限于交互、页面制作技巧、前端开发、原生APP开发等方面的专业知识及案例。

关注 1980

阿古达木 回答了问题 · 8月16日

解决vue data中的引用数据类型作为函数参数的问题

所以对于参数是引用类型的情况,参数传递的实际上是这个对象的地址
arr = []实际上是开辟了一个新的空间,新的地址,所以原数据没有发生改变
简单概括:
函数的参数是值传递,对象类型作为参数的时候传递的是地址(指针)的值,而不是对象本身堆内存中的value

所以在这种场景,函数内部用参数去修改对象,那么查找到的还是原对象,因为指向相同,所以修改的话原对象也受影响

如果新实例化一个对象赋值给改指针,那么指针指向的就是一个全新的对象了,和原来指向的对象失去联系

关注 3 回答 2

认证与成就

  • 获得 11 次点赞
  • 获得 4 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-06-21
个人主页被 175 人浏览