前端面试题

1. 前端开发中常见的拖拽排序效果视频:https://www.bilibili.com/video/BV1Jm4y1M79D/?spm_id_from=333.880.my_history.page.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
2.axios请求二次封装都做些什么视频:https://www.bilibili.com/video/BV1fg4y1M7pe/?spm_id_from=333.880.my_history.page.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
3.Vue axios 前端接口请求封装API视频:https://www.bilibili.com/video/BV18R4y1J7sm/?spm_id_from=333.880.my_history.page.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
4.项目上线打包优化:https://www.bilibili.com/video/BV1z34y1T7ez/?spm_id_from=333.788&vd_source=5c584bd3b474d579d0bbbffdf0437c70
5.使用nginx部署vue项目及常见问题:https://www.bilibili.com/video/BV1x84y1k7qf/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
6.es6 class的使用参考文章:https://juejin.cn/post/6964312782552432670
7.图片加水印:https://juejin.cn/post/6844903645155164174(网页加水印)
https://juejin.cn/post/7236286358389047354
8.开发中的难点亮点:https://juejin.cn/post/7074573053979525151
9.react hooks的学习视频:https://www.bilibili.com/video/BV1ZB4y1Z7o8/?spm_id_from=333.337.search-card.all.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70   , https://www.bilibili.com/video/BV1sV411c7u9/?spm_id_from=autoNext&vd_source=5c584bd3b474d579d0bbbffdf0437c70
10.react开发中,结合useState(list)给数组排序可以使用 lodash这个库来使用。lodash官网:lodash.com   /  lodash.com/docs/
11.react开发中,className动态添加类名可以加载classnames包:使用方法在下面
12.vue自定义指令与修饰符文章:https://blog.youkuaiyun.com/m0_71485750/article/details/126175776
13.react全家桶+底层原理:https://www.bilibili.com/video/BV17e411r7TH/?spm_id_from=333.337.search-card.all.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70

上述11点,使用classnames包的方法

1.命令安装classnames包
npm install classnames

2.在组件中引入
import classNames from 'classname';

3.在组件内使用, ‘’包裹的是静态的class, 
{
	// active是动态的class名,值是判断条件,是否需要加active这个class
	active: type === item.type
}包裹的是动态的class

从这样的:nav-item为静态class类名,active为动态class类名
<span className={`nav-item ${type === item.type && 'active'}`}>span...</span>

变成这样:
<span className = {classNames('nav-item', {
	active: type === item.type
})}>span...</span>

1、webpack是什么?

webpack是一个用于现代javascript应用程序的 静态模块打包工具。当webpack处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图,然后将项目中所需的每一个模块组合成一个或多个包(bundles),他们均为静态资源,用于展示内容。

2、webpack中loader和plugin的作用是什么?

