2021面试笔记

基础相关

html

-

css

盒模型

  • marginborderpaddingcontent
  • box-sizing: content-box(默认)border-box
  • inline: <a>、<span>、<br>、<i>、<em>、<strong>、<label>、<q>、<var>、<cite>、<code>
  • block: <div>、<p>、<h1>-<h6>、<ol>、<ul>、<dl>、<table>、<address>、<blockquote> 、<form>
  • inline-block: imageinput

居中

  • 水平:

    • 内联:text-align: center

    • 块元素:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // transform
      .son {
      position: absolute;
      left: 50%;
      margin-left: - 1/2 width; // 或者
      transform: translateY(-50%);
      }

      // flex
      .father {
      display: flex;
      justify-content: center;
      }

      // 常规
      .son {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      margin: auto
      }
  • 垂直

    • 内联:line-height: height

    • 块元素:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // transform
      .son {
      position: absolute;
      top: 50%;
      margin-top: - 1/2 height; // 或者
      transform: translateY(-50%)
      }

      // flex
      .father {
      display: flex;
      align-item: center;
      }

      // 常规,同上
      .son {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      margin: auto
      }
  • 完全:

flex

  • flex-direction: row, row-reserve, column, column-reserve
  • flex-wrap: wrap, no-wrap, wrap-reserve
  • flex-flow: flex-direction + flex-wrap 组合
  • justify-content: flex-start, flex-end, center, space-between, space-around
  • align-content: stretch, flex-start, flex-end, center, space-between, space-around
  • align-items: stretch, flex-start, flex-end, center, baseline
  • align-self: auto, stretch, flex-start, flex-end, center, baseline

grid

  • display: grid | inline-grid
  • grid-template-column, grid-template-row, grid-auto-column, grid-auto-row
    • repeat, auto-fill / auto-fit, fr, minmax(), auto, 网格线的名字
    • auto-fill: 完全填充时一样。不完全填充时,保留 原宽度不变
    • auto-fit: 完全填充时一样。不完全填充时,会 拉伸原宽度至填充满
  • grid-template-area: 区域,后面定位用
  • grid-row-gap, grid-column-gap, grid-gap(row | column)
  • grid-auto-flow: row, row dense, column, column dense
  • 位置
    • 容器,整体位置:
      • justify-content, align-content, place-content(align | justify)
      • start, end, center, stretch, space-around, space-between, space-evenly(等间距)
    • 容器,每个项目内元素位置
      • justify-items, align-items, place-items, place-items(align | justify)
      • start, end, center, stretch
    • 项目,每个项目内元素位置
      • justify-self, align-self, place-self(align | justify)
      • start, end, center, stretch
  • 合并

    • grid-column-start, grid-column-end, grid-row-start, grid-row-end
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      .item-1 {
      grid-column-start: 1;
      grid-column-end: 2;
      }

      // 等效于
      .item-2 {
      grid-column-start: span 2;
      }
      // 或者
      .item-3 {
      grid-column-end: span 2;
      }
    • grid-column: grid-column-start / grid-column-end

    • grid-row: grid-row-start / grid-row-end

      • 对于 xx-end 也可用 span 表示跨越
      • / 后可省略,默认跨越1格
      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        .item-1 {
        grid-column: 1 / 3;
        // grid-column: 1 e("/") 3;
        // calc(~'50% - @{bg} - 10px')
        }

        // 等效于
        .item-1 {
        grid-column-start: 1;
        grid-column-end: 3;
        }
        // 或者
        .item-1 {
        grid-column: 1 / span 2;
        }
    • grid-area: 区域定位

      • 1
        2
        3
        4
        5
        6
        7
        8
        .item-1 {
        grid-area: e;
        }

        // 等效于
        .item-2 {
        grid-area: 2 / 2 / 3 / 3
        }
      • 使用 grid-template-area 的定位

      • 或者等效于 grid-area: grid-row-start / grid-column-start / grid-row-end / grid-column-end

伪元素

  • 链接相关:link, visited, hover, active
  • 常用:
    • first-child
    • nth-child, nth-of-type, nth-col(odd: 奇数, even: 偶数)
    • focus, not, has, root
  • before, after: 创建一个已选中元素的第x个元素

穿透

  • 穿透父级/高优先级样式
  • /deep/
  • >>>

优先级

  • 7大优先级,
  • 内联style、id选择器、属性选择器、类选择器、伪类选择器、元素选择器、通用选择器(*)
  • 权重计算:
    • 1000、100、10*3、1*2
    • 不能跨层级

background

  • color:
  • image: url(‘xxx’)
  • position: top, right bottom left center
  • repeat: repeat, repeat-x, repeat-y, no-repeat
  • attachment:
    • local: 相对 元素内容 固定,会在子窗口中随滚动条滚动
    • scroll: 相对 元素 固定,不会在子窗口中随滚动条滚动
  • size: 固定值,百分比,cover, contain
  • 合并写法:url position / size repeat attachment color
  • 高级用法: linear-gradient: 渐变

position

  • static, fixed, relative, absolute,
  • sticky: 粘性定位,相对定位和固定定位的混合。在跨越特定阈值前为相对定位,之后为固定定位。需指定 toprightbottomleft 4个值其中1个。

js

基础类型

  • 值类型:StringNumberNullUndefinedBoolean
    • 保存在
  • 引用类型:ObjectArrayDateFunction
    • 变量名保存在 中,变量值保存在

