::: tip
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式的js框架,发布于 2014 年 2 月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router,vue-resource,vuex)或既有项目整合。
:::
- Model:模型层,在这里表示 JavaScript 对象
- View:视图层,在这里表示 DOM(HTML 操作的元素)
- ViewModel:连接视图和数据的中间件,Vue.js 就是 MVVM 中的 ViewModel 层的实现者
在 MVVM 架构中,是不允许 数据 和 视图 直接通信的,只能通过 ViewModel 来通信,而 ViewModel 就是定义了一个Observer观察者
- ViewModel 能够观察到数据的变化,并对视图下对应的内容进行更新
- ViewModel 能够监听到视图的变化,并能够通知数据发生改变
至此,我们就明白了,Vue.js 就是一个 MVVM 的实现者,他的核心就是实现了 DOM 监听 与 数据绑定
AngularJS
简单介绍一下,AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购。是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVVM、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。
ReactJS
React引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。
微信小程序
微信小程序的视图层和数据层就是通过MVVM进行绑定的。
- 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 20多kb (Angular 压缩后 56kb+,React 压缩后 44kb+)
- 移动优先。更适合移动端,比如移动端的 Touch 事件
- 易上手,学习曲线平稳,文档齐全
- 吸取了 Angular(模块化)和 React(虚拟 DOM)的长处,并拥有自己独特的功能,如:计算属性
- 开源,社区活跃度高
当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。
每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
- 页面上每个独立的可交互的区域视为一个组件
- 每个组件对应一个工程目录,组件所需的各种资源在这个目录下就近维护
- 页面不过是组件的容器,组件可以嵌套自由组合(复用)形成完整的页面
注意:cdn是一种加速策略,能够快速的提供js文件
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js">script>
创建一个div,id是app
<div id="app">div>
其中该vue对象,绑定了页面中id是app的那个div
<script>
new Vue({
el: "#app",
data: {
title: "hello vue!",
age: 18,
flag: true
}
});
script>
# el: element的简称,也就是Vue实例挂载的元素节点,值可以是 CSS 选择符,或实际 HTML元素,或返回 HTML 元素的函数。
# data: 用于提供数据的对象,里面存放键值对数据。
<div id="app">
{{title}}
<hr />
{{age}}
<hr />
{{flag}}
div>
插值表达式的作用是在View中获得Model中的内容
Model中的内容如下:
new Vue({
el:"#app",
data:{
title:"hello world!"
},
methods:{
sayHello:function(){
return "hello vue";
}
}
});
<div id="app">
{{title}}
div>
此时,页面上将会显示"Hello world!"
<div id="app">
{{[1,2,3,4][2]}}
div>
此时,页面上会显示“3”,也就是数组中的第三个元素被获取。
<div id="app">
{{ {"name":"xiaoyu","age":20}.age }}
div>
此时,页面上会显示“20”,也就是对象中age属性的值。
注意:中间一定要有空格
<div id="app">
{{ sayHello()}}
div>
此时,页面上会显示“hello vue”,也就是调用了vue对象中的sayHello方法,并展示了方法的返回值。
Vue.js通过加载js,实现对页面的快速渲染。vue封装的js该如何使用? 就必须了解MVVM双向数据绑定模式。Vue将视图层和数据层分离,通过MVVM建立视图层和数据层的连接。其中,插值表达式是一种连接方式,可以通过插值表达式以多种方式,快速的从数据层获取数据并展示在视图层上。数据层Vue对象,也是由很多部分组成,比如之前介绍的el,data,methods等,以及之后要介绍的mount,computed等。
Vue中的分支语句v-if非常好理解,逻辑跟Java中的if-else相同。v-if语句块包含以下内容:
- v-if
- v-else-if
- v-else
接下来以一个简单例子即可理解:
<html>
<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>Documenttitle>
<script src="/js/vue.js">script>
head>
<body>
<div id="app">
{{title}}
<p v-if="flag">
今天天气很好呀
p>
<p v-else="flag">
刮大风,不好
p>
div>
<script>
new Vue({
el: "#app",
data: {
title: "hello vue!",
age: 18,
flag: true
}
});
script>
body>
html>
从这个例子可以看出,vue对象中的data提供了分支的条件。根据条件,如果是true,则v-if的内容就会显示,反之不显示。
v-if和v-show之间有着看似相同的效果,但优化上却有区别。先看下面这个例子:
<div id="app">
<p v-if="rich">
有钱!
p>
<p v-show="rich">
有钱!
p>
<button @click="rich=!rich">今天彩票开奖button>
div>
<script>
new Vue({
el: "#app",
data: {
rich: true
}
});
script>
通过点击“今晚彩票开奖”按钮,能切换rich的值,此时发现,v-if和v-show的显示状态都会来回切换。看起来是一样的,但通过查看控制台代码发现,v-show实际会将p标签的css样式的display属性设为none来达到隐藏的效果。
而v-if是直接在页面上添加和删除p标签来达到效果,因此v-show在反复切换的应用场景下,效率比v-if更高。
Vue中的循环关键字并没有Java的那么多,只有v-for,但用法上有多种。接下来我们来逐一介绍。
数组 对象
我们需要定义数据源,然后通过v-for来遍历数据源,再使用差值表达式输出数据。
<body>
<div id="app">
<ul>
<li v-for="a in args">{{a}}li>
ul>
div>
<script>
new Vue({
el: "#app",
data: {
args:[1,2,3,4,5]
}
});
script>
body>
在这个例子中,数据源提供了一个数组。视图层通过v-for来循环输出多个li标签,非常简单。
<body>
<div id="app">
<ul>
<li v-for="(a,i) in args">{{i}},{{a}}li>
ul>
div>
<script>
new Vue({
el: "#app",
data: {
args:[1,2,3,4,5]
}
});
script>
body>
此时的i就是每次循环的循环变量 ,从0开始一直到元素个数-1
<body>
<div id="app">
<ul>
<li v-for="(v,k,i) in student">{{i+1}}-{{v}}-{{k}}li>
ul>
div>
<script>
new Vue({
el: "#app",
data: {
student: {
name: 'xike',
age: 20,
girl: 'yige'
}
}
});
script>
body>
v、k、i 这几个字符可以自己定义,分别表示每次循环内容的值、键、序号。
- v: 循环中每条数据的值 小鱼、20、如花
- k: 循环中每天数据的键 username、age、girl
- i: 循环的序号,从0开始
<body>
<div id="app">
<ul>
<div v-for="stu in students">
<li v-for="(v,k,i) in stu">{{i+1}}--{{k}}--{{v}} li><br/>
div>
ul>
div>
<script>
new Vue({
el: "#app",
data: {
students:[
{
username:'小鱼',
age:20,
girl:'如花'
},
{
username:'小飞',
age:21,
girl:'翠花'
},
{
username:'小鹏',
age:22,
girl:'志玲'
}
]
}
});
script>
body>
可以清楚的看到,此时数据源是一个student数组,通过两层v-for循环,外层遍历数组中的每个student对象,内层v-for遍历每个对象的v、k、i。
页面效果如下:
Vue提供了多个关键字,能快速的将数据对象中的值绑定在视图层中。
通过v-model将标签的value值与vue对象中的data属性值进行绑定。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-modeltitle>
<script src="/js/vue.js">script>
head>
<body>
<div id="app">
<p>v-model绑定value属性的值和model的数据p>
<input type="text" v-model="msg"> `{{msg}}`
div>
<script>
new Vue({
el: "#app",
data: {
msg:"Hello Vue"
}
});
script>
body>
html>
此时input标签中加入了“v-model='msg'”,表示input的value值与vue对象中的msg属性绑定,当在input输入框中输入内容会实时修改msg的值。于是`{{msg}}`插值表达式能实时输出input输入框内的值。
页面效果如下: |
---|
我们知道插值表达式是不能写在html的标签的属性内的,那如果一定要用vue中的属性作为html标签的属性的内容,就可以通过v-bind进行属性绑定。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-bind绑定其他属性的title>
<script src="/js/vue.js">script>
head>
<body>
<div id="app">
<a href="https://www.baidu.com">百度一下a> <hr />
<a v-bind:href="link">百度一下a>
<a :href="link">百度一下a>
div>
<script>
new Vue({
el: "#app",
data: {
link: "https://www.baidu.com"
}
});
script>
body>
html>
这样,a标签内的href属性就可以使用vue对象中的属性值。
注意: v-bind也可以简写,使用冒号“:”来代替。
<a v-bind:href='link'>a>
<a :href='link'>
关于事件,要把握好三个步骤:设参、传参和接参。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件绑定title>
<script src="/js/vue.js">script>
head>
<body>
<div id="app">
<p>@事件名称 帮绑定相应的事件 v-on:可以简写为@p>
{{sum}}
<p>{{sum > 10 ? "大于10" : "小于10"}}p>
<input type="button" value="增加" @click="increment(1)">
<input type="button" value="减少" v-on:click="decrease(1)">
div>
<script>
new Vue({
el: "#app",
data: {
sum: 0
},
methods: {
increment: function (num) {
this.sum += num;
},
decrease: function (num) {
this.sum -= num;
}
}
});
script>
body>
html>
从这里例子中:
<button type="button" @click="increase(2)">增加button>
increase:function(s);
this.sum+=s
::: tip
接下来我们来看一下VUE中如何进行事件绑定。
:::
通过配合具体的事件名,来绑定vue中定义的函数
<body>
<div id="app">
<input type="button" value="按钮" v-on:click="myAlert" />
div>
<script>
new Vue({
el: "#app",
data: {
link:'http://www.baidu.com'
},
methods:{
myAlert:function(){
alert("AAAAAAA");
}
}
});
script>
body>
此时,该按钮,在点击时将会调用Vue对象中定义的myAlert方法。
注意: v-on也可以简写,使用"@"替代。
<input type="text" @click="myAlert" />
可以使用Vue中定义好的事件修饰符,快速达到效果。查看以下例子:
<body>
<div id="app">
<p @mousemove="mm">
x:{{x}}
y:{{y}}
<span @mousemove.stop>鼠标移动到此即停止span>
p>
div>
<script>
new Vue({
el:'#app',
data:{
x:0,
y:0
},
methods:{
mm:function(event){
this.x = event.clientX;
this.y = event.clientY;
}
}
});
script>
body>
当鼠标经过P标签区域内时,区域内就会显示X和Y轴的坐标,如果经过P标签内的Span标签内时,此时会调用事件属性mousemove.stop预定的效果,鼠标移动的效果将会被取消,X和Y不再显示信息。
计算:这个属性的值可以计算出来后缓存起来
属性: 本质上是一个属性,不是一个方法
计算属性的重点突出在 属性 两个字上(属性是名词),首先它是个 属性 其次这个属性有 计算 的能力(计算是动词),这里的 计算 就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;
<body>
<div id="app">
{{message}}
<input type="text" v-model="message">
<p>调用当前时间的方法:{{currentTime1()}}p>
<p>当前时间的计算属性:{{currentTime2}}p>
div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
},
methods: {
currentTime1: function () {
return Date.now();
}
},
computed: {
currentTime2: function () {
this.message;
return Date.now();
}
}
});
script>
body>
说明
- methods:定义方法,调用方法使用 currentTime1(),需要带括号
- computed:定义计算属性,调用属性使用 currentTime2,不需要带括号;
- this.message 是为了能够让 currentTime2 观察到这个数据变化而变化
注意:methods 和 computed 里不能重名
仔细看图中说明,观察其中的差异
调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点;计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,
所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。
不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VUE演示title>
<script src="/js/vue.js">script>
head>
<body>
<div id="app">
<h2>绑定样式 v-bind:classh2>
<div :class="[activeClass]">AAAdiv>
<div :class="{active: isActive}">AAAdiv>
<h2>绑定内联样式 v-bind:styleh2>
<div :style="bgColor">AAAdiv>
div>
<script>
new Vue({
el: "#app",
data: {
activeClass: 'active',
isActive: true,
bgColor: {
color: "blue"
}
}
});
script>
<style>
.active {
width: 100px;
height: 100px;
border: red 1px dashed;
}
style>
body>
html>
Vue的组件化设计思想借鉴了Java的面向对象思想。
Java认为万物皆对象,在Vue中,万物皆组件。
也就是说,在实际的vue项目中,以及使用了Vue框架的项目中,Vue的对象都会以组件的形式出现,能被反复使用。
要想实现组件化,需要在页面中注册组件:关于注册的方式有两种,分别是全局注册和本地注册。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue组件的全局注册title>
head>
<body>
<div id="app">
<model1>model1>
<model1>model1>
<model1>model1>
div>
<hr/>
<div id="app1">
<model1>model1>
<model1>model1>
<model1>model1>
div>
body>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js">script>
<script>
//通过Vue.component实现组件的全局注册,全局注册后的组件可以被重复使用。
Vue.component("model1",{
template:"{{title}}
",
data:function(){
return {
title:"hello vue"
}
},
methods:{
btnfn:function(){
alert("hello !!!");
}
}
});
new Vue({
el:'#app'
})
new Vue({
el:'#app1'
})
script>
html>
细节:Vue.component(“model1”, {}); 可以多次使用,如果模块名称一样的话会覆盖之前注册的。
vue的全局注册,也就意味着在页面的任意一个被vue绑定过的div中,都可以使用全局注册了的vue组件。
但是,如果是对vue组件进行本地注册,那么在其他被vue绑定的div中,不能使用该组件。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件title>
<script src="/js/vue.js">script>
head>
<body>
<div id="app">
<model1>model1>
<model2>model2>
<model3>model3>
div>
<script>
Vue.component('model1', {
template: '{{title}}
',
data: function () {
return {
title: 'Hello model1'
}
},
methods: {
btnfn: ()=>alert("model1")
}
})
let model2 = {
template: '{{title}}
',
data: function () {
return {
title: "Hello model2......."
}
},
methods: {
innerBtn:()=>alert("model2")
}
};
let model3 = {
template: '{{title}}
',
data: function () {
return {
title: "Hello model3......."
}
},
methods: {
innerBtn:()=>alert("model3")
}
};
new Vue({
el: "#app",
components: {
model2,
model3
}
});
script>
body>
html>
这是一个完整的Vue组件。该组件包含了三个部分:template(html视图层内容)、script(Model层)、style(CSS样式)。这样封装好的组件可以被复用,也可以作为其他组件的组成部分而被封装——Java的面向对象再次体现。
特点1: template标签内,必须有且只能有一个根标签。
特点2: componet中注册的组件中的data,必须是已函数的形式。
如下:
data:function(){
return {
title:"hello vue"
}
}
Vue中的组件也是有生命周期的。一个Vue组件会经历:初始化、创建、绑定、更新、销毁等阶段,不同的阶段,都会有相应的生命周期钩子函数被调用。
钩子函数是跟特定的生命周期函数绑定的,执行到特定的生命周期时,如果写的有对应的生命周期函数就会自动被调用。
<html>
<head>
<meta charset="UTF-8">
<title>生命周期title>
head>
<body>
<div id="app1">
{{title}}
<button type="button" @click="changeTitle">change titlebutton>
<button type="button" @click="destroy">destroybutton>
div>
body>
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js">script>
<script>
new Vue({
el:"#app1",
data:{
title:"this is title"
},
methods:{
changeTitle:function(){
this.title= "new title";
},
destroy:function(){
this.$destroy();
}
},
beforeCreate(){
console.log("beforeCreate")
},
created(){
console.log("created")
},
beforeMount(){
console.log("beforeMount")
},
mounted(){
console.log("mounted")
},
beforeUpdate(){
console.log("beforeUpdate")
},
updated(){
console.log("updated")
},
beforeDestroy(){
console.log("beforeDestory")
},
destroyed(){
console.log("destory")
}
})
script>
html>
组件的生命周期钩子 |
---|
验证销毁:当调用了vm.$destroy 则触发销毁流程
效果:
发现点击了vm销毁,但是页面上仍不变
因为虽然vm没了但是vm的工作成果还是在的
并不是vm销毁后,它的生成的dom也销毁掉,只不过是没有人帮你去管理了
cli: Command Line 命令行工具,vue-cli就是vue的命令行工具,也称之为脚手架,使用vue-cli提供的各种命令可以拉取、创建、运行我们需要使用到的框架,比如webpack、Element UI、Element Admin等等。那么要想使用vue-cli命令,需要先安装node.js。
node.js提供了前端程序的运行环境,可以把node.js理解成是运行前端程序的服务器。
从官网下载安装即可:
http://nodejs.cn/download/
测试node.js是否安装成功: 在DOS窗口中输入“node -v” 查看版本,如果看到版本,就表示安装成功。 |
---|
使用如下命令安装vue-cli
node自带了一个包管理工具npm => node package manager
类型于 maven
npm install vue-cli -g
- npm/cnpm: 使用node.js的命令
- install: 安装
- vue-cli: 要安装的vue-cli
- -g: 全局安装
当出现以下界面,表示正在安装: |
---|
如果使用npm官方镜像速度比较慢,可以使用淘宝镜像来安装:
npm install -g cnpm --registry=https://registry.npm.taobao.org
之后使用npm命令时就可以替换成cnpm
cnpm install vue-cli -g
## 之后使用下面的两个命令
npm install
npm run dev
就像maven一样,vue为我们提供了一些官方项目骨架。使用vue list命令可以列出当前官方提供的骨架,可以使用这些骨架来快速搭建出项目。
vue list
mkdir e:/my-vue-project
cd e:/my-vue-project
在my-vue-project目录中使用以下命令下载项目骨架
vue init webpack demo1
- webpack: 骨架名称
- demo1: 项目名称
过程中会出现如下界面,需要手动操作。 |
---|
出现如下界面,表示安装成功。 |
---|
进入到demo1文件夹内后,使用以下命令来运行项目。
npm run dev
访问http://localhost:8080,页面效果如下: |
---|
此时,项目目录及各目录介绍如下: |
---|
npm install
在运行和调试项目前,一般都需要先执行该命令,目的是安装项目运行所需要的环境。
npm run dev
以调试的方式运行项目
npm run build
生成用于项目部署所需的最小资源,生成的内容存放在build文件夹内。
在idea中下载vue插件
创建子组件Header组件
<template>
<div>
<h1>Header {{MyTitle}}h1>
<button @click="MyFun('哈哈哈')">点我button>
div>
template>
<script>
export default {
name: "Header",
props:["MyTitle","MyFun"]
}
script>
<style scoped>
style>
<template>
<div id="app">
<img src="./assets/logo.png">
<Header :my-title="msg" :my-fun="fun1"/>
div>
template>
<script>
import Header from "./components/Header";
export default {
name: 'App',
components: {
Header
},
data() {
return {
msg: "父元素传递的数据"
}
},
methods: {
fun1: function (mm) {
this.msg = mm;
}
}
}
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>
::: tip
通过子组件的props部分,来指明可以接收的参数,父组件通过在标签中写明参数的键值对来传递参数。
props是表示一个组件的参数部分,那么props的写法有两种:
1)props:[参数列表]
比如: props:['MyProp1','MyProp2',...]
2)props:{参数名1:{type:String,required:true,default:‘XX’},参数名2:{…}}
:::
子传父的过程十分复杂,下列图解中已经配上了详细的步骤。
npm install vue-router -s
也可以在vue init 初始化项目时指定使用路由模块
::: tip
在main.js中引入路由模块并使用
:::
import Vue from 'vue'
import App from './App'
import router from './router' //引入路由模块
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router, //使用路由模块
components: { App },
template: ' '
})
<template>
<div id="app">
<ul class="nav nav-tabs">
<li role="presentation" class="active"><router-link to="/Home">首页router-link>li>
<li role="presentation"><router-link to="/Product">商品列表router-link>li>
ul>
<router-view/>
div>
template>
<script>
export default {
name: 'App'
}
script>
改变url,发现中的内容发生改变
- http://localhost:8080/#/ 显示home
- http://localhost:8080/#/user 显示user
向router实例中添加mode属性: 路由的两种模式 了解即可
- 值"hash": url带# 适用于调试模式
- 值"history" url不带#
::: tip
:::
::: tip
:::
修改路由表src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Home from "../components/Home";
import Product from "../components/Product";
Vue.use(Router)
export default new Router({
routes: [
{
path:'/Home',
component: Home
},
{
path:'/Product/:id', //设参 来接受 route-link 中传过来的参数
component:Product
}
],
mode:"hostory" //可选参数
})
在路由表中设参
export default new Router({
routes: [
...
{
path:'/Product/:id', //设参
component:Product
}
]
})
在App.vue中传参
<template>
<div id="app">
<ul class="nav nav-tabs">
...
<li role="presentation"><router-link to="/Product/1">商品列表router-link>li>
...
ul>
<router-view/>
div>
template>
在Product.vue中接参
商品列表 商品的id:{{id}}
使用
进行路由跳转是有局限性的,可以通过this.$router.push(‘/Product/1’)的js方式实现路由跳转,更加灵活。
<template>
<div id="app">
<ul class="nav nav-tabs">
<li role="presentation" class="active"><router-link to="/Home">首页router-link>li>
<li role="presentation"><router-link to="/Product">商品列表router-link>li>
<button type="button" @click="btnfn">点我button>
ul>
<router-view/>
div>
template>
<script>
export default {
name: 'App',
methods:{
btnfn(){
//代替router-link实现路由跳转
this.$router.push("/Product/1");
}
}
}
script>
Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,她的主要作用就是实现 AJAX 异步通信,其功能特点如下:
GitHub:https://github.com/axios/axios
由于 Vue.js 是一个 视图层框架 并且作者(尤雨溪)严格准守 SoC (关注度分离原则),所以 Vue.js 并不包含 AJAX 的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource 的插件,不过在进入 2.0 版本以后停止了对该插件的维护并推荐了 Axios 框架
npm install --save axios vue-axios
在项目中使用axios模块
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
Regist
Email
{{mail}}
Password
{{password}}
Gender
男
女
Hobby
音乐
电影
运动
<mvc:cors>
<mvc:mapping path="/"
allowed-origins="*"
allowed-methods="POST, GET, OPTIONS, DELETE, PUT,PATCH"
allowed-headers="Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
allow-credentials="true" />
mvc:cors>
在spring-mvc.xml中加入上述这一段。其中,allowed-origins指的是允许的访问源的域名,"*"表示任何人都可以访问,也可以指明具体的域名
原因:默认情况下发送axios时请求头中的内容类型为: (后端没有使用@RequestBody)
Content-Type:application/json;charset=UTF-8
而实际服务端需要的是:
Content-Type:application/x-www-form-urlencoded
因此,使用axios的qs内置库中的方法进行内容类型的转换。
import Qs from 'qs'
this.axios({
method:'post',
url:'http://localhost:8081/regist',
transformRequest: [function (data) {
return Qs.stringify(data)
}],
data:{
email:this.email
}
})
.then(function (response) {
alert(response.data.message)
});
对项目进行中的内容进行调整,结构如下: |
---|
各部分内容:
import Vue from 'vue'
import Router from 'vue-router'
import Login from '../views/Login'
import Home from '../views/Home'
// 安装路由
Vue.use(Router);
// 配置路由
export default new Router({
routes: [
{
// 路由路径
path: '/Login',
// 路由名称
name: 'Login',
// 跳转到组件
component: Login
},
{
path:'/Home',
name:'Home',
component:Home
}
]
});
在项目文件夹内使用命令来安装element-ui模块
npm i element-ui -S
在vue项目中引入element-ui并使用。在 main.js 中写入以下内容:
import Vue from 'vue';
import ElementUI from 'element-ui'; //加入
import 'element-ui/lib/theme-chalk/index.css';//加入
import App from './App.vue';
Vue.use(ElementUI);//加入
new Vue({
el: '#app',
render: h => h(App)
});
在官方组件库中,根据需求找到组件,然后在项目中使用。 |
---|
<template>
<div class="login-box">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<h3>欢迎登录h3>
<el-form-item label="用户名" prop="name">
<el-input v-model="form.name" placeholder="请输入用户名">el-input>
el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" v-model="form.password" placeholder="请输入密码">el-input>
el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('form')">登录el-button>
el-form-item>
el-form>
div>
template>
<script>
export default {
name: "Login",
data(){
return{
form:{
name:'',
password:''
},
rules:{
name:[
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
],
password:[
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
]
}
}
},
methods:{
onSubmit(formName){
this.$refs[formName].validate((valid) => {
var vm = this;
if (valid) {
// 发送axios请求
this.axios({
method:'post',
url:'http://localhost:8090/login',
data:{
name:vm.form.name,
password:vm.form.password
}
}).then(function(resp){
// console.log(resp.data)
if(resp.data.errno==0){
//登录成功,要向vuex中存放user对象
var user = resp.data.data;
vm.$store.dispatch('asyncUpdateUser', user);
vm.$message({
message: '登录成功',
type: 'success'
});
setTimeout(function(){
vm.$router.push("/Home")
},2000)
}else{
vm.$message.error('用户名或密码错误');
}
})
} else {
this.$message.error('用户名或密码格式错误');
return false;
}
});
}
}
}
script>
<style scoped>
.login-box{
width: 500px;
height: 300px;
border: 1px solid #DCDFE6;
margin: 150px auto;
padding: 20px 50px 20px 30px;
border-radius: 20px;
box-shadow: 0px 0px 20px #DCDFE6;
}
style>
输入的用户名和密码,通过Axios请求去后端做校验,若校验成功则路由至Home组件。
登录页面的展示效果:(Element UI) |
---|
import Vue from 'vue'
import App from './App'
import VueRouter from 'vue-router'
// 导入上面创建的路由配置表
import router from './router'
// 导入ElementUI模块
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueRouter);
// 使用ElementUI模块
Vue.use(ElementUI);
// 使用Axios (vue中实现ajax功能的组件)
Vue.use(VueAxios, axios)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
// 配置路由
router,
render: h => h(App)
})
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
在项目根目录执行如下命令来安装 Vuex
若失败,可使用cnpm
npm install vuex --save
修改
main.js
文件,导入 Vuex,关键代码如下:
import Vuex from 'vuex'
Vue.use(Vuex);
在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex,代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
// 全局 state 对象,用于保存所有组件的公共数据
const state = {
// 定义一个 user 对象
// 在组件中是通过 this.$store.state.user 来获取
user: {
name: ''
}
};
// 实时监听 state 值的最新状态,注意这里的 getters 可以理解为计算属性
const getters = {
// 在组件中是通过 this.$store.getters.getUser 来获取
getUser(state) {
return state.user;
}
};
// 定义改变 state 初始值的方法,这里是唯一可以改变 state 的地方,缺点是只能同步执行
const mutations = {
// 在组件中是通过 this.$store.commit('updateUser', user); 方法来调用 mutations
updateUser(state, user) {
state.user = user;
}
};
// 定义触发 mutations 里函数的方法,可以异步执行 mutations 里的函数
const actions = {
// 在组件中是通过 this.$store.dispatch('asyncUpdateUser', user); 来调用 actions
asyncUpdateUser(context, user) {
context.commit('updateUser', user);
}
};
export default new Vuex.Store({
state,
getters,
mutations,
actions
});
修改 main.js 增加刚才配置的 store/index.js,关键代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
import store from './store'
Vue.use(Vuex);
new Vue({
el: '#app',
store
});
Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。但是有一个问题就是:vuex 的存储的数据只是在页面的中,相当于我们定义的全局变量,刷新之后,里边的数据就会恢复到初始化状态。但是这个情况有时候并不是我们所希望的。
监听页面是否刷新,如果页面刷新了,将 state 对象存入到 sessionStorage 中。页面打开之后,判断 sessionStorage 中是否存在 state 对象,如果存在,则说明页面是被刷新过的,将 sessionStorage 中存的数据取出来给 vuex 中的 state 赋值。如果不存在,说明是第一次打开,则取 vuex 中定义的 state 初始值。
在 App.vue 中增加监听刷新事件
export default {
name: 'App',
mounted() {
window.addEventListener('unload', this.saveState);
},
methods: {
saveState() {
sessionStorage.setItem('state', JSON.stringify(this.$store.state));
}
}
}
修改 store/index.js 中的 state
const state = sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')) : {
user: {
name: ''
}
};