loader: loader让webpack能够去处理那些非javescript文件(webpack自身只理解javascript)
plugins:插件(plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。

3、说一下原型链和继承?

(1)每个构造函数都有一个原型对象,原型对象都包含指向构造函数的指针,而实例都包含一个指向原型对象的内部指针(proto)
(2)一个对象会指向一个原型,原型对象会有自己的原型,以此类推,构成原型链
(3)实例使用方法和属性时,会先从构造函数内部找,找不到再去原型对象上找,还找不到就去原型对象的原型上找,直到(原型对象的原型为null) Object.prototype.proto===null,停止寻找
继承:(4)继承是指能够访问另外一个对象中的方法和属性。

4、js去重你能想到多少种方法?

(1)Array.from(new Set(array))
(2)newArray.filter 返回[item]/newArray.find 返回item项
(3)newArray.include(旧item) 返回true/false
(4)使用对象属性名的唯一性来保证不重复 let obj = {} if(!obj[array[i]]) { result[array[i]] = true } Object.keys(obj);
(5)使用indexOf(item) === -1, newArray.indexOf(arr1[i]) === -1
(6)使用Map数据结构去重: let map = new Map(); if (map.has(array[i])) {map.set(array[i], true)}

5、js中this的指向

(1)全局环境下指向widow,严格模式下undenfind, 函数也指向window。
(2)对象内部方法的this指向调用这些方法的对象,也就是谁调用就指向谁。
(3)箭头函数中的this指向于函数作用域所用的对象。
(4)构造函数中的this是指向实例
更改this指向:call / apply / bind()方法

6、promise的用法?你在什么情况下会使用promise?

用法(Promise对象是一个构造函数,用来生成Promise实例)
let p = function () {return new Promise((resolve,reject) => {resolve(‘需要返回出去的值’)})}
p.then(data => {})

7、node是如何处理高并发的

(1)事件循环机制:
Node.js采用事件驱动机制,通过事件轮询的方式实现异步I/O操作。当有异步I/O操作完成时,会将对应的事件加入到事件队列中,由事件循环机制负责调度执行。事件循环机制是Node.js实现高并发的核心,它能够充分利用CPU资源,减少I/O的阻塞等待时间,提高了处理请求的效率。
(2)非阻塞I/O:
Node.js采用非阻塞I/O,避免了I/O的阻塞等待时间,从而提高程序的并发性能。非阻塞I/O是指在进行I/O操作时,不会阻塞后续代码的执行,而是在后台等待I/O操作完成后立即返回结果。这种方式可以减少线程切换的开销,提高了整个应用程序的执行效率。
(3)事件驱动回调函数:
Node.js采用事件驱动回调函数,使得应用程序能够及时响应用户的请求。通过这种方式,当用户请求进入系统时,会获取一个事件处理器,并注册一个回调函数,当事件处理器产生事件时,会自动调用注册的回调函数进行处理。这种方式能够提高系统的响应速度和处理能力。
(4)内置异步模块:
Node.js内置的异步模块也是实现高并发的关键。Node.js提供了各种内置的异步模块,例如HTTP、HTTPS、FS、Net等,这些模块都是通过事件循环机制结合非阻塞I/O实现的,可以在高并发的情况下高效地处理请求。
(5)Cluster模块:
Cluster是Node.js中用于多进程处理的模块。该模块可以启用多个子进程来处理请求,从而提高应用程序的并发性能。每个子进程都是独立的,因此可以充分利用CPU资源,提高系统的处理能力。使用Cluster模块可以有效地解决单线程模型下的瓶颈问题,使得系统能够更加高效地处理大量的请求。

8、介绍一下node中require模块的加载机制

模块在第一次加载后会被缓存。这也意味着多次调用require()不会导致模块的代码被多次执行。
(1)内置模块的加载优先级最高:例如,require(‘fs’) 始终返回内置的 fs 模块,即使在 node_modules 目录下有名字相同的包也叫做 fs。

(2)自定义模块的加载机制:
使用 require() 加载自定义模块时,必须指定以 ./ 或 …/ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 …/ 这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。
同时,在使用 require() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:
1.按照确切的文件名进行加载
2.补全 .js 扩展名进行加载
3.补全 .json 扩展名进行加载
4.补全 .node 扩展名进行加载
5.加载失败,终端报错

(3)第三方模块的加载机制:如果传递给 require() 的模块标识符不是一个内置模块,也没有以 ‘./’ 或 ‘…/’ 开头,则 Node.js 会从当前模块的父目录开始,尝试从 /node_modules 文件夹中加载第三方模块。
例如,假设在 ‘C:\Users\itheima\project\foo.js’ 文件里调用了 require(‘tools’),则 Node.js 会按以下顺序查找:

C:\Users\itheima\project\node_modules\tools
C:\Users\itheima\node_modules\tools
C:\Users\node_modules\tools
C:\node_modules\tools

(4)目录作为模块:当把目录作为模块标识符,传递给 require() 进行加载的时候,有三种加载方式:
在被加载的目录下查找一个叫做 package.json 的文件,并寻找 main 属性,作为 require() 加载的入口
如果目录里没有 package.json 文件,或者 main 入口不存在或无法解析,则 Node.js 将会试图加载目录下的 index.js 文件。
如果以上两步都失败了,则 Node.js 会在终端打印错误消息,报告模块的缺失:Error: Cannot find module ‘xxx’

9、闭包的作用是什么?你能举个使用闭包的例子吗?

(1)延长局部变量的寿命
(2)使外部能够访问到函数内部的变量
例子: 如果一个属性是全局变量,不想让人直接访问,局部变量又访问不到时,暴露一个函数让间接访问;

function(){
  var lives = 50
  window.奖励一条命 = function(){  // 简明起见,用了中文
    lives += 1
  }
  window.死一条命 = function(){
    lives -= 1
  }
}()

10、当在浏览器输入url后,发生了些什么?

(1)URL 解析:浏览器首先对 URL 解析,解析出协议、域名、端口、资源路径、参数等。
(2)DNS 域名解析:首先需要将一个域名转化为相应的 IP 地址
(3)建立TCP连接
(4)发送HTTP请求:当浏览器与服务器建立连接后,就可以进行数据通信过程,浏览器会给服务器发送一个 HTTP 请求报文,请求报文包括请求行、请求头、请求空行和请求体。在请求行中会指定方法、资源路径以及 HTTP 版本,其中资源路径是指定所要操作资源在服务器中的位置,而方法是指定要对这个资源做什么样的操作。
(5)服务器对请求进行处理并作出响应
(6)浏览器解析渲染页面
(7)断开TCP连接
注意:
浏览器为了提升性能,在 URL 解析之后,实际会先查询是否有缓存,如果缓存命中,则直接返回缓存资源。
如果是 HTTPS 协议,在建立 TCP 连接之后,还需要进行 SSL/TLS 握手过程,以协商出一个会话密钥,用于消息加密,提升安全性。

11、说下vue的生命周期

Vue的生命周期钩子函数主要包括:

  • 1.beforeCreate(): 在实例初始化之后调用, data和methods都还没有初始化完成, 通过this不能访问
  • 2.created(): 此时data和methods都已初始化完成, 可以通过this去操作, 可以在此发ajax请求
  • 3.beforeMount(): 模板已经在内存中编译, 但还没有挂载到页面上, 不能通过ref找到对应的标签对象
  • 4.mounted(): 页面已经初始显示, 可以通过ref找到对应的标签, 也可以选择此时发ajax请求
  • 5.beforeUpdate(): 在数据更新之后, 界面更新前调用, 只能访问到原有的界面
  • 6.updated(): 在界面更新之后调用, 此时可以访问最新的界面
  • 7.beforeDestroy(): 实例销毁之前调用, 此时实例仍然可以正常工作
  • 8.destroyed(): Vue 实例销毁后调用, 实例已经无法正常工作了
  • 9.deactivated():组件失活, 但没有死亡
  • 10.activated(): 组件激活, 被复用
  • 11.errorCaptured(): 用于捕获子组件的错误,return false可以阻止错误向上冒泡(传递)
  • 我们通常在created()/mounted()进行发送ajax请求,启动定时器等异步任务,而在beforeDestory()做收尾工作,如: 清除定时器操作。

不过需要注意的是mounted生命周期钩子中并不代表界面已经渲染成功,因为 mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick。
js面试题:

<div ref="text"></dev>
beforeMouted() {
 console.log(this.$refs.text); // 会打印什么
}

mouted() {
 console.log(this.$refs.text); // 会打印什么
}

12、怎样判断在屏幕的可视化区域

  • offsetTop、scrollTop
  • getBoundingClientRect
  • Intersection Observer
<body>
  <div class="box1">
    <!-- offsetTop, scrollTop -->
    <!-- getBoundingClientRect -->
    <!-- IntersectionObserver -->
  </div>
  <div class="box2" id="box2">123456</div>

  <style>
    .box1 {
      height: 1000px;
    }
  </style>
</body>
<script>
  console.log(window.innerHeight)
  console.log(document.getElementById('box2').offsetTop)
  console.log(document.documentElement.scrollTop)
  console.log(document.getElementById('box2').getBoundingClientRect())

  // 方法一:offsetTop - scrollTop <= window.innerHeight;
  function isInViewPortOfOne(el) {
    // viewPortHeight 兼容所有浏览器写法
    const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
    const offsetTop = el.offsetTop;
    const scrollTop = document.documentElement.scrollTop;
    const top = offsetTop - scrollTop;
    return top <= viewPortHeight;
  }

  // 方法二:getBoundingClientRect
  // {
  //   top, // 元素顶部距离屏幕顶部的距离
  //   bottom, // 元素底部距离屏幕顶部的距离
  //   left, // 元素左边距离屏幕左边的距离
  //   right, // 元素右边距离屏幕右边的距离
  //   x,
  //   y,
  //   width,
  //   height, // 元素的宽和高
  // }
  /**
   * 1.top 大于等于0
   * 2.left大于等于0
   * 3.bottom小于等于视窗高度
   * 4.right小于等于视窗宽度
   */
  function isInViewPortOfOne(el) {
    const viewWidth = window.innerWidth || document.document.clientWidth;
    const viewHeight = window.innerHeight || document.document.clientHeight;
    const {
      top,
      right,
      bottom,
      left,
    } = el.getBoundingClientRect();

    return (
      top >= 0 &&
      left >= 0 &&
      right <= viewWidth &&
      bottom <= viewHeight
    )
  }

  // 以上方法可以实现,但是需要监听浏览器滚动,需要有大量计算
  // 方法三:Intersection Observer 浏览器原生提供的构造函数,接收两个参数,callback和options
  // callback会触发两次,元素刚进入可视区域时,元素完全看不到时
  const intersectionObserver = new IntersectionObserver(function (entries) {
    if (entries[0].intersectionRatio <= 0) return; // 小于0完全不可见,0-1部分可见,大于等于1完全可见
    console.log('已经进入可视区域');
  })

  // 开始观察,监听元素
  intersectionObserver.observe(document.getElementById('box2'));

  // 停止观察
  intersectionObserver.unobserve(document.getElementById('box2'));

  // 关闭观察器
  intersectionObserver.disconnect();
</script>

13、说下如下代码的值

let a= {
	name: 'A',
	fun: function() {
		console.log(this.name);
	}
}
console.log(a.fun()); // A
console.log(a.fun.call({name: 'B'})); // B
let f1 = a.fun();
console.log(f1); // A

console.log(typeof undefined) // undefined
console.log(typeof null) // object
console.log(typeof console.log) // function
console.log(typeof [1, 2, 3]) // object
console.log(typeof '123') // string
console.log(typeof 123) // number

13.promise用过什么方法?

promise.all
promise.resolve
promise.reject
promise.race 与 all的区别
promise还有async和await
面试题:Promise.all一个请求失败也能得到其余正确的请求结果

参考文章:https://blog.csdn.net/qq_43592064/article/details/130829067
Promise.all默认只要有一个错误就直接返回错误。promise.all中任何一个promise 出现错误的时候都会执行reject,导致其它正常返回的数据也无法使用
Promise.all(
  [
    Promise.reject({ code: 500, msg: "服务异常" }),
    Promise.resolve({ code: 200, list: [] }),
    Promise.resolve({ code: 200, list: [] })
  ].map(p => p.catch(e => e))
)
  .then(res => {
    console.log("res=>", res);
  })
  .catch(error => {
    console.log("error=>", error);
  });
res=> [ { code: 500, msg: '服务异常' },
  { code: 200, list: [] },
  { code: 200, list: [] } ]
核心内容是map方法,map的每一项都是promise,catch方法返回值会被promise.reslove()包裹,这样传进promise.all的数据都是resolved状态的。
 
// 使用Promise.all 其中id为69的商品,返回失败,会导致整个Promise接受到reject状态.
// 所以进行改造, p catch 得到的err  为返回失败抛出的信息, 进行置空
.map(p => p.catch(err => '')))

