MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。
通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
图示
M
V
C
优点
耦合性低
视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。
模型是自包含的,并且与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。如果把数据库从MySQL移植到Oracle,或者改变基于RDBMS数据源到LDAP,只需改变模型即可。一旦正确的实现了模型,不管数据来自数据库或是LDAP服务器,视图将会正确的显示它们。由于运用MVC的应用程序的三个部件是相互独立,改变其中一个不会影响其它两个,所以依据这种设计思想能构造良好的松耦合
重用性高
随着技术的不断进步,需要用越来越多的方式来访问应用程序。MVC模式允许使用各种不同样式的视图来访问同一个服务器端的代码,因为多个视图能共享一个模型,它包括任何WEB(HTTP)浏览器或者无线浏览器(wap),比如,用户可以通过电脑也可通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面使用。例如,很多数据可能用HTML来表示,但是也有可能用WAP来表示,而这些表示所需要的命令是改变视图层的实现方式,而控制层和模型层无需做任何改变。由于已经将数据和业务规则从表示层分开,所以可以最大化的重用代码了。模型也有状态管理和数据持久性处理的功能,例如,基于会话的购物车和电子商务过程也能被Flash网站或者无线联网的应用程序所重用。
生命周期成本低
MVC使开发和维护用户接口的技术含量降低。
部署快
使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上。
可维护性高
分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。
有利软件工程化管理
由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化管理程序代码。控制器也提供了一个好处,就是可以使用控制器来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后选择视图将处理结果显示给用户。
缺点
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
MVVM(Model–View–Viewmodel)是一种软件架构模式
M
模型
V
视图
VM
视图模型
Binder
优点
缺点
1、全局安装cli
2、初始化项目
3、进入项目
4、安装依赖
5、启动项目
在实例初始化之后,数据观测(data observer)和event/watcher 事件配置之前调用
实例已经已经创建完成之后被调用。在这一步,实例一完成了以下配置:数据观测(data observer), 属性和方法的运算,watch/event 事件回调,但是挂载阶段还未开始,$el 属性目前还是undefined
在挂载开始直接被被调用;相关的render韩式首次被调用。该钩子在服务器端渲染期间不被调用
el 被薪创建的vm. e l 替 换 , 并 挂 载 到 实 例 上 去 之 后 调 用 该 钩 子 , 如 果 r o o t 实 例 挂 载 了 一 个 文 档 内 元 素 , 当 m o u n t e d 被 调 用 时 v m . el 替换,并挂载到实例上去之后调用该钩子,如果root 实例挂载了一个文档内元素,当mounted 被调用时 vm. el替换,并挂载到实例上去之后调用该钩子,如果root实例挂载了一个文档内元素,当mounted被调用时vm.el 也在文档内。
v-pre主要用来跳过这个元素和它的子元素编译过程。可以用来显示原始的Mustache标签。跳过大量没有指令的节点加快编译。
这个指令是用来保持在元素上直到关联实例结束时进行编译。
在页面加载时会闪烁,先显示:
然后才会编译为:
也是用根据条件展示元素,和v-if不同的是,如果v-if的值是false,则这个元素被销毁,不在dom中。但是v-show的元素会始终被渲染并保存在dom中,它只是简单的切换css的dispaly属性。
用v-for指令根据遍历数组来进行渲染
v-model会忽略所有表单元素的value、checked、selected特性的初始值。因为它选择Vue实例数据做为具体的值。
- v-model修饰符
- <1> .lazy
默认情况下,v-model同步输入框的值和数据。可以通过这个修饰符,转变为在change事件再同步。
- <2> .number
自动将用户的输入值转化为数值类型
- <3> .trim
自动过滤用户输入的首尾空格
v-on主要用来监听dom事件,以便执行一些代码块。表达式可以是一个方法名。
v-on事件修饰符
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: ‘
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: ‘/foo’, component: Foo },
{ path: ‘/bar’, component: Bar }
]
// 3. 创建 router 实例,然后传 routes
配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount(’#app’)
响应路由参数的变化
{ path: ‘/user/:id’, component: User }
响应路由参数的变化
复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象:
const User = {
template: ‘…’,
watch: {
$route(to, from) {
// 对路由变化作出响应…
}
}
}
或者使用 2.2 中引入的 beforeRouteUpdate 导航守卫:
const User = {
template: ‘…’,
beforeRouteUpdate(to, from, next) {
// react to route changes…
// don’t forget to call next()
}
}
router.push(location, onComplete?, onAbort?)
// 字符串
router.push(‘home’)
// 对象
router.push({ path: ‘home’ })
// 命名的路由
router.push({ name: ‘user’, params: { userId: ‘123’ }})
// 带查询参数,变成 /register?plan=private
router.push({ path: ‘register’, query: { plan: ‘private’ }})
router.replace(location, onComplete?,onAbort?)
router.go(n)
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)
User
这跟下面这个代码是一个意思
router.push({ name: ‘user’, params: { userId: 123 } })
重定向
别名
全局前置守卫
const router = new VueRouter({ … })
router.beforeEach((to, from, next) => {
// …
})
每个守卫方法接收三个参数:
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
全局解析守卫
全局后置钩子
路由独享的守卫
routes: [
{
path: ‘/foo’,
component: Foo,
beforeEnter: (to, from, next) => {
// …
}
}
]
组件内守卫
最后,你可以在路由组件内直接定义以下路由导航守卫:
beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave
const Foo = {
template: ...
,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 this
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 this
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 this
}
}
完整的导航解析流程
1、vue异步组件加载
2、es import()方法
3、webpack提供的require.ensure(),
移动端适配
state: {
userInfo: [],
name:‘张飞’,
title: “标题”
},
单一状态树
写法
获取方式
this.$store.state
mapState
写法:
getters: {
getUserInfo: state => state.userInfo
},
写法
可以认为是 store 的计算属性
获取方式
this.$store.getters.getUserInfo // -> [{ id: 1, text: ‘…’, done: true }]
mapGetters
mutations: {
setUserInfo(state, data) {
state.userInfo = data
}
},
写法
mutations: {
setUserInfo(state, data) {
state.userInfo = data
}
},
使用常量替代 Mutation 事件类型
import Vuex from ‘vuex’
import { SOME_MUTATION } from ‘./mutation-types’
const store = new Vuex.Store({
state: { … },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
Mutation 必须是同步函数
触发方式
触发 $store.commit(‘title’, ‘标题二’)
mapMutations
this.add()
映射为 this.$store.commit('increment')
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit(‘increment’)
}
}
})
写法
触发方式
// 以载荷形式分发
store.dispatch(‘incrementAsync’, {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: ‘incrementAsync’,
amount: 10
})
mapActions
this.increment()
映射为 this.$store.dispatch('increment')
mapActions
也支持载荷:this.incrementBy(amount)
映射为 this.$store.dispatch('incrementBy', amount)
actions是异步操作
const moduleA = {
state: () => ({ … }),
mutations: { … },
actions: { … },
getters: { … }
}
const moduleB = {
state: () => ({ … }),
mutations: { … },
actions: { … }
}
const store = new Vuex.Store({
modules: {
moduleA,
moduleB,
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
全局注册
HTML
new Vue({ el: ‘#app’ })
局部注册
在用到的页面注册即可
new Vue({
el: ‘#app’,
components: {
‘component-a’: ComponentA,
‘component-b’: ComponentB
}
})
自定义属性 props
父组件可以使用 props 把数据传给子组件。
组件props中声明的属性
// 父组件
:is-visible=“false”
:on-change=“handlePropChange”
:list=[22,33,44]
title=“属性Demo”
class=“test1”
:class="[‘test2’]"
:style="{ marginTop: ‘20px’ }" //注意:style 的优先级是要高于 style
style=“margin-top: 10px”>
// 子组件
props: {
name: String,
type: {
//从父级传入的 type,它的值必须是指定的 ‘success’, ‘warning’, 'danger’中的一个,如果传入这三个以外的值,都会抛出一条警告
validator: (value) => {
return [‘success’, ‘warning’, ‘danger’].includes(value)
}
},
onChange: {
//对于接收的数据,可以是各种数据类型,同样也可以传递一个函数
type: Function,
default: () => { }
},
isVisible: {
type: Boolean,
default: false
},
list: {
type: Array,
// 对象或数组默认值必须从一个工厂函数获取
default: () => []
}
}
原生属性attrs
特殊属性class、style
$emit
子组件可以使用 $emit 触发父组件的自定义事件
普通事件与修饰符事件
事件驱动
先通过特定的选择器查找到需要操作的节点 -> 给节点添加相应的事件监听
数据驱动
是指视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据
普通事件
修饰符事件
<1> .lazy
默认情况下,v-model同步输入框的值和数据。可以通过这个修饰符,转变为在change事件再同步。
插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。
普通插槽
默认插槽
父组件
默认插槽:default slot
子组件
具名插槽
父组件
具名插槽:title slot1
具名插槽:title slot2
子组件
缩写:例如 v-slot:header 可以被重写为 #header
作用域插槽
父组件
作用域插槽:item slot-scope {{ props }}
子组件
父组件
// 父组件
:is-visible=“false”
:on-change=“handlePropChange”
:list=[22,33,44]
title=“属性Demo”
class=“test1”
:class="[‘test2’]"
:style="{ marginTop: ‘20px’ }" //注意:style 的优先级是要高于 style
style=“margin-top: 10px”>
父传子
:type=‘type’
:style="{ marginTop: ‘20px’ }" //注意:style 的优先级是要高于 style
:on-change=“handlePropChange”
name=‘属性’
:show.sync=“show” 使用sync 进行父子组件数据双向绑定
1)不能和表达式一起使用(如v-bind:title.sync="doc.title + ‘!’“是无效的);
2)不能用在字面量对象上(如v-bind.sync=”{ title: doc.title }"是无法正常工作的)。
子组件
通过props接收父组件的传值
name: String,
type: {
validator: (value) => {
return [‘success’, ‘warning’, ‘danger’].includes(value)
}
},
//从父级传入的 type,它的值必须是指定的 ‘success’, ‘warning’, 'danger’中的一个,如果传入这三个以外的值,都会抛出一条警告
onChange: {
type: Function,
default: () => { }
},
//对于接收的数据,可以是各种数据类型,同样也可以传递一个函数
isVisible: {
type: Boolean,
default: false
},
list: {
type: Array,
default: () => []
}
// 对象或数组默认值必须从一个工厂函数获取
通过$emit 触发父组件
兄弟组件
bus.js
import Vue from ‘vue’
export default new Vue
bus.$emit(“event”, data) 发布消息
import Bus from “./bus.js”
Bus.$emit(‘getTarget’, event.target);
bus.$on(“event”,()=> {}) 接收消息
import Bus from “./bus.js”
created() {
Bus.$on(‘getTarget’, target => {
console.log(target);
});
},
$root
$parent
ref
你可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用,直接访问子组件
this.$refs.usernameInput
这样就允许父级组件通过下面的代码聚焦 里的输入框:
this.$refs.usernameInput.focus()
当 ref 和 v-for 一起使用的时候,你得到的 ref 将会是一个包含了对应数据源的这些子组件的数组。
注意:$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。
循环引用
递归组件
组件之间的循环引用
vm.$forceUpdate() 强制更新
activated() {
window.addEventListener(‘scroll’, this.handleScroll)
},
deactivated() {
window.removeEventListener(‘scroll’, this.handleScroll)
},
不解绑全局事件可能会出现跟中bug
activated: 表示在页面展示的时候触发
deaatived 表示在页面即将被隐藏或替换成新的页面的时候触发