前端知识点总结

285 阅读17分钟

vue

1.什么是动态组件

  1. 在一个挂载点可以挂载多个组件,并进行切换。
  2. 在vue中有两种方法动态切换组
    component is='active'></component> 使用v-if 切换
    以上两种方式均可做到组件的动态切换,但是组件切换时每次都会将上一个组件销毁,然后渲染下一个组件,有时候这并不是我们所想的,对于频繁切换的时候会很消耗性能,所以我们可以通过 keep-alive 对组件进行缓存对于不显示的组件不是销毁他,而是使他处于不被激活状态
  3. 和keep-alive 相关的两个生命周期 activated deactivated
  4. keep-alive的两个属性
    include 表示只能允许匹配到的组件生效
    exclude 则相反,除了匹配到的组件之外有效
<!-- 字符串 逗号分隔字符串, a,b 分别为组件名 -->
<keep-alive include="a,b">
  <component :is="currentView"></component>
</keep-alive>
<!-- 正则表达式 -->
<keep-alive :include="/a|b/">
  <component :is="currentView"></component>
</keep-alive>
<!-- 数组 -->
<keep-alive :include="['a', 'b']">
  <component :is="currentView"></component>
</keep-alive>
<keep-alive>
	<component :is="currentView"></component>
</keep-alive>

import a from "./a";
import b from "./b";
data(){
	return{
    	currentView:'a'
    }
}

method:{
	activated() {
     	console.log("a 页面被添加");// 被缓存的组件激活时触发
 	},
 	deactivated() {
     	console.log("a 页面被移除"); // 被切换到其他组件时触发
 	}
}

2.函数式组件

Vue 提供了一种称为函数式组件的组件类型,用来定义那些没有响应数据,也不需要有任何生命周期的场景,
它只接受一些props 来显示组件。

3.vue中的路由钩子

1.全局路由钩子

这类钩子主要作用于全局,一般用来判断权限,以及以及页面丢失时候需要执行的操作
  • router.beforeEach

    此钩子有三个参数 to from next

  • router.afterEach

    `此钩子没有next,不能改变导航`
    

2.路由独享的钩子

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

3.组件内的钩子

  • beforeRouteEnter

    不能访问 this,因为钩子在导航确认前被调用,因此即将登场的新组件还没被创建。 不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

    beforeRouteEnter (to, from, next) {
      next(vm => {
        // 通过 `vm` 访问组件实例
      })
	}

  • beforeRouteUpdate
  • beforeRouteLeave 可以访问this

4.路由传参方式

1.直接通过$router.push来传递参数

此种情况需要在对应的路由下配置参数
 {
     path: '/about/:id',
     name: 'Describe',
     component: Describe
  }
  this.$router.push(`/about/${id}`)
  

2.通过params传参

  此种情况刷新参数会消失,不会在地址栏看到,并且只能通过name来跳转,
  this.$router.push({
      name:'about',
      params:{
          id:id
      }
  })

3.通过query传参

   此种情况刷新参数不会消失,并且会在地址栏看到,可以通过name,或者path跳转
   this.$router.push({
      path:'/about',
      query:{
          id:id
      }
  })

5.computed和watch区别

1.computed
	a.支持缓存,只有依赖的数据发生了变化才会重新计算。
    b.不支持异步,在computed中的异步操作无效,无法监听数据的变化。
    c.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,
      也就是基于data中声明过的数据通过计算得到的。
2.watch
	a.不支持缓存,数据变,直接会触发相应的操作。
    b.支持异步。
    c.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值。
    d.深度监听 deep。
    e.使用watch时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。
      如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。

6.vue子组件如何获取父组件的异步数据

1.可以在父组件中通过v-if控制,只有当数组成功拿到后再渲染子组件
2.通过在子组件中使用watch监听传过来的值

7.生命周期

1.beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed
2.keep-alive 有自己独立的钩子函数 activated 和 deactivated

微信截图_20210407153831.png

JS

1.promise

主要解决异步问题,传统方法一般为回调函数解决异步问题

1.三种状态

  • 进行中(pending)
  • 成功(fulfilled)
  • 失败(rejected)

2.优点

  • 有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
  • Promise 对象提供统一的接口,使得控制异步操作更加容易。

3.缺点

  • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

4.promise.all()

Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。但是只要有一个失败了,就会返回失败,并且为第一个失败的promise的结果。

5.promise.race()