14.ajax,axios请求应该放在created里还是mounted里?放在creacted里会造成什么影响?

参考视频
vue的created和mounted是同步执行,会先执行created里面的同步代码,然后把请求当成微任务挂起(先发送请求padding状态),然后再执行mounted里的同步代码,mounted里的代码执行完毕后,会渲染页面。请求结果才接收到,当收到结果后有更新data里面的数据,那么会再渲染一次。
看一下代码

<template>
	<div>
		<ul>
			<li v-for="item in cityList" :key="item.id">{{item.name}}</li>
		</ul>
	</div>
</template>
<script>
export default {
	data() {
		cityList: [],
	},
	created() {
		conslole.log('created');
		alert('created'); // 会阻止后续代码运行
		this.getCityList();
	},
	mounted() {
		conslole.log('mounted');
		alert('mounted'); // 会阻止后续代码运行
	},
	methods: {
		async getCityList() {
			const res = await axions({...});
			this.cityList = res.data.list;
		}
	}
}
</script>

执行上面代码顺序为:
conslole.log(‘created’);
alert(‘created’);
this.getCityList();发送请求,但是请求状态为padding,挂起等待状态
再执行mounted的代码
conslole.log(‘mounted’);
alert(‘mounted’); // mounted代码执行完毕加载页面,组件首次渲染
最后this.getCityList();请求执行完毕获取数据,组件重新渲染,刷新页面,所以会刷新两次页面

