getter setter 怎么做的
vue怎么避免css全局污染
scoped
route 区别
router
- router为VueRouter的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象,例如history对象经常用的跳转链接就可以用this.$router.push,和router-link跳转一样
- this.$router.push会往history栈中添加一个新的记录
route相当于当前正在跳转的路由对象。。可以从里面获取name,path,params,query等
vue name作用
- 当项目使用keep-alive时,可搭配组件name进行缓存过滤
- DOM做递归组件时,比如说detail.vue组件里有个list.vue子组件,递归迭代时需要调用自身name
- 当你用vue-tools时,vue-devtools调试工具里显示的组见名称是由vue中组件name决定的
keep-alive怎么实现的
Vue-router 中hash模式和history模式的区别
- url中 hash 带了一个很丑的 # 而history是没有#的
- hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:www.abc.com/#/hello,has… 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
- history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
- 使用场景 1.一般场景下,hash 和 history 都可以,除非你更在意颜值,# 符号夹杂在 URL 里看起来确实有些不太美丽。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成URL 跳转而无须重新加载页面。
- 调用 history.pushState() 相比于直接修改 hash,存在以下优势:
- pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;
- pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
- pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
- pushState() 可额外设置 title 属性供后续使用。
总结
- hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
- history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 www.abc.com/book/id。如果后… /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
- 结合自身例子,对于一般的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式即可,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。
Vue生命周期
Vue 的 diff 算法
- 同层比较,看两个节点是否一致
- 不一样:直接替换
- 一样:调用 patchVnode 方法
- 老节点有子节点,新节点没有,将老节点的子节点移除
- 老节点没有子节点,新节点有,给新节点新增子节点
- 都只有文本节点则直接替换
- 都有子节点,则调用 updateChildren 方法
- 头和头比,尾和尾比,头和尾比,尾和头比
- 若元素相同,再调用 patchVnode 方法
- 不相同,头依次和后面的元素比较
- 头和头比,尾和尾比,头和尾比,尾和头比
- 3.0 采用的位运算先判断 vnode 类型
vue 的$nextTick 什么原理
- 由来
- vue 驱动视图更新是异步的,即修改数据之后,试图不会立刻更新,而是等同一时间循环中的所有数据变化完成之后再统一进行试图更新
- 触发时机
- 在视图更新后基于新的视图进行操作
- 同一时间循环中的代码执行完毕-》DOM 更新->nextTick callback 触发
- 作用
- 可以避免 DOM 频繁变动,避免因此造成的浏览器卡顿,大幅提升性能
v-show和v-if的区别
- v-show只是单纯使用css隐藏
- v-if是整个元素的移除
- v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好
Key的作用,不写key会有什么影响
- key提高diff算法效率:虚拟dom对比是首先对比key
index不能作为key的原因
- 涉及到删除元素时会有问题,删除的那一项和他的下一项key会重合,此时会造成元素复用,会出现bug(数组塌陷,元素复用)
computed属性对应函数是否执行,主要是看对应观察者的dirty属性
- 该属性是true时,会让对应函数执行,获取新值
- 怎么变为true:当依赖改变时会触发观察者的update方法
- false时,会直接获取上次的计算结果
- 怎么变为false:获取新值之后,观察者中的evaluate会把dirty变为false
computed/watch 区别
- 能使用 computed 优先使用 computed,但是 computed 不能写异步方法
- 多个数据影响一个数据的时候,我们考虑使用 computed
- 一个数据影响多个数据的时候,考虑使用 watch
- computed
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
- 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
- watch
- 不支持缓存,数据变,直接会触发相应的操作;
- watch支持异步;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
- 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数, immediate:组件加载立即触发回调函数执行, deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
watcher中的deep和immediate
deep
- 是否深度监听,当data为obj时如果deep设置为true,
immediate
- watcher中变量绑定的方法是否立即执行
什么是 mvvm
- 进行数据劫持
- ?怎么做的
- 模板编译
- 观察者模式 watcher 把两条线连接起来
- 每当数据更新的时候通知 watcher,让 watcher 监听到的地方更新
- 利用 Object.defineProperty
vue 如何检测数组变化
- 对数组原型链上的方法进行重写
Data 方法为什么是函数
- 一个组件被多次复用,会创建多个实例。本质上用的同一个构造函数,如果 data 是对象,属于引用类型,会影响所有的实例,为了保证不同实例间 data 不冲突,data 必须是一个函数
vue 模板编译原理
- 生成一个虚拟 dom 树,用正则对模板进行解析
- 优化,深度遍历 dom 树,按照条件对节点进行标记,被标记过的可以跳过对他们的对比
- 将优化后的 dom 树转换为可执行的代码
computed 和 watcher
- 能使用 computed 优先使用 computed,但是 computed 不能写异步方法
- 多个数据影响一个数据的时候,我们考虑使用 computed
- 本质是一个具备缓存的 watcher,依赖的属性发生变化就会更新视图,适用于比较消耗性能的场景
- 一个数据影响多个数据的时候,考虑使用 watcher
- 没有缓存性,更多的是观察作用,可以监听某些数据执行回调,需要深度监听时,打开 deep:true,优化可以使用字符串形式监听。
keep-alive
- 可以实现组件缓存,当组件切换时不会对当前组件进行卸载
- 常用属性 include/exclude,允许有条件的进行缓存
- 两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态
Vue 中组件生命周期调用顺序
- 调用顺序都是先父后子,渲染完成是先子后父
- 组件销毁都是先父后子,销毁完成是先子后父
vue 组件通信方式
- 父-子 props,子-父 emit
- 获取父子组件实例 children 或者ref
- listeners
- $attrs:接受的是父组件通过行内属性传给子组件的没有被props接收的属性,没有响应式
- $listeners:接收的都是父组件传进来的自定义事件
- eventBus:创建一个空的vue实例。然后通过emit/$off去实现组件之间的数据交互
- on:绑定
- emit:触发
- off:移除
- provide/inject
- 适用在高阶组件或者插件,数据不可响应,不常用
- 一个组件上写了provide,所有的后代组件都可以使用
- vuex:最常用的兄弟组件交互方式
做过哪些 vue 性能优化
- 尽量减少 data 中的数据,data 中的数据会增加 getter 和 setter,回收机对应的 watcher
- spa 页面采用 keep-alive 缓存组件
- 按需使用 v-if 和 v-show
- key 保证唯一
- 使用路由懒加载
- 第三方模块按需导入
- 图片懒加载
- 长列表滚动到可视区域动态加载
- cdn 加载第三方模块
Vue和react有什么区别,设计模式有什么不同
共同点
- 数据驱动视图
- 隐藏操作DOM的频繁操作,在开发时只需要关注数据变化
- 组件化
- 都遵从组件化思想,将页面分成一些细块,组件之间的组合嵌套形成最后的页面
- 都有父子组件船费,数据状态管理,前端路由,插槽等
- 都使用Virtual DOM
- 都使用了Virtual DOM+Diff算法,vue的template模板+options api,react的class或function写法,最后都生成render函数,render函数执行返回VNode(虚拟dom数据结构,本质是棵树)
- 每一次ui更新时,会根据render重新生成最新的VNode,然后跟以前缓存起来的旧VNode进行对比,再使用Diff算法去更新真是的DOM(虚拟DOM是JS对象结构,同样在JS引擎中,但真实DOM在浏览器渲染引擎中,所以操作虚拟dom比操作真实dom开销要小很多)
不同点
- 核心思想不同
- vue尽可能的降低前端开发的门槛,让更多的人能更快的上手开发,主要特点是:
- 灵活易用的渐进式框架
- 数据拦截、代理
- 对侦测数据的变化更加敏感,精确
- react,推崇函数式变粗,数据不可变以及单向数据流,也可以借助onChange和setState手动实现双向数据绑定
- vue尽可能的降低前端开发的门槛,让更多的人能更快的上手开发,主要特点是:
- 组件写法差异
- react是jsx+inline style,把html和css都写进js中
- vue是template的单文件组件格式,html、css、js写进同一个文件
- diff算法不同
- 传统diff算法是循环递归每一个节点,复杂度O(n^3)
- vue/react,复杂度O(n)
- 不同的组件产生不同的dom结构,当type不相同时,对应dom操作就是直接销毁老的dom,创建新的dom
- 同一层级的一组子节点,可以通过唯一的key区分
- 响应式原理不同
- vue
- 依赖收集,自动优化,数据可变
- 递归监听data的多有熟悉,直接修改
- 数据改变时,自动找到引用组件重新渲染
- react
- 手动优化,数据不可变,需要setstate驱动新的state替换老的state,当数据改变时,以组件为根目录,默认全部重新渲染,react需要shouldComponentUpdate这个生命周期函数来进行控制
- vue
vue 的 mvvm 和 react 的 mvc
- 都是操作数据来影响视图,告别了传统操作 DOM 的时代
- Model 控制 View 层
- vue 基于数据劫持,拦截到最近的数据,从而重新渲染视图
- React 是提供了对应的 API,通过操作 API,让最新数据渲染视图
- 都一定存在 DOM 的差异化渲染(DOM DIFF)
- 每一次数据更改,只把需要改变的试图部门进行重新渲染
- React 默认只实现了单项控制(只有数据影响视图),而 Vue 基于 v-model 实现了双向控制(即也包含了视图影响数据)
- 不论 vue 还是 react,在实现视图影响数据的方式上,也都是基于 change/input 事件,监听表单元素内容的改变,从而去修改数据,达到数据的更新
- Model 控制 View 层
vuex mutation和action有哪些区别
- 流程顺序:视图触发action,action再触发mutation
- 角色定位
- mutation专注于修改state,理论上是修改state的唯一途径
- action:业务代码,异步请求
- 限制
- mutation:必须同步执行
- action:可以异步,但是不能直接操作state
getter setter怎么触发的
- 只要使用属性就会调用get
- 设置属性的时候会调用set
vue怎么实现的双向数据绑定
- Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通知视图更新。
- 实现一个Compile,对指令进行解析,初始化视图,并且订阅数据变化的更新,绑定好更新函数
- 实现一个observer,对数据进行劫持,通知数据的变化
- 实现一个Watcher,将其作为以上两者的一个中介点,在接受数据变更的同时,让Dep添加当前的Watcher,并及时通知视图进行update
- 实现MVVM,整合以上三者,作为一个入口函数
vue3.0相对于2.0有哪些改进
- 2.0
- 数据劫持改为用Object.defineProperty
- data嵌套太深,拦截的时候Object.defineProperty需要递归调用,性能不好
- 3.0
- 数据劫持改为用proxy
- 整个对象代理
- object.defineproperty怎么去劫持数组对象。proxy又是怎么做的
- object.defineproperty会造成什么问题 为什么改成proxy
- composition
VUE怎么优化
代码层面
- data尽可能的扁平化护理(vue的数据劫持方面优化)
- 不需要修改的数据使用Object.freeze冻结处理
- v-if和v-show区分使用场景
- v-for循环式,注重key的作用
- 对于没有使用vue语法的模块,用v-pre指令来提升编译效率
- 长列表中,不去直接渲染,而是采用虚拟列表去渲染
- 组件懒加载+骨架屏,优化首屏加载速度
- 对于图片,采用图片懒加载 vue-lazyload
- 对于切换时,不用销毁的组件采用keep-alive进行缓存
VUEX
mutation
- 放的都是一些函数,这些函数是用来修改state中的属性
- 这里的函数通过commit触发
- this.$store.commit('mutation中的函数名',传给options对应的参数)
- commit最多两个参数,最少一个参数
- 原则上必须是同步函数,否则不好维护
actions
- 放的也是一些函数,这些函数可以是异步的,但是这些函数想要修改state中的数据,还是要通过mutation
- 这里的函数通过dispatch触发 this.$store.dispatch('action函数名',options)
- 这里一般用来放一些ajax请求
getters
- 这里放的也是一些函数,就是vuex的计算属性,复杂的业务逻辑
modules
- 划分模块
- 若模块中的mutation或者actions中的属性名重复了,都会执行
- 若想要区分出来,则需要给模块上加一个属性 namespaced:true
辅助函数
mapState/mapGetters/mapMutatuons/mapActions