position

CSS 中 position 属性用于指定元素的定位方法的类型(static、relative、absolute、fixed、sticky)。 静态定位 position: static 此属性未 HTML 元素默认定位,一个元素没有以任何特殊的方式定位,它总是按照页面的正常流程定位。 在此属性下,属性值 top、left、right、bottom 和 z-index 对HTML元素没有影响。 <div class="parent"> <div class="child"></div> </div> <div class="sibling"></div> <style type="text/css"> .parent { width: 480px; height: 320px; background-color: #008800; } .child { width: 240px; height: 160px; background-color: #ff0000; } .sibling { width: 240px; height: 160px; background-color: #0000ff; } </style> 效果如图: 为什么使用它呢?将元素设置为 position: static 的唯一原因是强制删除应用于无法控制的元素上的某些定位。 相对定位 position: relative 此属性相对于其正常位置的,在不改变布局的情况下根据元素的当前位置定位元素。 ...

October 8, 2024

生命周期

vue3生命周期 在Vue中,生命周期指的是Vue实例从创建到销毁的过程。在这个过程中,Vue实例会经历一系列的阶段,包括数据观测、模板编译、挂载DOM、更新DOM、卸载等等。在这些阶段,Vue提供了一些生命周期钩子函数,让我们可以在特定的时机执行某些代码。 为什么需要生命周期 生命周期钩子函数为我们提供了在Vue实例生命周期不同阶段执行代码的能力。这对于非响应式的操作非常有用,例如直接操作DOM、添加事件监听器或者创建定时器等。这些操作如果在数据变化时执行可能会导致不必要的性能开销,而生命周期钩子函数让我们可以更合理地安排这些操作的执行时机。一些常用的库,例如react,webpack都提供了生命周期的钩子,方便开发者理解整个运行流程。 Vue3 有哪些生命周期,分别在什么时候调用? Vue3的生命周期钩子函数主要包括: beforeCreate: 创建实例之前调用,此时实例的数据观测、事件等还未初始化。 created: 创建实例后调用,此时实例的数据观测、事件等已经初始化完成。 beforeMount: 挂载实例之前调用,此时模板已经编译完成,但是还未挂载到DOM。 mounted: 挂载实例后调用,此时实例已经挂载到DOM,可以进行DOM操作。 beforeUpdate: 数据更新之前调用,此时可以进行状态的最后更改。 updated: 数据更新后调用,此时DOM已经完成更新,可以进行DOM操作。 beforeUnmount: 卸载实例之前调用,此时实例仍然完全可用。 unmounted: 卸载实例后调用,此时实例已经完全卸载。 生命周期类型的枚举位于packages/runtime-core/src/enums.ts目录下: export const enum LifecycleHooks { BEFORE_CREATE = 'bc', // 创建之前 CREATED = 'c', // 创建 BEFORE_MOUNT = 'bm', // 挂载之前 MOUNTED = 'm', // 挂载 BEFORE_UPDATE = 'bu', // 更新之前 UPDATED = 'u', // 更新 BEFORE_UNMOUNT = 'bum', // 卸载之前 UNMOUNTED = 'um', // 卸载之后 DEACTIVATED = 'da', // 被 keep-alive 缓存的组件停用时调用。 ACTIVATED = 'a', // 被 keep-alive 缓存的组件激活时调用。 RENDER_TRIGGERED = 'rtg', // 依赖被收集时调用 RENDER_TRACKED = 'rtc', // 依赖被触发时调用 ERROR_CAPTURED = 'ec', // 错误捕获 SERVER_PREFETCH = 'sp' } 上面生命周期可以用一张图来表示: 选项式和组合式生命周期的区别 在Vue3中,我们可以使用选项式API和组合式API来定义组件。这两种API对应的生命周期钩子函数有一些区别。 选项式API的生命周期钩子函数定义在组件选项对象的methods中,例如: export default { methods: { created() { // do something } } } 组合式API的生命周期钩子函数定义在setup函数中,需要使用import从vue中引入,例如: import { onMounted } from 'vue'; ​ export default { setup() { onMounted(() => { // do something }); } } 需要注意的是,在组合式API中没有beforecate和created钩子,因为他们本身被setup这个函数替代了,直接把相关逻辑写在setup中就行了 ...

October 4, 2024

export/import

export,import 是什么 我们知道,JS 模块导入导出,使用 import , export 这两个关键字 export 用于对外输出本模块 import 用于导入模块 也就是说使用 export 导出一个模块之后,其它文件就可以使用 import 导入相应的模块了 下面我们具体看看, import 和 export 到底怎么用?怎么导出模块(比如变量,函数,类,对象等) 1 导出单个变量 //a.js 导出一个变量,语法如下 export var site = "www.helloworld.net" //b.js 中使用import 导入上面的变量 import { site } from "/.a.js" //路径根据你的实际情况填写 console.log(site) //输出: www.helloworld.net 2 导出多个变量 上面的例子是导出单个变量,那么如何导出多个变量呢 //a.js 中定义两个变量,并导出 var siteUrl="www.helloworld.net" var siteName="helloworld开发者社区" //将上面的变量导出 export { siteUrl ,siteName } // b.js 中使用这两个变量 import { siteUrl , siteName } from "/.a.js" //路径根据你的实际情况填写 console.log(siteUrl) //输出: www.helloworld.net console.log(siteName) //输出: helloworld开发者社区 ``` ### 3 导出函数 导出函数和导出变量一样,需要添加`{ }` ````js //a.js 中定义并导出一个函数 function sum(a, b) { return a + b } //将函数sum导出 export { sum } //b.js 中导入函数并使用 import { sum } from "/.a.js" //路径根据你的实际情况填写 console.log( sum(4,6) ) //输出: 10 ``` ### 4 导出对象 js中一切皆对象,所以对象一定是可以导出的,并且有两种写法 #### 4.1 第一种写法 使用 `export default` 关键字导出,如下 ````js //a.js 中,定义对象并导出, 注意,使用export default 这两个关键字导出一个对象 export default { siteUrl:'www.helloworld.net', siteName:'helloworld开发者社区' } //b.js 中导入并使用 import obj from './a.js' //路径根据你的实际情况填写 console.log(obj.siteUrl) //输出:www.helloworld.net console.log(obj.siteName) //输出:helloworld开发者社区 ``` #### 4.2 第二种写法 同样是使用`export default` 关键字,如下 ````js //a.js 中定义对象,并在最后导出 var obj = { siteUrl:'www.helloworld.net', siteName:'helloworld开发者社区' } export default obj //导出对象obj //b.js 中导入并使用 import obj from './a.js' //路径根据你的实际情况填写 console.log(obj.siteUrl) //输出:www.helloworld.net console.log(obj.siteName) //输出:helloworld开发者社区 ``` ### 5 导出类 导出类与上面的导出对象类似,同样是用 `export default` 关键字,同样有两种写法 #### 5.1 第一种写法 ````js //a.js 中定义一个类并直接导出 export default class Person { //类的属性 site = "www.helloworld.net" //类的方法 show(){ console.log(this.site) } } //b.js 中导入并使用 //导入类 import Person from './a.js' //创建类的一个对象person let person = new Person() //调用类的方法 person.show() //输出:www.helloworld.net ``` #### 5.2 第二种写法 ````js //a.js 中定义一个类,最后导出 class Person { //类的属性 site = "www.helloworld.net" //类的方法 show(){ console.log(this.site) } } //导出这个类 export default Person //b.js 中导入并使用 //导入类 import Person from './a.js' //创建类的一个对象person let person = new Person() //调用类的方法 person.show() //输出:www.helloworld.net ``` ### 小结 下面我们简单总结一下 `export`与`export default`的区别 * export与export default均可用于导出常量、函数、文件、模块等 * 可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用 * export default后面不能跟const或let的关键词 * export、import可以有多个,export default仅有一个。 * 通过export方式导出,在导入时要加 { },export default则不需要 * export具名导出xxx ,export default匿名。区别在于导入的时候,export需要一样的名称才能匹配,后者无论取什么名都可以。 * 模块化管理中一个文件就是一个模块,export可以导出多个方法和变量,export default只能导出当前模块,一个js文件中只支持出现一个 对于 `import` ,`export` , `export default` ,他们的用法上面的例子已经很详细的列出了,忘记的时候,可以当作参考看看 最重要的还是要明白为什么要这么写,实在不明白记住就行了。 常见考点 1. export 的基本使用 export 用于导出模块中的内容(如变量、函数、类等),使得其他模块能够导入这些内容。 ...

October 2, 2024

文件上传安全

文件上传漏洞简介 原理 攻击者可以上传一个与网站脚本语言相对应的恶意代码动态脚本到服务器上,然后访问这些恶意脚本中包含的恶意代码,从而获得了执行服务器端命令的能力,进一步影响服务器安全。 危害 文件上传漏洞最直接的威胁就是上传任意文件,包括恶意脚本、可执行程序等。 如果Web 服务器所保存上传文件的可写目录具有执行权限,那么就可以直接上传后门文件,导致网站沦陷。 如果攻击者通过其他漏洞进行提权操纵,拿到系统管理权限,那么直接导致服务器沦陷。 同服务器下的其他网站无一幸免,均会被攻击者控制。 利用 对于利用,我们只需要有个文件上传的点,以及我们知道我们所传的文件所在目录并且上传的恶意文件能够被web容器解析执行,便可能存在文件上传漏洞。 实战 说是实战但是因为不能随便入侵别人的机器(有概率获取银手镯一套),这里给大家推荐一个专门用来练习文件上传漏洞的不同利用方法的靶场。 github.com/c0ny1/uploa… 可以看到里面有很多关卡,对于环境的搭建,这里我推荐用phpstudy去进行一键搭建,比较适合入门的萌新,具体搭建方法不是本文的重点,于是我们跳过,接下来给大家带来文件上传漏洞一些常见的绕过方法。 客户端检测绕过 有的客户端会存在客户端校验,校验上传文件的后缀名,当我们传入php木马时可能会因为类型不同被禁止,此时我们要怎么进行上传一句话木马呢,这里给大家提供两个思路,第一个我们可以禁用浏览器的JS脚本,第二种方法便是使用渗透中常用工具burpsuit进行抓包,然后修改文件后缀为php即可绕过,因为他只是做了一个前端的简单验证。 修改MIME 类型 有的服务器端将会对上传文件content-type类型进行检查以此来防范恶意文件的上传,这时我们打开burp suite将php文件上传,因为content-type的类型不是服务器认可的类型,那么我们就可以使用burpsuite进行修改该文件类型,并将该文件进行上传。 常见的content-type对应类型如下: 00截断绕过 运用此漏洞需要满足两个条件: 1.php版本小于5.3.4 2.php的magic_quotes_gpc为OFF状态 0x开头表示16进制,0在十六进制中是00, 0x00就是%00解码成的16进制,具体运用方法如下,假如我们上传一个一句话木马: 攻击者修改了path以后的拼接结果为:uploads/XINO.php%00/20190818.php,移动文件的时候会将文件保存为:uploads/XINO.php,以此我们便可以绕过检测来上传一句话木马。 .htaccess文件攻击 .htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。 简单来说该文件的作用就是根据里面的内容解析为我们想要的类型,这么说可能很难理解,比如我上传一个图片马(JPG文件里插入了一句话木马),因为网站对我们的上传类型做了限制,我们上传不了PHP文件,我们先上传一个JPG的图片马,然后上传 .htaccess文件, 将图片解析为php语言,来达到getshell的目的。具体的文件内的内容为: <FilesMatch "evil.gif"> SetHandler application/x-httpd-php#在当前目录下,如果匹配到evil.gif文件,则被解析成PHP代码执行 AddHandler php5-script .gif #在当前目录下,如果匹配到evil.gif文件,则被解析成PHP代码执行 </FilesMatch> 当然不止这一种,还能如下写法: AddHandler php5-script .jpg AddType application/x-httpd-php .jpg Sethandler application/x-httpd-php 文件头检测 当我们上传一些文件时,有些拦截会检测我们文件里的具体内容,如果匹配了正确的文件头则会通过检测,反之则会拦截我们的文件,针对如此我们可以利用在文件前写入适当文件类型的文件头来绕过。常用的文件头如下: JPG:FF D8 FF E0 00 10 4A 46 49 46 GIF :47 49 46 38 39 61 (GIF89a) ...

October 2, 2024

作用域链与闭包

初学JavaScript的时候,我在学习闭包上,走了很多弯路。而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战。 闭包有多重要?如果你是初入前端的朋友,我没有办法直观的告诉你闭包在实际开发中的无处不在,但是我可以告诉你,前端面试,必问闭包。面试官们常常用对闭包的了解程度来判定面试者的基础水平,保守估计,10个前端面试者,至少5个都死在闭包上。 可是为什么,闭包如此重要,还是有那么多人没有搞清楚呢?是因为大家不愿意学习吗?还真不是,而是我们通过搜索找到的大部分讲解闭包的中文文章,都没有清晰明了的把闭包讲解清楚。要么浅尝辄止,要么高深莫测,要么干脆就直接乱说一通。包括我自己曾经也写过一篇关于闭包的总结,回头一看,不忍直视[捂脸]。 因此本文的目的就在于,能够清晰明了得把闭包说清楚,让读者朋友们看了之后,就把闭包给彻底学会了,而不是似懂非懂。 一、作用域与作用域链 在详细讲解作用域链之前,我默认你已经大概明白了JavaScript中的下面这些重要概念。这些概念将会非常有帮助。 基础数据类型与引用数据类型 内存空间 垃圾回收机制 执行上下文 变量对象与活动对象 如果你暂时还没有明白,可以去看本系列的前三篇文章,本文文末有目录链接。为了讲解闭包,已经为大家做好了基础知识的铺垫哦。 作用域 在JavaScript中,我们可以将作用域定义为一套规则,这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找。 这里的标识符,指的是变量名或者函数名 JavaScript中只有全局作用域与函数作用域(因为eval我们平时开发中几乎不会用到它,这里不讨论)。 作用域与执行上下文是完全不同的两个概念。我知道很多人会混淆他们,但是一定要仔细区分。 JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。 作用域链 回顾一下,我们分析过的执行上下文的生命周期,如下图: 我们知道函数在调用激活时,会开始创建对应的执行上下文,在执行上下文生成的过程中,变量对象,作用域链,以及this的值会分别被确定。之前一篇文章我们详细说明了变量对象,而这里,我们将详细说明作用域链。 作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。 为了帮助大家理解作用域链,我我们先结合一个例子,以及相应的图示来说明。 var a = 20; function test() { var b = a + 10; function innerTest() { var c = 10; return b + c; } return innerTest(); } test(); 在上面的例子中,全局,函数test,函数innerTest的执行上下文先后创建。我们设定他们的变量对象分别为VO(global),VO(test), VO(innerTest)。而innerTest的作用域链,则同时包含了这三个变量对象,所以innerTest的执行上下文可如下表示。 innerTestEC = { VO: {...}, // 变量对象 scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链 } 我们可以直接用一个数组来表示作用域链,数组的第一项scopeChain[0]为作用域链的最前端,而数组的最后一项,为作用域链的最末端,所有的最末端都为全局变量对象。 很多人会误解为当前作用域与上层作用域为包含关系,但其实并不是。以最前端为起点,最末端为终点的单方向通道我认为是更加贴切的形容。如图。 注意,因为变量对象在执行上下文进入执行阶段时,就变成了活动对象,这一点在上一篇文章中已经讲过,因此图中使用了AO来表示。Active Object 是的,作用域链是由一系列变量对象组成,我们可以在这个单向通道中,查询变量对象中的标识符,这样就可以访问到上一层作用域中的变量了。 二、闭包 对于那些有一点 JavaScript 使用经验但从未真正理解闭包概念的人来说,理解闭包可以看作是某种意义上的重生,突破闭包的瓶颈可以使你功力大增。 闭包是一种特殊的对象。 它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。 当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。 在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。 因此我们只需要知道,一个闭包对象,由A、B共同组成,在以后的篇幅中,我将以chrome的标准来称呼。 // demo01 function foo() { var a = 20; var b = 30; function bar() { return a + b; } return bar; } var bar = foo(); bar(); 上面的例子,首先有执行上下文foo,在foo中定义了函数bar,而通过对外返回bar的方式让bar得以执行。当bar执行时,访问了foo内部的变量a,b。因此这个时候闭包产生。 ...

October 1, 2024

包管理

现如今,前端开发的同学已经离不开 npm 这个包管理工具,其优秀的包版本管理机制承载了整个繁荣发展的NodeJS社区,理解其内部机制非常有利于加深我们对模块开发的理解、各项前端工程化的配置以加快我们排查问题(相信不少同学收到过各种依赖问题的困扰)的速度。 本文从三个角度:package.json、版本管理、依赖安装结合具体实例对 npm 的包管理机制进行了详细分析。 一、剖析 package.json 在 Node.js 中,模块是一个库或框架,也是一个 Node.js 项目。Node.js 项目遵循模块化的架构,当我们创建了一个 Node.js 项目,意味着创建了一个模块,这个模块必须有一个描述文件,即 package.json。它是我们最常见的配置文件,但是它里面的配置你真的有详细了解过吗?配置一个合理的 package.json 文件直接决定着我们项目的质量,所以首先带大家分析下 package.json 的各项详细配置。 1.1 必备属性 package.json 中有非常多的属性,其中必须填写的只有两个:name 和 version ,这两个属性组成一个 npm 模块的唯一标识。 npm包命名规则 name 即模块名称,其命名时需要遵循官方的一些规范和建议: 包名会成为模块url、命令行中的一个参数或者一个文件夹名称,任何非url安全的字符在包名中都不能使用,可以使用 validate-npm-package-name 包来检测包名是否合法。 语义化包名,可以帮助开发者更快的找到需要的包,并且避免意外获取错误的包。 若包名称中存在一些符号,将符号去除后不得与现有包名重复 例如:由于react-native已经存在,react.native、reactnative都不可以再创建。 如果你的包名与现有的包名太相近导致你不能发布这个包,那么推荐将这个包发布到你的作用域下。 例如:用户名 conard,那么作用域为 @conard,发布的包可以是@conard/react。 查看包是否被占用 name 是一个包的唯一标识,不得和其他包名重复,我们可以执行 npm view packageName 查看包是否被占用,并可以查看它的一些基本信息: 若包名称从未被使用过,则会抛出 404 错误: 另外,你还可以去 https://www.npmjs.com/ 查询更多更详细的包信息。 1.2描述信息 基本描述 { "description": "An enterprise-class UI design language and React components implementation", "keywords": [ "ant", "component", "components", "design", "framework", "frontend", "react", "react-component", "ui" ] } description用于添加模块的的描述信息,方便别人了解你的模块。 ...

August 7, 2025

DNS解析

TCP/IP提供了通过 IP地址 来连接到设备的功能,但对用户来讲,记住某台设备的IP地址是相当困难的,因此专门设计了一种字符串形式的主机命名机制,这些主机名与IP地址相对应。在IP地址与主机名之间需要有一种转换和查询机制,提供这种机制的系统就是域名系统DNS(Domain Name System)。 为什么要有DNS? 互联网中,一台计算机与其他计算机通信时,通过 IP地址 唯一的标志自己。此时的IP地址就类似于我们日常生活中的电话号码。但是,这种纯数字的标识是比较难记忆的,而且数量也比较庞大。例如,每个IPv4地址是一个32位长的二进制数字,或者采用点分十进制展示成192.168.1.1这种格式,有接近43亿个的IPv4地址。DNS的作用就是将人类可读的名称转换为机器识别的IP地址,供计算机相互连接。DNS的工作原理和电话簿相似,都是管理名称和数字之间的映射关系。就像我们日常打电话,一般使用人名查找,很少直接输入电话号码一样。当我们上网打开某个网页、视频时,也很少直接使用IP地址,而是在浏览器里输入的URL地址,例如:https://www.huawei.com,这其实使用的就是计算机的名字,一般称为域名。 域名的构成 最初设备的域名由字符序列组成、所有设备的域名组成一个未分级的域名结构。未分级的域名结构存在命名冲突、管理维护复杂的缺点。因此,TCP/IP把DNS的域名设计成了分级的树状结构。每个申请加入Internet的国家都要向NIC注册一个顶级域名,顶级域采用组织模式和地理模式的划分模式,如cn代表中国、us代表美国等。 常见的顶级域名如下表所示。NIC将顶级域的管理权分派给由其指定的管理机构,由这些管理机构再对被授权管理的域继续进行划分,从而形成了二级域。负责划分二级域的管理机构可以授权其下属的管理结构,由它们继续划分域。由此下去,便形成了层次型的Internet域名体系结构。 顶级Internet域名 含义 com 商业组织 edu 教育机构 gov 政府机构 mil 军事部门 net 主要网络支持中心 int 国际组织 org 其他组织 国家代码 国家(按照地理模式划分) 从语法上讲,每一个域名都是有标号序列组成,而各标号之间用点(小数点)隔开。以www.huawei.com域名为例,从右到左依次是: com:顶级域名。代表商业组织。 huawei:二级域名,归属于某个公司自己的域名。 www:三级域名,表明某个公司提供的是什么服务,www代表普通网页。 DNS服务器、DNS客户端和DNS中继 网络中与DNS相关的设备角色包括DNS服务器、DNS客户端和DNS中继。 DNS服务器 DNS服务器是将域名指向对应IP地址的服务器。DNS服务器中保存了一张域名和与之相对应的IP地址的表,以解析消息的域名。 由于互联网连通的是全球资源,单一的域名服务器不足以支撑全部的地址转换操作,因此全球有多套域名服务器相互配合使用。 域名是分层结构,域名DNS服务器也是对应的层级结构。通过根域名服务器,依次请求顶级域名服务器和权威域名服务器,最终获取对应IP地址,并将该结果保存在本地域名服务器,以待下次DNS请求使用。当用户再次对同一域名发起访问时,可以直接从本地域名服务器获得结果,无需再次发起全球递归查询。 表1-2 DNS服务器的分类 分类 作用 根DNS服务器 根DNS服务器是最高层次的域名服务器,它知道所有顶级服务器的域名和IP地址,当本地域名服务器无法对域名进行解析时,首先对根域名服务器发起请求。 顶级域名服务器 顶级域名服务器负责管理该服务器下的所有二级域名,当收到DNS查询请求时,就会给权威域名服务器相应的回答。 权威域名服务器 负责某一个区域的域名服务器。当一个顶级域名服务器还不能给出最后查询回答时,就会告知下一步应当请求的权威域名服务器。 本地域名服务器 当一个主机发出DNS查询请求时,这个查询请求报文就发送给本地域名服务器。每一个互联网服务提供者ISP都可以拥有一个本地域名服务器。当本地域名服务器无法给出应答时,就会请求最高级的根域名服务器。 DNS客户端 DNS客户端的作用是接收用户程序(User Program)的DNS请求,并对其作出回应。作为DNS客户端的设备上一般具备以下能力: 启动DNS解析 要使用DNS客户端功能,需要在设备上打开DNS解析的开关。 指定服务器的IP地址 要进行DNS域名解析,需要在设备上指定DNS服务器的IP地址。这样才能把查询请求发动到正确的DNS服务器上进行解析。 指定DNS域后缀搜索列表 DNS客户端所访问的一些服务器或主机的域名后缀往往都是相同的。用户可以预先设置一些域名后缀,在域名解析的时候,用户只需要输入域名的部分字段,系统会自动将输入域名加上不同的后缀进行解析。例如,用户想查询域名“huawei.com”,那么可以在后缀列表中配置com,然后输入“huawei”,系统会自动将输入域名与后缀连接成“huawei.com”进行查询。 DNS中继 当DNS服务器的IP地址发生变化时,用户网络中每个DNS客户端上的配置都需要改变,这样工作量极大并且容易出错。此时,可以通过部署DNS中继解决该问题。DNS客户端上配置DNS中继的IP地址,DNS服务器的IP地址在DNS中继上配置。之后,DNS客户端会将DNS请求报文直接发送给DNS中继,由DNS中继将收到的DNS请求报文转发至DNS服务器。由此,当DNS服务器的IP地址发生变化时,仅需改变DNS中继上的配置即可,简化了网络管理。 DNS中继的工作原理 DNS客户端将DNS请求报文发送给DNS中继,即请求报文的目的地址为DNS中继的IP地址。 DNS中继收到请求报文后,将报文转发给DNS服务器,通过DNS服务器进行域名解析。 DNS域名解析过程 通过域名获取对应IP地址的过程称为域名解析。DNS域名解析分为以下两种方式: 静态域名解析 静态域名解析是通过静态域名解析表进行的,即手动建立域名和IP地址之间的对应关系表,该表的作用可以将一些常用的域名放入表中。当DNS客户端需要域名所对应的IP地址时,即到静态域名解析表中去查找指定的域名,从而获得所对应的IP地址,提高域名解析的效率 动态域名解析 动态域名解析需要专用的域名解析服务器(DNS服务器)运行域名解析服务器程序,提供从域名到IP地址的映射关系,负责接收客户提出的域名解析请求。 为提高查询速度,在解析域名时,首先采用静态域名解析的方法,如果静态解析不成功,再采用动态域名解析的方法。 ...

July 28, 2025

列表和key

当我们在 React 要渲染一个列表时,如果没有在每一个被渲染的元件加上 key 这个 prop,就会跳出 Warning: Each child in a list should have a unique “key” prop. 这个错误讯息。直到开发者把 key 加上后,这个警告讯息才会消失。为什么要加上 key? 以及 key 有什么原则需要遵守? 这是在开发 React 时需要有的重要概念,也是面试经常会问的基础题。 为什么需要 key? key 就像一个独特身份,让 React 可以去分辨哪些子元件被新增、 移除,或是修改。 (编按:若要进一步说明此概念,推荐在面试时画下 Dan Abramov 的这个系列推文例子) 从上面的例子可以看到,当今天红黄蓝三个圈,变成红蓝黄;这会有两个可能性。可能性一第二个圈跟第三个圈的位置互换;可能性二是位置没互换,但第二个圈变蓝色、第三个圈变黄色。如果没有一个独特辨识的机制,我们会没办法知道,究竟是哪一个可能性。 如果没办法有效辨识,将可能出现 bug。举例来说,假如今天的圈圈是有状态的,例如里面有勾选方块,然后第个二圈有被勾选。假如今天换颜色是因为第二个圈被交换到第三个位置,这时表示新一次的渲染时,第三个圈要是被勾选的。不过假如我们没有一个辨识机制,要是 React 误以为换颜色不是因为换位置,而是单纯的第二个圈换颜色,那么将会渲染出仍是第二个圈是被勾选的;那么这就会是一个 bug。 然而,有了 key 这个辨识机制,React 就会知道在新的一次渲染时,原本的状态应该被保留在列表中的哪一个元件中。因此,React 之所以需要 key,正是因为 key 可以让 React 知道,哪些子元件被新增、 移除,或是修改。 除此之外,看到下面这段 React 官方文件的例子 (编按:面试时也推荐直接举这例子)。原本有个列表,我们在最上方新增一个 <li>Connecticut</li> ,如果没有 key,对 React 来说将会是,Duke 变成 Connecticut、Villanova 变成 Duke,而最后新增一个 Villanova。换句话说,整个列表都改变了,所以 React 会打掉旧的,重建一个新的列表。当列表变大时,就会很消耗效能。 ...

December 17, 2024

position: sticky

如果问,CSS 中 position 属性的取值有几个?大部分人的回答大概是 static、relative、absolute、fixed,实际上MDN上还有一个 sticky。 今天我们就来给大家介绍这个容易被忽视的position属性值 sticky。 先来看看MDN上对于 sticky 的介绍: 粘性定位元素(stickily positioned element)是计算后位置属性为 sticky 的元素。 粘性定位可以被认为是相对定位和固定定位的混合。 什么是结合两种定位功能于一体呢? 元素在跨越特定阈值前为相对定位,之后为固定定位。 也就是元素先按照普通文档流定位,然后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。 元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。 这个特定阈值指的是 top、right、bottom 或 left 之一,换言之,指定 top、right、bottom 或 left 四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。 示例 上面的文字描述估计还是很难理解,看看下面这张 GIF 图,想想要实现的话,使用 JS + CSS 的方式该如何做: 按照常规做法,大概是监听页面 scroll 事件,判断每一区块距离视口顶部距离,超过了则设定该区块 position:fixed,反之去掉。 而使用 position:sticky ,则可以非常方便的实现: <div class="container"> <div class="sticky-box">内容1</div> <div class="sticky-box">内容2</div> <div class="sticky-box">内容3</div> <div class="sticky-box">内容4</div> </div> .container { background: #eee; width: 600px; height: 1000px; margin: 0 auto; } .sticky-box { position: sticky; height: 60px; margin-bottom: 30px; background: #ff7300; top: 0px; } div { font-size: 30px; text-align: center; color: #fff; line-height: 60px; } 看看上面的 CSS 代码,只是给每个内容区块加上 ...

November 9, 2024

全方位解读this

我们在学习JavaScript的过程中,由于对一些概念理解得不是很清楚,但是又想要通过一些方式把它记下来,于是就很容易草率的给这些概念定下一些方便自己记忆的有偏差的结论。 危害比较大的是,有的不准确的结论在网上还广为流传。 比如对于this指向的理解中,有这样一种说法:谁调用它,this就指向谁。在我刚开始学习this的时候,我是非常相信这句话的。因为在一些情况下,这样理解也还算说得通。可是我常常会在开发中遇到一些不一样的情况,一个由于this的错误调用,可以让我懵逼一整天。那个时候我也查资料,在群里问大神,可是我仍然搞不清楚“我特么到底错哪里了”。其实只是因为我心中有一个不太准确的结论。 所以,我认为需要有这样一篇文章,来帮助大家全方位的解读this。让大家对this,有一个正确的,全面的认知。 在这之前,我们需要来回顾一下执行上下文。 在前面的几篇文章中,我有好几个地方都提到执行上下文的生命周期,为了防止大家没有记住,再次来回顾一下,如下图。 在执行上下文的创建阶段,会分别生成变量对象,建立作用域链,确定this指向。其中变量对象与作用域链我们都已经仔细总结过了,而这里的关键,就是确定this指向。 首先我们需要得出一个非常重要一定要牢记于心的结论,**this的指向,是在函数被调用的时候确定的。**也就是执行上下文被创建时确定的。因此,一个函数中的this指向,可以是非常灵活的。比如下面的例子中,同一个函数由于调用方式的不同,this指向了不一样的对象。 var a = 10; var obj = { a: 20 } function fn () { console.log(this.a); } fn(); // 10 fn.call(obj); // 20 除此之外,在函数执行过程中,this一旦被确定,就不可更改了。 var a = 10; var obj = { a: 20 } function fn () { this = obj; // 这句话试图修改this,运行后会报错 console.log(this.a); } fn(); 一、全局对象中的this 关于全局对象的this,我之前在总结变量对象的时候提到过,它是一个比较特殊的存在。全局环境中的this,指向它本身。因此,这也相对简单,没有那么多复杂的情况需要考虑。 // 通过this绑定到全局对象 this.a2 = 20; // 通过声明绑定到变量对象,但在全局环境中,变量对象就是它自身 var a1 = 10; // 仅仅只有赋值操作,标识符会隐式绑定到全局对象 a3 = 30; // 输出结果会全部符合预期 console.log(a1); console.log(a2); console.log(a3); 二、函数中的this 在总结函数中this指向之前,我想我们有必要通过一些奇怪的例子,来感受一下函数中this的捉摸不定。 // demo01 var a = 20; function fn() { console.log(this.a); } fn(); // demo02 var a = 20; function fn() { function foo() { console.log(this.a); } foo(); } fn(); // demo03 var a = 20; var obj = { a: 10, c: this.a + 20, fn: function () { return this.a; } } console.log(obj.c); console.log(obj.fn()); 这几个例子需要花点时间仔细感受一下,如果你暂时没想明白怎么回事,也不用着急,我们一点一点来分析。 ...

November 6, 2024