百度前端常考 vue 面试题(附答案)
怎么实现路由懒加载呢
这是一道应用题。当打包应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应组件,这样就会更加高效
回答范例
- 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。利用路由懒加载我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样会更加高效,是一种优化手段 
- 一般来说,对所有的路由都使用动态导入是个好主意 
- 给 - component选项配置一个返回- Promise组件的函数就可以定义懒加载路由。例如:- { path: '/users/:id', component: () => import('./views/UserDetails') }
- 结合注释 - () => import(/* webpackChunkName: "group-user" */ './UserDetails.vue')可以做- webpack代码分块
Vue complier 实现
- 模板解析这种事,本质是将数据转化为一段 - html,最开始出现在后端,经过各种处理吐给前端。随着各种- mv*的兴起,模板解析交由前端处理。
- 总的来说, - Vue complier是将- template转化成一个- render字符串。
可以简单理解成以下步骤:
- parse过程,将- template利用正则转化成- AST抽象语法树。
- optimize过程,标记静态节点,后- diff过程跳过静态节点,提升性能。
- generate过程,生成- render字符串
assets 和 static 的区别
相同点: assets 和 static 两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下,这是相同点
不相同点:assets 中存放的静态资源文件在项目打包时,也就是运行 npm run build 时会将 assets 中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在 static 文件中跟着 index.html 一同上传至服务器。static 中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是 static 中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于 assets 中打包后的文件提交较大点。在服务器中就会占据更大的空间。
建议: 将项目中 template需要的样式文件 js 文件等都可以放置在 assets 中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css 等文件可以放置在 static 中,因为这些引入的第三方文件已经经过处理,不再需要处理,直接上传。
Vue 的性能优化有哪些
(1)编码阶段
- 尽量减少 data 中的数据,data 中的数据都会增加 getter 和 setter,会收集对应的 watcher 
- v-if 和 v-for 不能连用 
- 如果需要使用 v-for 给每项元素绑定事件时使用事件代理 
- SPA 页面采用 keep-alive 缓存组件 
- 在更多的情况下,使用 v-if 替代 v-show 
- key 保证唯一 
- 使用路由懒加载、异步组件 
- 防抖、节流 
- 第三方模块按需导入 
- 长列表滚动到可视区域动态加载 
- 图片懒加载 
(2)SEO 优化
- 预渲染 
- 服务端渲染 SSR 
(3)打包优化
- 压缩代码 
- Tree Shaking/Scope Hoisting 
- 使用 cdn 加载第三方模块 
- 多线程打包 happypack 
- splitChunks 抽离公共文件 
- sourceMap 优化 
(4)用户体验
- 骨架屏 
- PWA 
- 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启 gzip 压缩等。 
v-show 与 v-if 有什么区别?
v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。
所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。
v-model 可以被用在自定义组件上吗?如果可以,如何使用?
可以。v-model 实际上是一个语法糖,如:
实际上相当于:
用在自定义组件上也是同理:
相当于:
显然,custom-input 与父组件的交互如下:
- 父组件将 - searchText变量传入 custom-input 组件,使用的 prop 名为- value;
- custom-input 组件向父组件传出名为 - input的事件,父组件将接收到的值赋值给- searchText;
所以,custom-input 组件的实现应该类似于这样:
参考 前端进阶面试题详细解答
v-model 是如何实现的,语法糖实际是什么?
(1)作用在表单元素上 动态绑定了 input 的 value 指向了 messgae 变量,并且在触发 input 事件的时候去动态把 message 设置为目标值:
(2)作用在组件上 在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件
本质是一个父子组件通信的语法糖,通过 prop 和 $.emit 实现。 因此父组件 v-model 语法糖本质上可以修改为:
在组件的实现中,可以通过 v-model 属性来配置子组件接收的 prop 名称,以及派发的事件名称。例子:
默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event。但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。js 监听 input 输入框输入数据改变,用 oninput,数据改变以后就会立刻出发这个事件。通过 input 事件把数据 $emit 出去,在父组件接受。父组件设置 v-model 的值为 input $emit过来的值。
Vue 组件间通信有哪几种方式?
 Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
