前端面试题(算法、vue3、小程序)
一、求和算法(1)普通求和function sum(arr){var result = 0;for(var i in arr){result += arr[i];}return result;}(2)快捷求和function sum(arr) {return eval(arr.join('+'));}二、排序(1)冒泡排序比较相邻的元素。如果第一个比第二个大,就交换它们两个;对每一对相邻元素作同样
一、数据扁平化
(1)forEach
export function flatten(arr) {
if (arr.length === 0) { return []; }
var newArr = [];
arr.forEach(function(item) {
if (item instanceof Array) {
newArr = newArr.concat(flatten(item));
} else {
newArr.push(item);
}
});
return newArr;
}
(2)reduce
function fn(arr) {
return arr.reduce((prev,cur) =>{
return prev.concat(Array.isArray(cur) ? fn(cur) : cur)
},[])
}
二、排序
(1)冒泡排序
1、对元素进行两次遍历
2、第一次遍历所有元素
3、第二次判断当前元素是否比之后的元素大,如果大则交换位置,否则不动
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { // 相邻元素两两对比
var temp = arr[j+1]; // 元素交换
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
(2)sort
-
简单的arr.sort()是根据unicode编码来排序的,这也显示了其不稳定性
var arr1 = [10,1,5,2,3];
arr1.sort((a, b) =>{
return a - b;
});
console.log(arr1)
三、数组去重
(1)ES6的Set对象
let result = Array.from(new Set(oldArray))
(2)indexOf单独使用
建立一个新的空数组,遍历原数组,把值push到新数组中,每次push前,判断新数组是否有相同的值,如果没有push进去。
let arr = [1,1,1,2,3]
let result= [];
function arr(arr){
for (var i=0;i<arr.length;i++) {
if (result.indexOf(arr[i]) == -1) {
result.push(arr[i])
}
}
}
arr(aa);
(3)filter和indexOf
var arr = [3,2,2,4,5,1,2,2,1];
var r = arr.filter(function(e,index){
return arr.indexOf(e) == index;
})
(4)双重for循环去重
let arr = [1,1,2,3,4,5,5]
for(let i=0;i<arr.length;i++){
for(let j=i+1;j<arr.length;j++){
if(arr[i]===arr[j]){
arr.splice(j,1)
j--
}
}
}
console.log(arr)
四、求和算法
(1)普通求和
function sum(arr){
var result = 0;
for(var i in arr){
result += arr[i];
}
return result;
}
(2)快捷求和
function sum(arr) {
return eval(arr.join('+'));
}
(3)reduce
var numbers = [65, 44, 12, 4]
var result = numbers.reduce((total, num) => { return total + num })
(4)JS求解两数之和算法详解
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数
let nums = [2,7,11,15]
let target = 9
let newArr = []
for(var i =0;i<nums.length;i++){
if(nums.includes(target-nums[i])){
newArr.push(i)
}
}
console.log(newArr) // [0,1]
(5)JS求解三数之和算法详解
let nums = [2,6,1,2,5]
let target = 9
let newArr = []
for(var i =0;i<nums.length;i++){
for(var j=i+1;j<nums.length;j++) {
if(nums.includes(target-nums[i]-nums[j])){
newArr.push({a:nums[i],b:nums[j],c:target-nums[i]-nums[j]})
}
}
}
五、常用ES6
- let const 两者区别:
(1)let与const没有变量提升;
(2)let与const不能重复声明变量;
(3)let与const都是只在声明所在的块级作用域内有效。
(4)let声明变量。const声明常量,不可修改,修改后会报错;
(const:如果使用const声明的是对象的话,是可以修改对象里面的值的) - 模板字符串:console.log(
hello${name1}
); - Promise:最大的好处就是可以链式的调用函数,起到异步回调函数的作用,看起来更加直观简洁;
- import代替了之前的require;
- 解构赋值:简化提取数组或对象中的值,给变量进行赋值,称为解构赋值;(let [a, , b] = [1, 2, 3]; a=1,b=3)
- 扩展运算符(…object,可对数组和对象进行操作)
- 箭头函数:ES6中箭头函数VS普通函数的this指向
- 常用方法:
(1)includes()存在就返回true,不存在返回false;返回一个布尔值;不修改原数组。直接查找元素;
(2)find() 从左到右依次查找,找到一个就不往后找了;如果找不到,那么返回undefined;返回找到的那一项;不改变原数组。用函数去查找;
(3)filter() 过滤;把所有符合条件的数组成员放到一个新数组中返回;返回一个过滤后的新数组,不改变原数组;
(4)every() 每一个都是true则返回true,如果有一个是false,那么直接返回false;返回一个布尔值;
(5)some() 返回一个布尔值,只要有一个符合条件就返回true;找到true,就不再向右进行查找;不修改原数组;
(6)reduce()方法返回的是最后一次调用回调函数的返回值;可以求和和拍平数组;
六、get、post的区别
- get传参方式是通过地址栏URL传递,是可以直接看到get传递的参数,post传参方式参数URL不可见,get把请求的数据在URL后通过?连接,通过&进行参数分割。post将参数存放在HTTP的体内
- get传递数据是通过URL进行传递,对传递的数据长度是受到URL大小的限制,URL最大长度是2048个字符。post没有长度限制。
- get请求只URL编码,post支持多种编码方式
- get只支持ASCII字符,post没有字符类型限制
- get后退不会有影响,post后退会重新进行提交
- get请求可以被缓存,post不可以被缓存
- get请求的记录会留在历史记录中,post请求不会留在历史记录
- get请求使用params方法,post使用data方法
七、宏任务和微任务
JavaScript 单线程中的异步任务任务可以细分为宏任务和微任务。先执行同步任务,再执行异步任务里的微任务队列,微任务队列执行完之后执行宏任务队列。
顺序
任务执行顺序
- 宏任务:当前调用栈中执行的任务称为宏任务。(主代码快,定时器等等)。
- 微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务为微任务。(可以理解为回调事件,promise.then,process.nextTick等等)。
- 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。
举例:
setTimeout(function(){
console.log(1);
},0);
new Promise(function(a,b){
console.log(2);
for(var i=0;i<10;i++){
i==9&&a();
}
console.log(3)
}).then(function(){
console.log(4)
});
console.log(5);
// 2 3 5 4 1
// 同步任务>微任务>宏任务
nextTick的原理:
主要依赖于JS的事件循环机制。nextTick是将回调函数加入到一个异步队列中,之后nextTick内部会使用MutationObserver(用于监听DOM变化的API),当检测到DOM更新后,就会执行异步队列中的回调函数。
八、ajax请求
(1). 如何中断ajax请求?
一种是设置超时时间让ajax自动断开,另一种是手动停止ajax请求,其核心是调用XML对象的abort方法,ajax.abort()
(2)请求的四个步骤?
- 创建Ajax对象
if(window.XMLHttpRequest){
//创建Ajax对象 获取浏览器内部的一个XMLHttpRequest对象,创建该对象.(非IE6)
var xml= new XMLHttpRequest();
}else{
//ie6使用的自己特殊的东西
var xml = new ActiveXObject("Microsoft.XMLHTTP");
}
- 连接服务器
//open(方法,亟待读取文件名,默认为true,异步方式)
xml.open("get","ajax.htm",true);
- 发送请求
xml.send();
- 接受请求
判断浏览器和服务器的交互进度和结果
//接收返回
//0 (未初始化)在创建完XMLHttpRequest对象时
//1 (初始化)使用open()方法创建了HTTP请求时
//2(发送数据状态)使用send()方法发送数据时
//3(接收数据状态)服务器接收完数据并处理完毕之后,向客户端传送返回的结果
//4(完成)XMLHttpRequest对象接收数据完毕后
xml.onreadystatechange=function(){
//xml.readyState浏览器和服务器的交互情况
if(xml.readyState == 4)//读取完成
{
if(xml.status==200)//http状态码,用来判断是否成功接受并返回文件
{
alert(xml.status+" Success" +oAjax.responseText);//读取响应文件里面的TEXT
}else{
alert(xml.status+" NotFound");
}
}
};
(3)浏览器清除缓存
- 在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“If-Modified-Since”,“0”)。
- 在ajax发送请求前加上 anyAjaxObj.setRequestHeader(“Cache-Control”,“no-cache”)。
- 在URL后面加上一个随机数:“fresh=” + Math.random();
- 在URL后面加上时间戳:nowtime=" + new Date().getTime();
- 如果是使用jQuery,直接这样就可以了$.ajaxSetup({cache:false})。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录。
(4)优点:异步加载、局部刷新、提高用户体验
九、双向数据绑定
原理:主要使用Object.defineProperty的get和set方法对对象进行监听。
https://www.cnblogs.com/tugenhua0707/p/10261170.html
十、原型链与作用域链
(1)
- 作用域: 一块代码区域, 在编码时就确定了, 不会再变化
- 作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量
(2)
- 原型:prototype 。
- 继承:实例对象的__proto__指向构造函数的prototype,从而实现继承。
- 原型链是针对构造函数的,比如我先创建了一个函数,然后通过一个变量new了这个函数,那么这个被new出来的函数就会继承创建出来的那个函数的属性,然后如果我访问new出来的这个函数的某个属性,但是我并没有在这个new出来的函数中定义这个变量,那么它就会往上(向创建出它的函数中)查找,这个查找的过程就叫做原型链。
function a(){};
a.prototype.name = "追梦";
var b = new a();
console.log(b.name); //追梦
- 原型示例:
- 类数组对象转为数组:Array.prototype.slice.call(arguments)
问:为什么不能用 Array.slice.call(arguments)?
答:因为Array是一个类,不能直接引用,需要获取原型后才能使用。
如果要直接引用,需要实例化Array,如下。
var array = new Array();
array.slice.call(arguments);
十一、call和apply
- 区别:
call 和bind方法第一个参数是作为函数上下文的对象,但是后面传入的是一个参数列表,而不是单个数组。(bind不会立即执行,后面加上括号后才执行)
apply 方法传入两个参数:一个是作为函数上下文的对象,另外一个是作为函数参数所组成的数组。
var obj = {
name: 'linxin'
}
function func(firstName, lastName) {
console.log(firstName + ' ' + this.name + ' ' + lastName);
}
func.bind(obj, 'C', 'D')(); // C linxin D
func.call(obj, 'C', 'D'); // C linxin D
func.apply(obj, ['A', 'B']); // A linxin B
- 改变 this 指向
var obj = {
name: 'linxin'
}
function func() {
console.log(this.name);
}
func.call(obj); // linxin
- 借用别的对象的方法
var Person1 = function () {
this.name = 'linxin';
}
var Person2 = function () {
this.getname = function () {
console.log(this.name);
}
Person1.call(this);
}
var person = new Person2();
person.getname(); // linxin
从上面我们看到,Person2 实例化出来的对象 person 通过 getname 方法拿到了 Person1 中的 name。因为在 Person2 中,Person1.call(this) 的作用就是使用 Person1 对象代替 this 对象,那么 Person2 就有了 Person1 中的所有属性和方法了,相当于 Person2 继承了 Person1 的属性和方法。
- 调用函数
function func() {
console.log('linxin');
}
func.call(); // linxin
- 其他
Math.max(1,2,3) //3
Math.max.apply(null,[1,2,3]) //3 此方法可以获取数组的最大值
十二、localstorage和vuex区别
- localStorage的读取相当于一次磁盘读取、文件读取。而vuex的读取时内存读取,自然localStorage的读取会慢于vuex的读取。
- 为了减少localStorage的读取次数,即刷新或初次打开页面的时候读取一次localStorage的数据到vuex,之后要用直接去vuex里面拿就可以了。
十三、hash和history
- hash指url尾巴后的#号以及后面的字符。也称作锚点,本身是用来做页面定位的。hash值变化不会导致浏览器向服务器发出请求,而且hash改变会触发hashchange事件,浏览器的进后退能对其进行控制。
特点
:
不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。其次,hash的传参是基于url的,如果要传递复杂的数据,会有体积的限制。
window.location.hash='qq'//设置 url 的 hash,会在当前url后加上'#qq'
var hash = window.location.hash //'#qq'
window.addEventListener('hashchange',function(){
//监听hash变化,点击浏览器的前进后退会触发
})
- history模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中。
history利用了HTML5 History Interface 中新增的pushState()和replaceState()方法。 - 404
1、hash模式下,仅hash符号之前的内容会被包含在请求中,如 http://www.abc.com, 因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误;
2、history模式下,前端的url必须和实际后端发起请求的url一致,如http://www.abc.com/book/id 。如果后端缺少对/book/id 的路由处理,将返回404错误。
https://www.jianshu.com/p/ae8f9c41a77c
十四、高内聚低耦合
高内聚:指的是模块内部各个元素的关系。模块的内聚反映模块内部元素联系的紧密程度。主要是指一个模块内部是由相关性很强的代码组成,该模块只负责一个任务,即常说的单一职责原则。 例如系统中存在A、B两个模块进行交互,如果修改了A模块,不影响B模块的工作,反之,修改了B模块也不影响A模块工作,即A、B模块都各司其职,那么我们就认为A模块有足够的内聚。
低耦合:指的是模块间的关系。A模块与B模块存在依赖关系,当A模块发生改变时,B模块仍然可以正常工作,那么就认为A与B是低耦合的。
十五、vuex的理解
vuex是一个专门为vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态。它的核心是store(仓库)。“store”基本上就是一个容器,它包含着应用中大部分的状态(state),vuex和单纯的全局对象有以下两点不同:1.vuex的状态存储是响应式的,当vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应的得到高效更新;2.不能直接改变store中的状态,改变store中的状态的唯一途径就是显示的提交(commit)mutation。
- state:是存储的基本数据。
- mutations:提交更改数据。
- actions:处理异步,通过store.commit方法触发mutations中的方法,从而改变state值。
- getters:对state加工,和computed计算属性一样。
- module:是store分割的模块,每个模块拥有自己的state、mutations、getters、actions
十六、虚拟DOM实现原理
虚拟dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要 的 dom 操作,减少浏览器的回流和重绘,从而提高性能。具体实现步骤如下:
-
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树, 插到文档当中;
-
当状态变更的时候,重新构造一棵新的对象树。通过diff 算法,比较新旧虚拟 DOM 树的差异。
-
根据差异,对真正的 DOM 树进行增、删、改。
React通过 babel 转为 createElement 形式
Vue通过 vue-loader 转为 h 形式
- 重排和重绘
- 当HTML或CSS发生变化的时候,就会导致浏览器重新解析DOM树和CSSOM树,这就导致了重新布局和渲染,即reflow和repaint
- reflow:当元素的几何信息发生变化的时候,比如元素的大小、位置、结构、内容、字体大小等发生了变化,就需要浏览器重新解析,即reflow
- repaint:当页面元素的样式发生变化,并且并不银杏果i昂元素在文档流中的位置时,比如元素的颜色、背景颜色等外观发生变化的时候,就需要浏览器重新绘制,即repaint
十七、rem的适配计算
十八、webpack
其中UglifyjsPlugin是单线程,加上属性parallel: true就可以分给多个子进程去并行压缩,或者使用ParallelUglifyPlugin的插件。
Vite和webpack的区别
-
构建方式
Vite使用了一种新的构建方式,称为「原生ESM」构建。它利用浏览器原生支持ES模块的能力,通过将每个模块作为一个单独的文件进行构建,而不是像webpack那样将所有模块打包成一个文件。这种方式可以提供更快的冷启动和热更新速度。 -
开发服务器
Vite使用了一个基于ES模块的开发服务器,Vite在启动开发服务器时,会根据需要动态地生成和提供每个模块的依赖关系,而不是像webpack那样提前构建好整个应用的依赖关系。webpack也提供了一个开发服务器,但它是基于传统的构建方式,需要提前将所有模块打包成bundle文件,然后在浏览器中通过HTTP请求获取这些文件。
-
构建速度
Vite利用了浏览器原生支持ES模块的能力,冷启动和热更新时的速度比webpack更快。Vite可以在浏览器中动态地解析和编译模块,而不需要像webpack那样在每次修改代码后重新构建整个应用。
十九、组件通信
父——>子:props;
子——>父: e m i t , r e f ;兄弟—— > 兄弟,任意—— > 任意: emit,ref; 兄弟——>兄弟,任意——>任意: emit,ref;兄弟——>兄弟,任意——>任意:bus。
组件隔代通信可使用 a t t r s 和 attrs和 attrs和listeners,inject和provide
复杂关系:vuex
<script>
export default {
// 父组件通过provide将自己的数据以对象形式传出去
provide(){
return {
msg:'一条信息'
}
}
};
</script>
<script>
export default {
// inject:["msg"], // 孙组件使用一个注入的值作为数据入口
inject:{ //或者写成对象
// 使用一个默认值使其变成可选项
msg: {
from: 'msg',
default: ''
}
}
}
</script>
二十、小程序支付
一、小程序登录流程
- wx.getUserInfo授权获取用户信息nickname、avatar、country等
- wx.login获取临时登录凭证code码
- 向后台发送登录请求let data = await api_loginCode({code:code}),拿到data数据中的token,openId等
二、1. 微信支付:wx.requestPayment 发起微信支付
timeStamp 时间戳
nonceStr 随机字符串
package 统一下单接口返回的 prepay_id 参数值
signType 签名算法
paySign 支付签名
success 接口成功回调
fail 接口失败回调
complete 接口完成回调(成功,失败都执行)
- 小程序余额支付:
首先用passkeyborad的 ref让支付密码的数字弹窗展示,其次用@keyBack="keyboardBack"的密码输入完成后的事件调取接口,检查密码是否输入正确
二十一、双向数据绑定原理
Object.defineProperty 不能监听原生数组的变化。如需监听数组,要将数组转成对象。
二十二、vue3
vue3性能变化:打包大小减少41%,初次渲染快55%,内存减少54%
-
响应式的区别
2.0中vue的响应式是基于数据劫持(object.defineProperty这个方法来进行劫持的)当前这个方法是兼容主流浏览器的ie9以上都可以使用 他是可以监听数据的变化 从而让vue做出改变,但是他有一个bug object.defineProperty这个方法只能够监听初始化的时候数据 如果程序运行到一半你给data中的对象或者是数组添加新属性的时候 由于属性没有初始化监听,所以没有双向数据绑定,只能使用$set。3.0中 对2.0的bug进行了解决,3.0中的响应式是采用ES2015中最新的一个规范 Proxy来进行替代的 proxy是惰性监听(他不会在初始化的时候监听数据 而是在你使用数据的时候才回去监听)proxy是一个比较新的规范 ie还不支持 vue为了解决ie不支持的问题 所以单独给ie进行了适配,传统主流浏览器 使用proxy来进行监听,但是在ie中还是使用2.0中的object.defineProperty
-
属性声明方式
2.0中都是使用vue属性的方式来创建数据 方法 计算属性等内容的,3.0中 改变了 变成了 api方式进行创建 也就是或vue3.0把变量 方法 计算属性等内容封装成了一个个的方法 使用方法的方式进行调用 -
生命周期
-
组件通信
1.通过ref通信
// 子组件
const resetForm = (row) => { // 弹框打开时的回调
state.menuForm = {
ID: 0, // 默认值为0
type: 0, // 默认类型为0
isFrame: false, // 默认否
}
}
defineExpose({ // 导出需要被父组件调用的方法
resetForm,
})
// 父组件
<add-menu-dialog ref="addEl"/>
const addEl = ref()
addEl.value.resetForm()
2、通过defineProps和defineEmits
// 子组件
<el-dialog
v-model="props.dialogVisible"
:title="props.title"
width="600px"
:before-close="() => emit('close')"
/>
import {defineProps,defineEmits,defineExpose,ref} from 'vue'
const emit = defineEmits(['close']) // 声明触发父组件的方法
const props = defineProps({ // 声明接收过来的参数
dialogVisible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
isAdd: {
type: Boolean,
default: false,
},
})
// 父组件
<add-menu-dialog
ref="addEl"
:dialog-visible="state.isShow"
:title="state.isAdd ? '新增菜单' : '编辑菜单'"
:is-add="state.isAdd"
@close="close"
/>
5.目录结构
打包工具Vite替代了webpack;TS替代了JS;pinia替代了vuex;Element-plus替代了Element等等
vue2搭建:npm install -g @vue/cli (通过vue-cli脚手架工具生成webpack);vue create projectName2
vue3搭建:npm init vue@latest(通过安装执行create-vue脚手架工具生成vite)
二十三、TypeScript
https://blog.csdn.net/weixin_43638968/article/details/122962207?spm=1001.2014.3001.5502
二十四、VUE中的diff算法
二十五、闭包
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念
闭包有三个特性:
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
举例:function test(){ var a = 0; return function(){ console.log(a); } }
二十六、路由钩子
二十七、computed和watch
- 功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
- 是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
- 是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
- computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)
- 使用场景:computed----当一个属性受多个属性影响的时候,使用computed-----购物车商品结算。watch–当一条数据影响多条数据的时候,使用watch-----搜索框.
(watch: deep深度监听、immediate第一次改变就执行)
watchEffect和watch区别
•执行时机:watchEffect是立即执行的,在页面加载时会主动执行一次,来收集依赖;而watch是惰性地执行副作用,它不会立即执行,但可以配置immediate,使其主动触发,
watchEffect(0) => {
console. log (test.value)
})
watch (test.value, (val, oldval) => console. log (val)
},{ immediate: true }) // 两者效果差不多
•参数不同:watchEffect只需要传递一个回调函数,不需要传递侦听的数据,它会在页面加载时主动执行一次,来收集依赖;而watch至少要有两个参数 (第三个参数是配置项),第一个参数是侦听的数据,第二个参数是回调函数
•结果不同:watchEffect获取不到更改前的值;而watch可以同时获取更改前和更改后的值
二十八、小程序优化
- 非核心页面转成h5,通过webview显示,节省内存
- 图片、音频等资源较大的文件可放到cdn服务器中,图片加密可用base64,视频加密可用blob格式。(其中base64可使用canvas.toDataUrl转换,blob可用xml.responseType=“blob”)
- 一些城市信息,服务条款等静态化数据首次加载完可缓存到本地
- 开发层面减少重复,提取公共模块,组件化。
- 配置分包,主包2兆,分包20兆
- 通过umtrack-wx进行数据埋点和数据上报
(小程序生命周期:onLoad、onShow、onReady、onHide、onUnload)
(小程序跳转:navigateTo、navigateBack、redirectTo、switchTab、reLanch)
二十九、前端性能优化
https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899638&idx=1&sn=5571863db55c99029f68075f72008067&chksm=8c5fa4b8bb282dae576cb38ff03e85dc138ac38806be06627c753ae431d6b81851a4468728b9#rd
1、session和cookie相对于token来说,cookie会在浏览器之间来回传递,不仅不安全,还会增加服务器负荷影响加载速度,用token更具有安全性和扩展性
2、优化高频事件:scroll,touchMove,搜索等尽量用节流防抖。
3、在生成海报的时候可以用canvas.toDataURL(),将图片转为base64,用以压缩图片。
4、封装一些公共方法,调用的时候直接引入。
5、使用CDN,把静态资源放到互联网上的各个服务节点上,提高用户访问速度和稳定性。
6、可以用uglifyjs-webpack-plugin压缩js文件(压缩之后还是很大可以在入口起点配置多个入口文件),用clean-webpack-plugin清除上次生成的dist文件。
7、老项目npm run dev本地构建热更新卡顿“75% advanced module optimization”的解决方案:npm i babel-plugin-dynamic-import-webpack,然后再配置文件.babelrc中写入"env": { "development": { "plugins": ["dynamic-import-webpack"] } }
三十、reactive与ref区别有以下几方面:
1、 从定义数据方面:
ref通常用来定义基本类型数据
reactive用来定义:对象(或者数组)类型数据
ref也可以用来定义对象或者数组类型的数据,内部会通过reactive转为代理对象
2、从原理方面:
ref通过Object.defineProperty()的get和set实现数据代理。
reactive使用Proxy实现数据代理,并且通过Reflect操作源对象内部的数据。
3、从使用方面:
ref操作数据需要.value,template模板中不需要。
reactive都不需要,value
三十一、React与Vue区别有以下几方面:
1、 vue中有很多的指令,react中只需要使用单花括号就行。
https://baijiahao.baidu.com/s?id=1765496734709504410&wfr=spider&for=pc
三十二、for in和for of
for in适合遍历对象,for of适合遍历数组;
- for in遍历数组时会把可枚举属性遍历出来,所以不能使用for in。
- 因为for of遍历普通对象会报错。如果是类数组对象(有length属性并且键值从0开始的对象)需要用Array.from先转化成以value值为元素的数组;如果是非数组对象可以使用Object.keys()转化成以键值为元素的数组。
- for in遍历的是字符串类型的key值,for of遍历的是value值,类型取决于value的数据类型
三十三、 Typescript
Typescript 支持类型最大的好处是可读性
为什么要用TypeScript ??
(一)类型检查,语法提示
在TS中允许你为变量指定类型。当你给已定义类型的变量赋值的时候,值的类型不对,便会有错误提示
(二)约束类型,减少不必要的代码逻辑
开发条件:1. 大型项目,代码量较多 2. 进行封装,组件化开发的时候(有一定开发基础)
//定义一个函数计算二个数据的合计
function sum(x,y){
if(typeof x != 'number') { //对于形参的类型要添加转换
x = parseInt(x);
}
return x+y
};
sum('1',2);
//TS的方式,直接约束了类型
function sum2(x:number,y:number){
return x+y
};
// 布尔类型
let isShow = ref<boolean>(false);
// 数字类型
let age = ref<number>(18)
区别一
1、any类型表示任意类型,放弃了ts类型检查,ts中应该少用
2、unkonw类型是暂时未知类型(之后会知道),仍然会进行ts的类型检查
3、在ts的类型系统中never(空类型)是最窄的类型,any是最宽的类型
区别二
1.扩展方式不同
// interface使用extends
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
}
// type使用联合类型或者交叉类型
type Cat = Animal & {breed: string;}
2.同名处理不同:interface 同名时会自动合并成一个接口,type 同名时会报错。
interface Person {
name: string;
age: number;
}
interface Person {
gender: string;
}
const alice: Person = { name: 'Alice', age: 30, gender: 'female' }; // 合并后的 Person 接口包含 name、age 和 gender 属性
type Animal = {
name: string;
age: number;
};
type Animal = { // Error: Duplicate identifier 'Animal'
breed: string;
};
3.用途不同:interface 更适合用于定义对象的形状,type 更适合用于定义联合类型、交叉类型和其他高级类型。
interface Person {
name: string;
age: number;
}
type PersonOrAnimal = Person | Animal;
type PersonAndAnimal = Person & Animal;
type PartialPerson = Partial<Person>;
区别三
- typeof 可以获取一个值的类型和函数的类型
- keyof用于获取一个类型的所有属性名构成的联合类型。
//typeof
const num = 1;
type NumType = typeof num; // NumType 的类型为 number
function add(a: number, b: number) {
return a + b;
}
type AddType = typeof add; // AddType 的类型为 (a: number, b: number) => number
//keyof
interface Person {
name: string;
age: number;
gender: 'male' | 'female';
}
type Boy = keyof Person // 'name' | 'age' | 'gender'
三十四、 cookie session token
三十五、 跨域
- CORS
后端使用Access-Control-Allow-Origin,Access-Control-Allow-Credentials等。前端可以设置请求头setRequestHeader(‘origin’:‘http://www.baidu.com’)和是否携带cookie:axios.defaults.withCredentials = true
-
dataType:“jsonp”
-
proxy代理
在config.js里
正向代理是代理客户端,反向代理是代理服务器。
前端的域名为:bc.server.com,后端服务的域名为:dev.server.com。现在如果bc.server.com向dev.server.com发请求一定会出现跨域问题。
现在我们只需要启动一个nginx服务器,将server_name 设置为bc.server.com,然后设置相应的location以拦截前端需要跨域的请求,最后将请求代理回dev.server.com,如:
server{
listen 80;
server_name bc.server.com;
location /{
proxy_pass dev.server.com;
}
}
//这样就可以绕过浏览器的同源策略
三十六、判断数据类型
1、(typeof Null ==‘Object’)
对于 Array, Null 等特殊对象使用 typeof 一律返回 Object;typeof返回的结果是字符串,返回值有number、string、boolean、function、undefined、object六个
2、(a instanceof Array)
用于弥补typeof不能判断array和null
3、 Array.isArray([1,2,3,4]);
三十七、盒模型和捕捉冒泡
- CSS3中盒模型有两种标准盒模型和IE盒模型两种盒模型是content+padding+border+margin构成,其大小都是由content+padding+border决定的
- 两种盒模型的唯一不同之处在于盒子内容宽/高度的计算范围不同
- 标准盒模型(box-sizing:content-box)在计算宽和高的时候只计算content内容的宽高。
- IE盒模型(box-sizing: border-box)在计算宽高时包含content+padding+border的宽高。
捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)。
冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签。
避免冒泡:event.stopPropagation()
在vue中可以用.stop避免冒泡:
<div @click.stop="handleEdit(row)">编辑</div>
三十八、小程序埋点
引入友盟小程序sdk
前端埋点与日志系统架构设计
三十九、TCP/IP
四十、resetfulAPI
四十一、快速监听线上bug
四十二、监测浏览器卡顿
四十三、从输入url到页面加载发生了什么
1、浏览器的地址栏输入URL并按下回车。
2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
3、DNS解析URL对应的IP。
4、根据IP建立TCP连接(三次握手)。 SYN和ACK包的传递
5、HTTP发起请求。
6、服务器处理请求,浏览器接收HTTP响应。
7、关闭TCP连接(四次挥手)。FIN和ACK包的传递
8、渲染页面,构建DOM树。
四十四、Node.js
-
连接mongodb数据库
在 app.js 文件中通过mongoose.connect()连接到本地的mongodb数据库,然后监听数据库连接的状态,如果连接成功,则打印出 “Database connected!” 的信息 -
创建数据模型
在 models 文件夹下创建一个新的文件 user.js,用于定义用户数据模型,名为 User 的数据模型,包含三个属性:name、age、gender -
增操作
在 app.js 文件中,使用 app.post() 方法监听 ‘/users’ 的 POST 请求,将请求体的参数转换生成一个新的 User 对象,并保存到数据库中,最后响应请求。 -
删操作
使用 app.delete() 方法监听 ‘/users/:id’ 的删除请求,通过用户的 ID 找到需要删除的用户,然后调用 User.deleteOne() 方法从数据库中删除用户 -
改操作
使用app.put()监听,调用User.updateOne() 更新 -
查操作
使用app.get()监听,调用User.find() 查找 -
运行node app.js 启动服务器
更多推荐
所有评论(0)