Web Storage

一、 简介 浏览器本地存储是指浏览器提供的一种机制,允许 Web 应用程序在浏览器端存储数据,以便在用户下次访问时可以快速获取和使用这些数据。一共两种存储方式:localStorage 和 sessionStorage。下面介绍下两种缓存的特性和在内部平台的一些应用。 二、localStorage 和 sessionStorage 2.1、区别 localStorage 和 sessionStorage 的主要区别是生命周期,具体区别如下: localStorage sessionStorage 生命周期 持久化存储:除非自行删除或清除缓存,否则一直存在 会话级别的存储:浏览器标签页或窗口关闭 作用域 相同浏览器,同域名,不同标签,不同窗口 相同浏览器,同域名,同源窗口 获取方式 window.localStorage window.sessionStorage 存储容量 5M 5M 容量限制的目的是防止滥用本地存储空间,导致用户浏览器变慢。 2.2、浏览器兼容性 1)现在的浏览器基本上都是支持这两种 Storage 特性的。各浏览器支持版本如下: Chrome Firefox IE Opera Safari Android Opera Mobile Safari Mobile localStorage 4 3.5 8 10.5 4 2.1 11 iOS 3.2 sessionStorage 5 2 8 10.5 4 2.1 11 iOS 3.2 2)如果使用的是老式浏览器,比如Internet Explorer 6、7 或者其他,就需要在使用前检测浏览器是否支持本地存储或者是否被禁用。以 localStorage 为例: ...

October 9, 2024

Flexbox

一、Flexbox布局的概念 Flexbox布局也叫Flex布局,弹性盒子布局。它的目标是提供一个更有效地布局、对齐方式,并且能够使父元素在子元素的大小未知或动态变化情况下仍然能够分配好子元素之间的间隙。主要思想是使父元素能够调整子元素的宽度、高度、排列方式,从而更好的适应可用的布局空间。设定为flex布局的元素能够放大子元素使之尽可能填充可用空间,也可以收缩子元素使之不溢出。 ​ Flex布局更适合小规模的布局,可以简便、完整、响应式的实现各种页面布局。但是,设为Flex布局以后,其子元素的float、clear和vertical-align属性将失效。Flex弹性盒模型的优势在于只需声明布局应该具有的⾏为,⽽不需要给出具体的实现⽅式,浏览器负责完成实际布局,当布局涉及到不定宽度,分布对⻬的场景时,就要优先考虑弹性盒布局。 Flex布局是一个完整的模块,它包括了一套完整的属性。其中采用 Flex 布局的元素,称为 Flex 容器,简称"容器"。它的所有子元素就是容器成员,称为 Flex 项目,简称"项目"。 容器默认存在两个轴:水平轴(main axis)和垂直轴(cross axis),项目默认沿主轴排列(水平轴): 这里面涉及到了几个概念,下面来看一下: main axis: Flex 父元素的主轴是指子元素布局的主要方向轴,它由属性flex-direction来确定主轴是水平还是垂直的,默认为水平轴。 main-start & main-end: 分别表示主轴的开始和结束,子元素在父元素中会沿着主轴从main-start到main-end排布。 main size: 单个项目占据主轴的长度大小。 cross axis: 交叉轴,与主轴垂直。 cross-start & cross-end: 分别表示交叉轴的开始和结束。子元素在交叉轴的排布从cross-start开始到cross-end。 cross size: 子元素在交叉轴方向上的大小。 二、父元素属性 想要使用flex布局,首先需要给父元素指定为flex布局,这样容器内的元素才能实现flex布局: <div class="container"></div> .container { display: flex | inline-flex; } 这里有两种方式可以设置flex布局,使用display: flex;会生成一个块状的flex容器盒子,使用display: inline-flex;会生成一个行内的flex容器盒子。如果我们使用块状元素,比如div标签,就可以使用flex,如果使用行内元素,就可以使用inline-flex。多数情况下,我们会使用display: flex;。 父元素(容器)可以设置以下六个属性: flex-direction flex-wrap flex-flow justify-content align-items align-content 1. flex-direction flex-direction:主轴方向,它决定了容器内元素排列方向,它有四个属性值: .container { flex-direction: row | row-reverse | column | column-reverse; } (1)flex-direction: row:默认值,沿水平主轴从左到右排列,起点在左沿 (2)flex-direction: row-reverse:沿水平主轴从右到左排列,起点在右沿 (3)flex-direction: column:沿垂直主轴从上到下排列,起点在上沿 ...