(1)props / $emit  适用 父子组件通信
这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
(2)ref 与 $parent / $children 适用 父子组件通信
- ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
- $parent/- $children:访问父 / 子实例
(3)EventBus ($emit / $on)  适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
(4)$attrs/$listeners 适用于 隔代组件通信
- $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过- v-bind="$attrs"传入内部组件。通常配合 inheritAttrs 选项一起使用。
- $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过- v-on="$listeners"传入内部组件
(5)provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 
- 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。 
Vue 中的 key 到底有什么用?
key 是给每一个 vnode 的唯一 id,依靠 key,我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快,但会产生一些隐藏的副作用,比如可能不会产生过渡效果,或者在某些节点有绑定数据(表单)状态,会出现状态错位。)
diff 算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的 key 与旧节点进行比对,从而找到相应旧节点.
更准确 : 因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确,如果不加 key,会导致之前节点的状态被保留下来,会产生一系列的 bug。
更快速 : key 的唯一性可以被 Map 数据结构充分利用,相比于遍历查找的时间复杂度 O(n),Map 的时间复杂度仅仅为 O(1)
Vue computed 实现
- 建立与其他属性(如: - data、- Store)的联系;
- 属性改变后,通知计算属性重新计算 
实现时,主要如下
- 初始化 - data, 使用- Object.defineProperty把这些属性全部转为- getter/setter。
- 初始化 - computed, 遍历- computed里的每个属性,每个- computed属性都是一个- watch实例。每个属性提供的函数作为属性的- getter,使用- Object.defineProperty转化。
- Object.defineProperty getter依赖收集。用于依赖发生变化时,触发属性重新计算。
- 若出现当前 - computed计算属性嵌套其他- computed计算属性时,先进行其他的依赖收集
vuex 是什么?怎么使用?哪种功能场景使用它?
Vuex是一个专为Vue.js应用程序开发的状态管理模式。vuex就是一个仓库,仓库里放了很多对象。其中state就是数据源存放地,对应于一般 vue 对象里面的data里面存放的数据是响应式的,vue组件从store读取数据,若是store中的数据发生改变,依赖这相数据的组件也会发生更新它通过mapState把全局的state和getters映射到当前组件的computed计算属性
- vuex一般用于中大型- web单页应用中对应用的状态进行管理,对于一些组件间关系较为简单的小型应用,使用- vuex的必要性不是很大,因为完全可以用组件- prop属性或者事件来完成父子组件之间的通信,- vuex更多地用于解决跨组件通信以及作为数据中心集中式存储数据。
- 使用 - Vuex解决非父子组件之间通信问题- vuex是通过将- state作为数据中心、各个组件共享- state实现跨组件通信的,此时的数据完全独立于组件,因此将组件间共享的数据置于- State中能有效解决多层级组件嵌套的跨组件通信问题
vuex的State在单页应用的开发中本身具有一个“数据库”的作用,可以将组件中用到的数据存储在State中,并在Action中封装数据读写的逻辑。这时候存在一个问题,一般什么样的数据会放在State中呢? 目前主要有两种数据会使用vuex进行管理:
- 组件之间全局共享的数据 
- 通过后端异步请求的数据 
 
 包括以下几个模块