Promise.race([p1, p2,p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

2.this 问题

1.this是什么

this是指包含它的函数作为方法被调用时所属的对象。 谁特么的调用了我,我就是谁的对象,谁特么的最后调用了我,我就是谁的对象。

function showName(){
     console.log(this); //window
 }
showName();

function testFun () {
    var name="hello this!";
    console.log(this.name);
}
testFun();
/**
*此时打印不出,因为此时的this指向的是window
*/

var name = "hey";
function testFun() {
    var name = "hello this!";
    console.log(this.name);//hey
}
testFun();


var obj={
    a:"哈哈哈",
    b:function(){
        var a="嘿嘿嘿";
        console.log(this.a);
    }
};
obj.b();
/**
*因为此时是obj调用的b 所以此时this属于obj  打印 '哈哈哈'
*/

2.箭头函数中的this

  • 箭头函数中的this是在定义函数时绑定,普通函数是在执行函数时绑定,
  • 箭头函数的this绑定看的是this所在的函数定义在哪个对象下,绑定到哪个对象则this就指向哪个对象
  • 如果有对象嵌套的情况,则this绑定到最近的一层对象上

3.原型与原型链

  • 所有引用类型(数组、函数、对象)都可以自由扩展属性(null除外)
  • 所有的引用类型都有一个 _ proto _ 属性(隐式原型)
  • 所有的函数都有一个 prototype 属性(显示原型)
  • 所有的引用类型的 _ proto _ 属性指向他的构造函数的prototype属性
  • 当寻找一个对象的某个属性时,如果这个对象本身不存在该属性,则会在该对象的 _ _ proto _ _ 属性中去查找(也是该对象的构造函数的prototype中查找)

微信截图_20210311170025.png

4.继承

  1. 原型继承
    子类的原型对象指向父类的实例;即将父类挂载到子类的原型上。
    优点: 可以继承父类所有属性和方法
    缺点: 
    1.子类无法向父类传参;
    2.如果要给子类的原型上新增属性和方法,就必须放在Child.prototype = new Parent()这样的语句后面;
    3.父类原型链上的属性会被多个实例共享,这样会造成一个实例修改了原型,其他的也会改变。
    
  2. 构造函数继承
    在子类构造函数内部使用call/apply来调用父类构造函数。
       * 通过call()、apply()或者bind()方法直接指定this的绑定对象, 如foo.call(obj)
       * 使用.call()或者.apply()的函数是会直接执行的
       * 而bind()是创建一个新的函数,需要手动调用才会执行
       * call()和.apply()用法基本类似,不过call接收若干个参数,而apply接收的是一个数组
    function Child () {
        Parent.call(this, ...arguments)
    }
    优点: 
    1.保证了原型链中引用类型值的独立,不再被所有实例共享;
    2.子类可以向父类传递参数
    缺点:构造函数继承只能继承父类的属性和方法,无法继承父类的原型。
    
    
  3. 组合继承
    使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.
    function Parent(name, sex){
      this.name = name;
      this.sex = sex;
      this.hobby = ['游泳','跑步'];
      this.desc = '我是描述';
      console.log('我是parent')
    }
    Parent.prototype.getInfo=function(){
      console.log(this.name, this.sex)
    }
    
    function Child (name, sex){
      //继承父类构造函数的属性和方法
      Parent.call(this, name, sex)
    }
    
    //继承父类的原型
    Child.prototype = new Parent();
    
    var lisa = new Child('lisa','girl');
    console.log(lisa)
    lisa.getInfo() //lisa girl
    
    优点:
    1.可以继承父类实例属性和方法,也能够继承父类原型属性和方法;
    2.弥补了原型链继承中引用属性共享的问题;
    3.可传参,可复用
    缺点:
    1.使用组合继承时,父类的构造函数被调用了两次。
    2.生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法,
      所以增加了不必要的内存
    
  4. 寄生组合式继承
    原理: 使用Object.create(proto, [propertiesObject])方法创建一个新的对象。
           proto参数必须,代表新建对象的原型对象,
      function Person(desc){
         this.color = ['red'];
         this.desc = desc;
         console.log('哈哈')
      }
    
     Person.prototype.getName = function(){
       console.log(this.name);
     }
    
     Child.prototype = Object.create(Person.prototype);
    
     function Child(name, age, desc) {
       this.name = name;
       this.age = age;
       Person.call(this,desc);
     }
    
     const Jack = new Child('Jack', 23, '我是Jack');
     Jack.color.push('pink')
     const Iric = new Child('Iric', 20, '我是Iric');
     Iric.color.push('orange')
     console.log(Jack);
     Jack.getName();
     console.log(Iric);
     Iric.getName()
     
     优点:
     1.公有的写在原型;
     2.私有的写在构造函数;
     3.可以向父类传递参数;
     4.不会重复调用父类
     缺点:需要手动绑定 constructor (如果重写 prototype)
    
  5. ES6继承
    Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多 
    主要利用class配合extendssuper实现继承;
    class Person{
      constructor(name, age){
        this.name = name;
        this.age = age;
        this.color = ['red'];
      }
      getName(){
        console.log(this.name);
      }
    }
    
    class Child extends Person{
      constructor(name, age){
        super(name, age)    
      }
    }
    
    const Jack = new Child('Jack',20);
    const Iric = new Child('Iric',23);
    Jack.color.push('pink');
    Iric.color.push('orange');
    Jack.getName();
    Iric.getName();
    console.log(Jack);
    console.log(Iric);
    

5.new 做了些啥

1.new命令的作用执行构造函数,返回一个实例对象。
2.使用new命令经历的步骤
  (1).创建一个空对象,作为将要返回的对象实例;
  (2).将这个空对象的原型指向构造函数的prototype属性;
  (3).绑定this指向(将构造函数的作用域赋给新对象,因此 this 就指向了这个新对象);
  (4).执行构造函数(为这个新对象添加属性);
  (5).返回新对象(确保返回的是对象).

6.for循环中的var和let声明

for(var i=0,i<5,i++){
  setTimeout(()=>{console.log(i)},0)	// 5,5,5,5,5
}
for(let i=0,i<5,i++){
  setTimeout(()=>{console.log(i)},0)	// 0,1,2,3,4
}

之所以会这样,因为在退出循环时,迭代变量保存的是导致循环退出的值:5;在之后执行超时逻辑时,所有的i都是同一个变量。 而在使用let声明迭代变量时,JavaScript引擎在后台会给每个迭代循环声明一个新的迭代变量,每个setTimeout引用的都是不同的变量实例

7.防抖、节流

1.防抖

防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。 防抖重在清零 clearTimeout(timer)

场景:

  • 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
  • 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
  • 文本编辑器实时保存,当无任何更改操作一秒后进行保存
function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

2.节流

控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。 节流重在加锁

场景:

  • scroll 事件,每隔一秒计算一次位置信息等
  • 浏览器播放事件,每个一秒计算一次进度信息等
  • input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)
function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}