原型/原型链

  • 原型详解

  • 箭头函数不能用来继承,因为没有 prototype 属性

  • 原型

    • 原型,又叫原型对象,指构造函数的 prototype, 比如 Father.prototype
    • 原型的作用就是共享方法,Father.prototype.method 上的方法,可以被共享
    • 原型中的 this 指向实例
  • 原型链

    • 原型与原型之间相链接的过程即为 原型链

    • 实例的 __proto__ 指向的是构造函数的 protoType 原型对象: console(obj.__proto__ === Star.prototype) // true

    • 原型查找方式

      1. 查找obj实例上是否有 dance 方式: this.dance = function(){}
      2. 查找Star构造函数 原型对象 prototype 上是否有 dance 方法:Star.prototype.dance = xxx
      3. 查找Object原型对象 prototype 上是否有 dance 方法:Object.prototype.dance = xxx
      4. 还没找到,就报错
    • 原型构造器

      • 原型的构造器指向构造函数

        • 1
          2
          console.log(Star.prototype.constructor === Star) // true
          console.log(obj.__proto__.constructor === Star) // true
      • Star.prototype = {} 会丢失构造器,所以一般用 Star.prototype.xxx = function() {}

  • 继承

    • call 只可以继承 属性,要继承 函数 的话,需要使用 原型链继承

    • 方法一:利用 Son.prototype = Father.prototype 改变原型指向,但子类增加原型方法,也会影响到父类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      function Father(name) {
      this.name = name
      }
      Father.prototype.dance = function () {..}
      function Son(name, age) {
      Father.call(this, name)
      this.age = age
      }
      Son.prototype = Father.prototype
      // 为子类添加方法
      Son.prototype.sing = function () {...}
      let son = new Son('xiaohong', 18)
      // 此时父类也被影响了
      console.log(Father.prototype) // {dance: f, sing: f, constructor: f}
      console.log(Father) // f Father(name) {}
    • 方法二:子类的原型指向父类的实例 Son.prototype = new Father(), 这样就可以顺着原型链继承父类的方法了。并且子类添加原型方法的时候,不会影响父类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      function Father(name) {
      this.name = name
      }
      Father.prototype.dance = function () {...}
      function Son(name, age) {
      Father.call(this, name)
      this.age = age
      }
      Son.prototype = new Father()
      Son.prototype.sing = function() {...}
      let son = new Son('xiaoming', 20)
      // 此时父类不受影响
      console.log(Father.prototype) // {dance: f, constructor: f}
      console.log(Son.prototype) // Father实例 {name: undefined, sing: f, __proto__: { dance: f, constructor: f }}
    • 继承的写法

      • ES5写法:上面的原型链写法

      • ES6写法: 类写法

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        class Father {
        constructor(name) {
        this.name = name
        }
        dance() {...}
        }
        class Son extends Father {
        constructor(name, age) {
        super(name)
        this.age = age
        }
        sing() {...}
        }
        let obj = new Son('xiaohong', 18)
        console.log(Father.prototype) // {dance: f, constructor: f}

常用函数

  • String: slice, toUpperCase, toLowerCase, indexOf, startsWith, endsWith,padStart, padEnd
  • Array: find, filter, map, includes, flat, reduce, slice, splice, concat
    • reduce: (reducer, initialValue)
    • reducer: (accumulator, currentValue, index, array)
  • 非变异方法(不改变现有数组): filter, slice, concat
  • Object: keys, hasOwnproperty, values
  • Map, Set: has, set, get, delete, entites

正则表达式

  • ^$

  • *+?

  • {n, m}

  • [^xyz]

  • 非贪婪匹配(?): (.*)(&arr=(.))

  • 举例: 匹配url字符串

    • 方法一:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      function getValue (key) {
      // 规则 ?|&key=value|&|$, 也就是 /(\?|&)${key}=([^&]*)(&|$)/
      // (\?|&): 以?或& 开头
      // ([^&]*): 匹配非&字符,贪婪匹配
      // (&|$): 以&或最后字符结尾
      let url = decodeURI(window.location.href)
      let reg = new RegExp(`(\\?|&)${key}=([^&]*)(&|$)`, 'i')
      let res = url.match(reg)
      return (res && res.length > 2) ? res[2] : ''
      }
    • 方法二:

      1
      2
      3
      4
      5
      6
      7
      8
      import qs from 'qs'

      function getValue2 (key) {
      let search = window.location.href.split('?')[1]
      let params = qs.parse(search)
      console.log('params', params, params[key])
      return params[key]
      },

this

  • 普通函数
    • 普通函数调用: 全局windows
    • 作为对象的函数调用:上级对象
    • 作为构造函数调用:new 出来的对象
    • call、apply: 传入的上下文
  • 箭头函数
    • 没有绑定this,它的this取决于该函数外部非箭头函数的this值
  • setTimeout, setInterval
    • 普通函数:指向windows
    • 箭头函数:指向外层对象obj

bind,call,apply

  • 都是改变执行的上下文,也就是 this 的指向

  • call, apply: 立即执行

    • call: pa1, pa2, pa3, …
    • apply: [pa1, pa2, pa3, …]
  • bind: 只是生成一个新函数,在调用时才执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Object.prototype.toString.call(null);  //  "[object Null]"
    Object.prototype.toString.call([]); // "[object Array]"
    Object.prototype.toString.call({}); // "[object Object]"
    Object.prototype.toString.call(123); // "[object Number]"
    Object.prototype.toString.call('123'); // "[object String]"
    Object.prototype.toString.call(false); // "[object Boolean]"
    Object.prototype.toString.call(undefined); // "[object Undefined]"
    // 优化
    Object.prototype.toString.call([]).slice(8, -1); // Array

promise、async、await

  • promise:
    • new Promise, Promise.resolve, Promise.reject
    • then, catch
    • all: 全成功或1个失败,then/catch, 并行
    • race: 1个成功或失败,then/catch, 并行
    • allSettled: 全部成功或失败,then, [{status: ‘fulfilled’, value: ‘’}, {status: ‘rejected’, reason: ‘’}]
  • async:
    • 返回 promise
  • await:
    • async 内部才能使用
    • await 默认只能链式成功的promise,除非加 .catch
    • forfor of 内会按顺序执行,其他有回调循环的循环(forEachfiltermapreduce)不会按顺序

axios

  • 1
    2
    3
    4
    5
    6
    axios.carete({
    baseURL: '',
    timeout: 30000,
    token: '',
    headers: {}
    })
  • 1
    2
    3
    axios.interceptors.request.use(request => {

    })
  • 1
    2
    3
    axios.interceptors.response.use(response => {
    // response.data, code
    })
  • 添加请求参数

    • get: axios.get(url, { params: {aa: 1, bb: 2} })
    • post: axios.post(url, { aa: 1, bb: 2 })
  • 防止多次请求

    • 使用axiom.cancelToken = new Axios.CancelToken(function executor(cancel){ // 执行cancel })

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      // request interceptor
      service.interceptors.request.use(
      (config) => {
      removePending(config, "请求前触发");
      addPending(config);
      return config;
      },
      (error) => {
      return Promise.reject(error);
      }
      );

      const pendings = {}
      /**
      * 添加请求
      */
      export let addPending = (config) => {
      const { method, url, params, data } = config;
      const id = [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
      config.cancelToken = new Axios.CancelToken(function executor(cancel) {
      if (!pendings[id]) {
      pendings[id] = cancel
      }
      })
      return config;
      }

      /**
      * 移除请求
      */
      export let removePending = (config) => {
      if (!Object.keys(pendings).length) {
      return
      }
      // console.log(who, pendings, '取消前')
      let { method, url, params, data } = config;
      try {
      data = JSON.parse(data)
      } catch (error) {
      // data
      }
      const id = [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
      const cancel = pendings[id];
      if (cancel && typeof cancel === 'function') {
      cancel();
      delete pendings[id]
      }
      // console.log(who, pendings, '取消后')
      }

      /**
      * 清空所有pending请求
      */
      export let clearPending = () => {
      Object.keys(pendings).forEach(c => pendings[c]());
      }

防抖和节流

  • 防抖:执行高频函数n秒后才执行x函数,如果期间执行n,重新计时并结束后再执行x

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /*
    * 1. 函数防抖
    * 执行高频函数x秒后才执行n函数
    * 场景:dom更新,onresize,input下拉框
    * */
    function debounce(fn) {
    let timeout = null
    return function () {
    clearTimeout(timeout)
    timeout = setTimeout(() => {
    fn.call(this, arguments)
    }, 300)
    }
    }
    function sayHi() {
    console.log('say hi')
    }
    let inp = document.getElementById('inp')
    inp.addEventListener('input', debounce(sayHi))
  • 节流:n秒内只执行一次x函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /*
    * 2. 函数节流
    * x秒内只执行一次n函数
    * 场景:api请求
    * */
    function throttle(fn) {
    let canRun = true
    return function () {
    if (!canRun) {
    return
    }
    canRun = false
    setTimeout(() => {
    fn.call(this, arguments)
    canRun = true
    }, 300)
    }
    }

    function sayH(e) {
    console.log(e.target.innerWidth, e.target.innerHeight)
    }
    window.addEventListener('resize', throttle(sayH))

defer、async

  • defer: 异步下载,最后执行
  • async: 异步下载,下载完执行

捕获/冒泡

  • 事件有3个阶段:事件捕获阶段事件目标阶段事件冒泡阶段

  • 事件捕获阶段:父 –> 子

  • 事件目标阶段:按js添加顺序执行

  • 事件冒泡阶段:子 –> 父

  • https://segmentfault.com/q/1010000004542336

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    var div1 = document.getElementById("div1");
    var div2 = document.getElementById("div2");
    var btn = document.getElementById("btn1");

    div1.addEventListener("click",function ( event ) {
    console.log("div1,false");
    },false)
    div2.addEventListener("click",function ( event ) {
    console.log("div2,false");
    },false)
    btn.addEventListener("click",function ( event ) {
    console.log("btn,false");
    },false)
    div1.addEventListener("click",function ( event ) {
    console.log("div1,true");
    },true)
    div2.addEventListener("click",function ( event ) {
    console.log("div2,true");
    },true)
    btn.addEventListener("click",function ( event ) {
    console.log("btn,true");
    },true)

    // 结果
    // div1,true
    // div2,true
    // btn,false 这里
    // btn,true
    // div2,false
    // div1,false

ES6+

进阶1(技能方向)

git

基础命令

  • git add, git commit, git branch, git checkout, git checkout -b, git pull, git push, git clone

git flows

  • master, env-release, env-test, env-dev, feat-xx, hot-fix
  • feat: feat -> dev -> test -> master
  • hotfix: hotfix -> dev, hotfix -> test, hotfix -> master

变基(rebase)与合并

  • 变基都要在 push
  • 交互式变基:优化log提交记录。合并、修改log、调整顺序
  • 普通变基:
    • 把A合并到B,代码上等效于,把B变基到A
    • 代码层面相同,log不一样

遴选(cherry-pick)

  • 把某一条提交记录,合并到分支A

贮藏(stash)

  • 把当前修改临时存储起来,需要的时候应用
  • 修改文件直接贮藏,新增文件要 git add 后在贮藏

patch

  • 打补丁,类似遴选

vue 2 全家桶(重点)

原理

  • 遍历 data 对象,通过 Object.defineProperty 添加 gettersetter 方法。

    • 发布订阅和通知
    • getter: 添加订阅 addSub
    • setter: 触发通知 notify
  • 实例化订阅 Watcher,实例化时触发 getter 订阅。

  • 修改触发 setter 时,通知 DOM 修改模板

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // getter 与 setter
    function observe (data) {
    if (!data || typeof data !== 'object') {
    return
    }
    Object.keys(data).forEach(key => {
    defineReactive(data, key, data[key])
    })
    }

    function defineReactive (obj, key, value) {
    // 递归子属性
    observe(value)
    let dp = new Dep()
    Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
    console.log('get: ', value)
    // 添加订阅: Dep.target的this指向的是dp实例
    if (Dep.target) {
    dp.addSub(Dep.target)
    }
    return value
    },
    set: function reactiveSetter (newValue) {
    console.log('set: ', newValue)
    value = newValue
    // 执行通知
    dp.notify()
    }
    })
    }
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    class Dep {
    constructor () {
    this.subs = []
    }
    addSub (sub) {
    this.subs.push(sub)
    }
    notify () {
    this.subs.forEach(sub => {
    sub.update()
    })
    }
    }
    Dep.target = null // 全局属性
    function updateDiv (value) {
    document.getElementById('div').innerText = value
    }

    class Watcher {
    constructor (obj, key, cb) {
    // 手动触发getter,添加「订阅」
    Dep.target = this
    this.obj = obj
    this.key = key
    this.value = obj[key]
    this.cb = cb
    Dep.target = null
    }
    update () {
    // 获取新值
    this.value = this.obj[this.key]
    // 更新DOM
    this.cb(this.value)
    }
    }

    // ---------------------------- 调 用 ----------------------------
    let data = { name: 'yy' }
    observe(data)
    // 模拟解析到 `{{name}}` 触发的操作; 手动触发「订阅」
    new Watcher(data, 'name', updateDiv)
    // 更新DOM
    data.name = 'zz'

new Vue 过程

1
2
3
4
5
6
7
8
9
10
11
12
Vue.prototype._init = function(options) {
// mergeOptions 实例化属性合并,包含extends/mixins属性
initLifecycle(vm) // 初始化组件生命周期标志符
initEvents(vm) // 初始化组件事件系统
initRender(vm) // 初始化组件render相关属性方法($createElement, $attrs, $listeners)
callHook(vm, 'beforeCreate') // 拿不到 props data, 调用beforeCreate
initInjections(vm) // 初始化组件依赖注入内容
initState(vm) // 初始化state(data, props, methods, watch, computed)
initProvide(vm)
callHook(vm, 'created') //调用created
// 挂载template模板
}

Vue 虚拟DOM

操作 DOM 耗费性能太大,改用虚拟 DOM

  1. js 创建虚拟 dom对象
  2. 判断差异 diff 算法
    1. 树的递归,广度优先
    2. 判断列表差异
      1. 判断属性的更改
      2. 遍历子元素打标识
  3. 渲染差异

vue 基础

生命周期
  • beforeCreate: vue 实例的挂载元素 $el 和数据对象 data 都是 undefined,还未初始化
  • created: 完成了 data 的初始化,$el 还未初始化
  • beforeMount: vue 实例的 $eldata 都初始化了,相关的 render 函数首次被调用。实例已完成以下配置:编译模板,把 data 里的数据和模板生成 html。 注意此时 html 还没有挂载到页面上
  • mounted: 在 el 被新创建出来的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下配置:用编译好的 html 内容替换 el 属性指向的 DOM对象。完成模板中的 html 渲染到 html 页面中,此过程中进行 ajax 交互
  • beforeUpdate: 在数据更新之前调用,发生在虚拟 DOM 重新渲染和打补丁之前调用。可以在该钩子中进一步地更改状态,不会触发附加的渲染状态
  • updated: 在由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。调用时,组件 DOM 已经更新,所以可以依赖与 DOM 的操作。然而在大多数情况下应避免在此期间更改状态,因为这可能会导致无限循环,该钩子在服务器端渲染期间不可用
  • activated: keep-alive 激活时触发
  • deactivated: keep-alive 缓存时触发
  • beforeDestroy: 在实例销毁之前,实例仍完全可用
  • destroyed: 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子函数在服务器端渲染期间被调用
父子组件渲染顺序
  • 先父后子的原则

  • 多个子组件 单线程 渲染

  • 场景:父组件A,子组件B、C

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 进入页面
    A.beforeCreate
    A.Created
    A.beforeMount

    B.beforeCreate
    B.Created
    B.beforeMount

    C.beforeCreate
    C.Created
    C.beforeMount

    B.Mounted
    C.Mounted
    A.Mounted
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 离开页面
    A.beforeDestroy

    B.beforeDestroy
    B.destroyed

    C.beforeDestroy
    C.destroyed

    A.destroyed
双向绑定
  • 响应化:Vue.observable(object)
  • 对象和数组要初始化
  • 单独响应化
    • 对象
      • Vue.$set(object, key, value) or vm.$set(object, key, value) or this.$set(object, key, value)
      • Object.assign({}, object, {a: 1, b:2})
    • 数组
      • 不能响应:用下标修改数组值和修改数组长度
      • 修改值:
        • this.$set(array, index, value)
        • array.splice(index, 1, value)
      • 修改长度:
        • array.splice(newIndex)
传值(6种)
  • props$emit

  • 中央事件总线 vue bus

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // xxx.js
      let eventBus = new Vue()
      export eventBus

      // 创建
      import eventBus from '../xxx.js'
      eventBus.$emit('name', params)

      // 监听
      mounted () {
      eventBus.$on('name', fn)
      },
      beforeDestroy () {
      eventBus.$off('name')
      },
      methods: {
      fn (params) {
      console.log('params', params)
      }
      }
  • provideinject

    • 多层父子组件,传变量(函数),非响应式

    • 若要变为响应式,两种方法

      • 传递父组件实例过去,即传递 this

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        provide () {
        return {
        xxx: this
        }
        }

        inject: ['xxx']
        inject: {
        'xxx': () => {}
        }
      • 通过 Vue.observable() 传递响应式变量

        1
        2
        3
        4
        5
        6
        7
        8
        provide () {
        this.theme = Vue.observable({
        color: 'red'
        })
        return {
        theme: this.theme
        }
        }
  • vuex

  • $parent$children$ref

    • $parent: 父组件实例
    • $children: 子组件实例,不保证顺序
    • $ref:
      • 当前组件:DOM 元素
      • 子组件:子组件实例
  • $attrs$listeners

    • $attrs: 子组件内使用,包含所有 父组件传递了但子组件 props 里未定义 的值,对象结构,值为 {key1: value1, key2: value2}

    • $listeners: 子组件内使用,包含所有 父组件的非.native 方法。

    • 通过下面的操作,可以在多层父子组件传递

      • 1
        v-bind="$attrs"
      • 1
        v-on="$listeners"
v-on事件修饰符
  • https://cn.vuejs.org/v2/api/#vm-listeners
  • .stop: 阻止冒泡 <button @click.stop="xxx"></button>
  • .prevent: 阻止默认行为
  • .capture: 添加事件监听器时使用 capture 模式(捕获)
  • .self: 只当事件是从监听器绑定的元素本身触发时才触发回调
  • .native: 监听组件根元素的原生事件
prop单向数据流传递
  • 子组件的prop调用的父组件数据,当子组件修改时不想修改父组件
  • 方法一:
    • prop作为 data 初始值,之后使用 data 的新值
  • 方法二:
    • 使用计算属性转换
  • 注意: 数组和对象是以引用的方式传递的,所以修改子组件的值时,也会修改父组件的值。这种情况下,需使用 深拷贝 生成新值,然后对新值修改。
computed写法
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    computed: {
    aa () {
    return this.xxx + '11'
    },
    bb () {
    get: function () {
    return this.bb + '22'
    },
    set: function (value) {
    this.bb = value + 'xx'
    }
    }
    }
watch写法
  • aa () {}

  • aa.bb () {}

    1
    2
    3
    4
    5
    6
    7
    watch: {
    cc: {
    handler: 'methods',
    deep: true,
    immediate: true
    }
    }
computed,watch,methods区别
  • watch:监听回调,当依赖的值有改变时,触发回调执行一些逻辑
  • computed:计算属性,根据依赖的值动态显示最新的结果,会缓存(getter后)。能监听到obj深层key
  • methods:方法,执行函数
  • methods和computed:methods每次渲染时都会计算,而computed会从缓存取值
mixin
  • 混入文件写法和单页面文件一样

  • 调用混入以数组方式引入 mixins: [myMixin]

  • 合并规则

    • data: 递归合并,同名时以 组件 优先
    • 钩子函数:都会调用,混入文件 优先调用
    • 其他对象(methods, components, directives): 合并为一个对象,同名时以 组件 为准
  • 全局混入

    • 1
      2
      3
      4
      5
      6
      7
      8
      Vue.mixin({
      created: function () {
      var myOption = this.$options.myOption
      if (myOption) {
      console.log(myOption)
      }
      }
      })
keep-alive
  • 缓存未激活的组件

    • 1
      2
      3
      <keep-alive>
      <component :is="view"></component>
      </keep-alive>
  • include: 字符串或正则,匹配的才会缓存

  • exclude: 字符串或正则,匹配的都不会缓存

  • max: 数字,最多缓存多少实例

slot
  • 定义插槽: <slot name="xxx"></slot>

  • 使用插槽:<template v-slot:"xxx"></template>, v-slot 可以缩写为 #

  • 当插槽所在的父作用域 要 使用子作用域时,可以把子作用域作为变量传递给父

    • 1
      2
      3
      4
      5
      6
      7
      <!-- 子 -->
      <span>
      <slot v-bind:user="user">
      {{user.firstName}}
      </slot>
      <slot name="other" v-bind:dou="dou" v-bind:ruai="ruai" v-bind:mi="mi"></slot>
      </span>
    • 1
      2
      3
      4
      5
      <!-- 父 -->
      <current-user>
      <template v-slot:default="{ user }"></template>
      <template v-slot:other="{ dou, ruai, mi }"></template>
      </current-user>
    • v-slot:aa#aa : 使用插槽

    • v-slot:bb={}: 使用插槽传值

    • v-slot={}: 使用插槽传值,默认名,等效于 v-slot:default={}

过度、动画效果
  • 触发时机
    • v-ifv-show动态组件组件根节点
  • 单组件触发
    • <transition name="t"></transition>
    • 使用 t-xx 定义状态样式,如未定义 name, 默认为 v-xx
    • x-enterx-enter-activex-enter-tox-leavex-leave-activex-leave-to,一般使用 x-enterx-enter-activex-leave-activex-leave-to 4个状态
      • x-enter-activex-leave-active: 激活时状态(稳定)
  • 多组件 li 触发
    • <transition-group name="x"></transition-group>
    • 样式定义同上
    • x-move:位置移动时平滑移动 transition: transform 1s
常用API
指令
  • Vue.directive('name', { ... })<button v-name="xxx"></button>官网

  • 钩子函数:

    • bind: 只调用一次,第一次绑定到元素时调用。
    • inserted: 被绑定元素插入到父节点时调用。
    • update: 组件所在 VNode 更新时调用。
    • componentUpdated: 组件所在 VNode 及其子 VNode 全部更新后调用。
    • unbind: 只调用一次,解绑时调用。
  • 钩子函数参数:所有钩子参数一样,除了 el 其他参数都只读

    • el: 绑定元素DOM,可直接操作
    • binding: 一个对象,包含下面值
      • name: 指令名,不包含 v- 前缀
      • value: 指令的绑定值。v-dire="1 + 1" 中,绑定值为 2
      • oldValue: 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用
      • expression: 字符串形式的表达式。v-dire="1 + 1" 中,表达式为 1 + 1
      • arg: 传给指令的参数,可选。v-dire:foo 中,参数为 foo
      • modifiers: 一个包含修饰符的对象。v-dire.foo.bar 中,修饰符对象为 { foo: true, bar: true }
    • vnode: 虚拟节点
    • oldVnode: 上一个虚拟节点
  • 实例 v-longpress

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    // directive.js
    import Vue from 'vue'

    Vue.directive('longpress'', {
    bind: function (el, binding, vNode) {
    // 确保提供的表达式是函数
    if (typeof binding.value !== 'function') {
    // 获取组件名称
    const compName = vNode.context.name
    // 将警告传递给控制台
    let warn = `[longpress:] provided expression '${binding.expression}' is not a function, but has to be `
    if (compName) { warn += `Found in component '${compName}' ` }
    console.warn(warn)
    }

    // 定义变量
    let pressTimer = null

    // 定义函数处理程序
    // 创建计时器( 2秒后执行函数 )
    // 不是鼠标左键返回
    let start = (e) => {
    if (e.type === 'click' && e.button !== 0) {
    return
    }
    if (pressTimer === null) {
    pressTimer = setTimeout(() => {
    // 执行函数
    handler()
    }, 2000)
    }
    }

    // 取消计时器
    let cancel = () => {
    // 检查计时器是否有值
    if (pressTimer !== null) {
    clearTimeout(pressTimer)
    pressTimer = null
    }
    }

    // 运行函数
    const handler = (e) => {
    // 执行传递给指令的方法
    binding.value(e)
    }

    // 添加事件监听器
    el.addEventListener('mousedown', start)
    el.addEventListener('touchstart', start)

    // 取消计时器
    el.addEventListener('click', cancel)
    el.addEventListener('mouseout', cancel)
    el.addEventListener('touchend', cancel)
    el.addEventListener('touchcancel', cancel)
    }
    })

