Skip to content

1、生命周期

beforeCreate、create、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed、actived、deactivated、errorCapture

生命周期具体分析描述
beforeCreate -> created初始化vue实例,进行数据观测
created1、完成数据观测,属性与方法的运算,watchevent事件回调的配置
2、可调用methods中的方法,访问和修改data数据触发响应式渲染dom,可通过computedwatch完成数据计算
3、vm.$el没有被创建
created -> beforeMount1、判断是否存在el选项,若不存在则停止编译,直到调用vm.$mount(el)才会继续编译
2、优先级:render > template > outerHTML
3、vm.el获取的是挂载DOM
beforeMount1、在此阶段可获取到vm.el
2、此阶段vm.el虽然已经完成DOM初始化,但并未挂载在el选项上
beforeMouted ->1、此阶段可获取到vm.el
2、在此阶段vm.el虽已完成DOM初始化,但并未挂载在el

2、双向绑定的原理

  • new Vue()首先执行初始化,对data执行响应化处理,这个过程发生在Observe
  • 同时对模板执行编译,找到其中动态绑定得到数据,从data中获取并初始化视图,这个过程发生在Compile
  • 同时定义一个更新函数和Watcher,将来对应数据变化时Watcher会调用更新函数
  • 由于data的某一个key在一个视图中可能会出现多次,所以每一个key都需要一个管家Dep来管理多个Watcher
  • 将来data中数据一旦发生变化,会首先找到对应的Dep,通知所有的Watcher执行更新函数
js
// 创建构造函数,执行初始化,对data执行响应化处理
class Vue {
    constructor(options) {
        this.$options = options;
        this.$data = options.data;
		// 对 data 选项做响应式处理  
        observe(this.$data);
		// 代理data 到 vm  
        proxy(this);
		// 执行编译
        new Compile(options.el, this);
    }
}
js
// 对data选项执行响应化操作
function observe(obj) {
    if (typeof obj !== "object" || obj == null) {
        return;
    }
    new Observer(obj);
}
class Observer {
    constructor(value) {
        this.value = value;
        this.walk(value);
    }
    walk(obj) {
        // 对选项做响应式处理
        Object.keys(obj).forEach((key) => {
            defineReactive(obj, key, obj[key]);
        });
    }
}
js
// 编译Compile,对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数
class Compile {
    constructor(el, vm) {
        this.$vm = vm;
        this.$el = document.querySelector(el);  // 获取dom
        if (this.$el) {
            // 对dom进行解析编译
            this.compile(this.$el);
        }
    }
    compile(el) {
        const childNodes = el.childNodes; // 获取子元素
        Array.from(childNodes).forEach((node) => { // 遍历子元素
            if (this.isElement(node)) {   // 判断是否为节点
                console.log("编译元素" + node.nodeName);
            } else if (this.isInterpolation(node)) {
                console.log("编译插值文本" + node.textContent);  // 判断是否为插值文本{{}}
            }
            if (node.childNodes && node.childNodes.length > 0) {  // 判断是否有子元素

                this.compile(node);  // 对子元素及逆行递归遍历
            }
        });
    }
    isElement(node) {
        return node.nodeType == 1;
    }
    isInterpolation(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
}
js
// 依赖收集,视图中会用到data中某key,称为依赖
/* 实现思路
1、defineReactive 时为每一个key创建一个key创建一Dep实例
2、初始化视图时读取某一个key,如name,创建一个Watcher
3、由于触发name的getter方法,便将watcher添加到name对应的Dep中
4、当name更新,setter触发,便可以通过Dep通知其管理所有Watcher更新
*/
//   
class Watcher {
    constructor(vm, key, updater) {
        this.vm = vm
        this.key = key
        this.updaterFn = updater
		// 创建实例时,把当前实例指定到Dep.target静态属性上  
        Dep.target = this
		// 读一下key,触发get  
        vm[key]
		// 置空
        Dep.target = null
    }
	// 未来执行dom更新函数,由 dep 调用的  
    update() {
        this.updaterFn.call(this.vm, this.vm[this.key])
    }
}

// 声明Dep
class Dep {
    constructor() {
        this.deps = [];  // 依赖管理
    }
    addDep(dep) {
        this.deps.push(dep);
    }
    notify() {
        this.deps.forEach((dep) => dep.update());
    }
}
js
// 依赖收集,创建Dep实例
function defineReactive(obj, key, val) {
    this.observe(val);
    const dep = new Dep();
    Object.defineProperty(obj, key, {
        get() {
            Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher实例
            return val;
        },
        set(newVal) {
            if (newVal === val) return;
            dep.notify(); // 通知dep执行更新方法
        },
    });
}

三、组件通信

  • 父子之间
  • 兄弟之间
  • 祖孙后代之间
  • 非关系组件之间

四、组件实例中为什么data是一个函数不是对象

函数返回的对象的内存地址不相同,这样引用的的时候,此组件的子组件和另一个组件的子组件值不会一致变化

五、动态给vue的data添加新属性

vue2中是用Object.defineProperty()实现数据响应式

js
const obj = {}
Object.defineProperty(obj, 'foo', {
    get() {
        console.log(`get foo:${val}`);
        return val
    },
    set(newVal) {
        if (newVal !== val) {
            console.log(`set foo:${newVal}`);
            val = newVal
        }
    }
})
js
// 实现数据与视图的同步更新
Vue.set()
Object.assign()
$forceUpdate()

// Vue.set()
function set (target: Array<any> | Object, key: any, val: any): any {
...
    defineReactive(ob.value, key, val)
    ob.dep.notify()
    return val
}

// Object.assign()
this.someObject = Object.assign({},this.someObject,{newProperty1:1,newPrope
 rty2:2 ...})

// $forceUpdate() 强制更新

6、v-for和v-if的优先级

v-for要比v-if的优先级高,不要将其置于同一个标签上

7、v-show和v-if的区别

8、vue中key的作用

  • 如果不用key,Vue采用就地复用原则:最小化element的移动,并会尝试尽最大程度在适当的地方对相同类型的element,做patch或者reuse。
  • 如果使用的key,Vue会根据keys的顺序记录element,曾静拥有了key的element如果不再出现的话,会不直接remove或者destoryed
  • 设计key可以大大减少对页面的DOM操作,提高了diff效率

九、Vue的mixin属性

十、Vue常用修饰符

  • 表单修饰符:lazy、trim、number
  • 事件修饰符:stop、prevent、self、once、capture、passive、native
  • 鼠标按键修饰符:left、right、middle
  • 键值修饰符:enter、tab、delete、space、ctrl、alt、shift、。。。
  • v-bind修饰符:async、prop、camel

十一、$nextTick

可以在修改数据之后立刻得到更新后的DOM结构

十二、Vue实例挂载过程

十三、vue的diff算法

diff算法是一种通过同层的树节点进行比较高效算法

1、比较方式

深度优先,同层比较:

比较只会在同层进行,不会跨层级比较

比较过程中,循环从两边想中间比较

当数据发生变化,set方法会调用Dep.notify,通知所有的订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁

十四、Vue中组件和插件的区别

十五、怎么解决跨域

同源策略:协议相同、主机相同、端口相同

1、CORS

cors(Cross-Origin resource sharing,跨域资源共享),由一系列传输的HTTP头组成,其据欸的那个浏览器是否阻止JavaScript代码获取挂于请求的相应,可以让服务器声明允许的访问来源

js
// 设置Access-Control-Allo响应头w-Origin响应头
app.use(async (ctx, next)=> {
    ctx.set('Access-Control-Allow-Origin', '*');
    ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, A
    uthorization, Accept, X-Requested-With , yourHeaderFeild');
    ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTION
    S');
    if (ctx.method == 'OPTIONS') {
        ctx.body = 200;
    } else {
        await next();
    }
})

2、Proxy

js
amodule.exports = {
    devServer: {
        host: '127.0.0.1',
        port: 8084,
        open: true,// vue
        proxy: {
            '/api': { // '/api' node url /api
                target: "http://xxx.xxx.xx.xx:8080",
                changeOrigin: true,
                 pathRewrite: { // pathRewrite Request Url '/
                 '^/api': "" 
                }
            }
        }
    }

十八、slot

1、默认插槽

vue
<!-- Child.vue -->
<template>
  <slot>
    <p> 插槽后备内容 </p>
  </slot>
</template>
vue
<Child>
	<div> 默认插槽 </div>
</Child>

2、具名插槽

vue
<!-- Child.vue -->
<template>
  <slot>插槽后背的内容</slot>
  <slot name="content">插槽后背的内容</slot>
</template>
vue
<temolate>
    <Child>
        <template v-slot:default>具名插槽</template>
        <template v-slot:content>内容。。。</template>
    </Child>
</temolate>

3、作用域插槽

vue
<template>
	<slot name="footer" prop1="" prop2="">
        <h3>没有传footer插槽</h3>
    </slot>
</template>
vue
<Child>
    <template v-solt:footer="props">
		来自子组件数据:{{ props.prop1 }}
    </template>
    <template #footer="props">
    	来自子组件数据:{{ props.prop2 }}
    </template>
    <template #footer="{ prop1, prop2 }">
    	来自子组件数据:{{ prop2 }}
    </template>
</Child>

十九、虚拟DOM

用于描述真实DOM树结构的一个JavaScript对象

二十、Vue项目封装axios

二十六、首屏优化

  • 减小入口文件体积,使用动态路由配置
  • 静态资源本地缓存,设置Cache-Control,Last-Modified、Etag等响应头
  • UI框架按需加载
  • 图片资源的压缩
  • 组件重复打包:minChunks:3
  • 开启Gzip压缩:compression-webpack-plugin
  • 使用SSR:Nuxt.js

二十八、SSR

SSR意为服务端渲染