2023-09-01
VueJS
00
请注意,本文编写于 329 天前,最后修改于 185 天前,其中某些信息可能已经过时。

目录

Vue通信有几种方式
说一下scoped Css
对Vue设计原则的理解
对Vue组件化的理解
Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?
watch与computed异同
nextTick的实现原理
Vue中的路由钩子有那些
Vuex 严格模式及Plugin
Vue中key的作用
Vue.mixin合并策略
Vue为什么要求组件模板只能有一个根元素
Vue中的性能优化方式
Vue递归组件的使用
Vue的插槽
Vue组件与WebComponent异同
vue异步组件
vue2错误处理
vue2 keep-alive的缓存策略
Vue生命周期
Vue3 新特性

记录Vue2常见问题

Vue通信有几种方式

  1. 父子组件通信props $emit
  2. 当嵌套层级比较多 使用 provide/inject 或者 element-ui的广播broadcast/dispatch
  3. 非嵌套关系 用 事件总线 即EventEmmitter机制, 在复杂一些可以用tapable
js
Vue.$prototype.$bus = new Vue(); this.$bus.on() // 监听 this.$bus.emit() // 触发
  1. 自己实现一个发布订阅模式
  2. $refs$parent$children$attrs$listeners 也算 不推荐,存在耦合
  3. Vuex

说一下scoped Css

<style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素。
其原理是通过使用 PostCSS 来实现以下转换:

html
<template> <div class="red" data-v-f3f3eg9>hi</div> </template> <style> .red[data-v-f3f3eg9] { color: red; } </style>
  1. 作用范围: 当前组件中的元素及子组件根元素样式
  2. 如果想覆盖子元素的样式 使用 深度作用选择器
  • css >>>
  • scss /deep/ or ::v-deep

对Vue设计原则的理解

  1. 渐进式JavaScript框架
    • 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用Vue的核心库只关注视图层,不仅易 于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使 用时,Vue 也完全能够为复杂的单页应用提供驱动。
  2. 易用、灵活和高效
    • 易用性 vue提供数据响应式声明式模板语法基于配置的组件系统等核心特性。这些使我们只需要关注应用 的核心业务即可,只要会写js、html和css就能轻松编写vue应用。
    • 灵活性 渐进式框架的最大优点就是灵活性,如果应用足够小,我们可能仅需要vue核心特性即可完成功能;随着应用规模不断扩大,我们才可能逐渐引入路由、状态管理、vue-cli等库和工具,不管是应用体积还是 学习难度都是一个逐渐增加的平和曲线。 c. 高效性 超快的虚拟 DOMdiff 算法使我们的应用拥有最佳的性能表现。追求高效的过程还在继续,vue3中引入Proxy对数据响应式改进以及编译器中对于静态内容编译的改进都会让vue更加高效。

对Vue组件化的理解

  1. 组件是独立可复用的代码组织单元。组件系统是Vue核心特性之一
  2. 组件化开发能大幅提高应用开发效率、测试性、复用性等;
  3. 组件使用按分类有:页面组件、业务组件、通用组件;合理的划分组件,有助于提升应用性能;
  4. vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函数,它们基于VueComponent,扩展于Vue;
  5. vue中常见组件化技术有:属性prop,自定义事件,插槽,mixins、指令等,它们主要用于组件通信、扩展等;
  6. 组件应该是高内聚、低耦合的;
  7. 遵循单向数据流的原则。
  8. 组件创建顺序自上而下 ,组件挂载顺序自下而上

Vue组件data为什么必须是个函数而Vue的根实例则没有此限制?

  1. Vue组件可能存在多个实例,如果使用对象形式定义data,则会导致它们共用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数形式定义,在initData时会将其作为工厂函数返 回全新data对象,有效规避多实例之间状态污染问题。
  2. 而在Vue根实例只能有一个,所以不需要担心这种情况。

watch与computed异同

  1. api方面
    • computed 参数可以是函数或对象,如果是函数转换为对象的存值器,返回一个值
    • watch 参数可以是函数或对象,函数会转换为对象{immediate: false, deep: false}
  2. 功能方面
    • computed不支持异步,有缓存作用;
    • watch没有缓存、支持异步操作
  3. 使用场景
    • 有多个值到一个值用 computed
    • 处理副作用异步操作使用 watch