8.执行上下文

执行上下文类型:

1.全局执行上下文; 2.函数执行上下文; 3.eval函数执行上下文

this绑定

在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this引用 Window 对象)。 在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么this会被设置成那个对象,否则this的值被设置为全局对象或者 undefined(在严格模式下)

9.闭包

闭包是一个可以访问外部作用域的内部函数,即使这个外部作用域已经执行结束。
当外部作用域执行完毕后,内部函数还存活(仍在其他地方被引用)时,闭包才真正发挥其作用。
应用场景

  1. setTimeout
    原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果
    //通过闭包可以实现传参效果
    function func(param){
        return function(){
            alert(param)
        }
    }
    var f1 = func(1);
    setTimeout(f1,1000);
  1. 回调
    我们定义行为,然后把它关联到某个用户事件上(点击或者按键)。我们的代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上
<body>
    <p>哈哈哈哈哈哈</p>
    <a href="#" id="size-12">12</a>
    <a href="#" id="size-14">14</a>
    <a href="#" id="size-16">16</a>

    <script>
        function changeSize(size){
            return function(){
                document.body.style.fontSize = size + 'px';
            };
        }

        var size12 = changeSize(12);
        var size14 = changeSize(14);
        var size16 = changeSize(16);

        document.getElementById('size-12').onclick = size12;
        document.getElementById('size-14').onclick = size14;
        document.getElementById('size-16').onclick = size16;
    </script>
</body>

  1. 封装变量
    如创建一个计数器
    在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说, 闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。
function create_counter(initial) {
    var x = initial || 0;
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}
var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

10.拷贝

浅拷贝----只复制引用,而未复制真正的值;深拷贝----对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。

  • 复制运算符 属于浅拷贝
  • JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;如对象的JSON.stringify/parse,数组有两个方法 concat 和 slice以及...扩展运算符
  • JSON.stringify/parse,对于对象中存在 undefined、function、symbol 会在转换过程中被忽略。
  • 真正的深拷贝请用递归
function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
  for(let keys in source){ // 遍历目标
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 如果不是,就直接赋值
        targetObj[keys] = source[keys];
      }
    } 
  }
  return targetObj;
}

11.原生操作dom和bom

