一.MVVM
MVVM 是 Model-View-ViewModel 的缩写。
Model
代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。View
代表UI 组件,它负责将数据模型转化成UI 展现出来。ViewModel
监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
注意, MVVM模型中, Model和View是不会直接连接的,而ViewModel则会以双向连接的形式连接Model和View。
二.Vue和React的区别
Vue的优点
- 轻量级的框架
- 双向数据绑定
- 指令
- 插件化
与React的对比
两者具有许多的相似之处
- 使用 Virtual DOM
- 提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。
- 将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。
Vue与React的不同之处
-
- 组件的重渲染 React中组件的重渲染需要通过
shouldComponentUpdate
来避免不必要的子组件的重渲染,而Vue中组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染。 - JSX vs Template 在 React 中,所有的组件的渲染功能都依靠 JSX。JSX 是使用 XML 语法编写 JavaScript 的一种语法糖。Vue默认推荐使用Vue模板(更适用于表现类),但Vue也支持JSX。
组件作用域的CSS 在React中,CSS 作用域是通过 CSS-in-JS 的方案实现的 (比如 styled-components、glamorous 和 emotion),而Vue则有更好的解决方案,如下:
<style scoped> @media (min-width: 250px) { .list-container:hover { background: orange; } } </style>复制代码
这个可选
scoped
属性会自动添加一个唯一的属性 (比如data-v-21e5b78
) 为组件内 CSS 指定作用域,编译的时候.list-container:hover
会被编译成类似.list-container[data-v-21e5b78]:hover
,这样就可以控制CSS只在这个组件内生效。
- 组件的重渲染 React中组件的重渲染需要通过
三.VUE知识点
1. Vue的生命周期
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdated
- updated
- beforeDestroy
- destroyed
大致过程就是
- 数据初始化(1~2)
完成数据观测、属性和方法的运算加载,event/wather时间回调。 - dom挂载阶段(3~4)
el被新创建的vm.$el替换并挂载到实例上去,之后调用钩子函数。 - 数据更新阶段(5~6)
数据更新,虚拟dom重渲染 - 组件卸载阶段(7~8)
销毁实例及子实例
2. Vue实现数据双向绑定的原理
Vue实现这种数据双向绑定的效果,需要三大模块:
- Observer:能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者。
- Compile:对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
- Watcher:作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
Observer
Observer的核心是通过Obeject.defineProperty()
来监听数据的变动,这个函数内部可以定义setter
和getter
,每当数据发生变化,就会触发setter
。这时候Observer
就要通知订阅者,订阅者就是Watcher
。
Watcher
Watcher
订阅者作为Observer
和Compile
之间通信的桥梁,主要做的事情是:
- 在自身实例化时往属性订阅器(dep)里面添加自己
- 自身必须有一个
update()
方法 - 待属性变动
dep.notice()
通知时,能调用自身的update()方法,并触发Compile
中绑定的回调
Compile
Compile
主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。
vue数据双向绑定原理详解在这里剖析Vue原理&实现双向绑定MVVM
3.vue-router
Vue Router 是vue官方推建的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vue.js 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 CSS class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
(1)动态路由匹配
一个“路径参数”使用冒号 :
标记。当匹配到一个路由时,参数值会被设置到 this.$route.params
,可以在每个组件内使用。
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})复制代码
使用$route.params和$route.query获取路由中的参数。
(2)嵌套路由
在项目的开发中常常在一个父路由下添加若干的字路由,也可以在子路由中添加后代路由,即路由的嵌套。
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
path: 'profile',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}
]
}
]
})复制代码
要注意,以 /
开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。
(3).编程式导航(跳转)
router-link: 路由标签实现前端路由的跳转,本质上是$router.push(),通过tag渲染成目标标签,to转到目的路由。
<router-link tag='div' to='./user'></router-link>复制代码
router.push: 想要导航到不同的 URL,则使用 router.push
方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
router.replace: 跟 router.push
很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
router.go(n): 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)
。
(4)路由重定向和路由模式
重定向:通过redirect属性,将目的路由指向对应路由
路由模式:hash路由和history路由(通过设置mode的值来改变路由模式)
vue-router
默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载,但是会在URL后添加’#‘号,不是很美观。
http://www.rosenwang.xyz:8080/#/home ----hash
复制代码
history路由则显得更加符合要求,但是需要后端的支持,如何直接使用,上线后访问会出现404.
http://www.rosenwang.xyz:8080/home -history复制代码
(5)导航守卫
全局守卫:beforeEach,afterEach
路由独享守卫:beforeEnter,afterEnter
组件内守卫:beforeRouterEnter,beforeRouterUpdate,beforeRouterLeave
每个守卫方法接收三个参数:
to: Route
: 即将要进入的目标 路由对象from: Route
: 当前导航正要离开的路由next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数。
确保要调用 next
方法,否则钩子就不会被 resolved。
(6)路由懒加载
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')复制代码
4.父子组件
(1)父组件向子组件传参
父组件通过标签上定义传值,子组件通过props
方法接受数据
<parent>
<child :child-msg="msg"></child> //这里必须要用 - 代替驼峰
</parent>
props: {
childMsg: {
type: Array, //传入的类型
default: [] //默认值
}
}复制代码
(2)子组件向父组件传递数据
子组件通过$emit
方法传递参数,父组件通过v-on
(简写为@
)来监听DOM事件,并在触发时接收数据运行js函数。
//子组件
testClick() {
this.$emit('test','123');
//$emit(even,value)even 是一个函数,value 是传给父组件的值
}
//父组件
<div>
<child @test="change" :msg="msg"></child> //监听子组件触发的test事件,然后调用change方法
</div>
methods: {
change(val) {
this.msg = val; // val: 123
}
}复制代码
(3)非父子组件数据传递
通过vuex统一实现状态管理,实现了非父子组件内的数据通信
5. Vue响应数据变化的几种做法
- methods: 每次获取都会重新计算求值
- computed(计算属性,有缓存): 基于数据依赖进行缓存,只有当数据变化时,才会重新求值。(计算属性只有 getter,可以在需要的时候自己设定 setter)。computed 擅长处理的情景:一个数据受多个数据影响。
- watch: 当需要在数据变化时执行异步操作或者消耗较大的操作时,比较有效。watch 擅长处理的情景:一个数据影响多个数据。
- v-model: 基于数据双向绑定(对于 v-for 循环列表中的项,需要使用键值)
6. Vue的条件渲染
v-if
是真正的条件渲染,它会适当地销毁和重建DOM达到让元素显示和隐藏的效果。
v-show 通过修改元素的display的CSS属性让其显示或者隐藏,元素始终会被渲染并保留在DOM中。
v-if vs v-show
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。