高频面试题

127 阅读8分钟

vue相关

vue的生命周期,父子执行顺序

 - 加载渲染顺序
`父组件beforeCreate`->`父组件的created`->`父组件的beforeMount` -> `子组件beforeCreate`->`子组件的created`->`子组件的beforeMount` -> `子组件的mounted` -> `父组件的mounted`
 - 父组件更新
父组件beforeUpdate->父组件的updated
- 子组件更新
父beforeUpdate->子beforeUpdate->子updated->父updated
 - 销毁顺序
 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

vue响应式实现

vue通过数据劫持+发布订阅者模式来实现响应式

vue首先会遍历data的每一个属性,通过`Object.defineProty`给属性添加`getter``setter`,
并通过发布订阅者模式,给每一个属性创建一个发布者(dep)。
每个组件实例都对应一个watcher实例(订阅者),在渲染过程中组件接触过的属性,将会触发这个属性的getter,从订阅者加入到发布者的队列中。
当属性改变。触发setter调用时,该属性的发布者会通知所有订阅者更新内容

data.png

vue.nextTick是的使用场景和原理

- 使用场景:在数据改变后,想要获取改变后的dom属性
Vue.component('example', 
{ 
    template: '<span>{{ message }}</span>', 
    data: function () { 
        return { message: '未更新' } 
    }, 
    methods: { 
        updateMessage: function () { 
            this.message = '已更新'
            console.log(this.$el.textContent) // => '未更新' 
            this.$nextTick(function () { 
                console.log(this.$el.textContent) // => '已更新'
            }) 
        }
    } 
})

原理

Vue 在更新 DOM 时是**异步**执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 `Promise.then``MutationObserver` 和 `setImmediate`,如果执行环境不支持,则会采用 `setTimeout(fn, 0)` 代替。

什么时虚拟dom及虚拟dom的优劣

虚拟dom实际就是一个js对象,用来模拟真实dom的属性。
优点:
 1、减少对真实dom的操作
 2、跨平台的兼容性好
 
 缺点:
 1、首次大量渲染dom时,由于多了一层虚拟dom的计算,比innerHtml插入慢

diff算法的原理

diff算法是一种对比算法。
对比的两者是新虚拟dom和旧虚拟dom,找出更改的虚拟dom节点,只更新这个虚拟dom节点对应的真实dom。

原理我还不太明白,可以学习这个大大的diff原理分析

vue.$set

使用场景: 向data中添加一个新的属性,并且保证这个属性是响应式的。
原理:vue响应式是通过数据劫持完成的,数据劫持在初始化vue的时候就完成了,所以在后面添加的属性,是没有响应式 的,就通过使用vue.set给新的添加属性。
set方法接收三个参数:
第一个参数: target是对象,或者数组
第二个参数: 想要添加的属性名或下标
第三个参数: 想要添加的值
判断如果`target`是数组,判断传入的下标是不是合法的,如果合法调用数组的splice方法(splice方法被vue改写,它会把对象的元素都变成响应式的,然后手动触发依赖通知),并且返回想要添加的值
如果`target`是对象,判断属性是否已存在,如果不存在,会获取 `target.__ob__` 并赋值给 `ob`,它是在 `Observer` 的构造函数执行的时候初始化的,表示 `Observer` 的一个实例,如果它不存在,则说明 `target` 不是一个响应式的对象,则直接赋值并返回。最后通过 `defineReactive(ob.value, key, val)` 把新添加的属性变成响应式对象,然后再通过 `ob.dep.notify()` 手动的触发依赖通知,
export function 
 (target: Array<any> | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) {
    target[key] = val
    return val
  }
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

v-for为什么要设置key

key的作用:
key主要作用在虚拟dom的计算,在新旧节点对比时辨识虚拟节点。如果不设置key或者key为index时,在diff算法进行新旧节点对比时,会命中新旧节点相同的逻辑,会只更新元素的文本节点(就地更新)。这种模式只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
使用key时,他会基于key的变化重新排列顺序,并且会移除key不存在的元素。

watch

选项
{
    deep: 是否深度监听,为true时可以 监听对象属性的变化
    immediate: 为true时,立即以表达式的当前值触发回调(用于子组件监听prop变化)
    handler: function(newVal, oldVal){} 
    回调函数不能为箭头函数,箭头函数绑定父级作用域上下文,this不会指向vue
}
vm.$watch()会返回一个取消观察函数
var unwatch = vm.$watch('a', cb)
// 之后取消观察 
unwatch()

minxin的用法及优缺点分析

特点:
1、可以定义公共的变量在各个组件中使用,并且各个组件中的变量都是独立的。
2、在引入组件后会与组件中的方法和属性合并,如果有相同的属性名或者方法名,组件的属性和方法会覆盖minxin中的。
3、如果在组件和minxin中都定义了相同的生命周期函数,会将生命周期合并为数组,先执行minxin的生命周期函数。
使用场景:
在项目中有大量相同的业务逻辑,可以写在mixin中混入组件。
优点:
可以分发vue组件中的可复用功能
缺点:
如果使用全局混入,因为它会影响每个单独创建的 Vue 实例 (包括第三方组件)

组件属性和方法继承,及对组件库的二次封装

$attrs: 包含父作用域中不作为prop被识别(且获取) 的 attribute 绑定 (`class` 和 `style` 除外),
如果父作用域声名任何prop时,,这里会包含所有父作用域的绑定 (`class` 和 `style` 除外),并且可以通过 `v-bind="$attrs"` 接收传给子组件

$listeners: 包含了父作用域中的 (不含 `.native` 修饰器的) `v-on` 事件监听器。它可以通过 `v-on="$listeners"` 接收父组件的事件传给子组件
使用$attrs和$listeners对element组件进行二次封装
子组件
<div>
  <div>{{label}}</div>
  <el-select  :value="value" placeholder="Select" v-bind="$attrs" v-on="$listeners">
    <el-option
      v-for="item in options"
      :key="item.value"
      :label="item.label"
      :value="item.value">
    </el-option>
  </el-select>
</div>

export default {
  name: 'HelloWorld',
  inheritAttrs: false,
 model: {
    event: 'change',
    prop: 'value'
  },
  props: [
    'label', 'value'
  ],
  data () {
    return {
      options: [],
    }
  }
 }
 父组件
 <HelloWorld :label="msg" v-model="loveVue" :clearable="true" :multiple="true" :filterable="true" @focus="focus" @input="input" @change="change" @click="toggle" />

v-model的实现

v-model可以作用与一些表单组件和自定义组件上。 v-model实际上时一个语法糖,相当与使用v-bind和$emit

vuex的了解

vuex是vue的状态管理模式。可以多个组件共享状态。vuex是单一状态树。
vuex的属性: {
    state:() => ({ ... }), // 储存一些全局状态
    getters: {}, // store的计算属性
    mutations: {}, // 同步操作,修改state中的数据
    actions: {}, // 异步操作,通过mutations修改state中的数据
    modules: {} // 模块化
}
使用:
    state: this.$store.state.name / ...mapstate({})
    getters: this.$store.getter.name / ...mapgetter({})
    mutations: this.$store.commit(name, paylod) / ...mapMutation([]),
    actions: this.$store.dispatch(name, paylod) / ...mapAction([])
   
mutations为什么要是同步的?
同步的意义在于这样每一个 mutation 执行完成后都可以对应到一个新的状态(和 reducer 一样),这样 devtools 就可以打个 snapshot 存下来,然后就可以随便 time-travel 了。

在页面刷新之后vuex的状态会被清空,怎么保持vuex持久化:
1、手动储存在本地
2、使用`vue-persist`插件

vue-router的两种路由模式

1、hash模式(**HashHistory**)
特点:
 1.1hash虽然出现在URL中,但不会被包括在HTTP请求中。它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面。
 1.2   每一次改变hash(window.location.hash),都会在浏览器的访问历史中增加一个记录
 1.3、可以添加事件监听hash的改变window.addEventListener("hashchange", funcRef, false)
原理:
HashHistory有两个方法HashHistory.push()和HashHistory.replace()
HashHistory.push()是将新路由添加到浏览器历史访问站中
HashHistory.replace()是替换当前路由

2、history模式( HTML5History)
特点:
    2.1、 利用history.pushState()来完成URL 跳转而无须重新加载页面
    2.2、 监听地址变化window.onpopstate
原理:
window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)


区别:
-   pushState设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL
-   pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串
-   pushState可额外设置title属性供后续使用
-   history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误。

路由的钩子函数

全局前置守卫:beforeEach
全局解析守卫:beforeResolve
全局后置守卫:afterEach
路由独享守卫:beforeEnter
组件内守卫:-   `beforeRouteEnter``beforeRouteUpdate`-   `beforeRouteLeave`

完整导航解析流程
1.  导航被触发。
2.  在失活的组件里调用 `beforeRouteLeave` 守卫。
3.  调用全局的 `beforeEach` 守卫。
4.  在重用的组件里调用 `beforeRouteUpdate` 守卫 (2.2+)。
5.  在路由配置里调用 `beforeEnter`6.  解析异步路由组件。
7.  在被激活的组件里调用 `beforeRouteEnter`8.  调用全局的 `beforeResolve` 守卫 (2.5+)。
9.  导航被确认。
10.  调用全局的 `afterEach` 钩子。
11.  触发 DOM 更新。
12.  调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数,创建好的组件实例会作为回调函数的参数传入。

vue.config.js常用配置

const path = require('path')

module.exports = {
    publicPath: './', // 基本路径
    outputDir: 'dist', // 输出文件目录
    lintOnSave: false, // eslint-loader 是否在保存的时候检查
    // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
    // webpack配置
    chainWebpack: (config) => {
    },
    configureWebpack: (config) => {
        if (process.env.NODE_ENV === 'production') {
            // 为生产环境修改配置...
            config.mode = 'production'
        } else {
            // 为开发环境修改配置...
            config.mode = 'development'
        }
        Object.assign(config, {
            // 开发生产共同配置
            resolve: {
                alias: {
                    '@': path.resolve(__dirname, './src'),
                    '@c': path.resolve(__dirname, './src/components'),
                    '@p': path.resolve(__dirname, './src/pages')
                } // 别名配置
            }
        })
    },
    // 生产环境是否生成 sourceMap 文件
    productionSourceMap: false, 
    // css相关配置
    css: {
        extract: true, // 是否使用css分离插件 ExtractTextPlugin
        sourceMap: false, // 开启 CSS source maps?
        loaderOptions: {
            css: {}, // 这里的选项会传递给 css-loader
            postcss: {} // 这里的选项会传递给 postcss-loader
        },
         // css预设器配置项 详见https://cli.vuejs.org/zh/config/#css-loaderoptions
        modules: false // 启用 CSS modules for all css / pre-processor files.
    },

    // 是否为 Babel 或 TypeScript 使用 thread-loader。
    //该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
    parallel: require('os').cpus().length > 1, 
    // PWA 插件相关配置 see 
    //https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
    pwa: {}, 
    // webpack-dev-server 相关配置
    devServer: {
        open: process.platform === 'darwin',
        host: '0.0.0.0', // 允许外部ip访问
        port: 8022, // 端口
        https: false, // 启用https
        overlay: {
            warnings: true,
            errors: true
        }, // 错误、警告在页面弹出
        proxy: {
            '/api': {
                target: 'http://www.baidu.com/api',
                changeOrigin: true, // 允许websockets跨域
                // ws: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        } // 代理转发配置,用于调试环境
    },
    // 第三方插件配置
    pluginOptions: {}
}

vue2.0和vue3.0的区别

js

1. js数据类型

2. es6新特性

3. promise(并发、取消)

4. apply、bind

5. 数组去重

6. class

7. 数组扁平化

8. this

9. 深拷贝

10. sleep函数

11. 闭包

12. 继承

13. 原型链

14. new的过程

15. 发布订阅模型

16.

浏览器相关

输入地址到页面展示的过程

eventloop

本地存储

浏览器缓存

dns

get和post请求的区别

同源策略

怎么解跨域

常见状态码

内存泄漏

js脚本的同步加载和异步加载

业务相关

大文件上传

图片处理、视频上传

项目优化

单页面看法

sso单点登录

节流和防抖

git常用命令,怎么解决git冲突

首页白屏优化

在小程序app.js中,请求的接口,在首页如何确保能拿到数据

如何等dispatch事件执行之后再执行后面的js