vue2x 复习笔记

223 阅读3分钟

vue2x

对Vue的理解

当创建一个 Vue 实例时,Vue 会遍历选项中的 data 对象,并用 Object.defineProperty 将这些属性转为getter/setter,使 Vue 能够在内部追踪到这些依赖,当依赖被修改就能及时通知更新。

每个组件实例都有一个对象的 watcher,在组件渲染过程中会把用到的数据记录为依赖,当依赖被修改时会触发 setter 通知组件重新渲染。

生命周期

  • 生命周期分为 4 部分,创建 挂载 更新 销毁
  • 创建 beforeCreate created
  • 挂载 beforeMount mounted
  • 更新 beforeUpdate updated
  • 销毁 beforeDestroy destroyed
- 当实例被创建后,首先会调用 initLifecycle initEvents initRender 做一些初始化操作。
- 在 beforeCreate 被执行时 data\methods\watch\computed 都不能访问,可以在这里展示一个loding。
- 从 beforeCreate 到 created 过程中完成了数据的绑定、计算属性和方法的挂载、watch/event的事件回调。
- 在 created 被执行时,可以操作data\methods, 但挂载尚未开始,$el还不能使用。可以在这里结束loding, 进行简单的异步请求,如果操作 DOM 可以放在 Vue.nextTict 的回调中。
- 从 created 到 beforeMount 过程中首先会判断是否有 el&template 属性,然后开始解析模板数据和指令。
- 在 beforeMount 被执行时,$el 创建完成,尚未挂载到 DOM 中。
- 从 beforeMount 到 mounted 过程中完成了 DOM 的渲染和挂载,el 被 vm.$el 替换。可以在发起异步请求,操作DOM。
- 当数据更新时会调用 beforeUpdate, 这时页面尚未更新,可以获取到最新的 DOM。
- 当 DOM 渲染完成后会调用 Updated,这时候可以执行一些依赖 DOM 的操作。
- 在实例被销毁之前会调用 beforeDestroy,这时候实例还能使用,可以做一些提示(保存提示、删除提示)。
- 当实例被销毁之后会调用 destroyed, 实例的所有指令被解绑、事件监听被移除,所有子实例被销毁。

Object.defineProperty

  • Object.defineProperty 是 ES5 的语法,IE8+ 才支持。
  • 无法监听对象属性的添加和移除。
  • 无法监听数据的变动
    • 利用索引直接设置一个数组项
    • 修改数组的长度
    • 使用改变原数组的方法 如 push pop shift unshift reverse sort splice

在 Vue 中可以通过 Vue.set 或 vm.set 添加新的属性。 Vue 将 push pop shift unshift reverse sort splice 进行重写,所以能监听的数组的变化。

双向数据绑定原理

  • observer 对 data 的数据进行遍历,使用 Object.defineProperty 把属性转为 getter/setter
  • compile 用于解析模板中指令, 比如{{}}\v-bian\v-on\v-model
  • watcher 用于订阅数据的属性,然后执行相应的监听回调。
  • dep 用于存储 watcher,当属性被修改时会触发执行 deps 里的所有 watcher 的 update 方法
  • mvvm 入口文件 整合 observer compile

双向数据绑定的原理和实现

虚拟DOM & diff

nextTick 原理

组件通信

  • eventBus

    const EventBus = new Vue();
    // 定义监听事件
    EventBus.$on(event, callback)
    // 定义回调事件
    EventBus.$emit(eventName, data);
    
  • 父子组件通信

    <!-- 父组件通过 v-bind 绑定 articles 属性并传值给子组件 -->
    <!-- 通过 v-on 绑定自定义事件接收事件变化通知 -->
    <com-article :articles="articleList" @onEmit="onEmit"></com-article>
    
    // 子组件
    export default {
      // 通过props接收父组件的数据
      props: ['articleList'],
      methods: {
        onEmit(idx) {
          // 通过 $emit 触发 onEmit 事件
          this.$emit('onEmit', index)
        }
      }
    }
    
  • $parent$children

    // 在父组件调用子组件的属性并修改数据
    this.$children[0].message; // 获取
    this.$children[0].message = 'hello world'; // 修改
    
    // 在子组件中调用
    this.$parent.msg; // 获取
    // this.$parent.msg = 'hello world';
    
  • ref$refs

    <!-- 定义ref -->
    <com-article ref="articleRef" @onEmit="onEmit"></com-article>
    
    // 父组件通过 $refs 调用子属性
    const son = this.$refs.articleRef
    son.name; // 获取子组件的属性值
    son.sayHello; // 调用子组件方法
    
  • provide / inject

  • $attrs / $listeners

  • localStorage / sessionStorage

  • Vuex 状态管理库

组件通信方式

Vuex

Vuex 是一个状态管理库,用于集中存储和管理应用中的状态。

  • state 用于存储状态和数据
    • mapState
  • getter store 的计算属性,和 vue 的计算属性一样,会被缓存,依赖改变会自动重新计算
    • mapGetters
  • mutations 定义改变 state 状态的方法,只能用 store.commit 提交 mutation。
    • mapMutations
  • actions 定义异步操作的方法,通过 store.dispatch 触发 action, 在 action 中使用 commit 提交 mutation。
    • mapActions
  • modules 用于将 store 分解成模块,每个模块都有 state getter mutations actions, 可以添加 namespace , 使模块有更高的封装度和复用性。

vue-route

路由守卫

路由守卫分为 全局守卫(3个) 路由守卫(1个) 组件守卫(3个)

  • 全局守卫有 beforeEach beforeResolve afterEach
  • 路由守卫有 beforeEnter
  • 组件守卫有 beforeRouteEnter beforeRouteUpdate beforeRouteLeave

当点击导航时,首先判断是否时从其他路由跳转过来的,如果是则调用失活组件的 beforeRouteLeave,如果不是则调用全局的 beforeEach,接下来判断组件是否被渲染过,如果被渲染过则调用组件的 beforeRouteUpdate,如果不是则调用路由的 beforeEnter,然后解析组件,当组件/异步组件解析完成后调用组件的 beforeRouteEnter,接下来调用全局的 beforeResolve,当导航被确认之后,调用全局 afterEach, 然后触发DOM更新,最后执行 beforeRouteEnter 的 next 回调函数,至此导航解析完成。

路由应用场景

  • beforeEach
    • 添加统计
    • 动态改变页面的 title
    • 权限管理
    • 加载进度条(nprogress)
  • beforeRouteUpdate
    • 重新加载数据
  • beforeRouteLeave
    • 清楚定时器
    • 关闭提示或者保存提醒
  • afterEach
    • 结束加载进度条

权限管理

权限分为4中 接口权限 路由权限 菜单权限 按钮权限

  • 接口权限 用户登录时,后端用 jwt(json web token) 或者其他方式生成一个 token 返回给前端,前端存储在 localStorage 里, 请求接口时在 headers 添加一个 token 属性,并把token值赋值给headers.token, 这样每次请求的时候都会自动带上token 去让后台去验证了。

  • 路由权限 & 菜单权限

    • 方案1: 有前端控制,在前端的router.js配置所有的路由,后端返回用户类型,比如 admin、editor,当路由跳转是,在router的全局钩子beforeEach判断是否有权限。 缺点:如果修改访问权限或这标题、icon等,都需要在前端去修改,重新编译上线。

    • 方案2 路由、菜单全在后台配置,接口只需要返回有权限的路由,然后前端把组件字符串替换成真正的组件,最后通过 router 的实例方法 addRoute 动态添加路由即可。

  • 按钮权限

webpack

性能优化