October 8, 2024

v-for

在 Vue.js 开发中,v-for 是我们用来遍历数组、对象或其他数据结构的核心指令。然而,很多开发者可能忽略了其中的一个细微差别:in 和 of。这两个关键字看似相似,但却有各自不同的使用场景和适用数据类型。理解它们的区别,可以帮助我们编写更简洁、高效的代码。 本文将详细探讨 v-for 中的 in 和 of,并讲解它们各自的适用场景,帮助你在 Vue.js 项目中做出最佳选择。 什么是 v-for? v-for 是 Vue.js 提供的用于遍历数据的指令,它可以帮助你快速渲染列表、表格等动态生成的内容。在使用 v-for 时,我们通常需要遍历一个数据集合,并将每一项绑定到 DOM 元素上。 in 和 of 的概念 v-for 中的 in 和 of 是遍历数据时的两个常用关键字,它们的主要区别在于:in 更通用,可以遍历数组、对象和整数范围;而 of 专注于遍历可迭代对象(如数组、字符串、Set、Map 等) 。 接下来,我们深入探讨它们的具体用法及差异。 1. in:适用于数组、对象、整数范围 1.1 遍历数组 in 是 Vue.js 中默认推荐的方式,用于遍历数组中的元素。你可以轻松地用 in 处理数组中的每个值。 <ul> <li v-for="item in items">{{ item }}</li> </ul> 假设 items = ['apple', 'banana', 'cherry'],输出结果为: <li>apple</li> <li>banana</li> <li>cherry</li> 1.2 遍历对象 当你需要遍历对象的键和值时,in 也非常方便。通过 (value, key) 语法,你可以轻松获取对象的键和值。 ...

October 5, 2024

Class

