VUE 第一大原理MVVM
1.声明式和命令式
- 命令式编程: 命令“机器”如何(how)去做事情,按照你想要的(what)发布命令,然后“机器”按照你的命令一步步执行。
- 声明式编程: 告诉“机器”你想要的(what),让“机器”自己去想办法去找到你想要的结果。其实就是提前在“机器”内置一些模块,例如放在数组原型上的方法。
2.MVC & MVVM
- 架构模式:一个架构模式描述软件系统里基本的结构组织或纲要。架构模式提供一些事先定义好的子系统,指定它们的责任,并给出把它们组织在一起的法则和指南。一个架构模式常常可以分解成很多个设计模式的联合使用。
- MVC(Model View Controller): 通过用户操作view(试图层)来向controller发布命令,进而controller进行一系列的操作来操控model的改变,进而重新渲染视图,给予用户展示想要的结果。
- MVVM(Model View ViewModel): MVVC也是一种架构模式,Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。Model和View没有直接对接,而是通过ViewModel作为中间层来交互的,ViewModel通过双向数据绑定自动同步到Model和View。
- 俩者区别:
- MVC中Controller需要手动将更改的数据更新视图上,对应MVVC中的ViewModel,实现了数据的双向绑定。
- MVC需要操作大量的DOM,页面渲染效率降低。相反MVVC架构模式只需要关注业务逻辑,无需手动操作DOM。
- MVVC提高了加载速度,提高了用户体验。
下图为vue使用的MVVC架构模式
-
vue&react
- vue
- model:对应vue存放数据的各个属性,例如:data、copmuted、vuex等
- view:对应各个页面的template、根组件的el、以及render
- viewmodel:对应的就是vue数据绑定的核心原理,也就是vue本身,通过viewmodel监听数据层更新视图
- react
- model:对应也是数据层,相对于react的属性和状态。
- view:对应的是视图层,用JSX语法来搭建的视图层。
- controll:对应操作数据的逻辑,就是提前内置到系统的代码命令
- react:监听数据的改变,来帮组我们渲染DOM,生成DOM DIFF,然后将虚拟DOM变为真实DOM。
- vue
基于上述总结,React相对于Vue在功能层面,少实现了一个由视图变动直接改动数据,进一步说就是没有实现当用户操作表单元素的时候修改对应的数据。在项目选取框架的时候基于项目本身表单元素的多少来选取。俩者在很多层面都差不多,都让我们告别了操作DOM的时代。俩者都是渐进式框架,在实现移动端的需求上,react分支出react-native,帮组我们很好的实现移动界面。
3.VUE双向数据绑定的实现原理
Vue 主要通过以下 4 个步骤来实现数据双向绑定的:
- 实现一个监听器 Observer: 对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
- 实现一个解析器 Compile: 解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
- 实现一个订阅者 Watcher: Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
- 实现一个订阅器 Dep: 订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。
基于Object.defineProperty实现的底层原理
//observer:观察者
function observer(obj) {
if (obj && typeof obj === 'object') {
for (let key in obj) {
if (!obj.hasOwnProperty(key)) break;
defineReactive(obj, key, obj[key]);
}
}
}
function defineReactive(obj, key, value) {
observer(value);
Object.defineProperty(obj, key, {
get() {
return value;
},
set(newValue) {
observer(newValue);
if (value === newValue) return;
value = newValue;
}
});
}
function $set(data, key, value) {
defineReactive(data, key, value);
}
4.VUE中的组件(component)
组件就相当于架构模式中的子系统,组件有以下优良的特征:
①:基于组件的开发,我们提取公共的组件,有助于组件复用,方便项目快速开发,而且能构建敏捷化开发平台②:方便团队协作开发,可以单独开发,单独维护③在vue中每一个组件都是一个独立个体,不会与其他的组件的数据混淆。
组件分类
-
全局组件: 在任何组件中可以直接使用(不需要引入,直接在组件模板中调用即可)
- 组件名字的规范
- kebab-case: 只能
<kebab-base>
调用 - PasalCase:既可类似前种方式调用,也可以
<pasalcase>
方式调用(渲染的时候会把所有单词字母都渲染为小写,但是在组件中可以大写)
- kebab-case: 只能
在其它的TEMPLATE语法中,基于
<BtnList></BtnList>
或者<btn-list></btn-list>
都可以正常的调用,只有在new Vue基于el指定的视图中,才只能用<btn-list></btn-list>
这种模式-
调用组件的细节规范
- 采用双闭合方式
- 单闭合方式不符合W3C规范并且只能识别一个
-
template
- 每一个组件只能有一个根元素; template中最外层只有一个元素
- 采用ES6模板字符串模式
- template标记方式
- slot插槽处理 预留插槽:让组件自定义扩展内容
- data是一个闭包函数 返回的对象中初始化数据 (只有这样最后把N多组件放在一起渲染,相互之间的DATA才不会冲突。 data:function(){...} => data(){...})
<body> <div id="app"> <h3> 插槽</h3> <page> <!-- 具名插槽指定内容的时候需要用TEMPLATE把内容包起来: v-slot:[name] 把模板中的内容放置到指定的命名插槽下 剩余不指定的都是插入到默认的插槽中<slot> --> <template v-slot:header> <div>我是页眉</div> </template> <template v-slot="z"> <!-- z存储 模板传递给该插槽所用的数据 --> <!--如果没有指定为默认,则以当前这一个为主,用来接收模板传过来的数据, 如果设置了加了default即v-slot:default="zz" 则会以设置default的这个为主--> <p>{{z.content}}是一个好男孩</p> </template> <template v-slot:footer> <div>我是页尾</div> </template> </page> </div> <!-- 具名插槽:在模板中给预留的插槽设置名字 --> <template id="pageTemplate"> <div class="box"> <slot name="header"></slot> <!-- 给插槽的代码提供数据 --> <slot :content="content"></slot> <p>学习使我快乐!zZ</p> <slot name="footer"></slot> </div> </template> <!-- IMPORT JS --> <script src="node_modules/vue/dist/vue.min.js"></script> <script> Vue.component('page', { template: '#pageTemplate', data() { return { content:'zZ先森' }; } }); let vm = new Vue({ el: '#app' }); </script> </body>
- 组件名字的规范
-
局部组件: 基于components属性声明组件:想用哪个组件需要先声明
- 创建局部组件
const [name]=[options];
- 局部组件需要注册一下才能在视图中使用
const BaseInfo = { template: '#baseInfoTemplate', data() { return { content: '学习使我快乐!' }; } }; let vm = new Vue({ el: '#app', // 局部组件需要注册一下才能在视图中使用 components: { // "base-info": BaseInfo BaseInfo }, data: {} });
- 创建局部组件
5.VUE生命周期函数
可谓是一个个鲜活的生命在服务于各个在使用VUE框架的码农~
- beforeCreate: 创建实例之前;
- 初始化 注入&校验 把data、methods、props、computed、provide、watch...依次挂载到实例上
methods 中普通的方法和computed中的方法区别在于后者存在缓存,如果数据值没有变直接调取缓冲中的结果,不会再走函数体
- created: 实例创建完成(一般在这里做数据的异步请求,保证数据尽快拿回来);
v-if
vsv-show
:前者是控制组件是否存在,当v-if
值为true则存在,值为false则销毁不存在, 从而控制组件的生命周期。后者是控制组件是否显示,不会引发组件的生命周期函数
- beforeMount 渲染DOM之前,编译模板,把模板放到render函数中。
- mounted 编译完成,并将数据放到指定位置
上述生命周期函数在生命周期内只执行一次
- beforeUpdate 数据更新之前 (数据更新控制DOM重新渲染)
- update 重新渲染完成
- beforeDestroy 销毁之前
- destroyed 销毁完成