- state:- Vuex使用单一状态树,即每个应用将仅仅包含一个- store实例。里面存放的数据是响应式的,- vue组件从- store读取数据,若是- store中的数据发生改变,依赖这相数据的组件也会发生更新。它通过- mapState把全局的- state和- getters映射到当前组件的- computed计算属性
- mutations:更改- Vuex的- store中的状态的唯一方法是提交- mutation
- getters:- getter可以对- state进行计算操作,它就是- store的计算属性虽然在组件内也可以做计算属性,但是- getters可以在多给件之间复用如果一个状态只在一个组件内使用,是可以不用- getters
- action:- action类似于- muation, 不同在于:- action提交的是- mutation,而不是直接变更状态- action可以包含任意异步操作
- modules:面对复杂的应用程序,当管理的状态比较多时;我们需要将- vuex的- store对象分割成模块(- modules)
 
 
modules:项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理
 
 回答范例
思路
- 给定义 
- 必要性阐述 
- 何时使用 
- 拓展:一些个人思考、实践经验等 
回答范例
- Vuex是一个专为- Vue.js应用开发的 状态管理模式 + 库 。它采用集中式存储,管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- 我们期待以一种简单的“单向数据流”的方式管理应用,即状态 -> 视图 -> 操作单向循环的方式。但当我们的应用遇到多个组件共享状态时,比如:多个视图依赖于同一状态或者来自不同视图的行为需要变更同一状态。此时单向数据流的简洁性很容易被破坏。因此,我们有必要把组件的共享状态抽取出来,以一个全局单例模式管理。通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。这是 - vuex存在的必要性,它和- react生态中的- redux之类是一个概念
- Vuex解决状态管理的同时引入了不少概念:例如- state、- mutation、- action等,是否需要引入还需要根据应用的实际情况衡量一下:如果不打算开发大型单页应用,使用- Vuex反而是繁琐冗余的,一个简单的- store模式就足够了。但是,如果要构建一个中大型单页应用,- Vuex基本是标配。
- 我在使用 - vuex过程中感受到一些等
可能的追问
- vuex有什么缺点吗?你在开发过程中有遇到什么问题吗?
- 刷新浏览器, - vuex中的- state会重新变为初始状态。解决方案-插件- vuex-persistedstate
- action和- mutation的区别是什么?为什么要区分它们?
action中处理异步,mutation不可以
mutation做原子操作
action可以整合多个mutation的集合
mutation是同步更新数据(内部会进行是否为异步方式更新数据的检测)$watch严格模式下会报错
action异步操作,可以获取数据后调佣mutation提交最终数据
- 流程顺序:“相应视图—>修改 State”拆分成两部分,视图触发 - Action,- Action再触发Mutation`。
- 基于流程顺序,二者扮演不同的角色: - Mutation:专注于修改- State,理论上是修改- State的唯一途径。- Action:业务代码、异步请求
- 角色不同,二者有不同的限制: - Mutation:必须同步执行。- Action:可以异步,但不能直接操作- State
Vue-router 除了 router-link 怎么实现跳转
声明式导航
编程式导航
回答范例
- vue-router导航有两种方式:声明式导航和编程方式导航
- 声明式导航方式使用 - router-link组件,添加- to属性导航;编程方式导航更加灵活,可传递调用- router.push(),并传递- path字符串或者- RouteLocationRaw对象,指定- path、- name、- params等信息
- 如果页面中简单表示跳转链接,使用 - router-link最快捷,会渲染一个 a 标签;如果页面是个复杂的内容,比如商品信息,可以添加点击事件,使用编程式导航
- 实际上内部两者调用的导航函数是一样的 
ref 和 reactive 异同
这是Vue3数据响应式中非常重要的两个概念,跟我们写代码关系也很大
- ref接收内部值(- inner value)返回响应式- Ref对象,- reactive返回响应式代理对象
- 从定义上看 - ref通常用于处理单值的响应式,- reactive用于处理对象类型的数据响应式
- 两者均是用于构造响应式数据,但是 - ref主要解决原始值的响应式问题
- ref返回的响应式数据在 JS 中使用需要加上- .value才能访问其值,在视图中使用会自动脱- ref,不需要- .value;- ref可以接收对象或数组等非原始值,但内部依然是- reactive实现响应式;- reactive内部如果接收- Ref 对象会自动脱- ref;使用展开运算符(- ...)展开- reactive返回的响应式对象会使其失去响应性,可以结合- toRefs()将值转换为- Ref对象之后再展开。
- reactive内部使用- Proxy代理传入对象并拦截该对象各种操作,从而实现响应式。- ref内部封装一个- RefImpl类,并设置- get value/set value,拦截用户对值的访问,从而实现响应式
异步组件是什么?使用场景有哪些?
分析
因为异步路由的存在,我们使用异步组件的次数比较少,因此还是有必要两者的不同。
体验
大型应用中,我们需要分割应用为更小的块,并且在需要组件时再加载它们
回答范例
- 在大型应用中,我们需要分割应用为更小的块,并且在需要组件时再加载它们。 
- 我们不仅可以在路由切换时懒加载组件,还可以在页面组件中继续使用异步组件,从而实现更细的分割粒度。 
- 使用异步组件最简单的方式是直接给 - defineAsyncComponent指定一个- loader函数,结合 ES 模块动态导入函数- import可以快速实现。我们甚至可以指定- loadingComponent和- errorComponent选项从而给用户一个很好的加载反馈。另外- Vue3中还可以结合- Suspense组件使用异步组件。
- 异步组件容易和路由懒加载混淆,实际上不是一个东西。异步组件不能被用于定义懒加载路由上,处理它的是 - vue框架,处理路由组件加载的是- vue-router。但是可以在懒加载的路由组件中使用异步组件
Vue 中组件和插件有什么区别
1. 组件是什么
组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在 Vue 中每一个.vue 文件都可以视为一个组件
组件的优势
- 降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现 
- 调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单 
- 提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级 
2. 插件是什么
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
- 添加全局方法或者属性。如: - vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如 - vue-touch
- 通过全局混入来添加一些组件选项。如 - vue-router
- 添加 - Vue实例方法,通过把它们添加到- Vue.prototype上实现。
- 一个库,提供自己的 - API,同时提供上面提到的一个或多个功能。如- vue-router
3. 两者的区别
两者的区别主要表现在以下几个方面:
- 编写形式 
- 注册形式 
- 使用场景 
3.1 编写形式
编写组件
编写一个组件,可以有很多方式,我们最常见的就是 vue 单文件的这种格式,每一个.vue文件我们都可以看成是一个组件
vue 文件标准格式
我们还可以通过template属性来编写一个组件,如果组件内容多,我们可以在外部定义template组件内容,如果组件内容并不多,我们可直接写在template属性上
编写插件
vue插件的实现应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象
3.2 注册形式
组件注册
vue 组件注册主要分为全局注册与局部注册
全局注册通过Vue.component方法,第一个参数为组件的名称,第二个参数为传入的配置项
局部注册只需在用到的地方通过components属性注册一个组件
插件注册
插件的注册通过Vue.use()的方式进行注册(安装),第一个参数为插件的名字,第二个参数是可选择的配置项
注意的是:
注册插件的时候,需要在调用 new Vue() 启动应用之前完成
Vue.use会自动阻止多次注册相同插件,只会注册一次
4. 使用场景
- 组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 - App.vue
- 插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身 
简单来说,插件就是指对Vue的功能的增强或补充
Vue 组件之间通信方式有哪些
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。 Vue 组件间通信只要指以下 3 类通信 :
父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信
组件传参的各种方式
 
 组件通信常用方式有以下几种
- props / $emit适用 父子组件通信
- 父组件向子组件传递数据是通过 - prop传递的,子组件传递数据给父组件是通过- $emit触发事件来做到的
- ref与- $parent / $children(vue3废弃)适用 父子组件通信
- ref:如果在普通的- DOM元素上使用,引用指向的就是- DOM元素;如果用在子组件上,引用就指向组件实例
- $parent / $children:访问访问父组件的属性或方法 / 访问子组件的属性或方法
- EventBus ($emit / $on)适用于 父子、隔代、兄弟组件通信
- 这种方法通过一个空的 - Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件
- $attrs / $listeners(vue3废弃)适用于 隔代组件通信
- $attrs:包含了父作用域中不被- prop所识别 (且获取) 的特性绑定 (- class和- style除外 )。当一个组件没有声明任何- prop时,这里会包含所有父作用域的绑定 (- class和- style除外 ),并且可以通过- v-bind="$attrs"传入内部组件。通常配合- inheritAttrs选项一起使用
- $listeners:包含了父作用域中的 (不含- .native修饰器的)- v-on事件监听器。它可以通过- v-on="$listeners"传入内部组件
- provide / inject适用于 隔代组件通信
- 祖先组件中通过 - provider来提供变量,然后在子孙组件中通过- inject来注入变量。- provide / injectAPI 主要解决了跨级组件间的通信问题, 不过它的使用场景,主要是子组件获取上级组件的状态 ,跨级组件间建立了一种主动提供与依赖注入的关系
- $root适用于 隔代组件通信 访问根组件中的属性或方法,是根组件,不是父组件。- $root只对根组件有用
- Vuex适用于 父子、隔代、兄弟组件通信
- Vuex是一个专为- Vue.js应用程序开发的状态管理模式。每一个- Vuex应用的核心就是- store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 (- state)
- Vuex的状态存储是响应式的。当- Vue组件从- store中读取状态的时候,若- store中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 - store中的状态的唯一途径就是显式地提交 (- commit)- mutation。这样使得我们可以方便地跟踪每一个状态的变化。
根据组件之间关系讨论组件通信最为清晰有效
- 父子组件: - props/- $emit/- $parent/- ref
- 兄弟组件: - $parent/- eventbus/- vuex
- 跨层级关系: - eventbus/- vuex/- provide+inject/- $attrs + $listeners/- $root
下面演示组件之间通讯三种情况: 父传子、子传父、兄弟组件之间的通讯
1. 父子组件通信
使用
props,父组件可以使用props向子组件传递数据。
父组件vue模板father.vue:
子组件vue模板child.vue:
回调函数(callBack)
父传子:将父组件里定义的method作为props传入子组件
子组件向父组件通信
父组件向子组件传递事件方法,子组件通过
$emit触发事件,回调给父组件
父组件vue模板father.vue:
子组件vue模板child.vue:
2. provide / inject 跨级访问祖先组件的数据
父组件通过使用provide(){return{}}提供需要传递的数据
子组件通过使用inject:[“参数1”,”参数2”,…]接收父组件传递的参数
3. children 获取父组件实例和子组件实例的集合
- this.$parent可以直接访问该组件的父实例或组件
- 父组件也可以通过 - this.$children访问它所有的子组件;需要注意- $children并不保证顺序,也不是响应式的
4. listeners 多级组件通信
$attrs包含了从父组件传过来的所有props属性
$listeners包含了父组件监听的所有事件
5. ref 父子组件通信
6. 非父子, 兄弟组件之间通信
vue2中废弃了broadcast广播和分发事件的方法。父子组件中可以用props和$emit()。如何实现非父子组件间的通信,可以通过实例一个vue实例Bus作为媒介,要相互通信的兄弟组件之中,都引入Bus,然后通过分别调用 Bus 事件触发和监听来实现通信和参数传递。Bus.js可以是这样:
另一个组件也在钩子函数中监听on事件
7. $root 访问根组件中的属性或方法
- 作用:访问根组件中的属性或方法 
- 注意:是根组件,不是父组件。 - $root只对根组件有用
8. vuex
- 适用场景: 复杂关系的组件数据传递 
- Vuex 作用相当于一个用来存储共享变量的容器 
 
 - state用来存放共享变量的地方
- getter,可以增加一个- getter派生状态,(相当于- store中的计算属性),用来获得共享变量的值
- mutations用来存放修改- state的方法。
- actions也是用来存放修改 state 的方法,不过- action是在- mutations的基础上进行。常用来做一些异步操作
小结
- 父子关系的组件数据传递选择 - props与- $emit进行传递,也可选择- ref
- 兄弟关系的组件数据传递可选择 - $bus,其次可以选择- $parent进行传递
- 祖先与后代组件数据传递可选择 - attrs与- listeners或者- Provide与- Inject
- 复杂关系的组件数据传递可以通过 - vuex存放共享的变量
组件中写 name 属性的好处
可以标识组件的具体名称方便调试和查找对应属性
keep-alive 使用场景和原理
keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
- 常用的两个属性 include/exclude,允许组件有条件的进行缓存。 
- 两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态。 
- keep-alive 的中还运用了 LRU(最近最少使用) 算法,选择最近最久未使用的组件予以淘汰。 
v-if 和 v-for 哪个优先级更高
- 实践中不应该把 - v-for和- v-if放一起
- 在 - vue2中,- v-for的优先级是高于- v-if,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在- vue3中则完全相反,- v-if的优先级高于- v-for,所以- v-if执行时,它调用的变量还不存在,就会导致异常
- 通常有两种情况下导致我们这样做: 
- 为了过滤列表中的项目 (比如 - v-for="user in users" v-if="user.isActive")。此时定义一个计算属性 (比如- activeUsers),让其返回过滤后的列表即可(比如- users.filter(u=>u.isActive))
- 为了避免渲染本应该被隐藏的列表 (比如 - v-for="user in users" v-if="shouldShowUsers")。此时把- v-if移动至容器元素上 (比如- ul、- ol)或者外面包一层- template即可
- 文档中明确指出永远不要把 - v-if和- v-for同时用在同一个元素上,显然这是一个重要的注意事项
- 源码里面关于代码生成的部分,能够清晰的看到是先处理 - v-if还是- v-for,顺序上- vue2和- vue3正好相反,因此产生了一些症状的不同,但是不管怎样都是不能把它们写在一起的
vue2.x 源码分析
在 vue 模板编译的时候,会将指令系统转化成可执行的
render函数
编写一个p标签,同时使用v-if与 v-for
创建vue实例,存放isShow与items数据
模板指令的代码都会生成在render函数中,通过app.$options.render就能得到渲染函数
- _l是- vue的列表渲染函数,函数内部都会进行一次- if判断
- 初步得到结论: - v-for优先级是比- v-if 高
- 再将 - v-for与- v-if置于不同标签
再输出下render函数
这时候我们可以看到,v-for与v-if作用在不同标签时候,是先进行判断,再进行列表的渲染
我们再在查看下 vue 源码
源码位置:\vue-dev\src\compiler\codegen\index.js
在进行if判断的时候,v-for是比v-if先进行判断
最终结论:v-for优先级比v-if高
怎么监听 vuex 数据的变化
分析
- vuex数据状态是响应式的,所以状态变视图跟着变,但是有时还是需要知道数据状态变了从而做一些事情。
- 既然状态都是响应式的,那自然可以 - watch,另外- vuex也提供了订阅的 API:- store.subscribe()
回答范例
- 我知道几种方法: 
- 可以通过 - watch选项或者- watch方法监听状态
- 可以使用 - vuex提供的 API:- store.subscribe()
- watch选项方式,可以以字符串形式监听- $store.state.xx;- subscribe方式,可以调用- store.subscribe(cb),回调函数接收- mutation对象和- state对象,这样可以进一步判断- mutation.type是否是期待的那个,从而进一步做后续处理。
- watch方式简单好用,且能获取变化前后值,首选;- subscribe方法会被所有- commit行为触发,因此还需要判断- mutation.type,用起来略繁琐,一般用于- vuex插件中
实践
watch方式
subscribe方式:











 
    
评论