nextTick的实现原理

  1. Vue的nextTick其本质是对JavaScript执行原理EventLoop的一种应用。
    nextTick的核心是利用Promise、MutationObserver、setImmediate、setTimeout原生方法来模拟宏微任务的实现。
  2. 引入nextTick的原因是避免强制布局(同步执行访问dom),提升性能

Vue中的路由钩子有那些

  1. 全局钩子函数
    • beforeEach (to,from,next) 常用于登录权限相关验证
      • next:路由控制参数
      • next():如果一切正常,则调用这个方法进入下一个钩子
      • next(false):取消导航(即路由不发生改变)
      • next('/login'):当前导航被中断,然后进行一个新的导航
      • next(error):如果一个Error实例,则导航会被终止且该错误会被传递给router.onError()
    • afterEach(to,from) 路由改变后的钩子
  2. 路由配置中的钩子
    • beforeEnter(to, from, next)
  3. 组件内的钩子函数
    • beforeRouteEnter (to,from,next)
      • 方法内部不能访问this ,如果想使用this则在next方法回调里使用
    • beforeRouteUpdate (to,from,next)
    • beforeRouteLeave (to,from,next)
  4. 路由监测变化
    • watch '$route'

Vuex 严格模式及Plugin

  • 开启严格模式 strict: true 直接修改state会控制台报红
  • 使用插件 plugin: [myPlugin]
