前端知识点梳理

1、HTML + CSS

选择器

  1. 分组选择器
  2. 派生选择器
  3. id 选择器
  4. 类选择器
  5. 属性选择器

选择器优先级

!important > 行内样式 > id > class > tag > * >父元素继承 从右往左解析css

px,em,rem、vw、vh

px

px 是一个虚拟长度单位,是计算机系统的数字化图像长度单位,若是px要换算成物理长度,须要指定精度DPI

em

em 是一个相对长度单位,相对于其父元素的字体大小html

rem

rem 相对根元素(html)的字体大小前端

vw、vh

vw表示相对于视图窗口的宽度,vh表示相对于视图窗口高度vue

盒模型

页面渲染时,dom采用的渲染模型,能够经过box-sizing设置,
  1. content-box(W3C 标准盒模型):宽度为content
  2. border-box(ie盒模型):宽度为content+padding+border

浮动

浮动的框能够向左或向右移动,直到它的外边缘碰到包含框或另外一个浮动框的边框为止。因为浮动框不在文档的普通流中,因此文档的普通流中的块框表现得就像浮动框不存在同样。java

float:left | rightnode

定位 position

  1. static 元素框正常生成。块级元素生成一个矩形框,做为文档流的一部分,行内元素则会建立一个或多个行框,置于其父元素中。react

  2. relative 元素框偏移某个距离。元素仍保持其未定位前的形状,它本来所占的空间仍保留。webpack

  3. absolute 元素框从文档流彻底删除,并相对于其包含块定位。包含块多是文档中的另外一个元素或者是初始包含块。元素原先在正常文档流中所占的空间会关闭,就好像元素原来不存在同样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。css3

  4. fixed 元素框的表现相似于将 position 设置为 absolute,不过其包含块是视窗自己。es6

文本

color 设置文本颜色

line-height 设置行高

text-align 对齐元素中的文本。

white-space 设置元素中空白的处理方式。

BFC

块级格式化上下文,是一个独立渲染的区域,让处于 BFC 内部元素和外部元素相互不干扰

触发条件

  1. position:absolute | fixed
  2. float: 不为none
  3. display: inline-block / table
  4. overflow: hidden

应用

  1. 阻止margin重叠
  2. 能够包含浮动元素 —— 清除内部浮动(清除浮动的原理是两个div都位于同一个 BFC 区域之中)

居中布局

水平居中

  1. 行内元素:text-align:center
  2. 块级元素:margin:0 auto
  3. absolute+transform
  4. flex justify-content:center

垂直居中

  1. line-height
  2. absolute+transform
  3. flex align-items:center

