网站的后缀名怎么建设,七牛云存储 wordpress 没用,哪个网站可以免费下载电视剧看,网站开发实验报告模版vue-router中如何保护路由
分析
路由保护在应用开发过程中非常重要#xff0c;几乎每个应用都要做各种路由权限管理#xff0c;因此相当考察使用者基本功。
体验
全局守卫#xff1a;
const router createRouter({ ... })
router.beforeEach((to, from) {// .…vue-router中如何保护路由
分析
路由保护在应用开发过程中非常重要几乎每个应用都要做各种路由权限管理因此相当考察使用者基本功。
体验
全局守卫
const router createRouter({ ... })
router.beforeEach((to, from) {// ...// 返回 false 以取消导航return false
})路由独享守卫
const routes [{path: /users/:id,component: UserDetails,beforeEnter: (to, from) {// reject the navigationreturn false},},
]组件内的守卫
const UserDetails {template: ...,beforeRouteEnter(to, from) {// 在渲染该组件的对应路由被验证前调用},beforeRouteUpdate(to, from) {// 在当前路由改变但是该组件被复用时调用},beforeRouteLeave(to, from) {// 在导航离开渲染该组件的对应路由时调用},
}回答
vue-router中保护路由的方法叫做路由守卫主要用来通过跳转或取消的方式守卫导航。路由守卫有三个级别全局、路由独享、组件级。影响范围由大到小例如全局的router.beforeEach()可以注册一个全局前置守卫每次路由导航都会经过这个守卫因此在其内部可以加入控制逻辑决定用户是否可以导航到目标路由在路由注册的时候可以加入单路由独享的守卫例如beforeEnter守卫只在进入路由时触发因此只会影响这个路由控制更精确我们还可以为路由组件添加守卫配置例如beforeRouteEnter会在渲染该组件的对应路由被验证前调用控制的范围更精确了。用户的任何导航行为都会走navigate方法内部有个guards队列按顺序执行用户注册的守卫钩子函数如果没有通过验证逻辑则会取消原有的导航。
原理
runGuardQueue(guards)链式的执行用户在各级别注册的守卫钩子函数通过则继续下一个级别的守卫不通过进入catch流程取消原本导航
// 源码
runGuardQueue(guards).then(() {// check global guards beforeEachguards []for (const guard of beforeGuards.list()) {guards.push(guardToPromiseFn(guard, to, from))}guards.push(canceledNavigationCheck)return runGuardQueue(guards)}).then(() {// check in components beforeRouteUpdateguards extractComponentsGuards(updatingRecords,beforeRouteUpdate,to,from)for (const record of updatingRecords) {record.updateGuards.forEach(guard {guards.push(guardToPromiseFn(guard, to, from))})}guards.push(canceledNavigationCheck)// run the queue of per route beforeEnter guardsreturn runGuardQueue(guards)}).then(() {// check the route beforeEnterguards []for (const record of to.matched) {// do not trigger beforeEnter on reused viewsif (record.beforeEnter !from.matched.includes(record)) {if (isArray(record.beforeEnter)) {for (const beforeEnter of record.beforeEnter)guards.push(guardToPromiseFn(beforeEnter, to, from))} else {guards.push(guardToPromiseFn(record.beforeEnter, to, from))}}}guards.push(canceledNavigationCheck)// run the queue of per route beforeEnter guardsreturn runGuardQueue(guards)}).then(() {// NOTE: at this point to.matched is normalized and does not contain any () PromiseComponent// clear existing enterCallbacks, these are added by extractComponentsGuardsto.matched.forEach(record (record.enterCallbacks {}))// check in-component beforeRouteEnterguards extractComponentsGuards(enteringRecords,beforeRouteEnter,to,from)guards.push(canceledNavigationCheck)// run the queue of per route beforeEnter guardsreturn runGuardQueue(guards)}).then(() {// check global guards beforeResolveguards []for (const guard of beforeResolveGuards.list()) {guards.push(guardToPromiseFn(guard, to, from))}guards.push(canceledNavigationCheck)return runGuardQueue(guards)})// catch any navigation canceled.catch(err isNavigationFailure(err, ErrorTypes.NAVIGATION_CANCELLED)? err: Promise.reject(err))源码位置(opens new window)
使用 Object.defineProperty() 来进行数据劫持有什么缺点
在对一些属性进行操作时使用这种方法无法拦截比如通过下标方式修改数组数据或者给对象新增属性这都不能触发组件的重新渲染因为 Object.defineProperty 不能拦截到这些操作。更精确的来说对于数组而言大部分操作都是拦截不到的只是 Vue 内部通过重写函数的方式解决了这个问题。
在 Vue3.0 中已经不使用这种方式了而是通过使用 Proxy 对对象进行代理从而实现数据劫持。使用Proxy 的好处是它可以完美的监听到任何方式的数据改变唯一的缺点是兼容性的问题因为 Proxy 是 ES6 的语法。
用VNode来描述一个DOM结构
虚拟节点就是用一个对象来描述一个真实的DOM元素。首先将 template 真实DOM先转成 ast ast 树通过 codegen 生成 render 函数 render 函数里的 _c 方法将它转为虚拟dom
如何从真实DOM到虚拟DOM
涉及到Vue中的模板编译原理主要过程
将模板转换成 ast 树 ast 用对象来描述真实的JS语法将真实DOM转换成虚拟DOM优化树将 ast 树生成代码
Vue为什么没有类似于React中shouldComponentUpdate的生命周期
考点: Vue的变化侦测原理
前置知识: 依赖收集、虚拟DOM、响应式系统
根本原因是Vue与React的变化侦测方式有所不同
React是pull的方式侦测变化,当React知道发生变化后,会使用Virtual Dom Diff进行差异检测,但是很多组件实际上是肯定不会发生变化的,这个时候需要用shouldComponentUpdate进行手动操作来减少diff,从而提高程序整体的性能.
Vue是pullpush的方式侦测变化的,在一开始就知道那个组件发生了变化,因此在push的阶段并不需要手动控制diff,而组件内部采用的diff方式实际上是可以引入类似于shouldComponentUpdate相关生命周期的,但是通常合理大小的组件不会有过量的diff,手动优化的价值有限,因此目前Vue并没有考虑引入shouldComponentUpdate这种手动优化的生命周期.
Vue 组件间通信有哪几种方式
Vue 组件间通信是面试常考的知识点之一这题有点类似于开放题你回答出越多方法当然越加分表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信父子组件通信、隔代组件通信、兄弟组件通信下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
1props / $emit 适用 父子组件通信
这种方法是 Vue 组件的基础相信大部分同学耳闻能详所以此处就不举例展开介绍。
2ref 与 $parent / $children 适用 父子组件通信
ref如果在普通的 DOM 元素上使用引用指向的就是 DOM 元素如果用在子组件上引用就指向组件实例$parent / $children访问父 / 子实例
3EventBus $emit / $on 适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线事件中心用它来触发事件和监听事件从而实现任何组件间的通信包括父子、隔代、兄弟组件。
4$attrs/$listeners 适用于 隔代组件通信
$attrs包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时这里会包含所有父作用域的绑定 ( class 和 style 除外 )并且可以通过 v-bind$attrs 传入内部组件。通常配合 inheritAttrs 选项一起使用。$listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on$listeners 传入内部组件
5provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题不过它的使用场景主要是子组件获取上级组件的状态跨级组件间建立了一种主动提供与依赖注入的关系。
6Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store仓库。“store” 基本上就是一个容器它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候若 store 中的状态发生变化那么相应的组件也会相应地得到高效更新。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
参考 前端进阶面试题详细解答
keep-alive 使用场景和原理
keep-alive 是 Vue 内置的一个组件可以实现组件缓存当组件切换时不会对当前组件进行卸载。
常用的两个属性 include/exclude允许组件有条件的进行缓存。两个生命周期 activated/deactivated用来得知当前组件是否处于活跃状态。keep-alive 的中还运用了 LRU(最近最少使用) 算法选择最近最久未使用的组件予以淘汰。
computed和watch有什么区别?
computed:
computed是计算属性,也就是计算值,它更多用于计算值的场景computed具有缓存性,computed的值在getter执行后是会缓存的只有在它依赖的属性值改变之后下一次获取computed的值时才会重新调用对应的getter来计算computed适用于计算比较消耗性能的计算场景
watch:
更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作无缓存性页面重新渲染时值不变化也会执行
小结:
当我们要进行数值计算,而且依赖于其他数据那么把这个数据设计为computed如果你需要在某个数据变化时做一些事情使用watch来观察这个数据变化
Vue组件如何通信
Vue组件通信的方法如下:
props/$emitv-on: 通过props将数据自上而下传递而通过$emit和v-on来向上传递信息。EventBus: 通过EventBus进行信息的发布与订阅vuex: 是全局数据管理库可以通过vuex管理全局的数据流$attrs/$listeners: Vue2.4中加入的$attrs/$listeners可以进行跨级的组件通信provide/inject以允许一个祖先组件向其所有子孙后代注入一个依赖不论组件层次有多深并在起上下游关系成立的时间里始终生效这成为了跨组件通信的基础
还有一些用solt插槽或者ref实例进行通信的使用场景过于有限就不赘述了。
v-show 与 v-if 有什么区别
v-if 是真正的条件渲染因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建也是惰性的如果在初始渲染时条件为假则什么也不做——直到条件第一次变为真时才会开始渲染条件块。
v-show 就简单得多——不管初始条件是什么元素总是会被渲染并且只是简单地基于 CSS 的 “display” 属性进行切换。
所以v-if 适用于在运行时很少改变条件不需要频繁切换条件的场景v-show 则适用于需要非常频繁切换条件的场景。
一般在哪个生命周期请求异步数据
我们可以在钩子函数 created、beforeMount、mounted 中进行调用因为在这三个钩子函数中data 已经创建可以将服务端端返回的数据进行赋值。
推荐在 created 钩子函数中调用异步请求因为在 created 钩子函数中调用异步请求有以下优点
能更快获取到服务端数据减少页面加载时间用户体验更好SSR不支持 beforeMount 、mounted 钩子函数放在 created 中有助于一致性。
Vue中的key到底有什么用
key是为Vue中的vnode标记的唯一id,通过这个key,我们的diff操作可以更准确、更快速
diff算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的key与旧节点进行比对,然后超出差异. diff程可以概括为oldCh和newCh各有两个头尾的变量StartIdx和EndIdx它们的2个变量相互比较一共有4种比较方式。如果4种比较都没匹配如果设置了key就会用key进行比较在比较的过程中变量会往中间靠一旦StartIdxEndIdx表明oldCh和newCh至少有一个已经遍历完了就会结束比较,这四种比较方式就是首、尾、旧尾新头、旧头新尾. 准确: 如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug.快速: key的唯一性可以被Map数据结构充分利用,相比于遍历查找的时间复杂度O(n),Map的时间复杂度仅仅为O(1).
使用vue渲染大量数据时应该怎么优化说下你的思路
分析
企业级项目中渲染大量数据的情况比较常见因此这是一道非常好的综合实践题目。
回答 在大型企业级项目中经常需要渲染大量数据此时很容易出现卡顿的情况。比如大数据量的表格、树 处理时要根据情况做不同处理 可以采取分页的方式获取避免渲染大量数据 vue-virtual-scroller (opens new window)等虚拟滚动方案只渲染视口范围内的数据 如果不需要更新可以使用v-once方式只渲染一次 通过v-memo (opens new window)可以缓存结果结合v-for使用避免数据变化时不必要的VNode创建 可以采用懒加载方式在用户需要的时候再加载数据比如tree组件子树的懒加载
还是要看具体需求首先从设计上避免大数据获取和渲染实在需要这样做可以采用虚表的方式优化渲染最后优化更新如果不需要更新可以v-once处理需要更新可以v-memo进一步优化大数据更新性能。其他可以采用的是交互方式优化无线滚动、懒加载等方案
虚拟DOM真的比真实DOM性能好吗
首次渲染大量DOM时由于多了一层虚拟DOM的计算会比innerHTML插入慢。正如它能保证性能下限在真实DOM操作的时候进行针对性的优化时还是更快的。
Vue 组件间通信有哪几种方式
Vue 组件间通信是面试常考的知识点之一这题有点类似于开放题你回答出越多方法当然越加分表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信父子组件通信、隔代组件通信、兄弟组件通信下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
1props / $emit 适用 父子组件通信
这种方法是 Vue 组件的基础相信大部分同学耳闻能详所以此处就不举例展开介绍。
2ref 与 $parent / $children 适用 父子组件通信
ref如果在普通的 DOM 元素上使用引用指向的就是 DOM 元素如果用在子组件上引用就指向组件实例$parent / $children访问父 / 子实例
3EventBus $emit / $on 适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线事件中心用它来触发事件和监听事件从而实现任何组件间的通信包括父子、隔代、兄弟组件。
4$attrs/$listeners 适用于 隔代组件通信
$attrs包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时这里会包含所有父作用域的绑定 ( class 和 style 除外 )并且可以通过 v-bind$attrs 传入内部组件。通常配合 inheritAttrs 选项一起使用。$listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on$listeners 传入内部组件
5provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题不过它的使用场景主要是子组件获取上级组件的状态跨级组件间建立了一种主动提供与依赖注入的关系。
6Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store仓库。“store” 基本上就是一个容器它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候若 store 中的状态发生变化那么相应的组件也会相应地得到高效更新。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
如果让你从零开始写一个vuex说说你的思路
思路分析
这个题目很有难度首先思考vuex解决的问题存储用户全局状态并提供管理状态API。
vuex需求分析如何实现这些需求
回答范例
官方说vuex是一个状态管理模式和库并确保这些状态以可预期的方式变更。可见要实现一个vuex
要实现一个Store存储全局状态要提供修改状态所需APIcommit(type, payload), dispatch(type, payload)
实现Store时可以定义Store类构造函数接收选项options设置属性state对外暴露状态提供commit和dispatch修改属性state。这里需要设置state为响应式对象同时将Store定义为一个Vue插件commit(type, payload)方法中可以获取用户传入mutations并执行它这样可以按用户提供的方法修改状态。 dispatch(type, payload)类似但需要注意它可能是异步的需要返回一个Promise给用户以处理异步结果
实践
Store的实现
class Store {constructor(options) {this.state reactive(options.state)this.options options}commit(type, payload) {this.options.mutations[type].call(this, this.state, payload)}
}vuex简易版
/*** 1 实现插件挂载$store* 2 实现store*/let Vue;class Store {constructor(options) {// state响应式处理// 外部访问 this.$store.state.***// 第一种写法// this.state new Vue({// data: options.state// })// 第二种写法防止外界直接接触内部vue实例防止外部强行变更this._vm new Vue({data: {$$state: options.state}})this._mutations options.mutationsthis._actions options.actionsthis.getters {}options.getters this.handleGetters(options.getters)this.commit this.commit.bind(this)this.dispatch this.dispatch.bind(this)}get state () {return this._vm._data.$$state}set state (val) {return new Error(Please use replaceState to reset state)}handleGetters (getters) {Object.keys(getters).map(key {Object.defineProperty(this.getters, key, {get: () getters[key](this.state)})})}commit (type, payload) {let entry this._mutations[type]if (!entry) {return new Error(${type} is not defined)}entry(this.state, payload)}dispatch (type, payload) {let entry this._actions[type]if (!entry) {return new Error(${type} is not defined)}entry(this, payload)}
}const install (_Vue) {Vue _VueVue.mixin({beforeCreate () {if (this.$options.store) {Vue.prototype.$store this.$options.store}},})
}export default { Store, install }验证方式
import Vue from vue
import Vuex from ./vuex
// this.$store
Vue.use(Vuex)export default new Vuex.Store({state: {counter: 0},mutations: {// state从哪里来的add (state) {state.counter}},getters: {doubleCounter (state) {return state.counter * 2}},actions: {add ({ commit }) {setTimeout(() {commit(add)}, 1000)}},modules: {}
})简述 mixin、extends 的覆盖逻辑
1mixin 和 extends mixin 和 extends均是用于合并、拓展组件的两者均通过 mergeOptions 方法实现合并。
mixins 接收一个混入对象的数组其中混入对象可以像正常的实例对象一样包含实例选项这些选项会被合并到最终的选项中。Mixin 钩子按照传入顺序依次调用并在调用组件自身的钩子之前被调用。extends 主要是为了便于扩展单文件组件接收一个对象或构造函数。
2mergeOptions 的执行过程
规范化选项normalizeProps、normalizelnject、normalizeDirectives)对未合并的选项进行判断
if (!child._base) {if (child.extends) {parent mergeOptions(parent, child.extends, vm);}if (child.mixins) {for (let i 0, l child.mixins.length; i l; i) {parent mergeOptions(parent, child.mixins[i], vm);}}
}
合并处理。根据一个通用 Vue 实例所包含的选项进行分类逐一判断合并如 props、data、 methods、watch、computed、生命周期等将合并结果存储在新定义的 options 对象里。返回合并结果 options。
Vue中v-html会导致哪些问题
可能会导致 xss 攻击v-html 会替换掉标签内部的子元素
let template require(vue-template-compiler);
let r template.compile(div v-htmlspanhello/span/div) // with(this){return _c(div,{domProps: {innerHTML:_s(spanhello/span)}})}
console.log(r.render);// _c 定义在core/instance/render.js
// _s 定义在core/instance/render-helpers/index,js
if (key textContent || key innerHTML) { if (vnode.children) vnode.children.length 0 if (cur oldProps[key]) continue // #6601 work around Chrome version 55 bug where single textNode // replaced by innerHTML/textContent retains its parentNode property if (elm.childNodes.length 1) { elm.removeChild(elm.childNodes[0]) }
}在Vue中使用插件的步骤
采用ES6的import ... from ...语法或CommonJS的require()方法引入插件使用全局方法Vue.use( plugin )使用插件,可以传入一个选项对象Vue.use(MyPlugin, { someOption: true })
Vue.extend 作用和原理
官方解释Vue.extend 使用基础 Vue 构造器创建一个“子类”。参数是一个包含组件选项的对象。
其实就是一个子类构造器 是 Vue 组件的核心 api 实现思路就是使用原型继承的方法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并