1.dom
document.createElement(tagName) 创建元素
paranetElement.appendChild(child) 添加到节点,子节点的最后
paranetElement.insertBefore(newElement, Element) 添加到节点的前面
Element.innerHTML 修改DOM的文案
paranetElement.removeChild(element)删除DOM
2.bom
window.alert
prompt() 显示可提示用户输入的对话框
confirm() 显示一个带有提示信息、“确定”和“取消”按钮的对话框
open() 打开一个新的浏览器窗口,加载给定URL所指定的文档
location对象等

12.es6中的class

浏览器

1.输入url 发生了什么

  1. 首先进行url解析,根据dns系统进行IP查找;
  2. 根据IP找到服务器,然后浏览器和服务器进行三次握手建立连接,如果是https还会建立TLS连接以及协商加密算法;
  3. 连接建立后浏览器开始发送请求获取文件(后面步骤当作不走缓存)
  4. 首先获取html文件,构建dom树,这个过程是边下载边解析;
  5. 解析HTML头部的时候(当作css放头部,js放尾部),发现有css文件,此时下载css文件(css也是边下载边解析),构建cssom树,当dom树和cssom树全部构建完成之后,浏览器会将dom树和cssom树构建成渲染树(包含样式计算:主要为DOM树上的节点找到对应的样式);
  6. 构建布局树,样式计算完成之后就开始构建布局树,主要是为DOM树上的节点找到页面上对应位置以及一些"display:none"元素的隐藏;
  7. 构建分层树,主要为了满足滚动条,z-index,position这些复杂的分层操作;
  8. 将分层树图块化,利用光栅找到视图窗口下的对应的位图。主要是因为一个页面可能很长,一下渲染出来比较浪费,所以浏览器会找到视图窗口对应的图块,将这部分的图块进行渲染;
  9. 最终渲染进程将整个页面渲染出来,在渲染过程中还会出现重排和重绘。

2.浏览器如何管理内存,清除变量,垃圾回收

网络

1.304

该状态码表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但未满足条件的情况。304 状态码返回时,不包含任何响应的主体部分。304 虽然被划分在 3XX 类别中,但是和重定向没有关系。
状态码304并不是一种错误,而是告诉客户端有缓存,直接使用缓存中的数据。返回页面的只有头部信息,是没有内容部分的,这样在一定程度上提高了网页的性能。
1.强缓存
强缓存的控制字段:HTTP1.0:Expires;HTTP1.1 :Cache-Control
判断过程:请求再次发起 -> 浏览器根据 expires 和 cache-control 判断目标资源是否命中"强缓存" -> 若命中,直接从缓存获取资源,不再与服务器发生通讯。
Cache-Control取值:

public:所有内容都将被缓存(客户端和代理服务器都可缓存)  
private:所有内容只有客户端可以缓存,Cache-Control的默认取值  
no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定  
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存  
max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效  
must-revalidate: 强制浏览器严格遵守你设置的cache规则  
proxy-revalidate: 强制proxy严格遵守你设置的cache规则

2.协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:

协商缓存生效,返回304   
协商缓存失效,返回200和请求结果。

协商缓存的控制字段:
HTTP1.0:Last-Modified && If-Modified-Since
HTTP1.1 :Etag && If-None-Match

微信截图_20210407161652.png

微信截图_20210407161708.png

2.http和https

1.http
超文本传输协议,是一个基于TCP实现的应用层协议。HTTP由请求和响应构成,是一个标准的客户端服务器模型(B/S)。HTTP协议永远都是客户端发起请求,服务器回送响应。HTTP是一个无状态的协议。无状态是指客户机(Web浏览器)和服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息.HTTP遵循请求(Request)/应答(Response)模型。客户机(浏览器)向服务器发送请求,服务器处理请求并返回适当的应答。所有HTTP连接都被构造成一套请求和应答。
2.https
超文本传输安全协议。HTTPS是在HTTP上建立SSL加密层,并对传输数据进行加密,是HTTP协议的安全版。
主要作用: 1.对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;2.对网站服务器进行真实身份认证
区别:

项目优化

1.使用webpack对项目进行的优化

juejin.cn/post/684490…

CSS

1.清除浮动

  1. 结尾出添加空div,{clear:both;overflow:hidden;height:0}
  2. 伪类同上 zoom:1 解决ie问题
  3. 父级定义高度
  4. 结尾添加 br

2.rem原理

等比缩放

3.有哪些写动画的方式

  1. animation
  2. Lottie 动画
    在Web 端其本质是基于 SVG 或 Canvas,使用 JS 进行开发的动画实现
  3. transition