另外观点:参考文章:https://blog.youkuaiyun.com/dongwei666/article/details/126364290
总结:如果无需操作dom,可以在created里面进行请求,数据与dom同步渲染,减少渲染次数,但是不可过多请求,否则屏幕将会出现白屏
如果有dom操作就在mounted里面请求,也不可过多请求,否则渲染次数过多

15.怎么验证用户有没有登录?路由守卫怎么设置?

  • 直接在配置路由的地方设置beforeEnter()方法
export default new Router({
	routes: [
		{
			path: '/login',
			name: 'login',
			component: Login
		}, 
		{
			path: '/register',
			name: 'register',
			component: Register, // component: () => import 'xxx/xx.vue'
			beforeEnter(to, from, next) {
				console.log(to);
				console.log(from);
				console.log(next);
				next(); // 放行,必须有,否则跳不到页面
				next(false); // 禁止跳转路由点击无效
				if (from.path === '/login') {
					next(); // 如果从登录页面来的注册页面就放行跳到注册页面
				} else {
					// 重定向
					next('/login'); // 如果不是,重定向到登录页面
				}
			}
		}
	]
})
  • 使用router.beforeEach()方法
const router = new Router({
	router: [
		{
			path: '/login',
			name: 'Login',
			component: 组件/ import 'xx/xx.vue'
		}
	]
})

