vue的双向绑定原理是什么?里面的关键点在哪里?
Vue数据双向绑定原理是通过数据劫持结合发布者-订阅者模式的方式来实现的,首先是对数据进行监听,然后当监听的属性发生变化时则告诉订阅者是否要更新,若更新就会执行对应的更新函数从而更新视图。
通过Object.defineProperty()来劫持各个属性的setter, getter,在数据发生变动时通知Vue实例,触发相应的getter和setter回调函数。
其关键点是 Object.defineProperty() 方法,这个方法内有三个参数,分别为 obj(要定义其上属性的对象)、prop(要定义或修改的属性)、descriptor(具体的改变方法)简单来说,就是用这个方法定义一个值,当调用时我们使用了它里面的get方法;当我们给这个属性赋值时,同时又调用了里面的set方法
实现水平垂直居中的方式?
很多,但需要具体问题具体分析,常用的如:
flex 布局实现水平垂直居中,display: flex;justify-content: center;align-items: center;;
绝对定位的方式;
通过父元素使用 display: table 子元素使用 vertical-align: middle 实现水平垂直居中(水平居中采用 margin)
常用伪元素有哪一些?
::before 该伪元素定义在元素之前添加内容;
::after 该伪元素定义在元素之后添加内容;
::first-line 该伪元素向文本的首行添加特殊样式;
::first-letter 该伪元素向文本的第一个字母添加特殊样式;
移动端如何适配不同屏幕尺寸?
px 为主,搭配 vw/vh、媒体查询与 flex 进行布局(推荐):
编写 <meta> 标签设置 viewport 的内容 width=device-width,让网页宽度等于视窗宽度;
在 css 中使用 px;
在适当的场景使用flex布局,或者配合vw进行自适应;
在跨设备类型的时候(pc <-> 手机 <-> 平板)使用媒体查询;
在跨设备类型如果交互差异太大的情况,考虑分开项目开发;
关于布局,我们可以使用 flex 实现弹性布局,当实现特定宽高元素时,可以适当的使用 vw/vh,当特定的值使用 vw/vh 计算复杂或存在误差时,也可以使用 rem 。
flexible 方案:
使用rem,它是CSS3新增的一个相对单位;
vw/vh 布局:
vw/vh 方案与 rem 方案类似,都是将页面分成一份一份的,只不过 vw/vh 是将页面分为 100 份,1vw = device-width/100
本地存储有哪一些?他们三者有什么区别?
localStorage: 数据会永久存储,除非代码或手动删除(5M)
sessionStorage: 数据只存在于当前会话,浏览器关闭则清空(5M)
cookie: 设计初衷是用来和服务器通讯,而不是本地存储,他只是被‘借用’到本地存储。(4k),Cookie有一些缺点:存储空间小,最大4k、http请求时需要发送到服务器,增加请求数据量、只能用document.cookie=’…’ 来修改,太过简陋
JS的数据类型?如何判断js的数据类型?
基本数据类型(值类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol;
引用数据类型:对象(Object)、数组(Array)、函数(Function)。
JS数据类型判断:
typeof用以获取一个变量或者表达式的类型,一般用于判断值类型(基本类型)
instanceof 运算符是用来判断一个对象是否在其原型链原型构造函数的属性,所以在比较对象(引用类型)时才有意义
通用完美的判断方法:Object.prototype.toString.call()
说一下ES6的新特性有哪些?
ES6 引入了class(类)
模块化(Module),导出(export)、导入(import)
箭头(Arrow)函数
函数参数默认值
模板字符串
解构赋值
let、const、var三者有什么区别?
主要是作用域(全局作用域、局部作用域),es6新增let、const
var声明的变量属于函数作用域,let和const声明的变量属于块级作用域;
var存在变量提升现象,而let和const没有;
var变量可以重复声明,而在同一块级作用域,let变量不能重新声明,const变量不能修改(基本类型),对于引用类型,仍然可以修该值。
数组去重有哪些办法?
利用reduce+includes (推荐)
reduce(function(total, currentValue, currentIndex, arr), initialValue)方法,可以接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
includes()方法用来判断一个数组是否包含一个指定的值,如果是返回true,否则false。
function unique(arr) { return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []); }
利用ES6 Set去重,但无法去掉{}空对象:
function unqiue(arr) { return Array.from(new Set(arr))}
或者
var Arr = [...new Set(arr)]
利用for嵌套for,然后splice去重(ES5中最常用):
双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
利用Map数据结构去重(推荐):
创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。
利用filter(推荐):
function unique(arr) { return arr.filter(function(item, index, arr) { //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素 return arr.indexOf(item) === index; }); }
说一下深拷贝和浅拷贝,如何自己实现一个深拷贝?
浅拷贝:如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:不管原数据中值是什么类型的数据,拷贝后的新数据跟原数据是相互独立,没有关联的
实现深拷贝
1.利用json数据和json字符串之间的转换(缺点: 无法实现对 对象中 方法 的拷贝,会显示为undefined)
JSON.parse(JSON.stringify(obj))
2.循环赋值和使用递归完整实现;
3.使用ES6新特性,解构赋值的方式(推荐)
对象:let {...obj2} = obj
数组: arr2 = arr.concat(); arr2 = arr.slice(0);
let arr2 = [...arr]
Vue的生命周期有哪一些?说一下它们每个阶段做什么操作?
8个,vue2的生命周期
创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象Data都为undefined,还未初始化。created阶段,vue实例的数据对象data有了,el还没有
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
activated: keep-alive组件激活时调用
deactivated: keep-alive组件销毁时调用
errorCaptured: 当捕获一个来自子孙组件的错误时被调用
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
具体说明:
beforeCreate() 创建前 在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。在此生命周期执行的时候,data和methods中的数据都还没有初始化。不能在这个阶段使用data中的数据和methods中的方法
created()被创建 data 和 methods都已经被初始化好了,可以调用了
beforeMount() 挂载前 在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的
mounted()已挂载 Vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
beforeupdate()更新前 页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步
updated()更新 页面显示的数据和data中的数据已经保持同步了,都是最新的
beforeDestroy() 销毁前 Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁
destroyed()被销毁 这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
vue组件通讯方式有哪一些?
props/$emit(父传子,子传父,子调父)
$bus(任意组件传值,任意组件调方法)
$bus.$emit();
$bus.$on();
ref/$refs(父调子)
$parent/ $children(父子组件)
provide/reject(跨级组件)
Vuex(任意组件)
localStorage/sessionStorage(任意组件)
$attrs/$listeners(跨级组件)
Vuex有几个属性及作用?
state:用于数据的存储,是store中的唯一数据源
getters:从基本数据(state)派生的数据,相当于store的计算属性;getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件;this.$store.commit()
actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作;this.$store.dispatch()
odules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护
Vuex的应用及 Vue内部的监听数据变化的机制
方法一、可以在组件中通过组件的 watch方法来做, 因为组件可以将state数据映射到 组件的计算属性上,然后 监听 映射的计算属性即可。
方法二、vuex中store对象本身提供了watch函数 ,可以利用该函数进行监听。
watch(fn: Function, callback: Function, options?: Object): Function
响应式地侦听 fn 的返回值,当值改变时调用回调函数。fn 接收 store 的 state 作为第一个参数,其 getter 作为第二个参数。最后接收一个可选的对象参数表示 Vue 的 vm.$watch 方法的参数。
created () { this.$store.watch((state, getters) => { return state.count }, () => { this.changeCount++ }) }
Vue的监听属性和计算属性有什么区别?
computed特性
监测的是依赖值;
计算属性不能执行异步任务,计算属性必须同步执行;
具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再次执行函数
watch特性
监测的是属性值;
无缓存性,页面重新渲染时值不变化也会执行
说一下防抖和节流。怎么实现?
防抖:
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间;
实现方式:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法;
缺点:如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟。
节流:
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率;
实现方式:每次触发事件时,如果当前有等待执行的延时函数,则直接return 。
Vue的导航守卫有哪一些?
全局前置守卫:beforeEach(to, from, next),当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中;
全局后置守卫:afterEach(to, from),它是在路由跳转完成后触发,它发生在 beforeEach 之后 beforeRouteEnter(组件内守卫) 之前, 因为是跳转完成后触发,所以没有 next 参数。
全局解析守卫“beforeResolve”,在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用;
路由独享的守卫“beforeEnter”;
组件内的守卫
“beforeRouteEnter”,在渲染该组件的对应路由被 confirm 前调用,不!能!获取组件实例 `this`,因为当守卫执行前,组件实例还没被创建;
“beforeRouteLeave”导航离开该组件的对应路由时调用,可以访问组件实例 `this`;
“beforeRouteUpdate”在当前路由改变,但是该组件被复用时调用,可以访问组件实例 `this`。
你的登录拦截怎么实现的?
路由拦截(路由守卫)
首先在定义路由的时候就需要多添加一个自定义字段 requireAuth,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由, 否则就进入登录页面。
定义完路由后,利用 vue-router 提供的钩子函数 beforeEach() 对路由进行判断。
http拦截器
路由拦截只是简单的前端路由控制,并不能真正阻止用户访问需要登录权限的路由。还有一种情况便是:当前token失效了,但是token依然保存在本地。这时候你去访问需要登录权限的路由时,实际上应该让用户重新登录。
这时候就需要结合 http 拦截器 + 后端接口返回的http 状态码来判断。
axios 的拦截器可通过配置http response inteceptor,当后端接口返回401 Unauthorized(未授权),让用户重新登录。
有用过图表吗?用的多吗?
· Echarts (广泛)
· antv-g2plot
· Chart.js
· Chartist
· FlexChart
· NVD3
· C3.js
· TauCharts
· ReCharts
· Flot
闭包是什么?如何实现?
闭包是函数和声明该函数的词法环境的组合(就是能够读取其他函数内部变量的函数)。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
创建闭包最常见方式,就是在一个函数内部创建另一个函数。
闭包的this指向的是window
最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
注意:闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
Vue2.0和vue3.0有什么区别?
生命周期
Vue3 在组合式API(Composition API,下面展开)中使用生命周期钩子时需要先引入,而 Vue2 在选项API(Options API)中可以直接调用生命周期钩子
Tips: setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地去定义。
多根节点
Vue3 支持多个根节点,也就是 fragment ,vue2不支持
Composition API
Vue2 是选项API(Options API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。
Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。
异步组件(Suspense)
Vue3 提供 Suspense 组件,允许程序在等待异步组件加载完成前渲染兜底的内容,如 loading ,使用户的体验更平滑。使用它,需在模板中声明,并包括两个命名插槽:default 和 fallback。Suspense 确保加载完异步内容时显示默认插槽,并将 fallback 插槽用作加载状态。
Teleport
Vue3 提供 Teleport 组件可将部分 DOM 移动到 Vue app 之外的位置。比如项目中常见的 Dialog 弹窗。
响应式原理
Vue2 响应式原理基础是 Object.defineProperty;Vue3 响应式原理基础是 Proxy。
虚拟DOM
Vue3 相比于 Vue2,虚拟DOM上增加 patchFlag 字段
事件缓存
Vue3 的cacheHandler可在第一次渲染后缓存我们的事件。相比于 Vue2 无需每次渲染都传递一个新函数。加一个 click 事件。
Diff算法优化
patchFlag 帮助 diff 时区分静态节点,以及不同类型的动态节点。一定程度地减少节点本身及其属性的比对。
打包优化和typescript支持
Vue常用的指令有哪些?
v-model多用于表单元素实现双向数据绑定
v-bind 动态绑定
v-for 动态渲染,在写v-for的时候,都需要给元素加上一个key属性,key的主要作用就是来提高渲染性能的(能够更高效的更新虚拟 DOM)
v-show 显示内容,通过设置DOM元素的display样式,block为显示,none为隐藏
v-if 显示与隐藏,是通过控制dom节点的存在与否来控制元素的显隐
v-on 给标签绑定函数
v-text 用于解析文本,但不能解析标签,并且会覆盖元素内部原有的内容!
v-html可以把带有标签的字符串,渲染成真正的 HTML 内容!
v-If和v-show有什么区别?
v-if 是动态添加,当值为 false 时,是完全移除该元素,即 dom 树中不存在该元素。
v-show 仅是隐藏 / 显示,值为 false 时,该元素依旧存在于 dom 树中。若其原有样式设置了 display: none 则会导致其无法正常显示。
v-for为什么要加一个key?
设置key值是为了提高动态加载的效率。
因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM。
key主要用来做dom diff算法用的,diff算法是同级比较,比较当前标签上的key还有它当前的标签名,如果key和标签名都一样时只是做了一个移动的操作,不会重新创建元素和删除元素。
有增删操作的需要用唯一标识(如:id)作为key值
你是如何封装一个组件的?
vue将组件分为全局组件和局部组件;可以分为函数式(无状态)组件和普通(无状态)组件
开放式问题:
有自己从0到1搭建过项目吗?
有用过uni-app吗?
你会写后台吗?有搞过服务端渲染吗?
说一下你项目中遇到的难点,如何解决?
Url到浏览器的一个过程有哪些步骤?
浏览器查看缓存,如果请求资源在缓存中失效或者资源未缓存,则发起新请求,跳转到转码步骤
DNS解析——解析域名,获取对应的ip地址
TCP连接——TCP三次握手
浏览器发送http请求
服务器处理请求并返回http报文
浏览器解析返回的数据并渲染页面
断开连接:TCP四次挥手
如何实现小程序的request封装及拦截?
TBD
在vue的项目应用中,不使用框架,怎么封装?
TBD
什么是Js原型?原型链是什么?
在js中,每个构造函数内部都有一个prototype属性,该属性的值是个对象,该对象包含了该构造函数所有实例共享的属性和方法。当我们通过构造函数创建对象的时候,在这个对象中有一个指针,这个指针指向构造函数的prototype的值,我们将这个指向prototype的指针称为原型。
或者用另一种简单却难理解的说法是:js中的对象都有一个特殊的[[Prototype]]内置属性,其实这就是原型
原型链就是通过原型组成的一条查找链。
用闭包的原理做过哪些封装或应用?
最常用的:
防抖函数和节流函数;
getter和setter的封装函数(vue响应式即是通过闭包实现)
Vue中data返回值,computed返回值都是闭包的应用
作用域是什么?
作用域是在运行时代码中的某些特定部分的变量,函数和对象的可访问性。(作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。)
作用域与执行上下文:
JavaScript 属于解释型语言,JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:
解释阶段:词法分析、语法分析、作用域规则确定
执行阶段:创建执行上下文、执行函数代码、垃圾回收
JavaScript 解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是 this 的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。
作用域和执行上下文之间最大的区别是:
执行上下文在运行时确定,随时可能改变;
作用域在定义时就确定,并且不会改变。
一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。
操作数组的方式有哪些?
isArrary():判断参数是不是数组,返回布尔值Array.isArrary();
Array.from():对伪数组或可迭代对象(包括arguments Array,Map,Set,String…)转换成数组对象
toString():把数组转成字符串------数组名.toString–对象类型数组不能直接转换;
valueOf():返回数组本身-----数组名.valueOf();
push():在数组末尾添加元素;
unshift():在数组开头添加元素;
pop():删除数组末尾的值,并返回删除的值;
shift():删除数组第一个的值,并返回删除的值;
reverse(): 翻转数组;
join(‘分割符号’):以所用的符号对数组进行拼接方法;
sort():排序;
concat(数组名1,…数组名N):把多个数组拼接成一个数组 并且不影响原有的数组;
slice(开始索引,结束索引):根据索引截取里面参数(下标),包括左边不包括右边;如果第二个数是正数,返回从开始位置到结束的位置(但不包括最后结束的位置);如果第二个数是负数,返回的是开始位置到结束位置减一;如果第二个数是0或者两个数都是负数,则返回[]。
splice(index, howmany, item1, ....., itemX):向/从数组添加/删除项目,并返回删除的项目,会改变原始数组.
index必需:整数,指定在什么位置添加/删除项目,使用负值指定从数组末尾开始的位置。howmany 可选。要删除的项目数。如果设置为 0,则不会删除任何项目。item1, ..., itemX 可选。要添加到数组中的新项目。
indexOf():某个元素从左到右第一次出现的下标;
lastIndexOf:某个元素从左到右最后一次出现的下标;
filter():过滤满足条件为true的元素,返回一个新数组,不会影响原数组,筛选单个值;
find():查找数组中满足条件的第一个元素,返回该元素,没找到输出undefinded ;
findIndex():查找数组中满足条件的第一个元素,返回该元素下标,没找到输出-1 ;
includes():判断数组是否包含某个元素,不用回调函数(不用函数作为参数)不包含返回false ;
map(function(v,index,arr){ }):返回原来的数组操作之后的数据,返回新数组不改变原数组;
forEach():方法对数组的每个元素执行一次提供的函数;
every():全部满足条件 全部都满足则返回true,否则返回false 相当于&& ;
some():只要有一个满足就返回true 全部都不满足则返回false 相当于|| ;
reduce(function(返回值,元素,下标,当前数组){},返回值的初始值):reduce()方法接收一个函数累加器,数组中的每个值(从左到右)开始缩减,最后累计为一个值。
0.1 + 0.2 等于 0.3吗?为什么?如何解决?
不等于(两个值转化为二进制是循环小数,计算过程会精度丢失),
解决:
一、先将其转化为整数,运算之后,然后再将其转化为小数;
二、用Math.js等数学计算库来解决。
keep-alive是什么?有哪几个生命周期阶段?
<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。作用:缓存组件内部状态,避免重新渲染。
当引入keep-alive的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。
用法:
keep-alive可以接收3个属性做为参数进行匹配对应的组件进行缓存:
include包含的组件(可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存)
exclude排除的组件(以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)
max缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数)
判断一个变量是否是数组,有哪些办法?
调用Object.prototype.toString.call(),返回true,则说明该变量是数组类型;反之,说明该变量不是数组类型。console.log(Object.prototype.toString.call(arr) === '[object Array]')
用 instanceof 运算符, 该运算符左边是我们想要判断的变量, 右边则是我们想要判断的对象的类型
console.log(typeof arr)
利用构造函数来判断他的原型是否为Array:
console.log(arr.constructor === Array)
利用的一个专门的方法 isArray(), 用法:Array.isArray(变量),返回true,则说明该变量是数组类型;反之,说明该变量不是数组类型
console.log(Array.isArray(arr))
通过对象的原型方式来判断
console.log(arr.__proto__ === Array.prototype)
通过 Object.getPrototypeOf()来判断是否为数组类型
function unqiue(arr) { return Array.from(new Set(arr))}0
通过 isPrototypeOf() 方法来判断是否为数组类型
function unqiue(arr) { return Array.from(new Set(arr))}1
判断一个变量是否是对象,有哪些办法?
判断字符串得到string,数字和NaN得到number,函数会得到function等,但是判断数组,对象和null时都会得到object,这就是typeof的局限性,并不能准确的判断该变量的"真实身份"。
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,意思就是该变量通过原型链上能否找到构造函数的prototype 属性
function unqiue(arr) { return Array.from(new Set(arr))}2
Object.prototype.toString.call()方法可以精准判断变量类型,它返回[object constructorName]的字符串格式
constructor:
console.log(arr.constructor === Array); //true console.log(arr.constructor === Object); //false console.log(obj.constructor === Object); //true
对象常用方法有哪些?
for in 遍历对象的属性
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致;
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。将现有对象作新对象的 proto。
Object.assign() 方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。返回目标对象。
Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。Object.assign(target, …sources)
Object.hasOwnProperty() 返回一个布尔值,用来检测对象自身属性中是否具有指定的属性。和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
Object.isPrototypeOf()用于测试一个对象是否存在于另一个对象的原型链上。
a.isPrototypeOf(b);判断的是a对象是否存在于b对象的原型链之中
A instanceof B 判断的是B.prototype是否存在与A的原型链之中
Object.defineProperty() 劫持变量的set和get方法,将属性添加到对象,或修改现有属性的特性
创建一个空数组/空对象有哪些方式?
let emptyObj1 = {};
let emptyObj2 = new Object();
let emptyObj2 = Object.create(Object.prototype);
let arr1 = new Array()
let arr2 = []
Set和Map各是什么?
Map是键值对,Set是值的集合,当然键和值可以是任何的值;
Map可以通过get方法获取值,而set不能因为它只有值;
都能通过迭代器进行for...of遍历;
Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储
map和set都是stl中的关联容器,map以键值对的形式存储,key=value组成pair,是一组映射关系。set只有值,可以认为只有一个数据,并且set中元素不可以重复且自动排序。
Map对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数。
Map对象的属性:
size:返回Map对象中所包含的键值对个数
Map对象的方法:
set(key, val): 向Map中添加新元素
get(key): 通过键值查找特定的数值并返回
has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false
delete(key): 通过键值从Map中移除对应的数据
clear(): 将这个Map中的所有元素删除
Set对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 本身是一个构造函数,用来生成Set 数据结构。Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
Set实例对象的属性
size:返回Set实例的成员总数
Set实例对象的方法
add(value):添加某个值,返回 Set 结构本身(可以链式调用)。
delete(value):删除某个值,删除成功返回true,否则返回false。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
介绍一下promise。
Promise是JS中进行异步编程的新的解决方案;指定回调函数的方法更加灵活;支持链式调用 ,可以解决回调函数问题。
Promise通常会解决三种问题
(1)链式回调
(2)同时发起几个异步请求,谁先有结果就拿谁的
(3)发起多个请求,等到所有请求后再做下一步处理
Promise.all 方法:
说明:只有所有的 promise 都成功才成功, 只要有一个失败了就直接失败;返回一个新的 promise(成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值)。
`Promise.all(promises) => {} ` promises: 包含 n 个 promise 的数组
Promise.race 方法:
说明:第一个完成的 promise 就是最终的结果状态;返回一个新的 promise。
`Promise.race(promises) => {} ` promises: 包含 n 个 promise 的数组
Promise.allSettled 方法:
可用于并行执行独立的异步操作,并收集这些异步操作的结果。
函数接受一个 promise 数组(或通常是一个可迭代的)作为参数,当所有输入 promises 都被履行或拒绝时,statusesPromise 会解析为一个具有其状态的数组。 返回的承诺总是以一系列状态实现,无论是否有一些(或者全部)输入承诺被拒绝。
Promise.allSettled() 和 Promise.all() 的最大不同:Promise.allSettled() 永远不会被 rejected 。
如何改变一个函数a的上下文?
call()、apply()、bind()
Call和applay有什么区别?
没有本质区别,参数形式不同
Evenbus是什么东西?
父子组件生命周期执行顺序是怎么样的?
创建与挂载
父beforeCreate > 父created > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > 父mounted
更新
父beforeUpdate > 子beforeUpdate > 子updated > 父updated
销毁
父beforeDestroy > 子beforeDestroy > 子destroyed > 父destroyed
mixins有几个生命周期阶段?
mixin的beforeCreate > 父beforeCreate > mixin的created > 父created > mixin的beforeMount > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > mixin的mounted >父mounted
弹性布局,一行两列,一列固定宽,如何实现?
对父元素设置弹性flex,使两个区域分成两列。给固定侧边栏#aside的宽高赋具体值。#detail只给高度值,然后使复合样式flex:1;
flex:1;为flex-grow=1,flex-shrink=1,flex-basis=:0;的复合写法
Flex:1 包含哪三种属性(flex: 1 1 0%;)
第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
第三个参数表示: flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小
首屏加载优化方案
使用CDN资源,减小服务器带宽压力
路由懒加载
将一些静态js css放到其他地方(如OSS),减小服务器压力
按需加载三方资源,如iview,建议按需引入iview中的组件
使用nginx开启gzip减小网络传输的流量大小
若首屏为登录页,可以做成多入口,登录页单独分离为一个入口
使用uglifyjs-webpack-plugin插件代替webpack自带UglifyJsPlugin插件
异步组件,类似路由懒加载
js外联文件放到body底部,css外联文件放到head内
http静态资源尽量用多个子域名
尽量减少http requests的数量
js/css/html/img资源压缩
使用css spirtes,可以减少img请求次数
大图使用lazyload懒加载
避免404,减少外联js
减少cookie大小可以提高获得响应的时间
减少dom elements的数量
使用异步脚本,动态创建脚本
js 类型的显示转换和隐式转换
显示转换:最简单的方式是使用函数:
转换为字符串:toString() 或 String()
转换为数值:Number()、parseInt()、parseFloat()
转换为布尔值:Boolean()
转换为对象:Object()
隐式转换:当运算符在运算时,两边数据不统一,编译器会自动将两边数据进行数据类型转换成统一的再计算。
触发隐式转换的条件:一元、二元(+)、==、if、? :、&&
逻辑语句的类型转换:当使用if、while、for 时,隐式转换为布尔值;
逻辑表达式:! 逻辑非,隐式转换为布尔值,并取反,!!两次逻辑非,隐式转换为布尔值;|| 和 && 会将非布尔值操作数,隐式转换为布尔值,再判断;
算术表达式:具体分析
常用:
x + "" // 等价于 String(x)
+x // 等价于 Number(x),也可以写成 x - 0
!!x // 等价于Boolean(x)
!x // 转换为布尔值,并取反
vue如何实现权限管理
权限管理
权限管理就是让不同的用户只能访问自己权限内的资源,有以下几种
路由权限,用户登录后只能看到自己权限内的导航菜单,且只能访问自己权限内的路由地址
视图权限,用户只能看到自己权限内的内容和按钮
请求权限,越权请求将其拦截
控制权限
接口权限:用户登录成功后可以得到一个token,将token存起来,通过axios请求拦截器进行拦截,请求头里要携带token
按钮权限:
方法一、用v-if判断,当如果页面很多的时候,每个页面都要获取用户权限role和路由表里的meta.btnUse,然后再做判断,比较繁琐
方法二、通过自定义指令进行按钮权限的判断。
菜单权限:菜单权限可以理解成将页面与路由进行解耦。
方法一、前端定义路由信息;全局路由守卫里做判断。根据路由name找不到对应的菜单,就表示用户有没权限访问,如果路由很多,可以应用初始化时,只挂载不需要权限控制的路由。拿到后端返回的菜单后,根据菜单与路由的对应关系,筛选出可访问的路由,通过addRoutes动态挂载。
方法一缺点:菜单需要和路由一一对应,前端添加了新功能,需要通过菜单管理功能添加新的菜单,如果菜单配置的不对会导致应用不能正常使用全局路由守卫里,每次路由跳转都要做判断
方法二、菜单和路由都由后端返回,前端统一定义路由组件,后端返回路由,将后端返回的路由通过addRoutes动态挂载,需要处理数据,将component字段换成对应的组件,注意嵌套路由的数据遍历。
方法二缺点:全局路由守卫里,每次路由跳转都要做判断;需要前后端配合。
路由权限:
方法一、在路由初始化的时候挂载全部路由,在路由上标记相应的权限信息,当路由跳转的时候做校验。
方法一缺点:会加载所有的路由,当路由很多的时候,对性能会有影响;每次路由跳转都要做权限判断;菜单信息写在前端,需要修改标题的时候,需要重新编译;菜单跟路由耦合在一起,路由不一定作为菜单显示,还要多加字段进行标识。
方法二、初始化的时候先挂载不需要权限控制的路由,例如登录页。如果用户通过URL访问,则会跳转到404页面,登录后,获取用户的权限信息,然后筛选有权限访问的路由,在全局路由守卫里进行调用addRoutes添加路由。
方法二缺点:全局路由守卫里,每次路由跳转都要做判断;菜单信息在前端,要修改个标题,需要重新编译;菜单跟路由耦合在一起,路由不一定作为菜单显示,还要多加字段进行标识
CSS盒子模型
W3C盒子模型(标准盒模型)
1.标准盒模型中width指的是内容区域content的宽度;height指的是内容区域content的高度。
2.标准盒模型下盒子的大小 = content + border + padding + margin
IE盒子模型(怪异盒模型)
1.怪异盒模型中的width指的是内容、边框、内边距总的宽度(content + border + padding);height指的是内容、边框、内边距总的高度
2.怪异盒模型下盒子的大小=width(content + border + padding) + margin
设置盒子模型(CSS3指定盒子模型种类)
box-sizing: content-box;
box-sizing: border-box;
box-sizing: inherit;
即box-sizing属性可以指定盒子模型种类,content-box指定盒子模型为W3C(标准盒模型),border-box为IE盒子模型(怪异盒模型)。
本文系前端老赵独家发表,未经许可,不得转载。
评论列表
涵盖了很多方面的知识,让我对这个领域的认识更加深入,感谢老师的精彩分享!
发表评论