前两篇文章记录了一些我遇到的js的面试题,这篇文章来介绍vue的一些常见面试题。
ViewModel把view和model关联起来,ViewModel负责把Model的数据同步到view显出来,还负责吧view修改同步到Model。 答案详情
Vue是一个典型的MVVM框架,模型(Model)只是普通的JavaScript对象,修改它则视图(View)会自动更新。这种设计让状态管理变得非常简单而直观。那么Vue是如何把模型和视图建立起关联的呢?
vue通过三大模块来实现的:
==一句话总结vue底层逻辑:创建vue实例后,遍历data选项的数据,通过object.defineProperty为属性添加getter和setter对数据进行劫持,劫持数据时会为属性创建 dep 用来收集watcher,当数据更新时通过dep.notify()通知watcher,派发更新,并且触发compile中绑定的回调,渲染视图== ==长话短说:劫持数据,创建def通知watcher,触发回调,更新数据,渲染视图==
==一个属性对象多个dep 一个dep对象多个watcher 一个watcher对应多个dep dep和watcher是多对多关系 ==
==答题思路:什么时候执行?实例内部发生了什么变化?能进行什么业务操作?==
beforeCreate
在实例初始化之后,数据观测(data observe)和watcher配置之前被调用;在此可加载loading事件create
实例已经创建完成之后被调用。在这一步实例已经完成数据观测(data observe)和watcher事件回调,但实例还未挂载到DOM上;可在此结束beforeCreate中的loading事件beforeMount
在挂载开始之前被调用:找到对应的template模板,并编译成render函数。mounted
实例挂载到DOM上了(可以通过DOM api回去到节点,ref
属性可以访问了)beforeUpdate
虚拟DOM重新渲染和打补丁之前调用updated
虚拟 DOM 重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作beforeDestroy
实例销毁之前调用,这一步实例仍然完全可用。destroyed
实例销毁之后调用,调用后实例绑定的所有东西都会解绑,所有事件监听会被销毁,所有的子实例也会被销毁keep-alive
(activated 和 deactivated)怎么回答?什么场景用什么方法?项目中有哪些地方用到了?
按照使用场景可以分为三类:
eventBus
; vuex
方法:
props/$emit
props
访问父组件的值$emit
自定义事件向父组件发送数据parent/children与ref这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据
但这两种方法无法在跨级或兄弟间通信,不过可以借助vuex来实现
provide
来提供变量,然后在子组件中通过 inject
来注入变量。vuex
状态管理实现组件之间的通信localStorage/sessionStorage
问题核心:如何将template转换成render函数 ?
简而言之,就是先将template转化成AST树,再将ast树转化成render函数
export const createCompiler = createCompilerCreator(function
baseCompile(
template: string,
options: CompilerOptions
): CompiledResult {
const ast = parse(template.trim(), options) // 1.解析ast语法树
if (options.optimize !== false) {
optimize(ast, options) // 2.对ast树进行标记,标记
静态节点
}
const code = generate(ast, options) // 3.生成代码
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
每个computed计算属性都会对应一个 Watcher实例,通过watcher来实现的
def
属性== export function set(target: Array<any> | Object, key: any, val:
any): any {
// 1.是开发环境 target 没定义或者是基础类型则报错
if (process.env.NODE\_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or
primitive value: ${(target: any)}`)
}
// 2.如果是数组 Vue.set(array,1,100); 调用我们重写的splice方法 (这样可以
更新视图)
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
每次使用组件时都会为组件进行实例化操作,并且调用data函数返回一个对象作为组件的数据源。这样可以保证多个组件间数据互不影响
Dom
nextTick
里面的回调函数在Dom
重新渲染之后执行每次event loop的最后,会有一个UI render步骤,也就是更新DOM
==原理:在dom更新之后的下一次的event loop 事件循环中执行nexttick里面的回调函数的异步任务,vue会采用Promise.then()、MutationObserve、setTimeout来执行nexttickhandler,依次测试浏览器支持的方法,如果前两个都不支持就用最后一个setTimeout来执行。==
源码中使用的是if (cb) { cb.call(ctx) }
,用.call()改变this指向, 所以不能使用箭头函数,箭头函数的this是固定的,是不可用apply,call,bind来改变的。
组件应用state 组件调用dispatch发布到action里面,在action里面可以进行异步网络请求,然后commit提交到mutation里面,在mutation里面修改state
需要注意的是:网络请求一般在action里面进行,不要在action里面修改state,因为devtool只能跟踪mutations里面的修改记录
分别是state、getters、mutations、actions、modules
state是状态、
getters:相当于vue中的计算属性computed
getters: {
total: state => {
return state.price * state.number
},
discountTotal: (state, getters) => {
return state.discount * getters.total
}
},
mutations:改变Vuex中的状态的唯一途径就是显式地提交 (commit) mutation。
actions:进行异步操作,比如网络请求
modules:store对象变得臃肿就需要分模块
因为data中的内容只会在create钩子触发前初始化一次。具体来说就是data中设置count: this.store.state.count 则count的值是created钩子执行前this.store.state.count的值,赋值之后属性的值就是纯粹的字面量,之后this.
var b = 1;
var a = b;
b = 2;
可以根据使用方法结合实际项目说
<keep-alive :include="include">
<router-view></router-view>
</keep-alive>
其中include可以是个数组,数组内容为路由的name选项的值。
给 path 配置通配符,然后用 redirect 重定向。示例代码:
const router = new VueRouter({
routes: [
{
path: '*', redirect: {path: '/'}
}
]
})
例如我做的这个后台管理系统,顶部栏和左侧菜单栏是全局通用的,把它当作父路由,右下侧的页面的内容部分放在子路由里
例如在我这个后台管理系统对的项目中,我们想同级展示多个视图,而不是嵌套展示。例如项目首页,有头部导航,侧边栏导航、主内容区域。头部导航、侧边栏导航==我们不想用组件方式引入==,==想用<router-view name=""></router-view>
视图方式展示==。那么这个首页上,就有三个视图,头部导航视图,侧边栏导航视图、主内容区域视图同级展示。
<router-link to="" tag="" active-class=""></router-link>
<router-view></router-view>
<keep-alive include="" exclude=""></keep-alive>
时<router-link/>
组件的属性,设置链接激活时使用的 CSS 类名。默认值可以通过路由的构造选项 linkActiveClass 来全局配置。
以冒号加参数的形式
path: 'user/:id'
在组件内通过路由对象route访问,例如
this.$route.params.id
beforeEach
beforeResolve
导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后被调用afterEach
beforeRouterEnter
beforeRouterUpdate
beforeRouterLeave
离开的组件的相关方法->全局beforeEach守卫->重用的组件的相关方法->路由配置里面的beforeEnter->(解析路由)调用被激活的组件的相关方法(beforeRouteEnter)->全局的beforeResolve->导航被确认->afterEach钩子->触发dom更新->调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
beforeRouterLeave
beforeEach
beforeRouterUpdate
beforeEnter
beforeRouterEnter
beforeResole
在导航被确认之前,同时所有组件内守卫和异步路由组件被解析之后afterEach
beforeRouterEnter
中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。$route和$
router的区别是什么?需要知道:分别由哪几种方式?有什么区别?
Params
this.$router.push{
name: Home,
params: {
number: 1,
code: 99
}
}
query
hash
模式:onhashchange
事件,hashchange
只能改变 #
后面的代码片段。可以在window对象上监听这个事件window.onhashchange = function(event){
console.log(event.oldURL, event.newURL)
let hash = location.hash.slice(1)
}
history
模式pushState()
和replaceState()
方法abstract
JavaScript
运行环境由于Vue项目是SPA应用(即单页面应用),nginx在跳转时会优先根据你请求的路径去寻找该路径下的index.html页面,而vue应用只有一个index.html文件放在项目根目录,所以要在 Nginx中将所有请求都转发到index.html上就可以了。
location / {
...
try_files $uri $uri/ /test/dist/index.html;
...
}
这句话的意思是,当路径变化后,nginx会优先去寻找地址栏上最后一个uri的index.html文件,如果找不到,就会返回指定的html文件(这里就是返回/test/dist/index.html)。
把导入路由写成方法的形式,然后在配置路由映射的时候把component对应导入路由的方法,当路由被访问时才执行导入路由的方法
例子:
const router = new VueRouter({
routes: [
{
path: '/home',
name: 'Home',
component:() => import('../views/home')
}
]
})
<router-link :to="/home">
来跳转1.后台同学返回一个json格式的路由表,我用easymock造了一段:动态路由表,大家可参考; 2.因为后端同学传回来的都是字符串格式的,但是前端这里需要的是一个组件对象啊,写个方法遍历一下,将字符串转换为组件对象; 3.利用vue-router的beforeEach、addRoutes、localStorage来配合上边两步实现效果; 4.左侧菜单栏根据拿到转换好的路由列表进行展示; 大体步骤:**==拦截路由->后台取到路由->保存路由到localStorage(用户登录进来只会从后台取一次,其余都从本地取,所以用户,只有退出在登录路由才会更新)==**
不能,因为当钩子执行前,组件实例还没被创建;
通过 next(vm => { // 通过 vm
访问组件实例 })来访问 vue 组件实例
Object.defineProperty
的缺点:
proxy
的优点:
详细区别参考地址:https://worktile.com/kb/ask/19553.html
定义:只在一个Web页面初始化时加载相应的HTML、js、css。避免了页面的重新加载。
我是 AndyHu,目前暂时是一枚前端搬砖工程师。
文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注呀😊
未经许可禁止转载💌
speak less,do more.