目录
一、Vue.js简介
传统网页开发步骤
请求数据—>生成结构—>监听变化—>元素变化—>发送请求—>更新结构
传统网页开发中,首先第一步,通过ajax请求服务器中存储的数据,当数据获取到本地后,第二步,根据数据生成对应的网页结构,形成我们最终看到的网页。第三步,通过一些事件的设置来监听用户对元素的操作以监听用户操作导致的数据变化。第四部,当元素发生变化后,需要将用户操作导致的结果发送给服务器,以告知服务器客户端进行了哪些处理,最后第六步,根据用户的操作更新结构,例如用户点击了删除,需要让网页进行对应的结构修改。
传统网页开发代码书写方式
原生操作演示(传统DOM方式)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原生操作演示</title>
</head>
<body>
<div id="container">
<p id="text">当前数值为:100</p>
<button id="btn1">点击增大</button>
<button id="btn2">点击减小</button>
</div>
<script>
var text = document.querySelector('#text');
var btn1 = document.querySelector('#btn1');
var btn2 = document.querySelector('#btn2');
var num = 100;
btn1.onclick = function(){
num++;
text.innerText = '当前数值为:' + num;
};
btn2.onclick = function(){
num--;
text.innerText = '当前数值为:' + num;
};
</script>
</body>
</html>
jQuery操作演示
jQuery是一个JavaScript库。jQuery作用:简化DOM操作,使DOM操作写法更加简单。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>jQuery操作演示</title>
</head>
<body>
<div id="container">
<p id="text">当前数值为:100</p>
<button id="btn1">点击增大</button>
<button id="btn2">点击减小</button>
</div>
<script src="lib/jquery.js"></script>
<!-- lib文件夹与此html文件同级,内部有jquery.js文件 -->
<script>
var $text = $('#text');
var $btn1 = $('#btn1');
var $btn2 = $('#btn2');
var num = 100;
$btn1.on('click' , function(){
num++;
$text.text ('当前数值为:' + num);
});
$btn2.on('click' , function(){
num--;
$text.text ('当前数值为:' + num);
});
</script>
</body>
</html>
运行结果:
两种方式对比图:
上述两段代码分别采用传统DOM方式和jQuery方式,最后运行结果即功能是完全相同的。二者都是根据id获取元素,然而在进行获取元素的代码书写上,一个是document.querySelector,一个是简写的$,这是jQuery的点击对象。点击事件代码jQuery方式也更简洁。
无论是采用原生DOM操作,还是采用jQuery这种JavaScript库做简化处理,它们所简化的其实知识内容,并没有对逻辑结构进行改变,即书写流程是完全相同的。
传统开发方式存在的问题
1. DOM 操作频繁,代码繁杂
DOM操作是使用基础语法进行操作时非常常用的功能,第一个特点就是——长,书写起来频繁且长,例如querySelector,每次使用都需要写完整代码。当代码又长功能又多时,代码会非常繁杂。
2. DOM 操作与逻辑代码混合,可维护性差
由上述代码可以看到,进行数据操作时,事件内部的数据操作与后续的DOM操作内容修改是完全混合在一起的。当进行内容功能处理时经常会有数据操作逻辑与结构操作逻辑混合的情况,如果后期进行数据维护的修改和结构的修改,那还需要到结合代码中找到需要更改的部分。
3. 不同功能区域书写在一起,可维护性低
当书写一个完整网页功能时,一个网页中的功能按照功能性来说是要分成很多个部分的。例如,轮播图与列表区以及商品区应该是完全独立的,不相关联的功能,书写时它们的结构却书写在同一个HTML文件中,样式书写在同一个CSS文件中,js也可能会书写在同一个JavaScript文件中,那么,当需要对结构做修改时,需要从整个HTML页面中找到对应结构,或者通过CSS文件找到指定样式代码,或者在大量JS中寻找需要更改的逻辑,可维护性是比较差的。
对于这一点,尽管通过jQuery等库进行操作,也无法避免维护性问题,只能简化代码书写
4. 模块之间的依赖关系复杂
网页中的功能越来越多,模块也越来越多,模块与模块之间的功能已经无法自己处理,反而需要通过一些其他工具新的模块帮我们维护已存在模块之间的关系,处理起来比较复杂。
问题分析完毕,提出解决问题的一种方案——Vue.js
Vue.js是什么
Vue.js是一个前端流行框架。
“前端”“流行”指它是前端中非常流行的工具,“框架”——条条框框——规则。
Vue.js官方文档
Vue.js的官网写道,Vue.js是渐进式JavaScript框架。
如何理解Vue.js的渐进式
进行开发时,框架提供了一套规则,我们需要遵守框架规则才能使用框架的功能。如果这个框架需要将项目完整的遵守规则才能够使用的话,那么这个框架可能是比较重的,我们需要将已有代码去迭代成新的代码才能进行开发。所谓的渐进式,指的是在传统网页中某一部分小功能可以通过Vue.js开发,当觉得这种开发方式好用时,可以将一个功能采用Vue.js开发改成三个功能采用Vue.js开发甚至改成更多功能采用Vue.js开发,最终变成整个网站都采用Vue.js开发,这就是所谓的渐进式。
二、Vue.js核心特性
数据驱动视图
数据——传统网页开发中的数据信息;视图——数据所对应的标签结构。
单向数据绑定
传统开发中,需要找到对应的数据并通过DOM操作设置给对应的元素进行处理。在Vue.js中,这个步骤得以简化。
在Vue.js中,数据变化会自动更新到对应元素中,无需手动操作 DOM,这种行为称作单向数据绑定。
下面看一个Vue.js示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue操作演示</title>
</head>
<body>
<div id="app">
<p>当前数值为:{{num}}</p>
<button @click="num++">点击增大</button>
<button @click="num--">点击减小</button>
</div>
<script src="lib/vue.js"></script>
<script>
new Vue({
el:'#app',
data:{
num: 100
}
});
</script>
</body>
</html>
在这段代码中,JS代码仅仅通过new Vue的方式进行了处理操作,内部通过#app选取了网页中id为app的div元素,下面的data数据中指令num为100,这是进行网页展示所需要的数据。在页面功能操作中,两个按钮内部通过@click方式设置了点击事件,内部指令num++与num--,在上面的p标签中通过{{}}的方式将数据绑定到了视图元素中。在整体代码中,仅仅进行了数据的展示与修改操作,并没有书写任何的DOM功能,这就是所谓的数据驱动视图。通过对数据的逻辑修改,数据变化会自动更新到视图中,大大解放了DOM操作。
上面演示了数据驱动视图的第一种表现形式,也是基础表现形式,单向数据绑定,这里的单向指从数据到视图的绑定过程,数据变化导致视图变化。
双向数据绑定
数据驱动视图第二种情况——双向数据绑定,但是想要使用双向数据绑定,需要针对特殊的元素才能操作,这些特殊元素指可输入元素,例如输入框。
对于输入框等可输入元素,可设置双向数据绑定。 双向数据绑定是在数据绑定基础上,可自动将元素输入内容更新给数据, 实现数据与元素内容的双向绑定。
Input、Text等输入框元素,输入框元素在进行内容输入后,这个数据可以自动更新到绑定数据上,绑定的数据如果进行了更改,也会自动的更新到元素中。由视图元素到数据,由数据到视图元素,双向数据绑定。
注意:双向绑定只适用于可输入元素,对于像div标签、p标签、scan标签等不具备输入能力的这种元素,无法设置双向数据绑定。除了普通的输入框外,也有其他元素可以进行双向数据绑定,比如文本域textarea,复选框checkbox等元素都可以进行双向数据绑定。
数据驱动视图实现原理
Vue.js 的数据驱动视图是基于 MVVM 模型实现的。
MVVM (Model – View – ViewModel )是一种软件开发思想,类似于MVC
• Model 层,代表数据
• View 层, 代表视图模板,即元素结构
• ViewModel 层,代表业务逻辑处理代码,也可理解为控制器,用于进行View和Model两个层的功能结合。例如,ViewModel可以将Model层中的数据进行倒入并绑定给View层中的对应结构,当Model层数据改变后,ViewModel可以自动地将信息更新到View层中,实现数据绑定操作。
数据驱动视图优点
- 基于MVVM 模型实现的数据驱动视图解放了DOM操作
- View 与 Model 处理分离,降低代码耦合度
数据驱动视图缺点
- 双向数据绑定时的 Bug 调试难度增大,因为问题可能出现在数据端,也可能出现在视图端。
- 大型项目的 View 与 Model 过多,维护成本
组件化开发
组件化开发指以组件作为功能的基础进行开发的一种方式。
组件的本质是一个自定义的HTML标签,HTML中传统标签比如div、ul等都有默认的样式效果。如果我们希望通过一个标签能够表示一些不同的自定义的含义,那么可以进行组件设置,组件允许我们自定义封装结构,自定义封装样式,自定义封装JS代码,那我们就可以将网页中,如一个列表的所有结构放到组件中,将它所对应的样式放到组件中,将它实现效果的逻辑代码也放到组件中,最终给组件起一个名字,这个名字就可以作为自定义的HTML标签名进行使用。以后当这个组件出现了一些问题的时候,不再需要到整体的文件中查找,只需要找到这个组件的功能,对组件内部的结构或样式或逻辑代码进行处理就可以了,大大提高可维护性。第二点,当进行组件使用时,只需书写一个自定义的HTML标签即可,如果想复用组件,也不需要复制大段代码,而只需要再写一个标签名,多写几遍这个自定义标签名就可以快速轻松地实现功能的复用,大大提高开发效率。并且当需要进行更改时,只需对组件的功能进行修改,多个应用了组件的地方可以自动更新,开发效率和维护性都很高。
组件化开发,允许我们将网页功能封装为自定义 HTML 标签,复用时书写自定义标签名即可。
组件不仅可以封装结构,还可以封装样式与逻辑代码,大大提交 了开发效率与可维护性。
三、Vue.js 安装
三种安装方式:本地引入、cdn引入、npm安装
本地引入
Vue.js本质是js文件,可以通过下载js文件并在本地进行文件引入的方式进行使用。
开发版本:https://cn.vuejs.org/js/vue.js
生产版本:https://cn.vuejs.org/js/vue.min.js
开发版本是一个未压缩的代码,可以查看内部代码功能学习Vue的书写方式。
生产版本是一个压缩后的代码,没有可读性但体积小,通常用于上线使用。
cdn引入
cdn引入是现在非常常用的一种方式,加载速度比本地文件放到服务器中加载更快。cdn文件可以通过一个script标签在src属性中直接传入一个地址即可。
最新稳定版: https://cdn.jsdelivr.net/npm/vue
指定版本: https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js
最新稳定版是一个固定的书写方式,将连接放到script标签中后,当vue进行了更新,就可以自动应用最新稳定版本。
指定版本,链接中2.6.12是版本号,可以将版本号换成需要使用的版本信息即可。
npm安装
npm是node进行包管理的工具,也可以通过npm帮助管理vue的功能。
最新稳定版: npm install vue
指定版本 • npm install vue@2.6.12 (2.6.12为版本号,也可根据自己需求替换为其他版本信息)
四、Vue.js基础语法
Vue实例
Vue 实例是通过 Vue 函数创建的对象,是使用 Vue 功能的基础。
Vue函数是Vue.js提供的一个全局函数,可以通过创建Vue函数的实际对象方式来使用Vue.js的所有功能。所以,若想用Vue功能,必须进行Vue实例的创建,书写方式如下:
var vm = new Vue({
// 选项对象
});
选项对象中可以提供一些选项去配置当前实例以实现不同功能。
基础选项
用于对Vue实例进行基础功能配置
指令
指令是Vue中最常用的功能,用于在视图中设置操作,可以让视图与数据进行更好结合
其他选项
其它选项中学习:
过滤器,用于进行数据筛选的工具
计算属性,用于进行计算处理的工具,可以提高计算效率
侦听器功能,可以在数据发生变化时进行一些处理操作
五、el选项
el选项简介
- el选项是Vue基础选项之一。
- 用于选取一个 DOM 元素作为 Vue 实例的挂载目标。
el选项只能选取一个DOM元素。在网页中存在很多标签元素,但是并不是所有元素都设置了Vue功能,操作时需要让Vue实例挂载到某一个元素上,这个元素内部就可以使用Vue功能了。所以注意,只能设置单个DOM元素才可以。
- 只有挂载元素内部才会被 Vue 进行处理,外部为普通 HTML 元素。
当前被挂载的元素内部的所有Vue语法都可以被Vue.js处理,除了当前元素意外,外部其他元素都是普通HTML元素,是不可以使用Vue功能的。所以在制作含有Vue功能的网页时,通常设置一个容器,在容器内部书写Vue代码,其他部分都是普通的HTML标签即可。
- 代表 MVVM 中的 View 层(视图)。
el选项设置方式
书写方式有两种。
CSS选择器格式的字符串
第一种为CSS选择器格式的字符串
var vm = new Vue({
el: '#app'
});
根据id名称app去获取网页中的指定id的元素,这个元素内部就可以进行Vue的语法操作。
HTMLElement实例
第二种为采用HTMLElement实例的方式
var app = document.querySelector('#app');
var vm = new Vue({
el: app
});
通过document.querySelector的语法进行DOM元素获取,内部#app代表id为app的元素,这个位置注意,获取元素方式多种多样,DOM中提供了很多种方式都是可以获取HTMLElement实例的,例如通过document.querySelector可以,通过document.getElementById也可以,或者通过document.getElementsByTagName也可以,但是要注意,类似于document.getElementsByTagName这种可以获取多个元素的方式,需要根据索引获取内部指定的一个元素才是HTMLElement实例,而不能将整体伪数组设置给el,因为el选项只能设置单个DOM元素。
注意:可以为 CSS 选择器格式的字符串 或 HTMLElement 实例,但不能 为 html 或 body。
挂载完毕后访问
通过el选项选取了挂载元素后,如果后续操作中需要操作此el选项对应的元素,可以通过vm.$el属性进行访问。
挂载完毕后,可以通过 vm.$el 进行访问:
var vm = new Vue({
el: '#app'
});
console.log(vm.$el);
创建Vue实例后挂载
未设置 el 的 vue 实例,也可以通过 vm.$mount() 进行挂载,参 数形式与 el 规则相同,即可以为 CSS 选择器格式的字符串 或 HTMLElement 实例,但不能为html或body。
var vm = new Vue({});
vm.$mount('#app');
再次强调:
el选项设置的元素必须是单个元素,通常通过id进行操作
el选项设置的元素不能是html或body元素
六、插值表达式
创建Vue实例后,通过el选项指定的元素,称为挂载元素。在挂载元素内部,可以使用 Vue.js 的模板语法,模板语法中最基础的功能为插值表达式,用于给当前元素设置动态的内容。传统书写中可以通过DOM操作给元素设置内容,但是这个内容是固定的,每次更改需要进行更新。通过插值表达式,可以将元素内容书写为组合形式或结合以后的数据绑定操作进行动态处理,书写灵活性很高。
插值表达式书写方式
插值表达式书写方式为:{{}}
<div id="app">
<ul>
<li>计算结果为:{{1+2+3}}</li>
<li>计算结果为:{{2>1?2:1}}</li>
</ul>
</div>
<script>
new Vue({
el:'#app'
});
</script>
若后续会修改1或2或3,写成1+2+3比直接写结果6更便于修改
注意下面要创建Vue实例并进行Vue实例的挂载才能让当前元素内部的Vue代码生效。
插值表达式特点
- 插值表达式只能书写在标签内容区域,用来做动态内容设置,不可以与其它内容混合。
例如,无法通过插值表达式进行元素属性等内容的设置。插值表达式仅用于设置内容。
- 内部只能书写 JavaScript 表达式,不能书写语句。
否则会出现模板解析错误
七、data选项
data选项是Vue基础选项之一。
当通过el选项选择了挂载实例后,这个实例内部就可以进行视图操作,当视图中需要使用数据时,这个数据需要在data中进行指定,所以data的本质就是用来存储数据。
data选项书写方式
data选项用于存储 Vue 实例需要使用的数据,值为对象类。
new Vue({
el: '#app',
data: {
title: '标题内容'
}
});
data选项数据访问
data 中的数据可以通过 vm.$data.数据 或 vm.数据 访问,其中vm.数据 访问方式更为常用。
new Vue({
el: '#app',
data: {
title: '标题内容'
}
});
console.log(vm.$data.title);
console.log(vm.title);//更常用
data选项特点
- data 中的数据可以直接在视图中通过插值表达式访问
<div id="app">
<p>{{title}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
new Vue({
el: '#app',
data: {
title: '标题内容'
}
});
</script>
- data 中的数据为响应式数据,在发生改变时,视图会自动更新
<div id="app">
<p>{{title}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
title: '标题内容'
}
});
vm.title = '新的标题内容';//最终页面显示 新的标题内容
</script>
在浏览器控制台输入vm.title = '新的标题内容';也可以更改title的内容——响应式数据
data选项特殊情况---data中存在数组
data 中存在数组时,索引操作与 length 操作无法自动更新视图, 这时可以借助 Vue.set() 方法替代操作
<div id="app">
<ul>
<li>{{ arr[0] }}</li>
<li>{{ arr[1] }}</li>
<li>{{ arr[2] }}</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
arr: ['内容1','内容2','内容3']
}
});
</script>
上述代码可以以列表形式在网页中展示。
涉及索引操作无法自动更新视图,例如在控制台输入vm.arr[0]然后回车可以在控制台看到“内容1”,若输入vm.arr[0] '新的内容'回车,控制台返回“新的内容”,但是网页中依然是“内容1”,并没有发生改变 。即,通过索引修改数组元素的方式无法触发数据更新。
length 操作也无法自动更新视图。例如,在控制台输入vm.arr.length=0,这时数组被清空,但是网页中的“内容1”“内容2”“内容3”其实依然存在。即,通过length操作数组进行修改时,信息修改并不能自动更新到视图中。
其他数组方法是可以自动更新视图的。例如,在控制台输入vm.arr.pop()即删除数组最后一个元素,可以看到网页中“内容3”消失,说明自动更新视图了。
为解决索引操作与 length 操作无法自动更新视图问题,引入Vue.set()方法。
Vue.set() 方法存在三个部分的参数,参数1是操作的数组,第二个参数是属性名,在数组中代表的是索引值,参数3为想设置的新值。
Vue.set(vm.arr, 1, '新的值');
Vue.set(vm.arr, 1, '新的值');即表示在arr数组中索引为1的值修改为新的值。
八、methods选项
methods为Vue基础选项之一。
用于存储需要在 Vue实例中使用的函数。
methods选项书写方式
当时用函数的视图功能时,视图中是可以使用data设置的数据的,如果需要对数据做一些处理操作,可能会在视图结构中书写大段的处理代码,这种操作是不太合适的,因为Vue希望将视图与数据处理的逻辑代码分离,这样可以降低代码的耦合度。所以,可以将一些复杂的操作设置在methods中。methods本身是一个对象,在对象内部可以指定多个方法结构,正如下面示例中,methods设置为对象后,内部可以通过方法名小括号大括号fn(){}的方式来指定一个方法,后续可以设置任意个数的方法用于进行功能处理。示例中,对data中的value1和value2两个字符串进行处理操作,上面通过插值表达式进行函数调用并通过value1和value2传参的方式得到处理结果,最终展示给用户。
需求:将title中的a-b-c-d-e变成abcde
原始方式(直接在插值表达式中通过字符串方法操作):
split()方法,按照-进行分割,分割完毕得到数组,在通过数组.join()方法进行合并
<div id="app">
<p>{{ title.split('-').join('')}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
title: 'a-b-c-d-e'
}
});
</script>
这种方式,代码书写复杂,若以后频繁在视图中书写结构并进行相关内容处理,视图结构会变得非常复杂,且视图操作与逻辑处理是结合在一起的,不利于代码的分离。另一点,若需要在各地方使用这个操作,例如多个相似title进行相同处理,这种情况下的代码是不具备复用性的,代码必须重新书写,无法在多个地方调用。
这种情况下,应当通过methods来指定函数进行处理。
<div id="app">
<p>{{ fn(value1)}}</p>
<p>{{ fn(value2)}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
value1: 'a-b-c-d-e',
value2: 'x-y-z'
},
methods: {
fn (value) {
return value.split('-').join('');
}
}
});
</script>
两种方式结果相同,但观察视图结构,通过methods封装函数的方法进行功能设置明显比原来书写完整代码更加清晰,分离度更高。
所以,后续如果要在视图中进行数据处理,推荐将功能封装在methods中再进行使用,保持干净的视图模板结构。
methods选项访问
methos选项内部的方法,除了可以在视图模板中使用外,还可以被Vue实例使用。如果希望通过Vue实例来操作methods中的方法,可以通过Vue实例vm.方法名进行操作。如果在方法中也操作Vue实例中存储的数据等相关功能,在方法中所使用的的this都代表Vue实例。也就是说,可以利用this在methods内部的方法中直接操作data中的数据或操作methds中的其他方法进行处理。
methods 中的方法可以通过 vm.方法名 访问。
方法中的 this 为 vm 实例,可以便捷的访问 vm 数据等功能。
需求:希望每次处理的结果都加一个前缀“处理结果为:”。
可以在<p>标签内部插值表达式中fn前面拼接字符串,但是又不符合视图和逻辑分离的处理方式,所以最好的处理方式是将prefix拼接加到fn中。
<body>
<div id="app">
<p>{{ fn(value1)}}</p>
<p>{{ fn(value2)}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
prefix: '处理结果为:',
value1: 'a-b-c-d-e',
value2: 'x-y-z'
},
methods: {
fn (value) {
return this.prefix + value.split('-').join('');
}
}
});
</script>
</body>
上述为利用this在methods内部的方法中直接操作data中的数据,也可以操作methds中的其他方法。
<body>
<div id="app">
<p>{{ fn(value1)}}</p>
<p>{{ fn(value2)}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
prefix: '处理结果为:',
value1: 'a-b-c-d-e',
value2: 'x-y-z'
},
methods: {
fn (value) {
this.fn1();
this.fn2();
return this.prefix + value.split('-').join('');
},
fn1() {
console.log('执行了fn1的代码');
},
fn2() {
console.log('执行了fn2的代码');
}
}
});
</script>
</body>
所以,后续书写功能时,可以将大功能内部可以重复使用的代码再封装为小功能。