开发模式MVVM模式
双向数据绑定
虚拟DOM技术
●低耦合:视图(View) 可以独立于Model变化和修改,一个ViewModel可以绑定到不同的
View上,当View变化的时候Nodel可以不变,当Model变化的时候View也可以不变。
●可复用: 你可以把一些视图逻辑放在一 个ViewModel里面,让很多View重用这段视图逻辑。
●独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel) .设计人员可以专注于页面设计。
●可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
CDN安装
本地安装下载
NPM安装
<body>
<div id="app">{{message}}div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el:'#app',
data:{
message:'hello Vue'
}
})
script>
body>
这里是最简单的一个例子,也就是在页面中输出一个hello Vue。
我们在网页的控制台可以动态的改变我们的网页显示信息,这里就是双向绑定的一个提现,不用刷新页面,我们的数据就可以动态改变
v-bind
绑定元素特性
<body>
<div id="app">
<span v-bind:title="message">鼠标悬停几秒钟产看此处动态绑定的信息span>
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el:'#app',
data:{
message:'hello wqh'
}
})
script>
body>
我们的网页悬停会提示一个hello wqh,当然这里的span元素可以换成a等等标签
你看到的v-bind等被称为指令。指令带有前缀v—.以表示它们是Vue提供的特殊特性。可能你已经猜到了,它们会在渲染的DOM上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的title 特性和Vue实例的message属性保持一致”.
v-if v-else
条件判断语句
<body>
<div id="app">
<h1 v-if="ok">Yesh1>
<h1 v-else>Noh1>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el:"#app",
data:{
ok: true
}
});
script>
body>
我们的Yes会输出,而我们的else网页上根本没有这个元素,没有渲染他
<body>
<div id="app">
<h1 v-if="type==='A'">Ah1>
<h1 v-else-if="type==='B'">Bh1>
<h1 v-else-if="type==='C'">Ch1>
<h1 v-else-if="type==='D'">Dh1>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el:"#app",
data:{
type: 'A'
}
});
script>
body>
数组
<body>
<div id="app">
<li v-for="item in items">
{{item.message}}
li>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el:"#app",
data:{
items: [
{message: '你好Vue'},
{message: '狂神说Vue'},
{message: '第一次拜访'}
]
}
});
script>
body>
<body>
<div id="app">
<li v-for="(item,index) in items">
{{item.message}}----{{index}}
li>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el:"#app",
data:{
items: [
{message: '你好Vue'},
{message: '狂神说Vue'},
{message: '第一次拜访'}
]
}
});
script>
body>
index就是下标 vue的数组下标也是从0开始
<body>
<div id="app">
<button v-on:click="sayHi">click mebutton>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: '狂神说Vue'
},
methods: {//方法必须定义在Vue的Methods对象中
sayHi:function (event) {
alert(this.message);//这里的this指向我们的vm对象
}
}
});
script>
body>
这里的坑 必须是methods
Vue.js是一个MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js的精髓之处了。
值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定。单向数据绑定是使用状态管理工具的前提。如果我们使用vuex ,那么数据流也是单项的,这时就会和双向数据绑定有冲突。
为什么要实现数据的双向绑定
在Vue.js 中,如果使用vuex,实际上数据还是单向的,之所以说是数据双向绑定,这是用的
UI控件来说,对于我们处理表单,Vue.js 的双向数据绑定用起来就特别舒服了。即两者并不互斥,在全局性数据流使用单项,方便跟踪;局部性数据流使用双向,简单易操作。
表单中使用双向绑定
你可以用
v-model指令在表单 、 及 元素上创建双向数据绑
定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
注意: v-model会忽略所有表单元素的value. checked. selected 特性的初始值而总是将
Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data选项中声明初始值!
<body>
<div id="app">
输入的文本:<input type="text" v-model="message"> {{message}}
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el: '#app',
data: {
message: '123'
}
});
script>
body>
我们输入的文本跟后面实现了双向绑定
多行文本框双向绑定
<body>
<div id="app">
输入的文本:<textarea v-model="message"> textarea>{{message}}
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el: '#app',
data: {
message: '123'
}
});
script>
body>
跟我们的输入框绑定基本相同,唯一不同就是改标签闭合有要求
<body>
<div id="app">
性别:<input type="radio" name="sex" value="男" v-model="wqh">男
<input type="radio" name="sex" value="女" v-model="wqh">女
<p>
选中了谁{{wqh}}
p>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el: '#app',
data: {
wqh: ''
}
});
script>
body>
单选框的大概意思就是,我们定义一个属性名字随意,我们使用了v-model实现双向绑定,我们选择了哪一个那么,这个属性的值就会被修改,下方也会显示出来我们选中的值
多选同理
下拉框
怎么实现v-model默认选中
<body>
<div id="app">
下拉框:
<select v-model="selected">
<option value="" disabled>----请选择----option>
<option>Aoption>
<option>Boption>
<option>Coption>
select>
<span>{{selected}}span>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el: '#app',
data: {
selected: 'B'
}
});
script>
body>
我们一般是要将第一个选项作为预留位置,如果不写这个则改变不了了就
注意:如果v-model 表达式的初始值未能匹配任何选项, 元素将被渲染为”未选
中”状态。在ios中,这会使用户无法选择第-个选项。因为这样的情况下,ios 不会触发change事件。因此,更推荐像上面这样提供一个值为空的禁用选项。
什么是组件
组件是可复用的Vue实例,说白了就是一组可以重复使用的模板,跟JSTL的自定义标签、
Thymeleaf的th:fragment 等框架有着异曲同工之妙。通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。这些就是组件,也就是我们的自定义标签
简单的例子
<body>
<div id="app">
<wqh>wqh>
div>
<script src="../js/vue.js">script>
<script>
//定义一个Vue组件
Vue.component("wqh",{
template: ' hello '
})
let vm = new Vue({
el: '#app',
});
script>
body>
也就是我们现在的wqh标签实际上就是一个li标签
<body>
<div id="app">
<wqh v-for="item in items" v-bind:wqh="item">wqh>
div>
<script src="../js/vue.js">script>
<script>
//定义一个Vue组件
Vue.component("wqh",{
props: ['wqh'],
template: '{{wqh}} '
})
let vm = new Vue({
el: '#app',
data:{
items : ["Java","Vue","前端"]
}
});
script>
body>
我们要想实现遍历我们的v-bind就需要绑定一个参数,也就是我们组件的props指向的那个参数,这个参数也是将来我们遍历的参数名字,当然这个参数名字随意
Axios是一个开源的可以用在浏览器端和NodeJS 的异步通信框架,她的主要作用就是实现AJAX
异步通信,其功能特点如下:
●从浏览器中创建XMLHttpRequests
●从node.js创建http请求
●支持Promise API [JS中链式编程]
●拦截请求和响应
●转换请求数据和响应数据
●取消请求
●自动转换JSON数据
●客户端支持防御XSRF (跨站请求伪造)
基于ES6写的
Vue实例的生命周期
Vue实例有一个完整的生命周期,也就是从开始创建、物始化数据、编译模板、挂载DOM、渲染+更新+渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。
在Vue的整个生命周期中,它提供了一系列的事件,可以让我们在事件触发时注册JS方法,可以让我们用自己注册的JS方法控制整个大局,在这些事件响应方法中的this直接指向的是Vue的实例。
第一个axios程序
<body>
<div id="vue">
div>
<script src="../js/vue.js">script>
<script src="../js/axios.js">script>
<script>
let vm = new Vue({
el: '#vue',
mounted(){//钩子函数 链式编程
axios.get('data.json').then(response=>{
console.log(response.data)
})
}
});
script>
body>
{
"name": "小Wang_start",
"url": "https://blog.csdn.net/qq_22155255",
"page": 1,
"isNonProfit": true,
"address": {
"street": "郑州路",
"city": "洛阳",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://space.bilibili.com/388485872"
},
{
"name": "小Wang_start",
"url": "https://blog.csdn.net/qq_22155255"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
渲染数据
<style>
[v-clock]{
display: none;
}
style>
head>
<body>
<div id="vue" v-clock>
<div>
{{info.name}}
{{info.address.street}}
<a v-bind:href="info.url">点我a>
div>
div>
<script src="../js/vue.js">script>
<script src="../js/axios.js">script>
<script>
let vm = new Vue({
el: '#vue',
//data是属性,是我们Vue的属性,我们的Vue还有一个data方法
data(){
return{
//请求的返回参数,必须和json字符串一样
info:{
name:null,
address:{
street:null,
city:null,
country:null
},
url:null
}
}
},
mounted(){//钩子函数 链式编程
axios.get('data.json').then(response=>{
this.info = response.data
})
}
});
script>
body>
我们已经拿到了数据,但是每次刷新请求的时候会发现又闪烁问题,怎么解决这个闪烁问题、display:none
什么是计算属性
计算属性的重点突出在属性两个字上(属性是名词),首先它是个属性其次这个属性有计算
的能力(计算是动词),这里的 计算就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已,可以想象为缓存!
计算出来的结果,保存在属性中~,内存中运行:虚拟DOM
例子
<body>
<div id="app">
<p>currentTime1: {{currentTime1()}}p>
<p>computed---currentTime2: {{currentTime2}}p>
div>
<script src="../js/vue.js">script>
<script>
let vm = new Vue({
el: '#app',
data:{
message: "hello,wqh"
},
methods:{
currentTime1:function () {
return Date.now();//返回当前时间戳
}
},
computed: { //计算属性 methods computed方法不能重名 重名之后只会调用methods中的方法
currentTime2:function () {
this.message;
return Date.now();//返回当前时间戳
}
}
});
script>
body>
我们的方法可以实施刷新,而我们计算属性不能并没有实时刷新,这是为什么呢
因为我们的这个计算属性,有点类似于缓存,第一次计算出来的值被浏览器缓存存储了,他的值所以不会实时刷新改变
结论
调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销;
内容分发
在Vue.js 中我们使用 元素作为承载分发内容的出口,作者称其为插槽,可以应用在组合组件的场景中;
待办事项
比如准备制作-个待办事项组件(todo) ,该组件由待办标题(todo-title) 和待办内容(todo-items)组成,但这三个组件又是相互独立的,该如何操作呢?
<body>
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title">todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" :item="item">todo-items>
todo>
div>
<script src="../js/vue.js">script>
<script>
//插槽组件 slot用来定义插槽
Vue.component("todo",{
template:
'\
\
\
\
\
'
}),
Vue.component("todo-title",{
props: ['title'],
template: '{{title}}'
}),
Vue.component("todo-items",{
props: ['item'],
template: '{{item}} '
})
let vm = new Vue({
el: '#app',
data:{
title: "秦老师列表",
todoItems: ['狂神说Java','wqh学Xue',"狂神说Linux"]
}
});
script>
body>
我们这个例子中,是这样操作的:我们一开始想到要循环li标签来显示书籍列表,比较麻烦,我们想到了组件的形式,这个前面的例子也有提到这样做的好处和便利,然后我们一个todo里面还有标题和列表,我们在这里的标题和列表采用插槽的方式动态插入,那么我们就得定义我们的标题和书籍列表的组件。然后,我们模板写好了,怎么联系起来呢?就涉及到我们怎么绑定插槽数据了:我们在我们的插槽使用name属性绑定我们这个插槽对应的组件,当然我们的视图层也要进行slot绑定我们的组件,其次呢?数据怎么来,数据一定是后台传参,在data中定义获取,那么我们的插槽就需要绑定数据,上面例子中:是v-bind的简写形式,我们利用:来绑定我们的数据,实现了动态获取
自定义事件
引入,上面的例子,我想动态删除书籍列表怎么做呢?
Vue.component("todo-items",{
props: ['item'],
template: '<li>{{item}}<button @click="remove">删除button>li>',
methods: {
remove:function () {
alert("111")
}
}
})
我们修改了这个组件,使组件循环的时候加上了一个删除按钮,然后这个按钮绑定了本组件中的方法remove,然后我们发现,他确实如下图一样,可以绑定上去事件,但是我们的这些数据是在我们的vm实例中,组件手没那么长,是删除不掉实例中的数据的
那我们在实例定义删除方法,然后在组件的按钮中绑定该删除事件可以么?
let vm = new Vue({
el: '#app',
data:{
title: "秦老师列表",
todoItems: ['狂神说Java','wqh学Xue',"狂神说Linux"]
},
methods: {
removeItems:function (index) {
console.log("删除了"+this.todoItems[index]+"OK");
this.todoItems.splice(index,1);//一次删除一个元素
}
}
});
运行发现,是不允许这样写的,那么我们的组件到底怎么绑定这个方法呢?
组件只能绑定当前组件的方法,那么我们能不能在组件的方法中想方设法的去调用我们实例中定义的方法呢?
自定义事件引入
通过以上代码不难发现,数据项在Vue的实例中,但删除操作要在组件中完成,那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递与事件分发了,Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题;使用this.$emit(‘自定义事件名’,参数),操作过程如下:
<body>
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title">todo-title>
<todo-items slot="todo-items" v-for="(item,index) in todoItems"
:item="item" :index="index"
v-on:remove="removeItems(index)" >todo-items>
todo>
div>
<script src="../js/vue.js">script>
<script>
//插槽组件 slot用来定义插槽
Vue.component("todo",{
template:
'\
\
\
\
\
'
}),
Vue.component("todo-title",{
props: ['title'],
template: '{{title}}'
}),
Vue.component("todo-items",{
props: ['item','index'],
template: '{{index}}----{{item}} ',
methods: {
remove:function (index) {
//自定义事件分发
this.$emit('remove',index);
}
}
})
let vm = new Vue({
el: '#app',
data:{
title: "秦老师列表",
todoItems: ['狂神说Java','wqh学Xue',"狂神说Linux"]
},
methods: {
removeItems:function (index) {
console.log("删除了"+this.todoItems[index]+"OK");
this.todoItems.splice(index,1);//一次删除一个元素
}
}
});
script>
body>
我们的想法是这样的:我们的组件没办法直接调用实例中的方法,我们可以绕一圈,这个绕一圈就是自定义事件的做法,使我们的组件可以根据下标来动态删除,因此第一我们要传递我们的下标参数,其次我们的todo-item标签绑定了我们的实例中的删除方法,并个规定了名字是remove(因为这个是前端,是可以调到实例的),那我们现在就只需要把组件中的方法跟前端页面绑定起来,就使用我们的**自定义事件绑定this.$emit(‘自定义事件名’,参数)**将我们的组件的方法跟前端绑定的实例中的方法联系起来,至此我们的功能得以实现