Vue技术栈
为什么Vue被称为”渐进框架”?
Vue框架本身只关注动态展现数据和与用户交互部分,它本身并不做诸如路由, 状态数据管理, ajax请求等方面, 如果应用功能开发需要, 可以根据需要逐渐引入vue的插件或其它第三方库来扩展,如: 引入vue-router增加路由功能,引入vuex管理状态, 引入axios发ajax请求与后台交互。
Vue 中的声明式渲染是什么?
需求:向div中插入一个动态的文本内容,点击按钮更新div中的内容
1. 使用原生JS, 不使用vue的编码
1). HTML
<div id="app"></div>
<button id="btn">更新</button>
2). JavaScript
// 动态显示初始值
const greeting = "Hello there!";
const appDiv = document.getElementById("app");
appDiv.innerHTML = greeting;
// 点击按钮更新div内容
document.getElementById("btn").onClick = function () {
appDiv.innerHTML = appDiv.innerHTML + "--";
}
3). 说明: 手动根据ID获取div插入动态指定的文本内容,根据ID获取button绑定监听, 在回调函数中手动去更新div中的内容
2. 使用Vue实现同样的效果
1). Template
<div>{{greeting}}</div>
<button @click="update">更新</button>
2). JavaScript
new Vue({
el: '#app',
data: {
greeting: 'Hello There!';
},
methods: {
update () {
this.greeting = this.greeting + '--';
}
}
});
3). 说明:在模板中使用插值语法, 声明显示greeting数据就能自动动态显示;点击按钮要更新界面,只需要更新数据,界面就会自动更新
3. Vue的声明式体现在下面2点
界面能根据初始数据做初始渲染显示, 不需要手动操作DOM来显示;
要更新界面,只需要更新数据即可, 不用手动操作DOM来更新界面。
作用域样式(scoped css)与深度作用选择器
1.作用域样式是什么?
1). <style scoped>
2). 让组件的样式限定在当前组件作用域(范围)内有效, 对其它外部或内部组件无效
2.组件不加scoped声明的问题
一个组件的样式可以影响到外部或内部的所有任何组件, 如果不做限制就会出现样式效果的问题
3.组件声明使用scoped
1). 标签变化: 组件内所有标签包括子组件的根标签都添加了自定义data属性来标识,如: data-v-2e8d0da5
2). 选择器的变化:组件内所有样式选择器的都右边都会添加上自定义data属性的条件, 如: .test2 .t3[data-v-2e8d0da5]
3). 结果:一旦加上scoped后, 样式选择器不再能匹配上子、孙组件的标签(根标签除外)
4.深度作用选择器
1). 作用
使用了scoped后, 还能修改子/孙组件的样式, 尤其是第三方UI组件库组件
2). 语法
// 原生css: 使用 >>>
.test2 >>> .t2 {
color: red;
}
// 预编译器: 使用/deep/
.test2 {
/deep/ .t2 {
color: red;
}
}
3)原理:
● 内部是由vue-loader来进行编译处理的
● 编译打包后样式选择器上的data属性条件加在了最左边第一层选择器上: .test2[data-v-2e8d0da5] .t3
● 结果: 对子/孙组件标签就没有此属性的条件, 就可以匹配上子/孙组件内的标签, 进而改变其样式效果
MVC和MVVM的区别
1. MVC
1). Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
通常模型对象负责在数据库中存取数据。
2). View(视图)是应用程序中处理数据显示的部分。
通常视图是依据模型数据创建的。
3). Controller(控制器)是应用程序中处理用户交互的部分。
通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
最典型的MVC就是JSP + serverlet + javabean的模式
2. MVVM
1). M: Model模型, 也就是包含数据的js对象(只包含属性, 不包含方法)
2). V: View视图,动态显示模型对象中的数据显示界面
3). VM: ViewModel视图模型, 本质是一个vm, 通过vm读取model中的数据显示到view上, 同时view输入数据改变, vm也可以将输入数据保存到model中
MVVM的优势: 不用亲自操作DOM, 数据是响应式的, 一旦数据变化, 自动更新界面
说说你对 SPA 单页面的理解,它的优缺点分别是什么?
SPA( single page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。
1. 优点:
1). 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
2). 基于上面一点,SPA 相对对服务器压力小;
3). 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
2. 缺点:
1). 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
2). 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
3). SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
v-show 与 v-if 有什么区别?
1. v-if 条件为假时, 条件模板块不会进行编译解析, 也就是不创建真实DOM结构, 切换为真是创建一个新的真实DOM结构显示。
2. v-show在条件为假时, 条件模板块也会进行编译解析, 也就是会创建真实DOM结构, 只是通过display为none不显示, 当切换为真时, 只是通过去掉display的none值来显示出来, 不需要重新创建真实DOM结构。
3. 所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则更适用于需要非常频繁切换条件的场景。
class 与 style 如何动态绑定?
1. class 可以通过字符串或对象语法或数组语法进行动态绑定
1). 字符串语法:
<div v-bind:class="myClass"></div>
data: {
myClass: 'classA classB'
}
2). 对象语法:
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
data: {
isActive: true,
hasError: false
}
3). 数组语法:
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
2. style 用对象语法或数组语法进行动态绑定:
1). 对象语法:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
2). 数组语法:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
data: {
baseStyles: {
color: 'red',
fontSize: '13px'
},
overridingStyles: {
color: 'blue',
width: '30px'
}
}
怎样理解 Vue 的单向数据流?
1. 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
2. 每次父级组件发生更新时,子组件中所有的 prop 都将会更新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,可以通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。
3. 有两种常见的试图改变一个 prop 的情形 :
1). 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
2). 这个 prop 以一种原始的值传入且需要进行转换。 在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
computed 和 watch 的区别和运用的场景?
1. computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
2. watch: 更多的是「观察/监视」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
3. 运用场景:
1). 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
2). 当我们需要在数据变化时执行异步( 如发送ajax请求 )时,应该使用 watch。
直接给一个数组项赋值,Vue 能检测到变化吗?
1. 问题分析
由于Vue并没有给每个元素的下标属性添加监视,Vue 不能检测到直接通过下标设置新的元素值。也就是执行vm.items[indexOfItem] = newValue时,Vue检测不到变化,界面就无法更新。
2. 解决办法一:
1). 使用数组的splice方法 (比较方便易懂, 推荐使用)
2). 原理: Vue重写了数组一系列更新元素的方法, 在更新元素后就会去更新界面
// 此时执行的就不是数组原生的splice, 而是Vue重写后的
vm.items.splice(indexOfItem, 1, newValue)
3. 解决办法二:
1). 使用Vue.set()或vm.$set()
2). 原理:方法内部设置好元素数据后,就会去更新界面
Vue.set(vm.items, indexOfItem, newValue)
vm.$set(vm.items, indexOfItem, newValue)
谈谈你对 Vue 生命周期的理解?
1. 生命周期是什么?
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 重新渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
2. 各个生命周期的作用
生命周期 | 描述 |
beforeCreate | 组件实例被创建之初,组件的属性生效之前 |
created | 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用 |
beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用, 但页面还没有挂载显示, $ref还不可用 |
mounted | 已挂载, 也就是已经显示到界面上了, 此时可能通过$ref访问到页面元素或组件对象 |
beforeUpdate | 组件数据更新之后, 界面更新之前调用, 如果此时获取的界面是旧的界面 |
updated | 界面更新之后调用, 如果此时获取界面主是新的界面了 |
activited | keep-alive 专属,组件激活时调用 |
deactivated | keep-alive 专属,组件失活时调用 |
beforeDestory | 组件销毁前调用, 一般在此做一些收尾的工作, 如: 取消定时器, 解绑监听 |
destoryed | 组件销毁后调用 |
3. 生命周期示意图
Vue 的父组件和子组件生命周期钩子函数执行顺序?
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
1. 加载渲染过程
父 beforeCreate –> 父 created –> 父 beforeMount –> 子 beforeCreate
–> 子 created –> 子 beforeMount –> 子 mounted –> 父 mounted
2. 子组件更新过程
父 beforeUpdate –> 子 beforeUpdate –> 子 updated –> 父 updated
3. 父组件更新过程
父 beforeUpdate –> 父 updated
4. 销毁过程
父 beforeDestroy –> 子 beforeDestroy –> 子 destroyed –> 父 destroyed
在哪个生命周期内调用异步请求?
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,用得较多的是created与mounted
1. created() 是发ajax请求最早的时机, 但无法读取初始界面(如果需要不选)
2. mounted() 是发ajax请求最晚的时机(在初始界面显示之后), 但它可以发请求前读取初始界面内容
在什么阶段才能访问操作DOM?
在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM,一般通过ref来得到DOM对象进而做相应的操作。
谈谈你对 keep-alive 的了解?
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
1. 一般结合路由一起使用,用于缓存路由组件;
2. 提供 include 和 exclude 属性,两者都支持字符串和正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
3. 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件离开时,触发钩子函数 deactivated。
组件中 data 为什么是一个函数?
1. 如果组件中的data选项是对象,那这个组件的每个实例都是通过配置的data属性得到的, 那此组件的多个标签实例data共用一个data数据对象, 一旦有一个组件对象改变了data中的数据, 其它组件对象界面也会更新
2. 如果组件中 data 选项是一个函数,那每个组件对象都是通过执行配置的data函数得到的data数据对象, 那此组件的多个组件对象的data都不是同一个对象,一个组件对象修改其data内数据, 其它组件对象不会更新,因为data中的数据没有变化。
v-model 的原理?
1. v-model用在html标签上:
1). text 和 textarea 类型 input 标签 使用 value 属性和 input 事件;
2). checkbox 和 radio 类型 input标签 使用 checked 属性和 change 事件;
3). select 标签使用 value 属性和 input 事件。
以 input 表单元素为例:
<input v-model='something'>
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">
2. v-model用在组件标签上: 则会传入 value 属性 和绑定 input 事件监听
父组件:
<ModelChild v-model="message"></ModelChild>
<!-- 等价于 -->
<ModelChild :value="message" @input="message=$event"></ModelChild>
data () {
return {
message: 'Hello atguigu'
}
}
子组件:
<div>
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
</div>
props: ['value']
Vue 组件间通信有哪几种方式?
1. 方式一: props
1). 实现父向子通信: 属性值是非函数
2). 实现子向父通信: 属性值是函数
2. 方式二: vue自定义事件
1). 用来实现子组件向父组件通信
2). 相关语法:
● 父组件中绑定自定义事件监听:
<Child @eventName="callback">
● 子组件中分发事件
this.$emit('eventName', data)
3. 方式三: 全局事件总线
1). 实现任意组件间通信
2). 编码:
● 将入口js中的vm作为全局事件总线对象:
beforeCreate() {
Vue.prototype.$bus = this
}
● 分发事件/传递数据的组件:
this.$bus.$emit('eventName', data)
● 处理事件/接收数据的组件:
this.$bus.$on('eventName', (data) => {})
4. 方式四: v-model
1). 实现父子之间相互通信
2). 组件标签上的v-model的本质
● 父组件:
<CustomInput v-model="name"/>
<!-- 等价于 -->
<CustomInput :value="name" @input="name=$event"/>
● 子组件:
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
props: ['value']
5. 方式五: .sync
1). 实现父子之间相互通信
2). 组件标签的属性上使用.sync的本质
● 父组件:
<child :money.sync="total"/>
<!-- 等价于 -->
<Child :money="total" @update:money="total=$event"/>
data () {
return {
total: 1000
}
}
● 子组件:
<button @click="$emit('update:money', money-100)">花钱</button>
props: ['money']
6. $attrs与$listeners
1). $attrs
● 实现当前组件的父组件向当前组件的子组件通信
● 它是包含所有父组件传入的标签属性(排除props声明, class与style的属性)的对象
● 使用: 通过 v-bind=”$attrs” 将父组件传入的n个属性数据传递给当前组件的子组件
2). $listeners
● 实现当前组件的子组件向当前组件的父组件通信
● $listeners是包含所有父组件传入的自定义事件监听名与对应回调函数的对象
● 使用: 通过v-on=”$listeners” 将父组件绑定给当前组件的事件监听绑定给当前组件的子组件
7. $refs, $children, $parent
1). $refs
● 实现父组件向指定子组件通信
● $refs是包含所有有ref属性的标签对象或组件对象的容器对象
● 使用: 通过 this.$refs.child 得到子组件对象, 从而可以直接更新其数据或调用其方法更新数据
2). $children
● 实现父组件向多个子组件通信
● $children是所有直接子组件对象的数组
● 使用: 通过this.$children 遍历子组件对象, 从而可以更新多个子组件的数据
3). $parent
● 实现子组件向父组件通信
● $parent是当前组件的父组件对象
● 使用: 通过this.$parent 得到父组件对象, 从而可以更新父组件的数据
8. Vuex
1). 实现任意组件间通信
2). Vuex 是一个专为 Vue 应用程序设计的管理多组件共享状态数据的 Vue 插件
● 任意组件都可以读取到Vuex中store的state对象中的数据
● 任意组件都可以通过dispatch()或commit()来触发store去更新state中的数据
● 一旦state中的数据发生变化, 依赖于这些数据的组件就会自动更新
说说你对Vuex 的理解?
1. Vuex 是一个专为 Vue 应用程序开发的状态管理模式插件库。每一个 Vuex 应用的核心就是 store(仓库)。store 就是一个容器,它包含着你的应用中大部分的状态 ( state )。
2. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
3. 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
4. 主要包括以下几个模块:
1). State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
2). Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
3). Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。
4). Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
5). Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
说说 SSR 的优缺点?
1. 服务端渲染的优点:
1). 更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
2). 更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
2. 服务端渲染的缺点:
1). 更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
2). 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive – CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
vue-router 路由模式有几种?
1. vue-router 有三种路由模式:hash、history、abstract
2. 三种路由模式的说明如下:
1). hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
2). history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
3). abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?
1. hash 模式的实现原理
1). 早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如http://www.atguigu.com/#/search,它的 location.hash 的值为 ‘#/search’:
2). hash 路由模式的实现主要是基于下面几个特性:
● URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
● hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
● 可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
● 我们可以使用 hashchange 事件来监听 hash 值的变化,从而进行路由跳转。
2. history 模式的实现原理
1). HTML5 提供了 History API 来实现 URL 的变化。其中最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
2). history 路由模式的实现主要基于存在下面几个特性:
● pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
● 我们可以使用 popstate 事件来监听 url 的变化,从而进行路由跳转;
● history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发路由跳转。
Vue 是如何实现数据绑定的?
Vue 主要通过以下 4 个步骤来实现数据绑定的:
1. 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
2. 实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
3. 实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
4. 实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
Proxy 与 Object.defineProperty 优劣对比
1. Proxy 的优势如下:
1). Proxy 可以直接监听对象而非属性;
2). Proxy 可以直接监听数组的变化;
3). Proxy 有多达13种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
4). Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
5). Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;
2. Object.defineProperty 的优势如下:
兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。
虚拟 DOM 的优缺点?
1. 优点
1). 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
2). 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
3). 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
2. 缺点
1). 首次显示要慢些: 首次渲染大量DOM时,由于多了一层虚拟DOM的计算, 会比innerHTML插入慢
2). 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中 无法进行针对性的极致优化。
简述虚拟 DOM 实现原理?
虚拟 DOM 的简单实现原理主要包括以下 3 部分:
1. 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
2. diff 算法 — 比较两棵虚拟 DOM 树的差异;
3. pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
说说你对Vue 中的 key 的理解?
1. 虚拟DOM的key的作用?
1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用
2). 详细的说: 当列表数组中的数据发生变化生成新的虚拟DOM后, React进行新旧虚拟DOM的diff比较
● key没有变
item数据没变, 直接使用原来的真实DOM
item数据变了, 对原来的真实DOM进行数据更新
● key变了
销毁原来的真实DOM, 根据item数据创建新的真实DOM显示(即使item数据没有变)
2. key为index的问题
1). 添加/删除/排序 => 产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低
2). 如果item界面还有输入框 => 产生错误的真实DOM更新 ==> 界面有问题
注意: 如果不存在添加/删除/排序操作, 用index没有问题
3. 解决:
使用item数据的标识数据作为key, 比如id属性值
你有对 Vue 项目进行哪些优化?
1. 代码层面的优化
1). v-if 和 v-show 区分使用场景
2). computed 和 watch 区分使用场景
3). v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
4). 长列表性能优化
5). 事件的销毁
6). 图片资源懒加载
7). 路由懒加载
8). 第三方插件的按需引入
9). 优化无限列表性能
10). 服务端渲染 SSR or 预渲染
2. 层面的优化
1). Webpack 对图片进行压缩
2). 减少 ES6 转为 ES5 的冗余代码
3). 提取公共代码
4). 模板预编译
5). 提取组件的 CSS
6). 优化 SourceMap
7). 构建结果输出分析
8). Vue 项目的编译优化
3. 基础的 Web 技术的优化
1). 开启 gzip 压缩
2). 浏览器缓存
3). CDN 的使用
4). 使用 Chrome Performance 查找性能瓶颈
对于即将到来的 Vue3.0 特性你有什么了解的吗?
Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:
1. 监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:
1). 检测属性的添加和删除;
2). 检测数组索引和长度的变更;
3). 支持 Map、Set、WeakMap 和 WeakSet。
2. 模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
3. 对象式的组件声明方式
1)Vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。
4. 其它方面的更改
1). 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
2). 支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
3). 基于 treeshaking 优化,提供了更多的内置功能。
Vue与Angular的区别
1. 相同点
1). 都支持指令:内置指令和自定义指令。
2). 都支持过滤器:内置过滤器和自定义过滤器。
3). 都支持双向数据绑定。
4). 都不支持低端浏览器。
2. 不同点:
1). Angular的学习成本高,比如增加了Dependency Injection特性,而Vue本身提供的API都比较简单、直观。
2). 在性能上,Angular依赖对数据做脏检查,所以Watcher越多越慢。
3). Vue使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。
与React的区别
1. 相同点:
1). React采用特殊的JSX语法,Vue在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。
2). 内部都使用虚拟DOM与DOM Diff算法来提升效率
3). 中心思想相同:一切都是组件,组件实例之间可以嵌套。
4). 都提供合理的钩子函数,可以让开发者定制化地去处理需求。
5). 都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载。
6). 在组件开发中都支持mixins的特性。
2. 不同点:
1). React: 基于JSX编码, 单向数据绑定, 不能直接更新状态数据必须setState()指定新数据
2). Vue: 基于模板语法, 双向数据绑定, 直接更新data数据11.35. Vue中mixin与extend区别
$route和 $router的区别是什么?
1. $router是VueRouter的实例,包含了路由跳转的方法、钩子函数等。
2. $route 是路由信息对象,每一个路由都会有一个route对象,包含path,params,hash,query,fullPath,matched,name等路由信息属性。
路由组件如何响应路由参数的变化
1. 方式一: 用watch 检测
// 监听当前路由发生变化的时候执行
watch: {
$route(to, from){
console.log(to.path)
// 对路由变化做出响应
}
}
2. 方式二: 组件内导航钩子函数
beforeRouteUpdate(to, from, next){
// to do somethings
}