router.beforeEach((to,from, next) => {
	...
	next();
})

16.说下原型和原型链

  1. 什么是原型?
    在js中,每个构造函数内部都有一个prototype属性,该属性的值是个对象,该对象包含了该构造函数所有实例共享的属性和方法。当我们通过构造函数创建对象的时候,在这个对象中有一个指针,这个指针指向构造函数的prototype的值,我们将这个指向prototype的指针称为原型。或者用另一种简单却难理解的说法是:js中的对象都有一个特殊的[[Prototype]]内置属性,其实这就是原型。(简单来说原型就是对象或者函数下面的prototype属性)

  2. 如何获取对象原型值?

  • 在浏览器上可以使用__proto__,例如a.__proto__
  • 推荐使用Object.getPrototypeOf(a)
  1. 原型链?
    JS的每个函数在创建的时候,都会生成一个属性prototype,这个属性指向一个对象,这个对象就是此函数的原型对象。该原型对象中有个属性为constructor,指向该函数。这样原型对象和它的函数之间就产生了联系。

17.new关键字做了什么?

构造函数创建一个实例的过程

  • 创建一个新对象
  • 将构造函数的作用域赋值给新对象(这样this就指向了新对象)
  • 执行构造函数中的代码(为新对象添加实例属性和实例方法)
  • 返回新对象

18. watch与computed的区别?

功能上:

  • computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
  • 是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
  • 是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
  • computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)
  • 区别总结:
    computed支持缓存,相依赖的数据发生改变才会重新计算;watch不支持缓存,只要监听的数据变化就会触发相应操作
    computed不支持异步,当computed内有异步操作时是无法监听数据变化的;watch支持异步操作
    computed属性的属性值是一函数,函数返回值为属性的属性值,computed中每个属性都可以设置set与get方法。watch监听的数据必须是data中声明过或父组件传递过来的props中的数据,当数据变化时,触发监听器

使用场景:
computed----当一个属性受多个属性影响的时候,使用computed-----购物车商品结算。watch–当一条数据影响多条数据的时候,使用watch-----搜索框.

19. px, rpx, em, rem有什么不同?

参考文档:https://zhuanlan.zhihu.com/p/156940153

  • px: 相对长度单位。像素px是相对于显示器屏幕分辨率而言的。
  • rpx: 其实是微信对于rem的一种应用的规定,或者说一种设计的方案,官方上规定屏幕宽度为20rem,规定屏幕宽为750rpx。
    所以在微信小程序中1rem=750/20rpx。
  • em: 相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。浏览器默认的字号是16px。所有未经调整的浏览器都符合: 1em=16px。
    em的特点:
    em的值并不是固定的;
    em会继承父级元素的字体大小。
    因为这两个特点,所以我们用em的时候,需要注意三点:
    body选择器中声明Font-size=62.5%;
    将你的原来的px数值除以10,然后换上em作为单位;
    重新计算那些被放大的字体的em数值。避免字体大小的重复声明。
  • rem: rem是相对于根元素,也就是说,我们只需要在根元素确定一个参考值,其他子元素的值可以根据这个参考值来转换。具体这个参考值设置为多少,完全可以根据我们自己的需求来定。
    用法:
 rem用法:以html根元素设置参考值
 html {
        font-size: 10px;
 }
 div {
     font-size: 4rem; /* 40px */
     width: 20rem;  /* 200px */
     height: 20rem;
 }
 p {
     font-size: 2rem; /* 20px */
     width: 10rem;
     height: 10rem;
     border: solid 1px blue;
 }

20. react中class组件与函数组件有什么不同?

  • 定义方式不同:
    class组件定义:class Xxx extends React.Compont {} 继承React.Component,且需要创建render方法来返回元素。
    函数组件: function Xxx() {} 函数组件名字必须大写
  • class:
  1. 有组件实例
  2. 有生命周期
  3. 有 state 和 setState
  4. 有ref
  • 函数组件:
  1. 没有组件实例
  2. 没有生命周期, 需要使用hook的 useEffect hooks函数组件的缺点: 没法保存state, 如需保存state,则需要使用useRef();
  3. 没有 state 和 setState,只能接收 props
  4. 函数组件是一个纯函数,执行完即销毁,无法存储 state
  5. 没有ref,但可以在函数组件内部使用 ref 属性,只要它指向一个 DOM 元素或 class 组件。

class 组件存在的问题:
大型组件很难拆分和重构,变得难以测试
相同业务逻辑分散到各个方法中,可能会变得混乱
复用逻辑可能变得复杂,如 HOC 、Render Props
所以 react 中更提倡函数式编程,因为函数更灵活,更易拆分,但函数组件太简单,所以出现了hook,hook就是用来增强函数组件功能的。

21. 原生小程序和uniapp小程序中有哪些生命周期钩子

参考:http://jerryzou.com/posts/cookie-and-web-storage/
原生小程序生命周期构子:

  • onLoad :页面加载时触发
  • onShow : 页面显示时触发 , 包括页面初次加载 , 从其他页面返回到当前页面
  • onReady : 页面初次渲染完成时触发
  • onHide : 页面隐藏时触发 , 当跳转到其他页面,关闭当前页面触发
  • onUnload:页面卸载时触发,当页面销毁时触发。
  • onPullDownRefresh : 下拉刷新时触发
  • onReachBottom:滚动到页面底部时触发。
  • onShareAppMessage:点击页面转发按钮时触发。
  • onPageScroll:页面滚动时触发。
  • onTabItemTap:点击底部tab栏时触发。

uniapp小程序生命周期钩子:

  • onLoad:页面加载时触发。
  • onReady:页面初次渲染完成时触发。
  • onShow:页面显示时触发,包括页面初次加载、从其他页面返回到当前页面。
  • onHide:页面隐藏时触发,当跳转到其他页面、关闭当前页面时触发。
  • onUnload:页面卸载时触发,当页面销毁时触发。
  • onPullDownRefresh:下拉刷新时触发。
  • onReachBottom:滚动到页面底部时触发。
  • onShareAppMessage:点击页面转发按钮时触发。
  • onResize:页面尺寸变化时触发。
  • onTabItemTap:点击底部tab栏时触发。

22. cookie、sessionStorage、localStorage的区别

  • cookie: 由服务器生成,大小限制为4KB左右, cookie在与服务器端通信每次都会携带在HTTP头中,可设置失效时间
  • sessionStorage:在客户端生成,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage 中的数据就会被清空。
  • localStorage:在客户端生成,localStorage除非被清除,否则会永久保存

三者异同
cookie 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效,存放数据大小一般4K左右,而sessionStorage与localStorage大小在5兆左右,在客户端生成,localStorage除非被清除,否则会永久保存,sessionStorage仅在当前会话下有效,关闭页面或浏览器后被清除,cookie在与服务器端通信每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题,而sessionStorage与localStorage仅在客户端(即浏览器)中保存,不参与和服务器的通信。

  1. cookie由服务端生成,用于标识用户身份;而两个storage用于浏览器端缓存数据
  2. 三者都是键值对的集合
  3. 一般情况下浏览器端不会修改cookie,但会频繁操作两个storage
  4. 如果保存了cookie的话,http请求中一定会带上;而两个storage可以由脚本选择性的提交
  5. 会话的storage会在会话结束后销毁;而local的那个会永久保存直到覆盖。cookie会在过期时间之后销毁。
  6. 安全性方面,cookie中最好不要放置任何明文的东西。两个storage的数据提交后在服务端一定要校验(其实任何payload和qs里的参数都要校验)。

23. vue3新特性

参考文章:https://juejin.cn/post/6968094627375087653

  • 创建app实例方式从原来的new Vue()变为通过createApp函数进行创建;这样带来的变化就是以前在全局配置的组件(Vue.component)、指令(Vue.directive)、混入(Vue.mixin)和插件(Vue.use)等变为直接挂载在实例上的方法;我们通过创建的实例来调用,带来的好处就是一个应用可以有多个Vue实例,不同实例之间的配置也不会相互影响
const app = createApp(App)
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
  • 周期函数钩子变化:新增setup钩子函数,在beforeCreate之前执行,将beforeDestroy改名为beforeUnmount,destroyed改名为unmounted
  • vue3新增了生命周期钩子,我们可以通过在生命周期函数前加on来访问组件的生命周期:
onBeforeMount
onMounted
onBeforeUpdate
onUpdated
onBeforeUnmount
onUnmounted
onErrorCaptured
onRenderTracked
onRenderTriggered

使用:
import { onBeforeMount, onMounted } from "vue";
export default {
  setup() {
    console.log("----setup----");
    onBeforeMount(() => {
      // beforeMount代码执行
    });
    onMounted(() => {
      // mounted代码执行
    });
  },
}

响应式API:

  • ref/reactive

24. 怎么让元素垂直居中

知道父元素高度的:

  • 方法一:使用transform来位移元素
<body>
  <div class="box">
    <div class="box-child">123456</div>
  </div>

  <style>
    .box {
      position: relative;
      width: 300px;
      height: 300px;
    }

    .box-child {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
  </style>
</body>
  • 方法二:使用display: flex布局
<body>
  <div class="box">
    <div class="box-child">123456</div>
  </div>

  <style>
    .box {
      display: flex;
      justify-content: space-around/center; // space-around/center都可以
      align-items: center;
      width: 300px;
      height: 300px;
    }
  </style>
</body>
  • 方法三:表格,display: table-cell;此元素会作为一个表格单元格显示 类似于td th
<body>
  <div class="box">
    <div class="box-child">123456</div>
  </div>

  <style>
    .box {
      display: table-cell;
      text-align: center;
      vertical-align: middle;
      width: 300px;
      height: 300px;
    }

    .box-child {
      vertical-align: middle;
      display: inline-block;
    }
  </style>
</body>
  • 方法四:使用postion和margin:auto来设置

注意:此方法需要设置子元素宽高,width,height

<body>
  <div class="box">
    <div class="box-child">123456</div>
  </div>

  <style>
    .box {
      position: relative;
      width: 300px;
      height: 300px;
    }

    .box-child {
      position: absolute;
      width: 50px;
      height: 50px;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      margin: auto;
    }
  </style>
</body>
  • 方法五:子元素设置display: inline-block,父元素设置text-align: center且line-height等于height
    将子元素设为行内块元素,父元素设置左右居中,同时通过让父元素的line-height属性等于height属性实现子元素上下居中。
<body>
  <div class="box">
    <div class="box-child">123456</div>
  </div>

  <style>
    .box {
      width: 100vw;
      height: 100vh;
      line-height: 100vh;
      text-align: center;
    }

    .box-child {
      display: inline-block;
    }
  </style>
</body>

25.如何渲染10万条数据?

  • 虚拟列表的方式,getBoundingClientRect判断滚动条是往上还是往下,只渲染可视区域
  • 时间分片:window.requestAnimationFrame和document.createDocumentFragment
<script>
  <ul id="box"></ul>

  const ul = document.getElementById('box');
  const totalNum = 1000000;
  const onceNum = 20;
  const page = totalNum / onceNum
  let index = 0;

  function loop(curTotal, curIndex) {
    if (curTotal <= 0) {
      return false;
    }

    const pageCount = Math.min(curTotal, onceNum);
    window.requestAnimationFrame(() => {
      let fragment = document.createDocumentFragment();
      for (let i = 0; i < pageCount; i++) {
        let li = document.createElement('li');
        li.innerText = curIndex + i + ':' + ~~(Math.random() * total);
        fragment.appendChild(li)
      }
      ul.appendChild(fragment);
      loop(curTotal - pageCount, curIndex + pageCount);
    })
  }
  loop(totalNum, index);
</script>

26.localStorage与sessionStorage的大小是多大?超出了怎么办?indexedDB有了解吗?

大小都在5MB左右
localstorage存储不是5m 是每个域5m 超了申请其他的域/修改ng配置 postmessge通信往其他域上存取,或者清除不需要的存储,压缩或减少体积,或者使用indexedDB
indexedDB参考文章:
https://juejin.cn/post/7026900352968425486
https://juejin.cn/post/7249386837369159735?from=search-suggest

27.keep-alive有了解吗?

keep-alive作为一种vue的内置组件,主要作用是缓存组件状态。当需要组件的切换时,不用重新渲染组件,避免多次渲染,就可以使用keep-alive包裹组件。
参考文章:https://juejin.cn/post/7091490734041219086

28.协商缓存,强缓存有了解吗?

  • 强缓存:浏览器不会像服务器发送任何请求,直接从本地缓存中读取文件并返回Status Code: 200 OK
    在这里插入图片描述
    在这里插入图片描述

200 form memory cache : 不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache。
200 from disk cache: 不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache。
优先访问memory cache,其次是disk cache,最后是请求网络资源

  • 协商缓存: 向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;
    在这里插入图片描述
    参考文章:https://juejin.cn/post/6844903838768431118

29. DNS域名解析是在哪一步操作?

  1. 浏览器先检查自身缓存中有没有被解析过的这个域名对应的IP地址,如果有解析结束,同时域名被缓存的时间也可通过TTL属性来设置。
  2. 如果浏览器缓存中没有命中(没有),浏览器会检查操作系统缓存中有没有对应的已解析的结果。而操作系统也有一个域名解析的过程,在windows中可通过C盘里面一个叫做hosts的文件来设置,如果你在这里指定一个域名对应的IP地址,那浏览器会首先使用这个IP地址。但是这种操作系统级别的域名解析过程也被很多黑客利用,通过修改你的hosts文件里的内容吧特定的域名解析到他指定的IP地址还是那个,造成所有的域名劫持。所以在window7中将hosts文件设置成了readonly,防止恶意篡改
  3. 如果还没有命中域名,才会真正的请求本地域名服务器(LDNS)来解析和这个域名,这个服务器一般在你的城市的某个角落,距离不是很远,而且这台服务器的性能很好,一般都会缓存域名解析结果,大约80%的域名解析到这里就完成了。浏览器(主机)向其本地域名服务器进行的是递归查询。
  4. 本地域名服务器采用迭代查询,它先向下一个根域名服务器查询
  5. 根域名服务器如果有查询的IP的值则返回,没有命中,则告诉本地域名服务器,下一次查询的顶级域名服务器的IP的值
  6. 本地域名服务器向收到的顶级域名服务器进行查询
  7. 顶级域名服务器如果有查询的IP地址则返回,没有命中,则告诉本地域名服务,下一次查询的权限域名服务器的IP的地址
  8. 本地域名服务器向权限域名服务器进行查询
  9. 权限域名服务器告诉本地域名服务器,所查询的IP地址
  10. 本地域名服务器最后把查询的结果告诉浏览器(主机)。

原文链接:https://blog.youkuaiyun.com/qq_37288477/article/details/86582130

30. script上的defer, async有什么区别?

  • 二者都是异步去加载外部JS文件
  • async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行;而Defer是在JS加载完成后,整个文档解析完成后执行。
  • defer更像是将script标签放在body之后的效果,但是它由于是异步加载JS文件,所以可以节省时间。
    参考文章: https://zhuanlan.zhihu.com/p/30898865

31.低代码有了解吗?

参考文章:https://zhuanlan.zhihu.com/p/559966882

32.浏览器渲染优化

  • (1)针对javascript:javascript会阻塞HTML的解析,也会阻塞css的解析。因此我们可以对javascript的加载方式进行改变来进行优化:
1. 尽量将javascript文件放在body的最后
2. body中间尽量不要写<script>标签
3. <script>标签的引入资源方式有三种,一种就是我们常用的直接引入,还有两种就是使用async属性和defer属性来异步引入,
两者都是去异步加载外部的js文件,不会阻塞dom的解析(尽量使用异步加载),三者的区别如下:
 *script立即停止页面渲染去加载资源文件,当资源加载完毕后立即执行js代码,js代码执行完毕后继续渲染页面;
 *async是在下载完成之后,立即异步加载,加载好后立即执行,多个带async属性的标签,不能保证加载的顺序;
 *defer是在下载完成后,立即异步加载。加载好后,如果dom树还没构建好,则先等dom树解析好再执行;如果dom树已经准备好,
 则立即执行。多个带defer属性的标签,按照顺序执行。
  • (2)针对css:使用css有三种方式:使用link,@import,内联样式,其中link和@import都是导入外部样式。它们之间的区别:
 *link: 浏览器会派发一个新等线程(http线程)去加载资源文件,于此同时GUI渲染线程会继续向下渲染代码
 *@import: GUI渲染线程会暂时停止渲染,去服务器加载资源文件,资源文件没有返回之前不会继续渲染(阻碍浏览器渲染)
 *style: GUI直接渲染
 外部样式如果长时间没有加载完毕,浏览器为了用户体验,会使用浏览器的默认样式,确保首次渲染的速度。所有css一般写在heard中,让浏览器尽快发送请求去获取css样式。所以,再开发过程中,导入外部样式使用link,而不用@import。如果css少,尽可能采用内嵌样式,直接写在style标签中。
  • (3)针对DOM树,CSSOM树:可以通过以下几种方式来减少渲染的时间:
*HTML文件的代码层级尽量不要太深
*使用语义化的标签,来避免不标准语义化的特殊处理
*减少CSS代码的层级,因为选择器是从左向右进行解析的
  • (4)减少回流与重绘
*操作DOM树时,尽量在低代码层级的DOM节点进行操作
*不要使用table布局,一个小的改动可能会使整个table进行重新布局
*使用css的表达式
*不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式
*使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
*避免频繁操作DOM,可以创建一个文档片段docmentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
*将元素先设置display:none,操作结束后再把它显示出来。因为display属性为none的元素上进行的DOM操作不会引发回流和重绘。
*将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机智

浏览器针对页面的回流与重绘,进行了自身的优化-----渲染队列
浏览器会将所有的回流,重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流,重绘变成一次回流重绘。
将多个读操作(或写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发一次回流。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值