最大得成就 做过最好得功能
promise
promise all promise race
清除浮动
浮动的定义:使元素脱离文档流,按照指定方向发生移动,遇到父级边界或者相邻的浮动元素停了下来。
高度塌陷: 浮动元素父元素高度自适应(父元素不写高度时,子元素写了浮动后,父元素会发生高度塌陷)
- clear:left | right | both | none | inherit:元素的某个方向上不能有浮动元素
- clear:both:在左右两侧均不允许浮动元素。
1、br 清浮动
br 标签自带clear属性,将它设置成both其实和添加空div原理是一样的。 问题:不符合工作中:结构、样式、行为,三者分离的要求。
<div class="box">
<div class="top"></div>
<br clear="both" />
</div>
2、给父级添加overflow:hidden 清浮动方法
问题:需要配合 宽度 或者 zoom 兼容IE6 IE7;
overflow: hidden;
*zoom: 1;
3、万能清除法 after伪类 清浮动(现在主流方法,推荐使用)
选择符:after{
content:".";
clear:both;
display:block;
height:0;
overflow:hidden;
visibility:hidden;
}
.clear:after{ //同时为了兼容 IE6,7 同样需要配合zoom使用例如:
content:'';
display:block;
clear:both;
height:0;
overflow:hidden;
visibility:hidden;
}
.clear{
zoom:1;
}
css盒模型的理解
CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。
不同部分的说明:
- Margin(外边距) - 清除边框外的区域,外边距是透明的。
- Border(边框) - 围绕在内边距和内容外的边框。
- Padding(内边距) - 清除内容周围的区域,内边距是透明的。
- Content(内容) - 盒子的内容,显示文本和图像。
js精度丢失 0.1 + 0.2 != 0.3
math.js 或者 # decimal.js
主题色改变
vue.config 和vite.config 配置
拦截器
3。1针对所有的接口的处理
3。2请求拦截器
3。3 相应拦截器
4、路由守卫
【1】全局守卫:**是指路由实例上直接操作的钩子函数,特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数
- beforeEach(to,from, next)
- beforeResolve(to,from, next)
- afterEach(to,from)
【2】路由守卫: 是指在单个路由配置的时候也可以设置的钩子函数
- beforeEnter(to,from, next)
**【3】组件守卫:**是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数。
- beforeRouteEnter(to,from, next)
- beforeRouteUpdate(to,from, next)
- beforeRouteLeave(to,from, next)
- 触发进入其它路由
- 调用要离开路由的组件守卫beforeRouteLeave
- 调用全局的前置守卫beforeEach
- 在重用的组件里调用 beforeRouteUpdate
- 在路由配置里调用 beforeEnter
- 解析异步路由组件
- 在将要进入的路由组件中调用beforeRouteEnter
- 调用全局的解析守卫beforeResolve
- 导航被确认
- 调用全局的后置钩子afterEach。
- 触发 DOM 更新mounted。
- 执行beforeRouteEnter守卫中传给 next的回调函数。
深拷贝浅拷贝
- 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
- 深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
6、vue.config
webpack基本配置
module.exports = { productionSourceMap: false, publicPath: './', outputDir: 'dist', assetsDir: 'assets', devServer: { port: 8090, host: '0.0.0.0', https: false, open: true }, // 其他配置 ...
productionSourceMap:生产环境是否要生成 sourceMap
publicPath:部署应用包时的基本 URL,用法和 webpack 本身的 output.publicPath 一致
可以通过三元运算去配置dev和prod环境, publicPath: process.env.NODE_ENV === 'production' ? '/prod/' : './' outputDir: build 时输出的文件目录
assetsDir: 放置静态文件夹目录
devServer: dev环境下,webpack-dev-server 相关配置
port: 开发运行时的端口 host: 开发运行时域名,设置成'0.0.0.0',在同一个局域网下,如果你的项目在运行,同时可以通过你的http://ip:port/...访问你的项目 https: 是否启用 https open: npm run serve 时是否直接打开浏览器
6.2、chainWebpack和configureWebpack(唯一的区别就是他们修改webpack配置方式不同,chainWebpack链式编程修改配置,configureWebpack是操作对象的形式修改配置)
rule的配置
rule 新增config.module .rule(name) .use(name) .loader(loader) .options(options)
rules的修改 (tap参数)
config.module .rule(name) .use(name) .tap(options => newOptions)
插件plugins 的配置
新增
config .plugin(name) .use(WebpackPlugin, args)
修改
config.plugin(name).tap(args => newArgs)
删除
config.plugins.delete(name)
DefinePlugin
定义全局全局变量,DefinePlugin 是 webpack 已经默认配置的,我们可以对参数进行修改
config.plugin('define').tap(args => [{ ...args, "window.isDefine": JSON.stringify(true), }]);
设置别名alias
webpack默认是将src的别名设置为@, 此外,我们可以进行添加
config.resolve.alias .set('@', resolve('src')) .set('api', resolve('src/apis')) .set('common', resolve('src/common'))
优化打包
1、externals 提取项目依赖,cdn引入(最好有备用cdn)
(1)external排除指定依赖,用cdn引入
module.exports = { configureWebpack: { externals: { vue: 'Vue', 'vue-router': 'VueRouter', axios: 'axios', echarts: 'echarts' } }
(2)在 index.html 中使用 CDN 引入依赖
减小打包体积
2、组件库的按需引入
3、减小三方依赖的体积
moment.js 剔除其他语言包 或者=> dayjs
4、HappyPack 多线程打包
5、Gzip压缩(最佳实践类型是html、js、css
web上使用gzip编码格式传输有几个要点:
- 浏览器和服务器都需要支持gzip编码
- 采用 LZ77 算法与 Huffman 编码来压缩文件,是一种无损压缩算法
- 压缩比率在3-10倍左右(纯文本),可以大大节省服务器的网络带宽
图片资源为什么不需要Gzip压缩? 这个和gzip使用的Deflate算法有关系,因为它使用了 LZ77 算法与 Huffman 编码来压缩文件,重复度越高的文件可压缩的空间就越大,但是图片的重复度是很低的,甚至压缩后页面的体积会变大,得不偿失
compression-webpack-plugin
6 缩小打包的体积、图片资源压缩
7去除debugger console (terser)
盘点 Vue3 与 Vue2 的区别
1. 生命周期(Tips: setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地去定义。)
2. 多根节点
Vue3 支持多个根节点,也就是 fragment。即以下多根节点的写法是被允许的。
3. Composition API
vue2选项api
vue3 组合式api
4. 异步组件(Suspense)
Vue3 提供 Suspense 组件,允许程序在等待异步组件加载完成前渲染兜底的内容,如 loading ,使用户的体验更平滑。使用它,需在模板中声明,并包括两个命名插槽:default 和 fallback。Suspense 确保加载完异步内容时显示默认插槽,并将 fallback 插槽用作加载状态。
<template #default>
<template #fallback>
Loading...
5. Teleport
Vue3 提供 Teleport 组件可将部分 DOM 移动到 Vue app 之外的位置。比如项目中常见的 Dialog 弹窗
<button @click="dialogVisible = true">显示弹窗</button>
<teleport to="body">
<div class="dialog" v-if="dialogVisible">
我是弹窗,我直接移动到了body标签下
</div>
</teleport>
6. 响应式原理
Vue2 响应式原理基础是 Object.defineProperty;Vue3 响应式原理基础是 Proxy。
一、Vue2中的响应式原理 Vue 2的响应式原理: 在Vue 2中,响应式是通过使用Object.defineProperty()方法来实现的。 在组件实例化过程中,Vue会对数据对象(data)进行递归地遍历,将每个属性都转换为getter/setter,并且为每个属性创建一个依赖追踪的系统。当属性被访问或修改时,getter/setter会触发依赖追踪系统,从而进行依赖收集和派发更新,以保证数据和视图的同步。 具体实现步骤如下: 1.创建Observer对象:通过递归地将data对象的属性转换为响应式属性,使用Object.defineProperty()为每个属性添加getter和setter方法。Vue2中 通过使用 Object.defineProperty() 方法,将对象的属性转换成 getter 和 setter,当数据发生变化时,会自动触发相应的更新函数,实现数据的响应式。
2.创建Dep对象:用来管理 Watcher,它用来收集依赖、删除依赖和向依赖发送消息等。用于解耦属性的依赖收集和派发更新操作。
3.创建Watcher对象:Watcher对象用于连接视图和数据之间的桥梁,当被依赖的属性发生变化时,Watcher对象会接收到通知并更新视图。当数据发生变化时,它会通知订阅该数据的组件更新视图。Watcher 在实例化时会将自己添加到 Dep 中,当数据发生变化时,会触发相应的更新函数。
4.模板编译:Vue会解析模板,将模板中的数据绑定指令转译为对应的更新函数,以便在数据发生变化时调用。
1、Object.defineProperty需要遍历所有的属性,这就造成了如果vue对象的data/computed/props中的数据规模庞大,那么遍历起来就会慢很多。
2、同样,如果vue对象的data/computed/props中的数据规模庞大,那么Object.defineProperty需要监听所有的属性的变化,那么占用内存就会很大。
缺点。
1、无法监听es6的Set、WeakSet、Map、WeakMap的变化;
2、无法监听Class类型的数据;
3、属性的新加或者删除也无法监听;
4、数组元素的增加和删除也无法监听。
- Object.defineProperty 基本用法:直接在一个对象上定义新的属性或修改现有的属性,并返回对象。
let obj = {};
let name = 'leo';
Object.defineProperty(obj, 'name', {
enumerable: true, // 可枚举(是否可通过 for...in 或 Object.keys() 进行访问)
configurable: true, // 可配置(是否可使用 delete 删除,是否可再次设置属性)
// value: '', // 任意类型的值,默认undefined
// writable: true, // 可重写
get() {
return name;
},
set(value) {
name = value;
}
});
那 Vue3 为何会抛弃它呢?那肯定是因为它存在某些局限性。
主要原因:无法监听对象或数组新增、删除的元素。
Vue2 相应解决方案:针对常用数组原型方法push、pop、shift、unshift、splice、sort、reverse进行了hack处理;提供Vue.set监听对象/数组新增属性。对象的新增/删除响应,还可以new个新对象,新增则合并新属性和旧对象;删除则将删除属性后的对象深拷贝给新对象。
- Proxy Proxy 是 ES6 新特性,通过第2个参数 handler 拦截目标对象的行为。相较于 Object.defineProperty 提供语言全范围的响应能力,消除了局限性。
vue3通过proxy代理的方式实现。
proxy的优势:不需要像Object.definedProperty()的那样遍历每一个属性,有一定的性能提升proxy可以理解为在目标对象之前架设一层“拦截”,外界对该对象的访问都必须通过这一层拦截。这个拦截可以对外界的访问进行过滤和改写。
当属性过多的时候利用Object.definedProperty()要通过遍历的方式监听每一个属性。利用proxy则不需要遍历,会自动监听所有属性,有利于性能的提升
优势
(1)、对象/数组的新增、删除
(2)、监测 .length 修改
(3)、Map、Set、WeakMap、WeakSet 的支持
基本用法:创建对象的代理,从而实现基本操作的拦截和自定义操作。
在 Vue 中体现最为明显的一点就是:Proxy 代理对象之后不仅可以拦截对象属性的读取、更新、方法调用之外,对整个对象的新增、删除、枚举等也能直接拦截,而 Object.defineProperty 只能针对对象的已知属性进行读取和更新的操作拦截。
const data = {}
for(let i = 0; i <= 100000; i++) {
data['pro' + i] = i
}
var proxyData = new Proxy(data, {
get(target, property, receiver) {
// console.log(`读取${property}的值为${target[property]}`)
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
// console.log(`更新${property}的值为${value}`)
return Reflect.set(target, property, value, receiver);
}
})
7. 虚拟DOM(待细看)
8、vue3依赖收集原理
Vue 3 的依赖收集原理主要涉及到响应式系统的实现。在 Vue 中,当数据发生变化时,视图会自动更新,这就是依赖收集和响应式系统的功劳。以下是 Vue 3 依赖收集的原理:
-
Proxy 代理对象:Vue 3 使用 ES6 的 Proxy 来代理数据对象,这样对数据的任何修改都会被 Proxy 捕获。
-
依赖收集:当组件中访问某个数据属性时,Vue 会使用一个叫做“依赖收集”的机制来记录当前组件对这个数据的依赖。这个过程大致如下:
- 当组件访问某个数据属性时,这个访问会被转化为一个“依赖描述符”(Dependency Descriptor),这个描述符会记录当前组件对数据的依赖关系。
- Vue 会将这些描述符存储在一个叫做“依赖列表”或“依赖树”的结构中,以便后续更新视图时使用。
- 响应式系统:一旦数据发生变化,Proxy 会触发一个更新事件。这个更新事件会通知到所有依赖于这个数据的组件。
- 触发更新:当数据变化时,Vue 会遍历所有依赖于这个数据的组件的依赖列表,然后触发这些组件的更新函数,从而更新视图。
具体来说,Vue 3 使用了一个叫做“Effect”的概念来实现依赖收集和触发更新。Effect 是一个函数,它会在数据变化时被调用,从而触发视图的更新。当一个组件访问某个数据属性时,这个访问会被转化为一个 Effect,并且这个 Effect 会被添加到依赖列表中。当数据变化时,所有依赖于这个数据的 Effect 都会被调用,从而触发视图的更新。
总的来说,Vue 3 的依赖收集原理就是通过 Proxy 代理对象来捕获数据的修改,并通过 Effect 和依赖列表来记录和触发组件的更新。这样,当数据发生变化时,视图能够自动更新,实现了响应式的效果。
低代码
function renderNode(node) {
if (!node) return null;
const component = components[node.componentName];
const props = compute(node.props);
const children = node.children.map(c => renderNode(c));
return React.render(component, props, children);
}
renderNode( componentTree);
vue
component //动态组件
事件循环 even loop
事件循环的事件阶段主要包括:宏任务(Macro Task)/主任务队列、微任务(Micro Task)队列、UI渲染、下一个宏任务。
事件循环的过程概括为:执行一个宏任务 -> 执行所有微任务 -> UI渲染 -> 执行下一个宏任务
任务队列的执行流程可概括为:
- 同步任务在主线程排队执行,异步任务在事件队列排队等待进入主线程执行。
- 遇到宏任务则推进宏任务队列,遇到微任务则推进微任务队列。
- 执行宏任务,执行完毕后检查当前层的微任务并执行。
- 继续执行下一个宏任务,执行对应层次的微任务,直至全部执行完毕。
**这个流程确保了异步任务能够在适当的时机插入执行,保持程序的高效性和响应性。
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
new Promise((resolve) => {
console.log(4);
resolve();
console.log(5);
}).then(() => {
console.log(6);
});
console.log(7);
执行顺序解析:1 => 3 => 4 => 5 => 7 => 6 => 2。
- 创建Promise实例是同步的,所以1、3、4、5、7是同步执行的。
then方法是微任务,放入微任务队列中,在当前脚本执行完毕后立即发生。- 同步任务执行完毕后,执行微任务队列中的微任务。
- 最后,
setTimeout放入宏任务队列,按照先进先出的原则执行。
注意:出现async、await,等价于promise、then。
浏览器从输入URL到页面渲染加载的过程
- 浏览器接收url并开启一个新进程(这一部分可以展开浏览器的进程与线程的关系)
- 浏览器解析输入的 URL,提取出其中的协议、域名和路径等信息。(这部分涉及URL组成部分)
- 浏览器向 DNS 服务器发送请求,DNS服务器通过 多层查询 将该 域名 解析为对应的 IP地址 ,然后将请求发送到该IP地址上,与 服务器 建立连接和交换数据。(这部分涉及DNS查询)
- 浏览器与服务器建立 TCP 连接。(这部分涉及TCP三次握手/四次挥手/5层网络协议)
- 浏览器向服务器发送 HTTP 请求,包含请求头和请求体。(4,5,6,7包含http头部、响应码、报文结构、cookie等知识)
- 服务器接收并处理请求,并返回响应数据,包含状态码、响应头和响应体。
- 浏览器接收到响应数据,解析响应头和响应体,并根据状态码判断是否成功。
- 如果响应成功,浏览器接收到http数据包后的解析流程(这部分涉及到html - 词法分析,解析成DOM树,解析CSS生成CSSOM树(样式树),合并生成render渲染树(样式计算)。然后layout布局,分层,调用GPU绘制等,最后将绘制的结果合成最终的页面图像,显示在屏幕上。这个过程会发生回流和重绘)。
- 连接结束 -> 断开TCP连接 四次挥手
闭包
闭包是指有权访问另一个函数作用域中变量的函数
内部的函数存在外部作用域的引用就会导致闭包。
闭包中的变量存储的位置是堆内存
闭包没有那么复杂,本质就是上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。就导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放。(个人理解,若有错误,欢迎指正)
var a = 0
function foo(){
var b =14
function fo(){
console.log(a, b)
}
fo()
}
foo() //这里的子函数 fo 内存就存在外部作用域的引用 a, b,所以这就会产生闭包
- 闭包的特性
- 闭包可以访问到父级函数的变量
- 访问到父级函数的变量不会销毁
闭包的作用
- 保护函数的私有变量不受外部的干扰。形成不销毁的栈内存。
- 保存,把一些函数内的值保存下来。闭包可以实现方法和属性的私有化
闭包的作用
- 延申变量的作用范围
- 隐藏变量,避免全局污染
使用闭包需要注意什么
容易导致内存泄漏。闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包。
- 闭包的缺点
- 因为垃圾回收机制的存在,会导致不必要的性能消耗
- 不恰当的使用会导致内存泄露
闭包应用
1、防抖节流
防抖节流 防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。
// 防抖
function debounce(fn, timeout){
let timer = null
return function(...arg){
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, arg)
}, timeout)
}
}
节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。
// 节流
function throttle(fn, timeout) {
let timer = null
return function (...arg) {
if(timer) return
timer = setTimeout(() => {
fn.apply(this, arg)
timer = null
}, timeout)
}
}
2、 函数柯里化(curry)
函数柯里化(curry)是函数式编程里面的概念。curry的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
简单点来说就是:每次调用函数时,它只接受一部分参数,并返回一个函数,直到传递所有参数为止。
要判断当前传入函数的参数个数 (args.length) 是否大于等于原函数所需参数个数 (fn.length) ,如果是,则执行当前函数;如果是小于,则返回一个函数。
const curry = (fn, ...args) =>
// 函数的参数个数可以直接通过函数数的.length属性来访问
args.length >= fn.length // 这个判断很关键!!!
// 传入的参数大于等于原始函数fn的参数个数,则直接执行该函数
? fn(...args)
/**
* 传入的参数小于原始函数fn的参数个数时
* 则继续对当前函数进行柯里化,返回一个接受所有参数(当前参数和剩余参数) 的函数
*/
: (..._args) => curry(fn, ...args, ..._args);
function add1(x, y, z) {
return x + y + z;
}
const add = curry(add1);
console.log(add(1, 2, 3));
console.log(add(1)(2)(3));
console.log(add(1, 2)(3));
console.log(add(1)(2, 3));
计算属性和watch的区别
computed对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性。计算属性是基于它们的反应依赖关系缓存的。
watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
计算属性是同步的,而侦听器是异步的
计算属性需要依赖于模板,侦听器不需要
计算属性必须有返回值,但是侦听器不需要
如果数据变化时需要执行异步(例如请求)或者开销较大的操作时,用侦听属性最有用的。
hash和history
hash 模式是一种把前端路由的路径用井号 # 拼接在真实 url 后面的模式。当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 onhashchange 事件。
- hash变化会触发网页跳转,即浏览器的前进和后退。
hash可以改变url,但是不会触发页面重新加载(hash的改变是记录在window.history中),即不会刷新页面。也就是说,所有页面的跳转都是在客户端进行操作。因此,这并不算是一次http请求,所以这种模式不利于SEO优化。hash只能修改#后面的部分,所以只能跳转到与当前url同文档的url。hash通过window.onhashchange的方式,来监听hash的改变,借此实现无刷新跳转的功能。hash永远不会提交到server端(可以理解为只在前端自生自灭)
history的特点
对于 history 来说,主要有以下特点:
- 新的
url可以是与当前url同源的任意url,也可以是与当前url一样的地址,但是这样会导致的一个问题是,会把重复的这一次操作记录到栈当中。 - 通过
history.state,添加任意类型的数据到记录中。 - 可以额外设置
title属性,以便后续使用。 - 通过
pushState、replaceState来实现无刷新跳转的功能。
原型和原型链
在js中,每一个函数类型的数据,都有一个叫做prototype的属性,这个属性指向的是一个对象,就是所谓的原型对象。
对于原型对象来说,它有个constructor属性,指向它的构造函数。
最主要的作用就是用来存放实例对象的公有属性和公有方法。
如果某个对象查找属性,自己和原型对象上都没有,那就会继续往原型对象的原型对象上去找,这个例子里就是Object.prototype,这里就是查找的终点站了,在这里找不到,就没有更上一层了(null里面啥也没有),直接返回undefined。
可以看出,整个查找过程都是顺着__proto__属性,一步一步往上查找,形成了像链条一样的结构,这个结构,就是原型链。所以,原型链也叫作隐式原型链。
栈、堆、队列深入理解
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;
当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它
什么是 XSS
Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
所以,网页上哪些部分会引起XSS攻击?简单来说,任何可以输入的地方都有可能引起,包括URL!
- 输入过滤: 一般是用于对于输入格式的检查,例如:邮箱,电话号码,用户名,密码……等,按照规定的格式输入。不仅仅是前端负责,后端也要做相同的过滤检查。因为攻击者完全可以绕过正常的输入流程,直接利用相关接口向服务器发送设置。
什么是 CSRF
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。如:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
token 验证的 CSRF 防御机制是公认最合适的方案。 (具体可以查看本系列前端鉴权中对token有详细描述)若网站同时存在 XSS 漏洞的时候,这个方法也是空谈。
webpack中 loader 与 plugin 有什么区别
Loader
Loader 用于转换模块的源代码。它们在模块被引入时应用,可以对文件进行预处理,比如将 TypeScript 转换为 JavaScript,或者将 SCSS 转换为 CSS。Loader 是函数,接收源文件内容作为参数,返回转换后的内容。
Plugin
Plugin 用于执行更广泛的任务,通常涉及到打包的各个阶段。它们可以用于优化打包结果、管理资源、注入环境变量等。Plugin 提供了更强大的 API,可以访问 Webpack 的编译过程。
使一个元素脱离标准文档流有三种方式
1.浮动 float
2.绝对定位 absolute
3.固定定位 fixed
Vue组件传值
1、props
父组件向子组件传送数据,这应该是最常用的方式了
子组件接收到数据之后,不能直接修改父组件的数据。
2、.sync 子组件可以修改父组件内容
.sync可以帮我们实现父组件向子组件传递的数据的双向绑定,所以子组件接收到数据后可以直接修改,并且会同时修改父组件的数据
3、v-model
父组件传给子组件的数据为双向绑定,子组件通过 $emit 修改父组件的数据
4、ref
ref 如果在普通的DOM元素上,引用指向的就是该DOM元素;
5、$emit / v-on
子组件通过派发事件的方式给父组件数据,或者触发父组件更新等操作
6 、$attrs / $listeners
多层嵌套组件传递数据时,如果只是传递数据,而不做中间处理的话就可以用这个,比如父组件向孙子组件传递数据时
8、provide / inject
provide / inject 是依赖注入,在一些插件或组件库里被常用
9、 EventBus
EventBus 是中央事件总线,不管是父子组件,兄弟组件,跨层级组件等都可以使用它完成通信操作
10、Vuex 在大项目中被常用
重绘和重排(回流)
重绘不一定导致重排,但重排一定会导致重绘
核心观念: 减少重排次数和减小重排范围(方法)
减少重排次数
1.样式集中改变
不要频繁的操作样式,对于一个静态页面来说,明智且可维护的做法是更改类名而不是修改样式,对于动态改变的样式来说,相较每次微小修改都直接触及元素,更好的办法是统一在 cssText 变量中编辑。虽然现在大部分现代浏览器都会有 Flush 队列进行渲染队列优化,但是有些老版本的浏览器比如IE6的效率依然低下。
// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// 当top和left的值是动态计算而成时...
// better
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
// better
el.className += " className";
2.分离读写操作
DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。
// bad 强制刷新 触发四次重排+重绘
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
div.style.right = div.offsetRight + 1 + 'px';
div.style.bottom = div.offsetBottom + 1 + 'px';
// good 缓存布局信息 相当于读写分离 触发一次重排+重绘
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
var curRight = div.offsetRight;
var curBottom = div.offsetBottom;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
div.style.right = curRight + 1 + 'px';
div.style.bottom = curBottom + 1 + 'px';
3.将 DOM 离线
“离线”意味着不在当前的 DOM 树中做修改,我们可以这样做:
-
使用 display:none
一旦我们给元素设置
display:none时(只有一次重排重绘),元素便不会再存在在渲染树中,相当于将其从页面上“拿掉”,我们之后的操作将不会触发重排和重绘,添加足够多的变更后,通过display属性显示(另一次重排重绘)。通过这种方式即使大量变更也只触发两次重排。另外,visibility : hidden的元素只对重绘有影响,不影响重排。 -
通过 documentFragment 创建一个
dom碎片,在它上面批量操作dom,操作完成之后,再添加到文档中,这样只会触发一次重排。 -
复制节点,在副本上工作,然后替换它!
vue双向绑定数据原理(v-model)
vue是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调。
第一步:
需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么久能监听到了数据变化
第二步:
compile解析摸板,将模板中的变量替换成数据.然后初始化渲染页面视图,并将每个令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:
Watcher订阅名是 observer和 Compile之间通信的桥梁,主要做的事情是:
1.在自身实例化时往属性订倒器(dep)里面添加自己
2.自身必须有一个 update()方法
3.待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中定的回调。
第四步:
MVVM作为数据绑定的入口,合 observer、 Compile和 Watcher三者,通过 Observer来监听自己的model数据変化,通过 Compile来解析编译模板指令,最终利用 Watcher搭起 Observer和 Compile之间的通信标梁,达到数据变化->视图更新新:视图交互变化(Input)->数据mode变更的双向绑定效果。
自己理解
2.vue的单项单项数据绑定原理:
单项绑定过程(自己总结的):变量变了,由set发通知给watcher,watcher告知虚拟DOM树,叫它该比较了,我这有值变了,于是生成新的dom树进行一个比较,然后逐级分类比较,比较出哪个元素发生变化就把这个元素更新到页面,这就是单项数据绑定原理。
3.vue双向数据绑定原理
vue的双向数据绑定原理,当页面有一个input元素咋办,这时候v-model双向数据绑定就派上用场了,但是v-model双向数据绑定原理又是怎样的呢?请看下图:
mixins的特点(简单来说,就是将一个组件的变量和方法作为公共的,可以合并到任意一个组件中,后者可以调用前者的变量与方法)
1.方法和参数在各组件中不共享,虽然组件调用了mixins并将其属性合并到自身组件中来了,但是其属性只会被当前组件所识别并不会被共享,也就是其他组件无法从当前组件中获取到mixins中的数据和方法。
组件1里改变了num里面的值,组件2中的num值还是混入对象里的初始值。
2.值为对象的选项,如methods,components等,选项会被合并,键冲突的组件会覆盖混入对象的
3 值为函数的选项,如created,mounted等,就会被合并调用,混合对象里的钩子函数在组件里的钩子函数之前调用
一.观察者模式(Observer Pattern):
v-model
当一个对象中的数据被多个对象所依赖,并且当被依赖发生变化的时候,会通知所有的依赖项。我们称这种模式为观察模式,被依赖的对象我们称之为目标,依赖项我们称之为观察者。
二.发布-订阅模式(Publish-Subscribe Pattern):
事件总线来实现发布-订阅模式(eventBus)
vue是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调。
(vue2的响应式主要是数据劫持,结合发布-订阅者模式的方式
通过Object.defineProperty()的方式劫持各个属性的getter,setter,在数据变化时,通知订阅者,触发响应的回调来实现的。)