## 前端面试笔记
前端面试笔记
vue中的computed和watch属性的原理
1.概念 computed(计算属性)和watch(监听器)都是以vue的依赖追踪机制为基础的,当依赖数据发生变化时,依赖此数据的相关数据会自动变化
2.应用场景 computed处理场景:一个数据受多个数据的影响;watch处理场景:一个数据影响多个数据
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的值有缓存的特性,避免每次获取值时,都要重新计算;所以如果v-if涉及较多的数值计算,用computed性能会比v-if更好 不需要每次重新计算
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
3.原理 watch实现过程:
watch的初始化在data初始化之后(此时的data已经通过Object.defineProperty的设置成响应式) watch的key会在Watcher里进行值的读取,也就是立马执行get获取value(从而实现data对应的key执行getter实现对于watch的依赖收集),此时如果有immediate属性那么立马执行watch对应的回调函数 当data对应的key发生变化时,触发user watch实现watch回调函数的执行 computed实现过程:
当组件初始化的时候,computed和data会分别建立各自的响应系统,Observer遍历data中每个属性设置get/set数据拦截 初始化computed会调用initComputed函数
注册一个watcher实例,并在内实例化一个Dep消息订阅器用作后续收集依赖(比如渲染函数的watcher或者其他观察该计算属性变化的watcher) 调用计算属性时会触发其Object.defineProperty的get访问器函数 调用watcher.depend()方法向自身的消息订阅器dep的subs中添加其他属性的watcher 调用watcher的evaluate方法(进而调用watcher的get方法)让自身成为其他watcher的消息订阅器的订阅者,首先将watcher赋给Dep.target,然后执行getter求值函数,当访问求值函数里面的属性(比如来自data、props或其他computed)时,会同样触发它们的get访问器函数从而将该计算属性的watcher添加到求值函数中属性的watcher的消息订阅器dep中,当这些操作完成,最后关闭Dep.target赋为null并返回求值函数结果。 当某个属性发生变化,触发set拦截函数,然后调用自身消息订阅器dep的notify方法,遍历当前dep中保存着所有订阅者wathcer的subs数组,并逐个调用watcher的 update方法,完成响应更新。 原文链接:https://blog.csdn.net/qq_24510455/article/details/81057261
vue中的proxy
- 用于定义基本操作的自定义行为
- 用于创建一个对象的代理,从而实现基本操作的拦截和自定义 ————查找 枚举 自定义
因为defineProperty自身的缺陷,导致vue2在实现响应式的过程需要其他方法的辅助(重写数组,增加 set delete方法)
var proxy =new Proxy(target,handler)
//target 要拦截的对象 handdler以函数作为属性的对象
- get(target,propKey,receiver):拦截对象属性的读取
- set(target,propKey,value,receiver):拦截对象属性的设置
- has(target,propKey):拦截
propKey in proxy
的操作,返回一个布尔值 - deleteProperty(target,propKey):拦截
delete proxy[propKey]
的操作,返回一个布尔值 - ownKeys(target):拦截
Object.keys(proxy)
、for...in
等循环,返回一个数组 - getOwnPropertyDescriptor(target, propKey):拦截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象 - defineProperty(target, propKey, propDesc):拦截
Object.defineProperty(proxy, propKey, propDesc)
,返回一个布尔值 - preventExtensions(target):拦截
Object.preventExtensions(proxy)
,返回一个布尔值 - getPrototypeOf(target):拦截
Object.getPrototypeOf(proxy)
,返回一个对象 - isExtensible(target):拦截
Object.isExtensible(proxy)
,返回一个布尔值 - setPrototypeOf(target, proto):拦截
Object.setPrototypeOf(proxy, proto)
,返回一个布尔值 - apply(target, object, args):拦截 Proxy 实例作为函数调用的操作
- construct(target, args):拦截 Proxy 实例作为构造函数调用的操作
vue响应式布局
最终响应式配置的逻辑链就形成了,根组件事实监听窗口大小->将窗口大小保存在本地->组件监听本地的窗口大小数据变化->赋值给组件参数->组件参数变化,进入切换类的判断->根据判断决定返回的类名->通过类名在CSS中找到对应的样式->样式改变。
- 因为在大部分的情况下是使用在web端的 所以此时窗口的大小>500px
- 首先再vue对应组件内使用局部性css写好基础类的样式
- 编写其他情况下的css样式 命名为mobile.less 然后再需要引用的地方引入less文件即可 全局适配的话需要全局云人员这个文件
- 通过vue中增加窗口大小的state、 更组件使用mounted声明周期函数 (window.onresize) 监听创建口宽度高度的变化
或者直接使用媒体查询的方式
@media (min-device-width:600px) {
img[src-600px] {
content: attr(src-600px, url);
}
}
@media (min-device-width:800px) {
img[src-800px] {
content: attr(src-800px, url);
}
}
数组原生的方法造成的数据更新 可以被vue监听到 push() pop()
使用this.$set 来解决无法监听数组属性内部变化的问题
vue初始化页面闪动 页面未被解析之前就展示再dom中 使用v-cloak将message 包起来
使用vue开启keep-alive缓存的页面
if
="$route.meta.keepAlive"
>` 页面的代码
meta:{keepAlive:``true``}
使用element ui 干过什么东西
使用layout布局
使用混合布局 响应式布局
使用container布局容器哦 el-header el-aside el-main el-footer
vue中阻止事件冒泡
- stop 阻止事件冒泡 。
- self 将事件绑定在自己的身上,相当于是阻止了事件冒泡
- prevent 阻止默认的事件
- once 事件只进行一次触发
vue中的单向数据流
父级 prop 的更新会向下流动到子组件中,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值
- 意义 防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
- 解决办法:
- 1、定义一个局部变量,并用 prop 的值初始化它。
- 2、定义一个计算属性,处理 prop 的值并返回。
vue中监听不到数组长度变化的原因
object.property的原因可以检测到通过索引改变数组操作的变化,但是vue并没有进行实现 ,主要是因为性能的代价和用户体验的收益不成正比
vue中的v-for v-if不能连用
1.v-for比v-if优先,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。
如果连用的话会把 v-if 给每个元素都添加一下,会造成性能问题。
一般时候把v-if放在外层,如果不符合就不去执行了。
也可以使用计算属性computed来代替v-if
vue组件中通信的方式
- 父子组件之间进行通信
- 父传子 父组件使用 v-bind 子组件使用props属性进行接受
- 子传父 子组件使用$emit 发送 父组件v-on进行接受
- 使用refs-$ref
- parent/children
- 全局组件之间的通信
- 任意组件 使用 $emit进行发送数据 使用 on进行接受
- 使用vuex 状态管理工具
- 注册全局组件$store 将共享数据放入state中进行存储
- 使用getters进行数据的获取
- 使用mutations进行全局变量的更改 同步数据的操作 使用 this.$store.commit 提价数据修改
- 使用actions进行异步数据操作 this.$store.dispatch(“方法名”,数据)
computed和watch的区别
computed 计算属性,依赖其他属性,当其他属性改变的时候下一次获取computed值时也会改变,computed的值会有缓存 watch 类似于数据改变后的回调 如果想深度监听的话,后面加一个deep:true 如果想监听完立马运行的话,后面加一个immediate:true
vue中的nexttick
实现的原理:
- 把回调函数放入callbacks等待执行
- 将执行函数放入宏任务或者为任务中
- 实现循环到了微任务或者宏仁无中,执行函数一次执行callbacks中的回调
如果想要修改数据后立刻得到更新后的dm
vue\实现响应式并不是在数据变化之后dom立即变化而是在同意时间循环中所有的数据变化完成之后 再进行dom统一更新(本质上是一种优化策略)
- 1.在Vue生命周期的created()钩子函数进行DOM操作一定要放到Vue.nextTick()的回调函数中
- 2.在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。
- 父
beforeCreate
->父created
->父beforeMount
->子beforeCreate
->子created
->子beforeMount
->子mounted
->父mounted
router中的钩子函数
- beforeEach
- 参数有
- to(Route路由对象)
- from(Route路由对象)
- next(function函数) 一定要调用才能进行下一步
- 参数有
- afterEach
- beforeRouterLeave
webpack中loader和plugin的区别是什么
loader loader是用来解析非js文件的,因为Webpack原生只能解析js文件,如果想把那些文件一并打包的话,就需要用到loader,loader使webpack具有了解析非js文件的能力 plugin 用来给webpack扩展功能的,可以加载许多插件
router中push replace的区别
1.this.$router.push()
描述:跳转到不同的url,但这个方***向history栈添加一个记录,点击后退会返回到上一个页面。
2.this.$router.replace()
描述:同样是跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。
3.this.$router.go(n)
相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)
。n可为正数可为负数。正数返回上一个页
router 的原理
原理是通过改变url 在不重新请求页面的情况下更新视图
更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式,Hash模式和History模式:
(1)利用URL中的hash("#");
(2)利用History interface在HTML5中新增的方法;
1、Hash模式:
hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中也不会不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;
2、History模式: (不怕前进不怕后退 就怕刷新)
//HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 //history.pushState API 来完成 URL 跳转而无须重新加载页面;
this.$router.push("/local")
this.$router.replace("/cal")
window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
通常情况下,我们会选择使用History模式,原因就是Hash模式下URL带着‘#’会显得不美观;但实际上,这样选择一不小心也会出问题;比如: 但当用户直接在用户栏输入地址并带有参数时: Hash模式:xxx.com/#/id=5 请求地址为 xxx.com,没有问题; History模式: xxx.com/id=5 请求地址为 xxx.com/id=5,如果后端没有对应的路由处理,就会返回404错误;
history模式下的原生api
window.history.pushState(state, title, url)
// state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
// title:标题,基本没用,一般传 null
// url:设定新的历史记录的 url。新的 url 与当前 url 的 origin 必须是一樣的,否则会抛出错误。url可以是绝对路径,也可以是相对路径。
//如 当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, './qq/'),则变成 https://www.baidu.com/a/qq/,
//执行history.pushState(null, null, '/qq/'),则变成 https://www.baidu.com/qq/
window.history.replaceState(state, title, url)
// 与 pushState 基本相同,但她是修改当前历史记录,而 pushState 是创建新的历史记录
window.addEventListener("popstate", function() {
// 监听浏览器前进后退事件,pushState 与 replaceState 方法不会触发
});
window.history.back() // 后退
window.history.forward() // 前进
window.history.go(1) // 前进一步,-2为后退两步,window.history.lengthk可以查看当前历史堆栈中页面的数量
路由传参
vue-router传递参数分为两大类
-
编程式的导航 router.push
-
this.route.query.queryId --- 使用push:this.router.push({ path: '/news', query: { userId: 123 }});
-
{{this.route.params.userId} -- this.router.push({ name: 'news', params: { userId: 123 }}
-
-
声明式的导航
<router-link>
-
<router-link :to="{ name: 'news', params: { userId: 1111}}">click to news page</router-link>
-
<router-link :to="{ path: '/news', query: { userId: 1111}}">click to news page</router-link>
-
虚拟DOM的优缺点
- 缺点
- 首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢
- 优点
- 减少了dom操作,减少了回流与重绘
- 保证性能的下限,虽说性能不是最佳,但是它具备局部更新的能力,所以大部分时候还是比正常的DOM性能高很多的
vue 中key的作用
- key
- key主要用在虚拟Dom算法中,每个虚拟节点VNode有一个唯一标识Key,通过对比新旧节点的key来判断节点是否改变,用key就可以大大提高渲染效率,这个key类似于缓存中的etag。
设置cookie允许跨域
//允许跨域的域名,*号为允许所有,存在被 DDoS攻击的可能。
getResponse().setHeader("Access-Control-Allow-Origin","*");
//表明服务器支持的所有头信息字段
getResponse().setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma,Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
/** 目前测试来看为了兼容所有请求方式,上面2个必须设 **/
//如果需要把Cookie发到服务端,需要指定Access-Control-Allow-Credentials字段为true;
getResponse().setHeader("Access-Control-Allow-Credentials", "true");
// 首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。
//该字段与 HTTP/1.1 Allow: response header 类似,但仅限于在需要访问控制的场景中使用。
getResponse().setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
//表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。
//请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。
getResponse().setHeader("Access-Control-Max-Age", "86400");
// IE8 引入XDomainRequest跨站数据获取功能,也就是说为了兼容IE
getResponse().setHeader("XDomainRequestAllowed","1");
使用axios配置跨域的解决方案
-
使用纯前端代理的方式解决跨域
-
使用方法
-
在全局引入axios进行导入
-
全局挂载axios对象
-
配置axios对象 在js文件中设置baseURl地址 定位地址/api
-
在全局的配置文件vue.fonfigure.js中修改相关的配置
-
'/api':{ target: 'https://www.baidu.com/', // 允许跨域 changeOrigin: true, ws: true, pathRewrite: { '^/api': '' } } } this.$axios.get('/').then(response => { if (response.data) { console.log(response.data) } }
-
js
下拉刷新来实现
而我们下拉刷新的过程主要是用到了触碰屏幕(touchstart),开始下拉(touchmove),松手(touchend)。
// 下拉刷新
_this.outerScroller.addEventListener(
'touchstart',
function (event) {
var touch = event.targetTouches[0];
// 把元素放在手指所在的位置
_this.touchStart = touch.pageY;
// event.preventDefault(); // 阻止浏览器默认下滑事件
},
false
);
_this.outerScroller.addEventListener(
'touchmove',
function (event) {
var touch = event.targetTouches[0];
_this.scroll.style.top =
_this.scroll.offsetTop + touch.pageY - _this.touchStart + 'px';
_this.touchStart = touch.pageY;
_this.touchDis = touch.pageY - _this.touchStart;
if (_this.scroll.offsetTop > 0 && document.body.scrollTop < 50) {
document.body.addEventListener('touchmove', _this.preEvent, {
passive: false
}); // passive 参数不能省略,用来兼容ios和android
_this.emptyDiv.style.marginTop =
Number(_this.scroll.style.top.replace(/\s+|px/gi, '')) + 20 + 'px'; // 让下拉的父级高度等于下滑部分+下滑距离,让整体呈现下拉的感觉
} else {
return;
}
if (_this.scroll.offsetTop >= 80) {
// 限制下拉的距离
_this.scroll.style.top = '80px';
}
},
false
);
_this.outerScroller.addEventListener(
'touchend',
function (event) {
_this.touchStart = 0;
var top = _this.scroll.offsetTop;
var num = _this.scroll.style.top.replace(/\s+|px/gi, ''); // 将top值转成数值
if (num < 0) {
// 当上滑到顶部的时候,不超过边界
_this.scroll.style.top = '0px';
return;
}
if (top > 40) refresh();
if (top > 0) {
var time = setInterval(function () {
_this.scroll.style.top = _this.scroll.offsetTop - 2 + 'px';
_this.emptyDiv.style.marginTop =
Number(_this.scroll.style.top.replace(/\s+|px/gi, '') + 20) +
'px'; // 让下拉的父级高度等于下滑部分+下滑距离,让整体呈现下拉的感觉
if (_this.scroll.offsetTop <= 0) clearInterval(time);
}, 1);
}
/* var time = setTimeout(function(){
scroll.style.top = scroll.offsetTop -2+'px';
emptyDiv.style.height = Number(360) + Number(scroll.style.top.replace(/\s+|px/gi,"")) + 'px'; // 让下拉的父级高度等于下滑部分+下滑距离,让整体呈现下拉的感觉
if(scroll.offsetTop<=0)clearTimeout(time);
time();
},1)
while(top>0){
time();
} */
// event.stopPropagation();
// window.event.returnValue = false;
document.body.removeEventListener('touchmove', _this.preEvent, {
passive: false
}); // passive 参数不能省略,用来兼容ios和android
},
false
);
function refresh () {
console.log('下拉刷新');
}
es7新特性
es7在es6的基础上新增加了三项的内容 分别是求幂运算符 ** Array.prototype.includes方法 以及在函数作用域中严格模式的变更
js原型原型链的理解
在JavaScript中是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性,它的属性值是一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说不应该能够获取到这个值的,但是现在浏览器中都实现了 proto 属性来访问这个属性,但是最好不要使用这个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,可以通过这个方法来获取对象的原型。
当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就是新建的对象为什么能够使用 toString() 等方法的原因。
- 原型链的终点
- 由于Object是构造函数,原型链终点是Object.prototype.proto,而Object.prototype.proto=== null // true,所以,原型链的终点是null。原型链上的所有原型都是对象,所有的对象最终都是由Object构造的,而Object.prototype的下一级是Object.prototype._proto
js判断类型
typeof
缺点:typeof null的值为Object,无法分辨是null还是Object instanceof
缺点:只能判断对象是否存在于目标对象的原型链上 constructor
Object.prototype.toString.call()
一种最好的基本类型检测方式 Object.prototype.toString.call() ;它可以区分 null 、 string 、
boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math 数据类型。
缺点:不能细分为谁谁的实例
null和undefined的区别
null
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
undefined
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
两种声明函数方式的区别
使用var a=function(){} 使用变量型的函数声明函数 函数不会实现变量的提升
const声明的对象可以更改?
const声明的基本数据类型是不能操作修改的
声明的引用数据类型因为存的是对象的指针地址
因为对象和数组是引用类型,a中保存的仅是数组的指针,这就意味着,const仅保证指针不发生改变,修改数组的值不会改变对象的指针,所以是被允许的。
js onload show
onload 在页面加载时出发 一个页面只会调用一次 onshow在页面进行显示切换的时候出发
比如从当前页面跳转到另一个页面 返回时不会触发onload 会触发onshow
js手写一个new
- 创建一个新对象
- 使新对象的
__proto__
指向原函数的prototype
- 改变this指向(指向新的obj)并执行该函数,执行结果保存起来作为result
- 判断执行函数的结果是不是null或Undefined,如果是则返回之前的新对象,如果不是则返回res
// 手写一个new
function myNew(fn, ...args) {
// 创建一个空对象
let obj = {}
// 使空对象的隐式原型指向原函数的显式原型
obj.__proto__ = fn.prototype
// this指向obj
let result = fn.apply(obj, args)
// 返回
return result instanceof Object ? result : obj
}
选择器优先级
1. !important
在属性后面写上这条样式,会覆盖掉页面上任何位置定义的元素的样式。
2. 行内样式,在style属性里面写的样式。 1000
3. id选择器 100
4. class选择器10 伪类 权值为10
3. 标签选择器 伪元素 1
\6. 通配符选择器*
7. 浏览器的自定义属性和继承
ajax 返回的状态
0 - (未初始化)还没有调用send()方法 1 - (载入)已调用send()方法,正在发送请求 2 - (载入完成)send()方法执行完成,已经接收到全部响应内容 3 - (交互)正在解析响应内容 4 - (完成)响应内容解析完成,可以在客户端调用了
js为什么要设计成单线程、
js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。 举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,此时浏览器就会一脸茫然,不知所措。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变
实现sum的无线累加
function sum(a){
let temp = function(b){
return sum(a+b)
}
// temp.toString这里写成temp.valueOf也可以
temp.toString = function(){
return a
}
return temp
}
let ans = sum(1)(2)(3)
console.log(ans)
js中map和foreach的区别
- 能用
forEach()
做到的,map()
同样可以。反过来也是如此。 map()
会分配内存空间存储新数组并返回,forEach()
不会返回数据。forEach()
允许callback
更改原始数组的元素。map()
返回新的数组
promise的原理
基于上面的应用场景发现promise
可以有三种状态,分别是pedding
、Fulfilled
、 Rejected
。
Pending Promise
对象实例创建时候的初始状态Fulfilled
可以理解为成功的状态Rejected
可以理解为失败的状态
- 构造一个
Promise
实例需要给Promise
构造函数传入一个函数。传入的函数需要有两个形参,两个形参都是function
类型的参数。分别是resolve
和reject
。 Promise
上还有then
方法,then
方法就是用来指定Promise
对象的状态改变时确定执行的操作,resolve
时执行第一个函数(onFulfilled),reject
时执行第二个函数(onRejected)- 当状态变为
resolve
时便不能再变为reject
,反之同理。
async awit 的实现一案例
本质上async还是一个promise函数 是将promise。then()这个异步操作的回调函数放在了awit之后进行执行
js实现异步操作的发展史
js promise.all的实现
class MyPromise2 {
constructor(executor) {
// 规定状态
this.state = "pending"
// 保存 `resolve(res)` 的res值
this.value = undefined
// 保存 `reject(err)` 的err值
this.reason = undefined
// 成功存放的数组
this.successCB = []
// 失败存放的数组
this.failCB = []
let resolve = (value) => {
if (this.state === "pending") {
this.state = "fulfilled"
this.value = value
this.successCB.forEach(f => f())
}
}
let reject = (reason) => {
if (this.state === "pending") {
this.state = "rejected"
this.value = value
this.failCB.forEach(f => f())
}
}
try {
// 执行
executor(resolve, reject)
} catch (error) {
// 若出错,直接调用reject
reject(error)
}
}
then(onFulfilled, onRejected) {
if (this.state === "fulfilled") {
onFulfilled(this.value)
}
if (this.state === "rejected") {
onRejected(this.value)
}
if (this.state === "pending") {
this.successCB.push(() => { onFulfilled(this.value) })
this.failCB.push(() => { onRejected(this.reason) })
}
}
}
//手写实现一个promise.all函数
Promise.all = function (promises) {
let list = []
let count = 0
function handle(i, data) {
list[i] = data
count++
if (count == promises.length) {
resolve(list)
}
}
return Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
handle(i, res)
}, err => reject(err))
}
})
}
promise.all和promise.race的使用场景
- promise.all可以将多个promise实例包装成一个新的promise实例
- 接收一个promise数组作为参数
- 数组可以是promise对象 也可以是其他值 promise会等待状态的改变
- 当所有的子promise都完成,该promise完成,返回值是全部值的数组
- 如果其中有任何一个失败,返回值是第一个失败的子promise的结构
promise.race 把异步操作和定时器放到一起 如果定时器先出发,认为超时,并告知用户
js实现键名的替换
var list = [{ id: 'a', title: 'A'},
{ id: 'b', title: 'B', children: [{ id: 'c', title: 'C' }, { id: 'd', title: 'D' }]}]
//实现一个函数 将对象中的所有id变为code 将title 变为name
function conve(list){
for( let i in list){
if(list[i].id!=null){
list[i].code=list[i].id;
delete list[i].id
}else if (list[i].title!=null){
list[i].title=list[i].name;
delete list[i].title
}else if(list[i].children!=null){
conve(list[i])
} }
return list}console.log(conve(list))
//实现一个单词的逆序
function nixu(str){ return str.split(" ").reverse().join(" ")}
//实现数字金额的格式化
function formatter(number){} //小数部分保留两位小数 整数部分分三位增加逗号
[1,2,3].map(parseInt)结果
-
map函数将数组种的每个元素传递给指定的函数处理 上式种将字符串123作为元素,0,1,2,作为下标分别调用parseInt函数
-
parseInt 接收两个参数parseInt(string,radix)
-
其中radix
1) 区间范围介于2~36之间;
2 ) 当参数为 0,parseInt() 会根据十进制来解析;
3 ) 如果忽略该参数,默认的基数规则:
js中的parseINt 和number方法的区别
- parseInt( string,radix ) 传入的只可以是解析的字符串,并且会根据第二个进制参数来进行转化
Number(object):将对象的值转换为数字;
如果对象的值无法转换为数字,那么 Number() 函数返回 NaN。
如果参数是 Date 对象,Number() 返回从 1970 年 1 月 1 日至今的毫秒数。
js中0.2+0.1>0.3为什么
- js中的浮点数用64位固定的长度来表示
- 52为表示位数
- 0.1转换为二进制是一个无限循环的小数0.00000110001100011000 在转换为十进制就不是0.1了
箭头函数的特点
- 不能作为构造函数,不能使用new
- 不能使用argumetns,取而代之用rest参数...解决
- 不绑定this
- 箭头函数没有原型对象
普通函数的this 指向他的调用者 如果没有调用者则指向默认的window
箭头函数的this指向:指向箭头函数定义时所处的对象,而不是箭头函数使用时所在的对象,默认使用父级的this
var a = 10
var obj = {
a: 20,
say: () => {
console.log(this.a)
}
}
obj.say()
var anotherObj={a:30}
obj.say.apply(anotherObj) // 10 10
如上该题的this指向的是箭头函数定义的地方对象 默认使用的是函数fn父级的this
3.箭头函数中的this,首先从它的父级作用域中找,如果父级作用域还是箭头函数,再网上找,如此直至找到this的指向
js中map和set的区别
map中存储的是键值对的形式,set中存储的是非重复的对象
map中的key不可以重复,但是值是可以重复的 set中的值不可以重复
存取值的方式不一样 map.set(key,value)
简单描述下this
//箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象 //每一个函数或对象的建立都是一个新的执行上下文 //构造函数的属性全部都是挂载在原型链上面 //当构造函数实例化对象调用的也是继承与原型的属性与方法 //构造函数里面的方法嵌套函数会改变this指向window需要先用变量存储this //在对象里调用的this是指向对象的 //setInterval(()=>{console.log(this)},1000)维护this一致 // setInterval(function(){console.log(this)},1000)this是window //不建议在定义函数方法且方法包含this的时候使用箭头函数 ————————————————
原文链接:https://blog.csdn.net/qq_43505774/article/details/105385360
js设计模式
-
单例模式 不管创建多少个对象都仅仅只有一个实例
-
单点登录的登录按钮 无论点击多少次登录 浮窗只允许被创建一次
-
function creatSingleton() { var obj = null // 核心思想:实例如已经创建过,直接返回 if (!obj) { obj = xxx } return obj }
-
var Single = (function () {
var instance = null
function Single(name) {
this.name = name
}
return function (name) {
if (!instance) {
instance = new Single(name)
}
return instance
}
})()
var oA = new Single('hi')
var oB = new Single('hello')
console.log(oA);
console.log(oB);
console.log(oB === oA);
-
工厂模式 代替new创建一个对象,且对象像工厂制作一样 批量制作属性相同的实例对象
- 构造函数和创建者分离,对new操作进行封装
- 符合开放封闭原则
-
function Animal(o) { var instance = new Object() instance.name = o.name instance.age = o.age instance.getAnimal = function () { return "name:" + instance.name + " age:" + instance.age } return instance } var cat = Animal({name:"cat", age:3}) console.log(cat);
手写实现eventbus(发布订阅模式)
class Observer {
constructor() {
this.events={}//事件中心
}
publish(eventName,...args) {//发布=>调用事件中心中对应的函数
if (this.events[eventName])
this.events[eventName].forEach(cb=>cb.apply(this,args))
}
subscribe(eventName,callback) {//订阅=>向事件中心中添加事件
if (this.events[eventName]) {
this.events[eventName].push(callback)
} else {
this.events[eventName]=[callback]
}
}
unSubscribe(eventName,callback) {//取消订阅
if (events[eventName])
events[eventName]=events[eventName].filter(cb=>cb!==callback)
}
}
class EventBus {
constructor(){
this.eventContainer = this.eventContainer || new Map() //用一个容器存放事件
}
on(type,callback){
if(!this.eventContainer.has(type)){
//如果容器里面没有这种类型的事件,就增加
this.eventContainer.set(type,callback)
}
}
off(type){
if(this.eventContainer.has(type)){
this.eventContainer.delete(type)
}
}
emit(type){
let fn = this.eventContainer.get(type)
fn.apply(this,[...arguments].slice(1))
}
}
let ev = new EventBus()
ev.on('myevent',name=>{console.log('hello,',name)})
ev.emit('myevent','jack')
编程题
实现数组的全排列
function quanpai(arr){
let len=arr.length //递归回溯深度优先搜索
let res=[];
let dui=[]
let used=[]
dfs(arr,len,0,dui,res,used)
return res
function dfs(arr,len,deep,dui,res,used){
if(dui.length==len){
res.push(Array.from(dui))
}
for(let i =0;i<len;i++){
if(used[i]) continue
dui.push(arr[i])
used[i]=true
dfs(arr,len,deep+1,dui,res,used)
dui.pop()
used[i]=false
}
}
}
闭包和立即执行函数的区别
立即执行函数和闭包没有关系,虽然两者会经常结合在一起使用,但两者有本质的不同
立即执行函数只是函数的一种调用方式,只是声明完之后立即执行,这类函数一般都只是调用一次(可用于单例对象上),调用完之后会立即销毁,不会占用内存
闭包则主要是让外部函数可以访问内部函数的作用域,也减少了全局变量的使用,保证了内部变量的安全,但因被引用的内部变量不能被销毁,增大了内存消耗,使用不当易造成内存泄露
原文链接:https://blog.csdn.net/Liu_yunzhao/article/details/90641956
ajax 和 jsonp的调用
jsonp('http://xxxxxxxx',{id:123},data=>{ //获取数据后的操作 })
ajax('get','https://www.baidu.com',{id:15},data=>console.log(data))
统计一个复杂对象值中字符匹配的个数
funciton fuza(obj){
var res=0;
for(let i in obj){
if(obj[i] instaceof Object){
fuza(obj[i])
}else if(obj[i] instaceof Array){
fuza(obj[i])
}else{
continue
}
}
}
css
rgba透明度,opacity透明度
rgba()和opacity都能实现透明效果,但最大的不同是opacity作用于元素,以及元素内的所有内容的透明度,而rgba()只作用于元素的颜色或其背景色。(设置rgba透明的元素的子元素不会继承透明效果!)比如,我们写透明的黑色部分都是用opcity(0.5),但这带出来一个问题就是如果你在这一div上写字的话,然后那个字体也会变成透明色。所以我们采取rgba的样式写,前面三个数字分别对应r,g,b,的三种颜色,第四位的数字对应的是透明的系数
设置文本溢出显示为省略号
- 单行文本
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
-
多行文本
#txt{ display: inline-block; height: 40px; width: 250px; line-height: 20px; overflow: hidden; font-size: 16px; } .t:after{ display: inline; content: "..."; font-size: 16px; }
块级元素和行内元素的区别
- 对于行内元素设置 padding和margin也是有效的,但是只有margin-left和margin-right是有效的 上下内边距和外边距只是在内容山进行了撑开 对盒子外的元素并没有影响
- 行内元素会在一条直线上排列,都是同一行的,水平方向排列。块级元素各占据一行,垂直方向排列。块级元素从新行开始结束接着一个断行。
- 块级元素可以包含行内元素和块级元素。行内元素不能包含块级元素。
- 行内元素与块级元素属性的不同,主要是盒模型属性上。
- 行内元素设置width无效,height无效(可以设置line-height),margin上下无效,padding上下无效。
实现块级元素和行内元素居中的方法
对于行内元素,只需在父元素中设置text-align=center即可;
对于块级元素有以下几种居中方式:
1.将元素放置在table中,再将table的margin-left和margin-right设置成auto,将table居中,使在其之中的块级元素叶居中,但是这种方式不符合语义化标签的规范; (margin:0 auto)
2.将块级元素转换行内元素(通过设置display:inline)后再居中.这种方式使居中元素变成行内元素而致使无法设置宽高;
3.设置父元素float:left,position:relative,left:50%;子元素float:left,position:relative,left:-50%,利用相对布局的方式居中.上面三种方式各有优劣,依使用情景具体选择.
-
垂直居中的设置方法
1.对于知道高度的元素可以设置上下padding相等;
2.设置line-height和height相等
3.利用vertical-align,但是这属性只在tr,td中适用,故可将元素放置入table中在居中
伪类和为元素的区别
- 伪类本质上是为了弥补常规CSS选择器的不足,以便获取到更多信息;
- 伪元素本质上是创建了一个有内容的虚拟容器;
- CSS3中伪类和伪元素的语法不同;
- 可以同时使用多个伪类,而只能同时使用一个伪元素;
css清除浮动的方式
浮动带来的副作用
父元素塌陷 行内元素为浮动元素让路 遮挡块状元素
\1. 使用空标签清除浮动。
这种方法是在所有浮动标签后面添加一个空标签 定义css clear:both. 弊端就是增加了无意义标签。
\2. 使用overflow。
给包含浮动元素的父标签添加css属性 overflow:auto; zoom:1; zoom:1用于兼容IE6。
\3. 使用after伪对象清除浮动。
该方法只适用于非IE浏览器。具体写法可参照以下示例。使用中需注意以下几点。一、该方法中必须为需要清除浮动元素的伪对象中设置 height:0,否则该元素会比实际高出若干像素;
可以给父元素设置overflow:auto或者hidden
引入css样式的方式
1 行内样式
2 head标签中写入style样式
3 外部的样式 - 通过link @import
-
1、链接式
2、导入式
margin 塌陷的问题
margin塌陷的问题 如果是同级元素 外边距小的会被外边距大的元素进行包围 解决的办法
父子元素 本身父元素的上边距为0 设置子元素的上边距后父元素会随着子元素一起掉下来
(1)同级元素:如果两个元素垂直方向有间距,只需要设置给一个元素,不要进行拆分。
(2)父子元素:让两个边距不要相遇,中间可以使用父元素border或padding将边距分隔开;更加常用的方法,父子盒模型之间的距离就不要用子元素的margin去设置,而是用父元素的padding挤出来。
css上下margin重叠的问题
- 内层元素添加 float 或者display
- 加一个inline元素
- overflow:hidden
- 内层元素使用绝对定位
- 养成良好的代码习惯 再设置 magin-bottm的时候给增加margin-top
1 <div style="background: red;">
2 <div style="margin-top:50px;float:left:display:inline-block;">margin</div>
3 </div>
1 <div style="background: red;padding-top:1px;">
2 <div style="margin-top:50px;">margin</div>
3 </div>
1 <div style="background: red;overflow:hidden;">
2 <div style="margin-top:50px;">margin</div>
3 </div>
css3新特性
\1. CSS3实现圆角(border-radius),阴影(box-shadow),
\2. 对文字加特效(text-shadow、),线性渐变(gradient),旋转(transform)
\3. transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);// 旋转,缩放,定位,倾斜
\4. 增加了更多的CSS选择器 多背景 rgba
\5. 在CSS3中唯一引入的伪类是 ::selection.
\6. 媒体查询,多栏布局 @media screen and (max-width: 500px) {当窗口的宽度小于500px时将背景色进行修改}
\7. border-image
css中设置aligN-item 和align-content 的区别
可以看到与初始状态并没有区别,即在flex容器不设置高度并且子项只有一行时,align-content
属性是不起作用的
多行元素的情况下 当容器的高度不确定时 存在的align-items只是保证每项的元素进行居中
而align-content
是将整体的内容进行紧凑居中如下图
link和import导入css样式的区别
1.从属关系区别
@import
是 CSS 提供的语法规则,只有导入样式表的作用;link
是HTML提供的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel 连接属性等。
2.加载顺序区别
加载页面时,link
标签引入的 CSS 被同时加载;@import
引入的 CSS 将在页面加载完毕后被加载。
3.兼容性区别
@import
是 CSS2.1 才有的语法,故只可在 IE5+ 才能识别;link
标签作为 HTML 元素,不存在兼容性问题。
4.DOM可控性区别
可以通过 JS 操作 DOM ,插入link
标签来改变样式;由于 DOM 方法是基于文档的,无法使用@import
的方式插入样式。
5.权重区别(该项有争议,下文将详解)
link
引入的样式权重大于@import
引入的样式。
计算机网络
http的区别进化
-
http0.9只能进行get请求
-
http1.0添加了POST,HEAD,OPTION,PUT,DELETE等
-
http1.1增加了长连接keep-alive,增加了host域,而且节约带宽
-
http2 多路复用,头部压缩,服务器推送
-
http3在UDP协议之上,新增了QUIC协议。我的理解是由于TCP协议相对于UDP协议控制比较复杂耗时,因此针对HTTP应用贴身开发了QUIC协议代替TCP协议中关于可靠、流量控制的部分。
- QUIC协议特性
- QUIC协议提供类似于HTTP2的流功能
- QUIC协议使用流ID取代IP和端口,这样就能实现连接迁移。例如说从4G信号切换到wifi,下层的IP和端口变了,但是由于QUIC的流ID没有变,这个连接不会变,可以继续使用这个连接。
然后我们看一下HTTP3在QUIC上有什么变化呢?HTTP3由HTTP2进化,HTTP2最大的变化就是基于二进制流的传输。那么到HTTP3,由于QUIC已经管理了流,HTTP3本身就减负了,将流管理下移QUIC,而本身就直接调用QUIC的接口就可以了
- QUIC协议特性
输入url到页面渲染的过程
- dns进行域名解析,将输入的域名翻译为浏览器的ip地址
- 进行三次握手建通信
- 建立链接之后客户端进行数据的请求 发送请求头 请求行 请求体
- 服务器进行处理 可能返回304 也可能是200
- 客户端自上而下执行代码
- 其中遇到CSS加载的时候,CSS不会阻塞DOM树的解析,但是会阻塞DOM树的渲染,并且CSS会阻塞下面的JS的执行 然后是JS加载,JS加载会影响DOM的解析,之所以会影响,是因为JS可能会删除添加节点,如果先解析后加载的话,DOM树还得重新解析,性能比较差。如果不想阻塞DOM树的解析的话,可以给script添加一个defer或者async的标签。 defer:不会阻塞DOM解析,等DOM解析完之后在运行,在DOMContentloaed之前 async: 不会阻塞DOM解析,等该资源下载完成之后立刻运行 进行DOM渲染和Render树渲染 获取html并解析为Dom树 解析css并形成一个cssom(css树) 将cssom和dom合并成渲染树(render树) 进行布局(layout) 进行绘制(painting) 回流重绘 回流必将引起重绘,重绘不一定引起回流
回流和重绘区别
- 回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流 当页面布局和几何属性改变时就需要回流 比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变(涉及到dom元素的更改,内容的变化)
- 添加或删除可见的DOM元素
- 元素的位置发生变化
- 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
- 页面一开始渲染的时候(这肯定避免不了)
- 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
减少回流和重绘的操作
- 使元素脱离文档流(只有第一次操作时候会导致回流 在进行操作时脱离文档流之后已经不会对页面造成回流)
- 对于经常操作的元素 将其隐藏设置为display:none 更改为 visibility=0
- 采用虚拟dom结构 采用单页面
- 脚本加上defer属性 在加载完成之后再进行执行
http缓存
https://www.jianshu.com/p/9c95db596df5
缓存分为强缓存和协商缓存
- 强缓存
在浏览器加载资源时,先看看cache-control里的max-age,判断数据有没有过期,如果没有直接使用该缓存 ,有些用户可能会在没有过期的时候就点了刷新按钮,这个时候浏览器就回去请求服务端,要想避免这样做,可以在cache-control里面加一个immutable. public 允许客户端和虚拟服务器缓存该资源,cache-control中的一个属性 private 只允许客户端缓存该资源 no-cache 不允许强缓存,可以协商缓存 no-store 不允许缓存
- 协商缓存
浏览器加载资源时,没有命中强缓存,这时候就去请求服务器,去请求服务器的时候,会带着两个参数,一个是If-None-Match,也就是响应头中的etag属性,每个文件对应一个etag;另一个参数是If-Modified-Since,也就是响应头中的Last-Modified属性,带着这两个参数去检验缓存是否真的过期,如果没有过期,则服务器会给浏览器返回一个304状态码,表示缓存没有过期,可以使用旧缓存。 etag的作用 有时候编辑了文件,但是没有修改,但是last-modified属性的时间就会改变,导致服务器会重新发送资源,但是etag的出现就完美的避免了这个问题,他是文件的唯一标识
etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT
etag的生成原理
stattafg()对应静态文件 etag生成的方式就是文件的size 和mtime
entitytag ()对应字符串或者buffer etag生成的方式就是字符串(Buffer)的长度加上通过sha1算法生成的hash串
页面攻击
- xss 跨站脚本攻击
不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。 防御 编码:对用户输入的数据进行HTML Entity 编码。把字符转换成 转义字符。Encode的作用是将$var等一些字符进行转化,使得浏览器在最终输出结果上是一样的。 过滤:·
- 移除用户输入的和事件相关的属性。对一些不需要html输入的地方对html标签及一些特殊字符做过滤,将其转化为不被浏览器解析执行的字符
- 对属性中可能存在截断的一些字符进行过滤
popper.w<script src="hack.js" type="text/javajscript"></script>
//这种
<a title="popper.w" onclick="alert(1)">popper.w" onclick="alert(1)</a>
这种包含脚本输入的字符
-
csrf跨域请求伪造 在未退出A网站的前提下访问B,B使用A的cookie去访问服务器 防御:
- token,每次用户提交表单时需要带上token(伪造者访问不到),如果token不合法,则服务器拒绝请求
- 解决的策略 通过refer token或者验证码来检测用户提交
-
sql脚本注入 ' OR '1'='1 攻击者成功的向服务器提交恶意的SQL查询代码,程序在接收后错误的将攻击者的输入作为查询语句的一部分执行,
-
什么是ddos DDOS:分布式拒绝服务攻击(Distributed Denial of Service),简单说就是发送大量请求是使服务器瘫痪。DDos攻击是在DOS攻击基础上的,可以通俗理解,dos是单挑,而ddos是群殴,因为现代技术的发展,dos攻击的杀伤力降低,所以出现了DDOS,攻击者借助公共网络,将大数量的计算机设备联合起来,向一个或多个目标进行攻击。
-
防范dos攻击的方式: 1.增加带宽,堵死了再买
2.CDN,各地部署子服务器,当子服务遭受到攻击时,其他地区的服务器和主服务器不会受到影响。
3.BGP流量清洗,通过BGP将通道内的无用UDP报文清洗干净再转给服务器 https://www.nowcoder.com/discuss/726027?channel=-1&source_id=discuss_terminal_discuss_sim_nctrack&ncTraceId=61f3b439214e46858f0ed601ea051215.154.16338340313052928来源:牛客网
-
防范 : 跨站请求伪造主要在服务器端做
- 进行同源检测,服务器根据 http 请求头中 origin 或者 referer 信息来判断请求是否为允许访问的站点,从而对请求进行过滤。当 origin 或者 referer 信息都不存在的时候,直接阻止请求。这种方式的缺点是有些情况下 referer 可以被伪造,同时还会把搜索引擎的链接也给屏蔽了。所以一般网站会允许搜索引擎的页面请求,但是相应的页面请求这种请求方式也可能被攻击者给利用。(Referer 字段会告诉服务器该网页是从哪个页面链接过来的)
- 使用 CSRF Token 进行验证,服务器向用户返回一个随机数 Token ,当网站再次发起请求时,在请求参数中加入服务器端返回的 token ,然后服务器对这个 token 进行验证。这种方法解决了使用 cookie 单一验证方式时,可能会被冒用的问题,但是这种方法存在一个缺点就是,我们需要给网站中的所有请求都添加上这个 token,操作比较繁琐。还有一个问题是一般不会只有一台网站服务器,如果请求经过负载平衡转移到了其他的服务器,但是这个服务器的 session 中没有保留这个 token 的话,就没有办法验证了。这种情况可以通过改变 token 的构建方式来解决。
- 对 Cookie 进行双重验证,服务器在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串,然后当用户再次向服务器发送请求的时候,从 cookie 中取出这个字符串,添加到 URL 参数中,然后服务器通过对 cookie 中的数据和参数中的数据进行比较,来进行验证。使用这种方式是利用了攻击者只能利用 cookie,但是不能访问获取 cookie 的特点。并且这种方法比 CSRF Token 的方法更加方便,并且不涉及到分布式访问的问题。这种方法的缺点是如果网站存在 XSS 漏洞的,那么这种方式会失效。同时这种方式不能做到子域名的隔离。
- 在设置 cookie 属性的时候设置 Samesite ,限制 cookie 不能作为被第三方使用,从而可以避免被攻击者利用。Samesite 一共有两种模式,一种是严格模式,在严格模式下 cookie 在任何情况下都不可能作为第三方 Cookie 使用,在宽松模式下,cookie 可以被请求是 GET 请求,且会发生页面跳转的请求所使用。
- 加验证码,手机验证码等等
作用域链和作用域
- 规定变量和函数的可使用范围称作作用域
- 每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链
token 登录验证的过程
1、数据库密码字段存入的数据为MD5加密后的数据
2、第一次访问登录页面通过uuid生成一个loginkey并存入session
3、前端提交登录数据的密码构成: MD5(MD5(用户填写的密码)+loginkey) 传入后台
4、后台验证用户存在的情况下取出用户数据库数据进行验证:MD5(数据库密码+session中的loginkey) === 用户提交的密码(步骤三传过来的密码
- 表单验证==》调用分装好的函数==》经过请求拦截器,添加响应头==》代理转发==》得到接口调用之后的结果==》保存token到vuex
- 使用vuex的基本逻辑:数据放在state中,要修改数据则调用mutations
- 先在state中补充定义token
- 同时,要提供对应的用来修改token的mutation,以方便在用户登陆成功之后,去设置token
- 需要的话 token做持久化
- 在对token进行初始化的时候先从本地取一下,优先使用本地取到的值
- 在设置token的时候除了在vuex中存一份,在本地也同步存一份
- 在删除token的时候除了把vuex中的删除掉,把本地的也一并删除
http1.0和http1.1,还有http2有什么区别?
http0.9只能进行get请求 http1.0添加了POST,HEAD,OPTION,PUT,DELETE等 http1.1增加了长连接keep-alive,增加了host域,而且节约带宽 http2 多路复用,头部压缩,服务器推送 ————————————————
xing 性能优化 合并http请求 节约请求响应时间
cpu的位数 与内存大小的关系(位数指的是同一时间内所能同时处理字长)
处理器的位数一般是指在同一时间中处理二进制数的位数,就是字长。通常称处理字长为8位数据的CPU叫8位CPU,32位CPU就是在同一时间内处理字长为32位的二进制数据。而内存是存储设备,一个存储单元可存储一串二进制代码,称这串二进制代码为一个存储字,这串二进制代码的个数叫作存储字长.存储字长可以是8位,16位或32位等.与内存无关的,放心吧。
开发实现移动端的自适应
-
即通过js操纵meta标签中的initial-scale(即网页初始缩放比例)属性即可。 一套css代码即可适应所有屏幕宽度不同的手机;
-
使用固定长度单位px,再使用js根据当前屏幕宽度与固定宽度计算比例,进行网页缩放,来实现移动端屏幕自适应
-
**一、**使用百分比长度单位,当前百分比长度单位一般如下:vw、vh、vm、em、rem、%,**优点:**一套css代码即可适应所有屏幕宽度不同的手机;
-
**vw:**相对于视口的宽度,视口被均分为100单位的vw;即将当前屏幕宽度均分为100份,1vw即为当前屏幕宽度的1%;**优点:**vw单位长度准确分明,常用此单位,推荐使用此单位。
-
**em:**相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于上一个父元素的默认字体尺寸。举例而言,浏览器默认字体大小为16px,在css中设置 于css中进行字体设置譬如:font-size1:4vw;,方可实现屏幕自适应。**基本不用此单位,**它的优点rem都有,但它是优先使用自身的字体大小,其次继承上一个父元素的字体大小
-
**rem:**相对长度单位,相对于根元素(即html元素)内文本的字体尺寸。rem继承且只继承html标签的字体大小。同样的,此单位若要用于屏幕自适应,需与vw配合使用设置根元素的字体大小。必须在css中进行字体设置:
html{font-size:4vw;}
方可实现屏幕自适应。**常用此单位,推荐使用此单位。**看到这里,相信很多朋友都会感到疑惑,明明直接使用vw单位即可实现自适应,为何要使用vw单位进行设置后来使用rem单位呢?原因很简单:便于计算。首先,设计师设计的移动端设计稿通常使用750px宽度(契合iphone678的屏幕宽度),前端工程师通常使用375px的网页宽度(在所有手机屏幕宽度中,它是最方便计算的宽度)。此时,1vw=3.75px;而4vw=15px;8vw=30px;那么请问,哪个单位更便于计算?由于30px与375px相除并非整数,我推荐使用15px(即1rem=4vw)
- em与rem的重要区别: 它们计算的规则一个是依赖父元素另一个是依赖根元素计**算
-
实现页面重定向的方法
-
使用html meta head 进行跳转
-
使用js location.href 属性进行跳转
-
-
location.href = "www.jd.com"
-
-
响应报文头
实现一个div的拖拽效果
var box = document.getElementsByClassName("box")[0]; //获取元素
var x, y; //鼠标相对与div左边,上边的偏移
var isDrop = false; //移动状态的判断鼠标按下才能移动
box.onmousedown = function(e) {
var e = e || window.event; //要用event这个对象来获取鼠标的位置
x = e.clientX - box.offsetLeft;
y = e.clientY - box.offsetTop;
isDrop = true; //设为true表示可以移动
}
document.onmousemove = function(e) {
//是否为可移动状态
if(isDrop) {
var e = e || window.event;
var moveX = e.clientX - x; //得到距离左边移动距离
var moveY = e.clientY - y; //得到距离上边移动距离
//可移动最大距离
var maxX = document.documentElement.clientWidth - box.offsetWidth;
var maxY = document.documentElement.clientHeight - box.offsetHeight;
//范围限定 当移动的距离最小时取最大 移动的距离最大时取最小
//范围限定方法一
/*if(moveX < 0) {
moveX = 0
} else if(moveX > maxX) {
moveX = maxX;
}
if(moveY < 0) {
moveY = 0;
} else if(moveY > maxY) {
moveY = maxY;
} */
//范围限定方法二
moveX=Math.min(maxX, Math.max(0,moveX));
moveY=Math.min(maxY, Math.max(0,moveY));
box.style.left = moveX + "px";
box.style.top = moveY + "px";
} else {
return;
}
}
document.onmouseup = function() {
isDrop = false; //设置为false不可移动
}
jwt基于base64进行加密
-
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密
- 防范csrf攻击 防止token被截获之后进行跨站攻击的不安全性
- 用户使用用户名密码来请求服务器
- 服务器进行验证用户的信息
- 服务器通过验证发送给用户一个token
- 客户端存储token,并在每次请求时附送上这个token值
- 服务端验证token值,并返回数据
一个标准的json webtoken 包含三部分
头部 header 载荷playload 以及signature 签证
这个部分需要base64加密后的header和base64加密后的payload使用
.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分 - 防范csrf攻击 防止token被截获之后进行跨站攻击的不安全性
不需要再服务端进行会话信息的保存 json格式的通用性
token的认证机制
- 第一次登录 用户从浏览器输入用户名,密码,提交到服务器登录处理的action 层
- 登录处理层 调用认证服务进行用户名密码认证 ,认证通过的话 login action 层调用用户信息服务获取用户的信息
- 返回用户的信息之后,login acition 从配置文件中获取token签名生成的密钥信息,进行token的生成
- 生成token的过程中 可以调用第三方的jwt lib生成签名后的jwt数据
- 生成jwt数据签名之后,将其设置到cookie对象中,重定向到首页,完成登录的过程
promise 实现链式调用顺序加载三张图片
function loadImg(url) {
let img = new Image()
img.src = url
return new Promise((resolve, reject) => {
img.onload = () => {
console.log(url)
resolve()
}
img.onerror = (e) => {
reject(e)
}
})
}
loadImg(url1).then(() => {
return loadImg(url2)
}).then(() => {
return loadImg(url3)
})
http 报文格式
request 请求
请求行包括: 请求方法,URL(包括参数信息),协议版本这些信息(GET /admin_ui/rdx/core/images/close.png HTTP/1.1)
请求头部(Header)是一个个的key-value值,比如
Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E) 空行(CR+LF):请求报文用空行表示header和请求数据的分隔
请求数据:GET方法没有携带数据, POST方***携带一个body Response报文
状态行包括:HTTP版本号,状态码和状态值组成。
响应头类似请求头,是一系列key-value值
Cache-Control: private Content-Encoding: gzip Server: BWS/1.1 Set-Cookie: delPer=0; path=/; domain=.baidu.com 空白行:同上,响应报文也用空白行来分隔header和数据
响应体:响应的data,本例中是一段HTML
js 动画和css动画的区别
- 代码的复杂度:js代码的复杂度高于css动画
- js由于代码的复杂度可能会因为线程的阻塞导致丢帧
- 控制:js谢谢的代码控制能力很强, 动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的 css 不能在半路反转动画,不能变换时间尺度,不能在特定的位置添加回调函数或是绑定回放事件,无进度报告
- css流畅度比较高
iframe可以解决加载换缓慢的第三方内容、并行加载脚本
缺点就是会阻塞主页面的onload时间,即使内容为空也需要加载时间
html5新增的语义化标签
section:在 web 页面应用中,该元素也可以用于区域的章节描述。
header:页面主体上的头部, header 元素往往在一对 body 元素中。
footer:页面的底部(页脚),通常会标出网站的相关信息。
nav:专门用于菜单导航、链接导航的元素,是 navigator 的缩写。
article:用于表现一篇文章的主体内容,一般为文字集中显示的区域。
级块性元素主要完成web页面区域的划分,确保内容的有效分割。
aside:用于表达注记、贴士、侧栏、摘要、插入的引用等作为补充主体的内容。
figure:是对多个元素进行组合并展示的元素,通常与 figcaption 联合使用。
code:表示一段代码块。
dialog:用于表达人与人之间的对话,该元素包含 dt 和 dd 这两个组合元素, dt 用于表示说话者,而 dd 用来表示说话内容。
行内语义性元素主要完成web页面具体内容的引用和描述,是丰富内容展示的基础。
meter:表示特定范围内的数值,可用于工资、数量、百分比等。
time:表示时间值。
progress:用来表示进度条,可通过对其 max 、 min 、 step 等属性进行控制,完成对进度的表示和监视。
video:视频元素,用于支持和实现视频文件的直接播放,支持缓冲预载和多种视频媒体格式。
audio:音频元素,用于支持和实现音频文件的直接播放,支持缓冲预载和多种音频媒体格式。
交互性元素主要用于功能性的内容表达,会有一定的内容和数据的关联,是各种事件的基础。
details:用来表示一段具体的内容,但是内容默认可能不显示,通过某种手段(如单击)与 legend 交互才会显示出来。
datagrid:用来控制客户端数据与显示,可以由动态脚本及时更新。
menu:主要用于交互菜单(曾被废弃又被重新启用的元素)。
command:用来处理命令按钮。
iframe 缺点
- iframe会阻塞主页面的onload时间
- 搜索引擎的检索程序无法解读这种页面 不利于seo
svg可任意的缩放图像的显示 但是不会破坏图像的清晰度、细节
文本独立 图像中的文字独立于图像
较小的文件
超强的显示效果、
超级颜色控制
获取url各个部分的方法
- window.location.host; //返回URL 的主机部分(带端口号),www.home.com:8080 window.location.hostname; //返回www.home.com:8080 window.location.href; //返回整个url字符串(在浏览器中就是完整的地址栏):http://www.home.com:8080/windows/location/page.html?ver=1.0&id=timlq#love window.location.pathname; //返回URL 的路径部分(就是文件地址)/windows/location/page.html window.location.protocol; //返回url 的协议部分 http: window.location.port //url 的端口部分,如果采用默认的80端口,那么返回值并不是默认的80而是空字符 :返回 8080
设置是事件冒泡还是事件捕获
- 在addeventListener()的第三个参数(useCapture)设为true,就会在捕获阶段运行,默认是false冒泡
cookie和session实现保存用户登录 状态的机制
cookie(客户端的状态保存机制) 用户登录验证成功后,如果是使用 Cookie 记住登录状态,则客户端会将用户名等信息放在响应头的 Set-Cookie 属性中返回给服务器,之后的 HTTP 请求都会携带这个 Cookie ,实现记住登录。 session(服务端的状态保存机制) 用户登录验证成功后,如果是 session 的话,则服务器会将用户名等信息存放在本地,再随机生成一个登录标识通过 Cookie 返回给浏览器,之后浏览器每次发送请求也会携带这个 Cookie,服务器收到后便通过这个标识得到已登录的用户信息。
什么是服务端渲染(SSR)
什么是服务端渲染(SSR)?
假设有项目需要渲染一个首页,平时我们的项目启动后,开始渲染,请求页面,返回的body为空,然后执行js将html结构注入body中,再结合css来渲染样式,展现出来。
而使用了服务端渲染(SSR)后,简单理解是将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序。渲染时请求页面,返回的body里已经存在服务器生成的html结构,之后只需要结合css显示出来。这就节省了访问时间和优化了资源的请求。
网页做首页优化
- 检查网页是否有title.alt标签 META标签优化:例如:TITLE,KEYWORDS,DESCRIPTION等的优化
- 内部优化 检查网页的keywords 是否有 deno 尽量的关键字价格Storng 标签 每天保持站内的更新(主要是文章的更新等)
- 外部优化每天添加一定数量的外部链接,使关键词排名稳定提升 与一些和你网站相关性比较高,整体质量比较好的网站交换友情链接,巩固稳定关键词排名
https的主要区别**
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
https的优缺点
(1)、使用https协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
(2)、https协议是由SSL+http协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
(3)、https是现行架构下较安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。
(1)、SSL证书需要去,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。
(2)、SSL证书通常需要绑定IP,不能再同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗(SSL有扩展可以部分解决这个问题,但是比较麻烦,而且要求浏览器、操作系统支持,Windows XP就不支持这个扩展,考虑到XP的装机量,这个特性几乎没用)。
(3)、https连接缓存不如http高效,大流量网站如非必要也不会采用,流量成本太高。
(4)、https连接服务器端资源占用高很多,支持访客稍多的网站需要投入更大的成本,如果全部采用https,基于大部分计算资源闲置的假设的VPS的平均成本会上去。
(5)、https协议握手阶段比较费时,对网站的响应速度有负面影响,如非必要,没有理由牺牲用户体验。
图像可以进行压缩的原理
- 空间冗余 上层图片和下层图片颜色一样的地方存在空间冗余
- 时间冗余 针对视频而言
- 视觉冗余 视觉的一般分辨能力为26灰度等级,而一般的图像的量化采用的是28灰度等级,即存在视觉冗余。
前后端分离之后保护用户登录信息的安全
- 使用router中的路由狗子函数,因为用户的信息是保存在webstorage中的或者是cookie中 可以在前端钩子函数中判断 localStorage.token) 中的token是否存在
(路由中设置的是否需要登录)
meta:{
requireAuth: true,
},
-
如果第一步中的token是存在的 拿到这个token 调用后台的接口去校验token是否有效,无效next()跳转到登录的页面
-
为所有的请求添加一个请求token头
Vue.http.interceptors.push((request, next) => { //为所有请求添加token头 if(localStorage.token){ request.headers.set('Authorization', "Bearer "+localStorage.token); } //后端响应回来,首先进入这个next的回调函数中 next((response) => { //如果后端返回401,则认为是token验证失败,跳转到登录页面 if(response.status == 401){ router.push("/login") } return response }); });
-
使用JWT保证用户信息的安全
-
利用https 的ssl加密传输协议进行用户信息的传递到后端 - ---- 后端使用base64编码形成一个JWT---前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。
前端性能优化的方式
1从这个例子可以看出,真正下载数据的时间占比为 13.05 / 204.16 = 6.39%,文件越小,这个比例越小,文件越大,比例就越高。这就是为什么要建议将多个小文件合并为一个大文件,从而减少 HTTP 请求次数的原因
2.使用 HTTP2
http2相对http1而言
解析速度块、多路复用
使用字体图标代替图片图标
使用服务端渲染
\3. 将 CSS 放在文件头部,JavaScript 文件放在底部
所有放在 head 标签里的 CSS 和 JS 文件都会堵塞渲染。如果这些 CSS 和 JS 需要加载和解析很久的话,那么页面就空白了。所以 JS 文件要放在底部,等 HTML 解析完了再加载 JS 文件。那为什么 CSS 文件还要放在头部呢?因为先加载 HTML 再加载 CSS,会让用户第一时间看到的页面是没有样式的、“丑陋”的,为了避免这种情况发生,就要将 CSS 文件放在头部了。另外,JS 文件也不是不可以放在头部,只要给 script 标签加上 defer 属性就可以了,异步下载,延迟执行。
4.善用缓存,不重复加载相同的资源
5.压缩文件
6.图片延迟加载 使用webp格式的图片
7.减少重绘重排
8.通过 webpack 按需加载代码,提取第三库代码
9.if-else 对比 switch
10.不要覆盖原生方法
11.使用 flexbox 而不是较早的布局模型
vue前端实现手机扫码登录的操作
-
使用后端生成二维码
-
使用前端生成二维码
self_redirect: true:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false。
id:为二维码的容器id
appid:后台配置好的appid 问后台拿!
scope:网页应用目前仅填写snsapi_login即可
redirect_url:扫码成功后的回调地址,由于本项目需要多个地方进行登录操作,所以需要模板字符串拼接回调。
state:为了防止csrf(跨站请求伪造攻击),设置一个3位的随机数 ———————————————— 版权声明:本文为CSDN博主「你看人家超」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_45018314/article/details/118835160
数据库索引优化方法
- 最左前缀匹配原则,上面讲到了
- 主键外检一定要建索引
- 对 where,on,group by,order by 中出现的列使用索引
- 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0
- 对较小的数据列使用索引,这样会使索引文件更小,同时内存中也可以装载更多的索引键
- 索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
- 为较长的字符串使用前缀索引
- 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
- 不要过多创建索引, 权衡索引个数与DML之间关系,DML也就是插入、删除数据操作。这里需要权衡一个问题,建立索引的目的是为了提高查询效率的,但建立的索引过多,会影响插入、删除数据的速度,因为我们修改的表数据,索引也需要进行调整重建
- 对于like查询,”%”不要放在前面。
SELECT * FROM
houdunwangWHERE
unameLIKE'后盾%' -- 走索引
SELECT * FROM
houdunwangWHERE
unameLIKE "%后盾%" -- 不走索引
- 查询where条件数据类型不匹配也无法使用索引
字符串与数字比较不使用索引;
CREATE TABLE
a(
achar(10));
EXPLAIN SELECT * FROM
aWHERE
a="1"
– 走索引 EXPLAIN SELECT * FROMa
WHEREa
=1 – 不走索引 正则表达式不使用索引,这应该很好理解,所以为什么在SQL中很难看到regexp关键字的原因
数据库分页的相关操作
-
直接使用数据库提供的sql语句
SELECT * FROM 表名称 LIMIT M,N
-
建立主键或唯一索引,利用索引
SELECT * FROM 表名称 WHERE id_pk > (pageNum*10) LIMIT M。
-
基于索引再排序
SELECT * FROM 表名称 WHERE id_pk > (pageNum*10) ORDER BY id_pk ASC LIMIT M
-
: 基于索引使用prepare
PREPARE stmt_name FROM SELECT * FROM 表名称 WHERE id_pk > (?* ?) ORDER BY id_pk ASC LIMIT M。
-
利用MySQL支持ORDER操作可以利用索引快速定位部分元组,避免全表扫描
SELECT * FROM your_table WHERE pk>=1000 ORDER BY pk ASC LIMIT 0,20。
数据库事务的特性:脏读、幻读、不可重复读**
事务特性有:原子性、一致性、持久性、隔离性
脏读:就是没有提交的数据,举个例子:比如某个事务在对数据库中某条记录进行修改,修改后没有提交也没有回滚,也就是让其处于一个待定的状态,这个时候如果有其他的事务来先一步对这条记录进行读取或者处理了的现象。
不可重复读取:一个事务先后读取某条记录,但在两次读取之间该条记录杯其他另一个事务给修改了,就造成了两次读取的数据不同的现象。
幻读:幻读就是一个事务按照查询条件查询以前检索过的数据可是发现该数据被其他事务插入了满足其查询条件的新数据的现象。
不可重复读喝脏读的区别是一个是读取了前一事务提交的数据,而一个是读取了另一个事务未提交的数据
递归和动态规划的区别
递归:自上至下,把大问题分解成小问题解决。
动态规划:自下至上,通过解决小问题,集合为解决大问题。
用递归能解决的问题,一般都可以用动态规划来解决。
区别:
递归: 缺点:会重复计算相同的问题,相当耗时。 优点:不会记录每个问题的结果,所以内存消耗相对小。
动态规划:缺点:会记录每一个问题的结果,内存消耗较大。 优点:不会计算相同问题,时间消耗较小。
预加载 prefetch
**有了浏览器缓存,为何还需要预加载? **1.用户可能是第一次访问网站,此时还无缓存 2.用户可能清空了缓存 3.缓存可能已经过期,资源将重新加载 4.用户访问的缓存文件可能不是最新的,需要重新加载 5.Chrome 的预加载技术
prefetch和preload的区别 prefetch通常是告诉浏览器 prefetch将加速下一次的导航
preload通常用于本页面要用到的关键资源,包括关键js、字体、css文件。preload将会把资源得下载顺序权重提高,使得关键数据提前下载好,优化页面打开速
一个完整的git提交流程
- fork到自己的仓库
- git clone 将代码从远程仓库copy下来
- git remote add upstream ssh://主仓库地址 与远程的仓库建立关系
- git branch查看本地仓库和远程仓库的分支 (git status)
- git chekout -b -check建立新的分支 git checkout develop 切换分支
-
- 这一步可以进行替换 git rabase -i将多次的提交合并为一次的提交
- git rabase master 将主仓库的代码同步到本地
- git pull拉取主仓库的代码
- git diff比较 git add 全选 git commit -m "feat:add UserManagement" 暂存到暂存区
- git push提交到主仓库
多台服务器配置负载均衡
-
配置nginx让他实现负载均衡
upstream ruoyi{ server 192.168.109.102:8080 weight=5; server 192.168.109.103:8080 weight=3; }
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /workspace/dist;
index index.html index.htm;
}
location /prod-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ruoyi/;
}
#### js桥接
#### bable原理
parse transform generator
编译 转化 生成
#### 常用的plugin和loader
- loader
style-loader & css-loader
less-loader
postcss-loader
file-loader & url-loader
babel-loader & @babel/preset-env & @babel/core
babel-polyfill
- plugin
html-webpack-plugin
clean-webpack-plugin
mini-css-extract-plugin
extract-text-webpack-plugin
webpack.HotModuleReplacementPlugin
copy-webpack-plugin
optimizi-css-assets-webpack-plugin
uglifyjs-webpack-plugin
happypack
webpack-parallel-uglify-plugin
DllPlugin & DllreferencePlugin
————————————————。
原文链接:https://blog.csdn.net/MoLvSHan/article/details/105702643
```js
// webpack.config.js
module.exports = {
// ...省略其他配置
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader'] // 从右向左解析原则
}
]
}
}
正向代理和反向代理的含义
- 正向代理类似一个跳板机 代理访问外部资源
- 比如请求google 国内直接访问是访问不到的 可以通过设置一个正向代理服务器 请求发到代理服
- 正向搭理的用途
- 访问原先无法访问的资源
- 可以做缓存 加速资源的访问
- 对客户端访问授权 上网进行认证 记录用户访问记录 对外隐藏用户信息
- 反向代理 设置代理服务器来接受internet上的链接请求 代理服务器对外表现为一个服务器
- 保证内网的完全 组织web攻击 大型网站中 反向代理作为公网访问地址
vue创建一个组件的流程
-
创建一个组件
-
使用vue.extend创建一个组件
-
使用template-id=“” 标签创建,需要加上id属性
注册一个组件
- 全局注册 ```js Vue.component('my-com',myCom)
- 局部注册
调用一个组件
- 直接在使用的地方写上组件的标签即可
实现百度的输入抖一抖摇摆的效果
-
可以直接设置一个函数 当文本框输入的值为抖一抖时 触发抖动函数
-
或者直接使用媒体查询的方式
-
@-webkit-keyframes pulse { 0% { transform: scale(1); } 40% { transform: scale(1.02); } 60% { transform: scale(1.02); } 100% { transform: scale(1); } } .overlay .page.pulse { -webkit-animation-duration: 180ms; -webkit-animation-iteration-count: 1; -webkit-animation-name: pulse; -webkit-animation-timing-function: ease-in-out; }
transition 和animation的区别
transtion:
transition所有的运行都需要条件进行触发。比如:hover、:focus、:checked或者js操作css样式的变更等等。
- transition需要事件触发,所以没法在网页加载时自动发生。
- transition是一次性的,不能重复发生,除非一再触发。
- transition只能定义开始状态和结束状态,不能定义中间状态,也就是说只有两个状态。
- 一条transition规则,只能定义一个属性的变化,不能涉及多个属性。
animation:
不需要事件触发,也可以自动执行
transtion-property: 规定设置过渡属性的css属性 transtion-duration:延迟时间,指定从一个属性到另一个属性过渡所要花费的时间 transiton-timing-function:过渡函数 transition-delay:延迟时间
animation-name 规定需要绑定到选择器的 keyframe 名称。。 animation-duration 规定完成动画所花费的时间,以秒或毫秒计。 animation-timing-function 规定动画的速度曲线。 animation-delay 规定在动画开始之前的延迟。 animation-iteration-count 规定动画应该播放的次数。 animation-direction 规定是否应该轮流反向播放动画。 js的隐式类转换
[]+[]=""
[]+{}="[object Object]"
{}+[]=0 //在解析的时候将{}设置魏代码语法块自动给跳过了, 然后
将字符串数组转为真正的数组
//可以直接使用JSON.parse()方法
为什么移动端的事件会有300ms的延迟
- 移动端的浏览器中存在默认双击的事件 当双击时会自动的将浏览器的页面缩放到原始的比例
- 当网页中存在一个链接时 点击的话无法确定是点击了链接还是要进行双击事件的点击
算法 要求实现对数组进行排序 并且奇数在前,偶数在后
function mysort(arr){ let ji=[] let ou=[] for(let i of arr){ if(i%2==0){ ou.push(i) }else{ ji.push(i) } } ou.sort((a,b)=>{return b-a}) ji.sort((a,b)=>{return a-b}) return [].concat(ji,ou) }
keepAlive 的生命周期
- activated: 页面第一次进入的时候,钩子触发的顺序是created->mounted->activated
- deactivated: 页面退出的时候会触发deactivated,当再次前进或者后退的时候只触发activated
get 和post的区别
幂等幂不等:再重复的请求下返回的值是否一致。
- get请求是幂等的,所以get请求到的数据是可以缓存的
- 传参,一个参数的传递是放在url中,一个放在body中,相对是比较安全的
- 想要做到安全,用https
- get传递参数长度是受限的,post传递的参数长度不受限制
- post传参发送的是两个请求包,一个是请求头部,一个是请求体,头部验证成功之后才会发送请求体
- get 的字符编码只允许ASCII编码
js中的for循环定时器
实现定时器内能够循环打印12345
//可以将for训话放入到定时器之内 setTimeout(()=>{ for(var i = 0; i < 5; i++) { console.log(i) } },50) console.log('哦吼~') //将settimeout实现匿名自执行函数中、 for(var i=0;i<5;i++){ ((i)=>{ setTimeout(()=>{console.log(i)},500) })(i) } //使用es6中的let
websoket
-