MVVM
MVC 与 前端中的 MVVM 之间的区别
-
M为module V为 views C为 control;MVC 是后端的分层开发概念;
-
MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel
-
MVVM模式
- Model:负责数据存储
- View:负责页面展示
- View Model:负责业务逻辑处理(比如Ajax请求等),对数据进行加工后交给视图展示
- 数据驱动视图,只关心数据变化,DOM操作被封装。
- 为什么有了MVC还要有MVVM—提供的数据的双向绑定(VM提供)
数据驱动视图,只关心数据变化,DOM操作被封装。
MVVM / Vue的三要素
-
响应式:vue 如何监听到 data 的每个属性变化?
-
模板引擎:vue 的模板如何被解析,指令如何处理?
-
渲染:vue 的模板如何被渲染成 html ?以及渲染过程
Vue双向绑定
-
vm的核心是view 和 data
-
当data 有变化的时候它通过Object.defineProperty()方法中的set方法进行监控,并调用在此之前已经定义好data 和view的关系的回调函数,来通知view进行数据的改变
-
而view 发生改变则是通过底层的input 事件来进行data的响应更改
实现
- vue是通过Object.defineProperty()来实现数据劫持的。
- Object.defineProperty( )是用来做什么的?它可以来控制一个对象属性的一些特有操作,比如读写权、是否可以枚举,这里我们主要先来研究下它对应的两个描述属性get和set
varBook= {}
varname= '';
Object.defineProperty(Book, 'name', {
set:function(value) {
name= value;
console.log('你取了一个书名叫做'+ value);
},
get:function() {
return'《'+ name+ '》'
}
})
console.log(Book)
Book.name= 'vue权威指南'; // 你取了一个书名叫做vue权威指南
console.log(Book.name); // 《vue权威指南》
// get 是在读取那么属性的时候触发的
// set 是在设置属性值的时候触发的
实例化一个Vue对象时,如new Vue一个实例对象a,其中有一个属性a.b,那么在实例化的过程中,通过Object.defineProperty()会对a.b添加getter和setter,同时Vue.js会对模板做编译,解析生成一个指令对象(这里是v-text指令),每个指令对象都会关联一个Watcher,当对a.b求值的时候,就会触发它的getter,当修改a.b的值的时候,就会触发它的setter,同时会通知被关联的Watcher,然后Watcher就会再次对a.b求值,计算对比新旧值,当值改变了,Watcher就会通知到指令,调用指令的update()方法,由于指令是对DOM的封装,所以就会调用DOM的原生方法去更新视图,这样就完成了数据改变到视图更新的一个自动过程
大致上是使用数据劫持和订阅发布实现双向绑定。通过实例化一个Vue对象的时候,对其数据属性遍历,实例化过程中通过Object.defineProperty()给数据对象添加setter getter,同时Vue.js会对模板做编译解析生成指令对象,每个指令对象绑定一个watcher对象,然后对数据赋值的时候就会触发setter,这时候相应的watcher对其再次求值,如果值确实发生变化了,就会通知相应的指令,调用指令的update方法,由于指令是对DOM的封装,这时候会调用DOM的原生方法对DOM做更新,这就实现了数据驱动DOM的变化。同时vue还会对DOM做事件监听,如果DOM发生变化,vue监听到,就会修改相应的data
Vue生命周期
Vuejs两个特性
1 章 Vuejs简介
数据绑定
组件化
2 章 基础特性
2.1 实例及选项
2.1.1 模板
-
el
-
template
2.1.2 数据
- data设置数据
- 实例化之后加入响应式变量,需要调用set方法(尽量初始化都设置好,对象才都为响应式)
- props获取数据
2.1.3 方法
- methods定义方法,使用
v-on
指令监听事件 - 自定义事件,events对象定义方法,通过实例对象的
$emit
方法触发
2.1.4 生命周期
- activated:动态组件初始化渲染的过程中调用该方法(需要配合动态组件keep-live属性使用)
- deactivated: 动态组件移出过程中调用该方法,配合keep-alive
2.2 数据绑定
Vue.js的核心是一个响应式的数据绑定系统,建立绑定后,DOM将和数据保持同步,这样就无需手动维护DOM,使代码更加简洁易懂,提高效率
2.2.1 数据绑定语法
-
文本插值
{{ message }}
支持单次插值v-once
-
HTML属性
v-bind
-
绑定表达式
{{ name.split('').join('|')}}
//v|u|e -
过滤器
{{ name|uppercase }}
- capitalize首字符大写, uppercase, lowercase, currency ‘货币符号’ [小数位],
- pluralize ‘single’ [double,striple]变复数, json [indent(number)] 空格缩进数,
- (函数名)|debonce [wait(number)]延迟执行, limitBy limit(num) [offset(num)]显示数组,
- filterBy ,orderBy
-
指令
-
参数 src
-
修饰符 .stop(特殊函数)
<img v-bind:src="avatar" /> <button v-on:click.stop="doClick"></button>
-
2.2.2 计算属性
- 基础例子
var vm=new Vue({
el:'#app',
data:{
firstname:'zoe',
lastname:'xyf',
},
computed:{
fullname:function(){
return this.firstName+this.lastname
}
}
})
- Setter ,使用 get 和 set 在传递给后端时 无需再手动转化数据
2.2.3 表单控件
- text , v-model=“message”
- rodio ,v-model="gender "(单)
- checkbox ,v-model=“checked “(单) ,v-model=” multiChecked” …(多)
- select ,v-model="selected "/v-model=“multiSelected” multiple
- 绑定value
v-bind
- 参数特性 trim modifier
2.2.4 Class与Style绑定
- Class绑定
- 对象语法 v-bind:class=“{‘active’:active ,‘unactive’: !active}”
- 数组语法 v-bind:class="[classA,classB]"
- 内联样式绑定
- 对象语法 v-bind:style="{ }"/或 alertstyle={},直接用名字
- 数组语法 v-bind:style="[styleObiectA,styleObjectB]"
- 自动添加前缀
2.3 模板渲染
2.3.1前后端渲染对比
- 前端:业务分离,计算量转移
- 后端:对搜索引擎友好,首页加载时间短
2.3.2 条件渲染
- v-if / v-else , v-if切换元素引起dom操作级别的变化;惰性; v-else若使用,必紧跟v-if
- v-show ,只是切换元素的display:none属性,仅发生样式变化;开始必渲染
- v-if有更高的切换消耗,而v-show有更高的初始渲染消耗
2.3.3 列表渲染(v-for)
-
v-for=“item in items” 数组各项
-
v-for="(index,item) in items" 数组index和各项
- 无法通过索引改数组 (可用set方法解决)vm.$set(‘item[0]’,{title:‘zoexyf’})
- 无法改变数组长度
-
v-for="(key,value) in item" 对象key和值
-
v-for=" n in 5" 单个整数做循环次数
2.3.4 template标签用法
< template v-if=“yes”>
< template v-for=“item in items”>
2.4 事件监听与绑定
< button v-on:click="say">Say< /button>
2.4.1 方法及内联语句处理器
v-on:后参数接受所有原生事件名称(函数样式获取不到event ) 缩写 @
内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入
2.4.2 修饰符
修饰符是由点开头的指令后缀来表示的。
.stop
等同于调用event.stopPropagation().prevent
等同于调用event.preventDefault().capture
使用capture模式添加事件监听器.self
只当事件是从监听元素本身触发时才触发回调.once
.passive
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
Vue 提供了绝大多数常用的按键码的别名:
.enter .tab .delete (捕获“删除”和“退格”键) .esc .space .up .down .left .right
可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
2.4.3 与传统事件绑定的区别
- 无需手动管理
- 解耦,和DOM无关,利于写自动化测试用例
2.5 Vue.extend()
生成可被重复调用的子组件
var Child=Vue.extend({
template:'#child',
//el,data选项需要通过函数返回值赋值 ,避免多个组件实例共用一个数据
data:function(){
return .....
}
})
Vue.component('child',Child)//全局注册子组件
//<child ... ></child>子组件在其他组件内的调用方式
3 章 指令
3.1 内置指令
- v-bind
- 缩写 :
- .camel 转驼峰命名
- v-model
- v-if/v-else/v-show
- v-for
- v-bind:key
<div v-for="item in items" v-bind:key="item.id">
- v-bind:key
- v-on 简写@
- v-text
- v-HTML
- v-el ,为DOM元素注册索引,加连字符转大小写
- v-ref,同上,用于子组件
- v-pre ,跳过编译这个子元素,
- v-cloak ,隐藏未编译的标签知道实例准备完毕
- v-once, 标明元素或组件只渲染一次
3.2 自定义指令基础
注册,全局 Vue.directive(id,definition)
;
组件的局部注册:
var comp=Vue.extend({
directives:{
'localDirective':{}
}
})
指令的定义对象
//<div v-if="isExist" v-my-directive="param"></div>
Vue.directive('my-directive',{
//bind:只被调用一次,在指令第一次绑到元素上时调用
bind:function(){
console.log('bind',arguments)
},
//update:发生变化时候调用,参数为newVale,oldValue
//只需要update函数时候,可以直接传入一个函数代替定义的对象
update:function(newValue,oldValue){
cosole.log('update',newValue,oldValue)
},
//inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
//componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
//UNbind:指令从元素上解绑的时候调用,只调用一次
unbind:function(){
console.log('unbind',arguments)
}
})
var vm=new Vue({
el:'#app',
data:{
param:'first',
isExist:'true'
}
})
指令钩子函数会被传入以下参数:
el
:指令所绑定的元素,可以用来直接操作 DOM 。- binding:一个对象,包含以下属性:
name
:指令名,不包括v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。
vnode
:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
4 章 过滤器
4.1 过滤器注册
Vue.filter('dateFormat',function(dataStr,pattern="YYYY-MM-DD HH:MM"){
return moment(dataStr).format(pattern)
})
//使用: {{date|dateFormat}}
5章 过渡
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡
- 条件渲染 (使用
v-if
) - 条件展示 (使用
v-show
) - 动态组件
- 组件根节点
这里是一个典型的例子:
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
new Vue({
el: '#demo',
data: {
show: true
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
JavaScript 钩子
可以在属性中声明 JavaScript 钩子
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
// ...
methods: {
// --------
// 进入中
// --------
beforeEnter: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// --------
// 离开时
// --------
beforeLeave: function (el) {
// ...
},
// 当与 CSS 结合使用时
// 回调函数 done 是可选的
leave: function (el, done) {
// ...
done()
},
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
这些钩子函数可以结合 CSS transitions/animations
使用,也可以单独使用。
当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。
推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false"
,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。
一个使用 Velocity.js 的简单例子:
<!--
Velocity 和 jQuery.animate 的工作方式类似,也是用来实现 JavaScript 动画的一个很棒的选择
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<div id="example-4">
<button @click="show = !show">
Toggle
</button>
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:leave="leave"
v-bind:css="false"
>
<p v-if="show">
Demo
</p>
</transition>
</div>
new Vue({
el: '#example-4',
data: {
show: false
},
methods: {
beforeEnter: function (el) {
el.style.opacity = 0
el.style.transformOrigin = 'left'
},
enter: function (el, done) {
Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
Velocity(el, { fontSize: '1em' }, { complete: done })
},
leave: function (el, done) {
Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
Velocity(el, {
rotateZ: '45deg',
translateY: '30px',
translateX: '30px',
opacity: 0
}, { complete: done })
}
}
})
列表的排序过渡
<transition-group>
组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 特性,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name
属性来自定义前缀,也可以通过 move-class
属性手动设置
6 章 组件
组件注册
var MyComponent=Vue.extend({ template:'<p>template</p>' });
全局注册
Vue.component(‘my-component’,MyComponent)
这条语句需要写在 Var vm=new Vue({})之前,注册成功后 ,就可以在模块中以自定义元素的形式使用组件
局部注册
//全局注册
Vue.component('my-component',{
template:'<p>this is template</p>'
})
//局部注册
var Parent=Vue.extend({
template:'<div>
<p>this is parent template</p>
<my-child></my-child>/
</div>',
components:{
'my-child':{
template:'<p>this is a child component</p>'
}
}
})
6.2 组件选项
组件选项中与VUe选项中的区别
//el和data,在Vue构造器中是直接赋值
var vm=new vue({
el:'#app',
data:{
name:'Vue'
}
})
//在组件中的定义--->data必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
var MyComponent=Vue.extend({
data:function(){
return {
name:'component'
}
}
})
组件props
props组件:父子桥梁的作用
当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。为了给博文组件传递一个标题,我们可以用一个 props
选项将其包含在该组件可接受的 prop 列表中:
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
6.3 组件间通信
直接访问
- $parent
- $children
- $root
自定义事件监听
- events选项
- $on
自定义事件触发机制
- $emit, 实例本身上触发事件
- $dispatch,派发事件,事件沿着父链向上冒泡,并且在第一次触发回调之后自动停止冒泡,除非触发函数明确返回true,才会继续向上冒泡
- $broadcast,广播事件,事件会向下传递给所有的后代
6.4 内容分发
父组件的内容代替了子组件中的slot标签,可以在不同地方使用子组件的结构而且填充不同的父组件内容,从而提升组件的复用性
插槽
slot
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
- v-slot ,缩写#
解构插槽 Prop
作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里:
function (slotProps) {
// 插槽内容
}
6.5 动态组件
- keep-alive
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
7章 vuejs 常用插件
7.1 Vue-router
Vue-router,是给Vuejs提供管理路由的插件,利用hash变化控制动态组件的切换
便于前后端分离
Vue-router的基本作用就是将每个路径映射到对应的组件,并通过修改路由进行组件间的切换
//router.js,先安装好路由插件,
import VueRouter from 'vue-router'
//导入对应的路由组件
import HomeContainer from './components/tabbar/HomeContainer.vue'
var router = new VueRouter({
routes: [
//配置路由规则的
{path:'/',component: HomeContainer},
]
)}
// 把路由对象暴露出去
export default router
<router-link to="/home/strange">
<img src="../../images/men3.png" alt="">
<div class="mui-media-body">可疑情况</div>
</router-link>
- 将标签改为 router-link,不要忘记标签闭合,不要忘记 to
7.2 Vue-resource
通过 npm install vue-resource安装,
import VueResource from 'vue-resource'
Vue.use(VueResource)
- 使用this.$http或者全局的Vue.http发起异步请求
- thisi.$http支持promise模式,使用 .then 方法处理回调函数,接受成功/失败两个回调函数,一般会在回调函数中再调用transition.next()方法,给组件中的data对象赋值,并执行组件的下一步骤
- this.$http.get(…).then()的方式返回处理值
export default {
data(){
return {
lunbotuList:[]//保存轮播图的数组
}
},
created(){
this.getLunbotu();
},
methods:{
getLunbotu(){
//获取轮播图的方法
this.$http.get('https://www.easy-mock.com/mock/5d29ce74575b0e0f11ff8bc5/childrenClick.com/api/getlunbo').then(result=>{
// console.log(result.body)
// console.log(result.body.status)
if(result.body){
//成功了
this.lunbotuList=result.body.rows;
}else{
//失败
Toast('加载轮播图失败')
}
console.log(this.lunbotuList)
})
}
},
components:{
swiper
}
}
拦截器
- 拦截器主要作用于给请求添加全局功能,例如身份验证、错误处理等,在请求发送给服务器之前或服务器返回时对request/response进行拦截修改,完成业务逻辑之后再传递给下一步骤。Vue-resource也提供了拦截器的具体实现方式
Vue.http.interceptors.push(function(request,next){
//修改请求
request.method='POST'
//继续进入下一个拦截器
next()
})
//也可以对返回的response进行处理
Vue.http.interceptors.push(function(request,next){
//修改请求
request.method='POST'
next(function(response){
response.body='....'
})
})
//或者直接拦截返回response,并不向后端发送请求
Vue.http.interceptors.push(function(request,next){
//body可以自己定义,request.respondWith会将其封装成respomse,并赋值到response.body上
next(request.respondWith(body,{
status:403,
statusText:'Not Allowed'
}))
})
8 章
8.1 准备工具
webpack
- webpack是一款模块加载及处理工具,他能把各种资源,例如JS,css样式 ,图片等都作为模块来处理和使用
- 使用
npm install webpack -g
的方式全局安装webpack,然后在项目根路径下配置一个webpack.config.js文件,P112
vue-loader
- Vue-loader是webpack的一个loader加载器,用于处理我们编写的.Vue 文件
npm install webpack-dev-server vue-loader vue-html-loader
- 有新变化
8.2 目录结构
vue-cli,脚手架生成工具,npm install -g vue-cli
全局安装
- webpack,主要使用这个,
vue init webpack my-project
- 生成的目录结构如下
- build,用于存放webpack相关配置和脚本
- config,主要存放配置文件,用于区分开发环境,测试环境,线上环境的不同
- src,项目源码及需要引用的资源文件
- static,不需要webpack处理的静态资源
- test,用于存放测试文件
- package.json
- 正常开发时,运行
npm run dev
,启动一个小型的express服务
- webpack-simple
- browerify
- browerify-simple
- simple
9章 状态管理:Vuex
Vuex是状态管理模式的一种实现库,主要以插件的形式和Vue.js进行配合和使用,能够使我们在Vuejs中管理复杂的组件事件流
npm install --save vuex
- 建立一个新文件,vuex/store.js
- store用于存储整个应用所需的信息
- 创建好store后,将其注入我们的应用中去,
import Vue from 'vue'
import Vue from 'vuex'
Vue.use(Vuex)
export default new Vuex.store({
state,
mutations
})
- action 能够通过分发(diapatch),调用对应的mutation函数,来触发对store的更新
- mutation,,action分发后就由mutation来对store进行更新
vuex的整体流程
- 1.操作组件,单击组件按钮,调用组件中获取action函数
- 2.action dispatch,action 函数不会直接对store数据进行修改,而是通过 diapatch的方式通知到对应的 mutation
- 3.mutation, mutation函数则包含了对store数据的具体修改内容
- 4.store/state,store是包含当前state的单一对象,数据更新后,自动通知到getter函数
- 5.getter ,getter函数从store获取组件所需的数据
- 6.组件展示 ,组件中使用getter函数,获取新的数据,进行展示
严格模式
- 注意不要在生产环境中开启严格模式,严格模式会对state树进行一个深入的观察,会造成额外的性能损耗
const store=new Vuex.Store({
//....,
strict:process.env.NODE_ENV!=='production'
})
中间件
- middlewars选项来加载中间件,是一个对象
10 章 跨平台开发
新文件,vuex/store.js
- store用于存储整个应用所需的信息
- 创建好store后,将其注入我们的应用中去,
import Vue from 'vue'
import Vue from 'vuex'
Vue.use(Vuex)
export default new Vuex.store({
state,
mutations
})
- action 能够通过分发(diapatch),调用对应的mutation函数,来触发对store的更新
- mutation,,action分发后就由mutation来对store进行更新
vuex的整体流程
- 1.操作组件,单击组件按钮,调用组件中获取action函数
- 2.action dispatch,action 函数不会直接对store数据进行修改,而是通过 diapatch的方式通知到对应的 mutation
- 3.mutation, mutation函数则包含了对store数据的具体修改内容
- 4.store/state,store是包含当前state的单一对象,数据更新后,自动通知到getter函数
- 5.getter ,getter函数从store获取组件所需的数据
- 6.组件展示 ,组件中使用getter函数,获取新的数据,进行展示
严格模式
- 注意不要在生产环境中开启严格模式,严格模式会对state树进行一个深入的观察,会造成额外的性能损耗
const store=new Vuex.Store({
//....,
strict:process.env.NODE_ENV!=='production'
})
中间件
- middlewars选项来加载中间件,是一个对象
10 章 跨平台开发