Vue核心知识
const app = new Vue({
template :`<div> {{name}} {{age}}</div>`,
data(){
return {
name: "张三",
age: 20
}
}
})
app.$mount('#root'); // 挂载
console.log(app.$data);
console.log(app.$props);
console.log(app.$el); // html的节点
console.log(app.$options); // 实例化vue时候的一整个对象, data props methods filters directives... 等
v-for中的key有什么用?
派发更新:vue/src/core/vdom/patch.js
在 updateChildren 方法中使用 sameVnode 需要比较key,
extend与minxins的差别?
mixins更倾向于一种方法在全局多次调用时候的封装, 且mixins支持多个 mixin 更灵活,各自的空间相互独立不污染.
extends只能一个;
有相同方法时候, 优先级 extends > mixins > 组件本身
在Vue中watch与created哪个先执行 ?
create // 执行时挂载阶段还没有开始,模版还没有渲染成html,所以无法获取元素。created钩子函数主要用来初始化数据。
beforeMount // 这一步的时候,模版已经在内存中编译好了,但是尚未挂载到页面中去。
computed // 是在DOM执行完成后立马执行(如:赋值)
mounted // 钩子函数一般用来向后端发起请求,拿到数据后做一些业务处理。该函数在模版渲染完成后才被调用。DOM操作一般是在mounted钩子函数中进行。
watch // 用于检测vue实例上数据的变动
默认加载的时候先computed再watch,不执行methods;等触发某一事件后,则是:先methods再watch。 methods方法有一定的触发条件,如click等。 如果watch 加了 immediate: true, 就是 watch 先执行,且watch会被提到最前, 否则就是created 先执行; 如果有watch的值依赖了computed,那computed依然会最前,因为vue默认先 computed 再执行watch;
异步组件与动态组件 路由组件懒加载 编程式组件 缓存组件
什么时候要使用异步组件?
当加载的组件庞大的时候 比如 地图或大屏等
当路由异步加载的时候 能避免首次渲染压力过大 导致卡顿闪屏
//`异步组件`
componets:{ myChild: ()=> import('@/components/mychild')}
// 路由组件懒加载
routes:[{path:'/',name:'login',component:import("../../components/Naviagter")}]
// `动态组件`
componentsList:['myChild','header','text',]
...
<div v-for=" (item,index) in componentsList">
<components :is='item'>
</div>
// 缓存组件 - exclude / include
// 当keepAlive 某组件之后, 该组件只会挂载销毁一次; 如果用v-if切换, 那会挂载与销毁N次;
<keep-alive>
<myChild/>
</keep-alive>
什么时候要使用beforeDestory
- 解绑自定义事件event.$off
- 清除定时器
- 关闭自定义Dom事件 比如window.scroll
当>>>无效的情况下该怎么处理?
可以使用::v-deep或者/deep/替代>>>
使用vnode描述一个dom结构
<div id="myDiv" class="div-class">
<p style="color:red">haha</p>
</div>
// vnode
{
tag:'div',
props:{
className:'',
id:'myDiv',
},
children:[
{
tag:'p',
props:{ style:'color:red'},
children:"haha"
}
]
}
vue为什么是异步渲染 $nextTick有什么用
异步渲染能避免多次修改多次渲染的弊端,提升了性能; nextTick存在的本质, 就是由于一个数据虽然会有多次修改,但 watcher 只会被载入事件队列一次. 而修改是同步操作, dom的更新是异步操作; 在主线程同步事件处理完毕后, 会比较 eventQueue 中的异步任务中的微任务与宏任务, 先执行微任务再执行宏任务. 只要eventQueue一有变化, 那么eventLoop会再次执行上述过程; 因此nextTick才能在dom彻底更新完毕后,再触发操作;
服务端渲染与预渲染有什么差异?
服务端渲染和预渲染的使用场景还是有较明显的区别的。预渲染的使用场景更多是我们所说的静态页面的形式。服务端渲染适用于大型的、页面数据处理较多且较为复杂的、与服务端有数据交互的功能型网站,一个明显的使用场景就是电商网站。
v-if 与 show谁的优先级更高? 以及区别
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。注意,v-show 不支持 template 元素,也不支持 v-else。
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
此外,当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中,所以不推荐同时使用v-if 和 v-for。
vue的.sync修饰符可以用表达式吗?为什么
# 从0到1自己构架一个vue项目,说说有哪些步骤、哪些重要插件、目录结构你会怎么组织
vue-cli实际上已经很成熟了,目录除了脚手架默认的,
1、一般会额外创建views,components,api,utils,stores等;
2、下载重要插件,比如axios,dayjs(moment太大),其他的会根据项目需求补充;
3、封装axios,统一api调用风格和基本配置;
4、如果有国际化需求,配置国际化;
5、开发,测试和正式环境配置,一般不同环境API接口基础路径会不一样;
v-model原理
原生input其实只是一个语法糖,是:bind="value"与@change="value = $event.target.value"的结合。 自定义组件的时候的v-model默认监听change事件和绑定value 的prop。
在使用计算属性的时,函数名和data数据源中的数据可以同名吗
可以同名,但data会覆盖methods。并且本就不该同名,同名说明你命名不规范。
然后解释为什么会覆盖,因为Props、methods、data、computed、watch都是在initState函数中被初始化的。初始化顺序就是我上面给出的顺序,本质上这些都是要挂载到this上面的,你如果重名的话,后面出现的属性自然而然会覆盖之前挂载的属性了。如果你的eslint配置比较严格的话,同名是编译不通过的。
怎么给vue定义全局的方法
有两种方法:
- 在一个文件中定义所有需要挂载的方法名,存在数组中;接着遍历这个数组,挨个挂载到prototype上
- 使用mixin混入到全局
forEach、filter、map、some、every、find、findIndex、reduce间的区别
forEach能够遍历 但没有返回值filter会返回一切符合条件的对象map最适合做的事是映射,生成原始数据的特征信息, map是有返回值的some会返回布尔值, item中只要有符合条件的就会返回true, 否则为falseevery会返回布尔值, 只有每一个item都符合条件才返回true, 否则为falsefind返回第一个符合条件的itemfindIndex返回第一个符合条件item的索引值reduce
1.x和2.x有什么区别
vue1.0的数据绑定完全依赖于数据侦测,使用Object.defineProperty方法使数据去通知相应watch,改变dom结构。vue2.0引入了虚拟dom,只通知到组件,提升了颗粒度。
生命周期详解
- 在
new Vue()之后, 机制会初始化事件与生命周期钩子;第一个钩子beforeCreate被触发时, data methods, props, computed watch 还有$el $ref都是不能被访问的; 因为源码的initState方法还没有被执行, 这个initState方法的作用就是初始化和挂载 options的各种属性; - 第二个生命周期钩子为created, 此时
props data methods computed watch都已经初始化完毕, initState干完一系列初始化的事情, 但此时$el $ref 依然不可使用. 真实dom还没有被挂载, 真实dom生成的前提是有虚拟dom, 但现在render函数都没有执行; - created之后先判断是否有$el挂载点, 如果el属性存在就直接挂载 ,否则等待后续手动挂载
- render函数开始行动, vue会判断是否有template模板的同时是否有render方法,如果有render 那就会避开template编译过程;
但是 vue-cli不同 他是无论你有没有render都会把你的template进行打包编译,直接覆盖render部分; - render函数走完 那么虚拟dom也转化完真实dom结束, el也挂载完毕就可以准备挂载了; 也就是beforeMount这个阶段;
- beforeMount接下来就是mounted, 把render准备好的dom挂载el上,现在所有东西都准备完毕,各种api最大程度开放,可以开始项目业务的处理了;
- 当我们执行业务操作的时候,会做各种动作,那么beforeUpdate与updated登场,这两个其实没啥好说的,无非就是虚拟dom diff算法 render函数 和 patch方法的不停重复
- 在页面快离开的时候 beforeDestory依然能够调用各种方法和api不受影响,但是这里可以开始准备善后工作,比如提前关闭定时器 ws任务 webWorkder等
- 当destoryed钩子正式执行,所有的组件被销毁 api无法使用 数据清空, 只剩下一个dom空壳.
两种方式:
1、组件外部加修饰符.navtive
2、组件内部声明$emit('自定义事件')
promise
一个 Promise 必然处于以下几种状态之一:
待定(*pending*):初始状态,既没有被兑现,也没有被拒绝
已兑现(*fulfilled*):意味着操作成功完成。
已拒绝(*rejected*):意味着操作失败。
-
all() 接收一个数组 ,如果有可能的话会执行完所有的promise;但会随着第一个promise的失败而终止,并在catch中可以捕获到第一个失败Promise的信息;如果都成功 就会在成功回调里得到所有成功的resolve信息;
-
allSettled() 接收一个数组 ,无论如何都会执行完所有的Promise, 并会在成功回调中(不是catch)返回所有的结果
-
any() 接收一个数组 ,只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
-
race() 接收一个数组, 会以第一个返回的promise作为最终状态;
-
resolve() 将promiseState 置为 fulfilled
-
reject() 将promiseState 置为 rejected
-
finally() 原型链上的方法 无论成功还是失败 都必然会返回的一个结果
如何手写一个Promise?
首先既然能够被调用那一定是一个Object, 所以我们需要定义一个class, 让其后期能够被实例化;
因为promise 是有状态的, 那么我们需要在构造器中定义一些类属性,用于记录一些状态值;
完全大写的status 用于记录当前的状态;完全大写的reason 会记录失败原因 ; 完全大写的result用于存储返回的数据值;
这里需要有改变status的方法, status 一共只有3个状态,分别定义resolve 与 reject 表示 将status 的状态定义为fulfiled 与 reject
then 是支持链式调用的方法,** 其内部同样有两种方法分别处理着fulfilled与reject;
分析下vue项目本地开发完成后部署到服务器后报404是什么原因呢?
1.检查nginx配置,是否正确设置了资源映射条件;
2.检查vue.config.js中是否配置了publicPath,若有则检查是否和项目资源文件在服务器摆放位置一致。
3.使用了history模式,而后端又没有进行相关资源配置。
在 url 输入到浏览器后经历了哪些事情
当一串地址进入浏览器地址框后,首先会尝试进行dns解析, dns解析也会分为几个步骤;
首先是浏览器缓存,找不到就操作系统缓存, 接下来就是host文件,如果还找不到就会本地的dns服务器,最后会互联网dns服务器,还找不到那就404了;
dns解析完毕就要开始封装http报文请求包
然后基于http的基础再次封装tcp/ip包 这其中有三次握手
接下来开始发送, 当服务端接收到客户端请求 服务端要做一个http包返回
接下来就是4次挥手
渲染流程结束
你都做过哪些Vue的性能优化
1. 合理区分v-if 与 v-show 的使用, 另外keep-alive 有时候才更加适合
2. 组件导入时, 有大消耗的组件如地图或大屏 或性能业务的, 应该异步加载
3. 路由使用异步加载不要同步
4. v-for 一定不要把 index 作为 key
5. 不需要响应式的数据不要放到 data 中 因为会走observeClass
v-model默认的触发条件是input事件,加了.lazy修饰符之后,v-model会在change事件触发的时候去监听
V3.0和v2.0的区别
- 支持TS
- proxy代替Object.definePrototype()
- setup函数
- ref reactive
- 去掉$listerner
- v-if v-for 优先级调换
- 生命周期有变化
- 函数式编程
- 优化diff算法
- 组件api
- hooks
- 多个根节点 Fragment
- 静态标识:hoistStatic, 事件缓存
- 删除了一些方法和属性
e.target 和 e.currentTarget
currentTarget:事件绑定的元素
target:鼠标触发的元素
扩展-> 时间委托
响应式原理
Vue2响应式与Vue3响应式
-
v2响应式的核心实际上依赖于Object.defineProperty(target,key,{}) 这个api.
-
其中第一个参数是对哪一个对象进行监控,第二个参数说的则是具体的属性,重点在于第三个参数. 在第三个参数中, 他包含了很多配置项.
1.比如configurable 该属性的类型不能在数据属性和访问器属性之间更改
2.enumable 是否可以被枚举
3.writable 是否可以被 如果与属性相关联的值可以使用[赋值运算符] 更改,则为
true。默认值为false。 重要的是get 和 set , 在set接受到value后,get就会被触发,从而return 出新的值 使得发生变化.
1.我们可以从set中动手, 但大概需要考虑如下情况:传递进来的数据类型 究竟是不是object 如果不是就直接return; 2.如果是还需要考虑递归的问题,以防止对象是多层结构, 从而无法让变量更新.
如果希望让一个新对象被监听(this.$set()这个方法 ), 那么需要加两个属性, 一个是dep,另一个是watcher
缺点
1)对象上原本不存在属性无法被劫持 如果往对象上添加属性无法做到数据响应,这时vue2就提供了额外的方法实现这一逻辑,也就是用this.$set( this.data,'job','teacher' )替代this.data.job='teacher',才能实现响应式
(2)默认递归会有性能问题 这样的代码会遍历出现在数据源里的所有的对象,即使没有使用到的数据也会被监测,如果这个对象嵌套很深,那么递归就会浪费性能。
(3)改变数组的length属性无效 data.job.length=3也是对数组进行了操作,但并不会被检测到
依赖收集
在Observe类中, 有value, dep ; Dep本身是个类,相当于data与watcher之间的桥梁
虚拟Dom 与 Diff
- 虚拟dom实际上就是一串能用以表示真实dom树的 json
- 真实dom就是一系列的语义标签
ES6 新特性
内置指令
- v-once 元素本身与其子孙组件只会渲染一次,后续若有变动也只会被视为静态内容(有时处理)
- v-cloak 解决初始化渲染的页面闪动问题
- v-on 即@click v-on:click @change v-on:change
- v-if/v-else render 函数中是三元表达式
- v-html 富文本渲染
- v-pre 指定性地跳过本元素的渲染 以加快速度
他们是css box-sizing的两个值,是两种不同的盒模型,用content-box,元素的宽高就等于width和height的值,他的宽高不受padding,border的影响,而用border-box,元素的宽高是受padding,border影响的,元素实际宽度是等于width-padding左右-border左右,也就是当width不变,padding/border发生变化时,内容的宽高是会相应的变化的。
Vue 传递参数的方式
插槽 路由传参
$ref $emit props
$parent $children
$EventBus
$attrs => <components v-bind="$attrs"></components>( 在孙组件直接获取到 祖组件传递给父的所有参数)
$listeners => 在孙组件直接使用$emit 出发祖组件
$provide-inject 组件开发
Vuex