vue cli

vue loader

vue router

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

    // 1. 定义 (路由) 组件。
    // 可以从其他文件 import 进来
    const Foo = { template: '<div>foo</div>' }
    const Bar = { template: '<div>bar</div>' }

    // 2. 定义路由
    // 每个路由应该映射一个组件。 其中"component" 可以是
    // 通过 Vue.extend() 创建的组件构造器,
    // 或者,只是一个组件配置对象。
    // 我们晚点再讨论嵌套路由。
    const routes = [
    { path: '/foo', component: Foo },
    { path: '/bar', component: Bar }
    ]

    // 3. 创建 router 实例,然后传 `routes` 配置
    // 你还可以传别的配置参数, 不过先这么简单着吧。
    const router = new VueRouter({
    routes // (缩写) 相当于 routes: routes
    })

    // 4. 创建和挂载根实例。
    // 记得要通过 router 配置参数注入路由,
    // 从而让整个应用都有路由功能
    const app = new Vue({
    router
    }).$mount('#app')

    // 现在,应用已经启动了!
hash、history模式
  • 默认 hash 模式,即 url 上带 # 的模式。hashchange 事件
  • history模式: url 像正常url一样,需要后端支持。popstatepushstate 事件
router、route
  • router: 路由实例
    • router.beforeEachrouter.beforeResolverouter.afterEachrouter.pushrouter.replacerouter.gorouter.backrouter.forward
  • this.$route: 当前路由对象,当前激活的路由对象信息
    • path:
    • params:
    • query:
    • hash:
    • fullPath:
    • matched: 一个数组,包含当前路由的所有嵌套路径片段的路由记录
    • name:
    • redirectedFrom: 如果存在重定向,即为重定向来源的路由的名字
传值
  • params:
    • 不能和 path 一起使用,一起使用时 params 会失效
    • 传递:this.$router.push({name: 'xxx', params: {...}})
    • 获取:let a = this.$route.params
  • query:
    • 任何场合
    • 传递:this.$router.push({path: '/setup', query: {}})
导航守卫
  • 组件内的守卫
    • beforeRouteLeave
    • beforeRouteUpdate
    • beforeRouteEnter
  • 全局导航守卫
    • beforeEach
    • afterEach
  • 全局解析守卫
    • beforeResolve:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
  • 路由独享的守卫
    • beforeEnter
路由导航顺序
  1. 导航被触发
  2. 在失活的组件里调用 beforeRouteLeave 守卫
  3. 调用全局的 beforeEach 守卫
  4. 在重用的组件内调用 beforeRouteUpdate 守卫
  5. 在路由配置里调用 beforeEnter 守卫
  6. 解析异步路由组件
  7. 在被激活的组件里调用 beforeRouteEnter 守卫
  8. 调用全局的 beforeResolve 守卫
  9. 导航被确认
  10. 调用全局的 afterEach 钩子
  11. 触发 DOM 更新
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的实例会作为回调函数的参数传入

vuex

  • 专门为 vue 设计的 状态管理模式
State
  • 状态:

  • store.state.xxxthis.$store.state.xxx

  • store.state.moduleName.xxxthis.$store.state.moduleName.xxx

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { mapState } from 'vuex'

    computed: {
    ...mapState({
    count1: state => state.count1,
    countAlias: 'count'
    }),
    // 或者
    ...mapState(['count', 'count1'])
    }
Getters
  • state 的计算属性,但如果有变量传入时,则每次都会计算(不会缓存)

  • this.$store.getters.xxxthis.$store.getters.modulesName.xxx

  • 通过属性访问

    • 1
      2
      3
      4
      5
      getters: {
      doneTodosCount: (state, getters) => {
      return getters.doneTodos.length
      }
      }
  • 通过方法访问

    • 1
      2
      3
      4
      5
      6
      // 每次访问都会调用,而不会缓存结果
      getters: {
      getTodoById: (state) => (id) => {
      return state.todos.find(todo => todo.id === id)
      }
      }
  • 通过辅助函数 mapGetters 访问

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import { mapGetters } from 'vuex'

      computed: {
      ...mapGetters([
      'doneTodosCount',
      'otherGetters'
      ]),
      // 或者
      ...mapGetters({
      aliasName: 'doneTodosCount'
      })
      }
Mutations
  • 修改 state 的唯一方式

  • 只能是同步

  • 逻辑只能是修改 state

  • 建议 大写、常量 命名

  • 定义

    • 1
      2
      3
      4
      5
      mutations: {
      UPDATE_USER (state, payload) {
      state.age += payload.age
      }
      }
  • 调用

    • 1
      2
      3
      4
      5
      6
      this.$store.commit('name', payload)
      // or
      this.$store.commit({
      type: name,
      ...payload
      })
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import { mapMutations } from 'vuex'

      methods: {
      ...mapMutations([
      'mA',
      'mB'
      ]),
      // 或者,可以重命名
      ...mapMutations({
      aliasName: 'mA'
      })
      }
Actions
  • 异步,返回 promise

  • 可以写复杂逻辑,一般用于调用 mutations

  • 用法,参数基本和 mutations 一致

  • 定义

    • 1
      2
      3
      4
      5
      6
      actions: {
      increment ({ state, getters, commit, dispatch }, payload) {
      // 可以写复杂逻辑
      commit('INCREMENT', payload)
      }
      }
  • 调用

    • 1
      2
      3
      4
      5
      6
      this.$store.dispatch('increment', payload)
      // 或者
      this.$store.dispatch({
      type: 'increment',
      ...payload
      })
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import { mapActions } from 'vuex'

      methods: {
      ...mapActions([
      'aA',
      'aB'
      ]),
      // 或者
      ...mapActions({
      aliasName: 'aA'
      })
      }
