狂神说Vue笔记
想要成为真正的“互联网Java全栈工程师”还有很长的一段路要走,其中前端是绕不开的一门必修课。本阶段课程的主要目的就是带领Java后台程序员认识前端、了解前端、掌握前端,为实现成为“互联网Java全栈工程师”再向前迈进一步。
文章目录
一、前端核心分析
1.1、概述
Soc原则:关注点分离原则
Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。
HTML + CSS + JS : 视图 : 给用户看,刷新后台给的数据
网络通信 : axios
页面跳转 : vue-router
状态管理:vuex
Vue-UI : ICE , Element UI
1.2、前端三要素
- HTML(结构):超文本标记语言(Hyper Text Markup Language),决定网页的结构和内容。
- CSS(表现):层叠样式表(Cascading Style Sheets),设定网页的表现样式。
- JavaScript(行为):是一种弱类型脚本语言,其源码不需经过编译,而是由浏览器解释运行,用于控制网页的行为。
1.3、表现层(CSS)
CSS层叠样式表是一门标记语言,并不是编程语言,因此不可以自定义变量,不可以引用等,换句话说就是不具备任何语法支持,它主要缺陷如下:
- 语法不够强大,比如无法嵌套书写,导致模块化开发中需要书写很多重复的选择器;
- 没有变量和合理的样式复用机制,使得逻辑上相关的属性值必须以字面量的形式重复输出,导致难以维护;
这就导致了我们在工作中无端增加了许多工作量。为了解决这个问题,前端开发人员会使用一种称之为【CSS预处理器】的工具,提供CSS缺失的样式层复用机制、减少冗余代码,提高样式代码的可维护性。大大的提高了前端在样式上的开发效率。
什么是CSS预处理器?
CSS预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为CSS增加了一些编程的特性,将CSS作为目标生成文件,然后开发者就只需要使用这种语言进行CSS的编码工作。转化成通俗易懂的话来说就是“用一种专门的编程语言,进行Web页面样式设计,再通过编译器转化为正常的CSS文件,以供项目使用”。
常用的CSS预处理器有哪些?
- SASS:基于Ruby ,通过服务端处理,功能强大。解析效率高。需要学习Ruby语言,上手难度高于LESS。
- LESS:基于NodeJS,通过客户端处理,使用简单。功能比SASS简单,解析效率也低于SASS,但在实际开发中足够了,所以如果我们后台人员如果需要的话,建议使用LESS。
1.4、行为层(JavaScript)
JavaScript一门弱类型脚本语言,其源代码在发往客户端运行之前不需要经过编译,而是将文本格式的字符代码发送给浏览器,由浏览器解释运行。
JavaScript框架
- JQuery:大家熟知的JavaScript库,优点就是简化了DOM操作,缺点就是DOM操作太频繁,影响前端性能;在前端眼里使用它仅仅是为了兼容IE6,7,8;
- Angular:Google收购的前端框架,由一群Java程序员开发,其特点是将后台的MVC模式搬到了前端并增加了模块化开发的理念,与微软合作,采用了TypeScript语法开发;对后台程序员友好,对前端程序员不太友好;最大的缺点是版本迭代不合理(如1代–>2 代,除了名字,基本就是两个东西;截止发表博客时已推出了Angular6)
- React:Facebook 出品,一款高性能的JS前端框架;特点是提出了新概念 【虚拟DOM】用于减少真实 DOM 操作,在内存中模拟 DOM操作,有效的提升了前端渲染效率;缺点是使用复杂,因为需要额外学习一门【JSX】语言;
- Vue:一款渐进式 JavaScript 框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性。其特点是综合了 Angular(模块化)和React(虚拟 DOM) 的优点;
- Axios:前端通信框架;因为 Vue 的边界很明确,就是为了处理 DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接选择使用jQuery 提供的AJAX 通信功能;
二、第一个Vue程序
2.1、什么是MVVM
MVVM(Model-View-ViewModel)是一种软件设计模式,由微软WPF(用于替代WinForm,以前就是用这个技术开发桌面应用程序的)和Silverlight(类似于Java Applet,简单点说就是在浏览器上运行WPF)的架构师Ken Cooper和Ted Peters开发,是一种简化用户界面的事件驱动编程方式。由John Gossman(同样也是WPF和Sliverlight的架构师)与2005年在他的博客上发表。
MVVM源自于经典的MVC(Model-View-Controller)模式。MVVM的核心是ViewModel层,负责转换Model中的数据对象来让数据变得更容易管理和使用。其作用如下:
- 该层向上与视图层进行双向数据绑定
- 向下与Model层通过接口请求进行数据交互
MVVM已经相当成熟了,主要运用但不仅仅在网络应用程序开发中。当下流行的MVVM框架有Vue.js
,Anfular JS
2.2、为什么要使用MVVM
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处
- 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewMode),设计人员可以专注于页面设计。
- 可测试:界面素来是比较难以测试的,而现在测试可以针对ViewModel来写。
(1)View
View是视图层, 也就是用户界面。前端主要由HTH L和csS来构建, 为了更方便地展现vi eu to del或者Hodel层的数据, 已经产生了各种各样的前后端模板语言, 比如FreeMarker,Thyme leaf等等, 各大MV VM框架如Vue.js.Angular JS, EJS等也都有自己用来构建用户界面的内置模板语言。
(2)Model
Model是指数据模型, 泛指后端进行的各种业务逻辑处理和数据操控, 主要围绕数据库系统展开。这里的难点主要在于需要和前端约定统一的接口规则
(3)ViewModel
ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层, 前端开发者对从后端获取的Model数据进行转换处理, 做二次封装, 以生成符合View层使用预期的视图数据模型。
需要注意的是View Model所封装出来的数据模型包括视图的状态和行为两部分, 而Model层的数据模型是只包含状态的
- 比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示)
- 页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互)
视图状态和行为都封装在了View Model里。这样的封装使得View Model可以完整地去描述View层。由于实现了双向绑定, View Model的内容会实时展现在View层, 这是激动人心的, 因为前端开发者再也不必低效又麻烦地通过操纵DOM去更新视图。
MVVM框架已经把最脏最累的一块做好了, 我们开发者只需要处理和维护View Model, 更新数据视图就会自动得到相应更新,真正实现事件驱动编程。
View层展现的不是Model层的数据, 而是ViewModel的数据, 由ViewModel负责与Model层交互, 这就完全解耦了View层和Model层, 这个解耦是至关重要的, 它是前后端分离方案实施的重要一环。
2.3、Vue
Vue(读音/vju/, 类似于view) 是一套用于构建用户界面的渐进式框架, 发布于2014年2月。与其它大型框架不同的是, Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层, 不仅易于上手, 还便于与第三方库(如:vue-router,vue-resource,vue x) 或既有项目整合。
(1)MVVM模式的实现者
- Model:模型层, 在这里表示JavaScript对象
- View:视图层, 在这里表示DOM(HTML操作的元素)
- ViewModel:连接视图和数据的中间件, Vue.js就是MVVM中的View Model层的实现者
在MVVM架构中, 是不允许数据和视图直接通信的, 只能通过ViewModel来通信, 而View Model就是定义了一个Observer观察者
- ViewModel能够观察到数据的变化, 并对视图对应的内容进行更新
- ViewModel能够监听到视图的变化, 并能够通知数据发生改变
至此, 我们就明白了, Vue.js就是一个MV VM的实现者, 他的核心就是实现了DOM监听与数据绑定
(2)为什么要使用Vue.js
- 轻量级, 体积小是一个重要指标。Vue.js压缩后有只有20多kb(Angular压缩后56kb+,React压缩后44kb+)
- 移动优先。更适合移动端, 比如移动端的Touch事件
- 易上手,学习曲线平稳,文档齐全
- 吸取了Angular(模块化) 和React(虚拟DOM) 的长处, 并拥有自己独特的功能,如:计算属性
- 开源,社区活跃度高
2.4、第一个Vue程序
【说明】IDEA可以安装Vue的插件!
注意:Vue不支持IE 8及以下版本, 因为Vue使用了IE 8无法模拟的ECMAScript 5特性。但它支持所有兼容ECMAScript 5的浏览器。
(1)下载地址
- 开发版本
- 包含完整的警告和调试模式:https://yuejs.org/js/vue.js
- 删除了警告, 30.96KB min+gzip:https://vuejs.org/js/vue.min.js
- CDN
<script src=“https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js”></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
(2)代码编写
Vue.js的核心是实现了MVVM模式, 她扮演的角色就是View Model层, 那么所谓的第一个应用程序就是展示她的数据绑定功能,操作流程如下:
1、创建一个HTML文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
2、引入Vue.js
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
3、创建一个Vue实例
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message:"hello,vue!"
}
});
</script>
说明:
el: '#app'
:绑定元素的IDdata:{message:'Hello Vue!'}
:数据对象中有一个名为message的属性,并设置了初始值 Hello Vue!
4、将数据绑定到页面元素
<!--view层,模板-->
<div id="app">
{{message}}
</div>
说明:只需要在绑定的元素中使用双花括号将Vue创建的名为message属性包裹起来, 即可实现数据绑定功能, 也就实现了View Model层所需的效果, 是不是和EL表达式非常像?
(3)完整的HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
{{message}}
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
/*Model:数据*/
data:{
message:"hello,vue!"
}
});
</script>
</body>
</html>
(4)测试
为了能够更直观的体验Vue带来的数据绑定功能, 我们需要在浏览器测试一番, 操作流程如下:
1、在浏览器上运行第一个Vue应用程序,F12 进入开发者工具
2、在控制台输入vm.message=‘HelloWorld’, 然后回车, 你会发现浏览器中显示的内容会直接变成HelloWorld
此时就可以在控制台直接输入vm.message来修改值, 中间是可以省略data的, 在这个操作中, 我并没有主动操作DOM, 就让页面的内容发生了变化, 这就是借助了Vue的数据绑定功能实现的; MV VM模式中要求View Model层就是使用观察者模式来实现数据的监听与绑定, 以做到数据与视图的快速响应。
三、基础语法指令
Vue官方文档: https://cn.vuejs.org/v2/guide/.
3.1、v-bind
我们已经成功创建了第一个Vue应用!看起来这跟渲染一个字符串模板非常类似, 但是Vue在背后做了大量工作。现在数据和DOM已经被建立了关联, 所有东西都是响应式的。我们在控制台操作对象属性,界面可以实时更新!
我们还可以使用v-bind来绑定元素特性!
上代码
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
message: '页面加载于:'+new Date().toLocaleString()
}
})
</script>
</body>
</html>
你看到的v-bind等被称为指令。指令带有前缀v以表示它们是Vue提供的特殊特性。可能你已经猜到了, 它们会在渲染的DOM上应用特殊的响应式行为在这里,该指令的意思是:“将这个元素节点的title特性和Vue实例的message属性保持一致”。
如果你再次打开浏览器的JavaScript控制台, 输入app, message=‘新消息’,就会再一次看到这个绑定了title特性的HTML已经进行了更新。
3.2、v-if, v-else
什么是条件判断语句,就不需要我说明了吧,以下两个属性!
v-if
v-else
上代码
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--<h2 v-if="ok===true">Yes</h2>-->
<h2 v-if="ok">Yes</h2>
<h2 v-else>No</h2>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data:{
ok: true
}
})
</script>
</body>
</html>
测试:
1.在浏览器上运行,打开控制台!
2.在控制台输入vm.ok=false
然后回车,你会发现浏览器中显示的内容会直接变成NO
注:使用v-*
属性绑定数据是不需要双花括号包裹的
v-else-if
- v-if
- v-else-if
- v-else
注:===
三个等号在JS中表示绝对等于(就是数据与类型都要相等)
==
只要数据相等,类型可以不相等
上代码:
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2 v-if="type==='A'">A</h2>
<h2 v-else-if="type==='B'">B</h2>
<h3 v-else-if="type=='C'">C</h3>
<h2 v-else>D</h2>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data:{
type: 'A'
}
})
</script>
</body>
</html>
</html>
测试:
在控制台输入vm.type='C'
,页面显示C,证明不涉及类型是==
也能判断,但最好使用===
3.3、v-for
- v-for
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<li v-for="(item,index) in items">
{{item.message}}---{{index}}
</li>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
//数据是数组类型[],数组中有3个对象{}
items:[
{message:'张三'},
{message:'李四'},
{message:'王五'}
]
}
})
</script>
</body>
</html>
注:items
是数组,item
是数组元素迭代的别名。我们之后学习的Thymeleaf模板引擎的语法和这个十分的相似!
测试:在控制台输入vm.items.push({message:'赵四'})
,尝试追加一条数据,你会发现浏览器中显示的内容会增加一条赵四---3
3.4、v-on
v-on
监听事件
事件有Vue的事件、和前端页面本身的一些事件!我们这里的click是vue的事件, 可以绑定到Vue中的methods
中的方法事件!
上代码:
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<button v-on:click="sayHi">点我</button>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
message: '你好啊'
},
methods: {
sayHi:function(event){
alert(this.message);
}
}
})
</script>
</body>
</html>
点击测试
Vue还有一些基本的使用方式, 大家有需要的可以再跟着官方文档看看, 因为这些基本的指令几乎我们都见过了,一通百通!掌握学习的方式!
四、表单双绑、组件
4.1、什么是双向数据绑定
Vue.js是一个MV VM框架, 即数据双向绑定, 即当数据发生变化的时候, 视图也就发生变化, 当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js的精髓之处了。
值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的非UI控件不会涉及到数据双向绑定。单向数据绑定是使用状态管理工具的前提。如果我们使用vue x那么数据流也是单项的,这时就会和双向数据绑定有冲突。
(1)为什么要实现数据的双向绑定
在Vue.js
中,如果使用vuex
, 实际上数据还是单向的, 之所以说是数据双向绑定,这是用的UI控件来说, 对于我们处理表单, Vue.js
的双向数据绑定用起来就特别舒服了。即两者并不互斥,在全局性数据流使用单项,方便跟踪;局部性数据流使用双向,简单易操作。
4.2、在表单中使用双向数据绑定
你可以用v-model
指令在表单、及元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇, 但v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
注意:v-model
会忽略所有表单元素的value
、checked
、selected
特性的初始值而总是将Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data选项中声明初始值!
(1)单行文本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<span>输入的文本:</span>
<input type="text" value="hello" v-model="message"/>{{message}}
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
message: ""
}
})
</script>
</body>
</html>
(2)多行文本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<span>输入的文本:</span>
<textarea v-model="message"></textarea>{{message}}
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
message: "111"
}
})
</script>
</body>
</html>
(3)单选按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<span>输入的文本:</span>
<input type="radio" name="sex" value="男" v-model="message" checked/>男
<input type="radio" name="sex" value="女" v-model="message"/>女
<br />
<span>选中的值:{{message}}</span>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
message: "111"
}
})
</script>
</body>
</html>
(4)下拉框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<select v-model="message">
<option value="" disabled>--请选择--</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br />
<span>选中的值:{{message}}</span>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
message: ""
}
})
</script>
</body>
</html>
注意:v-model
表达式的初始值未能匹配任何选项,元系将被渲染为“未选中”状态。 在iOS中, 这会使用户无法选择第一个选项,因为这样的情况下,iOS不会触发change
事件。因此,更推荐像上面这样提供一个值为空的禁用选项。
4.3、什么是组件
组件是可复用的Vue
实例, 说白了就是一组可以重复使用的模板, 跟JSTL
的自定义标签、Thymeleal
的th:fragment
等框架有着异曲同工之妙,通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
(1)第一个Vue组件
注意:在实际开发中,我们并不会用以下方式开发组件,而是采用vue-cli创建,vue模板文件的方式开发,以下方法只是为了让大家理解什么是组件。
使用Vue.component()
方法注册组件,格式如下:
<div id="app">
<!--使用注册的组件-->
<kunagshen></kunagshen>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
//注册组件
Vue.component("kunagshen",{
template: '<li>Hello</li>'
});
var vm=new Vue({
el: "#app"
});
</script>
说明:
- Vue.component():注册组件
- kunagshen:自定义组件的名字
- template:组件的模板
(2)使用props属性传递参数
像上面那样用组件没有任何意义,所以我们是需要传递参数到组件的,此时就需要使用props
属性了!
注意:默认规则下props属性里的值不能为大写;例:CONG
,而Cong
可以
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--使用注册的组件-->
<kunagshen v-for="item in items" v-bind:cong="item"></kunagshen>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
//注册组件
Vue.component("kunagshen",{
props:['cong'],
template: '<li>{{cong}}</li>'
})
var vm=new Vue({
el: "#app",
data: {
items: ["java","Linux","前端"]
}
});
</script>
</body>
</html>
说明:
v-for="item in items"
:遍历Vue实例中定义的名为items的数组,并创建同等数量的组件
v-bind:cong="item"
:将遍历的item
项绑定到组件中定义的名为cong
属性上;= 号左边的cong
为props
定义的属性名
五、Axios异步通信
5.1、什么是Axios
Axios是一个开源的可以用在浏览器端和Node JS的异步通信框架, 她的主要作用就是实现AJAX异步通信,其功能特点如下:
- 从浏览器中创建XMLHttpRequests
- 从node.js创建http请求
- 支持Promise API[JS中链式编程]
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御XSRF(跨站请求伪造)
GitHub:https://github.com/axios/axios
中文文档:http://www.axios-js.com/
5.2、为什么要使用Axios
由于Vue.js
是一个视图层框架并且作者(尤雨溪) 严格准守SoC(关注度分离原则)所以Vue.js
并不包含AJAX的通信功能, 为了解决通信问题, 作者单独开发了一个名为vue-resource
的插件, 不过在进入2.0版本以后停止了对该插件的维护并推荐了Axios
框架。少用jQuery, 因为它操作Dom太频繁!
5.3、第一个Axios应用程序
咱们开发的接口大部分都是采用JSON格式, 可以先在项目里模拟一段JSON数据, 数据内容如下:创建一个名为data.json的文件并填入上面的内容, 放在项目的根目录下
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com",
"page": 1,
"isNonProfit": true,
"address": {
"street": "含光门",
"city": "陕西西安",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://space.bilibili.com/95256449"
},
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
测试代码
<!DOCTYPE html>
<html lang="en" xmlns:v-binf="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--v-cloak 解决闪烁问题-->
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app">
<div>名字:{{info.name}}</div>
<div>url:{{info.url}}</div>
<div>地址:{{info.address.street}}--{{info.address.city}}--{{info.address.country}}</div>
<div>links:<li v-for="(link,index) in info.links" v-if="index===2">{{link}}--{{index}}--{{link.name}}</li></div>
<div>链接:<a v-bind:href="info.url" target="_blank">{{info.url}}</a></div>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data(){
return{
info:{
name:null,
url:null,
page:0,
isNonProfit:false,
address:{
street:null,
city:null,
country:null
},
links:[
{
name:null,
url:null
},
{
name:null,
url:null
},
{
name:null,
url:null
}
]
}
}
},
//钩子函数
mounted(){
axios.get('data.json').then(response=>(this.info=response.data));
}
});
</script>
</body>
</html>
说明:
- 在这里使用了v-bind将a:href的属性值与Vue实例中的数据进行绑定
- 使用axios框架的get方法请求AJAX并自动将数据封装进了Vue实例的数据对象中
- 我们在data中的数据结构必须和Ajax响应回来的数据格式匹配!
5.4、Vue的生命周期
Vue实例有一个完整的生命周期,也就是从开始创建初始化数据、编译模板、挂载DOM、渲染一更新一渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。
在Vue的整个生命周期中,它提供了一系列的事件,可以让我们在事件触发时注册JS方法,可以让我们用自己注册的JS方法控制整个大局,在这些事件响应方法中的this直接指向的是Vue的实例。
1.初始化阶段
- 开始时创建一个Vue实例。
- 在
beforeCreated
执行时所有的data和methods中的数据都没有初始化。 - 在
created
执行时,data和methods数据已经初始化好了,所以想操作data和methods中最早只能在created中。 - 然后Vue开始编译模板(Html),把Vue中的指令(v-bind)进行执行,最后在内存中生成一个编译好的模板字符串,然后将这个模板字符串渲染为内存中DOM(虚拟DOM)。
- 在
beforeMount
时,尚未挂载到界面中,界面还是旧的(Vue的白屏)。 - 在
Mounted
阶段,组件已挂载,整个Vue实例已经初始化完毕。(created和Mounted操作数据的区别:beforeCreated阶段,界面没挂载,不能操作DOM节点,而Mounted阶段,界面已挂载,随便操作)
2.更新阶段
- 当data中的数据发生变化,就会执行
beforeUpdate
,此时data是最新的,但是界面还没有与data同步, - 之后会根据data中的最新数据,在内存中重新渲染出一份最新的DOM树,然后将其挂载到界面上,也就完成了Model层和View层的更新。
updated
执行时,界面已经是最新的,data和界面已经同步。
3.销毁阶段
- beforeDestory阶段,Vue实例从运行进入到销毁阶段,此刻data和methods还可以使用。
- destoryed阶段,组件已经销毁,data,methods,指令之类的都不能使用了。
六、计算属性、内容分发、自定义事件
6.1、什么是计算属性
计算属性的重点突出在属性
两个字上(属性是名词),首先它是个属性
其次这个属性有计算
的能力(计算是动词),这里的计算
就是个函数:简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;可以想象为缓存!
上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<p>currentTime1:{{currentTime1()}}</p>
<p>currentTime2:{{currentTime2}}</p>
</div>
<!--1.导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script>
var vm=new Vue({
el: "#app",
data: {
message: "111"
},
methods: {
currentTime1:function(){
return Date.now();//返回一个时间戳
}
},
computed:{
currentTime2:function(){
this.message;
return Date.now();
}
}
});
</script>
</body>
</html>
注意:methods和computed里的东西不能重名
说明:
-
methods:定义方法, 调用方法使用currentTime1(), 需要带括号
-
computed:定义计算属性, 调用属性使用currentTime2, 不需要带括号:this.message是为了能够让currentTime2观察到数据变化而变化
-
如何在方法中的值发生了变化,则缓存就会刷新!可以在控制台使用vm.message=”222", 改变下数据的值,再次测试vm.currentTime2发现值改变,这和mybatis缓冲很相似。
结论:
调用方法时,每次都需要讲行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销;
6.2、内容分发
在Vue.js
中我们使用<slot>
元素作为承载分发内容的出口,作者称其为插槽,可以应用在组合组件的场景中;
测试
比如准备制作一个待办事项组件(todo) , 该组件由待办标题(todo-title) 和待办内容(todo-items)组成,但这三个组件又是相互独立的,该如何操作呢?
第一步定义一个待办事项的组件
<body>
<div id="app">
<todo></todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("todo",{
template:'<div>' +
'<div>人名列表</div>' +
'<ul>' +
'<li>张三</li>' +
'<li>李四</li>' +
'<li>王五</li>' +
'</ul>' +
'</div>'
});
</script>
</body>
真实的模板里的值不会像这样是写死了,所以需要一个东西替代,使用插槽<slot>
,修改如下。
Vue.component("todo",{
template:'<div>' +
'<slot></slot>' +
'<ul>' +
'<slot></slot>' +
'</ul>' +
'</div>'
});
第二步定义一个待办标题组件和一个待办内容组件
创建一个待办标题组件和待办内容组件,使用插槽的name
绑定对应组件。
<body>
<div id="app">
<todo>
<todo-title slot="todo-title"></todo-title>
<todo-items slot="todo-items"></todo-items>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//待办事项组件
Vue.component("todo",{
template:'<div>' +
'<slot name="todo-title"></slot>' +
'<ul>' +
'<slot name="todo-items"></slot>' +
'</ul>' +
'</div>'
});
//待办标题组件
Vue.component("todo-title",{
props:['td-title'],
template:'<div>{{td-title}}</div>'
})
//待办内容组件
Vue.component("todo-items",{
props:['td-items'],
template:'<li>{{td-items}}</li>'
})
var app = new Vue({
el: '#app'
})
</script>
</body>
第三步定义一个Vue实例并传入数据,使前台界面可以显示
<body>
<div id="app">
<todo>
<todo-title slot="todo-title" :ttitle="title"></todo-title>
<todo-items slot="todo-items" v-for="item in items" :titems="item"></todo-items>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data:{
title:"待办标题",
items:['待办事项1','待办事项2','待办事项3']
}
})
</script>
</body>
总体代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<todo>
<todo-title slot="todo-title" :ttitle="title"></todo-title>
<todo-items slot="todo-items" v-for="item in items" :titems="item"></todo-items>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//待办事项组件
Vue.component("todo",{
template:'<div>' +
'<slot name="todo-title"></slot>' +
'<ul>' +
'<slot name="todo-items"></slot>' +
'</ul>' +
'</div>'
});
//待办标题组件
Vue.component("todo-title",{
props:['ttitle'],
template:'<div>{{ttitle}}</div>'
})
//待办内容组件
Vue.component("todo-items",{
props:['titems'],
template:'<li>{{titems}}</li>'
})
var app = new Vue({
el: '#app',
data:{
title:"待办标题",
items:['待办事项1','待办事项2','待办事项3']
}
})
</script>
</body>
</html>
总结:
- 1.先想好视图大概什么样,先把数据写死,然后写出对应的前端代码。
- 2.想动态改变的地方使用插槽
<slot>
代替。 - 3.有几个插槽创建几个组件,并通过
name
进行绑定。 - 4.在html中对应的组件也要通过
slot
属性绑定组件
<todo-title slot="todo-title"></todo-title>
- 5.html中从Vue实例的
data
中拿到模拟数据,然后通过绑定属性ttitle
和titems
传递,组件使用props
接收数据,并动态改变,所有数据都改变后就实现了插槽的动态拔插。
注意:
:ttitle="title"
等价于v-bind:ttitle="title"
,是v-bind的简写。
:@click="remove"
等价于v-on:click="remove"
,是v-on的简写。
组件的属性不能写成td-title
,这样会报错,写成ttitle
就没问题。
6.3、自定义事件
通以上代码不难发现,数据项在Vue的实例中, 但删除操作要在组件中完成, 那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递与事件分发了, Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题; 使用this.$emit(‘自定义事件名’, 参数)
, 操作过程如下:
1.在vue的实例中增加了methods对象并定义了一个名为removeItems的方法
<script>
var app = new Vue({
el: '#app',
data:{
title:"待办标题",
items:['待办事项1','待办事项2','待办事项3']
},
methods:{
removeItems:function(index){
console.log("删除了"+this.items[index]+"OK");
//splice(2,2) 是从第二个开始删两个
//splice(2,2,"张三",'李四') 从第二个开始删两个,然后添加"张三","李四"
this.items.splice(index,1);
}
}
})
</script>
2.修改todo-items待办内容组件的代码,增加一个删除按钮,并且绑定事件!
<script>
//待办内容组件
Vue.component("todo-items",{
props:['titems','tindex'],
template:'<li>{{tindex}}--{{titems}}<button @click="remove_method">删除</button></li>',
methods:{
remove_method:function(index){
this.$emit("remove",index);
}
}
})
</script>
3.修改todo-items待办内容组件的HTML代码,增加一个自定义事件,比如叫remove,可以和组件的方法绑定,然后绑定到vue的方法!
<todo-items slot="todo-items" v-for="(item,index) in items"
:titems="item" :tindex="index" v-on:remove="removeItems(index)" :key="index"></todo-items>
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<todo>
<todo-title slot="todo-title" :ttitle="title"></todo-title>
<todo-items slot="todo-items" v-for="(item,index) in items"
:titems="item" :tindex="index" v-on:remove="removeItems(index)" :key="index"></todo-items>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//待办事项组件
Vue.component("todo",{
template:'<div>' +
'<slot name="todo-title"></slot>' +
'<ul>' +
'<slot name="todo-items"></slot>' +
'</ul>' +
'</div>'
});
//待办标题组件
Vue.component("todo-title",{
props:['ttitle'],
template:'<div>{{ttitle}}</div>'
})
//待办内容组件
Vue.component("todo-items",{
props:['titems','tindex'],
template:'<li>{{tindex}}--{{titems}}<button @click="remove_method">删除</button></li>',
methods:{
remove_method:function(index){
this.$emit("remove",index);
}
}
})
var app = new Vue({
el: '#app',
data:{
title:"待办标题",
items:['待办事项1','待办事项2','待办事项3']
},
methods:{
removeItems:function(index){
console.log("删除了"+this.items[index]+"OK");
//splice(2,2) 是从第二个开始删两个
//splice(2,2,"张三",'李四') 从第二个开始删两个,然后添加"张三","李四"
this.items.splice(index,1);
}
}
})
</script>
</body>
</html>
逻辑理解
6.4、Vue入门小结
核心:数据驱动,组件化
优点:借鉴了AngularJS的模块化开发和React的虚拟Dom,虚拟Dom就是把Demo操作放到内存中执行;
常用的属性:
- v-if
- v-else-if
- v-else
- v-for
- v-on绑定事件,简写@
- v-model数据双向绑定
- v-bind给组件绑定参数,简写:
组件化:
- 组合组件slot插槽
- 组件内部绑定事件需要使用到this.$emit(“事件名”,参数);
- 计算属性的特色,缓存计算数据
遵循SoC关注度分离原则,Vue是纯粹的视图框架,并不包含,比如Ajax之类的通信功能,为了解决通信问题,我们需要使用Axios框架做异步通信;
说明
Vue的开发都是要基于NodeJS,实际开发采用Vue-cli脚手架开发,vue-router路由,vuex做状态管理;Vue UI,界面我们一般使用ElementUI(饿了么出品),或者ICE(阿里巴巴出品)来快速搭建前端项目~~
七、第一个vue-cli项目
7.1、什么是vue-cli
vue-cli官方提供的一个脚手架,用于快速生成一个vue的项目模板;
预先定义好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个骨架项目,这个估计项目就是脚手架,我们的开发更加的快速;
项目的功能
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
7.2、需要的环境
- Node.js:http://nodejs.cn/download/
安装就是无脑的下一步就好,安装在自己的环境目录下 - Git:https://git-scm.com/doenloads
镜像:https://npm.taobao.org/mirrors/git-for-windows/
确认nodejs安装成功:
- cmd下输入
node -v
,查看是否能够正确打印出版本号即可! - cmd下输入
npm -v
,查看是否能够正确打印出版本号即可!
那么什么是npm?
npm是JavaScript运行时环境Node.js的默认包管理器。他集成在了node.js
里,类似于maven的作用。
安装Node.js淘宝镜像加速器(cnpm)
# -g 就是全局安装(不推荐,容易卡死,非常慢)
npm install cnpm -g
# 使用淘宝镜像安装(推荐,速度很快)
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装的位置:C:\Users\administrator\AppData\Roaming\npm
安装vue-cli
cnpm install vue-cli-g
#测试是否安装成功#查看可以基于哪些模板创建vue应用程序,通常我们选择webpack
vue list
如果出现vue不是内部或外部命令,可以输入npm install vue
试一下(这里我安装的有点乱,安装vue-cli前就执行了这个命令,没啥影响,出现问题可以尝试下)
npm install vue
到此
vue-cli
安装完成。
7.3、第一个vue-cli应用程序
1.创建一个Vue项目,我们随便建立一个空的文件夹在电脑上,我这里在D盘下新建一个目录
D:\Program Files\JetBrains\workspace\vue-study
2.创建一个基于webpack模板的vue应用程序
#1、首先需要进入到对应的目录 cd D:\Project\vue-study
cd D:\Program Files\JetBrains\workspace\vue-study
#2、这里的myvue是顶日名称,可以根据自己的需求起名
vue init webpack myvue
然后会跳出一些选项让你选,一路都选择no即可。
说明:
- Project name:项目名称,默认回车即可
- Project description:项目描述,默认回车即可
- Author:项目作者,默认回车即可
- Install vue-router:是否安装vue-router,选择n不安装(后期需要再手动添加)
- Use ESLint to lint your code:是否使用ESLint做代码检查,选择n不安装(后期需要再手动添加)
- Set up unit tests:单元测试相关,选择n不安装(后期需要再手动添加)
- Setupe2etests with Nightwatch:单元测试相关,选择n不安装(后期需要再手动添加)
- Should we run npm install for you after the,project has been created:创建完成后直接初始化,选择n,我们手动执行;运行结果!
初始化并运行
# 进入myvue项目
cd myvue
# 安装项目所需要的所有依赖
npm install
# 启动项目
npm run dev
打开浏览器输入http://localhost:8080/
能显示画面即创建成功!
然后可以使用idea打开myvue项目,查看目录结构及代码。
详细目录结构可参考:: https://www.cnblogs.com/huangfeihong/p/9141273.html.
八、webpack使用
8.1、什么是Webpack
本质上, webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler) 。当webpack处理应用程序时, 它会递归地构建一个依赖关系图(dependency graph) , 其中包含应用程序需要的每个模块, 然后将所有这些模块打包成一个或多个bundle.
Webpack是当下最热门的前端资源模块化管理和打包工具, 它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过loader转换, 任何形式的资源都可以当做模块, 比如Commons JS、AMD、ES 6、CSS、JSON、Coffee Script、LESS等;
伴随着移动互联网的大潮, 当今越来越多的网站已经从网页模式进化到了WebApp模式。它们运行在现代浏览器里, 使用HTML 5、CSS 3、ES 6等新的技术来开发丰富的功能, 网页已经不仅仅是完成浏览器的基本需求; WebApp通常是一个SPA(单页面应用) , 每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的JS代码,这给前端的开发流程和资源组织带来了巨大挑战。
前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。
8.2、模块化的演进
Script标签
<script src = "module1.js"></script>
<script src = "module2.js"></script>
<script src = "module3.js"></script>
这是最原始的JavaScript文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在window对象中,不同模块的调用都是一个作用域。
这种原始的加载方式暴露了一些显而易见的弊端:
- 全局作用域下容易造成变量冲突
- 文件只能按照
<script>
的书写顺序进行加载 - 开发人员必须主观解决模块和代码库的依赖关系
- 在大型项目中各种资源难以管理,长期积累的问题导 致代码库混乱不堪
CommonsJS
服务器端的NodeJS遵循CommonsJS规范,该规范核心思想是允许模块通过require方法来同步加载所需依赖的其它模块,然后通过exports或module.exports来导出需要暴露的接口。
require("module");
require("../module.js");
export.doStuff = function(){};
module.exports = someValue;
优点:
- 服务器端模块便于重用
- NPM中已经有超过45万个可以使用的模块包
- 简单易用
缺点:
- 同步的模块加载方式不适合在浏览器环境中,同步意 味着阻塞加载,浏览器资源是异步加载的
- 不能非阻塞的并行加载多个模块
实现:
- 服务端的NodeJS
- Browserify,浏览器端的CommonsJS实现,可以使用NPM的模块,但是编译打包后的文件体积较大
- modules-webmake,类似Browserify,但不如Browserify灵活
- wreq,Browserify的前身
AMD
Asynchronous Module Definition规范其实主要一个主要接口define(id?,dependencies?,factory);它要在声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到factory中,对于依赖的模块提前执行。
define("module",["dep1","dep2"],functian(d1,d2){
return someExportedValue;
});
require(["module","../file.js"],function(module,file){});
优点
- 适合在浏览器环境中异步加载模块
- 可以并行加载多个模块
缺点
- 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不畅
- 不符合通用的模块化思维方式,是一种妥协的实现
实现
- RequireJS
- curl
CMD
Commons Module Definition规范和AMD很相似,尽保持简单,并与CommonsJS和NodeJS的Modules规范保持了很大的兼容性。
define(function(require,exports,module){
var $=require("jquery");
var Spinning = require("./spinning");
exports.doSomething = ...;
module.exports=...;
});
优点:
- 依赖就近,延迟执行
- 可以很容易在NodeJS中运行缺点
- 依赖SPM打包,模块的加载逻辑偏重
实现
- Sea.js
- coolie
ES6模块
EcmaScript 6标准增加了JavaScript语言层面的模块体系定义。ES 6模块的设计思想, 是尽量静态化, 使编译时就能确定模块的依赖关系, 以及输入和输出的变量。Commons JS和AMD模块,都只能在运行时确定这些东西。
import "jquery"
export function doStuff(){}
module "localModule"{}
优点
- 容易进行静态分析
- 面向未来的Ecma Script标准
缺点
- 原生浏览器端还没有实现该标准
- 全新的命令,新版的Node JS才支持
实现
- Babel
大家期望的模块
系统可以兼容多种模块风格, 尽量可以利用已有的代码, 不仅仅只是JavaScript模块化, 还有CSS、图片、字体等资源也需要模块化。
8.3、安装Webpack
WebPack是一款模块加载器兼打包工具, 它能把各种资源, 如JS、JSX、ES 6、SASS、LESS、图片等都作为模块来处理和使用。
安装:
npm install webpack -g
npm install webpack-cli -g
测试安装成功
webpack -v
webpack-cli -v
配置
创建 webpack.config.js
配置文件
- entry:入口文件, 指定Web Pack用哪个文件作为项目的入口
- output:输出, 指定WebPack把处理完成的文件放置到指定路径
- module:模块, 用于处理各种类型的文件
- plugins:插件, 如:热更新、代码重用等
- resolve:设置路径指向
- watch:监听, 用于设置文件改动后直接打包
module.exports = {
entry:"",
output:{
path:"",
filename:""
},
module:{
loaders:[
{test:/\.js$/,;\loade:""}
]
},
plugins:{},
resolve:{},
watch:true
}
打包
在控制台直接运行webpack
命令进行打包。
8.4、使用webpack
1.创建一个空项目
2.创建一个名为modules的目录,用于放置JS模块等资源文件
3.在modules下创建模块文件,如hello.js,用于编写JS模块相关代码
//暴露一个方法:sayHi
exports.sayHi=function (){
document.write("<div>Hello Webpack!!!</div>");
}
4.在modules下创建一个名为main.js的入口文件,用于打包时设置entry属性
//require 导入一个模块,就可以调用这个模块中的方法了
var hello = require("./hello");
hello.sayHi();
5.在项目目录下创建webpack.config.js配置文件,配置入口和输出位置
module.exports = {
entry:"./modules/main.js",
output:{
filename:"./js/bundle.js"
}
}
6.控制台输入webpack
命令进行打包。
打包完成后会在工程下生成一个/dist/js/bundle.js
文件。
测试
1.工程下创建一个index.html,导入打包后生成的js文件
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>狂神说Java</title>
</head>
<body>
<script src="dist/js/bundle.js"></script>
</body>
</html>
2.打开浏览器即可看到效果
说明
# 参数--watch 用于监听变化
webpack --watch
执行此命令,修改js内容,会自动打包。
九、vue-router路由
9.1、说明
学习的时候,尽量的打开官方的文档
Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成, 让构建单页面应用变得易如反掌。包含的功能有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于Vue js过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的CSS class的链接
- HTML5 历史模式或hash模式, 在IE 9中自动降级
- 自定义的滚动行为
9.2、安装
基于第一个vue-cli进行测试学习; 先查看node modules中是否存在vue-router
vue-router是一个插件包, 所以我们还是需要用npm/cnpm来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。
npm install vue-router --save-dev
如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
9.3、测试
1.将项目原有的组件删除
2.在components
目录下存放我们自己编写的组件Content.vue
,Main.vue
组件
Content.vue
组件
<template>
<div>
<h6>内容页------------</h6>
</div>
</template>
<script>
export default {
name: "Content"
}
</script>
Main.vue
组件
<template>
<div>
<h6>首页------------</h6>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
3.安装路由,在src目录下,新建一个router
文件夹,专门存放路由,在router文件夹下创建一个index.js
配置路由
//导入vue,类似于java的导包
import Vue from 'vue'
//导入vue-router
import Router from 'vue-router'
//导入自定义的组件
import Content from '../components/Content'
import Main from '../components/Main'
//安装路由
Vue.use(Router);
//配置路由
export default new Router({
routes:[
{
//路由路径
path: '/content',
//路由名称
name: 'Content',
//跳转到组件
component: Content
},{
//路由路径
path: '/main',
//路由名称
name: 'Main',
//跳转到组件
component: Main
}
]
})
注意:
- name要和上面import导入的一致,否则报错
- 一定是export default new Router({
routes
}),不能写错,写错不报错(巨坑)
4.在App.vue
中使用路由
<template>
<div id="app">
<h1>Vue-Router</h1>
<!--
router-link:默认会被渲染成一个<a>标签,to属性为指定链接
router-view:用于渲染路由匹配到的组件
-->
<router-link to="/main">首页</router-link>
<router-link to="/content">内容页</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
5.在main.js
中配置路由
import Vue from 'vue'
import App from './App'
//导入上面创建的路由配置目录
//自动扫描里面的路由配置
import router from './router'
//关闭生产模式下给出的提示
Vue.config.productionTip = false
new Vue({
el: '#app',
//配置路由
router,
components: { App },
template: '<App/>'
})
6.在控制台输入npm run dev
,然后打开浏览器输入http://localhost:8080测试
十、实战快速上手
10.1、创建工程
1.打开命令行,进入目录D:\Program Files\JetBrains\workspace\Vue
,然后执行vue init webpack myvue-elementui
创建一个名为myvue-elementui的工程
2.安装依赖, 我们需要安装vue-router、element-ui、sass-loader和node-sass四个插件
#进入工程目录
cd myvue-elementui
#安装vue-router
npm install vue-router --save-dev
#安装element-ui
npm i element-ui -S
#安装所有其他依赖
npm install
# 安装SASS加载器
cnpm install sass-loader node-sass --save-dev
#启功测试
npm run dev
3、Npm命令解释:
npm install moduleName
:安装模块到项目目录下npm install -g moduleName
:-g的意思是将模块安装到全局,具体安装到磁盘哪个位置要看npm config prefix的位置npm install -save moduleName
:–save的意思是将模块安装到项目目录下, 并在package文件的dependencies节点写入依赖,-S为该命令的缩写npm install -save-dev moduleName
:–save-dev的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖,-D为该命令的缩写
10.2、代码编写
删掉原有的组件并在src下创建如下结构:
- assets:用于存放资源文件
- components:用于存放Vue功能组件
- views:用于存放Vue视图组件
- router:用于存放vue-router配置
创建首页视图,在views目录下创建一个名为Main.vue的视图组件:
<template>
<div>首页</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
*创建登录页视图在views目录下创建名为Login.vue的视图组件,其中el-的元素为ElementUI组件;
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog title="温馨提示" :visible.sync="dialogVisiable" width="30%" :before-close="handleClose">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return{
form:{
username:'',
password:''
},
//表单验证,需要在 el-form-item 元素中增加prop属性
rules:{
username:[
{required:true,message:"账号不可为空",trigger:"blur"}
],
password:[
{required:true,message:"密码不可为空",tigger:"blur"}
]
},
//对话框显示和隐藏
dialogVisible:false
}
},
methods:{
onSubmit(formName){
//为表单绑定验证功能
this.$refs[formName].validate((valid)=>{
if(valid){
//使用vue-router路由到指定界面,该方式称为编程式导航
this.$router.push('/main');
}else{
this.dialogVisible=true;
return false;
}
});
}
}
}
</script>
<style lang="scss" scoped>
.login-box{
border:1px solid #DCDFE6;
width: 350px;
margin:180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title{
text-align:center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>
创建路由,在router目录下创建一个名为index.js的vue-router路由配置文件
//导入vue
import Vue from 'vue';
import router from 'vue-router';
//导入组件
import Main from '../views/Main';
import Login from '../views/Login';
//使用路由
Vue.use(router);
//导出
export default new router({
routes:[
{
//首页
path: '/main',
name: 'Main',
component: Main
},
{
//登录页
path: '/login',
name: 'Login',
component: Login
}
]
})
修改APP.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
修改main.js
import Vue from 'vue'
import App from './App'
//配置路由
import router from "./router"
//配置ElementUI
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(router)
Vue.use(ElementUI)
new Vue({
el: '#app',
router,
render:h=>h(App)//ElementUI
})
然后测试,如出现错误可能是因为sass-loader的版本过高导致的编译错误,修改package.json内的版本
"sass-loader": "^7.3.1",
"node-sass": "^4.0.0",
修改后,执行npm install
重新安装依赖,再执行npm run dev
,没有错误,打开浏览器测试。
10.3、路由嵌套
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。
1、 创建用户信息组件,在 views/main目录下创建一个名为 Profile.vue 的视图组件;
Profile.vue
<template>
<h1>个人信息</h1>
</template>
<script>
export default {
name: "UserProfile"
}
</script>
2、在用户列表组件在 views/main 目录下创建一个名为 List.vue 的视图组件;
List.vue
<template>
<h1>用戶列表</h1>
</template>
<script>
export default {
name: "UserList"
}
</script>
3、 修改首页视图,我们修改 Main.vue 视图组件,此处使用了 ElementUI 布局容器组件,代码如下:
Main.vue
<template>
<div>
<el-container>
<el-aside width="200px">
<el-menu :default-openeds="['1']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
<el-menu-item-group>
<el-menu-item index="1-1">
<!--插入的地方-->
<router-link to="/main/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/main/list">用户列表</router-link>
</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
<el-menu-item-group>
<el-menu-item index="2-1">分类管理</el-menu-item>
<el-menu-item index="2-2">内容列表</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="3">
<template slot="title"><i class="el-icon-caret-right"></i>系统管理</template>
<el-menu-item-group>
<el-menu-item index="3-1">分类管理</el-menu-item>
<el-menu-item index="3-2">内容列表</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<!--在这里展示视图-->
<router-view />
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped lang="scss">
.el-header {
background-color: #B3C0D1;
color: #333;
line-height: 60px;
}
.el-aside {
color: #333;
}
</style>
4、 配置嵌套路由修改 router 目录下的 index.js 路由配置文件,使用children放入main中写入子模块,代码如下
index.js
//导入vue
import Vue from 'vue';
import router from 'vue-router';
//导入组件
import Main from '../views/Main';
import Login from '../views/Login';
//导入子模块
import UserProfile from '../views/main/Profile';
import UserList from '../views/main/List';
//使用路由
Vue.use(router);
//导出
export default new router({
routes:[
{
//首页
path: '/main',
name: 'Main',
component: Main,
children:[
{
path: 'profile',// path: '/main/profile'
component: UserProfile
},
{
path: 'list',// path: '/main/list'
component: UserList
}
]
},
{
//登录页
path: '/login',
name: 'Login',
component: Login
}
]
})
5.运行测试
10.4、参数传递
用的还是上述例子的代码 修改一些代码 这里不放重复的代码了
第一种传递方式
1、 修改路由配置, 主要是router下的index.js中的 path 属性中增加了 :id 这样的占位符
{
path: 'profile/:id',// path: '/main/profile'
name: 'UserProfile',
component: UserProfile,
}
2、传递参数
此时我们在Main.vue中的route-link位置处 to 改为了 :to,是为了将这一属性当成对象使用,注意 router-link 中的 name 属性名称 一定要和 路由中的 name 属性名称 匹配,因为这样 Vue 才能找到对应的路由路径;
<router-link :to="{name:'UserProfile',params:{id: 1}}">个人信息</router-link>
3、在要展示的组件Profile.vue中接收参数 使用 {{$route.params.id}}来接收
Profile.vue 部分代码
<template>
<!-- 所有的元素必须在根节点下-->
<div>
<h1>个人信息</h1>
{{$route.params.id}}
</div>
</template>
第二种传递方式
使用props 减少耦合
1、修改路由配置 , 主要在router下的index.js中的路由属性中增加了 props: true 属性
{
path: 'profile/:id',// path: '/main/profile'
name: 'UserProfile',
component: UserProfile,
props: true
}
2、传递参数和之前一样 在Main.vue中修改route-link地址
<router-link :to="{name:'UserProfile',params:{id: 1}}">个人信息</router-link>
3、在Profile.vue接收参数为目标组件增加 props 属性
Profile.vue
<template>
<div>
<h1>个人信息</h1>
{{id}}
</div>
</template>
<script>
export default {
props:['id'],
name: "UserProfile"
}
</script>
10.5、组件重定向
重定向的意思大家都明白,但 Vue 中的重定向是作用在路径不同但组件相同的情况下,比如:
在router下面index.js的配置
{
//回到首页
path: '/goHome',
redirect: '/main'
}
说明:这里定义了两个路径,一个是 /main ,一个是 /goHome,其中 /goHome 重定向到了 /main 路径,由此可以看出重定向不需要定义组件;
使用的话,只需要在Main.vue设置对应路径即可;
<el-submenu index="3">
<template slot="title"><i class="el-icon-caret-right"></i>系统管理</template>
<el-menu-item-group>
<el-menu-item index="3-1"><router-link to="/goHome">回到首页</router-link></el-menu-item>
</el-menu-item-group>
</el-submenu>
测试:先点击个人信息,再点击回到首页,观察地址栏,会重定向到/main
12.6、路由模式与 404
路由模式有两种
- hash:路径带 # 符号,如 http://localhost/#/login
- history:路径不带 # 符号,如 http://localhost/login
export default new Router({
mode: 'history',
routes: [
]
});
404 demo
1.创建一个NotFound.vue视图组件
NotFound.vue
<template>
<div>404 你的页面走丢了</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
2.修改路由配置index.js
//导入vue
import Vue from 'vue';
import router from 'vue-router';
//导入组件
import Main from '../views/Main';
import Login from '../views/Login';
import NotFound from '../views/NotFound';
//导入子模块
import UserProfile from '../views/main/Profile';
import UserList from '../views/main/List';
//使用路由
Vue.use(router);
//导出
export default new router({
mode: 'history',
routes:[
{
//首页
path: '/main',
name: 'Main',
component: Main,
children:[
{
path: 'profile/:id',// path: '/main/profile'
name: 'UserProfile',
component: UserProfile,
props: true
},
{
path: 'list',// path: '/main/list'
name: 'UserList',
component: UserList
}
]
},
{
//登录页
path: '/login',
name: 'Login',
component: Login
},
{
//回到首页
path: '/goHome',
redirect: '/main'
},
{
//404
path: '*',
component: NotFound
}
]
})
测试:地址栏输入不存在的路径,就跳到NotFound
组件
路由钩子与异步请求
beforeRouteEnter:在进入路由前执行
beforeRouteLeave:在离开路由前执行
在Profile.vue中写
export default {
name: "UserProfile",
beforeRouteEnter: (to, from, next) => {
console.log("准备进入个人信息页");
next();
},
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个人信息页");
next();
}
}
参数说明:
to:路由将要跳转的路径信息
from:路径跳转前的路径信息
next:路由的控制参数
next() 跳入下一个页面
next(’/path’) 改变路由的跳转方向,使其跳到另一个路由
next(false) 返回原来的页面
next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
在钩子函数中使用异步请求
1、安装 Axios
cnpm install --save vue-axios
2、main.js引用 Axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
3、准备数据 : 只有我们的 static 目录下的文件是可以被访问到的,所以我们就把静态文件放入该目录下。
数据和之前用的json数据一样 需要的去上述axios例子里
// 静态数据存放的位置
static/mock/data.json
4.在 beforeRouteEnter 中进行异步请求
Profile.vue
export default {
//第二种取值方式
// props:['id'],
name: "UserProfile",
//钩子函数 过滤器
beforeRouteEnter: (to, from, next) => {
//加载数据
console.log("进入路由之前")
next(vm => {
//进入路由之前执行getData方法
vm.getData()
});
},
beforeRouteLeave: (to, from, next) => {
console.log("离开路由之前")
next();
},
//axios
methods: {
getData: function () {
this.axios({
method: 'get',
url: 'http://localhost:8080/static/mock/data.json'
}).then(function (response) {
console.log(response)
})
}
}
}
5.路由钩子和axios结合图