MVVM:view-viewmodel-model:view和model是不可以直接通信的,它们之间存在viewmodel这个中介,当用户操作view,viewmodel感知到变化,然后通知model发生相应改变,反之亦然。viewmodel向上与view进行双向数据绑定,向下与model通过接口请求进行数据交互,起到承上启下的作用
<div id="vue_det">
<h1>site : {{site}}</h1>
<h1>url : {{url}}</h1>
<h1>{{details()}}</h1>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#vue_det',
data: {
site: "菜鸟教程",
url: "www.runoob.com",
alexa: "10000"
},
methods: {
details: function() {
return this.site + " - 学的不仅是技术,更是梦想!";
}
}
})
</script>
每个Vue应用都需要通过实例化Vue来实现,如下:
var vm = new Vue({
// 选项
})
在Vue构造器中有一个el参数,它是DOM元素中的id,在上面的实例中id为vue_det
当一个Vue实例被创建时,它向Vue的响应式系统中加入了其data对象中能找到的所有的属性,当这些属性的值发生变化时,html视图也会产生相应的变化
在上面的实例中,vm.site与data.site相同,即vm.site === data.site为true,单独修改其中一个也会影响到另外一个,因为他们指向相同
除了数据属性,Vue实例还提供了一些有用的实例属性与方法,它们都有前缀$,以便与用户定义的属性区分开来,如:
document.write(vm.$data === data) // true
document.write(vm.$el === document.getElementById('vue_det')) // true
Vue生命周期
每个Vue实例在创建时都要经过一系列的初始化过程,这就是生命周期,在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段不断添加自己代码的机会
Vue指令:
v-text:插值更新数据,无闪动问题,非双向绑定
<span v-text="msg"></span>
<span>{{msg}}</span>
这两条语句用法相同,都是从data中获取msg的值
v-html:更新元素的innerHTML,内容按普通HTML插入,不会作为Vue模板进行编译
<div v-html="html"></div>
注意:在网页上动态渲染任意HTML是非常危险的,因为容易导致XSS攻击,所以只能在可信内容上使用v-html,永不用在用户提交的内容上
在单文件组件里,scoped的样式不会应用在v-html内部,因为那部分HTML没有被Vue的模板编译器处理,可以通过额外的全局<style>元素实现
v-pre:跳过这个元素和它的子元素的编译过程,可以用来显示原始Mustache标签,跳过大量没有指令的节点会加快编译
<span v-pre>{{ this will not be compiled }}</span>
v-cloak:解决{{message}}表达式存在的闪动问题,先隐藏,等替换好值之后再显示最终的值
官方定义:这个指令保持在元素上直到关联实例结束编译
<div v-cloak> {{ message }} </div> //直到编译结束才会显示message的值
v-show:
<h1 v-show="ok">Hello!</h1>
根据条件展示元素,和v-if不同的是,如果v-if的值是false,则这个元素被销毁,不在DOM中,但是v-show的元素会从一开始就被渲染并始终保存在DOM中,只是简单的切换CSS的display属性
v-if有更高的切换开销,而v-show有更高的初始渲染开销,因此,如果要非常频繁的切换,则使用v-show较好,如果在运行时条件不太可能改变,则v-if较好
v-if:实现条件渲染,Vue会根据表达式的值的真假来渲染元素。在切换时元素及它的数据绑定/组件被销毁并重建
+v-else
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-for:基于源数据多次渲染元素或模板块
<div v-for="item in items">
{{ item.text }}
</div>
一般来说,使用v-for的时候要带上它的特殊属性key,其中key可以根据具体情况绑定数据,每个key的数据应该是唯一的
<div v-for="item in items" :key="item.id">
{{ item.text }}
</div>
v-on:绑定事件监听器,表达式可以是一个方法名(methods中)
<button @click="doThis"></button>
事件修饰符:
.stop:阻止单击事件冒泡
.prevent:提交事件不再重载页面,比如 submit 加了这个,就不会提交了。
.capture:使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
.self:只当事件在该元素本身(而不是子元素)触发时触发回调。在传递的父子事件中,加了这个,无论先点哪个,都先执行这个。
.{ keyCode | KeyAlias }:只当事件是从特定键触发时才触发回调
.native:监听组件根元素的原生事件
.once:事件将只会触发一次
.left:只当点击鼠标左键时才触发
.right:只当点击鼠标右键时才触发
.middle:只当点击鼠标中键时才触发
.passive:告诉浏览器不想阻止事件的默认行为
如:<input @keyup.enter="onEnter">
<button @click.stop.prevent="doThis"></button>
使用修饰符时,顺序很重要,相应的代码会以相应的顺序执行
v-bind:属性绑定,动态地绑定一个或多个特性,如class和style,当数据源变化时,v-bind绑定的标签对应的数会变化,但不是同步变化,即异步;标签对应的数发生变化不会影响数据源,即不是双向绑定
用法:v-bind属性名=“数据名”
<img :src="imageSrc">
对象语法:用{}和键值对的形式,其中red是style里的类名,即.red,isRed通过在data中定义布尔值,来决定red样式的显示与否
<div :class="{ red: isRed }"></div>
数组语法:用[]形式,其中activeClass和errorClass在data中以键值对activeClass:active来定义
<div :class="[ activeClass, errorClass ]"></div>
简化:<div :class="objClass"></div>,其中objClass可以为数组也可以为对象
objClass:[ 'active', 'error' ] 或者objClass:{ active: true, error: true},再分别使用数组和对象的api来操作对应元素
即使还有class,如<div class="base" :class="objClass"></div>,则base类和objClass会一起生效
内联样式:
对象语法
<div :style="{ fontSize: size + 'px' }"></div>,其中size在data中定义
数组语法:
也都可以通过把数据都放在data中定义一个对象或者数组的方式来简化,提高可读性
v-model:在表单控件或者组件上实现双向绑定
<div id="app">
<input v-model="somebody">
<p>hello {{somebody}}</p>
</div>
修饰符:.lazy:在默认情况下,v-model在input事件中同步输入框的值与数据,但是可以添加一个修饰符lazy,从而转变为在change事件中同步
.number:自动将用户的输入值转为Number类型
.trim:自动过滤用户输入的首尾空格
v-model本质上是v-bind和v-on的共同作用,v-bind绑定数据源,v-on绑定标签改变函数,如input输入框中this.msg = event.target.value来实现数据的双向绑定,或者msg = $event.target.value
v-slot:提供具名插槽或需要接收prop的插槽
Vue计算属性
<div id="app">
<p>原始字符串: {{ message }}</p>
<p>计算后反转字符串: {{ reversedMessage }}</p>
</div>
<script> var vm = new Vue({
el: '#app',
data: { message: 'Runoob!' },
computed: { // 计算属性的 getter
reversedMessage: function () { // `this` 指向 vm 实例
return this.message.split('').reverse().join('') } } }) //计算属性一定要有返回值
</script>
在上例中,vm.reversedMessage始终依赖于vm.message,即他们的值始终同步
在vue中methods和computed效果相同,不同为:调用的时候,如果属于methods,调用方法名的同时要加上(),
还有一个不同是:computed是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值,即只有需要计算的数据发生变化时,才重新进行computed中的计算,否则从缓存中取数据,而使用methods,在重新渲染的时候,函数总会重新调用执行,即methods是实时的
计算属性默认只有getter,不过在需要时也可以提供一个setter,即也可以传递参数
computed: {
fullName: {
// getter,即把值返回出去
get: function () { return this.firstName + ' ' + this.lastName },
// setter,即把参数获取回来
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
} } }
现在再运行vm.fullName = 'John Doe'时,setter会被调用,vm.firstName和vm.lastName也会被相应的更新
Vue监听属性(watch)
数据一旦发生变化就通知侦听器所绑定的方法
应用场景:数据变化时执行异步或开销较大的操作,通过在watch中的函数来实现
watch: {
firstName: function(val) {
this.fullName = val + ' ' + this.lastName
}
}
其中,函数名firstName要和被监听的数据名保持一致,val是firstName最新的值(首先要记得把相应的数据绑定到标签上)
Vue过滤器
格式化数据
全局过滤器:位置在var vm=new Vue()上面
Vue.filter( '过滤器名称', function(val) { //val是过滤器所在位置的数据的值
//过滤器业务逻辑,通过return返回值
})
使用:<div>{{msg | upper}}</div>
<div>{{msg | upper | lower }}</div> //可级联使用
<div :id="id | formatId"></div>
也可以传递参数
Vue.filter( 'format', function(val,arg1) { //val是过滤器所在位置的数据的值
//过滤器业务逻辑,通过return返回值
})
使用:
<div>{{date | format(`yyyy-MM-dd`)}}</div> //参数从第二个开始,第一个是要过滤的值
局部过滤器:位置在var vm =new Vue()里,和data,methods等平级
filters: {
'过滤器名称': function(){ }
}
Vue组件
一个组件其实相当于vue的一个实例,只是不需要new实例化而已。因此,和new Vue一个实例一样,也可以定义data(必须是一个函数)、methods、computed、等等
data:function(){return{ count:0}} //此处的函数其实是一个闭包的环境,即每个组件都有自己的私有变量
子组件与父组件:
将某段代码封装成一个组件,而这个组件又在另外一个组件中引入,那么引入该封装组件的组件叫做父组件,被引入的组件叫做子组件
例如:当创建一个组件时,那个组件名就是子组件,而当在这个组件下创建一个Vue实例时,el需要绑定div,那么这个div就是父组件
全局组件:它们在注册之后可以用在任何新创建的Vue根实例(new Vue)的模板中,在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以互相使用,每个组件都是可重用的,他们各自的数据互不影响
注册:Vue.component(tagName,options),其中tagName为组件名,options为配置对象
注册后,可以使用如下方法来调用:<tagName></tagName>
<div id="app">
//调用该全局组件
<runoob></runoob> </div>
<script>
// 注册
Vue.component('runoob', {
//这个子组件的一个模板,页面的组件标签会被子组件里的模板替换,可以理解成占位符
template: '<h1>自定义组件!</h1>' //这里的template要求必须有一个根元素,例如,如果想放两个按钮:template: '<button></button><button></button>',这样是不对的,应该是
template: '<div><button></button><button></button></div>',同根元素div来包裹它们,而且此处最好用模板字符串(反引号)来写,看起来更加直观
})
// 创建根实例
new Vue({
el: '#app'
})
</script>
局部组件:全局组件往往是不够理想的,在webpack中,全局注册造成了用户下载的JavaScript的无谓增加
在这种情况下,可以通过一个普通的JavaScript对象来定义组件:
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
然后在components选项中定义想要使用的组件:
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
} })
对于components对象中的每个属性来说,其属性名就是自定义元素的名字(冒号前),其属性值就是这个组件的选项对象(冒号后),使用方法和全局组件相同
局部组件只能在注册它的父组件中使用
当然,无论是全局组件还是局部组件,都可以写成一个vue单文件组件放在component文件夹中,使用import导入它
父子组件之间传值
prop是子组件用来接收父组件传递过来的数据的一个自定义属性(一个数组),父组件的数据需要通过props把数据传给子组件,子组件需要显式地用props选项声明prop
当数组中有多个参数时,如果有某个参数没有被传递,则显示undefined
注意:要在子组件props中使用驼峰式写法,在父组件调用时使用短横线形式传参(因为DOM元素的属性不区分大小写,使用驼峰形式可能会出现同名),如menu-title和menuTitle,但是在字符串模板中(反引号内)可以都使用驼峰式
props属性值类型:String、Number(使用v-bind可以得到number类型,否则为string类型)、boolean(使用v-bind可以得到boolean类型,否则为string类型)、Array、Object
//父组件
<div id="app">
<child message="hello!"></child>
</div>
<script>
// 注册,子组件
Vue.component('child', {
// 声明props
props: ['message'],
// 同样也可以在 vm 实例中像 "this.message" 这样使用
template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
el: '#app'
})
</script>
动态prop:利用v-bind动态绑定props的值到父组件的数据中,每当父组件的数据变化时,该变化也会被传导给子组件
<childv-bind:message="parentMsg"></child>
prop是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来
父组件使用props传递数据给子组件,但如果子组件要把数据传递给父组件,则需要使用自定义事件
子组件通过自定义事件向父组件传递信息:
父组件监听子组件的事件
兄弟组件之间传值,使用eventhub事件中心
事件中心:var hub = new Vue() //全局下定义
监听事件: // 在mounted函数中触发hub,因为在此时模板已经就绪
其中使用hub.$on()来监听事件,tom-event为自定义事件名,在这里使用箭头函数是因为箭头函数默认绑定外层this,val是接收的传递值
触发事件://触发兄弟组件的事件
使用hub.$emit()来触发事件,jerry-event是要触发的事件名,2为要传递的参数
销毁事件:hub.$off('tom-event')
使用hub.$off()来销毁事件,其中tom-event为要销毁的事件名
Vue插槽
实现父组件向子组件传递内容
组件插槽
使用:将自定义组件alert-box的<alert-box>错误</alert-box>中的红色内容传给<slot>默认内容</slot>显示,当红色内容为空时,显示默认内容
template:`
具名插槽:按照插槽的名字来匹配
<slot name="header"></slot> 具名匹配 <h1 slot="header"></h1>
<slot></slot> 默认匹配 ,匹配其他没有被具名匹配到的元素 <p>主要内容</p>
作用域插槽:实现父组件对子组件的内容进行加工处理
子组件:根据父组件的条件来操作子组件的显示
template: `
Vue前端交互
接口调用方式:原生ajax、基于JQuery的ajax、fetch、axios
传统格式的URL:schema://host:port/path?query#fragment
schema:协议,如http、https、ftp等
host:域名或者IP地址
port:端口,http默认端口80,可以省略
path:路径,如/abc/a/b/c
query:查询参数,如uname=list&age=12
fragment:锚点(哈希Hash),用于定位页面的某个位置
Restful形式的URL
HTTP请求方式:GET查询、POST添加、PUT修改、DELETE删除
如http://www.123.com/books GET
http://www.123.com/books GET /虽然URL一样,但请求不一样,所以也是不同的URL地址
Promise
回调地狱:因为作词异步调用的结果顺序是不确定的,如果想要确定顺序,则需要一层一层在上层异步语句中进行嵌套,嵌套的多了,就会对可读性和可维护性造成问题
回调地狱可以通过Promise来解决,从语法上讲,Promise是一个对象(构造函数,typeof(promise)为function),从它可以获得异步操作的消息
Promise基本用法:
- 实例化Promise对象,构造函数中传递函数,该函数中用于处理异步任务
- resolve和reject两个参数(这两个参数都是方法,可以调用)用于处理成功和失败两种情况,并通过p.then获取处理结果,当then只有一个函数时,表示只处理正常的情况,不处理异常情况
setTimeout()方法:属于window对象
setTimeout(function(){ alert("Hello"); }, 3000) //3000毫秒,即3秒后弹出Hello
then参数中有两种返回值
1.返回Promise实例对象,如上例,返回的该实例对象会调用下一个then
2.返回普通值,如return ‘hello',此时返回的普通值会直接传递给下一个then,通过then参数中函数的参数接收该值。在这里其实是产生了一个默认的Promise对象,保证then的链式操作可以继续执行
Promise常用API(用法同p.then,如上例)
常用的实例方法:
p.then()得到异步任务的正常结果
p.catch()获取异常信息,和p.then的第二个函数作用相同
p.finally()成功与否都会执行,常用来产生提示信息或销毁资源
常用的对象方法:
Promise.all():并发处理多个异步任务,所有任务都完成才能得到结果
Promise.race():并发处理多个异步任务,只要有一个任务完成就能得到结果
接上例: