1、生命周期
beforeCreate、create、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed、actived、deactivated、errorCapture
生命周期 | 具体分析 | 描述 |
---|---|---|
beforeCreate -> created | 初始化vue实例,进行数据观测 | |
created | 1、完成数据观测,属性与方法的运算,watch 、event 事件回调的配置2、可调用 methods 中的方法,访问和修改data 数据触发响应式渲染dom ,可通过computed 和watch 完成数据计算3、 vm.$el 没有被创建 | |
created -> beforeMount | 1、判断是否存在el 选项,若不存在则停止编译,直到调用vm.$mount(el) 才会继续编译2、优先级: render > template > outerHTML 3、 vm.el 获取的是挂载DOM 的 | |
beforeMount | 1、在此阶段可获取到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
意为服务端渲染