1.对SEO搜索优化的了解
含义:搜索引擎优化。是一种方式:利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名
2.img的title和alt的区别
alt 属性是当元素不能正常呈现时用作元素内容的替代文本。 是使用 alt 属性的最常用标签。 当无法加载图像时,浏览器将在其位置显示 alt 文本,以便用户了解包含图像的含义。 title 属性是将鼠标悬停在元素上时看到的工具提示文本,是对图片的描述和进一步的说明。
实例示范
alt属性效果如下:
<img src="../../assets/images/default.jpg" alt="图片未显示出来时,提醒你这是一张图片"/>
效果图:
当图片没有正常显示出来时,结果如图所示,出现的仅仅是alt里面设置的内容。 title属性效果如下:
<img src="../../assets/images/default.jpg" title="介绍这是一张图片默认图片">
效果图:
如图所示,这个图片正常显示,当鼠标经过图片时,出现title里面的内容,对图片进行描述。
3.行内元素和块级元素
块级元素
每个块级元素通常都会独占一行或者是多行,可以对其单独设置高度,宽度以及对齐等属性。
常见的块级元素有:<h1>~<h6>,<p>,<div>,<ul>,<ol>,<li>等
行内元素
行内元素(内联元素):不占有独立的区域,仅仅依靠自己的字体大小或者是图像大小来支撑结构。
一般不可以设置宽度,高度以及对齐等属性。
常见的行内元素有:
<a >,<strong>,<b>,<em>,<del >,<span >等
默认的宽度就是它本身的宽度
行内块级元素
块级元素,它既具有块级元素的特点,也有行内元素的特点,它可以自由设置元宽度和高度,
也可以在一行中放置多个行内块级元素。比如input、img就是行内块级元素,它可以设置高宽以及一行多个。
4.媒体查询的使用方式
一、媒体查询
@media all and (max-width:600px(条件值)){
选择符{样式}
}
实例展示:
@media all and (max-width:600px) {
div {
background: blue !important;
}
}
主要包含两个方面:媒体类型 函数 默认值写在最前面,或者给媒体查询样式添加important. 如果要使用max-width,条件值比较大的写在前面。 如果要使用min-width,条件值比较小的写在前面。
@media screen and (min-width: 1130px) { //最小屏幕宽度
.div_{
width: 21%;
height: 90%;
}
}
@media screen and (max-width: 700px) { 最大屏幕宽度
.div_{
width: 100%;
margin-top: 15px;
}
}
5、src和href之间的区别
href标识超文本引用,用在link和a等元素上,href是引用和页面关联,是在当前元素和引用资源之间建立联系
src表示引用资源,表示替换当前元素,用在img,script,iframe上,src是页面内容不可缺少的一部分。
6、css让元素看不到的方法
全部方法
1、display:none
2、visibility:hidden
3、opacity:0
4、position移除到窗口外面
5、z-index进行层次的遮盖
区别是否占位
display:none,隐藏之后不占位置。
visibility:hidden、opacity:0,隐藏后仍然占据位置。
7、重绘和回流
重绘:
DOM树没有元素增加或删除,只是样式的改变,针对浏览器对某一元素进行单独的渲染,这个过程就叫做重绘
回流:
DOM树中的元素被增加或者删除,导致浏览器需要重新的去渲染整个DOM树,回流比重绘更消耗性能,发生回流必定重绘,重绘不一定会导致回流.
因为重绘和回流的存在导致真实DOM性能不佳,所以vue和recat还有角-等框架增加了虚拟DOM技术,就是为了减少DOM的重绘和回流从而减少浏览器性能消耗,这就是虚拟DOM的好处.
重绘和回流(重排)是什么,如何避免?
- DOM的变化影响到了元素的几何属性(宽高),浏览器重新计算元素的几何属性,其他元素的几何
- 属性的位置也会受到影响,浏览器要重新构造渲染树,这个过程为回流(重排),浏览器将受到影响的部分。
- 重新绘制到屏幕的过程为重绘。引起重排的原因有
- 添加或删除可见的DOM元素,
- 元素位置、尺寸、内容改变,
- 浏览器页面初始化,
- 浏览器窗口尺寸改变,重绘一定重绘,重绘不一定重排
减少重绘和重排的方法:
- 不在布局信息改变时做
DOM查询 - 使
用cssText或者className一次性改变属性 - 使用
fragment - 对于多次重排的元素,如动画,使用绝对定位脱离文档流,让它的改变不影响到其他元素
DOM性能浏览器的性能大部分都是被这两个问题所消耗。
8、new操作符具体干了什么呢?
- 创建一个空对象,并且
this变量引用该对象,同时还继承了该函数的原型 - 属性和方法被加入到
this引用的对象中 - 新创建的对象由
this所引用,并且最后隐式
9、ajax的原理
什么是ajax?
Ajax(Asynchronous JavaScript and XML):异步的js和XML 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
ajax的实现原理
- 第一步,创建
XMLHttpRequest对象 xmlhttp=new XMLHttpRequest(); - 第二步,
open准备发送,open中有三个参数一是提交方式get和post,二是接口地址url,三是同步异步 - 第三步是用
send发送 - 第四步在发送的过程中通过
onreadystatechaange来监听接收的回调函数,可以通过判断readyState==4和status==200来判断是否成功返回,然后通过responseText接收成功返回的数据
10、常见的设计模式有哪些
设计模式:单例模式、观察者模式
单例模式
有些时候,允许自由创建某个类的实例没有意义,还可能造成系统性能下降。如果一个类始终只能创建一个实例,则这个类被称为单例类,这种模式就被称为单例模式。
**publicclass** Singleton {
**publicstaticvoid** main(String[] args)
{
//创建Singleton对象不能通过构造器,只能通过getInstance方法
Singleton s1 = Singleton.*getInstance*();
Singleton s2 = Singleton.*getInstance*();
//将输出true
System.*out*.println(s1 == s2);
}
//使用一个变量来缓存曾经创建的实例
**privatestatic** Singleton *instance*;
//将构造器使用private修饰,隐藏该构造器
**private** Singleton(){
System.*out*.println("Singleton被构造!");
}
//提供一个静态方法,用于返回Singleton实例
//该方法可以加入自定义的控制,保证只产生一个Singleton对象
**publicstatic** Singleton getInstance()
{
//如果instance为null,表明还不曾创建Singleton对象
//如果instance不为null,则表明已经创建了Singleton对象,将不会执行该方法
**if** (*instance* == **null**)
{
//创建一个Singleton对象,并将其缓存起来
*instance* = **new** Singleton();
}
**return** *instance*;
}
}
观察者模式:观察者的使用场合就是:当一个对象的改变需要同时改变其他对象,并且它不知道有具体多少对象发生改变的时候,就应该考虑使用观察者模式
总的来说,观察者模式所做的工作就是解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自变化都不会影响到另一边的变化。
11、说说对Promise的了解
Promise是什么? Promise是异步编程的一种解决方案
Promise的作用解决传说中的回调地狱的问题,使代码更好的维护
Promise的用法Promise 是ES6中新增的特性,本身是一个构造函数。接收一个函数,函数需要传入两个参数(resolve: Function, reject: Function),分别表示成功与失败的回调函数。Promise 的实例中可以用 then 响应 resolve 回调,catch 响应 reject 回调。
Promise 类常用的静态方法,resolve\reject\all\race
-
race 相当于赛跑,传入多个Promise,优先输入状态最快变化的Promise,不管结果是成功还是失败。
-
all 多个 Promise 任务同时执行。如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。
12、js数据类型有哪些
- 基本数据类型:
number、string、Boolean、null、undefined、symbol(ES6新增)、bigInt - 复杂类型:
Object、function
13、js的内存存储方式
在js中,存储方式有两种种,一是本地存储,另一种是临时性的本地存储
SesionStorage:临时性的本地存储,只要关闭浏览器,数据就会清理掉,仅当此会话有效。LocalStorage:本地存储(长期存储),关闭浏览器数据也会一直存在Cookie:临时性存储,可以设置失效时间,但没有自己的存取方法,需要时封装。 sessionStorage、localStorage可以存取自己的方法例如:setItem()、getItem()、removeItem()、clear(),例如:localStorage.setItem('属性',值)
14、JS里如何判断是不是一个数组
1.通过instanceof判断
instanceof运算符用于检验构造函数的prototype属性是否出现在对象的原型链中的任何位置,返回一个布尔值。
let a = [];
a instanceof Array; //true
let b = {};
b instanceof Array; //false
2.通过constructor判断
我们知道,实例的构造函数属性constructor指向构造函数,那么通过constructor属性也可以判断是否为一个数组。
let a = [1,3,4];
a.constructor === Array;//true
3.通过Object.prototype.toString.call()判断
Object.prototype.toString().call()可以获取到对象的不同类型,例如
let a = [1,2,3]
Object.prototype.toString.call(a) === '[object Array]';//true
它强大的地方在于不仅仅可以检验是否为数组,比如是否是一个函数,是否是数字等等
//检验是否是函数
let a = function () {};
Object.prototype.toString.call(a) === '[object Function]';//true
//检验是否是数字
let b = 1;
Object.prototype.toString.call(a) === '[object Number]';//true
甚至对于多全局环境时, Object.prototype.toString().call()也能符合预期处理判断
//为body创建并添加一个iframe标签
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
//取得iframe对象的构造数组方法
xArray = window.frames[window.frames.length-1].Array;
//通过构造函数获取一个实例
var arr = new xArray(1,2,3);
console.log(Object.prototype.toString.call(arr) === '[object Array]');//true
15、map和forEach的区别
定义
foreEach()方法:
针对每一个元素执行提供的函数
map()方法:
创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来。
区别
forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。
举例 制作一个数组的平方,如有一个数组
let arr =[1,2,3,4,5,6]
下面分别用forEach()和Map()
forEach()
注意,forEach是不会返回有意义的值的。 我们在回调函数中直接修改arr的值。
arr.forEach((value, key) => {
return arr[key] = value * value;
});
执行结果:
Map()
let list = arr.map(value => {
return value * value;
});
执行结果:
执行速度对比 foreach()的执行是速度小于map()执行速度
16、Vue2的双向数据绑定原理
vue.js则是采用数据劫持结合发布者-订阅模式的方式,通过Object.defineProperty()来劫持各个属性setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调
17、数组去重的方法
去重方法有五种,如下:
1.利用ES6中set去重(ES6最常用)
var arr = [1,1,8,8,12,12,15,15,16,16];
function unique (arr) {
return Array.from(new Set(arr))
}
console.log(unique(arr))
//[1,8,12,15,16]
不考虑兼容性,这种去重的方法代码最少。这种方法还无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法
2.利用for嵌套for,然后splice去重(ES5最常用)
var arr=[1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
for (var i = 0; i < arr.length; i++) { // 首次遍历数组
for (var j = i + 1; j < arr.length; j++) { // 再次遍历数组
if (arr[i] == arr[j]) { // 判断连个值是否相等
arr.splice(j, 1); // 相等删除后者
j--;
}
}
}
return arr
}
console.log(unlink(arr));
3、利用indexOf去重
var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
if (!Array.isArray(arr)) {
console.log('错误!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) { // 首次遍历数组
if (array.indexOf(arr[i]) === -1) { // 判断索引有没有等于
array.push(arr[i])
}
}
return array
}
console.log(unlink(arr));
4、利用includes
var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array =[];
for(var i = 0; i < arr.length; i++) {
if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
console.log(unique(arr))
5、利用filter
var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
console.log(unlink(arr));
18、防抖和节流
防抖和节流是什么
浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能
为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用 防抖(debounce) 和 节流(throttle) 的方式来减少调用频率
定义
-
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
-
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
代码实现
节流
完成节流可以使用时间戳与定时器的写法
使用时间戳写法,事件会立即执行,停止触发后没有办法再次执行
function throttled1(fn, delay = 500) {
let oldtime = Date.now()
return function (...args) {
let newtime = Date.now()
if (newtime - oldtime >= delay) {
fn.apply(null, args)
oldtime = Date.now()
}
}
}
使用定时器写法,delay毫秒后第一次执行,第二次事件停止触发后依然会再一次执行
function throttled2(fn, delay = 500) {
let timer = null
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay);
}
}
}
可以将时间戳写法的特性与定时器写法的特性相结合,实现一个更加精确的节流。实现如下
function throttled(fn, delay) {
let timer = null
let starttime = Date.now()
return function () {
let curTime = Date.now() // 当前时间
let remaining = delay - (curTime - starttime) // 从上一次到现在,还剩下多少多余时间
let context = this
let args = arguments
clearTimeout(timer)
if (remaining <= 0) {
fn.apply(context, args)
starttime = Date.now()
} else {
timer = setTimeout(fn, remaining);
}
}
}
防抖
简单版本的实现
function debounce(func, wait) {
let timeout;
return function () {
let context = this; // 保存this指向
let args = arguments; // 拿到event对象
clearTimeout(timeout)
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:
function debounce(func, wait, immediate) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout); // timeout 不为null
if (immediate) {
let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
timeout = setTimeout(function () {
timeout = null;
}, wait)
if (callNow) {
func.apply(context, args)
}
}
else {
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
}
}
}
节流和防抖的区别
相同点:
- 都可以通过使用
setTimeout实现 - 目的都是,降低回调执行频率。节省计算资源
不同点:
- 函数防抖,在一段连续操作结束后,处理回调,利用
clearTimeout和setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能 - 函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次
应用场景
防抖在连续的事件,只需触发一次回调的场景有:
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求
- 手机号、邮箱验证输入检测
- 窗口大小
resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
节流在间隔一段时间执行一次回调的场景有:
- 滚动加载,加载更多或滚到底部监听
- 搜索框,搜索联想功能
19、apply、call、bind的区别
call、apply、bind主要作用都是改变this指向的,但使用上略有不同:
call和apply的主要区别实在传递参数上不同,call后面传递的参数是以逗号的形式分开的,apply传递的参数是数组形式。
bind返回的是一个函数形式,如果要执行,则后面要再加上一个小括号(),例如:bind(obj,参数1,参数2)(),bind只能以都好分隔形式,不是以数组形式
20、this的指向
this的默认绑定
1.web全局环境下的this指向window
console.log(this); // window
2.函数独立调用
- 非严格模式下,函数内部的this指向window
function fn() {
console.log(this);
}
fn(); // window
window.fn();
- 严格模式下,为undefined
'use strict'
function fn() {
console.log(this);
}
fn(); // undefined
3.被嵌套的函数独立调用时,this默认指向window
var obj = {
a: 2,
foo: function() {
function test() {
console.log(this); // window
}
test();
}
}
obj.foo();
4.IIFE自执行函数
(function() {
console.log(this); // window
})()
var obj2 = {
foo: function() {
(function () {
console.log(this); // window
})()
}
}
obj2.foo()
5.闭包中this默认指向window
var obj3 = {
a: 2,
foo: function() {
console.log(this); // obj3
var c = this.a; // 2
return function test() {
console.log(this); // window
return c;
}
}
}
var fn = obj3.foo();
console.log(fn()); // 2
隐式绑定
当函数当做方法使用时,this指向直接对象
function foo() {
console.log(this); // obj4
}
var obj4 = {
foo: foo
}
obj4.foo();
三、隐式丢失
1.被隐式绑定的函数丢失了绑定对象,从而默认绑定到window
function foo1() {
console.log(this); // window
}
var obj6 = {
a: 2,
foo: foo1
}
var bar = obj6.foo; // 在这并未执行方法
bar(); // 在这执行了方法
参数传递
function foo3(){
console.log(this);
}
function bar1(fn) {
// 默认赋值 fn = obj7.foo
fn(); // window
}
var obj7 = {
a: 1,
foo: foo3
}
bar1(obj7.foo);
3.setTimeout() 和 setInterval() 第一个参数的回调函数中的this 默认指向window
setTimeout(function() {
console.log(this); // window
}, 0)
四、显示绑定
1、call和apply
var obj = {
a:1
}
function foo() {
console.log(this);
}
foo.call(obj); //obj
foo.apply(obj); //obj
2、bind
var obj = {
a:1
}
function foo() {
console.log(this); // obj
}
var fn = foo.bind(obj);
fn();
五、new绑定
1.new关键字来执行函数,相当于构造函数来实例化对象,则this指向当前实例化的对象
function Fn() {
console.log(this); // Fn{}
}
var fn = new Fn();
六、优先级
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
21、判断js的数据类型
举例:
var a = "iamstring.";
var b = 222;
var c= [1,2,3];
var d = new Date();
var e = function(){alert(111);};
var f = function(){this.name="22";};
1. 最常见的判断方法:typeof
alert(typeof a) ------------> string
alert(typeof b) ------------> number
alert(typeof c) ------------> object
alert(typeof d) ------------> object
alert(typeof e) ------------> function
alert(typeof f) ------------> function
其中typeof返回的类型都是字符串形式,需注意,例如:
alert(typeof a == "string") -------------> true
alert(typeof a == String) ---------------> false
2.判断已知对象类型的方法:instanceof
alert(c instanceof Array) ---------------> true
alert(f instanceof Function) ------------> true
alert(f instanceof function) ------------> false
注意:instanceof 后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。
3.根据对象的constructor判断:
alert(c.constructor === Array) ----------> true
alert(d.constructor === Date) -----------> true
alert(e.constructor === Function) -------> true
4.通用但繁琐的方法:prototype
alert(Object.prototype.toString.call(a) === ‘[object String]') -------> true;
alert(Object.prototype.toString.call(b) === ‘[object Number]') -------> true;
alert(Object.prototype.toString.call(c) === ‘[object Array]') -------> true;
alert(Object.prototype.toString.call(d) === ‘[object Date]') -------> true;
alert(Object.prototype.toString.call(e) === ‘[object Function]') -------> true;
alert(Object.prototype.toString.call(f) === ‘[object Function]') -------> true;
大小写不能写错,比较麻烦,但胜在通用。
5.万能的方法:jquery.type()
如果对象是undefined或null,则返回相应的“undefined”或“null”。
jQuery.type( undefined ) === "undefined"
jQuery.type() === "undefined"
jQuery.type( window.notDefined ) === "undefined"
jQuery.type( null ) === "null"
如果对象有一个内部的Class和一个浏览器的内置对象的 Class 相同,我们返回相应的Class名字。 (有关此技术的更多细节)
jQuery.type( true ) === "boolean"
jQuery.type( 3 ) === "number"
jQuery.type( "test" ) === "string"
jQuery.type( function(){} ) === "function"
jQuery.type( [] ) === "array"
jQuery.type( new Date() ) === "date"
jQuery.type( new Error() ) === "error" // as of jQuery 1.9
jQuery.type( /test/ ) === "regexp"
其他一切都将返回它的类型“object”。
通常情况下用typeof 判断就可以了,遇到预知Object类型的情况可以选用instanceof或constructor方法,实在没辙就使用$.type()方法
22、eventLoop的理解
- 是js的底层运行原理,js是单线程的,但是也有一些耗时任务,会影响执行效率,代码都在主线程中执行,当遇见ajax请求,setTimeout定时器时候,会单独开启异步线程,异步线程耗时之后会推入异步队列中等待执行,然后当主线程执行完毕之后,会到异步队列中取出到主线程中执行,然后再去异步线程取出第二个,这个来回的过程就是*(eventloop)事件循环机制*
23、存储方式的浏览器
常见的浏览器存储主要有:
1.属于文档对象模型:documentcookie,
2.属于浏览器对象模型 localStorage,sessionStorage,indexDB
一.cookie
h5之前,存储主要用cookie,缺点是请求头上带着数据,
h5之前,存储主要用cookies,缺点是在请求头上带着数据,导致流量增加。大小限制4k。
设置cookie的方式,及可以添加的参数
1.expires(过期时间)
过了过期时间,浏览器就会删除该cookie,想要删除cookie,只需要把它过期时间设置成过去的时间即可
(如果不设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。)
document.cookie = "username=linda; expires=Thu, 18 Dec 2013 12:00:00 GMT; path=/" // 设置cookie
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT" // 删除cookie
2.path(路径)
(值可以是一个目录,或者是一个路径。)
如果cc.com/test/index.html建立了一个cookie,那么在cc.com/test/目录里的所有页面,以及该目录下面任何子目录里的页面都可以访问这个cookie。因此在cc.com/test/test2/test3里的任何页面都可以访问cc.com/test/index.html建立的cookie。若cc.com/test/若想访问cc.com/test/index.html设置的cookie,需要把cookie的path属性设置成“/”。
在指定路径的时候,凡是来自同一服务器,URL里有相同路径的所有WEB页面都可以共享cookies。
3.domain(主机名,是指同一个域下的不同主机)
例如:www.baidu.com和map.baidu.com就是两个不同的主机名。默认情况下,一个主机中创建的cookie在另一个主机下是不能被访问的,但可以通过domain参数来实现对其的控制:document.cookie = "name=value;domain=.baidu.com"
这样,所有*.baidu.com的主机都可以访问该cookie。
可以用此特性实现
2、localStorage
以键值对(Key-Value)的方式存储,键值对总是以字符串的形式存储,永久存储,永不失效,除非手动删除。IE8+支持,每个域名限制5M。打开同域的新页面也能访问得到。
window.localStorage.setItem('myCat', 'Tom'); // 设置
let cat = window.localStorage.getItem('myCat'); // 读取
window.localStorage.removeItem('myCat'); // 移除
window.localStorage.clear(); // 清空所有
window.localStorage.key(1); // 读取索引为1的值
window.localStorage.length; // 数目
3、sessionStorage
sessionStorage操作的方法与localStorage是一样的,区别在于 sessionStorage 当前页面有效,在关闭页面后即被清空,而 localStorage 除非删除,会一直保存。
注意,刷新页面sessionStorage不会清除,但是打开同域新页面访问不到
4、indexedDB
当数据量不大时,我们可以通过SessionStorage或者LocalStorage来进行存储,但是当数据量较大,或符合一定的规范时,我们可以使用数据库来进行数据的存储。
24、浏览器的缓存策略
-
缓存分为强缓存和协商缓存。其中强缓存包括
Expires和Cache-Control,主要是在过期策略生效时应用的缓存。弱缓存包括Last-Modified和ETag,是在协商策略后应用的缓存。强弱缓存之间的主要区别在于获取资源时是否会发送请求。 -
Cache-Control中的max-age指令用于指定缓存过期的相对时间,优先级高于Expires。Cache-Control指定为no-cache时,由于no-cache相当于max-age:0,must-revalidate,所以都存在时优先级也是高于Expires。 -
no-cache并不是指不缓存文件,no-store才是指不缓存文件。no-cache仅仅是表明跳过强缓存,强制进入协商策略。 -
如果
Expires,Cache-Control: max-age,或Cache-Control:s-maxage都没有在响应头中出现,并且设置了Last-Modified时,那么浏览器默认会采用一个启发式的算法,即启发式缓存。通常会取响应头的Date_value - Last-Modified_value值的10%作为缓存时间
25、vue2的生命周期
| 生命周期 | 描述 |
|---|---|
| beforeCreate | 组件实例被创建之初,data 和 methods 中的数据还没有初始化 |
| created | 组件实例已经完全创建,data 和 methods 都已经初始化好了 |
| beforeMount | 模板渲染,相关的 render 函数首次被调用,模板已经在内存中编译好了,但是尚未挂载到页面中去 |
| mounted | el 被新创建的 vm.el替换, 真实dom已经生成,el替换,真实dom已经生成,el替换,真实dom已经生成,el 可用,组件脱离创建阶段,进入运行阶段 |
| beforeUpdate | 组件数据更新之前调用, 此时页面中显示的数据还是旧的,但 data 是最新的,页面尚未和最新的数据保持同步 |
| update | 组件数据更新之后,页面和 data 数据已经保持同步,都是最新的 |
| beforeDestory | 组件销毁前调用,vue 实例从运行阶段进入到销毁阶段,这时 vue 实例身上所有都可用,还没有真正执行销毁 |
| destoryed | 组件销毁后调用,vue 实例上的所有都不可以用了 |
| activited | keep-alive 专属,组件被激活时调用, |
| deactivated | keep-alive 专属,组件被销毁时调用, |
26、http的常见的状态码
状态码的职责是当客户端向服务器发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求还是出现了错误。
状态码的类别:
| 类别 | 原因短语 | |
|---|---|---|
| 1XX | Informational(信息性状态码) | 接受的请求正在处理 |
| 2XX | Success(成功状态码) | 请求正常处理完毕 |
| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
2XX——表明请求被正常处理了
-
200 ok:请求成功。一般用于GET与POST请求/ 请求处理成功,但没有任何资源可以返回给客户端,一般在只需要从客户端往服务器发送信息,而对客户端不需要发送新信息内容的情况下使用
-
204 No Content:无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档。
-
206 Partial Content:是对资源某一部分的请求,该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。响应报文中包含由Content-Range指定范围的实体内容 3XX——表明浏览器需要执行某些特殊的处理以正确处理请求
-
301 Moved Permanently:
资源的uri已更新,你也更新下你的书签引用吧。永久性重定向,请求的资源已经被分配了新的URI,以后应使用资源现在所指的URI。 -
302 Found:
资源的URI已临时定位到其他位置了,姑且算你已经知道了这个情况了。临时性重定向。和301相似,但302代表的资源不是永久性移动,只是临时性性质的。换句话说,已移动的资源对应的URI将来还有可能发生改变。 -
303 See Other:
资源的URI已更新,你是否能临时按新的URI访问。该状态码表示由于请求对应的资源存在着另一个URL,应使用GET方法定向获取请求的资源。303状态码和302状态码有着相同的功能,但303状态码明确表示客户端应当采用GET方法获取资源,这点与302状态码有区别 -
304 Not Modified:
资源已找到,但未符合条件请求。该状态码表示客户端发送附带条件的请求时(采用GET方法的请求报文中包含If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since中任一首部)服务端允许请求访问资源,但因发生请求未满足条件的情况后,直接返回304. -
307 Temporary Redirect:
临时重定向。与302有相同的含义 4XX——表明客户端是发生错误的原因所在。 -
400 Bad Request:
服务器端无法理解客户端发送的请求,请求报文中可能存在语法错误。 -
401 Unauthorized:
该状态码表示发送的请求需要有通过HTTP认证(BASIC认证,DIGEST认证)的认证信息。 -
403 Forbidden:
不允许访问那个资源。该状态码表明对请求资源的访问被服务器拒绝了。(权限,未授权IP等) 如何清除浮动 -
404 Not Found:
服务器上没有请求的资源。路径错误等
5XX——服务器本身发生错误
-
500 Internal Server Error:
貌似内部资源出故障了。该状态码表明服务器端在执行请求时发生了错误。也有可能是web应用存在bug或某些临时故障 -
503 Service Unavailable:抱歉,我现在正在忙着。该状态码表明服务器暂时处于超负载或正在停机维护,现在无法处理请求 ES6的新增语法
27、父组件和子组件之间的生命周期执行顺序
初次渲染就会触发的生命周期
- beforeCreate() , created()
- beforeMount() , mounted() 组件的调用顺序都是先父后子,渲染完成的顺序是先子后父。 组件的销毁操作是先父后子,销毁完成的顺序是先子后父。
加载渲染过程 子组件在父组件的beforeMount和Mounted之间渲染
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
- 父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
- 影响到子组件: - 父beforeUpdate -> 子beforeUpdate->子updated -> 父updted
- 不影响子组件: - 父beforeUpdate -> 父updated
销毁过程
- 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
补充单一组件钩子执行顺序
activated,deactivated是组件keep-alive时独有的钩子
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestroy
- destroyed
- errorCaptured
28、cookie、sessionStorsge、localStorage
cookie 可以设置失效时间,但没有自己的存取取的方法,需要时封装,每次请求时跟随请求发送,而 localStorage 和 sessionStorage 可以有自己存取的方法例如:setItem(),getItem(),removeItem(),clear(),如:localStorage.setItem(‘属性’,值)
三者的不同
29、let、const,var的区别
区别
var、let、const三者区别可以围绕下面五点展开:
- 变量提升
- 暂时性死区
- 块级作用域
- 重复声明
- 修改声明的变量
- 使用
变量提升
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined
let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错
// var
console.log(a) // undefined
var a = 10
// let
console.log(b) // Cannot access 'b' before initialization
let b = 10
// const
console.log(c) // Cannot access 'c' before initialization
const c = 10
暂时性死区
var不存在暂时性死区
let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
// var
console.log(a) // undefined
var a = 10
// let
console.log(b) // Cannot access 'b' before initialization
let b = 10
// const
console.log(c) // Cannot access 'c' before initialization
const c = 10
块级作用域
var不存在块级作用域
let和const存在块级作用域
// var
{
var a = 20
}
console.log(a) // 20
// let
{
let b = 20
}
console.log(b) // Uncaught ReferenceError: b is not defined
// const
{
const c = 20
}
console.log(c) // Uncaught ReferenceError: c is not defined
重复声明
var允许重复声明变量
let和const在同一作用域不允许重复声明变量
// var
var a = 10
var a = 20 // 20
// let
let b = 10
let b = 20 // Identifier 'b' has already been declared
// const
const c = 10
const c = 20 // Identifier 'c' has already been declared
修改声明的变量
var和let可以
const声明一个只读的常量。一旦声明,常量的值就不能改变
// var
var a = 10
a = 20
console.log(a) // 20
//let
let b = 10
b = 20
console.log(b) // 20
// const
const c = 10
c = 20
console.log(c) // Uncaught TypeError: Assignment to constant variable
使用
能用const的情况尽量使用const,其他情况下大多数使用let,避免使用var
30、computed和watch的区别
methods 中都是封装好的函数,无论是否有变化只要触发就会执行
computed:是vue独有的特性计算属性,可以对data中的依赖项再重新计算,得到一个新值,应用到视图中,和methods本质区别是computed是可缓存的,也就是说computed中的依赖项没有变化,则computed中的值就不会重新计算,而methods中的函数是没有缓存的。Watch是监听data和计算属性中的新旧变化
31、vue3常用的api
(1)setup
setup 函数也是 Compsition API 的入口函数,我们的变量、方法都是在该函数里定义的,来看一下使用方法
<template>
<div id="app">
<p>{{ number }}</p>
<button @click="add">增加</button>
</div>
</template>
<script>
// 1. 从 vue 中引入 ref 函数
import {ref} from 'vue'
export default {
name: 'App',
setup() {
// 2. 用 ref 函数包装一个响应式变量 number
let number = ref(0)
// 3. 设定一个方法
function add() {
// number是被ref函数包装过了的,其值保存在.value中
number.value ++
}
// 4. 将 number 和 add 返回出去,供template中使用
return {number, add}
}
}
</script>
(2)生命周期
Vue2中有 beforeCreate 、created 、beforeMount 、mounted 、beforeUpdate 等生命周期函数
而在Vue3中,这些生命周期部分有所变化,并且调用的方式也有所改变,下面放上一张变化图来简单了解一下
| vue2 | vue3 |
|---|---|
| beforeCreate | setup |
| created | setup |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeDestory | onBeforeUnmount |
| destoryed | destoryed |
Vue3的这些生命周期调用也很简单,同样是先从 vue 中导入,再进行直接调用
<template>
<div id="app"></div>
</template>
<script>
// 1. 从 vue 中引入 多个生命周期函数
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted} from 'vue'
export default {
name: 'App',
setup() {
onBeforeMount(() => {
// 在挂载前执行某些代码
})
onMounted(() => {
// 在挂载后执行某些代码
})
onBeforeUpdate(() => {
// 在更新前前执行某些代码
})
onUpdated(() => {
// 在更新后执行某些代码
})
onBeforeUnmount(() => {
// 在组件销毁前执行某些代码
})
unMounted(() => {
// 在组件销毁后执行某些代码
})
return {}
}
}
</script>
(3)reactive
reactive 方法是用来创建一个响应式的数据对象,该API也很好地解决了Vue2通过 defineProperty 实现数据响应式的缺陷
用法很简单,只需将数据作为参数传入即可,代码如下
<template>
<div id="app">
<!-- 4. 访问响应式数据对象中的 count -->
{{ state.count }}
</div>
</template>
<script>
// 1. 从 vue 中导入 reactive
import {reactive} from 'vue'
export default {
name: 'App',
setup() {
// 2. 创建响应式的数据对象
const state = reactive({count: 3})
// 3. 将响应式数据对象state return 出去,供template使用
return {state}
}
}
</script>
(4)ref
在介绍 setup 函数时,我们使用了 ref 函数包装了一个响应式的数据对象,这里表面上看上去跟 reactive 好像功能一模一样啊,确实差不多,因为 ref 就是通过 reactive 包装了一个对象 ,然后是将值传给该对象中的 value 属性,这也就解释了为什么每次访问时我们都需要加上 .value
我们可以简单地把 ref(obj) 理解为这个样子 reactive({value: obj})
<script>
import {ref, reactive} from 'vue'
export default {
name: 'App',
setup() {
const obj = {count: 3}
const state1 = ref(obj)
const state2 = reactive(obj)
console.log(state1)
console.log(state2)
}
}
</script>
打印效果:
注意: 这里指的
.value是在setup函数中访问ref包装后的对象时才需要加的,在template模板中访问时是不需要的,因为在编译时,会自动识别其是否为ref包装过的
(5)toRef
toRef 是将某个对象中的某个值转化为响应式数据,其接收两个参数,第一个参数为 obj 对象;第二个参数为对象中的属性名
<script>
// 1. 导入 toRef
import {toRef} from 'vue'
export default {
setup() {
const obj = {count: 3}
// 2. 将 obj 对象中属性count的值转化为响应式数据
const state = toRef(obj, 'count')
// 3. 将toRef包装过的数据对象返回供template使用
return {state}
}
}
</script>
ref是对传入数据的拷贝;toRef是对传入数据的引用ref的值改变会更新视图;toRef的值改变不会更新视图
(6)toRefs
了解完 toRef 后,就很好理解 toRefs 了,其作用就是将传入的对象里所有的属性的值都转化为响应式数据对象,该函数支持一个参数,即 obj 对象
<script>
// 1. 导入 toRefs
import {toRefs} from 'vue'
export default {
setup() {
const obj = {
name: '前端印象',
age: 22,
gender: 0
}
// 2. 将 obj 对象中属性count的值转化为响应式数据
const state = toRefs(obj)
// 3. 打印查看一下
console.log(state)
}
}
</script>
打印效果:
(7)shallowReactive
听这个API的名称就知道,这是一个渐层的 reactive,难道意思就是原本的 reactive 是深层的呗,没错,这是一个用于性能优化的API
其实将 obj 作为参数传递给 reactive 生成响应式数据对象时,若 obj 的层级不止一层,那么会将每一层都用 Proxy 包装一次,我们来验证一下
<script>
import {reactive} from 'vue'
export default {
setup() {
const obj = {
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
const state = reactive(obj)
console.log(state)
console.log(state.first)
console.log(state.first.second)
}
}
</script>
打印效果:
接下来我们再来看看
shallowReactive
<script>
import {shallowReactive} from 'vue'
export default {
setup() {
const obj = {
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
const state = shallowReactive(obj)
console.log(state)
console.log(state.first)
console.log(state.first.second)
}
}
</script>
打印效果:
(8)shallowRef
(9)toRaw
(10)markRaw
(11)provide && inject
(12)watch && watchEffect
watch 和 watchEffect 都是用来监视某项数据变化从而执行指定的操作的,但用法上还是有所区别watch:watch( source, cb, [options] )
参数说明:
- source:可以是表达式或函数,用于指定监听的依赖对象
- cb:依赖对象变化后执行的回调函数
- options:可参数,可以配置的属性有 immediate(立即触发回调函数)、deep(深度监听)
(13)getCurrentInstance
(14)useStore
在Vue2中使用 Vuex,我们都是通过 this.$store 来与获取到Vuex实例,但上一部分说了原本Vue2中的 this 的获取方式不一样了,并且我们在Vue3的 getCurrentInstance().ctx 中也没有发现 $store 这个属性,那么如何获取到Vuex实例呢?这就要通过 vuex 中的一个方法了,即 useStore
// store 文件夹下的 index.js
import Vuex from 'vuex'
const store = Vuex.createStore({
state: {
name: '前端印象',
age: 22
},
mutations: {
……
},
……
})
// example.vue
<script>
// 从 vuex 中导入 useStore 方法
import {useStore} from 'vuex'
export default {
setup() {
// 获取 vuex 实例
const store = useStore()
console.log(store)
}
}
</script>
(15)获取标签元素
最后再补充一个 ref 另外的作用,那就是可以获取到标签元素或组件
在Vue2中,我们获取元素都是通过给元素一个 ref 属性,然后通过 this.$refs.xx 来访问的,但这在Vue3中已经不再适用了
接下来看看Vue3中是如何获取元素的吧
<template>
<div>
<div ref="el">div元素</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
// 创建一个DOM引用,名称必须与元素的ref属性名相同
const el = ref(null)
// 在挂载后才能通过 el 获取到目标元素
onMounted(() => {
el.value.innerHTML = '内容被修改'
})
// 把创建的引用 return 出去
return {el}
}
}
</script>
获取元素的操作一共分为以下几个步骤:
- 先给目标元素的
ref属性设置一个值,假设为el - 然后在
setup函数中调用ref函数,值为null,并赋值给变量el,这里要注意,该变量名必须与我们给元素设置的ref属性名相同 - 把对元素的引用变量
el返回(return)出去
补充:设置的元素引用变量只有在组件挂载后才能访问到,因此在挂载前对元素进行操作都是无效的
32、JS常见的创建方式
js常用的几种创建对象的方式有:
{}new Object()- 使用字面量
- 工厂模式
- 构造函数模式(constructor)
- 原型模式(prototype)
- 构造函数+原型模式
一、通过{}创建对象
demo1: 01.newObj-1.html
<script>
'use strict'; //使用strict模式
/**
使用{}创建对象,等同于 new Object();
**/
var o = {};
o.name = 'jack';
o.age = 20;
o.sayName = function(){
alert(this.name);
}
alert(o.name+'-'+o.age);
o.sayName();
</script>
如果对象不用重复创建,这种方式是比较方便的。
二、通过new Object()创建对象
demo2: 02.newObj-2.html
<script>
'use strict';
// 使用 new Object() 创建对象
var o = new Object();
o.name = "zhangsna";
o.sayName = function(){
alert(this.name);
}
o.sayName();
alert('o instanceof Object>>>>>>>>>>>'+(o instanceof Object));//true
alert("typeof o >>>>> "+typeof o);//object
</script>
三、使用字面量创建对象
对象字面变量是对象定义的一种简写形式,举个例子:
var person = {name: 'zhang', age:20}, 这就是字面量形式,完全等价于var person = {}; person.name='zhang'; person.age=20;
小结:前面三种创建对象的方式存在2个问题:1.代码冗余; 2.对象中的方法不能共享,每个对象中的方法都是独立的。
四、使用工厂模式创建对象
这种方式是使用一个函数来创建对象,减少重复代码,解决了前面三种方式的代码冗余的问题,但是方法不能共享的问题还是存在。
demo4: 04.newObj-4.html
<script>
'use strict';
// 使用工厂模式创建对象
// 定义一个工厂方法
function createObject(name){
var o = new Object();
o.name = name;
o.sayName = function(){
alert(this.name);
};
return o;
}
var o1 = createObject('zhang');
var o2 = createObject('li');
//缺点:调用的还是不同的方法
//优点:解决了前面的代码重复的问题
alert(o1.sayName===o2.sayName);//false
</script>
五、通过构造函数创建对象
所谓构造函数,也是普通的函数,不过约定俗成,构造函数的名称首字母大写,普通函数的首字母小写。通过new 构造函数来创建对象。
demo5:05.newObj-5.html
<script>
'use strict';
/**
* 构造函数模式创建对象
**/
function Person(name){
this.name = name;
this.sayName = function(){
alert(this.name);
};
}
var p1 = new Person('zhang');
var p2 = new Person('li');
p1.sayName();
p2.sayName();
alert(p1.constructor === p2.constructor);//true
alert(p1.constructor === Person);//true
alert(typeof(p1));//object
alert(p1 instanceof Object); //true
alert(p2 instanceof Object); //trueb
alert(p1.sayName===p2.sayName);//false
</script>
六、通过原型模式创建对象
每个方法中都有一个原型(prototype),每个原型都有一个构造器(constructor),构造器又指向这个方法。
举个例子:
function Animal(){}
alert(Animal.prototype.constructor==Animal);//true
原型创建对象:
<script>
'use strict';
/*
* 原型模式创建对象
*/
function Animal() { }
Animal.prototype.name = 'animal';
Animal.prototype.sayName = function () { alert(this.name); };
var a1 = new Animal();
var a2 = new Animal();
a1.sayName();
alert(a1.sayName === a2.sayName);//true
alert(Animal.prototype.constructor);//function Animal(){}
alert(Animal.prototype.constructor==Animal);//true
</script>
通过原型创建对象,把属性和方法绑定到prototype上,通过这种方式创建对象,方法是共享的,每个对象调用的是同一个方法
七、通过原型+构造函数的方式创建对象
这种方式结合了上面两种方式,解决了代码冗余,方法不能共享,引用类型改变值的问题
<script>
'use strict';
function Animal(name){
this.name = name;
this.friends = ['dog','cat'];
}
Animal.prototype.sayName = function(){
alert(this.name);
};
var a1 = new Animal('d');
var a2 = new Animal('c');
a1.friends.push('snake');
alert(a1.friends);//[dog,cat,snake]
alert(a2.friends);//[dog,cat]
</script>
33、==和-==的区别
双等号==
-
如果两个值类型相同,再进行三个等号(===)的比较
-
如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:
-
如果一个是
null,一个是undefined,那么相等 -
如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较
-
三等号===
-
如果类型不同,就一定不相等
-
如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)
-
如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。
-
如果两个值都是true,或是false,那么相等
-
如果两个值都引用同一个对象或是函数,那么相等,否则不相等
-
如果两个值都是null,或是undefined,那么相等
34、阻止事件冒泡的方式
冒泡事件:比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡。
<div class="box">
<button class="btn">按钮</button>
</div>
<script type="text/javascript">
$('.btn').click(function () {
alert('按钮被点击了')
});
$('.box').click(function () {
alert('box被点击了')
})
</script>
阻止冒泡事件有三种方法:
1.event.stopPropagation()方法
$('.btn').click(function (even) {
even.stopPropagation();
alert('按钮被点击了');
})
这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开. 例如:
<a href="https://www.csdn.net/" class="box">
<button class="btn">按钮</button>
</a>
2.event.preventDefault()方法
$('.btn').click(function (even) {
even.preventDefault();
alert('按钮被点击了');
})
这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;
3.return false ;
$('.btn').click(function (even) {
alert('按钮被点击了');
return false;
})
这个方法比较暴力,他会同事阻止事件冒泡也会阻止默认事件;写上此代码,连接不会被打开,事件也不会传递到上一层的父元素;可以理解为return false就等于同时调用了event.stopPropagation()和event.preventDefault()
35、http请求方法有哪些
-
HTTP1.0 定义了三种请求方法: GET, POST 和 head 方法。
-
HTTP1.1 新增了六种请求方法:options、put、patch、detale、trace 和 connect 方法
| 序号 | 方法 | 描述 |
|---|---|---|
| 1 | GET | 请求指定的页面信息,并返回实体主体。 |
| 2 | head | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
| 3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
| 4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
| 5 | DELETE | 请求服务器删除指定的页面。 |
| 6 | CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
| 7 | OPTIONS | 允许客户端查看服务器的性能。 |
| 8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
| 9 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
36、rem和em的区别
-
rem是相对于根字号,即相对于标签的
font-size实现的,浏览器默认字号是font-size:16px -
em:是相对于父元素标签的字号,和百分比%类似,%也是相对于父级的,只不过%相对于父级宽度的,而em相对于父级字号的
37、实现一个div上下左右居中的三种方法
第一个方法
- 设置宽高
- 设置绝对定位
- 设置left/top为50%
- margin-left:-宽度/2
- margin-top:-高度/2
.box {
position: absolute;
left:50%;
top:50%;
margin-left:-50px;
margin-top:-50px;
width:100px;
height: 100px;
background-color: red;
}
第二种方法
- 设置宽高
- 设置绝对定位属性
- 设置left/right/top/bottom为0
- 设置margin:auto
.box {
position: absolute;
left:0;
top:0;
right:0;
bottom:0;
margin:auto;
width:100px;
height: 100px;
background-color: green;
}
第三种方法
transforms 变形(最简单的方法)
内容块定义transform: translate(-50%,-50%) 必须加上top: 50%; left: 50%;
.box {
padding: 20px;
background: orange;
color: #fff;
position: absolute;
top: 50%;
left: 50%;
border-radius: 5px;
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
第四种方法
给父元素加CSS属性
.box{
justify-content: center; /*子元素水平居中*/
align-items: center; /*子元素垂直居中*/
display: -webkit-flex;
}
38、v-for和v一if的区别
区别
在处于同一节点的时候,v-for 优先级比 v-if 高。 这意味着 v-if 将分别重复运行于每个 v-for 循环中。 即——先运行v-for 的循环,然后在每一个v-for 的循环中,再进行 v-if 的条件对比。
作用
v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染
v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组或者对象,而 item 则是被迭代的数组元素的别名
在 v-for 的时候,建议设置key值,并且保证每个key值是独一无二的,这便于diff算法进行优化
两者在用法上
<Modal v-if="isShow" />
<li v-for="item in items" :key="item.id">
{{ item.label }}
</li>
39、router传参的方式
一、使用vue里的标签来传递参数
1.标签传参
<router-link :to="{path:'/login',query:{userId: "33333"}}"></router-link>
<router-link :to="{name:''Message'',params:{userId:'1234'}}">Hi页面1</router-link>
2.接收参数用this.$route.params.userId
二、使用router的name属性也就是params来传递参数,params:参数不会显示到路径上,用params传参,这个方法有一个bug就是当你传参过去的时候,再次刷新页面时参数就会丢失
params传参: 1.配置路径router
export default new Router({
routes: [
{
path: '/testVueRouter',
name: 'TestVueRouter',
component: TestVueRouter
},
{
path: '/testVueRouterTo',
// 一定要写name,params必须用name来识别路径
name: 'TestVueRouterTo',
component: TestVueRouterTo
}
]
})
2.传递参数用$router
<!-- test-vue-router页面 -->
<template>
<div>
<a @click="routerTo()">query传参</a>
</div>
</template>
<script>
export default {
methods: {
routerTo() {
this.$router.push({
name: `TestVueRouterTo`,
params: {
page: '1', code: '8989'
}
})
}
}
}
</script>
3:接受参数用$route
<!-- test-vue-router-to页面 -->
<template>
<div>
</div>
</template>
<script>
export default{
data() {
return {
page: '',
code: ''
}
},
created() {
this.getRouterData()
},
methods: {
getRouterData() {
this.page = this.$route.params.page
this.code = this.$route.params.code
console.log('page', this.page)
console.log('code', this.code)
}
}
}
</script>
三、query:最好也用name来识别,保持与params一致性,好记了,路径传参,query:由于参数适用路径传参的所以F5强制刷新也不会被清空。(传参强烈建议适用string)
1.配置路径router
export default new Router({
routes: [
{
path: '/testVueRouter',
name: 'TestVueRouter',
component: TestVueRouter
},
{
path: '/testVueRouterTo',
// 一定要写name,params必须用name来识别路径
name: 'TestVueRouterTo',
component: TestVueRouterTo
}
]
})
2.query传参
<!-- test-vue-router页面 -->
<template>
<div>
<a @click="routerTo()">query传参</a>
</div>
</template>
<script>
export default {
methods: {
routerTo() {
this.$router.push({
name: `TestVueRouterTo`, // 只是把query改了,其他都没变
query: {
page: '1', code: '8989'
}
})
}
}
}
</script>
3.接收参数
<!-- test-vue-router-to页面 -->
<template>
<div>
</div>
</template>
<script>
export default{
data() {
return {
page: '',
code: ''
}
},
created() {
this.getRouterData()
},
methods: {
getRouterData() {
// 只是改了query,其他都不变
this.page = this.$route.query.page
this.code = this.$route.query.code
console.log('page', this.page)
console.log('code', this.code)
}
}
}
</script>
如果要隐藏参数用params,如果强制刷新不被清除用query
40、v一show和v一if的区别
v-if 和 v-show 都可以显示和隐藏一个元素,但有本质区别
-
v-if 是惰性的,只是值为 false 就不会加载对应元素,为 true 才动态加载对应元素
-
v-show:是无论为 true 和为 false 都会加载对应 html 代码,但为 false时用 display:none 隐藏不在页面显示,但为 true 时页面上用 display:block显示其效果
适用场景:切换频繁的场合用 v-show,切换不频繁的场合用 v-if
41、url输入到地址栏后发生了什么
DOM 树构建一个样式表.组合成一颗 render 树,页面经过重绘(重塑)和回流的过程。
过程大概是这样:
1. DNS 解析
2.TCP连接
-
发送
HTTP请求 -
服务器处理请求并返回需要的数据
-
浏览器解析渲染页面
-
连接结束
输入了一个域名,域名要通过DNS解析找到这个域名对应的服务器地址(ip),通过 TCP请求链接服务,通过 WEB 服务器(apache)返回数据,浏览器根据返回数据构建 DOM 树,再把css 形成一个样式表.这两个东西结合,变成了 render 树.页面上通过重绘和回流的过程,渲染出页面来
42、Nurmber和parselnt的区别
1、parseInt()
parseInt()函数将给定的字符串以指定的基数解析为整数。
parseInt(string,radix)
第二个参数表示使用的进制,我们一般使用10进制,也可能会有到8或者16进制。为了避免对“0”和“0x”开头的字符串解析错误,各种javascript编程规范都规定必须要明确给出第二个参数的值,如parseInt(“123”,10).
parseInt('16', 8) = 14
parseInt('10', 8) = 8
parseInt('16', 10) = 16
parseInt('10', 10) = 10
parseInt('16', 16) = 22
parseInt('10', 16) = 16
parseInt从头解析string为整数,在遇到不能解析的字符时就返回已经解析的整数部分,如果第一个字符就不能解析,就直接返回NaN。
2、Number()
Number()在不用new操作符时,可以用来执行类型转换。如果无法转换为数字,就返回NaN,
像“123a”,parseInt()返回是123,Number()返回是NaN。
// 当字符串是由数字组成的时候 他们转换的数字一样的没有差别
let numStr = '123'
console.log(parseInt(numStr)) //123
console.log(Number(numStr)) //123
// 当字符串是由字母组成的时候
let numStr = 'abc'
console.log(parseInt(numStr)) //NaN
console.log(Number(numStr)) //NaN
// 当字符串是由数字和字母组成的时候
let numStr = '123a'
console.log(parseInt(numStr)) //123
console.log(Number(numStr)) //NaN
// 当字符串是由0和数字
let numStr = '0123'
console.log(parseInt(numStr)) //123
console.log(Number(numStr)) //123
// **当字符串包含小数点**
let numStr = '123.456'
console.log(parseInt(numStr)) //123
console.log(Number(numStr)) //123.456
// **当字符串为null时**
let numStr = null
console.log(parseInt(numStr)) //NaN
console.log(Number(numStr)) //0
// **当字符串为''(空)时**
let numStr = ''
console.log(parseInt(numStr)) //NaN
console.log(Number(numStr)) //0
43、axios的封装有哪一些
对http封装
- 设置请求超时
post头设置- 请求拦截
- 响应拦截
- 重复请求取消(这里涉及到了如何取消请求)
- 错误处理
- 断网处理
- 工具函数
api封装
api 封装,主要是用于单个模块所需要的接口进行管理。其中包括了
- 总
api接口的映射 - 环境变量的切换
- 本地
mock的功能 - 单个模块的接口列表
44、vueRouter的原理
概念
通过改变 URL,在不重新请求页面的情况下,更新页面视图。
实现方式
更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式。
1.Hash利用url中的hash("#")
2.利用History interface在HTML5中新增的方法
Vue中,它是通过mode这一参数控制路由的实现模式
const router=new VueRouter({
mode:'history',
routes:[...]
})
mode区别
- mode:"
hash" 多了 “#”
http://localhost:8080/#/login
2.mode:"history"
http://localhost:8080/recommend
Hash 、History:
-
hash("#")的作用是加载 URL 中指示网页中的位置。 -
#本身以及它后面的字符称职位 hash,可通过window.location.hash获取
hash特点:
1. hash 虽然出现在 url 中,但不会被包括在 http 请求中,它是用来指导浏览器动作的,对服务器端完全无用,因此,改变 hash 不会重新加载页面。
- 可以为
hash的改变添加监听事件:
window.addEventListener("hashchange",funcRef,false)
3. 每一次改变 hash(window.localtion.hash),都会在浏览器访问历史中增加一个记录。
History拥有两个方法,一是push,二是replace
两个方法:HashHistory.push() 和 HashHistory.replace() |
|---|
History.push() 将新路由添加到浏览器访问历史的栈顶
HashHistory.replace() replace()方法与push()方法不同之处在于,它并不是将新路由添加到浏览器访问历史的栈顶,而是替换掉当前的路由