从2022年7月24开始记录哒
有时间会持续更新~~
什么是重绘和重排?如何减少重排和重绘?
重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。
重排/回流:当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。
注意:重绘不一定导致重排,但重排一定会导致重绘。
重排(回流)必将引起重绘,而重绘不一定会引起回流。 display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。
重排优化方案有哪些?
避免一条一条的修改DOM的样式,可以直接修改DOM的className
给动画的HTML元件使用fixed或absolute的position,那么修改他们的css是不会重排
避免在大量元素上使用:hover
避免使用Table布局
localstorage 与 cookie 的区别是什么?
存储大小
localstorage 存储数据的最大为5M或者更大
cookie 能保存的数据大小不超过4k(因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识)
数据有效期
cookie:一般由服务器生成,可以设置失效时间;若没有设置时间,关闭浏览器cookie失效,若设置了时间,cookie就会存放在硬盘里,过期才失效
localStorage:永久有效,窗口或者浏览器关闭也会一直保存,除非手动永久清除,因此用作持久数据
cookie需要自己定义方法或者引入js文件来实现获取、存储和移除
localstorage自身就有get、set、remove方法实现获取、存储和移除。
在发送请求时,cookie会被携带,而localstorage不会。同源的cookie信息会自动作为请求头的一部分发给服务器,如果过多设置cooke会额外增加通信负荷。而localstorage只会存在浏览器端。
应用场景
cookie:判断用户是否登录过网站,以便实现下次自动登录或记住密码;保存事件信息等
localStorage:常用于长期登录(判断用户是否已登录),适合长期保存在本地的数据
简述 ES6 的新特性
...、箭头函数、promise、async/await、let和const、set集合、导入improt 导出export default、smybol新的基本类型
Javascript 可以保存的最大数值是多少?
超过16位整数就无法精确地表示了 超过这个最大值,所有的奇数都会+1或者-1变成偶数,无法准确赋值
margin和padding的区别?
它们的作用对象不同,padding针对于自身的,margin作用于外部对象的。
vw和百分比有什么区别?
百分比有继承关系,继承至父级;vw只和设备的宽度有关系;
行内元素有哪些?块级元素有哪些?空(void)元素有哪些?行内与块级元素的区别?
行内元素有:a b span img strong select input;
块级元素有:div h1 h2 h3 h4 h5 ul ol li dt dd p;
行内元素:设置宽高无效;可以设置水平方向的margin和padding属性,不能设置垂直方向的margin和padding;不会自动换行;
块级元素:可以设置宽高;设置margin和padding都有效;可以自动换行;多个块状,默认排列从上到下;
空元素:
即没有内容的html元素。空元素是在开始标签中关闭的,也就是空元素没有闭合标签;
常见的有:br hr img input link meta
鲜见的有:area base col colgroup param
如何让谷歌浏览器支持小字体
//利用缩放来让字体变小,字体为12px的时候就小不了了。
.small-font{
transform:scale(0.5);
-webkit-transform:scale(0.5);
}
JS
1.let、var、const
区别:
let : 块级作用域;暂时性死区(声明变量前,该变量是不可用的);不存在变量提升;不需要初始化;
var : 不是块级作用域;不存在暂时性死区;存在变量提升;声明的是全局变量,给全局添加属性;不需要初始化;可以重复声明
const : 块级作用域;暂时性死区;不存在变量提升;必须初始化;不允许改变指针;
var a = 100;
function test() {
a = 10;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test()
// 10 100 10
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
// 5 5 5 5 5
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
}, 0)
}
// 0 1 2 3 4
内存中的堆和栈
栈:先进后出,自动分配释放
堆:先进先出,手动释放,容易内存泄漏
基本数据类型:null、undefined、String、Number、Boolean、Symbol(ES6) 基本数据类型可以直接访问,
按值进行分配,存放在**栈内存**中的简单数据段
引用型:OBject,存放在**堆内存**中,实际栈保存的是一个指针,这个指针指向另一个位置(**堆内存**)
2.深拷贝与浅拷贝
深拷贝和浅拷贝只针对Object和Array这样的引用数据类型的。
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是应用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟了一个新的区域存放新对象,且修改新对象不会影响原对象。
数组与对象赋值是浅拷贝;一维的数组和对象可以看做是深拷贝;二维的是浅拷贝;方法不能拷贝;
浅拷贝的实现方式:
1.展开运算符…
2.Object.assign()
let obj1 = {
person: {
name: "不完美女孩"
},
age: '14'
};
let obj2 = Object.assign({}, obj1);
obj2.person.name = "哈哈哈";
obj2.age = '23'
console.log(obj1); // { person: { name: '哈哈哈'}, age: '14' }
3.Array.prototype.concat()
let arr = [1, 3, {
name: '不完美女孩'
}];
let arr2 = arr.concat();
arr2[2].name = '哈哈哈哈';
console.log(arr); //[ 1, 3, { name: '哈哈哈哈' } ]
4.Array.prototype.slice()
let arr = [1, 3, {
name: '不完美女孩'
}];
let arr2 = arr.slice();
arr2[2].name = '哈哈哈哈';
console.log(arr); //[ 1, 3, { name: '哈哈哈哈' } ]
标准的深拷贝应该怎么写?
1.JSON.parse(JSON.stringify())
2.function dee(socur) {
const targetObj = socur.constructor === Array ? [] : {};
for (let keys in socur) {
if (socur.hasOwnProperty(keys)) {
// 应用数据类型
if (socur[keys] && typeof socur[keys] === 'object') {
targetObj[keys] = socur[keys].constructor === Array ? [] : {};
// 递归
targetObj[keys] = dee(socur[keys])
} else {
// 基础数据类型直接赋值
targetObj[keys] = socur[keys]
}
}
}
return targetObj
}
let objc = {
a: '嗯嗯',
b: 1,
c: [1, 2, 3],
d: {
arr: '10',
age: '78'
}
}
let newobjc = dee(objc)
newobjc.a = '哦哦'
newobjc.c.push(5)
newobjc.d.arr = '修改'
console.log(objc, newobjc)
3.promise
为什么会出现promise?
JavaScript是单线程语言,在执行任务的时候,会先执行第一个任务再执行第二个任务,假如第一个任务是个耗时的任务那么第二个任务就一直阻塞不能执行,那么这个时候就需要异步来处理这个操作;
同步:主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务;
异步:不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程;
最常用的异步操作就是将回调函数作为异步回调的参数来使用,而promise是通过then来做的;
new promise 的时候就会被立即执行,因而为了实现用的时候调用所以将promise写在函数里面;
面试题练习感觉这个很不错,可以看一看。
// 面试题
Promise.resolve().then(()=>{
console.log('Promise1');
setTimeout(()=>{
console.log('setTimeout2')
},0)
})
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
})
// Promise1 setTimeout1 Promise2 setTimeout2
4.原型与原型链
(1) 原型 prototype
常规的数组 [ ] 和对象 { } 是没有原型的,原型是函数function特有的;
每一个函数都会有prototype属性,被称为显式原型;
每一个实例对象都会有__proto__属性,其被称为隐式原型;
(2) 原型链 proto => [[prototype]]
原型链是大家都有的。
获取对象时,如果这个对象身上本身没有这个属性时,它就会去他的原型__proto__上去找,如果还找不到,就去原型的原型上去找…一直找到最顶层(Object.prototype)为止,Object.prototype对象也有__proto__属性值为null。
每一个prototype原型上都会有一个constructor属性,指向它关联的构造函数。
找私有属性用:hasOwnProperty
function Person() {
}
var person = new Person();
person.name = 'Kevin';
console.log(person.name) // Kevin
// prototype
function Person() {
}
Person.prototype.name = 'Kevin';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Kevin
console.log(person2.name) // Kevin
// __proto__
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype) // true
//constructor
function Person() {
}
console.log(Person === Person.prototype.constructor) // true
//综上所述
function Person() {
}
var person = new Person()
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
//顺便学习一下ES5得方法,可以获得对象得原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
//实例与原型
function Person() {
}
Person.prototype.name = 'Kevin';
var person = new Person();
person.name = 'Daisy';
console.log(person.name) // Daisy
delete person.name;
console.log(person.name) // Kevin
//原型得原型
var obj = new Object();
obj.name = 'Kevin',
console.log(obj.name) //Kevin
//原型链
console.log(Object.prototype.__proto__ === null) //true
// null 表示"没用对象" 即该处不应该有值
// 补充
function Person() {
}
var person = new Person()
console.log(person.constructor === Person) // true
//当获取person.constructor时,其实person中并没有constructor属性,当不能读取到constructor属性时,会从person的原型
//也就是Person.prototype中读取时,正好原型中有该属性,所以
person.constructor === Person.prototype.constructor
//__proto__
//其次是__proto__,绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于Person.prototype中,实际上,它
// 是来自与Object.prototype,与其说是一个属性,不如说是一个getter/setter,当使用obj.__proto__时,可以理解成返回了
// Object.getPrototypeOf(obj)
总结:
1、当一个对象查找属性和方法时会从自身查找,如果查找不到则会通过__proto__指向被实例化的构造函数的prototype
2、隐式原型也是一个对象,是指向我们构造函数的原型
3、除了最顶层的Object对象没有__proto_,其他所有的对象都有__proto__,这是隐式原型
4、隐式原型__proto__的作用是让对象通过它来一直往上查找属性或方法,直到找到最顶层的Object的__proto__属性,它的值是null,这个查找的过程就是原型链
5.防抖与节流
函数防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
应用场景:文本输入的验证;
限制 鼠标连击 触发;
function debounce(fn, delay) {
let timer;
return function () {
let args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(()=> {
fn.apply(this, args);
}, delay);
};
}
函数节流:每隔一段时间,只执行一次函数。
应用场景:滚动加载,加载更多或滚到底部监听。
搜索联想功能。
function throttle(fn, delay) {
let timer;
return function () {
let _this = this;
let args = arguments;
if (timer) {
return;
}
timer = setTimeout(function () {
fn.apply(_this, args);
timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
}, delay)
}
}
6.什么是闭包,什么是立即执行函数?
闭包:定义在一个函数内部的函数(方法里面返回方法)。
闭包的使用场景:settimeout、回调、函数防抖、封装私有变量
闭包存在的意义是什么?(1) 延长变量的生命周期;(2) 创建私有环境;
立即执行函数:声明一个函数,并马上调用这个匿名函数就叫做立即执行函数;
也可以说立即执行函数是一种语法,让你的函数在定义以后立即执行;
立即执行函数的作用:
1.不必为函数命名,避免了污染全局变量。
2.立即执行函数内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
3.封装变量。
7.箭头函数和普通函数有什么区别?
(1). 箭头函数更加简洁;
(2). 箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this,所以箭头函数中的this指向在它定义时就确认了,之后不会再改变,所以箭头函数的this值永远不会改变;
(3). call()、apply()、bind()等方法不能改变箭头函数this的的指向;
(4). 箭头函数不能作为构造函数使用;
(5). 箭头函数没有自己的arguments;
(6). 箭头函数没有prototype(原型),原型是undefined;
8.get请求传参长度的误区
HTTP协议从未规定GET/POST的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。
(1) HTTP协议未规定GET和POST的长度限制
(2) GET的最大长度显示是因为浏览器和web服务器限制了URL的长度
(3) 不同的浏览器和WEB服务器,限制的最大长度不一样
(4) 要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度为8182byte
9.get和post请求在缓存方面的区别
get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post不同,post一般做的是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适用于请求缓存。
10.DOM操作与BOM操作
(1) DOM操作
例如:document.getElementById 就是dom操作
DOM事件模型和事件流
DOM事件模型分为捕获和冒泡。一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
(1)捕获阶段:事件从window对象自上而下向目标节点传播的阶段;
(2)目标阶段:真正的目标节点正在处理事件的阶段;
(3)冒泡阶段:事件从目标节点自下而上向window对象传播的阶段。
如何阻止冒泡?
通过 event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。
事件代理(事件委托)
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理。
事件代理优点:
使代码简洁;减少浏览器的内存占用;
(2) BOM操作
BOM(浏览器对象模型)是浏览器本身的一些信息的设置和获取,例如获取浏览器的宽度、高度,设置让浏览器跳转到哪个地址。
例如:window.screen对象:包含有关用户屏幕的信息
window.location对象:用于获得当前页面的地址(URL),并把浏览器重定向到新的页面
window.history对象:浏览历史的前进后退等
window.navigator对象:常常用来获取浏览器信息、是否移动端访问等等
11.跨域
什么是跨域?
当协议、子域名、主域名、端口号中任意一个不相同时都算做不同域,不同域之间相互请求资源,就算作“跨域”。
常见的几种跨域解决方案(https://github.com/ljianshu/Blog/issues/55):
JSONP:利用同源策略对 script 标签不受限制,不过只支持GET请求
CORS:实现 CORS 通信的关键是后端,服务端设置 Access-Control-Allow-Origin 就可以开启,备受推崇的跨域解决方案,比 JSONP 简单许多
Node中间件代理或nginx反向代理:主要是通过同源策略对服务器不加限制
vue-cli代理跨域:devServer
12.New操作符做了什么事情?
(1) 创建了一个新对象;
(2) 将对象与构造函数的原型链连接起来;
(3) 将构造函数中的this绑定到新建的对象上;
(4) 根据构造函数返回类型做判断,如果是值类型,返回创建的对象;如果是引用类型,返回这个引用类型的对象;
13.说一下eventloop
event loop是一个执行模型,在不同的地方有不同的实现。
宏队列和微队列
宏队列,macrotask,也叫tasks。例如:
- setTimeout
- setInterval
- DOM事件
- AJAX请求
- setImmediate(Node独有的)
- I/O
- requestAnimationFrame(浏览器独有)
- UI rendering(浏览器独有)
微队列,microtask,也叫jobs。例如
- Promise
- async/await
- Object.observe
- process.nextTick(Node独有)
- MutationObserver
微任务 > DOM渲染 > 宏任务
14.Set 和 Map有什么区别?
(1) Map是键值对,Set是值得合集,当然键和值可以是任何的值;
(2) Map可以通过get方法获取,而set不能因为它只有值;
(3) 都能通过迭代器进行for…of遍历;
(4) Set的值是唯一的可以做数组去重,而Map由于没有格式限制,可以做数据存储;
Map与对象的互换
const obj = {}
const map = new Map([
['a', 2],
['b', 3]
])
for (let [key, value] of map) {
obj[key] = value
}
console.log(obj)
// {a: 2, b: 3}
15.Loader和Plugin 有什么区别
Loader:直译为“加载器”。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到‘loader’。所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。Plugin:直译为“插件”。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。在Webpack运行的生命周期中会广播出许多事件,Plugin可以 监听这些事件,在合适的时机通过Webpack提供的API改变输出结果。
16.在地址栏里输入一个地址回车会发生那些事情?
(1) 解析URL
(2) 缓存判断
(3) DNS解析
(4) 获取MAC地址
(5) TCP三次握手
(6) HTTPS握手
(7) 返回数据
(8) 页面渲染
(9) TCP四次挥手
17.UDP和TCP有什么区别
18.哪些情况会导致内存泄漏
(1) 意外的全局变量:由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收
(2) 被遗忘的计时器或回调函数:设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
(3) 脱离 DOM 的引用:获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
(4) 闭包:不合理的使用闭包,从而导致某些变量一直被留在内存当中。
19.说一下常见的检测数据类型的几种方式?
(1) typeof 其中数组、对象、null都会被判断为Object,其他判断都正确
(2) instanceof 只能判断引用数据类型,不能判断基本数据类型
(3) constructor 它有2个作用 一是判断数据的类型,二是对象实例通过constructor对象访问它的构造函数。需要注意的事情是如果创建一个对象来改变它的原型,constructor就不能来判断数据类型了
(4) Object.prototype.toString.call() 使用 object 对象的原型方法 tostring 来判断数据类型
instanceof和typeof的区别:
instanceof:
返回值为布尔值。
instanceof 用于判断一个变量是否属于某个对象的实例。
typeof:
返回值是一个字符串, 用来说明变量的数据类型。
typeof 一般只能返回如下几个结果: number, boolean, string, function, object, undefined。
20.说一下怎么把类数组转换为数组?
//通过call调用数组的slice方法来实现转换
Array.prototype.slice.call(arrayLike)
//通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)
//通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)
//通过Array.from方法来实现转换
Array.from(arrayLike)
21.说一下数组如何去重,你有几种方法?
let arr = [1,1,"1","1",true,true,"true",{},{},"{}",null,null,undefined,undefined]
// 方法1
let uniqueOne = Array.from(new Set(arr)) console.log(uniqueOne)
// 方法2
let uniqueTwo = arr => {
let map = new Map(); //或者用空对象 let obj = {} 利用对象属性不能重复得特性
let brr = []
arr.forEach( item => {
if(!map.has(item)) { //如果是对象得话就判断 !obj[item]
map.set(item,true) //如果是对象得话就obj[item] =true 其他一样
brr.push(item)
}
})
return brr
}
console.log(uniqueTwo(arr))
//方法3
let uniqueThree = arr => {
let brr = []
arr.forEach(item => {
// 使用indexOf 返回数组是否包含某个值 没有就返回-1 有就返回下标
if(brr.indexOf(item) === -1) brr.push(item)
// 或者使用includes 返回数组是否包含某个值 没有就返回false 有就返回true
if(!brr.includes(item)) brr.push(item)
})
return brr
}
console.log(uniqueThree(arr))
//方法4
let uniqueFour = arr => {
// 使用 filter 返回符合条件的集合
let brr = arr.filter((item,index) => {
return arr.indexOf(item) === index
})
return brr
}
console.log(uniqueFour(arr))
22.说一下JSON.stringify有什么缺点?
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6、如果对象中存在循环引用的情况也无法正确实现深拷贝;
23.说一下for…in 和 for…of的区别?
for…of遍历获取的是对象的键值, for…in获取的是对象的键名;
for…in会遍历对象的整个原型链, 性能非常差不推荐使用,而for…of只遍历当前对象不会遍历原型链;
对于数组的遍历,for…in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of只返回数组的下标对应的属性值;
总结:for…in循环主要是为了遍历对象而生,不适用遍历数组; for…of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象
24.说一下HTTP和HTTPS协议的区别?
1、HTTPS协议需要CA证书,费用较高;而HTTP协议不需要
2、HTTP协议是超文本传输协议,信息是明文传输的,HTTPS则是具有安全性的SSL加密传输协议;
3、使用不同的连接方式,端口也不同,HTTP协议端口是80,HTTPS协议端口是443;
4、HTTP协议连接很简单,是无状态的;HTTPS协议是具有SSL和HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP更加安全
25.文档碎片减少DOM操作
let list = document.getElementById('list')
let fragment = document.createDocumentFragment()
for (let i = 0; i < 5; i++) {
const item = document.createElement('li');
item.innerHTML = `项目${i}`
// list.appendChild(item)
fragment.appendChild(item)
}
list.appendChild(fragment)
26.判断数组的方式有哪些?
1.object.prototype.toString.call()
2.通过原型链判断:obj._proto_==Array.prototype
3.Es6:Array.isArray(obj)
4.obj instanceof Array
5.Array.prototype.isPrototypeOf(obj)
27.如何判断一个对象是空对象?
1.使用JSON自带的.stringify万法来判断:
if(Json.stringify(obj)=='{}'){
console.log('空对象')
}
2.使用ES6新增的方法Object.keys()来判断:
if(Object.keys(Obj).length<0){
console.log('空对象')
}
Vue
1.MVC和MVVM
MVVM的特点: 在MVVM的框架下,视图和模型是不能直接通信的,它们通过ViewModal来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。
2.v-model的原理
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
<input type="text" placeholder="输入内容" id="inp">
输入的内容是:<span id="box"></span>
<script>
let obj = {}
document.getElementById('inp').addEventListener('keyup', function() {
obj.inp = event.target.value
})
Object.defineProperty(obj, 'inp', {
set: function(val) {
console.log('设置值')
document.getElementById('box').innerHTML = val
},
get: function() {
console.log('取值')
}
})
</script>
3.data()为什么是函数?
每一个组件都有自己的私有作用域,确保各组件数据不会被干扰。
4.v-if与v-show的区别?使用场景分别是什么?
v-if:显示隐藏是将dom元素整个添加或删除;适用于运行时条件很少改变。
v-show:隐藏则是为该元素添加css–display:none,dom元素依旧还在;适用于非常频繁地切换。(不能用于权限操作)
5.虚拟dom
(1) 虚拟dom是什么?
vue2.x才有的虚拟dom;本质是js对象;
(2) 虚拟dom在vue中做了什么?
[1].将真实dom转化虚拟dom(js对象);
[2].更新的时候做对比;
(3) 虚拟dom是如何提升vue的渲染效率的?
[1].局部更新(节点更新);
[2].将直接操作dom的地方拿到两个js对象之中去做比较;
6.DIFF中的patch() 方法
还不太明白,后续明白了补充。
7. r o u t e r 和 router和 router和route的区别
route是当前正在跳转的路由对象,可以从route里面获取hash,name,path,query,mathsr等属性方法(接收参数时使用)
router跳转连接就可以使用
query和params 有何区别?
用法:query用path来引入,params只能用name来传递,不能使用path。
如果params中把name写成了path,接收参数页面将会是undefined。
query更像是get请求(会在地址栏显示参数);params更像post方法传递(不会在地址栏显示)。
// query传递
<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<!-- name:'detail' 可以写路由的name 也可以直接写路径 -->
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
// params传递
<router-link :to="`/home/news/shownews/${n.id}/${n.name}`">{{ n.name }}</router-link>
<router-link
:to="{
// 用params传递参数时用到他的对象写法中不可使用 path:'路径'
name: 'shownews',
params: {
id: n.id,
name: n.name,
},
}"
>
{{ n.name }}
</router-link>
// 获取
$route.params.id
$route.query.id
不借助router-link 也可以利用 $router的两个API
//$router的两个API
//push跳转 有操作记录
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
//replace 跳转没有操作记录 把之前的replace跳转记录销毁
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
// 缓存路由组件:让不展示的路由组件保持挂载,不被销毁。
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
两个生命周期函数:
actived:路由组件被激活时触发;
deactived:路由组件失活时触发。
路由守卫的作用 : 对路由进行权限管理,必须符合条件才能访问。
路由守卫有三种: 全局守卫、独享守卫、组件内守卫
在所有的路由发生改变前都执行 使用路由守卫就不能直接暴露路由实例,需要接收一下
// 全局守卫
// 然后调用里面的beforeEach((to,from,next)=>{})
// 有三个参数to:去哪个路径,from:从哪个路径里来,next:是个函数调用的时候next()放行
// 配置在实例对象外 初始化时调用,每次发生路由变化前调用
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
// 独享守卫
//放在需要进行权限设置的路由里面,参数语法和全局一样 当访问这个路径前才执行beforeEnter()
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
}
//第三种,组件守卫
// 放在组件里和methods,components同级别 ,必须是通过路由规则进入该组件才可以调用beforeRouteEnter(),beforeRouteLeave()
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
8.什么是vue声明周期?
Vue实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载DOM→渲染、更新→渲染、卸载等一系列过程,我们称这是Vue的声明周期。
Vue声明周期的作用是什么?
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
Vue生命周期总共有几个阶段?
8个阶段:创建前/后,载入前/后,更新前/后,销毁前/后。
第一次页面加载会触发那几个钩子?
第一次页面加载时会触发beforeCreate,created,beforeMount,mounted这几个钩子
每个生命周期适合那些场景?
1、beforeCreate(创建前) :数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
2、created(创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $el
属性。
3、beforeMount(挂载前) :在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
4、mounted(挂载后) :在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
5、beforeUpdate(更新前) :响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
6、updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7、beforeDestroy(销毁前) :实例销毁之前调用。这一步,实例仍然完全可用,this
仍能获取到实例。
8、destroyed(销毁后) :实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
另外还有 keep-alive
独有的生命周期,分别为 activated
和 deactivated
。用 keep-alive
包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated
钩子函数,命中缓存渲染后会执行 activated
钩子函数。
9.简述Vue的响应式原理
当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
10.Hash和history有什么区别
vue项目默认是hash
1.hash就是指url后面的#号以及后面的字符,history没有带#;
2.原理:
(1) hash通过监听浏览器的onhaschange()事件变化,查找对应的路由规则;
(2) history原理:理由H5的history中新增的两个API push State()和replace State() 和一个事件onpopstate监听url变化;
3.hash能兼容到IE8,history只能兼容到IE10;
4.由于hash值变化不会导致浏览器向服务器发出请求,而且hash改变会触发hashchange事件(hashchange只能改变#后面的url片段);虽然hash路径出现在url中,但是不会出现在HTTP请求中,对后端完全没有影响,因此改变hash值不会重新加载页面,基本都是使用hash来实现前端路由的。
11.为什么 Vuex 的 mutation 中不能做异步操作?
(1) Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过Action来提交mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
(2) 每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的跟踪,给调试带来困难。
12.computed 计算属性和watch监听区别?
computed计算属性: 必须return,不能有异步,依赖其他属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值时才会重新计算computed的值。(使用:当一个属性受多个属性影响时,例如:购物车结算)
watch监听: 可以没有return,可以有异步,每次监听变化都会调用回调(使用:当一个数据影响多条数据时,例如:搜索框)
13.Vue的父子组件生命周期钩子函数执行顺序?
<!-- 加载渲染过程 -->
<!-- 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted -->
<!-- 子组件更新过程 -->
<!-- 父beforeUpdate -> 子beforeUpdate -> 子updaed -> 父updated -->
<!-- 父组件跟新过程 -->
<!-- 父beforeUpdate -> 父updated -->
<!-- 销毁过程 -->
<!-- 父beforeDestroy -> 子beforeDestroy -> 子destroyed ->父destroyed -->
14.说一下vue3.0你了解多少?
vue2中,我们一般会采用mixin来复用逻辑代码,挺好用的,但是存在一些问题:例如代码来源不清晰、方法属性等冲突。
基于此在vue3中引入了Composition API(组合API),使用纯函数分隔复用代码。
Fragment在vue2中,组件必须只有一个根节点,很多时候会添加一些没有意义的节点用于包裹。Fragment组件就是用于解决这个问题的
15.父子组件传值
props、$emit、$refs、bus、vuex、provide / inject
父传子:
在父组件中,用v-bind动态绑定一个自定义属性,给子组件传递数据。
在子组件中,使用props接收父组件传过来的数据。
子传父:
子组件通过事件的方式,利用$emit给父组件传值。($emit的第一个参数是父组件自定义事件的方法名,后面的“value”是子组件要给父组件传递的数据)。
在父组件中,绑定一个自定义事件,用来接收子组件传来的值。
$parent 可以用来从一个子组件访问父组件的实例
$ref 可以用来获取子组件的数据(ref只在dom渲染完后才会有,可以在生命周期mounted(){}钩子中调用,或者在this.$nextTick(()=>{})中调用)。
provide / inject:
传递的时候:以对象形式传递过去
provide() {
return {
sonDate: '子组件数据',
childDate: '孙组件数据'
}
},
接收的时候和props接收方式一样:
inject:['参数']
Bus
bus.js
import Vue from 'vue'
const bus = new Vue()
export default bus
使用:先引入bus文件
传递:bus.$emit('message','传递的值')
接收:bus.$on('message',(e)=>{e就是获取到的值})
16.怎么重定向页面?
const router = new VueRouter({
routes:[
{ path: '/a', redirect: '/b' }
]
})
怎么配置404页面?
const router = new VueRouter({
routes:[
{
path: '*', redirect: {path:'/'}
}
]
})
17.vuex有哪几种属性?
state:存放公共数据的地方;
getter:获取根据业务场景返回的数据;
mutations:唯一修改state的方法,修改过程是同步的;
action:异步处理,通过分发操作触发mutation;
moule:将store模块分割,减少代码臃肿;
18.vue , 微信小程序 , uni-app属性的绑定
vue和uni-app动态绑定一个变量的值为元素的某个属性的时候,会在属性前面加上冒号":";
小程序绑定某个变量的值为元素属性时,会用两个大括号{{}}括起来,如果不加括号,为被认为是字符串。
18.vue , 微信小程序 , uni-app的页面生命周期函数
vue:
beforeCreate(创建前)
created(创建后)
beforeMount(载入前,挂载)
mounted(载入后)
beforeUpdate(更新前)
updated(更新后)
beforeDestroy(销毁前)
destroyed(销毁后)
小程序/uni-app:
1. onLoad:首次进入页面加载时触发,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
2. onShow:加载完成后、后台切到前台或重新进入页面时触发
3. onReady:页面首次渲染完成时触发
4. onHide:从前台切到后台或进入其他页面触发
5. onUnload:页面卸载时触发
6. onPullDownRefresh:监听用户下拉动作
7. onReachBottom:页面上拉触底事件的处理函数
8. onShareAppMessage:用户点击右上角转发
19.vue、小程序、uni-app中的本地数据存储和接收
vue:
存储:localstorage.setItem(‘key’,‘value’)
接收:localstorage.getItem(‘key’)
微信小程序:
存储:通过wx.setStorage/wx.setStorageSync写数据到缓存
接收:通过wx.getStorage/wx.getStorageSync读取本地缓存,
uni-app:
存储:uni.setStorage({key:“属性名”,data:“值”}) //异步
uni.setStorageSync(KEY,DATA) //同步
接收:uni.getStorage({key:“属性名”,success(res){res.data}}) //异步
uni.getStorageSync(KEY) //同步
20.vue-router 有哪几种导航守卫?
全局守卫、路由独享守卫、路由组件内的守卫
//路由的钩子函数总结有6个
全局的路由钩子函数:beforeEach、afterEach
单个的路由钩子函数:beforeEnter
组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate
21.nextTick的作用和使用场景?
vue中的nextTick主要用于处理数据动态变化后,DOM还未及时更新的问题,用nextTick就可以获取数据更新后最新DOM的变化,
数据更新和DOM更新不是同步,是异步的。
22.为什么说vue是一个渐进式的javascript框架,渐进式是什么意思?
vue允许你将一个页面分割成可复用的组件,每个组件都包含属于自己的html、css、js用来渲染网页中相应的地方。
对于vue的使用可大可小,它都会有相应的方式来整合到你的项目中。所以说它是一个渐进式的框架。
vue是响应式的(reactive)这是vue最独特的特性,也就是说当我们的数据变更时,vue会帮你更新所有网页中用到它的地方。
23.vue的性能优化
路由懒加载、图片懒加载、第三方组件库按需引入、keep-alive缓存页面、使用v-show复用DOM、避免v-if与v-for同时使用
24.为什么避免v-for和v-if同时使用?
v-for比v-if优先级高,使用的话每次v-for都会v-if判断,影响性能
25.异步请求适合在哪个生命周期调用?
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
我一般在 created 钩子函数中调用异步请求,能更快获取到服务端数据,减少页面 loading 时间;
26.过滤器的作用?如何实现一个过滤器?使用场景?
过滤器是用来过滤数据的,在vue中使用filters来过滤数据;使用场景:例如(时间/日期 格式化)