基础类问题
h5的新特性
html5 | 备注 |
---|---|
只有一种 DOCTYPE ⽂件类型声明(统 一标准) | <!DOCTYPE html> |
增加了一些新的标签元素(功能, 语义化) | section, video, progress, nav, meter, time, aside, canvas, command, datalist, details, embed, figcaption, figure, footer, header, hgroup... |
input 支持了几个新的类型值 | date, email, url 等等 |
新增了一些标签属性 | charset(⽤于 meta 标签);async(⽤于 script 标 签) |
新增的全域属性 | contenteditable, draggable... hidden... |
新增API | 本地存储, 地理定位, Canvas绘图, 拖拽API, 即时通信 WebSocket |
css3新增的特性
- 伪元素
- 弹性布局flex
- 媒体查询
- 圆角
- 渐变
- 阴影
- 平面转换
- 3D转换
- 动画
盒子/图片水平垂直居中
- flex弹性布局,父盒子设置
display: flex
,设置justify-content: center
和align-items: center
- 子绝父相,父盒子
position: relative
,子盒子position: absolute
,并设置top: 50%
、left: 50%
,让子盒子偏移父盒子的一半,再使用transform: translate(-50%, -50%)
往回再偏移自身的一半,就可以实现垂直、水平居中
css盒模型
- CSS盒模型,是描述HTML元素的一种方法,每个HTML元素都是一个盒子,其中包含内边距(padding)、边框(border)、外边距(margin),通过以下属性控制元素的外观和布局:
- width和height,控制元素的内容区域的大小
- padding,用于控制元素的内边距
- border,用于控制元素的边框
- margin,用于控制元素的外边距
- box-sizing属性,用来控制盒模型的类型,默认情况下,HTML元素使用的是标准盒模型content-box,在这个模式下,元素的宽高指的是内容区域的大小,不包括元素的padding内边距和border边框。而切换为border-box,则元素的宽高除了包含内容区域大小,还包括padding和border
块级元素和行内元素
注:元素的显示模式,可以通过display
属性来切换
- 块级元素
- 独占一行
- 可以设置宽高
- 可以设置padding内边距和margin外边距
- 常见块元素:div、h系列标签、p、form、header、footer、section等
- 行内元素
- 一行可以显示多个
- 不可以设置宽高,宽高由内容撑开
- 只能设置padding内边距,设置margin外边距会无效
- 常见行内元素:a、span、button、input、label、select等
css选择器权重值
- 在CSS中,选择器的权重值,也就是CSS的优先级,是用于确定哪些样式应该应用到HTML元素的一种机制。选择器的权重值,由2部分组成:选择器的特殊性和样式定义的位置
- 选择器的特殊性是根据选择器中使用的各种类型的选择器来计算的。每种类型的选择器都有一个固定的权重值,如下表所示:
选择器类型 | 权重值 |
---|---|
通配符(*) | 0 |
元素选择器(例如p) | 1 |
类选择器(例如.class) | 10 |
ID选择器(例如#id) | 100 |
属性选择器(例如[type]) | 10 |
伪类选择器(例如:hover) | 10 |
伪元素选择器(例如::before) | 10 |
- 为了计算选择器的特殊性,需要对选择器中使用的每种类型的选择器的权重值进行加权。例如,如果选择器为#id .class p,则其特殊性为(100 * 1) + (10 * 1) + (1 * 1) = 111
- 使用!important来强制应用样式规则,但!important不能提升继承的样式的优先级
H5事件
- 注:如果使用addEventListener,事件名要去掉on开头
- onblur失焦事件
- onfocus聚焦事件
- onchange改变事件
- onclick点击事件
- onerror错误事件
- oninput输入事件
- onkeydown键盘按下事件
- onkeyup键盘抬起事件
- onmousemove鼠标移动事件
- onmouseover鼠标进入事件
- onmouseout鼠标移出事件等
H5中input元素的type属性值
- color颜色
- password密码
- text文本
- checkBox复选框
- radio单选框
- date日期
- button按钮
- submit提交按钮等
js数据类型
-
JavaScript有七种数据类型
-
简单数据类型
- 字符串(String)
- 数字(Number)
- 布尔(Boolean)
- 未定义(undefined)
- 空值(null)
- 符号(Symbol)
- 大整数(BigInt)
-
复杂数据类型
- 对象(Object)、函数(Function)
- 数组(Array)
-
-
使用typeof运算符,来检查一个变量的数据类型
let num = 42;
console.log(typeof num); // "number"
let str = "hello world";
console.log(typeof str); // "string"
let bool = true;
console.log(typeof bool); // "boolean"
let obj = {name: "John", age: 30};
console.log(typeof obj); // "object"
let arr = [1, 2, 3];
console.log(typeof arr); // "object"
let func = function() {};
console.log(typeof func); // "function"
let undef;
console.log(typeof undef); // "undefined"
let nul = null;
console.log(typeof nul); // "object"
- 注意,使用typeof运算符检查数组和null的数据类型时,会返回"object"。因此,如果你想要确定一个变量是否是数组或null,应该使用
Array.isArray()
方法来检查是否为数组,通过变量 == null
来判断是否为null
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
let nul = null;
console.log(nul == null); // true
- 此外,JavaScript还有一种特殊的数据类型叫做符号(Symbol),它用于创建唯一的值。你可以使用Symbol()函数来创建一个符号,例如:
let sym = Symbol();
console.log(typeof sym); // "symbol"
- 符号是JavaScript的一种新的数据类型,可以用来创建唯一的标识符,例如在对象的属性名称中使用。因为符号是唯一的,所以你可以使用它来防止属性名称冲突。例如:
let sym1 = Symbol();
let sym2 = Symbol();
let obj = {
[sym1]: "hello",
[sym2]: "world"
};
console.log(obj[sym1]); // "hello"
console.log(obj[sym2]); // "world"
- 注意,使用符号作为对象属性名称时,需要在属性名称前后添加方括号,这样才能正确地将属性名称解析为符号
回流和重绘
- 回流(reflow)是指当浏览器渲染HTML页面时,对页面布局和几何的计算过程。当页面中的元素的大小、位置或布局方式改变时,浏览器就需要进行回流来调整布局
- 重绘(repaint)是指当浏览器渲染HTML页面时,对页面中元素的外观进行更新的过程。当页面中元素的颜色、字体或其他外观属性改变时,浏览器就需要进行重绘来更新外观
- 回流和重绘都是浏览器在渲染HTML页面时执行的必要过程,但是它们都有一定的开销,因此应该尽量避免不必要的回流和重绘。例如,你可以使用优化的CSS选择器和结构来减少回流的次数,或者使用缓存来避免重复的重绘
- 注意,在JavaScript中有一些操作会触发回流和重绘,例如:
- 改变HTML元素的尺寸或位置,例如使用offsetWidth、offsetHeight、offsetLeft、offsetTop等属性
- 改变HTML元素的布局方式,例如使用display、position、float等属性
- 改变HTML元素的外观,例如使用color、background-color、font-size等属性
- 你可以使用浏览器的开发工具来查看页面的回流和重绘次数,以及每次回流和重绘的原因。这有助于你发现并优化页面的性能问题
- 此外,你还可以使用requestAnimationFrame()函数来让浏览器在下一次重绘之前执行特定的代码。这可以让你在更新页面元素的外观时,将多个操作合并成一次重绘,从而减少重绘的次数。例如
function updatePage() {
// 更新页面元素的外观
requestAnimationFrame(updatePage);
}
updatePage();
- 注意,requestAnimationFrame()函数是异步执行的,因此你不能依赖它来控制代码的执行顺序
闭包
- 函数中声明的变量,基本数据类型会放在栈中,引用数据类型会放在堆中,当函数执行完后,这些变量就会被回收,闭包则是个例外,它能让使用到的外层函数的变量不会被回收,而是随着内层函数存活下来
- 闭包的表现形式:外层函数嵌套内层函数,内层函数使用了外层函数的变量,外层函数返回该内存函数
- 闭包的作用:实现数据私有化、实现防抖和节流
- 闭包的问题:由于产生闭包后,内存函数持有了外层函数的变量,这些变量不会被垃圾回收器所回收,所以会生产内存泄漏,解决办法是将内层函数变量设置为null,置空,让垃圾回收器进行回收
原型和原型链
- 在JavaScript中,每个对象都有一个原型对象,它可以定义一些属性和函数,这些属性和方法,可以被对象所继承,这种继承机制就是原型链
- 原型链的查找机制,当访问一个对象的属性时,优先查找对象身上的属性,如果没有找到,则查找对象的原型对象身上的属性,如果也没有找到,则查找原型对象的原型对象,以此类推,直到找到Object为止,就是为null,就会报错
this指向问题
- 在全局作用域中调用函数,this为window
- 在对象中的this,this为对象本身
- 在构造函数中的this,this为该构造函数准备要构造出来的实例
- 在DOM事件回调函数中,this为触发该事件的DOM元素
- 箭头函数中的this,箭头函数没有自己的this,而是沿用上级作用域的this
- 通过call、apply、bind来改变函数的this指向
数组中forEach和map
- 作用不同,forEach是遍历数组,map是映射数组
- 返回值不同,forEach没有返回值,而map有返回值,返回的是映射后的新数组
- 处理流程不同,forEach是顺序遍历所有元素,执行回调函数,就结束。而map在这个基础上,还收集回调函数的返回值,收集到一个新数组中,执行完毕后返回收集的新数组
call、bind、apply
- call、bind、apply都可以改变函数的this指向
- call和apply,在改变完this指向后,就会马上执行该函数。而bind是改变完this指向后,返回一个新函数
- call要求传入的函数形参是按顺序,一个个传入。而apply则是要求参数放到一个数组中,再传入
new操作符具体过程
- 构造函数创建对象时,先在堆中创建对象,给对象分配内存
- 将构造函数的this指向该对象
- 执行构造函数中的代码,一般为使用this给对象添加属性和方法
- 最后构造函数执行完毕,返回该对象出去
浅拷贝和深拷贝
- 浅拷贝,复制简单数据类型时,复制的是值,而复制引用数据类型时,复制的是对象的内存地址,所以修改浅拷贝后的对象的属性和方法时,会影响原始对象,因为它们都指向同一个内存地址
- 深拷贝,在复制引用数据类型时,会在堆中创建一个新的对象,将原始对象的数据拷贝过去,由于2个对象指向不同的内存地址,所以修改深拷贝后的对象的属性和方法时,不会影响原始对象
- 浅拷贝
- 通过Object.assign
- 通过展开运算符
- 深拷贝
- 通过JSON序列化来实现,JSON.stringify()和JSON.parse(),无法处理函数、undefined、环形结构数据
- 通过递归来实现,无法解决环形结构数据
- 通过第三方库,loadash
js事件循环
- JavaScript是一种单线程语言,所以同一时刻只能执行一个任务。那么遇到耗时任务就会阻塞JS线程,导致卡顿。JS通过事件循环来解决这个问题,将耗时任务,例如网络请求,交给JS引擎,也就是浏览器来执行,浏览器执行完毕后,通过回调通知JS线程来继续处理网络请求的响应结果
- 事件循环,有3个重要的角色,执行栈、微任务、宏任务,微任务有Promise.then,宏任务有DOM事件回调、定时器、延时器、Ajax网络请求等
- 当JS执行到同步任务时,会立即交给执行栈去执行,遇到微任务则加入到微任务队列,遇到宏任务则加入到宏任务队列。
- 当所有同步任务执行完毕后,就会去查看微任务队列中,是否存在微任务,若存在微任务,则取出一个微任务给执行栈进行执行,当所有的微任务都执行完毕,则去宏任务队列查看是否有宏任务
- 若宏任务队列中,存在宏任务,则取出一个宏任务去执行栈中执行,执行完一个宏任务就去检查微任务列表,优先执行微任务,所以执行完一个宏任务就会去执行完所有微任务,微任务的优先比比宏任务高,因为微任务一般是一些比较紧急的任务,这样的设计能让微任务有一个插队的可能性
防抖和节流
- 防抖和节流,都是一种性能优化的手段,都是控制事件触发的速率。
- 防抖,是指频繁触发的事件,只能有最后一次生效。一般使用延时器来实现,当第一次事件到来时,开启一个延时器,在延时时间还未到时,又来一个事件的话,就会取消延时器,重新再开启一个。来达到只有用户停止行为后一段时间,才能触发一次事件的目的。例如:礼品兑换按钮,防止用户疯狂点击,短时间内发出多次请求
- 节流,效果相当于一个节流阀,事件不断触发,一个单位事件内,只能触发一次。一般使用延时器来实现,当第一次事件到来时,会开启一个延时器,同时将定时器id赋值给外层函数的变量上,下一次事件到来时,判断定时器id是否为空,如果不为空,就代表要被过滤掉,除非到达一次延时时间,定时器id会被重置为空,下一次事件就能触发。例如:音视频进度条变化,需要记录到本地存储,1秒内频繁触发多次,值变化不大,频繁更新存储没有必要
ES6新增方法
- left、const,它们都是用来声明变量,而let能赋值多次,const只能赋值一次。let有块级作用域,var没有
- 箭头函数,一种简化函数的写法,会比传统函数写法更简洁。注意,箭头函数没有自己的this,而是沿用上一个作用域的this
- 模板字符串,使用反引号来定义字符串,比传统的使用+号来拼接字符串,更加简洁
- 解构赋值,能对对象、数组进行解构,提取对象的某个属性、数组的某个索引的元素
set和map的区别
- set是一种无序集合,存储的元素不能重复,set使用for-of循环来遍历
- map是一种键值对集合,key唯一,不能重复,而value可重复,同样可以使用for-of进行遍历
点击穿透现象及解决办法
- 点击穿透,常见于使用了透明背景和半透明背景的元素上
- 给元素,设置背景不透明
- 给元素,设置边框
- 使用JS,给DOM元素添加点击事件,通过event事件对象的
event.preventDefault()
,阻止默认的点击行为 - 通过CSS,给DOM元素设置
pointer-events: none
属性,禁用元素的点击事件,来防止点击穿透的问题 - 通过CSS的:active伪类,使用:active伪类来模拟点击效果,例如:
button:active {
background-color: blue;
}
js的继承
- 原型链继承:让子类的原型,等于父类的实例对象
- 可继承:实例的构造函数的属性,父类的构造函数属性,以及父类原型的属性
- 不可继承:父类实例的属性
- 借用构造函数继承:使用apply()和call()方法,将父类构造函数引入子类函数
- 可继承:父类构造函数的属性
- 不可继承:父类原型的属性
- 组合继承:将原型链和借用构造函数的技术组合在一块,从而发挥两者之长的一种继承模式
- 原型式继承:借助原型可以基于已有的对象创建新对象,类似复制了一个对象
- 寄生式继承:就是给原型式继承外面套个壳子,没用到原型
- 寄生组合式继承:通过借用函数来继承属性,通过原型链的混成形式来继承方法(常用)
如何理解promise
- Promise是一种异步编程的解决方案,使用链式编程,解决回调地狱的问题。通过then处理多个异步操作,catch处理异常
- 例如在JS中,使用axios发送网络请求,使用Promise来处理请求结果
axios.get('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
- Promise的原型还有一些静态方法,
- Promise.all,并行执行多个Promise
- Promise.race,处理多个Promise中,最先完成的那个Promise
- Promise.resolve,创建一个完成状态的Promise
- Promise.reject,创建一个拒绝状态的Promise
async/await的理解
- async和await是Promise的语法糖,异步编程的终极形态,能让异步代码写成同步代码的样子,代码可读性比Promise更直观
- 要使用await,要求函数必须使用async关键字修饰函数
- await会等待Promise的成功状态,当状态没有触发时,就会一直阻塞线程,当变为成功状态后,就会继续执行。如果Promise变为拒绝状态,则抛出一个异常,开发者需要通过try-catch进行捕获来处理拒绝状态
async function getData() {
const result = await fetch('https://example.com/data');
const data = await result.json();
return data;
}
作用域
- 作用域,指的是变量能被访问的范围
- 作用域分为全局作用域和局部作用域,局部作用域又分为块级作用域和函数作用域
- 全局作用域,全局作用域下声明的变量,为全局变量,在任何位置都能访问
- 块级作用域,在if、for等区域声明的变量,只能在这个区域内访问,出了这个区域访问变量就会报错
- 函数作用域,在函数中声明的变量,只能在函数内访问,函数体外访问变量,就会报错
事件对象和事件委托
- 事件对象,DOM元素设置事件监听,当元素触发事件时,执行事件回调函数,都会传入一个事件对象
- 通过事件对象的target属性,就能访问到触发事件的那个DOM元素实例
- 通过事件对象的
event.preventDefault()
方法,能阻止DOM元素的默认行为 - 通过事件对象的
event.stopPropagation()
方法,能阻止事件冒泡
- 事件委托,将事件监听设置给父元素,子元素触发事件时,会冒泡给父元素的事件处理函数。事件委托的核心是事件冒泡,如果监听的事件不会事件冒泡,则事件委托无法执行
- 事件委托能提升性能,因为只需要绑定一个事件监听,就能处理多个DOM元素的事件
sessionStorage和localStorage的区别
- 相同点
- 都是用来存储数据,都能使用
setItem
保存数据,getItem
获取数据,removeItem
删除数据
- 都是用来存储数据,都能使用
- 不同点
- 存储范围不同,sessionStorage的存储有效期在一次会话中,一次会话就是从打开网站到关闭浏览器的期间,当关闭浏览器时,就会清除掉sessionStorage中存储的数据。而localStorage是持久化存储,除非用户手动清除,否则localStorage存储的数据永久存在
- 存储容量不同,一般sessionStorage的容量会比localStorage的容量要小。localStorage的容量一般为5M到20M左右
rem和em
- 相同点:rem和em,都是用来设置字体大小的单位
- 不同点,rem是相对HTML根元素的字体大小来计算,而em相对于父元素的字体大小来计算
- 所以,rem一般用于设置整个网页的字体大小,而em一般用于设置单个元素的字体大小
响应式布局
- 响应式,指的是布局能根据不同设备的尺寸、分辨率,能自适应显示不同的布局
- 使用流式布局,通过百分比来设置元素的宽度、高度,而不是写死px值
- 使用flex弹性布局,通过让元素自动遵循一定的规则进行布局,在不同尺寸下自动调整大小
- 使用grid自适应网格布局,在不同尺寸下,自动调整网格的列数、行数
- 使用MediaQuery媒体查询,配置多种尺寸的CSS样式,来实现屏幕适配
有没有用过视频组件
- 一般使用H5提供的
video
标签,通过它可以实现视频的播放、暂停、快进、快退,设置视频的宽度、高度、是否自动播放等
<video src="video.mp4" width="480" height="360" autoplay></video>
- 在vue中,一般使用
vue-core-video-player
组件来实现视频播放
为什么会跨域 ?
- 跨域是浏览器的一种同源安全策略,它会限制2个URL之间的Ajax访问,2个URL分别是网站的URL和Ajax访问的URL
- 同源,要求URL的3部分,必须完全相同
- 协议
- IP或域名
- 端口
- 3个部分,有一个部分不同,就是不同源,就会产生跨域
- 注意:跨域只会产生在浏览器,服务器和服务器之间的请求是不会受到跨域限制的
不受同源策略影响的有哪些
- 除了,以下三个资源获取类型的标签,在浏览器中,⼤部分内容都受同源策略限制
<img>
<link>
<script>
跨域怎么处理 ?
- 通过代理服务器,本地搭建一个代理服务器,通过代理请求来规避跨域
- 使用CROS跨域资源共享,让服务端配置允许跨域
宏任务和微任务
- 宏任务、微任务,都是异步任务,不同的任务,有不同的任务队列来存放任务
- 常见的宏任务:定时器、延时器、Ajax请求、DOM元素事件监听
- 常见的微任务:Promise.then()
ES6中var let const的区别
- var存在变量提升,使用var声明的变量,将会提升到作用域的最顶层。而let、const没有变量提升
- var没有块级作用域,而let、const有块级作用域
- 如果变量声明时,没有写关键字,则为var,会导致声明的变量为全局变量,可能会覆盖已有的全局变量,产生意外的结果
- cosnt用于声明常量,只能赋值一次,而let和var可以赋值多次
JS的数据类型,储存上的差距
- JS的5种简单数据类型,布尔值(boolean)、数字(number)、字符串(string)和空值(null)、未定义(undefined),分配在栈中,占用字节数都很少,只要几个字节
- 而引用数据类型,数组(array)、函数(function)、对象(object)等,分配在堆中,占用的字节数一般会比较大
构造函数在被实例化的时候内部发生了什么
for of和 for in的区别
- for-of用于遍历数组,而for-in用于遍历对象
- for-of和for-in,都可以使用break来结束循环,通过continue来跳过本轮循环
- for-in,会遍历对象的所有可枚举属性,包括对象的原型上的属性。如果不希望遍历到非对象本身的属性,则可以通过
hasOwnProperty
来判断属性是否为对象本身的属性后,再遍历该属性 - for-in遍历对象的属性顺序是不固定的,如果希望按顺序遍历属性,则可以通过
Object.key()
来获取对象的所有属性数组,再通过for-of来遍历这个数组
什么是同步异步
- 由于JS是一个单线程语言,所以某一时刻只能执行一个任务。如果遇到耗时任务,使用同步的方式执行,JS线程会阻塞,导致页面卡顿。所以JS提供了异步编程,将耗时异步任务交给浏览器本身去执行,JS只执行同步任务,当浏览器执行完耗时异步任务后,通过回调、事件通知等方式,通知JS线程进行下一步操作
深拷贝 为什么他能做到互不影响
- 深拷贝,拷贝引用数据类型时,是在堆中重新开辟一个内存空间,再赋值对象数据过去。由于是2个内存地址的对象,修改复制后的对象,自然不会影响到原始对象
事件代理和场景
- 事件代理,又叫事件委托,核心是事件冒泡,如果监听的事件不支持冒泡,则无法实现事件委托
- 使用场景:动态新增元素,监听动态元素的事件、提升性能,只需一个监听监听,则可以监听和处理多个子元素的事件
Object.defineProperty有几个参数?每个参数做了什么?
- Object.defineProperty,有3个参数,分别是:
- obj,目标对象
- prop,要被定义或修改的属性名称
- descriptor,属性描述符,也就是配置对象,有以下可选配置
- value,要设置的属性的值,默认为undefined
- writable,代表该属性是否可写,可写时,通过赋值运算符可更改属性的值,默认为false
- enumerable,代表该属性是否可被枚举,默认为false
- configurable,代表该属性是否允许被delete运算符删除,默认为false
let obj = {};
Object.defineProperty(obj, 'foo', {
value: 'bar',
writable: true,
enumerable: true,
configurable: true
});
- 除了上面的配置选项外,还可以提供set、get回调函数
- set回调函数:当属性被设置时回调
- get回调函数,当属性被访问时回调
- 通过这2个回调函数,可以实现属性劫持,实现数据和视图的双向绑定
let obj = {};
Object.defineProperty(obj, 'foo', {
get: function() {
return this._foo;
},
set: function(value) {
this._foo = value;
}
});
纯函数是什么
- 纯函数,也是函数,但必须符合以下条件
- 纯函数需要传入的参数,每次传入相同的参数,函数返回的结果都必须一致
- 纯函数只依赖传入的参数,不会依赖外部的变量,也不会改变外部的变量
- 如果传入的参数一致,但返回的结果不一致,那么就不是纯函数
怎么创建一个文档碎片
- 通过
document.createDocumentFragment()
方法来创建一个文档碎片 - 文档碎片有一个特点,当需要添加多个DOM元素时,如果先将元素添加到文档碎片中,再将这个文档碎片添加到页面,能减少DOM渲染的次数,效果会提升。Vue的底层就是使用了文档碎片来提升页面渲染性能
工具类问题(git&webpack)
git命令、git的工作区有哪些
- 常用git命令
- git add,添加文件到暂存区
- git commit,提交文件到本地,形成版本
- git push,推送提交到远程
- git branch,查看本地分支
- git branch 分支名,新建分支
- git reset --hard 提交记录id,回滚本地记录到指定记录
- git statsh,暂存当前的修改
- git statsh pop,弹出最近的那一条暂存修改记录
- Git 工作区包括以下几个部分:
- 本地仓库:本地仓库是 Git 管理的文件的集合,它存储在本地磁盘上
- 工作目录:工作目录是用来修改和查看文件的地方。它包含了本地仓库中的文件的实际内容,也就是我们平常使用的文件
- 暂存区(Staging Area):暂存区是用来准备下一次提交的地方。当我们在工作目录中修改了文件,如果想要把这些修改提交到本地仓库,就需要先把修改的文件添加到暂存区
- 版本库(Repository):版本库是用来存储所有提交的地方。它包含了所有的提交历史,以及每次提交时本地仓库的快照
- 在 Git 中,我们通常会在工作目录中修改文件,然后使用 git add 命令将修改的文件添加到暂存区,再使用 git commit 命令将暂存区中的修改提交到本地仓库。本地仓库中的文件就会更新为最新的版本,同时也会把提交记录存储到版本库中
webpack是什么?
- 定义:是一个打包模块化的工具,在webpack中一切文件皆为模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件
- 作用:开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、Css 等语法,才能运行。所以我们需要打包工具帮我们做完这些事。除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等
- 配置
- 入口:指示webpack使用哪个模块作为构建其内部依赖图的开始,默认值是:‘./src/index.js’
- 出口:告诉webpack在哪里输出它所创建的bundle,以及如何命名这些文件,主要输出文件的默认值是‘./dist/main.js’
- mode:配置模式,development(开发环境)、production(生产环境)、none(不使用任何默认优化选项)
- loader:自带能力,用于转换某些类型的模块
- plugin:打包优化,资源管理,注入环境变量等
- 流程
- 初始化参数
- 开始编译
- 确定入口
- 编译
- 完成模块编译
- 输出资源
- 输出完成
webpack版本号
- 2015,webpack1支持CMD和AMD,同时拥有丰富的plugin和loader,webpack逐渐得到广泛应用。
- 2016,webpack2相对于webpack1最大的改进就是支持ES Module,可以直接分析ES Module之间的依赖关系
- 2017,webpack3相对于webpack2,过渡相对平稳,但是新的特性大都围绕ES Module提出
- 2018年,webpack4,可以零配置运行,打包速度比之前提高了90%
- 2020年,webpack5,对构建速度做了突破性的改进,开启文件缓存之后,再次构建,速度提升明显
vite和wabpack的区别
- 打包方面:webpack是先打包,打包好了,再启动开发服务器,所以项目越大,打包越慢。vite是先启动服务器,请求到哪个模块,再去编译该模块,所以启动速度比webpack快很多
- 热更新方面:vite在热更新时,只需要让浏览器重新请求该模块即可,而webpack需要将该模块相关的依赖模块重新打包一遍,vite的热更新效率更高
- 限制:vite使用了ES Module,所以不能使用CommonJS相关的代码,例如不能使用require,只能使用import。vite使用rollup打包,所以vite的优势是开发阶段
网络类问题
http有了解过么 展开说说?
- HTTP(Hypertext Transfer Protocol)是一种基于请求/响应模型的、无状态的协议,用于在网络中传输超文本文档
- HTTP默认端口是80,HTTPS默认端口是443
- HTTP协议,提供了5种请求方式,分别是GET、POST、PUT、DELETE、HEAD
- GET,用于获取资源
- POST,用于提交资源或数据,例如提交表单、上传文件
- PUT,用于更新指定资源
- DELETE,用于删除指定资源
- HEAD,用于获取指定资源,但不返回资源的实际内容
- HTTP常见状态码
- 200,请求成功
- 301,资源已被移动,永久性重定向
- 404,资源未找到
- 401,权限校验不通过
- 403,权限校验通过,但权限不足
- 500,内部服务器错误
http握手总共有几次 分别是什么
- 客户端发送一个连接请求给服务器,包含了一个随机的序列号
- 服务器收到连接请求后,会返回一个应答报文,包含了随机的序列号以及一个确认号
- 客户端收到服务器的应答后,会再次发送一个应答报文给服务器,其中包含服务器发送的随机序列号和确认号
- 这样,服务器和客户端就完成了三次握手,建立了一条可信的连接。在这条连接中,客户端和服务器就可以交换数据
- 三次握手的目的是为了保证数据的可靠传输。在建立连接之前,客户端和服务器都会发送一些控制报文来确认对方的存在,并且确定序列号和确认号,以便在之后的数据传输过程中使用。这样可以避免数据包丢失或重复出现,保证了数据的可靠传输
一个页面从输入 URL 到页面加载显示完成的过程
- 当用户在浏览器中输入 URL 并按下回车时,浏览器会向服务器发送 HTTP 请求,请求指定的资源。服务器收到请求后,会根据请求的 URL 返回对应的资源,这通常是一个 HTML 文件。浏览器收到服务器返回的 HTML 文件后,会开始解析 HTML 代码,并在浏览器中构建 DOM (Document Object Model) 树
- 在解析过程中,浏览器会根据 HTML 中的标签引用的外部资源(如 CSS、JavaScript 文件)发送请求,加载这些资源。浏览器会等待所有这些资源都加载完成,然后再渲染页面
- 在渲染过程中,浏览器会使用构建的 DOM 树和加载的 CSS 文件来计算每个元素的布局,并将这些元素绘制到浏览器窗口中。如果 HTML 中还包含 JavaScript 代码,浏览器会执行这些代码,可能会修改 DOM 结构或者添加新的内容
- 当所有的 HTML 代码解析完成,所有的资源加载完成,所有的 JavaScript 代码执行完成后,页面就会显示完成
常见的 HTTP 状态码
- 200 OK:服务器成功处理了请求。这是最常见的 HTTP 状态码,表示请求已成功
- 301 Moved Permanently:永久性重定向。请求的资源已被永久地移动到新位置,并且将来的所有请求都应该使用新的 URI
- 302 Found:临时性重定向。请求的资源临时从不同的 URI 响应请求。注意,在 HTTP/1.0 中,这个状态码也被用于重定向
- 400 Bad Request:服务器无法理解请求。这通常是因为客户端请求中的语法错误
- 401 Unauthorized:请求要求身份验证。客户端必须先使用授权标头发送身份验证信息
- 403 Forbidden:服务器拒绝了请求。这通常是因为服务器上的文件或目录的权限设置导致的
- 404 Not Found:服务器找不到请求的资源。这是最常见的 HTTP 错误代码,表示服务器无法找到请求的网页
- 500 Internal Server Error:服务器内部错误。这表示服务器遇到了意料不到的情况,导致了它无法处理请求。这可能是由于服务器上的代码错误或者其他原因造成的
三次握手和四次挥手
- 三次握手是用于在两台计算机之间建立网络连接。它包括以下三个步骤
- 客户端向服务器发送连接请求
- 服务器向客户端发送确认消息,表明服务器已准备好接受连接
- 客户端向服务器发送确认消息,表明客户端已收到服务器的确认消息,并准备好开始数据传输
- 四次挥手是用于在两台计算机之间终止网络连接。它包括以下四个步骤
- 客户端向服务器发送断开连接请求
- 服务器向客户端发送确认消息,表明服务器已收到断开连接请求
- 服务器向客户端发送断开连接请求
- 客户端向服务器发送确认消息,表明客户端已收到服务器的断开连接请求
HTTP和HTTPS区别
- HTTP (HyperText Transfer Protocol) 和 HTTPS (HTTP Secure) 都是用于在计算机之间传输数据的协议。但是,HTTPS在传输数据时使用了安全套接层 (SSL) 或者传输层安全 (TLS) 协议来加密数据,以防止第三方拦截或窃取数据
- 主要区别如下:
- 安全性:HTTPS 在传输过程中使用了加密技术,而 HTTP 没有。这意味着,使用 HTTPS 可以保护数据不被窃取或篡改
- 证书:使用 HTTPS 需要在服务器端安装 SSL 证书。这个证书可以用来验证服务器的身份,确保用户访问的是真实的网站
- 端口号:HTTP 使用的是 80 端口,而 HTTPS 使用的是 443 端口
- URL:HTTP 的 URL 以 "http://" 开头,而 HTTPS 的 URL 以 "https://" 开头
- 总的来说,使用 HTTPS 可以提供更好的安全性,特别是在处理敏感信息或者在公共网络上使用时
Axios拦截器
- Axios 是一个基于 promise 的 HTTP 库,支持promise所有的API
- 拦截器
- 可以拦截请求和响应
- 可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据
- 相关配置
- url:请求的服务器地址
- method:请求方法
- baseURL:基准路径
- headers:请求头
- parmas:路径参数
- 一般可以通过拦截器做什么?
- 请求拦截器
- 在请求发送前进行的操作,如:每个请求体里加上token
- 响应拦截器
- 接收到响应后进行的操作,如:服务器返回的登录状态失效,就跳转到登录页
Vue2
MVVM的理解
-
MVVM是一种设计模式,用于将用户界面(UI)与业务逻辑分离。这样可以使 UI 的开发与业务逻辑的开发相互独立,更容易维护和扩展
-
MVVM 的名称来自于三个部分:
- Model(模型):表示应用程序中的数据模型。它代表着应用程序中的业务逻辑和状态
- View(视图):表示应用程序的用户界面。它是用户与应用程序交互的方式
- ViewModel(视图模型):是一个桥梁,将模型与视图连接在一起。它提供了视图所需的数据和命令,并将用户的输入转换为模型的操作
-
通常,ViewModel 通过数据绑定将数据提供给视图,并使用命令处理视图中的用户输入。这样,视图可以直接与 ViewModel 交互,而无需与模型直接交互。这使得视图的开发和模型的开发相互独立,并且可以更轻松地测试和维护应用程序
-
MVVM 模式的优点包括:
- 将 UI 和业务逻辑分离,使得 UI 和业务逻辑的开发相互独立
- 提供了更好的测试支持,因为视图和业务逻辑是分离的,所以可以更轻松地对它们进行单元测试
- 易于维护和扩展,因为业务逻辑和 UI 分离,所以可以更轻松地更改一个而不会影响另一个。
MVVM 模式也有一些缺点,包括:- 可能会使代码变得较复杂,因为需要编写数据绑定和命令的代码
- 可能需要使用特定的框架才能实现 MVVM 模式,例如 Vue 或 Angular
-
总的来说,MVVM 是一种有用的设计模式,可以帮助你将用户界面与业务逻辑分离,从而使应用程序更易于维护和扩展
插槽
- 在Vue2中,插槽(Slot)是一种将父组件中的内容传递给子组件的机制。通过插槽,父组件可以向子组件传递HTML代码、组件实例或其他任意内容,并在子组件中使用
- 插槽分为两种类型:具名插槽和默认插槽(匿名插槽)
- 具名插槽可以让父组件传递多个插槽到子组件中,并在子组件中通过插槽名称进行访问
- 默认插槽是没有指定名称的插槽,当父组件没有传递具名插槽时,子组件就会使用默认插槽中的内容
- 通过使用插槽,Vue2中的组件可以更加灵活地处理内容,可以在子组件中动态渲染内容,实现更加复杂的UI效果
vue组件中data为什么必须是一个函数
- 在 Vue.js 中,组件的 data 选项必须是一个函数,这是因为每个组件实例都应该有自己的状态,如果 data 不是一个函数,那么所有实例将共享同一个数据对象,这会导致组件之间的状态混乱
Vue 数据双向绑定的原理
- 在 Vue.js 中,数据双向绑定是通过使用观察者模式来实现的。观察者模式是软件设计模式的一种,它允许对象之间的一对多关系。在这种关系中,一个对象(称为发布者)维护一组依赖于它的对象(称为订阅者),并在其状态更改时通知所有订阅者
- 在 Vue.js 中,发布者是 Vue 实例,订阅者是观察者。当 Vue 实例的数据发生变化时,Vue 会通知观察者,观察者会更新视图。这样,当用户在视图中进行操作时,Vue 会更新数据,并通知观察者更新视图
- 为了实现数据双向绑定,Vue 使用了一种叫做劫持的技术。劫持就是在数据变化时捕获数据并通知观察者。Vue 实现劫持的方法是使用 Object.defineProperty() 函数,该函数允许在对象的属性被访问或修改时进行拦截
- 当 Vue 实例被创建时,它会在内部遍历所有数据并使用 Object.defineProperty() 函数为每个属性添加 getter 和 setter。这样,当数据被访问或修改时,Vue 就可以捕获数据并通知观察者
自己举一个使用vuex的完整过程
- 安装 Vuex:使用 npm 或 yarn 安装 Vuex
npm install vuex
- 创建 store:创建一个 store.js 文件,并在文件中定义 store 的状态、getters、mutations 和 actions
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 定义状态
},
getters: {
// 定义 getters
},
mutations: {
// 定义 mutations
},
actions: {
// 定义 actions
}
})
- 在入口文件中加载 store:在应用的入口文件中引入 store.js 文件,并将 store 实例作为参数传递给 new Vue()
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
- 在组件中使用 Vuex:在组件中使用 $store 访问 store 中的状态、getters、mutations 和 actions
export default {
computed: {
// 使用 getters
count() {
return this.$store.getters.count
}
},
methods: {
// 使用 mutations
increment() {
this.$store.commit('increment')
},
// 使用 actions
asyncIncrement() {
this.$store.dispatch('asyncIncrement')
}
}
}
解释一下递归
- 递归是一种编程技术,允许函数调用自身。这种技术可用于解决一些复杂的问题,并且通常比循环更简洁易懂
v-model实现的原理
- v-model 指令是一种语法糖,它的作用是为表单元素绑定双向数据绑定。它是@input和v-bind:value的合写
什么是单页面应用
- 单页面应用(Single-Page Application,简称 SPA)是一种架构方式,它在一个单独的网页中加载所有的资源,并使用 JavaScript 在客户端渲染用户界面,而不是通过服务器进行渲染。这意味着,当用户在 SPA 应用中导航到不同的页面时,不会发生实际的页面跳转,而是在当前页面内使用 JavaScript 更新内容。这使得 SPA 应用的体验非常流畅,因为不会有页面加载的延迟
- 与传统的多页面应用相比,单页面应用具有许多优势,包括:
- 快速响应:由于不需要重新加载页面,因此 SPA 应用可以快速响应用户的操作
- 减少服务器负载:由于大部分的处理都是在客户端进行的,因此 SPA 应用可以减少服务器的负载
- 更好的用户体验:由于 SPA 应用的页面切换非常流畅,因此它可以提供更好的用户体验
- 然而,单页面应用也有一些缺点,包括:
- 较难调试:由于 SPA 应用的代码都在客户端运行,因此调试可能会更加困难
- SEO 难度较大:对于单页面应用,搜索引擎爬虫可能无法正常抓取页面内容,因此 SPA 应用的 SEO 较为困难。为了解决这个问题,可以使用服务端渲染(Server-Side Rendering,简称 SSR)技术,在服务器端渲染 SPA 应用的内容,使得爬虫可以正常抓取页面内容
- 总的来说,单页面应用是一种有效的架构方式,它可以提供流畅的用户体验,但在 SEO 和离线使用方面存在一定的困难
vue的理解?
- Vue.js 是一个渐进式的 JavaScript 框架,旨在通过尽可能简单的 API 实现响应式数据绑定和组合的视图组件
- Vue.js 的核心是一个叫做 Vue 实例的 JavaScript 对象,该对象用于控制一个 HTML 页面中的一个区域,称为“视图”。Vue 实例可以通过声明式模板或使用 JavaScript 代码动态渲染视图
- Vue.js 的响应式系统使得在模型数据变化时,视图会自动更新。这意味着,你可以在不手动操作 DOM 的情况下改变视图。Vue.js 还提供了许多其他功能,如组件系统、路由、状态管理等,可以帮助你构建复杂的单页应用
- 总的来说,Vue.js 是一个轻量级的、易于使用的前端框架,可以帮助你快速构建响应式的 Web 应用
vue.js中如何监听路由信息的改变
- 通过watch监听器,监听$router对象、或者监听router的
getPath
改变 - 通过组件的路由钩子回调函数,也就是组件路由守卫,在组件中配置
beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
等回调函数,都可以获取路由的to
、from
路径
nextTick的理解
- nextTick是用于在下一个事件循环中进行回调函数的方法。
- 由于Vue更新DOM是异步的,如果我们想在更新DOM后,再操作DOM元素,则需要在nextTick的回调中才能进行操作
- 计算属性的更新也是异步的,当我们设置完计算属性依赖的数据后,想要马上获取更新后的计算属性是获取不到的,所以页需要通过nextTick来获取
- 如果你想在created生命周期中,获取DOM元素,也是获取不到的,因为此时模板还没有渲染,你可以将获取代码挪到mounted生命周期中,或者使用nextTick
vue给对象添加新属性界面不刷新,怎么解决?
- 原因:Vue 不允许在已经创建的实例上,动态添加新的响应式属性
- 有三种解决办法:
- 使用
Vue.set( target , key , value)
- 使用
$fourceUpdate
强制刷新 - 克隆新对象,如
this.persons ={...this.persons}、this.persons = Object.assign({}, this.persons)
vue中key的原理
- 在 Vue.js 中,当使用 v-for 指令渲染一个列表的时候,Vue.js 强制要求使用 key 属性来提高性能。这个 key 属性是 Vue.js 内部使用的,它并不会对你的应用造成任何影响
- 在 Vue.js 内部,使用 key 属性可以帮助 Vue.js 识别列表中的每个元素。这样 Vue.js 就可以更快地更新列表中的元素,因为它可以直接找到要更新的元素,而不是遍历整个列表来寻找需要更新的元素
- 使用 key 属性的方法是在每个 v-for 指令的模板中给每个元素添加一个唯一的 key 属性,如下所示:
<div v-for="item in items" :key="item.id">{{ item.text }}</div>
- 在上面的例子中,Vue.js 会使用每个元素的 id 属性作为 key 属性。你可以使用任何唯一的值作为 key 属性,只要它能唯一标识列表中的每个元素即可
- 总的来说,使用 key 属性可以帮助 Vue.js 更快地更新列表中的元素,提高应用的性能
Vue 组件通讯
- 父传子:子组件定义
props
属性,接收父组件传来的数据 - 子传父:子组件中使用
this.$emit
方法,发送自定义事件给父组件,并可以携带数据 - 兄弟传值:EventBus事件总线,
$emit
发送事件,$on
监听事件 - 父传孙:
provide
和inject
方式
兄弟组件的传值
- 使用EventBus,可以跨组件通信
- 使用Vuex,进行状态管理
- 状态提升,将状态提升到共同的父组件中,进行父子传参
自定义指令的使用,以及指令的生命周期/钩子函数
-
指令分类:全局自定义指令、局部自定义指令
-
全局自定义指令
Vue.directive('指令名', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 操作
}
})
- 局部自定义指令
directives: {
指令名: {
// 指令的定义
inserted: function (el) {
// 操作
}
}
}
-
生命周期/钩子函数
-
inserted:被绑定元素插入父节点时调用
-
bind:只调用一次,指令第一次被绑定到元素时
-
update:元素本身更新时触发
-
componentUpdate:组件和子组件更新时触发
-
unbind:指令被移除时触发
说5个vue的指令
- v-if、v-else、v-else-if:条件渲染
- v-show:显示、隐藏
- v-bind:动态绑定
- v-model:双向绑定
- v-for:循环遍历
- v-html:解析html字符串作为内容
- v-text:使用字符串作为内容
- v-on:事件监听
- v-slot:插槽
路由懒加载
- 路由懒加载,将页面组件作为异步组件,将会在第一次访问该组件的路由时,才初始化。而不是项目一启动就加载所有的页面组件,能提高首屏加载的速度
// 路由懒加载,异步组件
const SomeComponent = () => ({
// 这个组件会在被访问时异步加载
component: import('./some-component.vue'),
loading: SomeLoadingComponent,
error: SomeErrorComponent,
delay: 200,
timeout: 3000
})
// 路由配置
const router = new VueRouter({
routes: [
{
path: '/some-route',
component: SomeComponent
}
]
})
Vuex核心属性
- state,状态,也就是数据
- mutation,同步修改state的函数。通过
store.commit
函数来提交mutation,该函数有2个参数,第一个为mutation的函数名称,第二个参数为需要携带的参数,需要注意的是,参数只能有一个,如果需要传递多个参数,请使用对象和数组 - action,异步修改state的函数,底层依旧是提交mutation来实现修改state。通过
store.dispatch
函数来派发action,和提交mutation类似,该函数也有2个参数,第一个为action的函数名称,第二个是要携带的参数 - getters,相当于计算属性,能将多个state进行组合、计算
- module,vuex的模块化,每个模块都有自己的state、mutation、action、getters,更加方便管理
路由模式
- hash模式
- hash模式,HTML5的新特性
- URL中存在#,#后面的路径就为hash路由
- 浏览器处理#时,不会触发请求,而是触发
hashchange
事件 - 通过
window.location.hash
,可以获取到#后面的路径 - 浏览器会记录hash路径的变化,所以浏览器的前进、后退都可以用
- hash路由的路径带有#,没有history模式美观
- history模式
- history模式,浏览器原生就支持,兼容性最好
- 路径变化时,页面会发生跳转,并发出请求
- 路径没有#,路径比较美观
- 因为会发出请求,需要服务器进行配置,所有页面路径请求,都返回根页面index.html,否则会返回404,例如在Nginx中做路径转发来处理
路由之间跳转方式
- 使用
router-link
标签组件,配置to
属性,指定跳转路径 - 通过
router.push('/login')
函数,传递路径跳转 - 通过
router.replace('/login')
函数,不会添加历史,所以不能回退 - 通过
router.go
函数,类似window.history.go(n)
,n为正数则前进,为负数则后退
如何封装组件
- 创建一个.vue的组件文件
- 在
props
属性中,定义组件需要的数据 - 在父组件中局部注册子组件,或在
main.js
中全局注册子组件 - 子组件通过
$emit(事件名, 参数)
,传递数据和父组件通信
v-show和v-if的区别
- v-show指令,使用元素的
display
属性,设置为none
,来切换元素的显示隐藏 - v-if指令,通过将元素在DOM树的结构移除,来实现元素隐藏
- v-if因为要操作DOM元素的创建和销毁,成本较高,如果在需要频繁切换显示、隐藏的场景,推荐使用v-show,而不是v-if,否则频繁的显示隐藏会导致性能问题
v-for和v-if为什么不能一起使用
- 因为v-for的优先级高于v-if,2个指令放在同一个标签时,会导致每次都要循环再判断,会消耗很多的性能
- 解决方案:包一层div,将v-if放在上层,v-for放在里层,让vue先判断v-if,不满足条件时,直接不需要里层,就不会触发v-for
- Vue3解决了这个问题,v-if的优先级高于v-for了
说说vue的生命周期
- beforeCreate,创建前回调,此时vue实例上的data数据还未初始化
- created,创建后回调,此时data数据已经初始化,已经将数据转换为响应式数据
- beforeMount,挂载前回调,此时模板还未渲染,页面显示的还是模板内容,此时获取DOM元素还是旧的,不要这时候获取DOM元素
- mounted,挂载后回调,已经将data中的数据渲染到模板中,此时可以获取DOM元素
- beforeUpdate,更新前回调,此时的响应式变量还是更新前的数据
- updated,更新后回调,此时响应式变量已经更新了,但不要在这个函数中又修改响应式变量,很容易出现死循环
- beforeDestroy,销毁前回调,vue在这个阶段没有做什么特别的事情,DOM的事件监听还未移除,响应式数据还没有取消,如果此时操作DOM和修改响应式数据虽然不报错,但已经没有意义了,因为下一步就要移除它们
- destroyed,销毁后回调,vue将DOM元素事件监听移除,取消变量的响应式,此时修改响应式变量,并不会更新DOM元素
Vue组件之间的传递方式
- 父节点传子节点,使用
props
,子传父,使用$emit()
发送自定义事件,缺点是组件层级太多时,数据要一层层传,传递数据太麻烦 - 兄弟节点通信,使用EventBus,缺点是数据不是响应式的
- 爷孙节点通信,使用inject、provide,祖先节点向子孙节点传递数据,缺点是无法监听数据修改的来源,不支持响应式
- 使用vuex,可以跨组件通信,数据有响应式,推荐使用
- 使用parent访问父节点,$childen访问子节点,ref访问子节点,可以访问组件的属性和方法,以此来进行通信
vue-router的钩子函数
- VueRouter的钩子函数,有3种,分别是全局守卫、独享守卫、组件内的路由守卫
- 全局守卫有3个,全局前置路由守卫、全局解析守卫、全局后置路由守卫。应用场景:前置路由守卫中处理私有页面的权限控制、开启进度条,后置路由守卫中关闭进度条
- 独享守卫,在路由配置种,配置
beforeEnter
,应用场景:支付宝H5支付,提供给支付宝的回跳地址,支付宝会添加上payResult
字段,我们可以在这个独享守卫回调中,判断参数,如果支付成功则放行,支付失败则拦截跳转,重定向到订单列表页面 - 组件内的路由守卫,在组件上添加回调函数,
beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
,应用场景:支付方式的选择弹窗组件,配置beforeRouteLeave
路由离开回调,禁止用户关闭弹窗,然后弹窗提示订单已经生成,如果确定要返回,则跳转到订单列表页面,用户可以继续支付
vue3和vue2区别
- 语法层面
- 在vue2中,我们只要定义在data()方法中的数据就是响应式数据,在vue3中我们可以使用ref和reactive定义的响应式数据
- 组合式api:为了让相关代码更紧凑vue3提出了组合式api,组合式api能将同一个逻辑关注点相关代码收集在一起。 组合式api的入口就是setup方法
- 在 vue2 中template不支持多根节点组件,vue3支持了多根节点的组件
- 底层实现方面
- vue2的响应式使用的是Object.defineProperty()实现,Vue3使用的Proxy实现
- 兼容性方面
- vue2 不支持 IE8 及以下版本,因为 Vue2 使用了 IE8 无法模拟的 ECMAScript 5 特性,例如:Object.defineProperty()
- vue3 不支持 IE11 及以下版本
- vscode插件和调试工具方面
- 代码高亮,语法提示方面:vue2项目主要用Vetur插件,vue3中主要用Volar
- 语法片段方面:在vue2中我们一直使用Vue 2 Snippets,在vue3我们推荐使用Vue 3 Snippets,因为它支持vue3的同时完全向前兼容vue2
- 在浏览器调试工具方面:vue2版本的chrome devtools不再支持vue3,vue3我们需要单独下载Vue.js devtools beta
深度侦听在什么情况下使用?立即侦听什么情况下使用?
- deep(深度侦听):默认情况下,侦听器无法侦听对象的属性值的变化,如果想实现这个效果,则需要添加deep配置为true
- handler(回调函数,名称不能改):侦听回调函数
- immediate(立即侦听):如果需要默认一进页面就触发一次,则添加
immediate
配置选项,并设置为true
路由的钩子函数
vue-router传参方式有哪些?
- 动态路由传参-路径拼接传参
- 传 this.{id}`)
- 接:this.$route.params.id
- 动态路由-params属性传参
- 传:this.$router.push({name: 'xxx', params: {id: 值})
- 接:this.$route.params.id
- 查询参数传参
- 传:this.$router.push({ name: 'xxx', query: { id: 值 } })
- 接:this.$route.query.id
什么是前置路由守卫?
- 在跳转之前,会回调的函数,就是前置路由守卫
- 语法:beforeEach(to, from, next)
- to:要跳到哪里去
- from:从哪里来
- next:放行函数,next()不传参数为放心,next(false)为不放行,next("/login")为不放行,并重定向到登录页面
路由传参的方式
- 编程式导航(JS的方式)
// 方式一:路径路由,使用查询参数,传递参数
this.$router.push(`/search?words=${this.inpValue}`);
// 方式二,路径路由,使用配置对象
this.$router.push({
path: "/search",
query: {
words: this.inpValue,
},
});
// 写法三,动态路由-直接拼路径的方式
this.$router.push(`/search/${this.inpValue}`);
// 写法四,动态路由-配置对象的方式
this.$router.push({
path: `/search/${this.inpValue}`,
});
// 写法五,命名路由的方式
this.$router.push({
name: "search",
params: {
words: this.inpValue,
},
});
- 声明式导航(router-link标签的方式)
// 查询参数传参
<router-link to="/search?words=程序员">程序员</router-link>
// 命名路由,传参
<router-link to="/search/程序员">程序员</router-link>
计算属性与watch的区别
- computed计算属性,写法是一个函数,但使用的时候不用加括号来调用。它的值有缓存功能,只要它依赖的data属性没有发生变化,就一直使用缓存
- watch监听器,可以监听某个变量的值改变,当值改变时,回调handler函数,它有3个配置项
- deep,深度监听,当要监听的变量是一个引用数据类型时,例如对象,要监听对象的属性变化时,就需要设置deep为true
- immediate,是否在第一次渲染时,就调用一次handler函数
- handler,监听的变量的值,发生改变时,会回调的回调函数
watch的深度监听在哪种场景下使用
- 当监听的变量为引用数据类型时,想要监听对象的属性变化时,需要将deep属性设置为true
keep-alive使用时从这个页面跳转到另外一个页面返回后这个数据还存在吗?为什么?
- keep-alive组件,具有缓存组件功能,默认页面跳转时,组件会销毁,如果加上keep-alive组件,则不会销毁页面组件,页面组件的数据就不会被销毁了
计算属性绑定数据后,跳转到另一个页面再返回,数据是否还在?为什么?
- 计算属性具有缓存功能,只要计算属性依赖的数据没有发生改变,计算属性就不会重新计算,提高页面性能和速度
页面优化有哪些?
- v-if和v-for不能连用
- 更多的情况下,使用v-if代替v-show
- 要保证key值的唯一
- 使用组件懒加载或者图片懒加载
- 防抖和节流的使用
- 模块按需导入
- 打包优化
- 使用cdn加载第三方模块
- 缓存常用信息
- 精灵图,base64
动态组件是什么?
- Vue中的动态组件是指可以在运行时动态切换的组件。它们可以根据不同的条件动态地改变,从而实现在不同组件之间的切换。动态组件的实现通常使用
<component>
元素的is特性,通过将一个字符串作为is的值,该字符串就是想要切换的组件的名称。例如:<component v-bind:is="currentComponent"></component>
- 应用场景
- 条件渲染:在某些情况下,我们可能需要根据不同的条件展示不同的组件。使用动态组件,我们可以根据条件动态地改变要渲染的组件,实现条件渲染。
- 异步加载:在某些复杂的场景下,我们可能需要异步加载一些组件。使用动态组件,我们可以通过异步组件的方式实现异步加载,提高应用的性能和用户体验。
- 标签页应用:在多标签页应用中,我们可以将每个标签页封装为一个组件,然后使用动态组件来实现标签页之间的切换。
- 通用的模态框组件:通过动态加载不同的内容组件来实现不同的功能。
- 动态加载表单组件、动态加载列表组件等:在实际开发中,我们经常会使用动态组件来实现一些复杂的功能,比如动态加载表单组件、动态加载列表组件等等。
- 优点
- 动态组件可以动态地决定要渲染哪个组件,可以根据不同的条件进行切换,使得组件的使用更加灵活和动态。
- 动态组件可以避免不必要的重新渲染和销毁,提高了切换的性能。
- 动态组件可以方便地传递数据和方法,使得组件之间的通信更加简单和方便。
- 缺点
- 动态组件的遍历来源不明确,不利于阅读和维护。
- 多个动态组件可能会造成命名冲突,需要进行额外的命名管理。
- 动态组件和静态组件之间可能会出现多对多的关系,增加了复杂度。
vue-router导航守卫/钩子函数
- beforeEach,全局前置守卫,一般进行登录权限校验
- beforeEnter,路由独享守卫,当路由独享时,可以进行权限校验
- beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave,组件内守卫,可以在组件内部进行跳转时,进行一些操作,例如获取数据、验证
Vue3
Pinia和vuex的区别,vuex的不足有哪些?
- Pinia相对于Vuex更加轻量级、易于使用和扩展,适用于中小型Vue.js项目;而Vuex则适用于大型、高复杂度的Vue.js项目
- Pinia取消了Vuex中的mutations,将它们合并成了actions。这意味着在Pinia中,我们不需要直接修改状态,而是通过定义actions来执行异步或同步的操作
- Pinia支持完整的TypeScript支持,而在Vuex中添加TypeScript可能需要额外的配置。这意味着在Pinia中,我们可以更方便地使用TypeScript进行类型检查和自动补全,提高了代码的可读性和可维护性
- Pinia支持插件扩展功能,可以在运行时动态添加插件,而不需要重新编译应用程序。这种设计使得Pinia更灵活,更易于扩展
Vue3双向绑定怎么实现
- Vue3使用的是Proxy来实现双向绑定,相比Object.definedProperty只能代理某个属性来说,Proxy能代理整个对象,可以监听对象动态新增的属性,支持监听数组索引和length属性
Vue3中 setup的作用是什么?为什么Vue3比Vue2在script中多了一个setup这个语法糖?
- setup 是一个新的选项,在 Vue 3.2 版本中引入,是 Vue Composition API 的一部分。其设计的主要目的是为了解决 Vue 2 中在使用 mixins、高阶组件或者 Vuex 等进行代码复用时,出现的代码重复问题。
- 在 Vue 3 中,我们可以在 setup 函数中声明组件需要用到的数据、计算属性、方法等,然后在组件自身的 setup 中将这些数据和方法注入。这样,我们就可以避免在多个组件中重复编写相同的代码,提高了代码的复用性。
- setup 函数还可以接收两个参数:props 和 context。props 是从组件外部传递给组件的属性,而 context 是一个包含了组件的许多有用信息的对象。
- Vue3 相对于 Vue2 多了一个 setup 这个语法糖的原因主要有两个:
- Vue3 的设计目标是更加模块化和组合性。这要求新的 API 应该更加强调函数式编程的思维方式,从而更加容易地组合各种不同的功能。而 setup 就是这样一个能够方便地实现逻辑复用的新 API。
- Vue3 中的新特性 Composition API 为 Vue 的模块化开发提供了强有力的支持。它提供了一种新的方式来组织和管理 Vue 代码,使得我们能够更加灵活地复用和组合代码,提高了代码的可维护性和可读性。
- 总的来说,setup 函数的引入是为了解决 Vue 2 中复用代码的问题,同时使得 Vue 3 中的代码组织更加灵活、清晰。
TypeScript
TS的作用、TS代码可以在浏览器解析吗
- TypeScript是在JavaScript的基础上,增加了类型,例如let a = 10,a = "20",编译器将会报错,将错误提前在编译期间进行报错,避免在运行时出错,让代码更加规范,减少Bug的出现
- TS不可以直接在浏览器中解析,需要使用一个node实现的TypeScript编译器,将TypeScript的代码编译为JavaScript,才能在浏览器中运行
微信小程序
微信小程序已上线要怎么重构,比如原生做的小程序升级为vue+uni-app框架
- 可以使用
miniprogram-to-uniapp
小程序转换工具插件 - 将转换后的代码导入HBuilderX,运行到微信开发者工具
- 如果有报错,则解决,按UniApp要求调整API和语法和样式兼容
- 逐个模块自测、提测
- 参考:https://www.jb51.net/article/256281.htm
做uniapp商城小程序遇到了什么坑?
- 页面跳转问题:使用navigateTo或redirectTo进行页面跳转时,如果跳转次数超过5次,就会失效,无法继续跳转
- 图片问题:在某些情况下,如果图片的宽度没有定义,会导致页面出现闪屏的情况。
- 安卓设备兼容性问题:在安卓设备上可能会出现获取位置或扫描二维码的接口被系统拦截的情况
- 页面背景颜色或背景图片不生效的问题:在某些情况下,控制台不会报错,但页面背景颜色或背景图片无法生效
- 在微信开发者工具中调试时报错:在HBuilderX中运行项目,测试一个功能的接口是否可以调用,但在微信开发者工具中调试时却报错,这可能是因为使用了代理来调用接口,而微信小程序中不允许使用代理
之前做uniapp开发的时候有做过多端吗?最多做过几端?
- H5
- 微信小程序
- Android
- iOS
扫码进来,有一条链接,但是我要获取链接里面带的参数,怎么获取?
- 方式一:通过URLSearchParams()
const url = new URL('https://example.com?foo=1&bar=2');
const params = new URLSearchParams(url.search);
params.get("foo") //1
params.get("bar") //2
- 方式二:通过字符串分割
- 通过字符串的substring()方法,截取查询字符串
- 通过字符串的split("&"),获取参数数组
- 遍历参数数组,组装成参数对象
- 通过对象.xxx属性名,获取参数
uni-app 项目要在不同多端发布,那么在开发的时候要考虑哪方面的问题呢?
- 平台兼容性:uni-app支持多个平台,包括微信小程序、H5、App等。因此,在开发过程中需要考虑不同平台的兼容性问题,确保应用程序在不同平台上能够正常运行。
- 界面设计:不同的平台可能有不同的界面设计规范和要求,例如微信小程序有自己的标签和组件,而H5则使用HTML和CSS。因此,需要根据不同平台的界面设计规范进行相应的开发。
- 功能实现:不同平台可能支持不同的功能和API,例如微信小程序支持微信支付和微信登录,而H5则不支持。因此,需要根据不同平台的功能需求进行相应的开发。
- 性能优化:不同的平台可能存在性能差异,例如微信小程序在运行时需要更多的内存和CPU资源。因此,需要根据不同平台的性能特点进行相应的优化。
- 安全问题:不同平台可能存在不同的安全风险和要求,例如微信小程序需要遵守微信的安全规定。因此,在开发过程中需要考虑不同平台的安全问题,并采取相应的安全措施
uni-app组件中父组件,子组件,兄弟组件,彼此之间的数据交换有哪些参数?
- 父传子,子组件定义
props
属性,接收父组件通过v-bind传递过来的属性 - 子传父,子组件通过
this.$emit
方法,发送自定义事件给父组件(可以携带数据),父组件通过v-on
监听事件 - 兄弟组件通信,通过EventBus事件总线,
$emit
发送事件,$on
监听事件 - 父传孙,通过
provider
和inject
的方式 - 通过
vuex
或pinia
等状态管理框架
微信小程序的生命周期
- 应用生命周期
- onLaunch,应用打开,小程序初始化完成,只触发一次
- onShow,小程序从后台切换到前台时触发,监听小程序显示
- onHide,小程序从前台切换到后台时触发,监听小程序隐藏
- onError,小程序发生错误,或API调用报错时触发
- 页面生命周期
- onLoad,监听页面加载,此时可以发送请求
- onShow,监听页面显示
- onReady,页面初次渲染完成,此时可以获取页面元素
- onHide,监听页面隐藏,小程序跳转其他页面或其他小程序、切换到后台时触发
- onUnload,监听页面卸载,可以取消定时器、异步任务等
- onResize,窗口尺寸发生改变时触发,最低基础库:2.4.0
- 组件生命周期
- created,组件实例被创建时触发
- attached,组件被挂载到页面节点树时触发
- ready,组件在页面布局完成时触发
- moved,组件实例被移动节点树的另外一个节点时触发
- detached,组件从页面节点数树中移除时触发
- error,组件方法抛出错误时触发
微信小程序的登陆流程
- 用户打开微信小程序,点击登录按钮。
- 小程序调用wx.login()接口获取到code。
- 小程序将code发送到微信服务器,微信服务器将session_key和encryptedData以及iv参数对传给小程序。
- 小程序通过wx.request()方法发送请求到开发者服务器,传递的参数包括appid、session_key、encryptedData、iv参数等。
- 开发者服务器根据appid、session_key等参数验证用户身份,并调用微信提供的接口进行用户身份的解密,获得用户的openid和会话密钥等敏感信息。
- 开发者服务器可以将用户的openid等信息存储到后端,用于后续的业务逻辑中识别用户身份。
- 小程序可以将从开发者服务器获取到的用户信息通过小程序页面进行展示,或者根据用户信息进行个性化推荐等服务。
微信小程序的跳转方式
- 使用导航组件:通过使用
<navigator></navigator>
标签和页面链接来实现跳转 - 使用
wx.navigateTo
和wx.redirectTo
方法:通过调用这两个方法可以实现页面之间的跳转,其中wx.navigateTo
方法保留当前页面,跳转到应用内的某个页面,而wx.redirectTo
方法关闭当前页面,跳转到应用内的某个页面 - 使用
wx.switchTab
方法:通过调用该方法可以跳转到tabBar页面 - 使用
wx.reLaunch
方法:通过调用该方法可以关闭所有非目标页面的导航栏,并跳转到应用内的某个页面 - 使用
wx.navigateBack
方法:通过调用该方法可以关闭当前页面,返回上一页面或多级页面
微信小程序的数据绑定和vue有什么区别
- 数据绑定的方式:
- 微信小程序:在微信小程序中,数据绑定需要使用
{{}}
语法,例如{{data}}
。这种绑定方式是将data变量的值直接显示在页面上,当data的值发生变化时,页面上的显示也会相应地更新 - Vue:在Vue中,数据绑定可以使用v-bind指令或者简写的冒号(
:
),例如v-bind:value
或:value
。这种绑定方式是将value属性的值绑定到页面上的元素上,当value的值发生变化时,页面上的元素会自动更新
- 微信小程序:在微信小程序中,数据绑定需要使用
- 双向绑定的方式:
- 微信小程序:微信小程序没有内置的双向数据绑定机制。当表单元素的值发生变化时,需要手动将新值传递给数据变量
- Vue:Vue有内置的双向数据绑定机制。当表单元素的值发生变化时,会自动更新关联的数据变量
- 总体来说,微信小程序的数据绑定更加简单直接,而Vue的数据绑定更加灵活和强大
微信小程序支付流程
场景
token失效处理
- 方案一:服务端保存token的状态,每次用户的操作都延长一下token的过期时间,session就是采用这种策略来保持token的有效期,但在前后端分离的架构下,前端为单页面应用,几秒钟发送一次请求来延长token过期时间,太消耗性能
- 方案二:使用双token方案,服务端生成2个token,refreshToken的过期时间会长一些,accessToken的过期时间短一些,当accessToken过期时,通过axios拦截器判断状态码401,则为token过期时,调用刷新token接口,将refreshToken传过去,服务端则重新生成一个accessToken给前端,重新发送请求即可
服务端渲染和客户端渲染分别是什么?
- 服务器渲染
- 页面渲染的工作都是由服务端来完成的,数据也是由服务端提供的,浏览器只负责展示页面内容
- 容易被爬虫爬取数据,同时能被搜索引擎搜索到,能在搜索引擎中向用户展示数据
- 客户端渲染
- 页面的渲染工作都是由浏览器来完成的,服务器只是负责提供数据
- 客户端渲染能尽早的把页面展示给用户,用户体验好
- 不容易被爬虫爬取数据,同时也无法被搜索引擎搜索到
一般vue开发用什么库来辅助
vantUI elementUI js-cookie socket.io axios ECharts
页面刚开始出现一片空白的原因
- 打包路径不对,默认webpack打包是绝对路径,要改为相对路径
- 网络设置的问题,有可能是IP、端口等出现问题了
- 网速比较慢导致(加载速度慢)
- 浏览器本身出现问题了
vue的项目如何做首屏的优化
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
- 长列表性能优化
- 事件的销毁 addEventlisenter 事件监听
- 图片资源懒加载
- 精灵图
- 路由懒加载
- 第三方插件的按需引入
- 优化无限列表性能
- 服务端渲染 SSR
- 代码压缩
在浏览商品时,点击商品详情,然后返回,要求进度条还在之前的位置,怎么做
- App.vue页面中使用keep-alive缓存组件
- 在离开商品详情页记录进度条的高度,返回商品详情的重新给进度条进行赋值
localStorage能实现token的存储需求,为啥还要存储在vuex中呢
- 因为localStorage里面存储的类型只能是字符串,如果存储的内容不是字符串,还需要转换,有额外的性能消耗
- localStorage里面的数据变化不能进行监控,如果有需求是根据token的变化进行的操作的,那localstorage就实现不了
- localStorage是磁盘操作,vuex是内存操作,localStorage性能比vuex去数据慢的多
项目中后台接口没有写好你怎么做,有几种方式
- 模拟数据
- mock.js
- json-serve
- 组件内写死数据
cookie为什么不能存储token或存储在cookie有什么问题
- cookie每次请求都会携带,比较消耗性能
- 防止CSRF攻击,CSRF主要就是攻击浏览器的cookie,这样如果存储token就容易被劫持到
axios挂到原型上有什么作用
- 各个组件内,可以直接拿到用axios的实例,就不需要各个组件都进行引入了
怎么知道自己项目所处的环境是什么
- 环境变量:在不同的环境中,可能会设置不同的环境变量。您可以查看项目的启动脚本或配置文件,查看是否设置了环境变量,并确定当前环境下的变量值。
- 配置文件:在不同的环境中,可能会使用不同的配置文件。您可以查看项目的配置文件,确定当前环境下使用的是哪个配置文件,并查看其中的配置信息。
- 日志信息:在不同的环境中,可能会输出不同的日志信息。您可以查看项目的日志文件或控制台输出,查看其中的环境信息。
- 代码中的配置信息,在不同的环境中,可能会使用不同的配置信息。您可以查看项目的代码,查
找其中的配置信息,并确定当前环境下使用的是哪些配置信息。
cli用的是那个版本和src里面都有哪些文件
版本:
Vue 2.6.10 版本
vuex 3.1.0 版本
Vue-cli 4.4.4 版本
webpack 4.4.0 版本
src:
api 接口请求
assets 静态图片
components 公共组件
icons 字体图标
router 路由配置
store vuex
utils axios封装
views 页面级路由
怎么做项目的权限控制
- 给员工分配角色,再给角色分配权限
- 权限,做到了页面和按钮2级,服务端会在用户信息中,返回页面权限、按钮权限的数组
- 将权限分拆为静态路由和动态路由(权限路由)
- 页面权限,在VueRouter的前置路由守卫中,请求用户信息,使用本地动态路由列表和服务端返回的权限列表进行比对,过滤出符合的动态权限列表,最后通过VueRouter的addRouters方法,添加动态路由到路由系统中
- 按钮权限,自定义了一个Vue的全局指令v-permission,在需要权限控制的DOM元素上,添加v-permission指令,配置权限点值。在指令的
inserted
回调中,判断指令的值,是否存在于服务端返回的按钮权限列表中,存在则代表有权限,就不需要处理。如果不存在,则代表没有权限,要将DOM元素移除 - 问题一
- 情况:刷新页面,出现404
- 解决方案:原因是404重定向配置在了静态路由中,路由匹配是从上到下的,所以404重定向配置必须配置在所有路由的最后面,所以只能将404重定向配置挪到动态路由时,再进行配置
- 问题二
- 情况:左侧的侧边栏的菜单列表,不是响应式的,只显示了静态路由中的菜单,没有显示动态路由的菜单
- 解决方案:将静态路由、动态路由合并后的路由列表,缓存到Vuex中。左侧菜单列表,使用Vuex中的路由列表来渲染菜单,当路由列表更新时,能响应式的更新菜单
- 问题三
- 情况:管理员账号退出后,登录普通员工账号,手动在地址栏中输入没有的权限的页面地址,能跳转并能访问
- 解决方案:退出登录后,将路由重置,保证每个用户的VueRouter路由,都只有这个用户的路由,不会残留上一个用户的路由配置,也就能保证不能访问没有权限的路由
axios是怎么封装的
- 封装request.js,主要创建axios的实例,配置基地址,配置请求、响应拦截器
- 封装api.js,封装不同的模块的接口API方法
PC端的兼容问题你遇到哪些?
1、img下的留白
解决方案:给img设定display:block。
2、如果图片加a标签在IE9-中会有边框
解决方案:给img设定border:none。
3、rgba不支持IE8
解决方案:可以用 opacity
4、标签最低高度设置min-height不兼容ie6/7
解决方案:如果我们要设置一个标签的最小高度200px,需要进行的设置为:{min-height:200px; height:auto !important; height:200px; overflow:visible;}
5、图片加a标签在IE9中出现边框
解决方案: img{border: none;}
移动端的兼容问题你遇到那些
1、在ios和andriod中,audio元素和video元素在无法自动播放
应对方案:触屏即播
$('html').one('touchstart',function(){
audio.play()
})
2、iOS 系统中文输入法输入英文时,字母之间可能会出现一个六分之一空格
可以通过正则去掉: this.value = this.value.replace(/\u2006/g, '');
3、IOS移动端click事件300ms的延迟响应
引入第三方插件fastclick可以解决在手机上点击事件的300ms延迟
4、 h5底部输入框被键盘遮挡问题
h5页面有个很蛋疼的问题就是,当输入框在最底部,点击软键盘后输入框会被遮挡。可采用如下方式解决
1)定义一个class:
.focusState {position: absolute;}
2)利用监听键盘的收起展开事件来添加移除定义的focusState 样式
created(){
var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
window.onresize = function() {
var nowClientHeight = document.documentElement.clientHeight || document.body.clientHeight;
if (clientHeight - nowClientHeight > 60 ) {
document.getElementById("apply").classList.add("focusState");
}
else {
document.getElementById("apply").classList.remove("focusState");
}
};
},
移动端如何做真机测试
- 公司有测试服务器,代码放到测试服务器上,进行手机访问测试
- 自己电脑上搭服务器,保证自己手机和电脑处于同一个局域网,然后用手机进行访问测试
H5和app的区别
- H5是通过链接进行访问,而APP是通过应用程序进行访问
- H5在应用商店里面没有,而APP是有的
- H5不需要审核就可以上线,而APP是需要审核的
- H5的响应速度没有APP快
- H5的开发成本比APP低
在请求回来的数据保存至localstorage里面页面没有刷新,怎么获取数据
- 开启一个定时器,例如5秒钟,定时获取localStorage的数据,然后同步到页面中
在使用vuex时怎么实现数据的持久化
- 保存数据到vuex后,也将数据存储到localStorage
- 页面刷新时,再从localStorage读取数据
全局前置守卫的应用,自己举个小例子说出来怎么使用
- 私有页面的权限校验,判断是否存在token,不存在token,强制跳转到登录页。有token或访问的是白名单页面(例如登录、注册),就放行跳转
有一个数组,数组都是数字怎么把里面的奇数偶数分开
- 通过filter,过滤出奇偶数
- 通过for循环
数组去重的方式有哪些?
- for、forEach循环遍历,使用indexOf方法,如果不为-1,则已存在,则不push到新数组
- for、forEach循环遍历,使用includes方法,如果为true,则已存在
- 通过ES6新增的Set,来保证唯一性
vuex第一次刷新导致首页空白
- 后台管理系统,页面权限控制,使用动态路由来进行控制。我们在获取到后端返回的路由列表后,会同步到vuex中,方便左侧的菜单列表实现响应式。当页面刷新时,vuex中的数据就丢失了,所以刷新会白屏
- 所以可以添加一个全局前置路由守卫,判断是否获取过路由列表,如果没有,则统一获取路由列表,然后同步到vuex,以及通过
addRouter
添加动态路由即可
项目中运用哪些框架
- Vue2+ elementUI(UI组件库)、Vue2 + iView(UI组件库)开发PC端网站
- Vue-element-Admin(花裤衩)开发后台管理系统
- Vue2+Vant(移动端组件库)开发移动端网站
- React + antd(蚂蚁金服UI组件库) 开发PC端网站
- React + antd-mobile(移动端UI组件库) 开发移动端网站
vue用了这么久 你对他有什么看法 比如语法 或者其他一些东西
- 优点
- 轻量级的框架
- 双向数据绑定
- 组件化开发
- 单页面路由
- 学习成本低
- 虚拟dom
- 渐进式框架
- 数据和结构的分离
- 运行速度快
- 插件化
- 缺点
- 不支持IE8以下
- 社区可能没有Angular和React那么丰富
- Vue 不缺入门教程,可是很缺乏高阶教程与文档。同样的还有书籍
- 因为是单页面应用,不利于SEO优化
- 初次加载时耗时多
服务端渲染和客户端渲染分别是什么?
-
服务器渲染
- 页面渲染的工作都是由服务端来完成的,数据也是由服务端提供的,浏览器只负责展示页面内容
- 容易被爬虫爬取数据,同时能被搜索引擎搜索到,能在搜索引擎中向用户展示数据
-
客户端渲染
- 页面的渲染工作都是由浏览器来完成的,服务器只是负责提供数据。
- 客户端渲染能尽早的把页面展示给用户,用户体验好
- 不容易被爬虫爬取数据,同时也无法被搜索引擎搜索到
移动端兼容(适配)、浏览器兼容、不同的分辨率兼容
- 移动端兼容(适配)参考:https://zhuanlan.zhihu.com/p/36021907
- 浏览器兼容参考:https://juejin.cn/post/6972937716660961317
- 不同的分辨率兼容 参考:https://juejin.cn/post/684490388191525274
echars移动端使用
低代码
- 低代码(Low Code)是一种可视化的应用开发方法,用较少的代码、以较快的速度来交付应用程序,将程序员不想开发的代码做到自动化,称之为低代码
- 低代码是一组数字技术工具平台,基于图形化拖拽、参数化配置等更为高效的方式,实现快速构建、数据编排、连接生态、中台服务。通过少量代码或不用代码实现数字化转型中的场景应用创新
- 参考:https://baike.baidu.com/item/%E4%BD%8E%E4%BB%A3%E7%A0%81/60863339?fr=aladdin
什么样的数据能放cdn
资源类型
- HTML\CSS\JavaScript
- 图片
- 音频
- 视频
- 作用
- 静态资源加速
- 动态资源加速
- 音频/视频加速
- 图片加速
- 下载加速
- 海外加速
- 安全加速
Web端即时通讯
- 基于Web的前端,存在以下几种可实现即时通讯的方式:
- 短轮询 (历史方案)
- 开个定时器, 每隔一段时间发请求 (实时性不强)
- Comet - ajax长轮询(历史方案)
- 发送一个请求, 服务器只要数据不更新, 就一直阻塞 (服务器压力过大)
- SSE(利用了http协议, 流数据的传输, 并不是严格意义的双向通信, 无法复用连接)
- WebSocket (主流):
- 性能和效率都会比较高
- 短轮询 (历史方案)
url从输入地址到访问的过程
商城购物车是怎么做的 ?在购物车订单页面怎么去更新数据?
- 用户已登录
- 每次用户添加商品,向后台发送用户添加的商品
- 切换到购物车页面,从后台查询用户的购物车商品列表
- 更新界面
- 用户未登录
- 每次用户添加商品,在本地缓存中,存储添加的商品信息
- 每次切换到购物车页面,从本地缓存中,查询用户的购物车商品列表
- 更新页面
后台管理的权限如何实现
- 现在权限相关管理系统用的框架都是element提供的vue-element-admin模板框架比较常见。
- 权限控制常见分为三大块
- 菜单权限控制
- 按钮权限控制
- 请求url权限控制
项目优化打包
- 减少请求数量
- 彩色的图片,使用雪碧图
- 10kb的小图标,使用base64
- 纯色图标,使用iconfont字体图标或者SVG
- 使用强缓存、协商缓存
- 减小资源大小
- HTML、CSS、JS,要经过压缩
- 开启GZIP压缩,能减少70%的体积
- 图片压缩,在不失真的情况下,丢弃一些无关紧要的色彩
- 图片格式,可以使用webp,体积比jpg、png少25%
- 优化网络连接
- 使用CDN,将一些常用的库,例如Vue、React、ElementUI、腾讯cos-js-sdk-v5
- 添加CDN预解析
- 优化资源加载
- CSS样式放header头部,JS放body底部
- 使用异步加载标签
- defer,在HTML解析完毕后执行,效果和将JS引入放在body异步类似
- async,异步加载,加载完毕就马上执行,不需要等待
- 减少重绘回流
- 避免频繁操作DOM
- 尽量使用批量DOM
- 使用事件委托,将事件委托为父元素,统一处理子元素的事件
- 及时清理定时器、延时器
- 使用防抖、节流,避免频繁操作
- 性能更好的API
- 需要每一帧执行某个任务,建议使用
requestAnimationFrame
,而不是setInterval
、setTimeout
- 图片懒加载,使用
IntersectionObserver
代替传统的监听scroll
事件,通过getBoundingClientRect
判断img标签,是否进入可视区域。因为getBoundingClientRect
即时使用了节流函数,会导致回流 - 将计算量大的任务,使用
WebWorker
处理,不阻塞UI线程
- 需要每一帧执行某个任务,建议使用
- webpack优化
- 使用按需导入,不推荐使用全量导入
- 开启TreeShaking,移除没有使用到的代码,对于一些第三方库有显著效果
- 参考链接:https://blog.csdn.net/weixin_44485276/article/details/119975366
原理
事件线程的理解
- 线程是进程内的一个独立执行单元,是程序执行的一个完整流程,是CPU的最小的调度单元,应用程序必须运行在某个进程的某个线程上
- 一个进程中至少有一个运行的线程:主线程,进程启动后自动创建
- 一个进程中也可以同时运行多个线程,我们说程序是多线程运行的
- 一个进程内的数据可以供其中的多个线程直接共享
JS的运行机制
- JS单进程语言
- 任务队列
- EventLoop、宏任务、微任务
图片懒加载底层原理
- 先将图片的
src
属性设置为空字符串,把真实的访问路由放在一个自定义属性data-original
中,监听页面的滚动事件,在事件回调中,判断图片是否进入可视区域,如果进入可视区域,就将自定义属性中的url地址,赋值给src
属性,这样就可以实现图片懒加载了
说一下vue2底层原理 ?vue2的特点 ?
- vue 作为一种MVVM模式的框架, 其数据绑定的底层原理为:数据劫持 + 发布订阅者模式,其中有4种角色
- Observer:主要负责 数据劫持,核心是通过Obeject.defineProperty()来监听数据的变动,这个函数内部可以定义setter和getter。每当数据发生变化,就会触发setter()。这时候 Observer 就要通知给Dep 说有数据发生了变化
- Dep数据收集:Dep 收到来自 Observer 的数据变化通知时,会调用 notice() 方法把发生变化的依赖告诉 Watcher
- Watcer订阅者:是连接 Observer 和 Compile 之间通信的桥梁,当它收到来自 Dep 的数据变化通知后,会调用自身的 update() 方法,并触发Compile中绑定的回调
- Compiler 模板编译器:主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦接收到数据有变动,收到通知,更新视图
网友评论