h5语意化标签
<nav><header><footer><article><section>
<nav><header><footer><article><section>
优点:
为了在没有CSS的情况下,页面也能呈现出很好地内容结构、代码结构比<div>标签有更加丰富的含义,方便开发与维护
解决布局问题
一般使用element中的layout布局实现,将页面实现分为24等分
还可以使用flex 布局 两栏布局---三栏布局---定位布局-浮动布局等
ajax和axios fetch 的区别
1、axios是一个基于Promise的HTTP库,而ajax是对原生XHR的封装;
2、ajax技术实现了局部数据的刷新,而axios实现了对ajax的封装。
同步与异步的区别:
同步的理解: 同步就是指一个进程在执行某个谓求的时候。若该请求需要一段时间才能返回信贽。那么这个进程将会一直等待下去,直到收到返仓信息才继绫执行下去; 同步就相当于是当客户端发送请求给服务岩。在等待服务端喇应的请求时,客户岩不做其他的事情。当服务端做完了才返回到客户缓。这样的话客户端需要一直等待。用户使用起来会有不友好。 异步的理解: 异步是指进程不需要一直等下去,而是继续执行下面的操怍,不管其他进程的状态。当有海息返回时系统会通知进程进行处理,这样可以提高执行的效率。 异步就相当于当客户端发送给服务剃请求时。在等待服务曦喇应的时候,客户橐可以做其他的事情,这样节约了时间。提高了效率,
同步是阻塞模式。异步是非阻塞模式
Ajax特点 优点;可以无需刷新页面,与服务器进行通信 允许依据用户事件来更新部分页面内容 缺点:
· 没有浏览历史,不能回退·
存在跨域问题(同源)
. SEO不友好(搜索引擎优化,对爬虫不友好)
2.Ajax原理 客户端发送请求,请求交给XMLHttpRequest,XMLHttpRequest 把请求提交给服务,服务器进行业务处理,服务器响应数据交给XMLHttpRequest对象,XMLHttpRequest对象接收数据,由javascript把数据写到页面上。 Ajax工作流程
(1)创建Ajax对象(XmlHttpRequest)
var xmlhttp = new XMLHttpRequest() (2)告诉浏览器以什么方式发送请求 以及请求发送到哪
xmlhttp.open("get","http://127.0.0.1:8090",true)方法 描述open(method,url,async) 规定请求的类型、URL 以及是否异步处理请求。<br />method:请求的类型;GET 或 POST<br/>url:文件在服务器上的位置<br/>async: true(异步)或false(同步)
(3)设置响应服务器端数据处理
xmlhttp.onreadystatechange=function(){ //做数据处理 document.querySelector("#div1").innerHTML = xmlhttp.responseText } (4)发送请求
ajax:本身是针对MVC的编程,不符合现在前端MVVM的浪潮基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
axios:从 node.js 创建 http 请求支持 Promise API客户端支持防止CSRF
fetch
fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。fetch的优点:1.符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里2.更好更方便的写法
-
语法简洁,更加语义化
-
基于标准 Promise 实现,支持 async/await
-
同构方便,使用 isomorphic-fetch
-
更加底层,提供的API丰富(request, response)
-
脱离了XHR,是ES规范里新的实现方式
缺点:
1 fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。2)fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: 'include'})3)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费4)fetch没有办法原生监测请求的进度,而XHR可以
computed跟getters区别
如一个过滤后的求和数据filterSum,如果很多组件都要使用filterSum这个数据,那么比起在需要用到的组件中一遍遍computed,我们为何不把这个数据抽离出来共享呢?
vue.getters类似于页面中的计算属性computed,是vuex的store.js中的设置项,可以对state属性进行进一步转化和处理并返回来
getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的
getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果
对象中常用的方法
Object.defineProperty(obj,key,descriptor)===对当前对象中的某个属性进行监听或劫持,以及设置些规则
toString---------------------------检测数据类型的 Object.prototype.toString.call([value])
object.prototype---------------------------检测数据类型的 Object.prototype.toString.call([value])
Object.key()以数组的形式返回对象所有的属性(key)
object.valueOf() 方法返回指定对象的原始值。
object.value
父子通信传值组件的生命周期顺序
挂载阶段
该过程主要涉及 beforeCreate、created、beforeMount、mounted 4 个钩子函数。执行顺序为:父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted一定得等子组件挂载完毕后,父组件才能挂在完毕,所以父组件的 mounted 在最后。
更新阶段
该过程主要涉及 beforeUpdate、updated 2 个钩子函数。注意,当父子组件有数据传递时,才有这个更新阶段执行顺序的比较。执行顺序为:
子组件更新阶段
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
父组件更新阶段
父beforeupdate==>父updated
销毁阶段
该过程主要涉及beforeDestroy、destroyed 2 个钩子函数。执行顺序为:父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
你是否了解中央总线
vueBus是中央数据总线,是为了解决非关系组件间的通信问题,主要是使用了vue中的和emit方法,使用发布者订阅者模式,必须在相同的实例才可以进行数据响应,中央数据总线可以使用空的vue实例来实现,有一个cache对象,使用新增订阅方法,通过off进行订阅方法的删除,通过$emit实现通知订阅信息的更新
-
vm.$on(事件名,回调函数);
不可被多次执行,否则,回调函数会被执行多次。 -
要注意在组件的
beforeDestroy
钩子中调用vm.$off()
清除掉监听器。
祖孙级通信
使用provide 和 inject 可以实现祖孙组件之间的通信;通常这两个属性要一起使用;祖先组件通过 provide将需要向子孙组件传递的值return 出去;然后子孙组件通过inject注入依赖;不管层级有多深,都可以使用祖先组件传递过来的数据以及方法。祖孙组件的传值,一般用于多层级的关系当中,使用便捷。 目前中央通信是解决兄弟间通信,祖父祖孙间通信的最佳方法,不仅限于此,也可以解决父组件子组件间的相互通信
是否了解provide和inject
provide函数:用于给自己的后代组件传递参数provide(‘car’,car),第1个参数为给你传入的参数起个名字,第2个参数为传入的参数。
inject函数:用于注入祖组件传递过来的参数inject(‘car’),形参为传递过来的参数名字
provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。
inject:一个字符串数组,或者是一个对象。属性值可以是一个对象,包含from和default默认值。
对于日历组件,你怎么创建绑定------------看人资笔记
Java你了解不,后端没有接口你怎么获取接口
自己使用mock模拟数据接口
对于拦截器的理解
请求拦截器是请求之前拦截响应拦截器是响应完成后拦截
请求拦截器 在请求发送前进行必要操作处理,例如添加统一cookie、请求体加验证、设置请求头等,相当于是对每个接口里相同操作的一个封装;
响应拦截器 同理,响应拦截器也是如此功能,只是在请求得到响应之后,对响应体的一些处理,通常是数据统一处理等,也常来判断登录失效等。
比如一些网站过了一定的时间不进行操作,就会退出登录让你重新登陆页面,当然这不用拦截器你或许也可以完成这功能,但是会很麻烦而且代码会产生大量重复,所以我们需要用到拦截器
说一下请求拦截器的流程
页面发送请求先经过请求拦截器进行相应处理,成功后发送请求到服务器
服务器返回数据也要先经由响应拦截器
在响应拦截器内可以统一处理服务器返回的错误信息
相应处理后由页面接收响应数据
token失效你怎么判断的,如果失效你怎么处理?
后端:收到用户访问某个接口时,检查当前token是否失效,如果token已经失效,返给前端一个约定好的状态码 10002
前端:在响应拦截器中,分析接口的返回值,如果状态码为10002, 则进行token失效操作 返回登录页提示用户身份过期
页面跳转你一般怎么做?
router-link跳转通常用于点击 查看 按钮,跳转到其他页面
this.$router.push() 跳转 看看是否需要携带参数 使用查询参数或者路径传参
说一下路由守卫 如果没有next会不会终止? 里面几个参数?
to:到哪去
from:从哪来
next: 放行
路由守卫 如果没有next不会终止 会进入死循环
vue双向绑定的原理------响应式原理
第一步,“数据劫持”:vue 2.x 用 Object.defineProperty() 方法来实现数据劫持,为每个属性分配一个订阅者集合的管理数组 dep;vue 3.x 用 ES6 的 Proxy 构造函数来实现数据劫持。第二步,“添加订阅者”:在编译的时候在该属性的数组 dep 中添加订阅者,添加方式包括:v-model 会添加一个订阅者,{{}} 也会,v-bind 也会,只要用到该属性的指令理论上都会。第三步,“为 input 添加监听事件”:为 input 添加监听事件,修改值就会为该属性赋值,触发该属性的 set() 方法,在 set() 方法内通知订阅者数组 dep,订阅者数组循环调用各订阅者的 update() 方法更新视图。
说一下$set的底层原理 一般你在什么情况下会使用到?
在我们开发过程中,经常会遇到,为一个数组或者对象data中添加一个属性,点击按钮后发现,控制台打印明明对象中已经出现了这个属性,视图层却并没有更新该数据,这是因为受到JS的限制,vue.js中不能监听对象属性的添加和删除,因为在vue组件初始化过程中,会调用getter和setter方法,所以该属性只有存在data中时,试图层才会响应数据的变化。
Vue.set 或者说是$set 原理如下
因为响应式数据 我们给对象和数组本身都增加了ob属性,代表的是 Observer 实例。当给对象新增不存在的属性 首先会把新的属性进行响应式跟踪 然后会触发对象ob的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组
递归的使用场景
递归应用场景: 深拷贝 菜单树 遍历 DOM 树
递归两个要素1.递归的边界——找到出口,在什么情况下跳出递归2.递归的逻辑——找到入口,什么情况下重复调用自己,调用自己做什么
echart的配置
echarts x轴的所有配置项基本都在这了(y轴同理)axisLine:坐标轴轴线相关设置。
axisTick:坐标轴刻度相关设置。
axisLabel:坐标轴刻度标签的相关设置。
splitLine: 坐标轴在 grid 区域中的分隔线设置。
splitArea :坐标轴在 grid 区域中的分隔区域,默认不显示。
事件循环
事件循环分为两种,分别是浏览器事件循环和node.js事件循环,JavaScript是一门单线程语言,指主线程只有一个。Event Loop事件循环,其实就是JS引擎管理事件执行的一个流程,具体由运行环境确定。目前JS的主要运行环境有两个,浏览器和Node.js。 事件循环机制告诉了我们JS代码的执行顺序,是指浏览器或Node的一种解决JS单线程运行时不会阻塞的一种机制。浏览器的事件循环又分为同步任务和异步任务
异步分为微任务宏任务
js代码开始执行后,主线程执行栈中会把任务分为两类.一类是同步任务, 一类是异步任务; 主线程执行栈优先执行同步任务, 异步任务会被放入特定的处理程序中,满足条件后,被放到消息(任务/事件)队列中,主线程执行栈中所有的同步任务执行完毕之后,通过事件循环去消息(任务/事件)队列中,挑选优先满足条件的程序,放入主线程执行栈中执行。事件循环,周而复始,一直执行。
宏任务: setTimeout setInterval Ajax DOM事件 script(整体代码) I/OUI交互事件 postMessageMessage Channel setImmediate(Node.js 环境)
微任务: Promise async/await Object.observe MutationObserver process.nextTick(Node.js 环境)微任务比宏任务的执行时间要早 异步和单线程 异步和单线程是相辅相成的,js是一门单线程脚本语言,所以需要异步来辅助
异步和同步的区别: 异步不会阻塞程序的执行, 同步会阻塞程序的执行,
同步任务
同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。
异步任务
异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务
setTimeout (setInterval) AJAX 事件绑定(如:.on,.bind,.listen,.addEventListener,.observe))观察者模式(例如:websocket中就有发布 和订阅之类的操作) promise 是一个异步操作,但是它是一个异步操作的解决方案,即保存着异步操作的结果,可以把异步函数以同步函数的形式写出来(Promise的成功|失败的回调(.then()/.catch())async/await — 使用generator的语法糖,可以理解为是generator的一种改进
作用域插槽使用场景
父组件想使用来自子组件的数据,如下图,想使用来子组件 data 中的 title 时,会因为作用域的问题,无法正常渲染显示。这时就需要用到作用域插槽
使用场景
例如,在使用elementUI组件库的表格组件时,表格的编辑和删除操作要用到作用域插槽。因为一个表格组件,就是当前组件的子组件。
此时我们通过作用域插槽很容易拿到当前表格行的索引和内容,这样就可以很方便地进行编辑展示、删除的操作。
并且,我们知道Vue是单向数据流。传递给子组件的数据,若要修改,应在父组件中处理。而我们通过作用域插槽,正好可以在父组件中修改数据,方便安全。
常见的跨域操作
同源策略会限制以下的内容:
1,cookie、localstorage和indexDB无法读取
2,DOM无法获得
3,ajax发送后会被浏览器拦截
当协议、域名、端口号任意一个不相同时,都算作不同域。不同域之间请求资源,就是跨域。
跨域并不是http请求发送不出去,请求可以发出去,服务端也能接受并正常返回结果,只是结果被浏览器拦截了。
同源策略:请求的url地址,必须与浏览器上的url地址相同,在同一个域上。
例如:用AJAX发送请求
get请求跟post请求区别
1.get请求一般是去取获取数据(其实也可以提交,但常见的是获取数据);post请求一般是去提交数据。
2.get因为参数会放在url中,所以隐私性,安全性较差,请求的数据长度是有限制的,不同的浏览器和服务器不同,一般限制在 2~8K 之间,更加常见的是 1k 以内;post请求是没有的长度限制,请求数据是放在body中;
3.get请求刷新服务器或者回退没有影响,post请求回退时会重新提交数据请求。
4.get请求可以被缓存,post请求不会被缓存。5.get请求只能进行url编码(appliacation-x-www-form-urlencoded),post请求支持多种(multipart/form-data等
vue响应式原理参数
vue2的响应式原理主要使用的是Object.defineProperty( ),里面需要传入三个参数,分别是:【响应源数据的对象,源数据中的需要读写的属性,相对应的对象方法(包含了get和set方法)】vue3的响应式原理主要依靠的是ES6新增的 Proxy 以及相配合的 Reflect,需要在Proxy的实例对象中传入两个参数【源数据对象,处理对象的方法【get,set,deleteProperty…等】
vue.use与 vue. prototype
Vue.use()用于注册具有install方法的变量,注册后install函数会自动调用,使得install的具体变量能够全局使用,包括全局变量,全局标签等等。
而Vue.prototype就是一个注册全局变量的方法,注册的全局的变量以$开头,调用this方法调用。
vue 提供非响应式数据变为响应式数据
响应式:
当Vue组件的实例初始化的时候已有的数据就是响应式数据通过Object.defineProperty代理实例this身上的响应式属性的值发生改变会触发视图更新非响应式:
当Vue组件的实例初始化的时候没有,后期添加的属性没有通过Object.defineProperty代理实例this身上的非响应式属性的值发生改变不会触发视图更新
路由的 2 种模式之间的区别? 利用了浏览器什么特性
hashhistory
形式上:hash模式url里面永远带着#号,开发当中默认使用这个模式。如果用户考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url,适合推广宣传;功能上:比如我们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react做的,咱们把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式,但是使用history模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404错误,那么就需要和后端人配合,让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok了
相同之处: 是两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新。
不同之处在于: pushState 会增加一条新的历史记录,而 replaceState 则会替换当前的历史记录。
hash 模式的优缺点:
-
优点:浏览器兼容性较好,连 IE8 都支持
-
缺点:路径在井号
#
的后面,比较丑
history 模式的优缺点:
-
优点:路径比较正规,没有井号
#
-
缺点:兼容性不如 hash,且需要服务端支持,否则一刷新页面就404了
call apply bind区别
-
共同点:
-
都可以修改this,第一个参数都是
修改的this
-
-
不同点
-
传参方式不同: call是逐一传参, apply是数组/伪数组传参
-
函数名.call(修改的this,参数1,参数2....)
-
函数名.apply(修改的this,数组/伪数组)
-
-
执行机制不同:call和apply会立即执行函数,bind不会立即执行
-
bind会得到一个修改this后的新函数
-
-
for in与for of区别
for-in与for-of区别 1.功能不同 for-in是遍历数组的下标 for-of是遍历数组的元素 2.原型的属性 for-in会遍历原型的属性 for-of不会遍历原型的属性 3.数据类型 for-in可以遍历Object类型 for-of不可以遍历Object类型
如何清除浮动
1、直接设置父元素的高度
2、单伪元素清除法
3、双伪元素清除法
4、给父元素设置一个overflow:hidden
sessionstorage与localstorage区别
1、localStorage没有过期时间是永久;2、sessionStorage针对一个session进行数据存储,生命周期与session相同,当用户关闭浏览器后,数据将被删除。
localStorage生命周期是永久,除非用户清除localStorage信息,否则这些信息将永远存在。sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过它存储的数据也就被清空了
js中什么情况会返回undefind
1,访问,声明,但是没有初始化的变量
2,访问不存在的属性
3,访问函数的参数没有被显示的传递值
4,访问任何被设置为undefined的变量
5没有被定义return的函数隐式返回值
6.函数return没有显示的返回任何内容
事件的3要素
事件源:就是你点击的那个 <div>触发的对象
事件类型:表示动作,点击,输入划过等
事件处理函数(处理事件的代码流程):表示结果,比如点开跳转到另一个页面
arguments的作用是什么
答案:
1.在任意函数内部都有一个看不见的arguments,除了箭头函数外
2.arguments是一个长的像数组的伪数组,可以对它进行遍历
3.函数调用的实参变成了arguments的元素
描述一下构造函数里面的静态成员和实例成员
静态成员:给构造函数本身上添加的属性与方法称之为静态成员 ,静态成员只能被构造函数访问
实例成员:在构造函数里面通过this添加的属性和方法 ,实例成员只能被实例化对象访问
序列化与反序列化
把对象转换为字节序列的过程称为对象的序列化*;----json.stringify
把字节序列恢复为对象的过程称为对象的反序列化---json.parse
说说对变量提升和函数提升的理解?
在当前作用域中,用var关键字声明的变量允许在变量声明之前即被访问,变量在var声明之前即被访问,变量的值为 undefined,let/const 声明的变量不存在变量提升,
是指函数在声明之前即可被调用,用function关键字声明的函数有函数提升
函数表达式声明的函数没有函数提升
使用XMLHttpRequest进行AJAX请求的步骤
1.建立XMLHttpRequest对象;(const xhr = new XMLHttpRequest())
2.配置请求信息,(如open,get方法),使用open方法与服务器建立链接;
3.向服务器发送数据,使用send方法;
4.在回调函数中针对不同的响应状态进行处理;( xhr.onload = function () {})
重绘与回流
回流(reflow):当render tree中的元素的宽高、布局、显示、隐藏或元素内部文字结结构
发生改变时,会影响自身及其父元素、甚至追溯到更多的祖先元素发生改变,则会导致元素内部、
周围甚至整个页面的重新渲染,页面发生重构,回流就产生了
重绘(repaint):元素的结构(宽高、布局、显示隐藏、内部文字大小)未发生改变,只是
元素的外观样式发生改变,比如背景颜色、内部文字颜色、边框颜色等。此时会引起浏览器重绘,
显然重绘的速度快于回流
回流必将引起重绘,而重绘不一定会引起回流
VUE--添加动态路由/删除路由
如根据用户不同的权限,注册不同的路由 时候往往使用添加路由方法
-
如果被添加路由和已存在路由重名,就会删除已存在路由并加入新路由
router.addRoute({name: 'home',path: '/home',component: () => import('@/view/Home.vue'), })
-
为route添加一个children路由,那么可以传入对应的name,home 会成为about 子路由
import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({routes: [{name: 'about',path: '/about1',children: [{name: 'home1',path: 'home1',component: () => import('@/view/Home.vue'),},],component: () =>import(/* webpackChunkName: "about动态打包" */ '@/view/About.vue'),},],history: createWebHistory(), // 开启history模式 }) router.addRoute('about', {name: 'home',path: 'home',component: () => import('@/view/Home.vue'), }) console.log(router.getRoutes()) export default router
动态删除路由
-
方式一:添加一个name相同的路由;
-
方式二:通过removeRoute方法,传入路由的名称;
-
方式三:通过addRoute方法的返回值回调;
vue2的响应式原理
通过数据劫持 defineProperty + 发布订阅者模式,当 vue 实例初始化后 observer 会针对实例中的 data 中的每一个属性进行劫持并通过 defineProperty() 设置值后在 get() 中向发布者添加该属性的订阅者,这里在编译模板时就会初始化每一属性的 watcher,在数据发生更新后调用 set 时会通知发布者 notify 通知对应的订阅者做出数据更新,同时将新的数据根性到视图上显示。缺陷:只能够监听初始化实例中的 data 数据,动态添加值不能响应,要使用对应的 Vue.set()。
关于Vue单页面应用的首屏加载优化
缩小项目体积
原理:体积越小,加载越快。方法:
通过webpack对项目体积进行压缩,开启gzip压缩文件通过对css3、js文件的合并,如在两不同组件中,拥有相同的样式,可通过全局css文件中设置。在js文件上,将相同的方法封装合并成一个方法,如API请求。减小图片体积,图标可通过矢量图来代替。减少请求次数/体积:原理:请求越少,加载越快方法:
通过精灵图来减少小图标的总请求数在图片数据多时,页面高度大于浏览器高度,通过图片懒加载,对未可见的图片进行延迟加载。vue方法: 通过安装vue-lazyload依赖->全局引入vue-lazyload依赖->配置依赖
将大文件上传到CDN,通过CDN加载依赖,CDN的全称是Content Delivery Network,即内容分发网络
减少加载模块原理:单页面应用的首屏加载较慢主要是因为单页面应用在首屏时,无论是否需要都会加载所有的模块,可通过按需加载、路由懒加载来优化。方法:
按需加载,通过对路由文件的配置,来对相关模块划分区间,如登录界面可以和首页、主页面划分一块,在进入首屏时,只对首屏所在的区块进行加载。通过require.ensure()来将多个相同类的组件打包成一个文件。如示例代码,打包时,将两个组件打包成一个js文件,文件名为good。
cookie与localStorage的区别
1.存储量的区别
cookie单个的最大存储为4k,如果大于4k,则存储失败,浏览器中找不到对应的cookie信息;localStorage的最大存储为5m。如果大于这个最大限制浏览器提示出错
2.存储量的区别
cookie默认是会话级存储 可以设置过期时间 localStorage是持久化存储,除非主动clear掉
-
可操作
cookie不仅仅只是存储数据,还有其他多个属性可供其操作设置: Domain与Path一起决定了cookie的作用范围 Expires/Max-Age决定了过期时间 HttpOnly 如果设为true,那么通过js(document.cookie)无法读取cookie数据 Secure 如果设为true,那么cookie只能用https协议发送给服务器
localStorage只是存储数据
4.使用场景
cookie的使用场景一般是作为客户端与服务端的一种信息传递,当添加了cookie,默认的同源的cookie信息会自动作为请求头的一部分被发送到服务端 localStorage一般仅用作客
权限控制分为页面级别和按钮级别
页面级别:用户登录后,获取用户role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。最后通过router.addRoutes动态挂载。现在是通过获取到用户的role之后,在前端用v-if手动判断来区分不同权限对应的按钮的。。
按钮级别:用户登录后,获取用户role,在前端用v-if或者封装一个自定义指令,手动判断来区分不同权限对应的按钮的思路:
登录:当用户填写账号和密码后向服务器验证是否正确,验证通过之后,服务端回返回一个token,拿到token之后(将token存在sessionStorage中,保证刷新新页面后能记住用户登录状态),前端会根据token再去拉取一个user_info的接口来获取用户的详细信息(如用户角色,用户权限,用户名等等信息)。
权限验证:通过token获取用户对应的role,自定义指令,获取路由meta属性里btnPermissions(注:meta.btnPermissions是存放按钮权限的数组,在路由表里配置),然后判断role是否在btnPermission数组里,若不再即删除该按钮DOM
vue-route跟vue-router区别
router是通过VueRouter构造函数new出来得到了的一个实例对象,这个对象是全局的路由对象,它包含了所有的路由对象和属性
类似于history对象
比较常用的属性是:
correntRoute:当前页面的路由对象
options.routes:所有的路由对象
比较常用的方法是
$router.push('url') : 跳转到指定路由
$router.back() : 跳转上一级路由
$router.go(n) :n为1时表示跳转下一级路由,-1时表示上一级路由route是一个页面的跳转路由对象,每一个页面都有一个route对象,是一个局部的路由对象
常见的属性有:
$route.path : 当前路由对象的路径,如' /login '
$route.params : 路由的动态匹配参数
路由的查询参数的键值对,如那么route.query.username==' hsq '
$route.name : 当前路由的名字,也就是路由的name属性
vue中sync的使用
vue 修饰符sync的功能是:当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定。如果我们不用.sync,我们想做上面的那个弹窗功能,我们也可以props传初始值,然后事件监听,实现起来也不算复杂。
路由跳转怎么做,replace怎么跳转
flex: 1;
的值是 flex-grow: 1; flex-shrink: 1; flex-basis: 0%;
意思就是: 元素占据剩余宽度的 1 份,收缩比例为 1,因为 flex-basis 具有最高优先级,元素首次分配宽度(flex-direction: colunm; 则为高度)如果父元素有设置宽度,则为 0%;父元素没有设置宽度,则和 auto效果一致。
脚手架用的哪个版本 public目录和assets目录有什么区别
public放不会变动的文件(相当于vue-cli2.x中的static)public/ 目录下的文件并不会被Webpack处理:它们会直接被复制到最终的打包目录(默认是dist/static)下。必须使用绝对路径引用这些文件,这个取决于你vue.config.js中publicPath的配置,默认的是/。assets放可能会变动的文件assets目录中的文件会被webpack处理解析为模块依赖,只支持相对路径形式。
.谈谈你对js的理解(浏览器中的js)
路由哪些配置
$nextick应用场景
适用场景:
第一种:有时需要根据数据动态的为页面某些dom元素添加事件,这就要求在dom元素渲染完毕时去设置,但是created与mounted函数执行时一般dom并没有渲染完毕,所以就会出现获取不到,添加不了事件的问题,这回就要用到nextTick处理
第二种:在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法,例如:应用滚动插件better-scroll时
谈谈promise,解决异步还有什么方法,接口报错如何捕获错误
try catch 是最常见的错误处理方式缺点:只能捕获同步的错误,对于异步的错误是无法捕获的
Promise.prototype.catchPromise提供了catch方法,用于捕获Promise的错误 因为它不仅可以捕获同步的错误,还可以捕获异步的错误
Promise.prototype.thenPromise提供了then方法,用于捕获Promise的成功和失败
Vue项目四种打包优化的方法总结
默认情况下,通过import语法导入的第三方依赖包,最终会全部打包到一个js文件中,会导致单文件体积过大大,在网速底下时会阻塞网页加载,影响用户体验。
项目优化策略:1.使用CDN 使用CDN,减少包的体积。 将线上环境中用到的一些依赖,使用CDN网络节点的方式进行引用,而不是直接打包到包里。 项目开发阶段和生产阶段不一样,我们在开发阶段还是要使用import导入依赖的方式来进行开发,在生产阶段再配置CDN外链来获取项目依赖。
默认情况下,Vue项目的开发模式和发布模式,共同使用一个打包的入口文件(src/main.js)为了将项目的开发过程与发布过程分离,我们可以分别使用两个入口文件,一个用于开发环境打包(这个就是main.js),一个用于生产环境打包
1.1 在项目根目录下新建 vue.config.js 文件
1.2 配置prod_env.js (发布模式打包入口文件)
凡是通过CDN节点方式引用的包,在这里都注释掉并在public/index.html 中加入如下引用 如果注释掉出现错误可以不进行注释
1.3 在 vue.config.js 中添加配置 (把用到的js脚本放到 externals中)
注:使用vue ui来打包项目一直报错,但你认为代码没问题的话,请直接使用 命令的方式 npm run build 打包!!!
2.使用路由懒加载
当打包构建项目时,js包会变得非常大,影响页面加载,我们把不同路由的对应组件分割成不同的代码块,然后当路由被访问的时候加载对应的组件,这样能提升访问效率。 切分之后,并不是浏览器访问某个路由,才去下载对应的模块,仍然在首次访问程序时,下载所有模块,只不过每个模块分的很小,加载首页时需要的模块能够更快的下载下来,所以能够更快的显示
具体分三步:
-
安装包 npm install --save-dev @babel/plugin-syntax-dynamic-impor
-
在babel.config.js配置文件中加入插件
-
将路由改为按需加载的形式
然后重新编译
3.服务端压缩文件 到了这一步,前端的可压缩空间以及不大了,追求极致的要在后端想办法。
3.1 开启node服务器采用compression 压缩文件
const express = require('express')const app = express()
app.use(express.static('./dist'))app.listen(80, () => { console.log('Server is running at http://127.0.0.1:80')})然后安装 compression -> npm install compression
const express = require('express')const app = express()
let compression = require('compression')app.use(compression())app.use(express.static('./dist'))app.listen(80, () => { console.log('http:127.0.0.1:80')})
运行后包的体积就又小了很多
注意:但是,我们希望的是,部署的时候 index.html 中使用这些 CDN 节点的依赖,开发阶段,希望继续使用本地的包,如果我们不做修改的话,会引入两份重复的文件。不嫌麻烦可以手动注释
解决方案:这就需要在 index.html 中根据当前的打包环境是开发还是部署,动态的决定 index.html 是否使用引用
4.移除代码中的console
打包上线后的console语句没有任何作用,所以我们应该移除它,除去手动移除,还可以使用插件的方式来移除代码里的console语句。 使用 babel-plugin-transform-remove-console 插件来移除代码console 步骤 : 1.安装 babel-plugin-transform-remove-console npm install babel-plugin-transform-remove-console --save-dev2.配置 项目根目录下的 babel.config.js 由于单纯的引用 babel-plugin-transform-remove-console ,会造成无论是开发环境还是运行环境都会运行该插件,但是在开发中console对于我们是有用的,所以如下配置,只有在 production 生产环境下再移除console
vue移动端(H5)打包
h5要唤起很多 的原生的api,唤起支付宝 相机等 是怎么实现的
目前项目中比较常用的第三方支付无非就是支付宝支付和微信支付。下面介绍一下Vue中H5页面如何使用支付宝支付。其实很简单的,只不过是调自己后台的一个接口而已(后台根据支付宝文档,写好支付接口)。
触发支付宝支付调用后台接口,后台会返回支付宝提供的form表单,我们只要在vue里面创建新节点,将返回的form表单append进去,并提交就可以唤起支付宝支付。另在此说一下这个returnUrl, 它是支付后支付宝回调的页面。具体可以根据自身业务,后台写死或者由前端控制。
methods () {/*** 支付宝支付*/goAlipay () {this.$loading.show()const data = {/* 自身接口所需的一些参数 */...amount: this.price,/* 支付后支付宝return的url */// returnUrl: 'www.baidu.com'returnUrl: window.location.origin + window.location.pathname + '?userParams=' + this.userParams}this.$http(this.$apiSetting.alipay,data).then(res => {this.$loading.hide()if (res.data.statusCode === '000000') {const div = document.createElement('div')/* 此处form就是后台返回接收到的数据 */div.innerHTML = res.data.data.alipayInfodocument.body.appendChild(div)document.forms[0].submit()}}, error => {this.$loading.hide()console.log(error)})} }
如何在用户登录的情况下封装二次登录,ip 验证
scoped
防止样式冲突,只要添加scoped就会在相应的标签加上一个属性data-v-219221,属性上会有一个随机的唯一值,会在选择题使用属性选择器选择元素,那这样选择器就是唯一的
移动端打包上线 apk
一、Vue项目打包移动端APP
需要准备的工具:Hbuilder
二、首先打包vue到dist目录
npm run build
三、然后再Hbuilder中打开dist目录
首先可以看到dist目录的图片是一个 W 字样的图标,表示这是一个 web项目
四、然后将dist包含的 web项目 转换为 移动 APP项目
此时可以看到dist目录的文件图标由 W 变成了 A,说明此时的web项目已经变成了移动APP项目 ,而且此时生成了一个新的文件manifest.json
下面要做的就是在manifest.json中配置移动APP所需要的配置项
Es6 新增的新特性
-
新增了块级作用域(let,const)
2.提供了定义类的语法糖(class) 3.新增了一种基本数据类型(Symbol) 4.新增了变量的解构赋值 5.函数参数允许设置默认值,引入了rest参数,新增了箭头函数。 6.数组新增了一些API,如isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法。 7.对象和数组新增了扩展运算符 8.ES6新增了模块化(import / export) 9.ES6新增了**Set和Map数据结构**。 10.ES6原生提供Proxy构造函数,用来生成Proxy实例 11.ES6新增了生成器(Generator)和遍历器(Iterator)
async和await
es6的set和map的区别是什么
区别:1、set指的是“集合”结构,而Map指的是“字典”结构;2、set是以“[value, value]”的形式储存元素,而Map是以“[key, value]”的形式储存;3、Map可用get()通过键查找特定值并返回,而set不行。
MVVM和MVC有什么区别
MVC是一种设计模式:
M(Model):模型层。是应用程序中用于处理应用程序数据逻辑的部分,模型对象负责在数据库中存取数据;V(View):视图层。是应用程序中处理数据显示的部分,视图是依据模型数据创建的;C(Controller):控制层。是应用程序中处理用户交互的部分,控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
MVVM
vue框架中MVVM的M就是后端的数据,V就是节点树,VM就是new出来的那个Vue({})对象
M(Model):模型层。就是业务逻辑相关的数据对象,通常从数据库映射而来,我们可以说是与数据库对应的model。V(View):视图层。就是展现出来的用户界面。VM(ViewModel):视图模型层。连接view和model的桥梁。因为,Model层中的数据往往是不能直接跟View中的控件一一对应上的,所以,需要再定义一个数据对象专门对应view上的控件。而ViewModel的职责就是把model对象封装成可以显示和接受输入的界面数据对象。
MVVM的优势1、mvc和mvvm都是一种设计思想。 主要就是mvc中Controller演变成mvvm中的viewModel。 mvvm主要解决了mvc中大量DOM操作使页面渲染性能降低,加载速度变慢的问题 。
2、MVVM与MVC最大的区别就是:它实现了View和Model的自动同步:当Model的属性改变时,我们不用再自己手动操作Dom元素来改变View的显示,它会自动变化。
3、整体看来,MVVM比MVC精简很多,我们不用再用选择器频繁地操作DOM。
MVVM并不是用VM完全取代了C,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现
浏览器兼容性问题及常见的解决方法 以及 渲染机制
三.常见的浏览器兼容性问题及解决方法1.不同浏览器标签默认的外边距和内边距不同(这是最常见也是最容易解决的)。
问题状况:随便写几个标签,在不加样式控制的情况下,不同浏览器的margin和padding的差异较大。
解决方法:在css里使用:*{margin:0px; padding:0px}。
2.块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大。
问题状况:后面的块属性标签被顶到下一行。
解决方法:在float的标签样式中加入:display:inline;将其转化为行内属性。
3.设置较小高度标签(一般小于10px),在IE6、IE7,遨游中高度超出自己设置的高度。
问题状况:IE6、7和遨游里这个表情的高度不熟控制,超出自己设置的高度。
解决方法:给超出高度的标签设置overflow:hidden;或者设置行高line-height小于你设置的高度。
4.行内属性标签,设置display:block后采用float布局,又有横行的margin的情况,IE6间距bug。
问题状况:IE6里间距比超过设置的间距。
解决方法:在display:block;后面加入display:inline;display:table;
5.图片默认有间距。
问题状况:几个img标签放在一起的时候,有些浏览器会有默认的间距,加了问题一中提到的通配符也不起作用。
解决方法:使用float为img布局。
6.标签最低高度设置min-height步兼容。
问题状况:因为min-height本身就是一个不兼容的css属性,所以设置min-height时不能很好的被给浏览器兼容。
解决方法:如果我们要设置一个标签的最小高度为200px,需要进行的设置为:{min-height:200px; height:auto; !important; height:200px; overflow:visible;}。
nginx和tomcat的区别
nginx常用做静态内容服务和代理服务器(不是你FQ那个代理),直面外来请求转发给后面的应用服务(tomcat,django什么的),tomcat更多用来做做一个应用容器,让java web app跑在里面的东西,对应同级别的有jboss,jetty等东西。
但是事无绝对,nginx也可以通过模块开发来提供应用功能,tomcat也可以直接提供http服务,通常用在内网和不需要流控等小型服务的场景。
tomcat的作用是:1、管理servlet应用的生命周期;2、把客户端请求的url映射到对应的servlet;3、与Servlet程序合作处理HTTP请求。
严格的来说,Apache/Nginx 应该叫做「HTTP Server」;而 Tomcat 则是一个「Application Server」,或者更准确的来说,是一个「Servlet/JSP」应用的容器(Ruby/Python 等其他语言开发的应用也无法直接运行在 Tomcat 上)。
一个 HTTP Server 关心的是 HTTP 协议层面的传输和访问控制,所以在 Apache/Nginx 上你可以看到代理、负载均衡等功能。客户端通过 HTTP Server 访问服务器上存储的资源(HTML 文件、图片文件等等)。通过 CGI 技术,也可以将处理过的内容通过 HTTP Server 分发,但是一个 HTTP Server 始终只是把服务器上的文件如实的通过 HTTP 协议传输给客户端。
而应用服务器,则是一个应用执行的容器。它首先需要支持开发语言的 Runtime(对于 Tomcat 来说,就是 Java),保证应用能够在应用服务器上正常运行。其次,需要支持应用相关的规范,例如类库、安全方面的特性。 对于 Tomcat 来说,就是需要提供 JSP/Sevlet 运行需要的标准类库、Interface 等。为了方便,应用服务器往往也会集成 HTTP Server 的功能,但是不如专业的 HTTP Server 那么强大,所以应用服务器往往是运行在 HTTP Server 的背后,执行应用,将动态的内容转化为静态的内容之后,通过 HTTP Server 分发到客户端。总结一下:nginx是一个HTTP Server ,侧重关心HTTP协议层面的传输和访问控制Tomcat是一个应用服务器,比如:将应用部署到tomcat服务器两者功能有重合,但是侧重点不同,目前都是将两者相结合使用
react
优点:
React速度很快
与其它框架相比,React采取了一种特立独行的操作DOM的方式。它并不直接对DOM进行操作。它引入了一个叫做虚拟DOM的概念,安插在JavaScript逻辑和实际的DOM之间。这一概念提高了Web性能。在UI渲染过程中,React通过在虚拟DOM中的微操作来实对现实际DOM的局部更新。
跨浏览器兼容
虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。
模块化
为你程序编写独立的模块化UI组件,这样当某个或某些组件出现问题是,可以方便地进行隔离。每个组件都可以进行独立的开发和测试,并且它们可以引入其它组件。这等同于提高了代码的可维护性。
单向数据流让事情一目了然
Flux是一个用于在JavaScript应用中创建单向数据层的架构,它随着React视图库的开发而被Facebook概念化。它只是一个概念,而非特定工具的实现。它可以被其它框架吸纳。例如,Alex Rattray有一个很好的Flux实例,在React中使用了Backbone的集合和模型。
纯粹的JavaScript
现代Web应用程序与传统的Web应用有着不同的工作方式。例如,视图层的更新需要通过用户交互而不需要请求服务器。因此视图和控制器非常依赖彼此。许多框架使用Handlebars或Mustache等模板引擎来处理视图层。但React相信视图和控制器应该相互依存在一起而不是使用第三方模板引擎,而且,最重要的是,它是纯粹的JavaScript程序。
同构的JavaScript
单页面JS应用程序的最大缺陷在于对搜索引擎的索引有很大限制。React对此有了解决方案。React可以在服务器上预渲染应用再发送到客户端。它可以从预渲染的静态内容中恢复一样的记录到动态应用程序中。因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。
React与其它框架/库兼容性好
比如使用RequireJS来加载和打包,而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务不再让人望而生畏。
缺点?
React本身只是一个V而已,所以如果是大型项目想要一套完整的框架的话,也许还需要引入Flux和routing相关的东西。
React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。
react全家桶都有:1、react是核心;2、redux相当于数据库;3、React Router是专为React设计的路由解决方案;4、axios用于浏览器和Node js的http客户端;5、Ant Degisn很好的React库
解决Vuex刷新页面数据丢失的问题
-
vuex存储的数据只是在页面中,相当于全局变量,页面刷新的时候vuex里的数据会重新初始化,导致数据丢失。
-
因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被重新赋值。
解决方法·
-
直接在vuex修改数据方法中将数据存储到浏览器本地存储
-
import Vue from 'vue'; import Vuex from 'vuex';Vue.use(Vuex);export default new Vuex.Store({state: {orderList: [],menuList: []},mutations: {orderList(s, d) {s.orderList= d;window.localStorage.setItem("list",jsON.stringify(s.orderList))}, menuList(s, d) {s.menuList = d;window.localStorage.setItem("list",jsON.stringify(s.menuList))},} })
-
利用第三方库进行持久化存储安装 vuex-persistedstatenpm install --save vuex-persistedstate
在store文件夹下的indedx.js中配置信息使用vuex-persistedstate默认存储到localStorage
-
vue全家桶
vue-cli vueRouter Axios vuex UI框架如:iview、vant、elementUI:
Vue3中的生命周期
1、setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
2、onBeforeMount() : 组件挂载到节点上之前执行的函数;
3、onMounted() : 组件挂载完成后执行的函数;
4、onBeforeUpdate(): 组件更新之前执行的函数;
5、onUpdated(): 组件更新完成之后执行的函数;
6、onBeforeUnmount(): 组件卸载之前执行的函数;
7、onUnmounted(): 组件卸载完成后执行的函数;
8、onActivated(): 被包含在 <keep-alive> 中的组件,会多出两个生命周期钩子函数,被激活时执行;
9、onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;
10、onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。
关于实现token无感刷新的解决方案
在开发中为了安全或满足分布式场景,通常会舍弃原有的session认证手段,而采用jwt(json web token);但是使用token难免遇到token有效期的问题,如果token长期有效,服务端不断发布新的token,导致有效的token越来越多,这必然是存在安全问题的。而token不想session一样,在用户操作时会进行刷新,为了用户体验,这个刷新就需要自己实现。使用旧token获取新token如果采取单个token的方式要实现token的自动刷新,就必须使用定时器,每隔一段时间自动刷新token,并且这个时候token一定要是没有过期的,因为如果已经过期的token也可以用来刷新,这和长期有效的token也没什么不同。但这种方式存在一定的问题:
为了保证同一时间,账户只被单个用户登录,后端必然要保证一个账户的多个token只有一个生效,最简单的方式就是使用分布式缓存中间件如redis,而存在并发请求时,可能前一个请求带着的是旧token,此时又到了刷新token的时间,就会产生请求的token与服务端存储的token不一致的问题使用定时器是增加了性能的损耗,不是最佳的手段使用双token的方式进行无感刷新登录后客户端收到两个token(access_token,refresh_token),其中access_token用来鉴定身份,而refresh_token用来刷新access_token;这就要求了refresh_token要比access_token有效时间要长,并且refresh_token不能用来鉴定身份使用这种方案又有一下解决方式:
后端每次响应都响应一个token过期时间,前端进行判断,在token过期前进行刷新,这种方式存在太多不可控因素,如客户端系统时间被修改、长时间没请求导致token过期却未刷新,无法做到无感刷新,并且也存在并发问题使用定时器,使用定时器增加的资源的损耗,亏损了性能,不推荐在得到token过期的请求时,再发送refresh_token;有点懒加载的意思,这种方案性能最优
何为动态组件
1.动态组件的作用动态组件指的是 动态切换组件的显示与隐藏 。动态组件可以拆分为:① <component> 是组件的 占位符② 通过 is 属性 动态指定 要渲染的组件名称③ <component is ="要渲染的组件的名称"></component>
-
如何实现动态组件渲染主要原理是改变了子组件的标签占位符,来完成组件的动态渲染
vue3双向数据绑定
现在,没必要把数据写到data里面,或者是写一个setup函数,再进行return出去。import进来的组件,可以直接在页面中使用,不再需要vue2的component或者是setup函数的return了。
ref() 函数
ref()这个函数使得我们的变量拥有了双向绑定属性使用步骤:1、引入ref2、给变量附初始值,ref(1)3、 重点!!!变量的值要用 .value来获取 ,例如a,a.value
reactive()函数这也是和ref一样使得我们的变量拥有了双向绑定属性, 这个函数接收一个对象作为参数使用步骤:1、引入ref2、 重点!!!给变量附初始值,reactive({value:1}) 接收对象作为参数的时候,应该这样写打印一个对象或者是数组,根据控制台输出, 可以看到 变量是一个Proxy挂钩, 挂在一个target 对象上3、变量的值要用 .value来获取 ,例如a,a.value
并发请求和promise问题
所谓并发请求,即有待请求接口100个,限制每次只能发出10个。即同一时刻最多有10个正在发送的请求。
每当10个之中有一个请求完成,则从待请求的接口中再取出一个发出。保证当前并发度仍旧为10。
直至最终请求完成。
设计思路简单思路如下:(假设并发请求函数名字为limitedRequest)
设定一个数组(命名为:pool),用于后续Promise.all的使用当limitedRequest被调用的时候,首先一次性发出10个请求,并放入到pool中
// 模仿一个fetch的异步函数,返回promise function mockFetch(param) {return new Promise((resovle) => {setTimeout(() => {resovle(param);}, 2000);}); } function limitedRequest(urls, maxNum) {const pool = [];// 处理maxNum比urls.length 还要大的情况。const initSize = Math.min(urls.length, maxNum);for (let i = 0; i < initSize; i++) {// 一次性放入初始的个数pool.push(run(urls.splice(0, 1)));}// r 代表promise完成的resolve回调函数// r 函数无论什么样的结果都返回promise,来确保最终promise.all可以正确执行function r() {console.log('当前并发度:', pool.length);if (urls.length === 0) {console.log('并发请求已经全部发起');return Promise.resolve();}return run(urls.splice(0, 1));}// 调用一次请求function run(url) {return mockFetch(url).then(r);}// 全部请求完成的回调Promise.all(pool).then(() => {console.log('请求已经全部结束');}); } // 函数调用 limitedRequest([1, 2, 3, 4, 5, 6, 7, 8], 3); # 最终返回结果 $ node .\src\views\doc\detail\index.js 当前并发度: 3 当前并发度: 3 当前并发度: 3 当前并发度: 3 当前并发度: 3 当前并发度: 3 并发请求已经全部发起 当前并发度: 3 并发请求已经全部发起 当前并发度: 3 并发请求已经全部发起 请求已经全部结束
promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
实正常的业务中,按理来说 是一个按钮控制一个请求,处理一项业务。但有的时候还是会出现 一个按钮控制多个并发请求的业务,这几个请求之间还没有关联。但问题是,你得知道所有的请求处理完成之后,要给用户一个反馈的。这个时候 如果使用传统的ajax请求的话,好像我们不知道啥时候 所有的并发请求都处理完了,或者是 那个请求出了问题 然后报出对应的错误。
其实通过一个系统的设计还是能用es5的方式处理这些并发请求的
但今天就讲解下 promise.all的用法,让你快速实现呢,promise这个东西出来也好几年了,建议能用新的语法还是用新的语法,毕竟下一代标准呢,大不了用babel转下,问题不大。
Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,
也不一定非得是数组, 可能es5写习惯的程序员还是比较喜欢用数组的方式
var promiseAll = new Set();function s1() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('1111')}, 1000)}) }function s2() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('22222222')}, 2000)}) }function s3() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('333')}, 3000)}) } promiseAll.add(s3()); promiseAll.add(s2()); promiseAll.add(s1());Promise.all(promiseAll).then(res => {// 输出结果 [ '333', '22222222', '1111' ]console.log(res); }).catch((err) => {console.log(err); })
先声明一个可迭代的对象,然后吧后续的promise对象推入进去,不要只是把函数名字推进去,人家要的promise对象,也就是你函数的执行结果,要不你就直接放入一个promise对象
Promise.all 当且仅当传入的可迭代对象为空时为同步
还有就是对失败的处理, 只要有一个失败了,那么整个promise就会转变成失败状态但是执行成功的还是成功执行的,只是失败的那个失败,而且整个状态也变成失败,会被catch捕获到。
vue中$nexttick()使用场景
$nextTick 是 vue 中的异步更新,在官网是这样解释的:Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0)代替。作用和用法:用法:接受一个回调函数作为参数,它的作用是将回调函数延迟到一次DOM更新之后的操作,如果没有提供回调函数参数且在支持Promise的环境中,nextTick将返回一个Promise。
使用场景:开发过程中,开发者需要在更新完数据之后,需要对新DOM做一些操作,其实我们当时无法对新DOM进行操作,因为这时候还没有重新渲染。
flex布局
1. flex-direction 主轴方向 2. flex-wrap 主轴一行满了换行 3. flex-flow 1和2的组合 4. justify-content 主轴元素对齐方式 5. align-items 交叉轴元素对齐方式//单行 6. align-content 交叉轴行对齐方式//多行
row(默认值):主轴为水平方向,起点在左端。row-reverse:主轴为水平方向,起点在右端。column:主轴为垂直方向,起点在上沿。column-reverse:主轴为垂直方向,起点在下沿。
justify-content 主轴元素对齐方式这里是元素的对齐,区别于刚刚的「方向」
flex-start (默认)靠着main-start对齐//参考常见术语(一般是左方向)flex-end 靠着main-end对齐//参考常见术语(一般是右方向)center 靠着主轴居中对齐//一般就是居中对齐space-between 两端对齐,靠着容器壁,剩余空间平分space-around 分散对齐,不靠着容器壁,剩余空间在每个项目二侧平均分配space-evenly 平均对齐,不靠着容器壁,剩余空间平分
微信小程序支付流程
微信小程序支付完整流程(前端)_雨滴2000的博客-CSDN博客v73insert_down3,201v4add_ask,239v2insert_chatgpt&utm_term=%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%94%AF%E4%BB%98%E6%B5%81%E7%A8%8B%20&spm=1018.2226.3001.4187s
单点登录、多点登录、token无感刷新
vue中的data为啥是一个函数
如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data(类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据)Object是引用数据类型,里面保存的是内存地址,单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。因为组件是可以复用的, JS里对象是引用关系, 如果组件data是一个对象, 那么在子组件中的data属性值会互相污染, 产生副作用。
所以一个组件的data选项必须是一个函数, 因此每个实例可以维护一份被返回对象的独立拷贝。new Vue的实例是不会被复用的,因此不存在以上问题。
js中this指向的五种情况
函数传参简单数据类型和引用数据类型的区别
vue中组件和插件有什么区别
编写组件编写一个组件,可以有很多方式,我们最常见的就是 vue 单文件的这种格式,每一 vue 文件我们都以看成是一个组件编写插件vue 插件的实现应该暴露一 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是个可选的选项对象
组件注册vue 组件注册主要分为全局注册与局部注册全局注册通过 Vue.component 方法,第一个参数为组件的名称,第二个参数为传入的配置项 局部注册只需在用到的地方通过 components 属性注册一个组件
插件注册
插件的注册通过 Vue.use()的方式进行注册(安装),第一个参数为插件的名字,第二个参数是可选择的配置项注意的是:注册插件的时候,需要在调用 new Vue() 启动应用之前完成
Vue.use 会自动阻止多次注册相同插件,只会注册一次
使用场景:
组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身
vue中的祖孙级别通信
数组去重的方法 数组排序 数组新增 合并数组 es6
map set
set和map是es6新增的集合类型。Set创建:可以用new set()进行创建。方法:add,添加一个集合到末尾;has判断集合中是否存在对应的数据。clear清空集合。set也可以用于去重,new set([]),原理主要根据Object.is进行判断。循环:for…of 或者 集合也可以用forEach进行循环,和循环数组的组别在于,forEach的第二个参数,因为set中不存在下标,所以第二个参数也是集合的元素。
map:创建:new Map()用于存储多个键值对数据。在map出现之前使用对象存储键值对,但是有几个问题,键名只能是字符串,获取数据的数量不方便。键名容易和原型上的名称冲突。当描述一个整体,且属性不能随意添加删除时,适合用对象,当存储一些数据可以随意添加删除时可以用map。方法:可以用size或者键的数量。可以用set设置一个键值对,如果存在就修改它的值,不存在就添加。判断方法和set一样。delete用于删除指定的键值。遍历:遍历也可以用forEach和for of
vue组件第一次加载 会触发那些生命周期
当页面第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子函数
实现深拷贝和浅拷贝
v-model双向绑定
v-model绑定在表单上时,v-model其实就是v-bind绑定value和v-on监听input事件的结合体
v-model绑定在表单上时,v-model其实就是v-bind绑定value和v-on监听input事件的结合体v-model绑定在表单上时,v-model其实就是v-bind绑定value和v-on监听input事件的结合体
在父组件内给子组件标签添加 v-model ,其实就是给子组件绑定了 value 属性子组件内使用 prop 创建 创建 value 属性可以拿到父组件传递下来的值,名字必须是 value。子组件内部更改 value 的时候,必须通过 $emit 派发一个 input 事件,并携最新的值v-model 会自动监听 input 事件,把接收到的最新的值同步赋值到 v-model 绑定的变量上
vue2响应式原理
通过 Object.defineProperty 遍历对象的每一个属性,把每一个属性变成一个 getter 和 setter 函数,读取属性的时候调用 getter,给属性赋值的时候就会调用 setter.
当运行 render 函数的时候,发现用到了响应式数据,这时候就会运行 getter 函数,然后 watcher(发布订阅)就会记录下来。当响应式数据发生变化的时候,就会调用 setter 函数,watcher 就会再记录下来这次的变化,然后通知 render 函数,数据发生了变化,然后就会重新运行 render 函数,重新生成虚拟 dom 树。
深入了解:
我们要明白,响应式的最终目标:是当对象本身或对象属性发生变化时,会运行一些函数,最常见的就是 render 函数。不是只有 render,只要数据发生了变化后运行了一些函数,就是响应式,比如 watch。
换肤功能
vuex跟pina区别
1)pinia它没有mutation,他只有state,getters,action【同步、异步】使用他来修改state数据 (2)pinia他默认也是存入内存中,如果需要使用本地存储,在配置上比vuex麻烦一点
(3)pinia语法上比vuex更容易理解和使用,灵活。 (4)pinia没有modules配置,没一个独立的仓库都是definStore生成出来的
(5)pinia state是一个对象返回一个对象和组件的data是一样的语法Vuex 和 Pinia 的优缺点Pinia的优点
完整的 TypeScript 支持:与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易
极其轻巧(体积约 1KB)store 的 action 被调度为常规的函数调用,而不是使用 dispatch 方法或 MapAction 辅助函数,这在 Vuex 中很常见支持多个Store支持 Vue devtools、SSR 和 webpack 代码拆分Pinia的缺点
不支持时间旅行和编辑等调试功能Vuex的优点
支持调试功能,如时间旅行和编辑适用于大型、高复杂度的Vue.js项目Vuex的缺点
从 Vue 3 开始,getter 的结果不会像计算属性那样缓存Vuex 4有一些与类型安全相关的问题何时使用Pinia,何时使用Vuex个人感觉:,由于Pinea是轻量级的,体积很小,它适合于中小型应用。它也适用于低复杂度的Vue.js项目,因为一些调试功能,如时间旅行和编辑仍然不被支持。将 Vuex 用于中小型 Vue.js 项目是过度的,因为它重量级的,对性能降低有很大影响。因此,Vuex 适用于大规模、高复杂度的 Vue.js 项目。
pinia和vuex在vue2和vue3都可以使用,一般来说vue2使用vuex,vue3使用pinia。
相对定位、绝对定位与固定定位
相对定位(relative) 相对定位是元素在移动位置的时候,是相对于它原来的位置来说的;
特点:
它是相对于自己原来的位置来移动的(移动位置的时候是参照点是自己原来的位置); 原来在标准流的位置继续占有,后面的盒子仍然以标准流的方式对待它(不脱标,继续保留原来的位置)
绝对定位(absolute)(子绝父相) 绝对定位是元素在移动位置的时候,是相对于它父级元素来说的。
特点:
如果没有父级元素或者父级元素没有定位,则以浏览器为准定位(Document文档); 如果父级元素有定位(相对、绝对、固定定位),则以最近一级的有定位父级元素为参考点移动位置; 绝对定位不再占有原先的位置(脱标)1.3 固定定位(fixed)
固定定位是元素固定与浏览器可视区的位置。
主要使用场景:可以在浏览器页面滚动时元素的位置不会改变.
特点:
-
以浏览器的可视窗口为参照点移动元素;
-
跟父元素没有任何关系;
-
不随滚动条滚动;
-
固定定位不再占有原先的位置
mutation action区别
mutation和action都是在Vuex中使用的概念。mutation用于改变store中的状态,而且只能通过mutation来改变状态。action则可以包含异步操作,可以在其中调用mutation方法来改变状态。换句话说,mutation是同步操作,而action是异步操作
分发调用action:
this.$store.dispatch('action中的函数名',发送到action中的数据)
在组件中提交 Mutation:this.$store.commit(“mutation函数名”,发送到mutation中的数据)
post上传文件跟json上传文件有什么区别
上传文件时,post请求需要将文件通过二进制流的形式上传,而json请求需要将文件转换为base64格式后再上传。此外,post上传文件通常用于上传大型文件,而json上传文件则适用于小型文件。
vuex的工作流程
Vuex是Vue.js状态管理的一个库。其工作流程大致如下:
-
创建一个store,store中包括state(状态)、mutations(改变状态的方法)、actions(异步方法)和getter(计算状态属性)。
-
Vue组件通过dispatch一个action来触发mutations的方法去修改state的值。
-
state值的变化会自动映射到组件的视图中。
-
通过getter可以对状态进行计算并输出,便于组件进行复杂的数据处理。
总体来说,Vuex提供了一种集中管理和控制state状态的方式,并且使Vue组件间数据的传递更加简单明了。
首先通过dispatch去提交一个actions,在actions接收到这个事件后,在actions种进行一些异步或同步操作,根据情况分发给不同的mutations,actions通过commit触发mutations,然后mutations去更新state,在state更新后,就会通知vue进行渲染。步骤如下:
1.通过dispatch去提交一个actions
2.在actions接收到事件后,在actions中执行一些同步或异步操作
3.根据不同的情况分发给不同的mutations,actions通过commit触发mutations
4.mustations在触发后就会去更新state
5.在state更新完毕后,就会通知vue进行渲染
computed和watch
computed和watch都是Vue中的响应式属性,但是它们的作用和使用方式有所不同。
computed是计算属性,它会根据依赖的数据动态计算出一个新的值,并且缓存计算结果,只有当依赖的数据发生变化时才会重新计算。computed的使用方式是在Vue实例中定义一个computed属性,这个属性是一个函数,函数返回的值就是计算结果。
watch是观察者,它会监听一个数据的变化,当这个数据发生变化时,执行相应的回调函数。watch的使用方式是在Vue实例中定义一个watch属性,这个属性是一个对象,对象的属性是要监听的数据,属性的值是一个回调函数,当监听的数据发生变化时,会执行这个回调函数。
总的来说,computed适用于计算复杂的数据,watch适用于监听数据变化并执行相应的操作功能:computed是计算属性;watch是监听一个值的变化执行对应的回调2、是否调用缓存:computed函数所依赖的属性不变的时候会调用缓存;watch每次监听的值发生变化时候都会调用回调3、是否调用return:computed必须有;watch可以没有4、使用场景:computed当一个属性受多个属性影响的时候;例如购物车商品结算;watch当一条数据影响多条数据的时候,例如搜索框5、是否支持异步:computed函数不能有异步;watch可以
如何判断一个变量是不是一个数组
使用Array.isArray 判断,如果返回 true, 说明是数组.第一种方法的原理如下:Array.isArray = function(value) { return Object.prototype.toString.call(value) === '[object Array]';}简单理解就是运用对象原型的toString方法将Array转换为字符串,之后进行比对,下面我们来详细分析下各个部分的组成。Object.prototype.toString代表这个toString方法本来是对象原型上面的,之后call方法将toString方法中的this转换为参数value,这样传进的参数value(实际上对于此方法应该是个数组[])就可以通过原型的方法来转换为字符串
使用 instanceof Array 判断,如果返回true, 说明是数组使用 Object.prototype.toString.call判断,如果值是 [object Array], 说明是数组通过 constructor 来判断,如果是数组,那么arr.constructor === Array.(不准确,因为我们可以指定obj.constructor = Array)
Promise 和 async/await
Promise 和 async/await 是 JavaScript 中异步编程的两种常用方式。
Promise 是 JavaScript 中一种支持链式调用的对象,用于描述一个异步操作的最终状态(成功或失败)。它提供了 then 方法和 catch 方法来处理异步操作的结果。
async/await 是 JavaScript 中的语法糖,它在语法上更像同步代码,使得异步代码的编写更加简洁易懂。await 关键字用于等待一个 Promise 对象的返回结果,而 async 关键字用于定义一个异步函数。
简单来说 Promise 是对象,async/await 是对Promise对象的封装。