ts
export default store => { // 初始化时从localStorage获取数据 if(localStorage) { const user = JSON.parse(localStorage.getItem('user')) if (user) { store.commit('user/login') store.commit('user/setUsername', user.username) } } // 用户状态发生变化时缓存之 store.subscribe((mutation, state) => { if (mutation.type.startsWith('user/')) { localStorage.setItem('user', JSON.stringify(state.user)) } else if (mutation.type === 'user/logout') { localStorage.removeItem('user') } }) }

Vue中key的作用

  1. key的作用主要是为了高效的更新VDOM,其原理是vuepatch过程中通过key可以精准判断两 个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
  2. 另外,若不设置key可能引发一些隐蔽的bug 如列表更新或相同标签名元素的过渡切换

Vue.mixin合并策略

  • 优先级 组件自身 > 组件混入 > 全局混入
  • 执行顺序 全局混入 、组件混入、组件自身
  • 默认策略
    • 数据对象在内部会进行递归合并,相同属性会覆盖
    • 同名钩子函数将合并为一个数组,因此都将被调用。
    • 值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象,同名会覆盖

Vue为什么要求组件模板只能有一个根元素

  1. new Vue(el:'#app') 只能指定一个入口
  2. 单文件组件中,template下的元素div。其实就是"树"状数据结构中的"根"。
  3. diff算法要求的

Vue中的性能优化方式

  1. 懒加载, 路由懒加载, 组件懒加载么, 三方组件按需引入,图片懒加载
  2. 长列表性能优化 如果只是纯数据展示不发生改变,可以不做响应式
js
export default { data() => ({ list: [] }), async created() { const list = axios.get('/api/list'); this.list = Object.freeze(list); } }

如果是大数据长列表,可采用虚拟滚动,只渲染少部分区域的内容
参考vue-virtual-scrollervue-virtual-scroll-list

html
<recycle-scroller class="items" :items="items" :item-size="24" > <template v-slot="{ item }"> <FetchItemView :item="item" @vote="voteItem(item)" /> </template> </recycle-scroller>
  1. 事件的销毁
  • Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。
  • 定时器要销毁
  • 总线通信注意销毁
  1. 无状态的组件标记为functional
html
<template functional> <div class="cell"> <section v-else class="off"></section> </div> </template> <script> export default { // 无状态组件不能有props和data // props: ['value'] } </script>
  1. 子组件分割
html
<template> <div> <ChildComp/> </div> </template> <script> export default { components: { ChildComp: { methods: { heavy () { /* 耗时任务 */ } }, render (h) { return h('div', this.heavy()) } } } } </script>
  1. 重复利用 key
  2. v-showv-if
  3. keep-alive合理使用
  4. computed替代methodwatch
  5. 使用is动态组件

Vue递归组件的使用

递归与不同组件使用无异,唯一要注意的是递归首要考虑终止条件,那递归组件必须与v-if配合使用

Vue的插槽

父组件中定义子组件中使用
默认插槽<template v-slot>
可简写<my-comp v-slot>
<slot>
具名插槽v-slot:header<slot name='footer'/>
作用域插槽v-slot:default="slotProps"<slot v-bind:user='user'>{{user.name}}</slot>
  • 插槽的作用域在当前组件
  • 后备内容<slot>后备内容</slot>

Vue组件与WebComponent异同

以下内容摘抄自官网

我们认为Vue和WebComponents是互补的技术。可以把自定义元素继承到Vue应用中,也可以使用Vue来构建和分发自定义元素。

自定义元素和Vue组件的相同点

  • 数据传递、事件发射、生命周期管理的可重用组件。

但是Vue组件显然更强大一下

  • 一个声明式、高效的模版系统
  • 一个响应式的,利于跨组件逻辑提取和状态管理系统
  • SEO方面Vue组件更高效
  • 其他: Vue支持作用域插槽,WC没有该功能

vue异步组件

js
components: { 'async-com': () => ({ component: new Promise(resolve => { setTimeout(() => { resolve(import('./components/AsyncComp.vue')) }, 2000) }), loading: LoadingComp, error: ErrorComponent, // error: {render: () => h('div', 'error')} timeout: 3000, delay: 200, timeout: 3000 }) }

vue2错误处理

  1. 全局错误处理
js
Vue.config.errorHandler = function (err, vm, info) { // 可以捕获的错误 // 1. 组件生命周期钩子里的错误 2.2.0+ // 2. 捕获 Vue 自定义事件处理函数内部的错误 2.4.0+ // 3. 会捕获 v-on DOM 监听器内部抛出的错误 和 Promise 错误 2.6.0+ }
  1. 组件错误处理 errorHandler(err: Error, vm: Component, info: string) => ?boolean

在捕获一个来自后代组件的错误时被调用。 可以通过返回 false 阻止事件的传播

vue2 keep-alive的缓存策略

LRU:Least Recently Used,最近最少使用算法

代码实现

Vue生命周期

  • beforeCreate之前做的事情
    • initLifecycle() 创建$parent $children
    • initEvents() 事件监听
    • initRender() 渲染模版
  • beforeCreate之后做的事情
    • initInjections()
    • initState
    • initProvide
  • keep-alive钩子
    • activated
      • 调用时机为首次挂载
      • 以及每次从缓存中被重新插入时
    • deactivated
      • 在从 DOM 上移除、进入缓存
      • 以及组件卸载时调用

Vue3 新特性

根据尤大的PPT总结,Vue3.0改进主要在以下几点:

  1. 更快
    • 虚拟DOM重写 Diff算法优化一方面更符合业务诉求另一方面利用最大上升子序列尽可能减少dom的移动
    • 优化slots的生成
    • 静态树提升
    • 静态属性提升
    • 基于Proxy的响应式系统 惰性劫持
  2. 更小:通过摇树优化核心库体积
  3. 更容易维护:TypeScript + 模块化
  4. 更加友好
    • 跨平台:编译器核心和运行时核心与平台无关,使得Vue更容易与任何平台(Web、Android、iOS)一起使用 (依赖注入的思想)
  5. 更容易使用
    • 改进的TypeScript支持,编辑器能提供强有力的类型检查和错误及警告
    • 更好的调试支持
    • 独立的响应化模块
    • Composition API 关注点分离 useXXX 优于 mixins

vue2 也提供了一些方法来避免OptionsAPI天生的逻辑碎片化 如下示例

js
mounted: function () { var picker = new Pikaday({ field: this.$refs.input, format: 'YYYY-MM-DD' }) this.$once('hook:beforeDestroy', function () { picker.destroy() }) }

本文作者:郭敬文

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!