namespace、module
  • 按模块区分,添加 namespaced: true 即可

  • 调用需加上 moduleName

    • this.$store.state.moduleName.xxthis.$store.getters.moduleName.xxthis.$store.commit('moduleName/xxx')this.$store.dispatch('moduleName/xxx')

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      computed: {
      ...mapState('moduleName', ['xxx', 'yyy']),
      ...mapGetters('moduleName', ['xxx', 'yyy'])
      },
      methods: {
      ...mapMutations('moduleName', ['xxx', 'yyy']),
      ...mapActions('moduleName', ['xxx', 'yyy'])
      }

      // 或者使用 createNamespacedHelpers 创建基于命名空间的辅助函数
      const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
      computed: {
      ...mapState(['xxx', 'yyy']),
      ...mapGetters(['xxx', 'yyy'])
      },
      methods: {
      ...mapMutations(['xxx', 'yyy']),
      ...mapActions(['xxx', 'yyy'])
      }
  • 在带命名空间的模块内调用全局内容

    • stategetters: rootStaterootGetters 作为第三、第四个参数传给 getters,也会作为 context 传给 action

    • mutationsactions: 若要在全局命名空间内分发 action 或 提交 mutation, 将 { root: true } 作为第三个参数传递给 dispatchcommit 即可

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        modules: {
        foo: {
        namespaced: true,

        getters: {
        // 在这个模块的 getter 中,`getters` 被局部化了
        // 你可以使用 getter 的第四个参数来调用 `rootGetters`
        someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
        },
        someOtherGetter: state => { ... }
        },

        actions: {
        // 在这个模块中, dispatch 和 commit 也被局部化了
        // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
        someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -> 'foo/someGetter'
        rootGetters.someGetter // -> 'someGetter'

        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation', null, { root: true }) // -> 'someMutation'
        },
        someOtherAction (ctx, payload) { ... }
        }
        }
        }
    • modules 定义全局 action: 添加 root: true, 并将这个 action 的定义放在函数 handler

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        {
        actions: {
        someOtherAction ({dispatch}) {
        dispatch('someAction')
        }
        },
        modules: {
        foo: {
        namespaced: true,

        actions: {
        someAction: {
        root: true,
        handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
        }
        }
        }
        }

动态导入 modules

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // https://webpack.js.org/guides/dependency-management/#requirecontext
    const modulesFiles = require.context('./modules', true, /\.js$/)

    // you do not need `import app from './modules/app'`
    // it will auto require all vuex module from modules file
    const modules = modulesFiles.keys().reduce((modules, modulePath) => {
    // set './app.js' => 'app'
    const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
    const value = modulesFiles(modulePath)
    modules[moduleName] = value.default
    return modules
    }, {})

    export default new Vuex.Store({
    modules,
    getters
    })

动态导入组件

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //  global.js文件
    import Vue from 'vue'
    function changeStr (str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
    }
    const requireComponent = require.context('./', false, /\.vue$/)
    // 查找同级目录下以vue结尾的组件
    const install = () => {
    requireComponent.keys().forEach(fileName => {
    let config = requireComponent(fileName)
    console.log(config) // ./child1.vue 然后用正则拿到child1
    let componentName = changeStr(
    //fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')
    fileName.replace(/^\.\/(.*)\.vue$/, '$1')
    )
    Vue.component(componentName, config.default || config)
    })
    }
    export default {
    install // 对外暴露install方法
    }

ssr

ui框架

  • ui框架类似,以 iview 举例

iview / iview Design

  • 安装

    • 全局引入: vue.use(iview) css
    • 按需引入: css + babelrc, babel-plugin-import
  • 国际化

    • i18n
  • 全局配置

    • Vue.use(ViewUI, {xxx})
  • validator

    • prop和key一致

    • required: true, message: 'xx', trigger: 'blur'

    • type: 'string', pattern: '/^(0|[1-9][0-9]*)$/', message: 'xx', trigger: 'change'

    • validator: this.xxx', trigger: 'blur'

      • 1
        2
        3
        4
        5
        6
        7
        const validateQualityValueMile = (rule, value, callback) => {
        if (this.isGasControl && !value && !this.carForm.qualityValueDay) {
        console.log('vMile ', value)
        return callback(new Error('质量保证期必须填写'))
        }
        return callback()
        }

vant

element-ui

ant-design

iconfont

  • 添加图标,命名
  • 下载后,添加6个文件(除了demo的css,js),引入iconfont.css
  • <span class="iconfont xxx">

vue 3(重点)

option API

composition API

区别(响应速度、原理等)

区别(vue 2)

语法?

ts

基础

  • 基本类型:string, number, boolean, null, undefined, Symbol, BigInt
  • | : 联合类型,同时只能是多个类型中其中一个
  • &: 交叉类型,同时是所有类型的所有

进阶

  • type: 类型别名,= 赋值
  • interface: 接口,直接赋值
    • typeinterface 的区别:基本一致。赋值方式不一样;接口可以合并,类型别名需用 & 生成 交叉类型
  • class:类
    • constructor : 构造函数
    • extends : 继承(类-类,类-接口,接口-接口,接口-类)
    • super: 调用父类的构造函数
    • 修饰符
      • public: 公开,默认
      • private: 私有的,只能当前类使用
      • protected: 和 private 类似,区别是子类也可以使用
    • implements: 类实现接口
    • TS 里,接口是可以继承类的
  • abstract: 抽象类,抽象方法
    • 抽象类不能被实例化
    • 抽象方法必须在子类实现

自带函数

  • typeof: 获取实例的类型, type x = typeof xxInstance
  • keyof: 获取 typeinterfacekey 的联合类型
  • partial: 所有key变为可选
  • required: 所有key变为必填
  • readonly: 所有key变为只读
  • record: 所有key变为指定T类型
  • pick: 选择部分key
  • extract: 遍历T.keys,返回U中存在的keys的类型,参数和结果都是联合类型
  • exclude: 遍历T.keys, 返回U中 不存在的keys类型,参数和结果都是联合类型
  • omit: pick+exclude组合,返回 对象中,除几个key外的其他所有类型。和pick对应

webpack

进阶2(管理方向)

脚手架搭建

中后台脚手架实践

基础封装

axios

es lint

git-hook

prettier

jenkins

进阶3(面试题)

原型链与继承

js -> 原型/原型链

输入url之后

跳转

异步:宏任务与微任务

  • https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7

  • 顺序:同步 -> 宏任务队列1 -> 微任务队列1 -> 宏任务队列2 -> 微任务队列2

  • 宏任务:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)

  • 微任务:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)

  • Tips:

    • promise(A).then(B)内的A函数为立即执行,B函数为微任务队列

      • B 里如果 return promise,会添加到微任务队列最后,如果 return xx(number | string),则立即执行
    • await A; B() 的A函数为立即执行,B函数为微任务队列

    • 微任务里创建的微任务(无论多少层),都在当前微任务队列内执行(添加到当前微任务列表最后)

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        promise.resolve().then(() => {
        console.log(1)
        promise.resolve().then(() => {
        console.log(2)
        })
        }).then(() => {
        console.log(3)
        })
        // => 返回1,2,3
    • script 执行完后,会直接执行 微任务,所以默认顺序为:同步 -> 微任务1 -> 宏任务2 -> 微任务2 -> …