Class 的基本语法 简介 类的由来 JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。 function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; }; var p = new Point(1, 2); 上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } 上面代码定义了一个“类”,可以看到里面有一个constructor()方法,这就是构造方法,而this关键字则代表实例对象。这种新的 Class 写法,本质上与本章开头的 ES5 的构造函数Point是一致的。 Point类除了构造方法,还定义了一个toString()方法。注意,定义toString()方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法与方法之间不需要逗号分隔,加了会报错。 ES6 的类,完全可以看作构造函数的另一种写法。 class Point { // ... } typeof Point // "function" Point === Point.prototype.constructor // true 上面代码表明,类的数据类型就是函数,类本身就指向构造函数。 使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。 class Bar { doStuff() { console.log('stuff'); } } const b = new Bar(); b.doStuff() // "stuff" 构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。 ...

October 2, 2024

原型链与继承

JavaScript 中没有类的概念的,主要通过原型链来实现继承。通常情况下,继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联(原型对象指针),这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。 原型 当我们 new 了一个新的对象实例,明明什么都没有做,就直接可以访问 toString 、valueOf 等原生方法。那么这些方法是从哪里来的呢?答案就是原型。 在控制台打印一个空对象时,我们可以看到,有很多方法,已经“初始化”挂载在内置的 __proto__ 对象上了。这个内置的 __proto__ 是一个指向原型对象的指针,它会在创建一个新的引用类型对象时(显示或者隐式)自动创建,并挂载到新实例上。当我们尝试访问实例对象上的某一属性 / 方法时,如果实例对象上有该属性 / 方法时,就返回实例属性 / 方法,如果没有,就去 __proto__ 指向的原型对象上查找对应的属性 / 方法。这就是为什么我们尝试访问空对象的 toString 和 valueOf 等方法依旧能访问到的原因,JavaScript 正式以这种方式为基础来实现继承的。 构造函数 如果说实例的 __proto__ 只是一个指向原型对象的指针,那就说明在此之前原型对象就已经创建了,那么原型对象是什么时候被创建的呢?这就要引入构造函数的概念。 其实构造函数也就只是一个普通的函数而已,如果这个函数可以使用 new 关键字来创建它的实例对象,那么我们就把这种函数称为 构造函数。 // 普通函数 function person () {} // 构造函数,函数首字母通常大写 function Person () {} const person = new Person(); 原型对象正是在构造函数被声明时一同创建的。构造函数被申明时,原型对象也一同完成创建,然后挂载到构造函数的 prototype 属性上: 原型对象被创建时,会自动生成一个 constructor 属性,指向创建它的构造函数。这样它俩的关系就被紧密地关联起来了。 细心的话,你可能会发现,原型对象也有自己的 __proto__ ,这也不奇怪,毕竟万物皆对象嘛。原型对象的 proto 指向的是 Object.prototype。那么 Object.prototype.__proto__ 存不存在呢?其实是不存在的,打印的话会发现是 null 。这也证明了 Object 是 JavaScript 中数据类型的起源。 ...

October 1, 2024

前端组件化开发

背景 不知道你有没有遇到过以下场景: 页面逻辑越来越多,代码越来越庞大,写到后面难以 hold 住所有逻辑,很容易牵一发而动全身. 你负责的页面好好的突然出现问题,查到最后是别人代码影响. 同样的逻辑在多个地方重复书写,每次一改要改一批文件 随着前端项目复杂度的急剧增加, 上面列出的几种场景就是传统开发中会出现的问题. 也是前端组件化出现的原因. 项目复杂度增加, 一个页面一个文件需要处理的内容过多. 重复性劳动多, 效率低 质量差, 不可控 组件化初探 正是由于出现了这样的问题, 为了在越来越复杂的前端项目中提高开发效率和保证开发质量, 各路大神们开始通过各种方式来尝试解决问题. 曾经非常火的 jQuery 就基于自己建立了 jQuery 插件机制. 你可以将一些常用逻辑进行封装变成 jQuery 插件, 还可以将插件开源进行共享. 比如纯手写会吐血的日期时间选择器,轮播, 多级菜单等等. 你可以在这里浏览更多jQuery插件. jQuery 插件的用法通常是: $(".select").pluginName(config) 除了 jQuery 插件模式, 还有一种常见模式是对象模式. 这种模式现在仍然有很多优秀的库在被我们直接或者间接使用. 比如: swiper 对象模式的写法: <!-- Slider main container --> <div class="swiper-container"> <!-- Additional required wrapper --> <div class="swiper-wrapper"> <!-- Slides --> <div class="swiper-slide">Slide 1</div> <div class="swiper-slide">Slide 2</div> <div class="swiper-slide">Slide 3</div> ... </div> </div> <script src="path/to/swiper.min.js"></script> <script> var mySwiper = new Swiper ('.swiper-container', { direction: 'vertical', loop: true, }) </script> 对象模式通过配置创建对象, 通常创建对象的参数中有一项是元素/元素选择器, 通过js代码将逻辑与这个元素紧密绑定. ...

August 7, 2025

Fragment 和 Portals

一、Portals 某些情况下,我们希望渲染的内容独立于父组件,甚至独立于当前挂在的DOM元素(默认都是挂载到id为root的DOM元素上的) Portal提供了一种将子节点渲染到存在于父组件以外的DOM节点的优秀的方案: ReactDOM.createPortal(child, container) 第一个参数:是任何可渲染的React子元素,例如一个元素、字符串或fragment 第二个参数:是一个DOM元素 通常来讲,当你从组件的 render 方法返回一个元素时,该元素将被挂载到 DOM 节点中离其最近的父节点: render() { // React 挂载了一个新的 div,并且把子元素渲染其中 return ( <div> {this.props.children} </div> ); } 然而,有时候将子元素插入到 DOM 节点中的不同位置也是有好处的: render() { // React 并没有创建一个新的 div。它只是把子元素渲染到 `domNode` 中。 // `domNode` 是一个可以在任何位置的有效 DOM 节点。 return ReactDOM.createPortal( this.props.children, domNode ); } 一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框 案例 比如将h2挂在到id为zs的节点下 index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>React App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <div id="zs"></div> </body> </html> App.jsx ...

December 17, 2024

浏览器的安全性

浏览器安全性是前端开发中的重要考察点之一,主要指浏览器在访问网站过程中如何防止攻击者利用漏洞或机制实施攻击、窃取数据、破坏用户体验等。 一、常见的浏览器安全威胁 1. XSS(跨站脚本攻击) 原理:攻击者注入恶意脚本到网页中,在用户浏览页面时执行。 危害:窃取 cookie、伪造操作、传播蠕虫。 防御: 对输出进行HTML转义; 使用 Content Security Policy(CSP); 严格控制用户输入(白名单); 使用框架自动防御(如 React 的 JSX 自动转义)。 2. CSRF(跨站请求伪造) 原理:用户登录目标网站后,被诱导访问恶意链接,触发网站上的有状态请求。 危害:修改密码、转账等敏感操作被伪造。 防御: 使用 CSRF Token; Referer 验证; SameSite Cookie 属性限制第三方请求。 3. 点击劫持(Clickjacking) 原理:攻击者在页面上嵌入透明 iframe,引诱用户点击。 防御: 禁止网页被嵌入 iframe:X-Frame-Options: DENY / SAMEORIGIN; 使用 CSP 中的 frame-ancestors 指定允许嵌入的来源。 4. 恶意文件上传 原理:上传可执行脚本,触发服务端或客户端执行。 防御: 严格限制文件类型与大小; 不在上传目录下执行脚本; 设置 CDN 或存储桶只读访问权限。 5. 恶意第三方脚本(供应链攻击) 原理:攻击者污染 CDN 或依赖源,注入恶意代码。 防御: 使用子资源完整性校验(Subresource Integrity, SRI); 只信任可靠的依赖源; 上线前锁定依赖版本。 二、浏览器原生安全机制 1. 同源策略(Same-Origin Policy) 限制不同源之间访问 Cookie、DOM、LocalStorage 等。 同源指:协议、域名、端口号都相同。 2. CORS(跨域资源共享) 浏览器通过预检请求和响应头判断是否允许跨域访问。 3. Content Security Policy(CSP) 通过设置 HTTP Header 控制资源加载策略,防止 XSS 和数据泄露。 示例: Content-Security-Policy: default-src 'self'; script-src 'self' https://trust.cdn.com; 4. HTTP-only & Secure Cookie HttpOnly: 防止 JavaScript 读取 Cookie; Secure: 仅在 HTTPS 下传输 Cookie; SameSite: 限制第三方请求携带 Cookie。 5. Sandbox(iframe 安全沙箱) <iframe sandbox> 属性限制 iframe 的行为; 可防止脚本执行、表单提交等危险操作。 三、前端开发中的安全实践 安全措施 说明 输入校验 客户端和服务端都要做,优先使用白名单策略 输出编码 HTML、JavaScript、URL 编码避免 XSS 注入 HTTPS 加密传输防止中间人攻击(MITM) 使用现代框架 React/Vue/Angular 等框架天然防御 XSS 限制权限 用户行为应严格授权与验证 CSP 策略 强制资源加载来源、禁止内联脚本 四、总结 浏览器安全是前端必须掌握的重要基础知识,核心目标是 防止前端受到攻击者控制或操纵。它涉及浏览器机制、HTTP协议、安全头部、数据验证等多个维度,需要前端开发者在日常开发中养成良好安全意识与编码习惯。 ...

December 17, 2024

跨域通信

什么是跨域 在了解跨域之前,我们必须要了解一下同源策略。 跨域问题其实就是浏览器的同源策略造成的。 同源策略 同源策略限制了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。这是浏览器的一个用于隔离潜在恶意文件的重要的安全机制。同源指的是:协议、端口号、域名必须一致。 跨源资源共享([CORS],或通俗地译为跨域资源共享)是一种基于 [HTTP] 头的机制,该机制通过允许服务器标示除了它自己以外的其他[源] (域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。 为什么会产生跨域 下表给出了与 URL wmq.com/dir/page.ht… 的源进行对比的示例: URL 是否跨域 原因 wmq.com/dir/page.ht… 同源 完全相同 wmq.com/dir/inner/a… 同源 只有路径不同 wmq.com/secure.html 跨域 协议不同 wmq.com:81/dir/etc.htm… 跨域 端口不同 ( http:// 默认端口是80) news.company.com/dir/other.h… 跨域 主机不同 同源策略:protocol(协议)、domain(域名)、port(端口)三者必须一致。 同源政策主要限制了三个方面: 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。 当前域下 ajax 无法发送跨域请求。 源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作。 有哪些解决跨域的办法 CORS CORS: 通过设置服务器端的响应头来允许跨域请求。这需要在服务器端进行配置,以允许特定的来源访问资源。例如,在Node.js的Express框架中,可以使用以下代码来设置CORS: const express = require('express'); const app = express(); app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); // 允许所有来源访问 res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); // 允许的请求头 res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); // 允许的请求方法 next(); }); // 其他路由和中间件 app.listen(3000, () => { console.log('Server is running on port 3000'); }); 减少OPTIONS请求次数 在跨域请求中,浏览器会自动处理一些非简单请求的预检流程,这包括"预检请求"(也被称为CORS预检请求)和"OPTIONS请求"。预检请求是发起一个HTTP OPTIONS请求到服务器,以确认目标资源是否支持跨域。这种机制是为了兼容同源策略而产生的,但有时这种额外的请求会导致性能问题。 ...

October 14, 2024

Grid布局

Grid 布局是什么? Grid 布局即网格布局,是一种新的 CSS 布局模型,比较擅长将一个页面划分为几个主要区域,以及定义这些区域的大小、位置、层次等关系。号称是最强大的的 CSS 布局方案,是目前唯一一种 CSS 二维布局。利用 Grid 布局,我们可以轻松实现类似下图布局,演示地址 Grid 布局和 flex 布局 讲到布局,我们就会想到 flex 布局,甚至有人认为竟然有 flex 布局了,似乎没有必要去了解 Grid 布局。但 flex 布局和 Grid 布局有实质的区别,那就是 flex 布局是一维布局,Grid 布局是二维布局。flex 布局一次只能处理一个维度上的元素布局,一行或者一列。Grid 布局是将容器划分成了“行”和“列”,产生了一个个的网格,我们可以将网格元素放在与这些行和列相关的位置上,从而达到我们布局的目的。 Grid 布局远比 flex 布局强大! flex布局示例: Grid 布局示例: Grid 的一些基础概念 我们使用 Grid 实现一个小例子,演示 Grid 的一些基础概念,演示地址 <div class="wrapper"> <div class="one item">One</div> <div class="two item">Two</div> <div class="three item">Three</div> <div class="four item">Four</div> <div class="five item">Five</div> <div class="six item">Six</div> </div> .wrapper { margin: 60px; /* 声明一个容器 */ display: grid; /* 声明列的宽度 */ grid-template-columns: repeat(3, 200px); /* 声明行间距和列间距 */ grid-gap: 20px; /* 声明行的高度 */ grid-template-rows: 100px 200px; } .one { background: #19CAAD; } .two { background: #8CC7B5; } .three { background: #D1BA74; } .four { background: #BEE7E9; } .five { background: #E6CEAC; } .six { background: #ECAD9E; } .item { text-align: center; font-size: 200%; color: #fff; } ...

October 8, 2024