React 和 Vue 有许多相似之处,它们都有使用 Virtual DOM;
提供了响应式(Reactive)和组件化(Composable)的视图组件。
将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。
React 比 Vue 有更丰富的生态系统
都有支持native的方案,React的RN,vue的Wee下
都支持SSR服务端渲染
都支持props进行父子组件间的通信
性能方面:React 和 Vue 在大部分常见场景下都能提供近似的性能。通常 Vue 会有少量优势,因为 Vue 的 Virtual DOM 实现相对更为轻量一些。
不同之处就是:
1. 数据绑定方面,vue实现了数据的双向数据绑定,react数据流动是单向的
2.virtual DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树.
而对于React而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react中会需要shouldComponentUpdate这个生命周期函数方法来进行控制
3.state对象在react应用中不可变的,需要使用setState方法更新状态;
在vue中,state对象不是必须的,数据由data属性在vue对象中管理(如果要 *** 作直接this.xxx)
4.组件写法不一样, React推荐的做法是 JSX , 也就是把HTML和CSS全都写进JavaScript了,即'all in js'; Vue推荐的做法是webpack+vue-loader的单文件组件格式,即html,css,js写在同一个文件
单页应用
单页面应用(SPA--------single page application),一个web项目只有一个页面(即一个HTML文件)刷新页面会请求一个HTML文件,切换页面的时候,并不会发起新的请求一个HTML文件,只是页面内容发生了变化。
vue.js原理:JS感知URL变化,当URL发生变化后,使用JS动态把当前的页面内容清除掉,再把下一个页面的内容挂载到页面上。此时的路由就不是后端来做了,而是前端来做,判断页面到底显示哪一个组件,再把以前的组件清除掉使用新的组件。就不会每一次跳转都请求HTML文件。
单页应用的优点:
1.分离了前后端的关注点,前端负责页面显示,后端负责数据储存和计算减轻了服务器的压力,不会让前后端的逻辑混淆
2.用户体验好,快,内容的改变不需要重新去加载
3.前端组件化,前端的开发不再以页面为单位,而是采用组件化的思想让代码的结构更加规范,有利于修改
单页应用的缺点:
1.首次加载耗时久,需要加载大量资源
2.不能使用浏览器前进后退的功能
3.对搜索引擎SEO不友好
解决方式:
1.按需加载,避免首屏加载过慢
2.配置好路由信息,通过记录浏览过的历史路由信息解决导航不可以问题
3.用#!代替#,因为谷歌会抓取带有#!的URL。我们可以解决ajax的不被搜索引擎抓取的问题
computed 、 methods 、 watch
computed 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值
watch: 适用于一条数据影响多个数据的场景;例如搜索
watch更多的是观察的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续 *** 作
methods: 函数调用,每使用一次,都需要重新加载,不需要缓存时用methods;性能开销较大,
总结
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用computed 的缓存特性,避免每次获取值时,都要重新计算;
-当我们需要在数据变化时执行异步或开销较大的 *** 作时,应该使用 watch,使用 watch 选项允许我们执行异步 *** 作 ( 访问一个 API ),限制我们执行该 *** 作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的
Vue为什么是异步渲染?
Vue是组件级的更新,一个组件可能涉及的数据非常的多,每次更新都会非常损耗性能,为了节约性能,Vue在更新DOM时是异步执行的。
只要监听到数据的变化,Vue就会开启一个队列,把同一事件循环中发生的所有数据变更都推入这个队列,如果同一个数据变更被多次触发,它也只会被推入队列中一次,这种缓冲行为可以去除重复数据的不必要计算和 DOM *** 作。
为什么要使用nextTick 因为Vue是异步渲染的,所以当我们改变组件的 data 时,该组件不会立即重新渲染,组件会在下一个事件循环 "tick" 中更新。 为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 nextTick,该方法的 callback 会在 DOM 更新完成后被调用。
v-show 和 v-if
v-show的元素总是会被渲染,对于不显示的元素 也只是在行内设置了display:none的样式
v-if 是真正的条件渲染,他不会去渲染不显示的元素,除非条件为真。
一般情况下v-if的切换开销比较高,而v-show的初始渲染开销比较高
如果一个元素会频繁切换隐藏或者显示的时候 使用v-show,反之如果元素在运行时条件改变较少,就用v-ifv-if 和 v-for 的优先级 v-for的优先级高于 v-if 在渲染时,会先去循环渲染出列表,然后再去看列表item上的v-if 的情况; 如果两者同时出现,每次渲染的时候它会去遍历整个列表,造成性能的浪费 1.为了避免这个情况就需要把循环执行的变量替换为一个计算属性;然后让他返回过滤后的列表;把需要渲染的留下,不需要渲染的去掉 从而 v-if=""就不需要,只遍历了自己需要渲染的 2.可以把V-if 移动到父元素 或者把列表经过过滤后再循环
Vue的生命周期 Vue实例从创建到销毁的过程就是生命周期;它总共分为8个阶段
beforeCreate | 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前 被调用。 在这个事件中我们 获取不到data数据 data,watcher,methods都不存在这个阶段;只有$route这个对象 因此此阶段就可以根据路由信息进行重定向等 *** 作。 常用于初始化非响应式的变量 |
created | 在实例创建完成后触发, 可以访问data、methods等属性, 但是组件还未被挂载到页面 ,所以不能访问$el,且页面视图未出现,如果请求过多页 面会长时间处于白屏状态; 我们一般在这个函数中进行页面初始化的工作, 例如ajax请求数据来对页面进行初始化 |
beforeMount | 在组件被挂载到页面之前调用,会找到对应template, 并翻译成render函数 |
mounted | 在组件被挂载到页面之后触发,可以通过DOM API来获取页面中的DOM元素; el 被新创建的 vm.$el 替换,并挂载到实例上去,可以进行dom *** 作 |
beforeUpdate | 在响应式数据更新时调用,发生在虚拟 DOM 打补丁之前, 此时我们可以对一些可能被移除的元素做一些 *** 作,例如 移除事件监听器 |
updated | 组件数据更新之后,发生虚拟DOM重新渲染和打补丁之 后进行调用 避免在这个钩子函数中 *** 作数据,可能陷入死循环 |
beforeDestory | 组件销毁前调用 做一些优化 *** 作,一般在这一步 我们去销毁定时器 解绑全局组件 |
destoryed | 组件销毁后调用,Vue实例中所有东西都会解除绑定 事件监听器会被移除,所有子实例会被销毁 |
activited | keep-alive专属,组件被激活时调用 |
deactivated | 在被包裹组件停止使用时调用 |
路由的导航守卫
VueX
VueX是一个专门为vue.js 程序开发的状态管理模式 它采用集中式 储存管理 所有引用的组件的状态
VueX的作用 在项目中可以用vueX来存放数据,可以实现多组件、多页面间的数据共享;解决了组件之间统一状态的共 享问题,实现了组件之间的数据持久化。 什么情况下去使用VueX 我们一般会在嵌套较为复杂的组件或者组件之间传参,参数可能在多个地方被改变的情况下去使用VueX,这样我们能够保证数据的单向性,避免数据流混乱,增加代码的可维护性。 如果应用足够简单,就不要去使用vuex,但如果要去构建中大型的单页面应用,想要更好的管理组件外部状 态就去使用Vuex 不使用Vuex的坏处 代码可维护性下降 代码可读性下降 耦合度增加 VueX的属性 state:Vuex是一个用于存放很多对象的仓库,state就是数据的存放地,对应Vue对象里的data,state里存放的数据是响应式的 getters:用于获得需要计算state后得到的值,实现简单的数据转换;接收state作为第一个参数,可以在多个组件之间复用 mutations:更改state的值的方法集合 actions:通常在这发送异步请求;通过调用mutations修改state的值,类似mutations但是不是直接改变状态;利用 context.commit 提交一个 mutation,modules:当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、避免冲突以便维护 action和mutaion的区别 ? mutation 是同步更新, $watch 严格模式下会报错,mutation主要是用于修改state的值的方法的集合,可以通过commit调用 action 是异步 *** 作,使用dispatch调用,之后可以获取数据后调用 mutation 提交最终数据
JS JS的基本数据类型
js有六种基本数据类型 :null undefined number string boolean symbol
JS复杂数据类型Object:Array,Function、RegExp、Date
总结
基本数据类型和复杂数据类型它们两者的不同主要是在存储的位置不一样。
基本数据类型存储在栈(stack)中,复杂数据类型存储在堆(heap)里,基本数据类型占据空间小,大小固定 且使用频繁; 复杂数据类型一般都是Obj类型,所有Array、Date等数据类型都可以理解为Obj的子类; 复杂数据类型 占据空间大,大小不固定,如果存储在栈中会影响程序性能;所以对于引用数据类型,栈中会存放他们的指针, 根据指针的地址指向堆中的实体; null 和 undefinedundefined 代表不存在这个值,他是一个变量最原始的状态
null 代表存在这个值 ,但是这个值是空的
什么是DOM 、BOM DOM( Document Object Model)(文档对象模型),把文档当做一个对象;DOM的最根本对象是document(window.docum ent)这个对象主要定义了处理网页内容的接口和方法 Bom(browser object model) 浏览器对象模型,把浏览器当做一个对象,BOM的最核心对象是window对象,这个对象既是js访问浏览 器的一个接口,又是一个全局对象,这意味着网页中定义的任意对象、方法和属性是全局对象的一个方法或 者属性;Window对象里有location、screen对象的子对象;且DOM的document对象也是BOM window对象的子对象;
闭包
什么是闭包?
一个可以访问其他函数内部变量的函数就是闭包,最常见创建闭包的方法就是在一个函数内部创建另一个函数
闭包的作用?
通过使用闭包我们可以从外部访问函数内部的变量,可以让已经运行结束的函数上下文中的变量继续存在于内存,让它不会被回收 闭包的缺点? 内存不能被释放Promise
什么是promise ?
promise是ES6提出的异步编程解决方案,相对于传统容易陷入回调地狱的异步回调方案来说,promise会让异步 *** 作更加的优雅;
首先Promise他是一个构造函数 需要通过new关键字来生成一个Promise实例对象
他接收一个函数作为参数,函数中的代码会在new Promise的时候立刻执行,作为参数的函数有两个默认的参数 resolve 和 reject,这两个参数是函数标记异步执行的状态
当异步 *** 作完成 我们可以调用resolve函数
当异步 *** 作失败 我们可以调用reject函数
这些标记的状态可以在then和catch中接收;.then代表异步完成的回调,.catch是异步失败的回调
1. Promise他本身是同步执行的,
输出结果为: 1 ,2 (如果是异步,输出结果应该为 2 , 1)
let a = new Promise( (res, rej) => {
console.log(1);
});
console.log(2);
2. promise的回调 .then 和.catch 是异步的
输出结果为 1,2, 3 因为是异步 所以先打印2
let a = new Promise((res, rej) => {
console.log(1);
res(3)
});
//异步
a.then((res) => {
console.log(res);
});
console.log(2);
promise的原理
//Promise内部有一个状态管理器的存在;一个promise实例有三种状态;pending,fulfilded,rejected;分别代表进行中、已成功以及以失败;
(1)promise的初始化状态是pending
(2)当调用成功resolve 会由pending变成fulfiled
(3)当调用失败会由pending => rejected
如何让promise 停止?
工作中什么时候会用到promise?
原型和原型链 1.原型
每个构造函数都有一个prototype属性,这个属性指向一个对象(prototype/实例原型/冰条模具),这个对象内包括该构造函数所有公用的方法和属性,这个对象就是调用该构造函数创建的实例原型,每个原型对象都有一个constructor属性,这个属性会指向关联的构造函数。
通过构造函数实例化出来的实例对象有一个_proto_ 可以访问实例原型,实例对象在创建的时候会默认关联原型,并从原型上继承属性
2.原型链当我们想去访问实例对象的属性时,实例对象却没有这个属性,JS只能去该实例对象的实例原型上寻找该属性,如果实例原型上依旧找不到,就会去原型的原型上找,直到找到Object为止,这个行为将形成一个链条,该链条就叫做原型链。
bind/call/apply
call和apply方法都是为了改变this的指向,作用相同,传参的方式不同;
call可以接受一个参数列表,而apply只能接受一个参数数组
bind也接收一个参数列表,和call以及apply一样 它的作用是为了改变this的指向,只是该方法会返回一个函数;通过bind实现柯里化
Event Loop
js最大的特点就是单线程 ,在同一时间就只能做同一件事,
这也意味着 任务是需要排队的。只有前一个任务完成了,才能去执行下一个任务。
所以任务也被分为了两种, 同步任务和异步任务(宏任务) /(微任务)
同步任务是在主线程执行,形成一个执行栈,只有前一个任务完成了,才能去执行下一个任务。
而异步任务不进入主线程,而是先在任务队列中放置
只有执行栈中所有同步任务被执行,才会去读取任务队列,把异步任务拉入执行栈执行
这个过程被不断的重复
在清空了调用栈,也就是所有的同步任务后, 首先会先去执行微任务队列的任务; 微任务执行完才会去执行宏任务; 宏任务(消息队列): 分类:setTimeout setInterval requrestAnimationFrame 事件的回调函数,例如onclike 1.第一个宏任务队列只有一个任务:执行主线程的js代码 2.宏任务队列可以有多个 微任务: 分类:new Promise()、.then(回调)、 process.nextTick 、.finally( )、 . catch() 、 MutationObserver Object.observe t 1.只有一个微任务队列 2.在上一个宏任务队列执行完毕后,如果有微任务队列就会执行微任务队列 把回调函数放进evenq(事件队列)然后根据轮询 放入主线程指向 流程 1.主线程执行完毕;调用栈被清空, 2.事件循环优先去寻找微任务队列中的任务; 3.直到微任务队列中的任务被清空才会去执行下一轮宏任务get和post的区别
1.在应用场景上, get是一个幂等的请求 ,一般用于不会对服务器造成影响的场景,例如请求一个页面;
post是一个非幂等的请求,一般用于会对服务器造成影响的场景;例如注册账号;所以浏览器一般会对get请求缓存,而很少对post请求进行缓存
2.从发送报文的格式来说,Get请求中实体部分为空,而post请求中,报文实体部分一般是向服务器发送的数据;
3.GET请求也可以将参数放在URL中向服务器发送,但是这种方法不是很安全,因为请求的URL会保存
在历史记录中,且浏览器对URL会有长度上的限制;会影响GET请求发送数据的长度;相比之下POST请求更加安全且参数的传递支持更多的数据类型;
深拷贝和浅拷贝
浅拷贝:对于基本数据类型 直接将值赋值给新对象,对于引用数据类型,只复制内存地址而非对象本身,新旧对象共享同一内存,改变其中一个对另一个也有影响
深拷贝:对于基本数据类型 直接将值赋值给新对象,对于引用数据类型,深拷贝会新建一个对象空间 然后拷贝里面的内容,他们指向不同的内存空间 改变其中一个对另一个没有影响
如何实现浅拷贝:
//对象浅拷贝
1. let obj2 = Object. assign ({}, obj ) 2. let obj2 = { ... obj } //数组浅拷贝 let arr = [ 1 , 2 , { a : 3 }] 1. let arr2 = arr . slice () 2. let arr2 = arr . concat () 3. let arr2 = [ ... arr ]如何实现深拷贝
1. 递归去复制所有层级的属性2. JSON对象的parse和stringify
function deepClone ( obj ){
let _obj = JSON.stringify ( obj ),
objClone = JSON.parse ( _obj );
return objClone
}
let a = [ 0 , 1 ,[ 2 , 3 ], 4 ],
b = deepClone ( a );
a [ 0 ] = 1 ;
a [ 2 ][ 0 ] = 1 ;
console . log ( a , b );
手写函数
要求手写:转换驼峰的函数、冒泡排序、翻转数组、数组去重、求数组最大值和最小值、forEach、map、filter、reduce的实现、伪数组转换真数组、函数节流和函数防抖、数组扁平化处理
节流函数和防抖函数1.节流函数:一段时间内只执行一次某个 *** 作,过了这段时间 如果还有 *** 作再继续执行新的 *** 作
2.防抖函数:一段时间内只执行最后一次 *** 作,
总结:防抖函数先清除定时器,再调用回调函数,节流函数先调用回调函数,再清除定时器
防抖函数
function debounce(fn, delay) {
// 记录上一次定时器
let timer = null
return () => {
// 每次点击都清除上一个定时器 保证触发的是最后一次
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this)
}, delay)
}
}
let btn = document.querySelector('.btn')
console.log(btn);
btn.onclick = debounce(() => {
console.log('debounce',);
}, 200)
节流函数
const box = document.querySelector('.box')
box.addEventListener('drag', throttle(() => {
console.log('test');
},2000))
function throttle(fn, delay) {
let timer = null
return () => {
//如果有定时器 就直接返回
if (timer) return
timer = setTimeout(() => {
fn()
timer = null
}, delay)
}
}
let btn = document.querySelector('.btn')
btn.addEventListener('click', throttle1(function () {
console.log(111);
}, 2000))
function throttle1(fn, delay) {
// 记录上一次触发的时间
let lastTime = 0
return function () {
let nowTime = Date.now()
if (nowTime - lastTime > delay) {
// 如果现在时间-最后触发时间大于 规定触发时间delay,就调用需要被执行的函数;并且同步一下最后一次调用的时间
fn.call(this)
// 这里要call回自己否则可能出现this指向的问题
lastTime = nowTime
}
}
}
转换驼峰的函数
let word = 'alice-love-u'
convertCamelCase('-', word)
function convertCamelCase(symbol, str) {
//根据- 来切割字符串 -> [alice,love,u]
let arr = str.split(symbol)
arr.map((item, index) => {
// 如果是第一个就reurn
if(index == 0) return
// 截取item的首字母 转换为大写 + 去除第一个字母的item
// alice -> A + lice
arr[index] = item.charAt(0).toUpperCase() + item.slice(1)
})
// 把[alice,Love,U] -> aliceLoveU
let newStr = arr.join('')
return newStr
}
HTML
回流和重绘
1.介绍一下回流和重绘
浏览器采用流式布局模型,浏览器会将HTML解析成DOM把CSS解析成CSSOM ,然后把CSSOM和DOM结合生成一个render tree
根据render tree我们能够知道节点的样式 浏览器会从根节点递归调用去计算每个节点的位置,然后把节点绘制到页面上
重绘的概念当渲染树中的一些元素属性需要更新,但是这些属性只涉及和影响了元素的外观以及风格并不影响布局的情况下,我们称之为重绘
回流的概念 当渲染树中的一部分或者全部元素的结构或者尺寸、布局、隐藏等发生改变需要重新建构,会 影响到布局,我们成为回流重绘不一定引起回流 但是回流一定会引起重绘
性能上 重绘的性能消耗小于回流
2.常见的引起回流的 *** 作
(1)添加或者删除可见的 DOM 元素; (2)元素尺寸改变——边距、填充、边框、宽度和高度 (3)内容变化,比如用户在 input 框中输入文字 (4)浏览器窗口尺寸改变——resize 事件发生时 (5)计算 offsetWidth 和 offsetHeight 属性 clientTop等 (6)设置 style 属性的值 (7)当你修改网页的默认字体时3.如何减少回流?
1.使用documentFragment的方式来 *** 作DOM
正确写法:先加入文档碎片 再一次性添加
错误写法:一条一条加
2.用transform代替 top
3.不要使用table布局
4.预先定义好class 直接修改className 而不是一条一条的去修改DOM样式
5.避免多层内联样式
6.将需要多次重排的元素 position属性设置为absolute 或fixed,脱离文档流
localStorage & sessionStorage & cookies cookies ;sessionStorage 和localStorage的区别(考察本地存储方式是否熟悉) sessionStorage, localStorage, Cookie 这三者都可以被用来在浏览器端存储数据,而且都是字符串类型的键值对。 1.cookies 的大小大概在4KB左右 存储的数据一般都是用来标记用户信息的加密字符串,且只在同源页面共享(域名、端口号、协议号相同),生命周期通过 expires设置 如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消息。 如果不在浏览器中设置过期时间,这种cookie简称会话cookie。 2.sessionStorage 是浏览器本地存储的一种方式,一般纯存储5M或更大的数据,;页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话;存储的数据会在浏览器关闭之后自动删除 3.localStorage 是浏览器本地存储的一种方式,存储的数据将保存在浏览器会话中,他储存的是一个持久化的数据;不会主动 删除;数据会一直存在,除非手动删除localStorage
只要在相同的协议、相同的主机名、相同的端口下,就能读取/修改到同一份localStorage数据。
sessionStorage
比localStorage
更严苛一点,除了协议、主机名、端口外,还要求在同一窗口(也就是浏览器的标签页)下
什么是跨域? 1.什么是跨域 一个项目中多台服务器提供不同功能,服务器的域名地址不同,就会产生跨域的问题;跨域主要是因为浏览器 要遵守同源政策,就算请求成功了,也会被拦截 2.什么是同源政策 同源策略:协议名、域名、端口号必须一致 是浏览器的安全策略,它限制了同一个源加载的文档或者脚本如何与另一源的资源进行交互,这是一个用于 隔离潜在恶意文件的重要安全机制; 违背同源策略就会产生跨域; 3.为什么要有同源策略 如果没有同源策略,浏览器很容易受到XSS、CSFR等攻击 ( 1 )XSS攻击 · 即Cross Site Script 跨站脚本攻击 · HttpOnly防止截取Cookie · 用户的输入检查 · 服务端的输出检查 ( 2 ) CSFR攻击 · 跨站请求伪造 是一种劫持受信任用户向服务器发送非预期请求的攻击方式 · 验证码 ·Referer Check ·Token 验证
4.跨域的解决方案
jsonP、 cors服务器代理
核心是动态添加script标签调用服务器提供的JS脚本,允许用户传递一个callback参数给服务器,服务器在返回数据会将这个callback参数作为函数名来包裹JSON数据,这样客户端就可以随意定制自己的 函数来自动处理返回数据(仅支持GET方法)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)