Promise原理

注册 then 回调函数,push 到 callbacks里。当 resolve 时,调用 callbacks 里所有函数。

PWA

渐进式应用程序

  • web worker: 单独开线程执行任务:

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      // main.js
      const myWorker = new Worker('worker.js')

      // 传递
      myWorker.postMessage('hello')
      // 接收
      myWorker.onmessage = function(e) {
      console.log(e.data)
      }

      // worker.js, 回立即执行
      self.onmessage = function(e) {
      console.log(e.data)
      // 向主文件发送信息
      self.postMessage('lala')
      }
  • service worker: 可以拦截网络请求,决定走网络还是返回缓存数据,所以可以实现「后退」功能

脚手架发布

  • package.json.main: "bin/mbs.js"

js取整

  • 简书
  • parseInt
  • ~~20.25
  • 20.25^0
  • 20.25 << 0
  • Math
    • Math.floor
    • Math.ceil
    • Math.round
    • Math.trunc: 直接取整

js动效

  • js动效中:setTimeoutsetInterval 并不是间隔 xx 开始渲染页面,而是间隔 xx 添加到 任务队列 中,如果队列里已经有任务的话…..

  • requestAnimationFrame(callback): 在下一次重绘前,执行动效。重绘是浏览器决定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const element = document.getElementById('ele')
    const start = null

    // timestamp: 当前时间戳
    function step(timestamp) {
    if (start === undefined) {
    start = timestamp
    }
    const elapsed = timestamp - start
    element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px )'
    // 2s 后停止动画
    if (elapsed < 2000) {
    window.requestAnimationFrame(step)
    }
    }

    window.requestAnimationFrame(step)

实际面试

this.$nextTick 原理

  • 创建异步延迟函数 timerFunc,在异步延迟结束后,调用传入的回调函数
  • timerFunc,延迟调用优先级:Promise.resolve().then() > MutationObserver > Setimmediate > setTimeout:2微任务,2宏任务
  • nextTick(cb?, ctx),如果未传入 cb,会自动返回 Promise.resolve(),可用 then 调用后续函数。

vuex缺点

  • 内存占用大

vue 渲染原理

  • 虚拟DOM啥的

vue 多页面是什么

  • 多个html页面,除了主页内是vue-router跳转,其他页面都是a标签跳转到其他页面
  • 主页面加载快,多页面SEO好
  • 多页面之间跳转慢,因为要加载css和js

js为啥会阻塞页面渲染

  • js可能会修改DOM结构,比如 document.write

事件委托/事件代理

  • 利用事件冒泡,只为父元素添加事件,进而达到为多个子元素添加事件的效果
  • 减少DOM查询,提升性能,
  • 可扩展,新增的子元素也可直接代理
  • 不冒泡的事件不支持;冒泡过多可能被阻止掉;别人使用时误判导致调用2次
  • 掘金

事件循环

  • 执行栈:主线程,同步
  • 事件队列:异步,
  • 主线程 -> 事件队列 -> 主线程 -> 事件队列 -> …
  • 知乎 github

Web 性能优化

网络、资源

  • 同域名下减少DNS解析
  • 开启http2,多路复用
  • 压缩图片,懒加载(layzsizes),雪碧图,iconfont图标
  • gzip压缩代码
  • 静态包,包含下载好的首页等

css

  • 修改类名而不是样式
  • 减少DOM访问:事件代理
  • 减少重排重绘:
    • 不使用table
    • 开启硬件加速:animations、transforms、transitions不会自动开启,当检测到DOM元素开启某些规则时会开启,比如3D变化
      • transform: translate3d(250px, 250px, 250px) rotate3d(250px, 250px, 250px, -120deg) scale3d(0.5, 0.5, 0.5) (左手,右下前)
      • 有时候不想3d的转换,可以使用小技巧欺骗 transform: translateZ(0)
      • 但此时会屏幕闪烁,可以这样解决 backface-visibility: hidden; perspective: 1000 或者 transform: translate3d(0, 0, 0) 参考资料
      • 元素背面朝向观察者时是否可见;观察者距离z=0平面的距离。

js

  • 减少js体积,webpack chunk打包
  • web worker 开线程
  • 优化代码,减少循环
  • 函数节流

commonJs, ES6 module

  • commonJs: 动态编译,使用时才编译;可使用变量;值拷贝,不影响
  • es6 module: 静态编译,先一次性编译好,后面使用;所以编译的时候就能够分析代码是否被使用,进而使用 tree shaking;值应用,共享数据

浏览器缓存

浏览器缓存

https建立过程

TLS建立过程

分享到:
11