水平垂直居中

  • absolute+transform
  • flex justify-content:center align-items:center
  • 清除浮动

    1. flex inline-block
    2. :after: clear: both
    3. BFC
    4. float

    预处理器

    ...

    语义化标签

    语义化的HTML就是写出的HTML代码,符合内容的结构化(内容语义化),选择合适的标签(代码语义化),可以便于开发者阅读和写出更优雅的代码的同时让浏览器的爬虫和机器很好地解析。

    常见的标签 nav header footer title main article

    块级标签 div p ul li h1-h6

    行内标签 span button i a img

    flex布局

    Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。

    注意,设为 Flex 布局之后,子元素的float、clear和vertical-align属性将失效。

    采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的全部子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。

    容器的属性

    1. flex-direction
    2. 属性决定主轴的方向(即项目的排列方向) flex-direction: row | row-reverse | column | column-reverse;

    3. flex-wrap
    4. 默认状况下,项目都排在一条线(又称"轴线")上。flex-wrap属性定义,若是一条轴线排不下,如何换行。 flex-wrap: nowrap | wrap | wrap-reverse;

    5. flex-flow
    6. 属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。 justify-content: flex-start | flex-end | center | space-between | space-around;

    7. justify-content
    8. 属性定义了项目在主轴上的对齐方式。 justify-content: flex-start | flex-end | center | space-between | space-around;

    9. align-items
    10. 属性定义项目在交叉轴上如何对齐。align-items: flex-start | flex-end | center | baseline | stretch;

    11. align-content
    12. 属性定义了多根轴线的对齐方式。若是项目只有一根轴线,该属性不起做用。align-content: flex-start | flex-end | center | space-between | space-around | stretch;

    项目的属性

    1. order
    2. 属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。order: ;

    3. flex-grow
    4. 属性定义项目的放大比例,默认为0,即若是存在剩余空间,也不放大。flex-grow: ; /* default 0 */

    5. flex-shrink
    6. 属性定义了项目的缩小比例,默认为1,即若是空间不足,该项目将缩小。flex-shrink: ; /* default 1 */

    7. flex-basis
    8. 属性定义了在分配多余空间以前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的原本大小 flex-basis: length | auto; /* default auto */

    9. flex
    10. 属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。flex: none | 'flex-grow' 'flex-shrink' || 'flex-basis' 该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。 建议优先使用这个属性,而不是单独写三个分离的属性,由于浏览器会推算相关值。

    11. align-self
    12. 属性容许单个项目有与其余项目不同的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,若是没有父元素,则等同于stretch。align-self: auto | flex-start | flex-end | center | baseline | stretch;

    css3

    1. 边框 border-radius border-shadow
    2. 文本 text-shadow word-wrap
    3. 2D transform: translate | rotate | scale | skew | matrix
    4. 3D transform: rotateX | rotateY

    transition过渡

    transition :css属性名称 过渡时间 时间曲线 延迟时间

    animation动画

    @keyframes

    1. animation 全部动画属性的简写属性,除了 animation-play-state 属性
    2. animation-name 规定 @keyframes 动画的名称
    3. animation-duration 规定动画完成一个周期所花费的秒或毫秒。
    4. animation-timing-function 规定动画的速度曲线。默认是 "ease"
    5. animation-delay 规定动画什么时候开始。默认是 0。
    6. animation-iteration-count 规定动画被播放的次数。默认是 1
    7. animation-direction 规定动画是否在下一周期逆向地播放。默认是 "normal"。
    8. animation-play-state 规定动画是否正在运行或暂停。默认是 "running"。
    9. animation-fill-mode 规定对象动画时间以外的状态。

    transition 和 animation 区别

    1. transition 是用在过渡的常见,只能规定动画的初始状态和结束状态
    2. animation 是能使动画阶段性变化,而且动画能中止,能无限循环

    2、Javascript

    this

    this指向谁,要看怎么调用,常见的三种方式

    1. 函数直接调用,this指向全局global,浏览器端也就是window对象
    2. 做为对象方法去调用,this指向调用方法的对象
    3. 构造器new this指向new生成的对象 new方式调用 call,bind,apply没法改变this
    其余的方式如:apply,bind,call this指向传入的对象,如箭头函数this指向定义时的this,与调用方式无关

    手写call

    Function.prototype.myCall = function (context) {
        if (typeof this !== "function") {
          throw new TypeError()
        }
        context = context || window
        context.fn = this
        const query = [...arguments].slice(1)
        const result = context.fn(...query)
        delete context.fn
        return result
      }
      
    复制代码

    手写bind

    Function.prototype.myBind = function (context) {
        if (typeof this !== "function") {
          throw new TypeError('bound is not callable')
        }
    
        var _this = this,
          args = [...arguments].slice(1),
          fNOP = function () {},
          fBound = function () {
            return _this.apply(this instanceof fBound ? this : context, [...args, ...arguments])
          }
    
        if (this.prototype) {
          fNOP.prototype = this.prototype
        }
    
        fBound.prototype = new fNOP()
    
        return fBound
      }
      
    复制代码

    做用域

    es5中做用域分为 全局做用域、局部做用域、eval做用域。局部做用域能向上访问到全局做用域,因此函数内部可以访问到函数外部的变量,反之不能。

    1. 全局做用域:定义在函数最外层的变量
    2. 局部做用域:定义在函数内部的变量
    3. eval做用域:定义在eval的变量
    var a = 10 // 全局做用域
    
    (function(){
        var b = 20 // 局部做用域
    }())
    
    eval("var c = 30") // eval做用域
    
    复制代码

    做用域链

    除了全局做用域, 每个做用域都是存在于某个做用域中的,在试图访问一个变量时JS引擎会从当前做用域开始向上查找直到Global全局做用域中止,变造成一条做用域链

    执行环境(Execution Context)

    执行环境定义了变量和函数有权访问的其余数据,每一个执行环境都有一个与之关联的变量对象(Variable Object)简称 VO,环境中定义的全部变量和函数都存储在这个变量对象中,全局执行环境是最外围的执行环境,在 web 浏览器中全局执行环境被认为 window 对象,函数都有本身的执行环境,当函数执行完成以后,执行环境随之销毁。当代码在环境中执行时,会建立一个变量对象的做用域链,做用域链的用途,是保证对执行环境有权访问的变量和函数的有序访问。做用域链的前端始终是当前代码执行的环境的变量对象,若是是函数,则将其活动对象(activation object)做为变量对象,下一个变量对象则是包含当前环境的外围环境,直到最后一个最外围的 window,标识符的解析是沿着做用域链一级一级的查找直到 window,这也是为何在函数内部能访问到函数外部的变量,而外部访问不到函数内部的缘由

    当程序执行前,会建立全局环境以及全局VO对象,执行过程分为两个阶段:

    1. 变量初始化阶段
    2. 代码执行阶段

    变量初始化阶段

    在VO对象中存储对应的函数参数、函数声明、变量声明

    1. 函数参数(若未传入,则初始化为undefined)
    2. 函数声明(若命名冲突,会覆盖)
    3. 变量声明(若命名冲突,忽略)

    代码执行阶段

    将声明后的变量赋值

    简单的测试以下代码

    alert(x)    // function
    var x = 10
    alert(x)    // 10
    x= 20
    function x(){}
    alert(x)    // 20
    if(true) {
        var a = 1
    }else{
        var b = 1
    }
    
    复制代码
    1. 第一步先看是否有函数参数传入,由于是全局环境,因此不会有函数参数

    2. 第二步看有没有函数声明,有则在 VO 对象中存储,值为 function

    VO = {
            x:function
        }
        
    复制代码
    1. 第三步看有没有变量声明,有就在VO对象中存储,值为 undefined
    VO = {
            x:function,
            a:undefined,
            b:undefined
        }
        
    复制代码

    由于变量声明发生命名冲突会忽略,因此 x 仍是 function ,而后到了代码执行阶段,第一个 alert 能在 VO 中找到 x,因此是 function,然后 x=10 ,VO 中 x 的赋值为10,因此第二个 alert 为10,然后函数声明忽略,if 为 true,VO 中的 a 赋值为1,b 执行不到仍是 undefined

    闭包

    闭包是指有权访问另外一个函数做用域中的变量的函数,建立闭包的常见方式是,在一个函数内部建立另外一个函数。利用闭包技巧便可以访问到函数内部的变量,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。

    function f1(){
        var n=999;
        function f2(){
            alert(n); // 999
        }
        return f2
    }
    
    var func = f1()
    func()
    
    复制代码

    以上代码,f2 定义在 f1 内部,因此 f2 的做用域链包含 f1 的活动对象,当 f1 调用完成后,f1 的执行环境的做用域链被销毁,可是 f2 仍然能访问到 f1 的活动对象,因此 f1 的活动对象还存在内存中,只有 f2 销毁,活动对象才销毁,咱们能够解除 func 的引用,即 func = null,通知垃圾回收机制回收,f2 的做用域链被销毁,f1 的活动对象也随之销毁

    使用闭包的注意点

    因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,因此不能滥用闭包,不然会形成网页的性能问题,在IE中可能致使内存泄露。解决方法是,在退出函数以前,将不使用的局部变量所有删除。

    原型

    原型就是一个简单的对象,对象中包含一些方法,用于对象属性的继承,每一个对象都有个__proto__属性指向该对象的原型,以及constructor属性指向构造函数

    原型链

    javascrip函数都有一个prototype属性指向原型对象,原型对象也有一个__proto__属性,这个属性指向本身的原型,直到Object的原型(null),这样就造成了原型链,原型链主要用于实现继承和共享属性,避免资源浪费

    手写new

    function myNew(fn, ...args) {
        let obj = {}
        Object.setPrototypeOf(obj, fn.prototype);
        let result = fn.apply(obj, args)
        return result instanceof Object ? result : obj
      }
    
       function test(val) {
         this.name = val
       }
    
       test.prototype.sayName = function () {
         console.log(this.name)
       }
    
       var a = myNew(test, "ghc")
       console.log(a)
       a.sayName()
       
    复制代码

    手写instanceof

    function myInstanceof(left, right) {
        let prototype = right.prototype
        left = Object.getPrototypeOf(left)
        while (true) {
          if (left === null || typeof left !== "object") {
            return false
          }
          if (left === prototype) {
            return true
          }
          left = Object.getPrototypeOf(left)
        }
      }
      
    复制代码

    继承

    组合继承

    function superType() {
        this.name = "ghc"
    }
    
    superType.prototype.sayName = function() {
        alert(this.name)
    }
    
    function supType(name) {
        superType.call(this,name)
    }
    
    supType.prototype = new superType()
    supType.constructor = supType
    
    var demo = new supType("ghc")
    
    
    复制代码

    组合继承是最经常使用的继承方式,但也有他的不足,不管什么状况下,组合继承都调用了两次超类型构造函数,第一次调用 superType 函数时,supType 的原型上指定了 name 属性,第二次在子类型构造函数调用,在实例上指定了 name 属性把原型上的属性覆盖了。咱们能够用寄生组合继承来解决这个问题

    寄生组合继承

    寄生组合继承就是借用构造函数来继承属性,用原型链的混成形式来继承方法

    function Parent(name) {
        this.name = name
     }
    
    function Child(name) {
        Parent.call(this, name)
    }
    
    Child.prototype = Object.create(Parent.prototype, {
        constructor: {
          value: Child,
          enumerable: false,
          writable: true,
          configurable: true
        }
    })
    
    var a = new Child("GHC")
    console.log(a)
    
    复制代码

    es6部分

    解构赋值

    ES6 容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构

    数组
    let [a, b, c] = [1, 2, 3];
    
    对象
    let { foo, bar } = { foo: "aaa", bar: "bbb" };
    
    字符串
    const [a, b, c, d, e] = 'hello';
    
    数字
    let {toString: s} = 123;
    
    布尔
    let {toString: s} = true;
    
    复制代码

    扩展运算符

    数组扩展运算符(spread)是三个点(...)。它比如 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列
    
    console.log(...[1, 2, 3])
    // 1 2 3
    
    console.log(1, ...[2, 3, 4], 5)
    // 1 2 3 4 5
    
    function add(x, y) {
      return x + y;
    }
    
    const numbers = [4, 38];
    add(...numbers) // 42
    
    对象的扩展运算符(...)用于取出参数对象的全部可遍历属性,拷贝到当前对象之中。
    
    let z = { a: 3, b: 4 };
    let n = { ...z };
    n  // { a: 3, b: 4 }
    
    // 等同于 {...Object(true)}
    {...true} // {}
    
    // 等同于 {...Object(undefined)}
    {...undefined} // {}
    
    // 等同于 {...Object(null)}
    {...null} // {}
    
    复制代码

    扩展运算符与解构赋值结合

    只能放在参数的最后一位,不然会报错

    数组扩展运算符能够与解构赋值结合起来,用于生成数组。
    
    const [first, ...rest] = [1, 2, 3, 4, 5];
    
    const [first, ...middle, last] = [1, 2, 3, 4, 5]; //err
    
    对象
    
    对象的解构赋值用于从一个对象取值,至关于将目标对象自身的全部可遍历的、但还没有被读取的属性,
    分配到指定的对象上面。全部的键和它们的值,都会拷贝到新对象上面。
    
    let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
    x // 1
    y // 2
    z // { a: 3, b: 4 }
    
    复制代码

    Promise

    基本用法

    const promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操做成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    // 不推荐写法
    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    });
    
    // 推荐写法
    promise.then(function(value) {
      // success
    }).catch(function(error){
        console.log('发生错误!', error);
    });
    
    复制代码

    高级用法

    1、 Promise.all

    Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。须要传入一个数组做为参数

    const p = Promise.all([p1, p2, p3]);
    
    复制代码

    Promise.all 的两种状态:

    1. 只有当p一、p二、p3状态都变成fulfilled,p的状态才会变成fulfilled,此时p一、p二、p3的返回值组成一个数组,传递给p的回调函数

    2. 只要p一、p二、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数

    2、Promise.race

    Promise.race 用法与 Promise.all 相似,只要p一、p二、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

    手写Promise

    const PENDING = 'pending'
      const RESOLVED = 'resolved'
      const REJECTED = 'rejected'
    
      function myPromise(fn) {
        var that = this
        that.state = PENDING
        that.value = null
        that.resolveCallballs = []
        that.rejectCallballs = []
    
        function resolve(value) {
          if (that.state === PENDING) {
            that.state = RESOLVED
            that.value = value
            that.resolveCallballs.map(v => v(that.value))
          }
        }
    
        function reject(value) {
          if (that.state === PENDING) {
            that.state = REJECTED
            that.value = value
            that.rejectCallballs.map(v => v(that.value))
          }
        }
    
        try {
          fn(resolve, reject)
        } catch (e) {
          reject(e)
        }
      }
    
      myPromise.prototype.then = function (onFulfilled, onRejected) {
        const that = this
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v
        onRejected = typeof onRejected === "function" ? onRejected : r => {
          throw r
        }
    
        if (that.state === PENDING) {
          that.resolveCallballs.push(onFulfilled)
          that.rejectCallballs.push(onRejected)
        }
    
        if (that.state === RESOLVED) {
          onFulfilled(that.value)
        }
    
        if (that.state === REJECTED) {
          onRejected(that.value)
        }
      }
    
       new myPromise((resolve, reject) => {
         setTimeout(() => {
           resolve(1)
         }, 1000)
       }).then(value => {
         console.log(value)
       })
       
    复制代码

    Generator

    Generator有两个特征,一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,调用Generator函数,函数并不会执行,而是返回一个指向内部状态的指针对象,当调用next方法时才会执行函数,遇到yield函数会暂停,Generator异步编程用的比较少通常配合co模块或thunk函数,用的比较多的会是async

    async

    async 是es7异步编程的解决方案 ,是Generator函数的语法糖,默认返回Promise,函数内部若是遇到await会阻塞下面的代码,跳出函数继续执行宏任务中的代码

    class

    class能够看做是构造函数的语法糖,constructor方法就是构造方法,this指向实例对象

    class A {
        constructor() {
          this.name = "ghc"
        }
    }
    class B extends A {
        constructor() {
          super()
        }
    }
    
    var b = new B()
    console.log(b)
      
    复制代码

    Symbol

    ES5 的对象属性名都是字符串,这容易形成属性名的冲突,Symbol能够保证每一个属性的名字都是独一无二的

    let mySymbol = Symbol();
    let a = {};
    a[mySymbol] = 'Hello!';
    
    let s = Symbol();
    let obj = {
      [s]() { ... }
    };
    
    复制代码

    Set

    ES6 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。

    const s = new Set();
    
    [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
    
    for (let i of s) {
      console.log(i);
    }
    // 2 3 5 4
    
    复制代码

    Set 实例的属性和方法

    属性:

    1. Set.prototype.constructor:构造函数,默认就是Set函数。
    2. Set.prototype.size:返回Set实例的成员总数。

    Set 实例的方法分为两大类:操做方法(用于操做数据)和遍历方法(用于遍历成员)

    操做方法:

    1. add(value):添加某个值,返回 Set 结构自己。
    2. delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
    3. has(value):返回一个布尔值,表示该值是否为Set的成员。
    4. clear():清除全部成员,没有返回值。

    遍历方法:

    1. keys():返回键名的遍历器
    2. values():返回键值的遍历器
    3. entries():返回键值对的遍历器
    4. forEach():使用回调函数遍历每一个成员

    Map

    JavaScript 的对象(Object),本质上是键值对的集合(Hash结构),可是传统上只能用字符串看成键。这给它的使用带来了很大的限制。ES6 提供了 Map 数据结构。它相似于对象,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键

    const m = new Map();
    const o = {p: 'Hello World'};
    
    m.set(o, 'content')
    m.get(o) // "content"
    
    m.has(o) // true
    m.delete(o) // true
    m.has(o) // false
    
    复制代码

    模块化

    模块化的好处是防止变量冲突、提升代码复用性、提升代码可维护性

    一、当即执行函数

    当即执行函数是模块化经常使用的手段

    (function(globalVariable){
       globalVariable.test = function() {}
       // ... 声明各类变量、函数都不会污染全局做用域
    })(globalVariable)
    
    复制代码

    二、AMD 和 CMD

    使用较少

    三、commonJS

    最先Node中使用,webpack中也常常看到

    // a.js
    module.exports = {
        a: 1
    }
    // or 
    exports.a = 1
    
    // b.js
    var module = require('./a.js')
    module.a // -> log 1
    
    复制代码

    四、ES Module

    ES Module 是原生实现的模块化方案

    // 引入模块 API
    import XXX from './a.js'
    import { XXX } from './a.js'
    // 导出模块 API
    export function a() {}
    export default function() {}
    
    复制代码

    更多查看阮一峰老师的 ECMAScript 6 入门 es6.ruanyifeng.com/#README

    DOM 相关 API

    节点查找API

    document.getElementById()

    document.getElementsByClassName()

    document.getElementsByTagName()

    document.getElementsByName()

    document.querySelector()

    节点建立API

    document.createElement()

    document.createTextNode()

    node.cloneNode()

    节点修改API

    parent.appendChild(child)

    parent.insertBefore(newNode,refNode)

    parent.removeChild(node) 通常用法 node.parentNode.removeChild(node)

    parent.replaceChild(newNode,oldNode)

    节点关系API

    父关系API

    parentNode 返回父节点,父节点多是 element、document、DocumentFragment

    parentElement 返回父节点,与 parentNode 区别是父元素只能是 element,不然返回 null

    子关系API

    children 返回一个实时的 HTMLCollection ,子节点都是Element,IE9如下浏览器不支持

    childNodes 返回一个实时的 NodeList ,表示元素的子节点列表,注意子节点可能包含文本节点、注释节点等

    firstChild 返回第一个子节点,不存在返回null,与之相对应的还有一个 firstElementChild

    lastChild 返回最后一个子节点,不存在返回null,与之相对应的还有一个 lastElementChild

    兄弟关系型API

    previousSibling 节点的前一个节点,若是不存在则返回null。注意有可能拿到的节点是文本节点或注释节点

    nextSibling :节点的后一个节点,若是不存在则返回null。注意有可能拿到的节点是文本节点

    previousElementSibling :返回前一个元素节点,前一个节点必须是Element,注意IE9如下浏览器不支持。

    nextElementSibling :返回后一个元素节点,后一个节点必须是Element,注意IE9如下浏览器不支持。

    属性型API

    setAttribute(name,value)

    name是特性名,value是特性值。若是元素不包含该特性,则会建立该特性并赋值

    getAttribute(name)

    getAttribute返回指定的特性名相应的特性值,若是不存在,则返回null

    样式相关API

    node.style.xxx 只能获取到内联样式

    window.getComputedStyle(node,pseudo-element) 第二个参数是伪类,能够获取应用到元素上的全部样式,IE8或更低版本不支持此方法

    node.getBoundingClientRect() 用来返回元素的大小以及相对于浏览器可视窗口的位置

    3、浏览器基础

    http

    http请求

    请求分为两种 get 和 post

    1. get:参数放于 url 传递,有必定大小限制,但请求速度较快,能够缓存
    2. post:传参无大小限制,较为安全,不能缓存

    http状态码

    1. 200开头,请求成功
    2. 300开头,重定向
    3. 400开头,客户端错误,好比404找不到资源
    4. 500开头,服务器问题

    事件的触发三个阶段

    1. window向触发处传播,遇到注册的捕获事件会触发
    2. 触发处触发事件
    3. 触发处想window传播,遇到注册冒泡的事件会触发

    捕获由外向内,冒泡由内向外 event.stopPropagation()阻止事件的冒泡

    跨域

    协议、域名、端口有一个不一样的就是跨域,ajax请求就会失败

    JSONP

    兼容性不错,但只限get请求

    手写jsonp

    function myjsonp(url, data) {
        return new Promise((resolve, reject) => {
          let dataString = url.indexOf('?') === -1 ? '?' : '&'
          if (data instanceof Object) {
            for (let key in data) {
              dataString += key + '=' + data[key] + '&'
            }
          }
          const callBackName = Math.random().toString().replace('.', '')
          dataString += "callback" + callBackName
          let el = document.createElement("script")
          el.src = url + dataString
          el.async = true
    
          window[callBackName] = function (v) {
            resolve(v)
            document.body.removeChild(el)
          }
          document.body.appendChild(el)
        })
      }
    复制代码

    CORS

    主要由后端实现

    储存

    cookie

    通常由服务器生成,能够设置过时时间 大小4K

    sessionStorage

    页面关闭就清理 通常大小5m

    LocalStorage

    除非被清理,不然一直存在 通常大小5m

    indexDB

    除非被清理,不然一直存在 大小无限

    Service Worker

    Service Worker 是运行在浏览器背后的独立线程,通常能够用来实现缓存功能 (去了解PWA)

    浏览器渲染机制

    将html转换为dom树 css转换为cssom 而后合成为render树 浏览器根据render树进行布局(回流)

    重绘(Repaint)和回流(Reflow)

    1. 重绘是当节点须要更改外观而不会影响布局的,好比改变 color 就叫称为重绘
    2. 回流是布局或者几何属性须要改变就称为回流。

    Event Loop

    事件循环是指: 执行一个宏任务,而后执行清空微任务列表,循环再执行宏任务,再清微任务列表

    1. 微任务 microtask(jobs): promise / ajax / Object.observe(该方法已废弃)
    2. 宏任务 macrotask(task): setTimout / script / IO / UI Rendering

    V8垃圾回收机制

    垃圾回收: 将内存中再也不使用的数据进行清理,释放出内存空间。V8 将内存分红 新生代空间 和 老生代空间。

    新生代空间: 用于存活较短的对象

    老生代空间: 用于存活时间较长的对象

    内存泄露

    1. 意外的全局变量: 没法被回收
    2. 定时器: 未被正确关闭,致使所引用的外部变量没法被释放
    3. 事件监听: 没有正确销毁 (低版本浏览器可能出现)
    4. 闭包: 会致使父级中的变量没法被释放
    5. dom 引用: dom 元素被删除时,内存中的引用未被正确清空
    可用 chrome 中的 timeline 进行内存标记,可视化查看内存的变化状况,找出异常点。

    4、性能优化

    性能优化能够经过5个方面入手,首先会同个 DNS 解析域名转换相应的 IP 地址,而后根据 IP 地址找到服务器进行 TCP 连接,这两个方面前端能作的很少,而后是客户端发送 HTTP 请求和服务端响应的过程,这点前端能作的是整合资源包的大小,而后浏览器拿到数据,进行渲染。总的来讲,前端能够经过整合资源包的大小和优化渲染过程两个方面入手。

    整合资源包的大小可使用按需加载,好比 Vue 路由可使用路由懒加载防止打包出来的资源包过大,同时也能够对图片进行压缩优化,JPG 压缩后图片清晰度不会变化太大适合一些大图,轮播图;PNG 体积较大,线条细腻,适合小图 logo,项目中尽可能使用字体图标去替代图片,或者用 base64 / 缓存图片也能够减小网络请求。

    而后前端还能够经过优化渲染过程方面入手,首先 HTML 生成 dom树,css 生成样式规则,二者结合生成 render 树,而后调用 GPU 去渲染页面。css 方面的优化要避免选择器的嵌套,由于选择器规则是从右到左去匹配的,嵌套就会增长开销。避免使用通配符,它会匹配全部元素。

    另外要防止 css 阻塞,只有样式规则完成,页面才能去渲染,这也是为何css要放在 head 头里面。防止 js 阻塞,在渲染的过程若是遇到 js 脚本,控制权会从渲染引擎到 js 引擎阻塞页面的渲染。因此脚本要写在最后,script 标签能够用 defer 和 async 异步加载。另外还要减小 dom 的访问和操做,这是 js 引擎和渲染引擎之间的通讯,很是消耗性能

    图片处理

    图片可分为位图和矢量图 常见的位图有JPG、PNG、GIF,矢量图有SVG,项目中尽可能使用字体图标或SVG替代图片,使用Base64也能够减小网络请求

    JPG

    大的背景图,轮播图

    png

    体积大 用在小logo 透明背景

    GIF

    动态图片

    雪碧图

    小图太多的时候,集中成一张图片减小 HTTP 请求,随着字体图片、SVG图片的流行,该技术渐渐退出了历史舞台

    SVG

    能适应不一样设备且画质不能损坏的图片

    使用缓存

    使用cach-control或expires这类强缓存时,缓存不过时的状况下,不向服务器发送请求。强缓存过时时,会使用last-modified或etag这类协商缓存,向服务器发送请求,若是资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源;若是资源更新了,则服务器将更新后的资源发送到浏览器,并返回200响应

    减小重绘回流

    一、避免使用层级较深的选择器,或其余一些复杂的选择器,以提升CSS渲染效率

    二、避免使用CSS表达式,CSS表达式是动态设置CSS属性的强大但危险方法,它的问题就在于计算频率很快。不只仅是在页面显示和缩放时,就是在页面滚动、乃至移动鼠标时都会要从新计算一次

    三、元素适当地定义高度或最小高度,不然元素的动态内容载入时,会出现页面元素的晃动或位置,形成回流

    四、给图片设置尺寸。若是图片不设置尺寸,首次载入时,占据空间会从0到彻底出现,上下左右均可能位移,发生回流

    五、不要使用table布局,由于一个小改动可能会形成整个table从新布局。并且table渲染一般要3倍于同等元素时间

    六、对于一些进行动画的元素,使用硬件渲染,从而避免重绘和回流

    使用节流和防抖

    使用函数节流(throttle)或函数去抖(debounce),限制某一个方法的频繁触发

    节流( Throttle )

    function throttle(fn, interval) {
      // last为上一次触发回调的时间
      let last = 0
      
      // 将throttle处理结果看成函数返回
      return function () {
          let context = this
          let args = arguments
          let now = +new Date()
          
          if (now - last >= interval) {
              last = now;
              fn.apply(context, args);
          }
        }
    }
    
    // 用throttle来包装scroll的回调
    const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)
    
    document.addEventListener('scroll', better_scroll)
    复制代码

    防抖( debounce )

    // fn是咱们须要包装的事件回调, delay是每次推迟执行的等待时间
    function debounce(fn, delay) {
      // 定时器
      let timer = null
      
      // 将debounce处理结果看成函数返回
      return function () {
        // 保留调用时的this上下文
        let context = this
        // 保留调用时传入的参数
        let args = arguments
    
        // 每次事件被触发时,都去清除以前的旧定时器
        if(timer) {
            clearTimeout(timer)
        }
        // 设立新定时器
        timer = setTimeout(function () {
          fn.apply(context, args)
        }, delay)
      }
    }
    
    // 用debounce来包装scroll的回调
    const better_scroll = debounce(() => console.log('触发了滚动事件'), 1000)
    
    document.addEventListener('scroll', better_scroll)
    复制代码

    用 Throttle 来优化 Debounce

    // fn是咱们须要包装的事件回调, delay是时间间隔的阈值
    function throttle(fn, delay) {
      // last为上一次触发回调的时间, timer是定时器
      let last = 0, timer = null
      // 将throttle处理结果看成函数返回
      
      return function () { 
        // 保留调用时的this上下文
        let context = this
        // 保留调用时传入的参数
        let args = arguments
        // 记录本次触发回调的时间
        let now = +new Date()
        
        // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
        if (now - last < delay) {
        // 若是时间间隔小于咱们设定的时间间隔阈值,则为本次触发操做设立一个新的定时器
           clearTimeout(timer)
           timer = setTimeout(function () {
              last = now
              fn.apply(context, args)
            }, delay)
        } else {
            // 若是时间间隔超出了咱们设定的时间间隔阈值,那就不等了,不管如何要反馈给用户一次响应
            last = now
            fn.apply(context, args)
        }
      }
    }
    
    // 用新的throttle包装scroll的回调
    const better_scroll = throttle(() => console.log('触发了滚动事件'), 1000)
    
    document.addEventListener('scroll', better_scroll)
    
    
    复制代码

    图片懒加载

    <script>
        // 获取全部的图片标签
        const imgs = document.getElementsByTagName('img')
        // 获取可视区域的高度
        const viewHeight = window.innerHeight || document.documentElement.clientHeight
        // num用于统计当前显示到了哪一张图片,避免每次都从第一张图片开始检查是否露出
        let num = 0
        function lazyload(){
            for(let i=num; i<imgs.length; i++) {
                // 用可视区域高度减去元素顶部距离可视区域顶部的高度
                let distance = viewHeight - imgs[i].getBoundingClientRect().top
                // 若是可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明元素露出
                if(distance >= 0 ){
                    // 给元素写入真实的src,展现图片
                    imgs[i].src = imgs[i].getAttribute('data-src')
                    // 前i张图片已经加载完毕,下次从第i+1张开始检查是否露出
                    num = i + 1
                }
            }
        }
        // 监听Scroll事件
        window.addEventListener('scroll', lazyload, false);
    </script>
    
    复制代码

    webpack优化

    一、按需加载,使用路由懒加载提升首次打开的速度

    5、框架(Vue)

    生命周期钩子函数

    1. beforeCreate (在组件建立以前执行,不能获取到data、props)
    2. created (在组件建立以后执行)
    3. beforeMount (在组件挂载以前执行,不能访问到dom节点)
    4. mounted (在组件挂载完成执行,能访问到dom节点)
    5. beforeUpdete (数据更新以前执行)
    6. updated (数据更新以后执行)
    7. beforeDestroy (组件销毁以前执行,可用于清除定时器等)
    8. destroyed (组件销毁以后之心)
    9. activated (keep-alive钩子进入组件以后触发)
    10. deactivated (keep-alive钩子离开组件以后触发)

    父子组件通讯

    父子组件通讯是单向数据流的,父组件经过props转递数据给子组件,子组件不能修改props,只能经过emit触发父组件的方法通知父组件修改数据

    兄弟组件通讯

    对于这种状况能够经过查找父组件中的子组件实现,也就是 this.parent.children,在 $children 中能够经过组件 name 查询到须要的组件实例,而后进行通讯。

    跨多层次组件通讯

    provide / inject,官方文档中不推荐直接使用在业务中,可使用 vuex 作状态管理

    mixin 和 mixins

    mixin 全局混入,官方不推荐使用,会影响到每一个组件实例,一般插件都是这样作初始化的。

    mixins 封装多个组件相同的逻辑

    computed 和 watch 区别

    computed 是计算属性,依赖其余属性计算值,而且computed的值有缓存,访问其值时不会每次计算,只有当计算值变化才会返回内容。

    watch 监听到值的变化就会执行回调,在回调中能够进行一些逻辑操做。

    keep-alive

    防止组件屡次渲染,keep-alive有两个独有的生命周期 activated、deactivated

    v-show和v-if区别

    v-show 通常用于切换较为平凡的场景,切换开销较小

    v-if 通常用于切换较少的场景,切换开销较大

    响应式原理

    响应式原理:首先会遍历 option 里的 data ,添加读取器属性到实例里来作一层代理,这也是为何可以经过 this 访问到 data 里的数据。而后会调用 observe 设置监听器,observe 主要是遍历 data 中的属性,将其修改成读取器属性,而且生成各自的订阅器。在 get 中进行依赖收集,把订阅者 watcher 添加到订阅器中,并返回属性的值。在 set 中修改数据的值,而且调用订阅者的 update 方法修改文本,这样监听器就设置完成。接着会遍历 dom 节点,将节点中的数据替换,并生成订阅者,触发监听器进行依赖收集。这样当数据发生改变,set 方法就会触发订阅器,订阅器就会通知全部的订阅者更新视图。

    class Vue {
        constructor(options) {
          this._data = options.data;
          observer(this._data);
          /* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */
          new Watcher();
          /* 在这里模拟render的过程,为了触发test属性的get函数 */
          console.log('render~', this._data.test);
        }
      }
    
      class Dep {
        constructor() {
          /* 用来存放Watcher对象的数组 */
          this.subs = [];
        }
    
        /* 在subs中添加一个Watcher对象 */
        addSub(sub) {
          this.subs.push(sub);
        }
    
        /* 通知全部Watcher对象更新视图 */
        notify() {
          this.subs.forEach((sub) => {
            sub.update();
          })
        }
      }
    
      class Watcher {
        constructor() {
          /* 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到 */
          Dep.target = this;
        }
    
        /* 更新视图的方法 */
        update() {
          console.log("视图更新啦~");
        }
      }
    
      function observer(value) {
        if (!value || (typeof value !== 'object')) {
          return;
        }
    
        Object.keys(value).forEach((key) => {
          defineReactive(value, key, value[key]);
        });
      }
    
      function defineReactive(obj, key, val) {
        /* 一个Dep类对象 */
        const dep = new Dep();
    
        Object.defineProperty(obj, key, {
          enumerable: true,
          configurable: true,
          get: function reactiveGetter() {
            /* 将Dep.target(即当前的Watcher对象存入dep的subs中) */
            dep.addSub(Dep.target);
            return val;
          },
          set: function reactiveSetter(newVal) {
            if (newVal === val) return;
            /* 在set的时候触发dep的notify来通知全部的Watcher对象更新视图 */
            dep.notify();
          }
        });
      }
      
      let o = new Vue({
         data: {
           test: "I am test."
         }
      });
      o._data.test = "hello,world.";
      
    复制代码

    vue-router

    跳转方法:

    this.$router.push()

    // 字符串

    router.push('home')

    // 对象

    router.push({ path: 'home' })

    // 命名的路由

    router.push({ name: 'user', params: { userId: '123' }})

    // 带查询参数,变成 /register?plan=private

    router.push({ path: 'register', query: { plan: 'private' }})

    this.$router.replace()

    replace跳转不会保留历史痕迹,其余用法和 push 一致

    vuex

    state:存放变量

    mutation:修改 state 里面的变量

    getter:获取 state 里的值

    action:异步修改 state 的值,也可用来封装多个 mutation

    相关文章
    相关标签/搜索