1. vue的双向数据绑定实现原理?
2. vue如何在组件之间进行传值?
3. vuex和vue的双向数据绑定有什么冲突?
4. vue-router中的history模式和hash模式的区别?
5. 路由懒加载的作用
6. 对虚拟dom的了解,说说实现原理
6. 对虚拟dom的了解,说说实现原理
什么是虚拟DOM树?(Virtual DOM)
virtual,指的是对真实DOM的一种模拟。相对于直接操作真实的DOM结构,我们构建一棵虚拟的树,将各种数据和操作直接应用在这棵虚拟的树上,然后再将对虚拟的树的修改应用到真实的DOM结构上。
虚拟DOM树其实就是一个普通的js对象,它是用来描述一段HTML片段的
- 当页面渲染的时候Vue会创建一颗虚拟DOM树
- 当页面发生改变Vue会再创建一颗新的虚拟DOM树
- 前后两颗新旧虚拟DOM树进行对比,Vue通过diff算法,去记录差异的地方
- 将有差异的地方更新到真实的DOM树中
虚拟DOM树有什么用?
vue中的虚拟DOM树只会重新渲染页面修改的地方,大大减少了对真实DOM树的操作。 -------虚拟DOM树是占内容的,但是可以帮我们提高DOM的性能。
可以这样理解,虚拟DOM树是用空间(虚拟DOM树占空间)换时间(虚拟DOM树可以提高DOM效率)。
Vue v-for 中 :key 到底有什么用?
vue不直接操作真实的DOM树,通过虚拟DOM树就可以重新渲染修改的地方,影藏在背后的原理其实就是 diff 算法。
key的作用是为了高效的更新虚拟DOM树,提高查找的效率,一次性定位到要修改的元
5. 路由懒加载的作用
在单页应用中,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,
造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,
运用懒加载可以将页面进行划分,按需加载页面,可以分担首页所承担的加载压力,减少加载用时。
- 常用的懒加载方式有两种:即使用
vue异步组件
和ES中的import
// 原加载方式
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component:HelloWorld
}
]
})
// vue异步组件实现懒加载
// 方法如下:component:resolve=>(require(['需要加载的路由的地址']),resolve)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: resolve=>(require(["@/components/HelloWorld"],resolve))
}
]
})
// ES 提出的import方法,(------最常用------)
const HelloWorld = ()=>import("@/components/HelloWorld")
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component:HelloWorld
}
]
})
**扩展知识**
:组件懒加载
// 原加载方式
import One from './one'
export default {
components:{
"One-com":One
},
...
// const方法
const One = ()=>import("./one");
export default {
components:{
"One-com":One
},
...
// 异步方法
export default {
components:{
"One-com":resolve=>(['./one'],resolve)
},
路由和组件的常用两种懒加载方式:
1、vue异步组件实现路由懒加载
component:resolve=>(['需要加载的路由的地址',resolve])
2、es提出的import(推荐使用这种方式)
const HelloWorld = ()=>import('需要加载的模块地址')
参考连接:
https://www.cnblogs.com/xiaoxiaoxun/p/11001884.html
4. vue-router中的history模式和hash模式的区别?
Vue-router 路由模式有三种 "hash" | "history" | "abstract",一般人只知道两种"hash" | "history"
// 源码:
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
mode
- 类型: string
- 默认值: "hash" (浏览器环境) | "abstract" (Node.js 环境)
- 可选值: "hash" | "history" | "abstract"
配置路由模式:
- hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。
- history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。
- abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
对于Vue 这类渐进式前端开发框架,为了构建SPA(单页面应用),需要引入前端路由系统,这也就是Vue-router存在的意义.
前端路由的核心,就在于——— 改变视图的同时不会向后端发出请求
浏览器对页面的访问是无状态的,所以我们在切换不同的页面时都会重新进行请求。 实际运用vue和vue-router开发就会发现,在切换页面时是没有重新请求的,使用起来就好像页面是有状态的。 其实是借助浏览器的History API来实现的,可以使页面跳转而不刷新,页面的状态就维持在浏览器中了。
- vue-router: hash
hash模式中url带有#号,修改成history模式,url中的#自动就去除了。
hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件:
window.onhashchange = function(event){
console.log(event.oldURL, event.newURL);
let hash = location.hash.slice(1)
}
hash模式下,仅hash符号之前的内容会被包含在请求中,如 http://www.abc.com, 因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误,因此改变hash不会重新加载页面.
- vue-router: history
随着history api的到来,前端路由开始进化了,前面的hashchange,你只能改变#后面的url片段,而history api则给了前端完全的自由。
history api可以分为两大部分:切换和修改
- 切换历史状态
包括back、forward、go三个方法,对应浏览器的前进,后退,跳转操作,有同学说了,(谷歌)浏览器只有前进和后退,没有跳转,嗯,在前进后退上长按鼠标,会出来所有当前窗口的历史记录,从而可以跳转:
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进
坑1:此处有一个开发的坑:在我们项目中,开发人员会在某个详情页面按钮上绑定history.go(-1)用来进入详情的主页面, 有时测试人员会直接输入url进入详情页,这样点击按钮就会出现问题,跳转的就不是项目中的主页面,就会是浏览器历史记录中的上一页
- 修改历史状态
包括了pushState、replaceState两个方法,这两个方法接收三个参数:stateObj,title,url
history.pushState(stateObj,title,url)
window.onpopstate = function(event){
console.log(event.state)
if(event.state && event.state.color === 'red'){
document.body.style.color = 'red';
}
}
坑2:通过history api,我们丢掉了丑陋的#,但是它也有个毛病:不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的。在hash模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,所以没有问题。但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个404来。
- popstate实现history路由拦截,监听页面返回事件
当活动历史记录条目更改时,将触发popstate事件
如果被激活的历史记录条目是通过对 history.pushState() 的调用创建的,或者受到对 history.replaceState() 的调用的影响,popstate事件的state属性包含历史条目的状态对象的副本。
-
需要注意的是调用 history.pushState() 或 history.replaceState() 用来在浏览历史中添加或修改记录,不会触发popstate事件;
只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back())
vue-router: abstract
abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。
根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,所以 在使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式
3. vuex和vue的双向数据绑定有什么冲突?
在严格模式下vuex中的state对象中的属性是不能随意更改的,但是在表单处理时使用v-model时绑定的是vuex的数据,用户可以随意更改数据时会抛出一个错误。
解决办法:
1.给 中绑定 value,然后侦听 input 或者 change 事件,在事件回调中调用一个方法
2.使用带有 setter 的双向绑定计算属性
2. vue如何在组件之间进行传值?
父子组件之间的传值
1.使用props
和$emit
2.在引入的子组件中使用ref
,通过this.$refs.xxx
.方法/值来获取子组件中的方法或者值
3.子组件中使用this.$parent
来获取父组件中的值或者方法
4.父组件中使用this.$children
来获取子组件中的值或者方法
⚠️tip:方法3和方法4有时候会失效,所以建议使用方法2兄弟组件中的传值
1.使用eventBus
作为中间件,然后使用$emit
去抛出事件,使用$on
去监听事件
⚠️这里要注意一些事项:
a.
bus.$emit
在beforeDestroy
中去触发
b.
bus.$on
在created
或者mounted
中使用,且回调函数使用箭头函数,解决this指向问题
c. 在
beforeDestroy
中使用bus.$off去销毁事件
d.比较特殊的是如果该组件存在缓存(路由跳转的时候使用了
来包裹
,此时该组件跳转的时候是不会触发
beforeDestroy
和destroy
,该组件只会触发created
和mounted
,但是它又离去的时候会触发deactivated
,返回的时候会触发active
,此时就将`bus.$emit 写在 deactivated 中)
tip:我在网上还看到如果是这种情况的话也可以将bus.nextTick里面去触发,尝试过,结果失败。这里只是当做一个参考。
多级组件嵌套传值
- 使用 v-bind = "listeners"
详情参考[点击链接](https://www.jianshu.com/p/26888cb10ce8 "点击链接")
- 使用provide和inject
多个组件重复使用,整个项目都需要的
- 使用vuex
参考链接:https://www.cnblogs.com/lanhuo666/p/11273128.html
1. vue的双向数据绑定实现原理?
-
vue2
:
在vue2中源码使用的defineProperty
来实现双向绑定。
实际上defineProperty它的主要作用是用来给对象中的属性配置操作权限,
例如:
writable:false, // 属性值是否可写
enumerable: false, // 属性值是否可循环
configurable: false //属性值是否可以delete
其中get
和set
实现了响应式原理。
数据改变之后触发了set方法->set方法触发更新和通知,同时get方法收集依赖->然后更改对应的虚拟dom->重新render.
缺点:
- 在这里defineProperty有一个不优雅的地方是,要使用get&set 方法实现读取的存储,必须将属性值重新赋值给新的变量,来实现
存储
和中转
。 - 而且对数组需要单独处理。
// 数组的双向绑定就是做了一个设计者模式;
// 取出数组的原型链并拷贝; - 并且defineProperty只能监听对象的某个属性,不能监听全对象;需要通过for in 循环。
-
vue3
:
而在vue3中源码使用了的proxy
来实现双向绑定原理。
proxy相对于defineProperty.
优点:
- proxy的get和set方法中直接可以返回参数target, key, value,可以直接获取到变量,不需要外部变量实现存储和读取。
- 而且可以监听数组,不需要单独的对数组进行特异性处理。
- proxy对原对象进行代理,生成新的代理对象,不会污染原对象。