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个)
- 全局守卫有
beforeEachbeforeResolveafterEach - 路由守卫有
beforeEnter - 组件守卫有
beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave
当点击导航时,首先判断是否时从其他路由跳转过来的,如果是则调用失活组件的 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 动态添加路由即可。
-
-
按钮权限