mpvue和vue的区别
大的项目用vue还是react好,为什么?
vue与react区别
1、监听数据变化的实现原理不同
Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能
React 默认是通过比较引用的方式(diff)进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的VDOM的重新渲染
2、数据流的不同
Vue中默认是支持双向绑定的
React 不支持双向绑定,一直提倡的是单向数据流,他称之为 onChange/setState()模式。
3、模板渲染方式的不同
React 是通过JSX渲染模板
而Vue是通过一种拓展的HTML语法进行渲染
对于MVVM的理解
Model 代表数数据层,在Vue中指的是 data属性
View 代表视图层,在Vue中指的是 html
ViewModel 代表视图模型层,ViewModel 通过双向数据绑定把 View 层和 Model层连接了起来,在Vue中指的是vue的实例
mvc和mvvm的区别
1.View传送指令到Controller。
2.Controller完成业务逻辑后改变Model状态。
3.Model将新的数据发送至View,用户得到反馈。
MVVM通过数据来显示视图层而不是节点操作
MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验
数据劫持的优点
无需显式调用。vue利用发布者订阅者模式+数据劫持,可以直接通知变化并更新视图。比如data.name = '渣渣辉‘;后直接触发变更,而比如Angular的脏值检测则需要显式调用markForCheck
可精确得知变化数据:劫持了属性setter之后,当属性值改变,我们可以精确知道变化的内容newVal,因此在这一部分不需要多余的diff操作。否则我们只知道数据变化了,但是不知道具体哪些数据发生变化,所以需要大量的diff来找出变化值。这是额外的性能损耗。
双向数据绑定原理
请详细说下你对vue生命周期的理解
1、初始化显示
-
beforeCreate(): 不能通过this读取data中的数据
在beforeCreate阶段,在实例初始化之后立即同步调用,此时还没有数据代理和数据观察,vue实例的挂载元素el和数据对象data都为undefined -
实现数据代理与数据监视
-
created(): 在此开始可以通过this读取data中的数据
在created阶段,vue实例的数据对象data有了,el还没有 -
实现在内存中去编译/解析模板
-
beforeMount(): 不能通过ref来读取页面中的标签
vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。 -
将编译的DOM挂载到页面上显示
-
mounted(): 在此开始可以通过ref来读取页面中的标签——初始化渲染页面,只会调用一次
在mounted阶段,vue实例挂载完成,$el 可以访问( $ el 是虚拟DOM编译之后的真实DOM),data.message成功渲染。
2、更新状态:更新了data中的数据
- beforeUpdate(): 读取界面内容是老的
- 更新界面
- updated(): 读取界面内容是新的
3、销毁vue实例: vm.$destory()
- beforeDestory(): 死亡前调用
- destoryed(): 死亡后调用
常用的生命周期方法
* mounted() / created(): 绑定自定义事件监听、发送ajax请求, 启动定时器等异步任务
* beforeDestory(): 做收尾工作, 如: 清除定时器
什么是vue生命周期?
答: Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
vue生命周期的作用是什么?
答:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑
vue生命周期总共有几个阶段?
答:它可以总共分为8个阶段:创建前/后、载入前/后、更新前/后、销毁前/销毁后。
第一次页面加载会触发哪几个钩子?
答:会触发下面这几个beforeCreat、created、beforeMount、mounted 。
DOM 渲染在哪个周期中就已经完成?
答:DOM 渲染在 mounted 中就已经完成
data为什么是函数
组件会复用
当我们使用组件的时候,虽然data是在原型链上被创建的,所有的实例化的组件都共享一份data;而函数是返回对象的独立拷贝
vuex
vuex是一个专为vue.js应用程序开发的状态管理器,它采用集中式存储管理应用的所有组件的状态,并且以相应的规则保证状态以一种可以预测的方式发生变化。
state: vuex使用单一状态树,用一个对象就包含来全部的应用层级状态
mutation: 更改vuex中state的状态的唯一方法就是提交mutation
action: action提交的是mutation,而不是直接变更状态,action可以包含任意异步操作
getter: 相当于vue中的computed计算属性
vue-router的使用
引入 vueRouter ,声明使用vueRouter插件,new vueRouter并配置路由
在main.js中注册路由
vue-router hash 模式和 history 模式有什么区别?
1、url 展示上,hash 模式有“#”,history 模式没有
2、刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求,没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由。
3、兼容性。hash 可以支持低版本浏览器和 IE
vue-router hash 模式和 history 模式是如何实现的
1、hash 模式:
这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发hashchange
这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange来实现更新页面部分内容的操作
2、history 模式:
history 模式的实现,主要是 HTML5 标准发布的两个 API,pushState
和 replaceState
,这两个 API 可以改变 url 地址且不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作。
history模式刷新会出现404
hash模式:
路径中带#: http://localhost:8080/#/home/news
发请求的路径: http://localhost:8080 项目根路径 = = > 返回index.html
响应: 返回的总是index页面 = = > path部分(/home/news)被解析为前台路由路径
history模式:
路径中不带#: http://localhost:8080/home/news
发请求的路径: http://localhost:8080/home/news ==> 后台处理不了,返回404
响应: 404错误
解决思路:
需要在服务器配置,如果URL匹配不到任何静态资源,就跳转到默认的index.html。, path部分(/home/news)被解析为前台路由路径
解决: 添加配置
devServer: historyApiFallback: true, // 任意的 404 响应都被替代为 index.html
output: publicPath: '/', // 引入打包的文件时路径以/开头
v-if 与 v-show的区别?
当v-if 条件为false时,会将元素从DOM中删除,
当v-if 条件为true时,会在DOM中新键一个元素;
当v-show条件为false时,会display : none
当v-show条件为true时,会display : block
常用的事件修饰符有哪些
.prevent : 阻止事件的默认行为 event.preventDefault()
.stop : 停止事件冒泡 event.stopPropagation()
v-on 可以监听多个方法吗
可以。
<button v-on="{mouseenter: onEnter,mouseleave: onLeave}">鼠标进来1</button>
vue初始化页面闪动问题
v-cloak指令
<div class='app' v-cloak>
</div>
[v-cloak] {
display: none!important;
}
隐藏未编译的双大括号标签
使用过哪些vue组件库,都用到了哪些组件库中的哪些组件
element-ul vant
$ route 和 $ router的区别?
$ route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
而$ router是路由器对象包括了路由的跳转方法(push , replace , back ,go),钩子函数等
< keep-alive>< /keep-alive>的作用是什么?
< keep-alive></ keep-alive> 可以在组件切换时,保存其包裹的组件的状态,使其不被销毁,防止多次渲染。有 include (包含组件)、exclude (排除组件)这两个属性
< keep-alive include="About,Home">
<router-view></router-view>
</ keep-alive>
比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用< keep-alive>< /keep-alive>进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染。
其拥有两个独立的生命周期钩子函数 actived
和deactived
,使用 keep-alive 包裹的组件在切换时不会被销毁,而是缓存到内存中并执行 deactived 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
组件的 data 为什么要写成函数形式?
Vue 的组件都是可复用的,一个组件创建好后,可以在多个地方复用,组件每复用一次,data 就应该复用一次,每一处复用组件的 data 改变应该对其他复用组件的数据不影响。
如果data值为对象,因为对象是引用类型,组件可能会被多个实例同时引用。如果data值为对象,将导致多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响。
data为函数,每个实例都有自己独立的对象,实例之间可以互不影响的改变data属性值。
v-model原理
为什么 v-for 和 v-if 不建议用在一起?
当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费。
这种场景建议使用 computed,先对数据进行过滤。
computed 和 watch 有什么区别?
computed 计算属性,是依赖其他属性的计算值,并且有缓存,只有当依赖的值变化时才会更新。
watch 是在监听的属性发生变化时,在回调中执行一些逻辑。
所以,computed 适合在模板渲染中,某个值是依赖了其他的响应式对象甚至是计算属性计算而来,而 watch 适合监听某个值的变化去完成一段复杂的业务逻辑。
computed 和 methods 有什么区别?
计算属性是基于他们的响应式依赖进行缓存的,只有在依赖发生变化时,才会计算求值,而使用 methods,每次都会执行相应的方法。
Vue中Computed的特点?
computed会拥有自己的watcher
,它内部有个属性dirty
开关来决定 computed 的值是需要重新计算还是直接复用之前的值。,假设计算属性sum依赖响应式属性count
1、在sum第一次进行求值的时候会读取响应式属性count,收集到这个响应式数据作为依赖。并且计算出一个值来保存在自身的 value上,把dirty设为false,接下来在模板里再访问sum就直接返回这个求好的值value,并不进行重新的求值。
2、而count发生变化了以后会通知sum所对应的watcher把自身的dirty属性设置成 true,这也就相当于把重新求值的开关打开来了。这个很好理解,只有count变化了,sum才需要重新去求值。
3、那么下次模板中再访问到this.sum的时候,才会真正的去重新调用sum函数求值,并且再次把dirty设置为 false。
vue中常见性能优化?
- 代码层面的优化
v-if 和 v-show 区分使用场景
computed 和 watch 区分使用场景 参考
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
长列表性能优化:数组冻结Object.freeze
事件的销毁
图片资源懒加载
路由懒加载
第三方插件的按需引入
优化无限列表性能
服务端渲染 SSR or 预渲染
- Webpack 层面的优化
Webpack 对图片进行压缩
减少 ES6 转为 ES5 的冗余代码
提取公共代码
模板预编译
提取组件的 CSS
优化 SourceMap
构建结果输出分析
Vue 项目的编译优化
- 基础的 Web 技术的优化
开启 gzip 压缩
浏览器缓存
CDN 的使用
使用 Chrome Performance 查找性能瓶颈
Vue中是如何检测数组和对象变化?
首先对于data中的属性是对象时候,vue会遍历对象的所有属性进行依赖收集,如果对象属性还是对象,那么会继续递归遍历这个对象的所有属性进行依赖收集。
对于数组,会遍历数组的每一项进行依赖收集,如果数组的单项是数组或者对象,会继续进行递归依赖收集。所以我们修改数组的任何一项或者任何一项的任意属性,都会触发对应的setter方法,触发更新操作。
同时,对于数组,vue重写了数组原型链的方法,在调用原型链方法的时候,会自动触发更新操作。其中,如果调用的是会增加数组项的push、unshift和splice这三个方法,那么在触发更新前,会对新增的项进行依赖收集。
为何Vue采用异步渲染?
当然也可以采用同步渲染,只是同步渲染的话,对数据的每次修改,都会立刻引发dom的修改,而对dom的操作是比较费时的,从性能的角度考虑,vue选择了异步更新。在数据变化下,无论是引起的重绘渲染还是重排渲染,都有可能会在性能消耗之下造成低效的页面性能,甚至造成加载卡顿问题。
实际上,Vue在默认情况下,每次触发某个数据的setter方法后,对应的Watcher对象其实会被push进一个队列queue 中,在下一个tick的时候将这个队列queue全部拿出来run(Watcher对象的一个方法,用来触发patch操作) 一遍。
Ajax请求放在哪个生命周期中?
官方实例的异步请求是在mounted生命周期中调用的,而实际上也可以在created生命周期中调用。
何时需要beforeDestory?
当需要在组件销毁前做一些善后收尾工作的时候,比如清除计时器,可以在这个回调里写。
Vue中v-html会导致哪些问题?
v-html
指令最终调用的是innerHTML
方法将指令的value
插入到对应的元素里,这很容易导致XSS跨站脚本攻击,一般情况下我们只对可信内容使用HTML插值,绝不要对用户提供的内容插值。
如果一定要用可以用<pre>
标签代替<div>
之类,主要是利用<pre>
的一个属性:被包围在<pre>
元素中的文本通常会保留空格和换行符,并且文本也会呈现为等宽字体。
站在项目全局的角度,如果不放心可以使用xss的npm包,在webpack里配置对所有innerHTML方法进行覆盖,对innerHTML方法的值外面包上一层xss方法。
vue-router中导航守卫有哪些?
全局前置/钩子:beforeEach、beforeResolve、afterEach
路由独享的守卫:beforeEnter
组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
完整的导航解析流程如下:
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
action和mutation的区别?
Mutation中是同步函数和Action中是异步函数
更改state的唯一方法是通过提交 mutation,它能被devtools 追踪状态变化
Action中不进行状态的直接更改,而是通过commit触发mutation
mutation的触发通过store.commit来进行
action的触发通过store.dispatch进行
为什么 Vuex 的 mutation 中不能做异步操作?
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。vuex中的数据是多个组件共享的,如果mutation支持异步操作,多个组件同时异步修改,导致数据错乱,没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
vue中模板编译原理?
模板编译的作用是生成渲染函数,通过执行渲染函数生成最新的vnode,最后根据vnode进行渲染。
模板———>模板编译——>渲染函数——>vnode——>用于界面
此过程可以分成两个步骤:先将模板解析成AST(abstract syntax tree,抽象语法树),然后使用AST生成渲染函数。
由于静态节点不需要总是重新渲染,所以生成AST之后,生成渲染函数之前这个阶段,需要做一个优化操作:遍历一遍AST,给所有静态节点做一个标记,这样在虚拟DOM中更新节点时,如果发现这个节点有这个标记,就不会重新渲染它。
所以,在大体逻辑上,模板编译分三部分内容:
1、将模板解析成AST
2、遍历AST标记静态节点
3、使用AST生成渲染函数
这三部分内容在模板编译中分别抽象出三个模块实现各自的功能:解析器、优化器和代码生成器。
标记静态节点有两个好处:
1、每次重新渲染的时候不需要为静态节点创建新节点,也就是静态节点的解析器不需要重新创建
2、在Virtual DOM中patching的过程可以被跳过
优化器的实现原理主要分两步:
1、用递归的方式将静态节点添加static属性,用来标识是不是静态节点
2、标记所有静态根节点(子节点全是静态节点就是静态根节点)
vue.use()
用于安装 Vue.js 插件。
如果插件是一个对象,自动调用 install 方法。
如果插件是一个函数,该函数就是 install 方法。
install 方法调用时,会将 Vue 作为参数传入。当 install 方法被同一个插件多次调用,插件将只会被安装一次。
第一,判断这个插件是否被注册过,如果已经注册了,不允许重复注册。
第二,如果传入的插件是一个对象,且对象中有install属性,且此属性对应的值是一个函数… … 如果传入的插件是一个函数 … …
nextTick实现原理?
Watch中的deep:true是如何实现的?
Watch中的deep、immediate作用和区别
deep,默认值是 false,代表是否深度监听。
immediate:true代表如果在 wacth 里声明了之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,最初绑定的时候是不会执行的,要等到 监听的属性 改变时才执行监听计算。
虚拟DOM和真实DOM的区别,虚拟DOM的优缺点
虚拟DOM不会进行排版与重绘操作
虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分
用js模拟一颗dom树,放在浏览器内存中.当你要变更时,虚拟dom使用diff算法进行新旧虚拟dom的比较,将变更放到变更队列中,反应到实际的dom树,减少了dom操作.
虚拟DOM将DOM树转换成一个JS对象树,diff算法逐层比较,删除,添加操作,但是,如果有多个相同的元素,可能会浪费性能,所以,react和vue-for引入key值进行区分.
vue过渡动画怎么实现
观察者和发布者订阅有什么区别
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。
订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Topic),当发布者(Publisher)发布该事件(Publish topic)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。
简述vue中diff算法原理?
在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。
v-for中为什么要用key?
- 循环遍历的时候为每一个个体加上key属性,且key的属性是唯一值
- 在循环中每次内容发生改变的时候Vue会根据key的标识去对比变化前后dom及数据的值,如果没有发生过改变就不去重新渲染,提高渲染效率,局部渲染
组件间通信
插槽
内置指令与自定义指令
1、内置指令
v-text、v-html、v-if、v-else、v-show、v-for、v-on、v-bind、v-model、ref、v-cloak
2、自定义指令
a、注册全局指令
Vue.directive('my-directive', function(el, binding){
el.innerHTML = binding.value.toupperCase()
})
b、注册局部指令
directives : {
'my-directive' : function (el, binding) {
el.innerHTML = binding.value.toupperCase()
}
}
3)使用指令
v-my-directive='xxx'
vue3.0你知道有哪些改进?
- Vue3采用了TS来编写
- 支持 Composition API
- Vue3中响应式数据原理改成proxy
- vdom的对比算法更新,只更新vdom的绑定了动态数据的部分
vue高阶特性
1、watch(handler、deep、immediate)
2、Vue 在更新 DOM 时是异步执行的。
3、父组件监听子组件的生命周期
// Parent.vue
<Child @hook:mounted="doSth" />
methods:{
doSth(){
//some codes here
}
}
4、样式穿透——深度选择器
<style scoped>
.a >>> .b { /* ... */ }
</style>
有些像 Sass 之类的预处理器无法正确解析 >>>
。这种情况下你可以使用 /deep/
或 ::v-deep
操作符,这两者都是 >>>
的别名,实现同样的功能。
5、路由传参,props解耦
routes: [{
path: '/user/:id',
component: User,
props: (route) => ({
id: route.query.id
})
}]
6、异步加载组件
const Home = () => import('../pages/Home/Home.vue');
7、keep-alive 组件缓存
图片预加载
https://www.jianshu.com/p/1618cb183d28
项目中引用的图片都直接引用静态目录下的图片,如果图片放到src\assets目录下,项目中引用图片的相对路径,在vue打包的时候会改变图片的引用名称,这样就无法实现图片预加载的效果
所以,需要创建loading.vue文件,编写preload()方法
<script>
export default {
data () {
return {
count: 0,
}
},
mounted: function() {
this.preload()
},
methods: {
preload: function() {
let imgs = [
"static/img/back.gif",
"static/img/correct.png",
"static/img/cover.gif",
"static/img/errExpress.png",
"static/img/error.png",
"static/img/ply.png",
"static/img/q1.png",
"static/img/q1a.png",
"static/img/q1b.png",
"static/img/q1c.png",
"static/img/q1d.png",
"static/img/share.png",
"static/img/start.png",
"static/img/stop.png"
]
for (let img of imgs) {
let image = new Image()
image.src = img
image.onload = () => {
this.count++
}
}
},
},
}
</script>
preload()方法中先将所有要加载的图片路径放到一个数组中,然后遍历数组中的每个图片路径,创建Image对象,将图片的路径绑定到image.src,实现图片加载,然后在图片加载的onload回调中记录图片加载的数量count,用作实现图片资源加载百分比的计算。
高阶组件
高阶组件就是一个函数,传给它一个组件,它返回一个新的组件。
受控组件
表单元素依赖于状态,表单元素需要默认值实时映射到状态的时候,就是受控组件,这个和双向绑定相似.
受控组件,表单元素的修改会实时映射到状态值上,此时就可以对输入的内容进行校验.
受控组件只有继承React.Component才会有状态.
受控组件必须要在表单上使用onChange事件来绑定对应的事件.
非受控组件
非受控组件即不受状态的控制,获取数据就是相当于操作DOM(ref)。
参考链接: