1. image.png

Author: Thor

Version: 9.0.1

一、Vue简介


1.1 简介

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式的js框架,发布于 2014 年 2 月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router,vue-resource,vuex)或既有项目整合。

1.2 MVVM 模式的实现者——双向数据绑定模式 MVC

  • Model:模型层,在这里表示 JavaScript 对象
  • View:视图层,在这里表示 DOM(HTML 操作的元素)
  • ViewModel:连接视图和数据的中间件,Vue.js 就是 MVVM 中的 ViewModel 层的实现者

image.png

在 MVVM 架构中,是不允许 数据 和 视图 直接通信的,只能通过 ViewModel 来通信,而 ViewModel 就是定义了一个Observer` 观察者

  • ViewModel 能够观察到数据的变化,并对视图下对应的内容进行更新
  • ViewModel 能够监听到视图的变化,并能够通知数据发生改变

至此,我们就明白了,Vue.js 就是一个 MVVM 的实现者,他的核心就是实现了 DOM 监听 与 数据绑定

1.3 其它 MVVM 实现者 angularjs — vue - reactjs

  • 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进行绑定的。

1.4 为什么要使用 Vue.js

  • 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 20多kb (Angular 压缩后 56kb+,React 压缩后 44kb+)
  • 移动优先。更适合移动端,比如移动端的 Touch 事件
  • 易上手,学习曲线平稳,文档齐全
  • 吸取了 Angular(模块化)和 React(虚拟 DOM)的长处,并拥有自己独特的功能,如:计算属性
  • 开源,社区活跃度高

1.5 Vue.js 的两大核心要素

1.5.1 数据驱动

image.png

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

1.5.2 组件化
  • 页面上每个独立的可交互的区域视为一个组件
  • 每个组件对应一个工程目录,组件所需的各种资源在这个目录下就近维护
  • 页面不过是组件的容器,组件可以嵌套自由组合(复用)形成完整的页面

二、Vue的初体验


2.1在页面引入vue的js文件即可。

注意:cdn是一种加速策略,能够快速的提供js文件

  1. <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>

2.2 在页面中绑定vue元素

  1. 创建一个div,id是app
  2. <div id="app"></div>

2.3 创建vue对象,设计对象的内容

其中该vue对象,绑定了页面中id是app的那个div

  1. <script>
  2. new Vue({
  3. el:"#app",
  4. data:{
  5. title:"hello vue!",
  6. args1:"hi!",
  7. age:18,
  8. flag:true
  9. }
  10. });
  11. </script>
  1. # el: element的简称,也就是Vue实例挂载的元素节点,值可以是 CSS 选择符,或实际 HTML 元素,或返回 HTML 元素的函数。
  2. # data: 用于提供数据的对象,里面存放键值对数据。

image.png

2.4 在页面的元素中使用插值表达式来使用vue对象中的内容

  1. <div id="app">
  2. {{ title }} el表达式 可以属性值和方法结果
  3. </div>

三、 插值表达式


插值表达式的作用是在View中获得Model中的内容

Model中的内容如下:

  1. new Vue({
  2. el:"#app",
  3. data:{
  4. title:"hello world!"
  5. },
  6. methods:{
  7. sayHello:function(){
  8. return "hello vue";
  9. }
  10. }
  11. });

3.1 简单使用插值表达式获取数据

  1. <div id="app">
  2. {{title}}
  3. </div>

此时,页面上将会显示”Hello world!”

3.2 在插值表达式中获取数组中的内容

  1. <div id="app">
  2. {{[1,2,3,4][2]}}
  3. </div>

此时,页面上会显示“3”,也就是数组中的第三个元素被获取。

3.3 使用插值表达式获取对象中的属性

  1. <div id="app">
  2. {{ {"name":"xiaoyu","age":20}.age }}
  3. </div>

此时,页面上会显示“20”,也就是对象中age属性的值。

3.4 使用插值表达式调用Vue中的方法

  1. <div id="app">
  2. {{ sayHello()}}
  3. </div>

此时,页面上会显示“hello vue”,也就是调用了vue对象中的sayHello方法,并展示了方法的返回值。

image.png

四、Vue对象总结


Vue.js通过加载js,实现对页面的快速渲染。vue封装的js该如何使用? 就必须了解MVVM双向数据绑定模式。Vue将视图层和数据层分离,通过MVVM建立视图层和数据层的连接。其中,插值表达式是一种连接方式,可以通过插值表达式以多种方式,快速的从数据层获取数据并展示在视图层上。数据层Vue对象,也是由很多部分组成,比如之前介绍的el,data,methods等,以及之后要介绍的mount,computed等。

五、Vue的分支 v-if


5.1 v-if

Vue中的分支语句v-if非常好理解,逻辑跟Java中的if-else相同。v-if语句块包含以下内容:

  • v-if
  • v-else
  • v-else-if

接下来以一个简单例子即可理解:

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title></title>
  5. </head>
  6. <body>
  7. <div id="app">
  8. <p v-if="flag">
  9. 今天天气很舒服!
  10. </p>
  11. <p v-else-if="rich">
  12. 今天天气很燥热!晚上要去放松一下!
  13. </p>
  14. <p v-else="rich">
  15. 晚上只能自嗨!
  16. </p>
  17. </div>
  18. </body>
  19. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  20. <script>
  21. new Vue({
  22. el:'#app',
  23. data:{
  24. flag:false,
  25. rich:false
  26. },
  27. methods:{
  28. }
  29. });
  30. </script>
  31. </html>

从这个例子可以看出,vue对象中的data提供了分支的条件。根据条件,如果是true,则v-if的内容就会显示,反之不显示。

5.2 v-show

v-if和v-show之间有着看似相同的效果,但优化上却有区别。先看下面这个例子:

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title></title>
  5. </head>
  6. <body>
  7. <p v-show="rich">
  8. 有钱!
  9. </p>
  10. <p v-if="rich">
  11. 有钱!
  12. </p>
  13. <button type="button" @click="rich=!rich">今晚彩票开奖</button>
  14. </div>
  15. </body>
  16. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  17. <script>
  18. new Vue({
  19. el:'#app',
  20. data:{
  21. flag:false,
  22. rich:false
  23. },
  24. methods:{
  25. }
  26. });
  27. </script>
  28. </html>

通过点击“今晚彩票开奖”按钮,能切换rich的值,此时发现,v-if和v-show的显示状态都会来回切换。看起来是一样的,但通过查看控制台代码发现,v-show实际会将p标签的css样式的display属性设为none来达到隐藏的效果。

image.png

而v-if是直接在页面上添加和删除p标签来达到效果,因此v-show在反复切换的应用场景下,效率比v-if更高。

image.png

六、Vue的循环 v-for


Vue中的循环关键字并没有Java的那么多,只有v-for,但用法上有多种。接下来我们来逐一介绍。

6.1 普通的for循环

我们需要定义数据源,然后通过v-for来遍历数据源,再使用差值表达式输出数据。

  1. <body>
  2. <div id="app">
  3. <ul>
  4. <li v-for="a in args">{{a}}</li>
  5. </ul>
  6. </div>
  7. </body>
  8. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  9. <script>
  10. new Vue({
  11. el:'#app',
  12. data:{
  13. args:[1,2,3,4,5,6]
  14. }
  15. });
  16. </script>

在这个例子中,数据源提供了一个数组。视图层通过v-for来循环输出多个li标签,非常简单。

6.2 带着索引的for

  1. <body>
  2. <div id="app">
  3. <ul>
  4. <li v-for=" (a,i) in args" :key='i'>{{i}}{{a}}</li>
  5. </ul>
  6. </div>
  7. </body>
  8. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  9. <script>
  10. new Vue({
  11. el:'#app',
  12. data:{
  13. args:[1,2,3,4,5,6]
  14. }
  15. });
  16. </script>

此时的i就是每次循环的循环变量 ,从0开始一直到元素个数-1

6.3 遍历一个对象中的信息: v、k、i (value key index)

  1. <body>
  2. <div id="app">
  3. <ul>
  4. <li v-for="(v,k,i) in student">{{i+1}}--{{k}}--{{v}}</li>
  5. </ul>
  6. </div>
  7. </body>
  8. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  9. <script>
  10. new Vue({
  11. el:'#app',
  12. data:{
  13. student:{
  14. username:'小鱼',
  15. age:20,
  16. girl:'如花'
  17. }
  18. }
  19. });
  20. </script>

v、k、i 这几个字符可以自己定义,分别表示每次循环内容的值、键、序号。

  • v: 循环中每条数据的值 小鱼、20、如花
  • k: 循环中每天数据的键 username、age、girl
  • i: 循环的序号,从0开始
页面效果如下:
image.png

6.4 遍历一个对象数组:嵌套的for循环 二维表

  1. <body>
  2. <div id="app">
  3. <ul>
  4. <li v-for=" student in students">
  5. <span v-for="(v,k,i) in student">{{i+1}}--{{k}}--{{v}}</span>
  6. </li>
  7. </ul>
  8. </div>
  9. </body>
  10. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  11. <script>
  12. new Vue({
  13. el:'#app',
  14. data:{
  15. students:[
  16. {
  17. name:'xiaoming',
  18. age:20
  19. },
  20. {
  21. name:'xiaowang',
  22. age:21
  23. }
  24. ]
  25. }
  26. });
  27. </script>

可以清楚的看到,此时数据源是一个student数组,通过两层v-for循环,外层遍历数组中的每个student对象,内层v-for遍历每个对象的v、k、i。

页面效果如下:

image.png

七、Vue的属性绑定


Vue提供了多个关键字,能快速的将数据对象中的值绑定在视图层中。

7.1 v-model

通过v-model将标签的value值与vue对象中的data属性值进行绑定。

  1. <html>
  2. <head>
  3. <meta charset="utf-8" />
  4. <title></title>
  5. </head>
  6. <body>
  7. <div id="app">
  8. <input type="text" v-model="title">
  9. {{title}}
  10. </div>
  11. </body>
  12. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  13. <script type="text/javascript">
  14. new Vue({
  15. el:'#app',
  16. data:{
  17. title:"hello vue"
  18. }
  19. })
  20. </script>
  21. </html>

此时input标签中加入了“v-model=’title’”,表示input的value值与vue对象中的title属性绑定,当在input输入框中输入内容会实时修改title的值。于是{{title}}插值表达式能实时输出input输入框内的值。

页面效果如下:
image.png

image.png

7.2 v-bind

我们知道插值表达式是不能写在html的标签的属性内的,那如果一定要用vue中的属性作为html标签的属性的内容,就可以通过v-bind进行属性绑定。

  1. <html>
  2. <head>
  3. <meta charset="utf-8" />
  4. <title></title>
  5. </head>
  6. <body>
  7. <div id="app">
  8. <a v-bind:href="link">点我</a>
  9. </div>
  10. </body>
  11. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  12. <script type="text/javascript">
  13. new Vue({
  14. el:'#app',
  15. data:{
  16. link:'http://www.baidu.com'
  17. }
  18. })
  19. </script>
  20. </html>

这样,a标签内的href属性就可以使用vue对象中的属性值。

注意: v-bind也可以简写,使用冒号“:”来代替。

  1. <a v-bind:href='link'></a> ==> <a :href='link'>

八、Vue的事件绑定


关于事件,要把握好三个步骤:设参、传参和接参。

  1. <html>
  2. <head>
  3. <meta charset="utf-8" />
  4. <title></title>
  5. </head>
  6. <body>
  7. <div id="app">
  8. sum={{sum}}<br/>
  9. {{sum>10?'总数大于10':'总数不大于10'}}<br/>
  10. <button type="button" @click="increase(2)">增加</button>
  11. </div>
  12. </body>
  13. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  14. <script type="text/javascript">
  15. new Vue({
  16. el:'#app',
  17. data:{
  18. sum:0
  19. },
  20. methods:{
  21. increase:function(s){
  22. this.sum+=s
  23. },
  24. btn:function (event){
  25. // var inputer = this.$refs.inputer;
  26. // this.file = inputer.file[0];
  27. //let files = document.getElementById("file").files
  28. this.file = event.target.files;
  29. let formData = new FormData();
  30. formData.append("file",this.file[0])
  31. var vm = this;
  32. $.ajax({
  33. method:"post",
  34. url:"/upload",
  35. contentType:false,
  36. data: formData,
  37. cache: false, //上传文件无需缓存
  38. processData: false, //用于对data参数进行序列化处理 这里必须false
  39. success:function (res){
  40. console.log(res)
  41. }
  42. });
  43. }
  44. }
  45. })
  46. </script>
  47. </html>

从这里例子中:

  • 设参:
  1. <button type="button" @click="increase(2)">增加</button>
  • 传参:
  1. increase:function(s)
  • 接参:
  1. this.sum+=s

接下来我们来看一下VUE中如何进行事件绑定。

8.1 v-on

通过配合具体的事件名,来绑定vue中定义的函数

  1. <html>
  2. <head>
  3. <meta charset="utf-8" />
  4. <title></title>
  5. </head>
  6. <body>
  7. <div id="app">
  8. <input type="text" v-on:click="changeMajor" />
  9. </div>
  10. </body>
  11. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  12. <script type="text/javascript">
  13. new Vue({
  14. el:'#app',
  15. data:{
  16. major:'java'
  17. },
  18. methods:{
  19. sayHi:function(){
  20. alert("HELLO VUE!");
  21. },
  22. changeMajor:function(){
  23. console.log("change Title")
  24. }
  25. }
  26. </script>
  27. </html>

此时,该按钮,在点击时将会调用Vue对象中定义的changeMajor方法。

注意: v-on也可以简写,使用”@”替代。

  1. <input type="text" @click="changeMajor" />

8.2 事件修饰符(了解)

可以使用Vue中定义好的事件修饰符,快速达到效果。查看以下例子:

  1. <html>
  2. <head>
  3. <meta charset="utf-8" />
  4. <title></title>
  5. </head>
  6. <body>
  7. <div id="app">
  8. <p @mousemove="mm">
  9. x:{{x}}
  10. y:{{y}}
  11. <span @mousemove.stop>鼠标移动到此即停止</span>
  12. </p>
  13. </div>
  14. </body>
  15. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  16. <script type="text/javascript">
  17. new Vue({
  18. el:'#app',
  19. data:{
  20. x:0,
  21. y:0
  22. },
  23. methods:{
  24. mm:function(event){
  25. this.x = event.clientX;
  26. this.y = event.clientY;
  27. },
  28. stopm:function(event){
  29. event.stopPropagation();
  30. }
  31. }
  32. })
  33. </script>
  34. </html>

当鼠标经过P标签区域内时,区域内就会显示X和Y轴的坐标,如果经过P标签内的Span标签内时,此时会调用事件属性mousemove.stop预定的效果,鼠标移动的效果将会被取消,X和Y不再显示信息。

8.3计算属性:computed(了解)

8.3.1 什么是计算属性

计算属性的重点突出在 属性 两个字上(属性是名词),首先它是个 属性 其次这个属性有 计算 的能力(计算是动词),这里的 计算 就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;

8.3.2 计算属性与方法的区别
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>布局篇 计算属性</title>
  6. <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
  7. </head>
  8. <body>
  9. <div id="vue">
  10. <p>调用当前时间的方法:{{currentTime1()}}</p>
  11. <p>当前时间的计算属性:{{currentTime2}}</p>
  12. </div>
  13. <script type="text/javascript">
  14. var vm = new Vue({
  15. el: '#vue',
  16. data: {
  17. message: 'Hello Vue'
  18. },
  19. methods: {
  20. currentTime1: function () {
  21. return Date.now();
  22. }
  23. },
  24. computed: {
  25. currentTime2: function () {
  26. this.message;
  27. return Date.now();
  28. }
  29. }
  30. });
  31. </script>
  32. </body>
  33. </html>

说明

  • methods:定义方法,调用方法使用 currentTime1(),需要带括号
  • computed:定义计算属性,调用属性使用 currentTime2,不需要带括号;this.message 是为了能够让 currentTime2 观察到数据变化而变化

计算属性:定义的方式,跟我methods定一样的,使用的时候又是采用变量的形式
计算属性和data区别在,计算属性定义的一个全局变量,data定义的时候局部变量

注意:methods 和 computed 里不能重名

8.3.3 测试效果

仔细看图中说明,观察其中的差异

image.png

8.3.4 结论

调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点;计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销

九、Vue的组件化(抽象)


9.1 什么是“组件化”

Vue的组件化设计思想借鉴了Java的面向对象思想。Java认为万物皆对象,在Vue中,万物皆组件。

也就是说,在实际的vue项目中,以及使用了Vue框架的项目中,Vue的对象都会以组件的形式出现,能被反复使用。

要想实现组件化,需要在页面中注册组件:关于注册的方式有两种,分别是全局注册和本地注册。

9.1.1 组件的全局注册
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>vue组件的全局注册</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <model1></model1>
  10. <model1></model1>
  11. <model1></model1>
  12. </div>
  13. <hr/>
  14. <div id="app1">
  15. <model1></model1>
  16. <model1></model1>
  17. <model1></model1>
  18. </div>
  19. </body>
  20. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  21. <script>
  22. //通过Vue.component实现组件的全局注册,全局注册后的组件可以被重复使用。
  23. Vue.component("model1",{
  24. template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
  25. data:function(){
  26. return {
  27. title:"hello vue"
  28. }
  29. },
  30. methods:{
  31. btnfn:function(){
  32. alert("hello !!!");
  33. }
  34. }
  35. });
  36. new Vue({
  37. el:'#app'
  38. })
  39. new Vue({
  40. el:'#app1'
  41. })
  42. </script>
  43. </html>

9.1.2 组件的本地注册

vue的全局注册,也就意味着在页面的任意一个被vue绑定过的div中,都可以使用全局注册了的vue组件。

但是,如果是对vue组件进行本地注册,那么在其他被vue绑定的div中,不能使用该组件。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>vue组件的本地(局部)注册</title>
  6. </head>
  7. <body>
  8. <div id="app">
  9. <model11></model11>
  10. </div>
  11. <hr/>
  12. <!--在这里使用组件model11会报错-->
  13. <div id="app1">
  14. <model11></model11>
  15. </div>
  16. </body>
  17. <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
  18. <script>
  19. new Vue({
  20. el:'#app',
  21. components:{
  22. "model11":{
  23. template:"<div><h1>{{title}}</h1><button type='button' @click='btnfn'>点我</button></div>",
  24. data:function(){
  25. return {
  26. title:"hello vue"
  27. }
  28. },
  29. methods:{
  30. btnfn:function(){
  31. alert("hello !!!");
  32. }
  33. }
  34. }
  35. }
  36. })
  37. new Vue({
  38. el:'#app1'
  39. })
  40. </script>

9.1.3 小结

这是一个完整的Vue组件。该组件包含了三个部分:template(html视图层内容)、script(Model层)、style(CSS样式)。这样封装好的组件可以被复用,也可以作为其他组件的组成部分而被封装——Java的面向对象再次体现。

  • 特点1: template标签内,必须有且只能有一个根标签。
  • 特点2: componet中注册的组件中的data,必须是函数的形式。
    如下:
  1. data:function(){
  2. return {
  3. title:"hello vue"
  4. }
  5. }

9.2 组件的生命周期

Vue中的组件也是有生命周期的。一个Vue组件会经历:初始化、创建、绑定、更新、销毁等阶段,不同的阶段,都会有相应的生命周期钩子函数被调用。

  1. <html>
  2. <head>
  3. <meta charset="UTF-8">
  4. <title>生命周期</title>
  5. </head>
  6. <body>
  7. <div id="app1">
  8. {{title}}
  9. <button type="button" @click="changeTitle">change title</button>
  10. <button type="button" @click="destroy">destroy</button>
  11. </div>
  12. </body>
  13. <script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
  14. <script>
  15. new Vue({
  16. el:"#app1",
  17. data:{
  18. title:"this is title"
  19. },
  20. methods:{
  21. changeTitle:function(){
  22. this.title= "new title";
  23. },
  24. destroy:function(){
  25. this.$destroy();
  26. }
  27. },
  28. beforeCreate(){
  29. console.log("beforeCreate")
  30. },
  31. created(){
  32. console.log("created")
  33. },
  34. beforeMount(){
  35. console.log("beforeMount")
  36. },
  37. mounted(){
  38. console.log("mounted")
  39. },
  40. beforeUpdate(){
  41. console.log("beforeUpdate")
  42. },
  43. updated(){
  44. console.log("updated")
  45. },
  46. beforeDestroy(){
  47. console.log("beforeDestory")
  48. },
  49. destroyed(){
  50. console.log("destory")
  51. }
  52. })
  53. </script>
  54. </html>
组件的生命周期钩子
image.png

作业:
采用ajax 编写我的登录和注册

十、使用Vue-Cli搭建Vue项目


10.1 什么是vue-cli 等同于我的maven

cli: Command Line 命令行工具,vue-cli就是vue的命令行工具,也称之为脚手架,使用vue-cli提供的各种命令可以拉取、创建、运行我们需要使用到的框架,比如webpack、Element UI、Element Admin等等。那么要想使用vue-cli命令,需要先安装node.js。

10.2 node.js的介绍及安装

node.js的介绍=等同于tomcat

node.js提供了前端程序的运行环境,可以把node.js理解成是运行前端程序的服务器。

node.js的安装

从官网下载安装即可:
http://nodejs.cn/download/ https://nodejs.org/zh-cn/ https://nodejs.org/en/download/releases/

建议:不要安装到系统C盘

完成后 配置环境变量:D:\install\nodejs\(自己的路径)

测试node.js是否安装成功: 在DOS窗口中输入“node -v” 查看版本,如果看到版本,就表示安装成功。
image.png

10.3 使用node.js安装vue-cli

使用如下命令安装vue-cli

  1. npm install vue-cli -g
  • npm: 使用node.js的命令
  • install: 安装
  • vue-cli: 要安装的vue-cli
  • -g: 全局安装
当出现以下界面,表示正在安装:
image.png

如果使用npm官方镜像速度比较慢,可以使用淘宝镜像来安装:

  1. npm install -g cnpm --registry=https://registry.npm.taobao.org 镜像

之后使用npm命令时就可以替换成cnpm

  1. cnpm install vue-cli -g
  2. cnpm install cnpm run dev

10.4 使用vue-cli下载项目骨架搭建我们的项目

就像maven一样,vue为我们提供了一些官方项目骨架。使用vue list命令可以列出当前官方提供的骨架,可以使用这些骨架来快速搭建出项目。 受网速影响

  1. vue list

image.png

10.5 创建项目目录并打开

需要去我们的工作空间,去手动新建一个项目目录

  1. mkdir e:/my-vue-projectcd e:/my-vue-project

10.6 使用Webpack骨架快速创建项目

在my-vue-project目录中使用以下命令下载项目骨架

  1. vue init webpack my-project1
  • webpack: 骨架名称
  • my-project1: 项目名称
过程中会出现如下界面,需要手动操作。
image.png
出现如下界面,表示安装成功。
image.png

image.png

进入到my-project1文件夹内后,使用以下命令来运行项目。

  1. npm run dev

image.png

访问http://localhost:8081,页面效果如下:
image.png
此时,项目目录及各目录介绍如下:
image.png

image.png

image.png

image.png

10.7 webpack项目的几个常用命令

  • npm install 或者 cnpm install
    在运行和调试项目前,一般都需要先执行该命令,目的是安装项目运行所需要的环境。
  • npm run dev 或者 cnpm run dev
    以调试的方式运行项目
  • npm run build
    生成用于项目部署所需的最小资源,生成的内容存放在build文件夹内。

十一、Vue组件间的参数传递


11.1 idea中安装Vue插件

在idea中下载vue插件

image.png

在idea中配置,让idea能够操作.vue文件

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25361323/1646014047515-c6e14a10-eb82-47cb-889f-d61fd1fe52af.png#averageHue=%23f9f7f7&clientId=ue2e80557-a3de-4&from=paste&height=380&id=u27f8fe42&originHeight=759&originWidth=1609&originalType=binary&ratio=1&rotation=0&showTitle=false&size=90799&status=done&style=none&taskId=u8ee279f2-c9a3-425f-8436-e9c5007d636&title=&width=804.5)

11.2 在项目中创建子组件

创建子组件Content组件

  1. <template>
  2. <div>
  3. 商品列表...
  4. {{MyTitle}}
  5. <button type="button" @click="btnfn('hello java')">点我</button>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name: "Content.vue",
  11. props:{
  12. 'MyTitle':{
  13. type:String,
  14. required:true,
  15. default:'XX'
  16. },
  17. 'btnfn':{
  18. type:Function
  19. }
  20. }
  21. }
  22. </script>
  23. <style scoped>
  24. </style>

11.3 注册子组件

在main.js中注册子组件

  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. //引入Content
  4. import Content from './components/Content'
  5. //全局注册组件
  6. Vue.component('MyContent',Content);
  7. new Vue({
  8. el: '#app',
  9. render: h => h(App)
  10. })

11.4 在App.vue中使用组件并传递参数

  1. <template>
  2. <div id="app">
  3. <MyContent :MyTitle="msg" :btnfn="FCfn" ></MyContent>
  4. </div>
  5. </template>
  6. <script>
  7. import MHeader from './components/Header'
  8. export default {
  9. name: 'app',
  10. data(){
  11. return {
  12. msg:'hello vue!!'
  13. }
  14. },
  15. components:{
  16. "MHeader":MHeader
  17. },
  18. methods:{
  19. FCfn:function(m){//hello java
  20. this.msg = m;
  21. }
  22. }
  23. }
  24. </script>
  25. <style>
  26. </style>

11.5 父传子

通过子组件的props部分,来指明明接收的参数,父组件通过在标签中写明参数的键值对来传递参数。

  1. props是表示一个组件的参数部分,那么props的写法有两种:
  2. 1props:[参数列表]
  3. 比如: props:['MyProp1','MyProp2',...]
  4. 2props:{参数名1:{type:String,required:true,default:'XX'},参数名2:{...}}

image.png
image.png

11.6 子传父

子传父的过程十分复杂,下列图解中已经配上了详细的步骤。

image.png

image.png

十二、Vue-router 路由


12.1 安装路由模块

第一步:采用下面命令安装我们的router

  1. npm install vue-router -s

第二步:在src 新建一个router文件夹,router文件里面创建一个index.js(这个是默认文件名字)

第三步:导入vue和router路径

import Vue from ‘vue’
import router from ‘./router’ //引入路由模块

第四步:配置我们自己的组件路由

  1. a.导入我们的组件路径(绝对路径(@)和相对路径(../))
  2. import MyRouter from '@/components/MyRouter'
  3. b.配置我们的route,配置一个pathcomponent
  1. routes: [
  2. {
  3. path:'/MyRouter', //给一个组件设置路径
  4. component: MyRouter
  5. }
  6. ]

第五步:在App.vue 中写上我的 标签,把所有的路由表加入的App组件中

第六步:导入我们的路由; 在new Vue中注册我们的 路由

  1. import router from './router'
  2. new Vue({
  3. el: '#app',
  4. router, //注册路由
  5. components: { App },
  6. template: '<App/>'
  7. })

12.2 引入路由模块并使用

在main.js中引入路由模块并使用

  1. import Vue from 'vue'
  2. import App from './App'
  3. import router from './router' //引入路由模块
  4. Vue.config.productionTip = false
  5. /* eslint-disable no-new */
  6. new Vue({
  7. el: '#app',
  8. router, //使用路由模块
  9. components: { App },
  10. template: '<App/>'
  11. })

注意事项:

  1. 解决“export default (imported as VueRouter‘) was not found in vue-router‘问题
  2. npm i vue-router@3.1.3

12.3 路由初体验

12.3.1 创建App.vue
  1. <template>
  2. <div id="app">
  3. <ul class="nav nav-tabs">
  4. <li role="presentation" class="active"><router-link to="/Home">首页</router-link></li>
  5. <li role="presentation"><router-link to="/Product">商品列表</router-link></li>
  6. </ul>
  7. <router-view/>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. name: 'App'
  13. }
  14. </script>

改变url,发现中的内容发生改变

向router实例中添加mode属性:

  • 值”hash”: url带# 适用于调试模式
  • 值”history” url不带#

12.3.2 创建Home.vue
  1. <template>
  2. <div>首页</div>
  3. </template>
  4. <script>
  5. export default {
  6. name: "Home"
  7. }
  8. </script>
  9. <style scoped>
  10. </style>

12.3.3 创建Product.vue
  1. <template>
  2. <div>商品列表 商品的id:{{id}}</div>
  3. </template>
  4. <script>
  5. export default {
  6. name: "Product",
  7. data(){
  8. return{
  9. id:this.$route.params.id //接参
  10. }
  11. }
  12. }
  13. </script>
  14. <style scoped>
  15. </style>

12.3.4 修改静态路由表

修改路由表src/router/index.js

  1. import Vue from 'vue'
  2. import Router from 'vue-router'
  3. import HelloWorld from '@/components/HelloWorld'
  4. import Home from "../components/Home";
  5. import Product from "../components/Product";
  6. Vue.use(Router)
  7. export default new Router({
  8. routes: [
  9. {
  10. path:'/Home',
  11. component: Home
  12. },
  13. {
  14. path:'/Product/:id', //设参
  15. component:Product
  16. }
  17. ]
  18. })

12.4 参数的传递

在路由表中设参

  1. export default new Router({
  2. routes: [
  3. ...
  4. {
  5. path:'/Product/:id', //设参
  6. component:Product
  7. }
  8. ]
  9. })

在App.vue中传参

  1. <template>
  2. <div id="app">
  3. <ul class="nav nav-tabs">
  4. ...
  5. <li role="presentation"><router-link to="/Product/1">商品列表</router-link></li>
  6. ...
  7. </ul>
  8. <router-view/>
  9. </div>
  10. </template>

在Product.vue中接参

  1. <template>
  2. <div>商品列表 商品的id:{{id}}</div>
  3. </template>
  4. <script>
  5. export default {
  6. name: "Product",
  7. data(){
  8. return{
  9. id:this.$route.params.id //接参
  10. }
  11. }
  12. }
  13. </script>
  14. <style scoped>
  15. </style>

12.5 程序式路由的实现

使用进行路由跳转是有局限性的,可以通过this.$router.push(‘/Product/1’)的js方式实现路由跳转,更加灵活。

  1. <template>
  2. <div id="app">
  3. <ul class="nav nav-tabs">
  4. <li role="presentation" class="active"><router-link to="/Home">首页</router-link></li>
  5. <li role="presentation"><router-link to="/Product">商品列表</router-link></li>
  6. <button type="button" @click="btnfn">点我</button>
  7. </ul>
  8. <router-view/>
  9. </div>
  10. </template>
  11. <script>
  12. export default {
  13. name: 'App',
  14. methods:{
  15. btnfn(){
  16. //代替router-link实现路由跳转
  17. this.$router.push("/Product/1");
  18. }
  19. }
  20. }
  21. </script>

十三、使用Axios发送请求


13.1 什么是 Axios

Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,她的主要作用就是实现 AJAX 异步通信,其功能特点如下:

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF(跨站请求伪造)

GitHub:https://github.com/axios/axios

13.2 为什么要使用 Axios

由于 Vue.js 是一个 视图层框架 并且作者(尤雨溪)严格准守 SoC (关注度分离原则),所以 Vue.js 并不包含 AJAX 的通信功能,为了解决通信问题,作者单独开发了一个名为 vue-resource 的插件,不过在进入 2.0 版本以后停止了对该插件的维护并推荐了 Axios 框架

13.3 Axios的使用

13.3.1 安装vue axios
  1. npm install --save axios vue-axios

13.3.2 在main.js中引入

在项目中使用axios模块

  1. import Vue from 'vue'
  2. import axios from 'axios'
  3. import VueAxios from 'vue-axios'
  4. Vue.use(VueAxios, axios)

13.3.3 发送ajax请求
  1. <template>
  2. <div id="app">
  3. <div style="width:50%" class="container">
  4. <div>
  5. <h3>Regist</h3>
  6. <h5>Email</h5>
  7. <input type="text" class="form-control" v-model="mail" /><br />
  8. {{mail}}
  9. <h5>Password</h5>
  10. <input type="password" class="form-control" v-model="password" /><br />
  11. {{password}}
  12. <h5>Gender</h5>
  13. <input type="radio" name="gender" v-model="gender" value="female" />男
  14. <input type="radio" name="gender" v-model="gender" value="male" />女<br />
  15. <h5>Hobby</h5>
  16. <input type="checkbox" name="hobby" v-model="hobby" value="music">音乐
  17. <input type="checkbox" name="hobby" v-model="hobby" value="movie">电影
  18. <input type="checkbox" name="hobby" v-model="hobby" value="sport">运动
  19. <br/>
  20. <button type="button" class="btn btn-success" @click="registfn">注册</button>
  21. </div>
  22. </div>
  23. </div>
  24. </template>
  25. <script>
  26. import MHeader from './components/Header'
  27. export default {
  28. name: 'app',
  29. data(){
  30. return {
  31. mail:'',
  32. password:'',
  33. gender:'',
  34. hobby:''
  35. }
  36. },
  37. methods:{
  38. registfn:function(){
  39. this.axios({
  40. method:'get',
  41. url:'http://localhost:8090/regist?mail='+this.mail+'&password='+this.password,
  42. }).then(function (response) {
  43. console.log(response.data)
  44. });
  45. }
  46. }
  47. }
  48. </script>

13.3.4 服务端解决跨域问题
  1. <mvc:cors>
  2. <mvc:mapping path="/"
  3. allowed-origins="*"
  4. allowed-methods="POST, GET, OPTIONS, DELETE, PUT,PATCH"
  5. allowed-headers="Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
  6. allow-credentials="true" />
  7. </mvc:cors>

在spring-mvc.xml中加入上述这一段。其中,allowed-origins指的是允许的访问源的域名,”*”表示任何人都可以访问,也可以指明具体的域名

13.3.5 解决axios无法传递data中的参数问题

原因:默认情况下发送axios时请求头中的内容类型为: (后端没有使用@RequestBody)

  1. Content-Type:application/json;charset=UTF-8

而实际服务端需要的是:

  1. Content-Type:application/x-www-form-urlencoded

因此,使用axios的qs内置库中的方法进行内容类型的转换。

  1. import Qs from 'qs'
  2. this.axios({
  3. method:'post',
  4. url:'http://localhost:8081/regist',
  5. transformRequest: [function (data) {
  6. return Qs.stringify(data)
  7. }],
  8. data:{
  9. email:this.email
  10. }
  11. })
  12. .then(function (response) {
  13. alert(response.data.message)
  14. });

十四、Vue实战项目:Webpack登录验证后路由至列表页


对项目进行中的内容进行调整,结构如下:
image.png

各部分内容:

14.1 router路由模块: index.js(路由配置表)

  1. import Vue from 'vue'
  2. import Router from 'vue-router'
  3. import Login from '../views/Login'
  4. import Home from '../views/Home'
  5. // 安装路由
  6. Vue.use(Router);
  7. // 配置路由
  8. export default new Router({
  9. routes: [
  10. {
  11. // 路由路径
  12. path: '/Login',
  13. // 路由名称
  14. name: 'Login',
  15. // 跳转到组件
  16. component: Login
  17. },
  18. {
  19. path:'/Home',
  20. name:'Home',
  21. component:Home
  22. }
  23. ]
  24. });

14.2 使用Element-UI组件库

14.2.1 安装

在项目文件夹内使用命令来安装element-ui模块

  1. npm i element-ui -S

14.2.2 使用

在vue项目中引入element-ui并使用。在 main.js 中写入以下内容:

  1. import Vue from 'vue';
  2. import ElementUI from 'element-ui'; //加入
  3. import 'element-ui/lib/theme-chalk/index.css';//加入
  4. import App from './App.vue';
  5. Vue.use(ElementUI);//加入
  6. new Vue({
  7. el: '#app',
  8. render: h => h(App)
  9. });

14.2.3 在Element-UI组件库中查找组件
在官方组件库中,根据需求找到组件,然后在项目中使用。
image.png

14.3 编写登录组件:Login.vue

  1. <template>
  2. <div class="login-box">
  3. <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  4. <h3>欢迎登录</h3>
  5. <el-form-item label="用户名" prop="name">
  6. <el-input v-model="form.name" placeholder="请输入用户名"></el-input>
  7. </el-form-item>
  8. <el-form-item label="密码" prop="password">
  9. <el-input type="password" v-model="form.password" placeholder="请输入密码"></el-input>
  10. </el-form-item>
  11. <el-form-item>
  12. <el-button type="primary" @click="onSubmit('form')">登录</el-button>
  13. </el-form-item>
  14. </el-form>
  15. </div>
  16. </template>
  17. <script>
  18. export default {
  19. name: "Login",
  20. data(){
  21. return{
  22. form:{
  23. name:'',
  24. password:''
  25. },
  26. rules:{
  27. name:[
  28. { required: true, message: '请输入用户名', trigger: 'blur' },
  29. { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
  30. ],
  31. password:[
  32. { required: true, message: '请输入密码', trigger: 'blur' },
  33. { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
  34. ]
  35. }
  36. }
  37. },
  38. methods:{
  39. onSubmit(formName){
  40. this.$refs[formName].validate((valid) => {
  41. var vm = this;
  42. if (valid) {
  43. // 发送axios请求
  44. this.axios({
  45. method:'post',
  46. url:'http://localhost:8090/login',
  47. data:{
  48. name:vm.form.name,
  49. password:vm.form.password
  50. }
  51. }).then(function(resp){
  52. // console.log(resp.data)
  53. if(resp.data.errno==0){
  54. //登录成功,要向vuex中存放user对象
  55. var user = resp.data.data;
  56. vm.$store.dispatch('asyncUpdateUser', user);
  57. vm.$message({
  58. message: '登录成功',
  59. type: 'success'
  60. });
  61. setTimeout(function(){
  62. vm.$router.push("/Home")
  63. },2000)
  64. }else{
  65. vm.$message.error('用户名或密码错误');
  66. }
  67. })
  68. } else {
  69. this.$message.error('用户名或密码格式错误');
  70. return false;
  71. }
  72. });
  73. }
  74. }
  75. }
  76. </script>
  77. <style scoped>
  78. .login-box{
  79. width: 500px;
  80. height: 300px;
  81. border: 1px solid #DCDFE6;
  82. margin: 150px auto;
  83. padding: 20px 50px 20px 30px;
  84. border-radius: 20px;
  85. box-shadow: 0px 0px 20px #DCDFE6;
  86. }
  87. </style>

输入的用户名和密码,通过Axios请求去后端做校验,若校验成功则路由至Home组件。

登录页面的展示效果:(Element UI)
image.png

14.4 编写入口js:main.js

  1. import Vue from 'vue'
  2. import App from './App'
  3. import VueRouter from 'vue-router'
  4. // 导入上面创建的路由配置表
  5. import router from './router'
  6. // 导入ElementUI模块
  7. import ElementUI from 'element-ui';
  8. import 'element-ui/lib/theme-chalk/index.css';
  9. import axios from 'axios'
  10. import VueAxios from 'vue-axios'
  11. Vue.use(VueRouter);
  12. // 使用ElementUI模块
  13. Vue.use(ElementUI);
  14. // 使用Axios (vue中实现ajax功能的组件)
  15. Vue.use(VueAxios, axios)
  16. Vue.config.productionTip = false
  17. /* eslint-disable no-new */
  18. new Vue({
  19. el: '#app',
  20. // 配置路由
  21. router,
  22. render: h => h(App)
  23. })

十五、 Vuex的应用


15.1 什么是Vuex

Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

15.2 安装

在项目根目录执行如下命令来安装 Vuex

若失败,可使用cnpm

  1. npm install vuex --save

修改 main.js 文件,导入 Vuex,关键代码如下:

  1. import Vuex from 'vuex'
  2. Vue.use(Vuex);

15.3 配置 vuex

15.3.1 创建 Vuex 配置文件

在 src 目录下创建一个名为 store 的目录并新建一个名为 index.js 文件用来配置 Vuex,代码如下:

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. Vue.use(Vuex);
  4. // 全局 state 对象,用于保存所有组件的公共数据
  5. const state = {
  6. // 定义一个 user 对象
  7. // 在组件中是通过 this.$store.state.user 来获取
  8. user: {
  9. name: ''
  10. }
  11. };
  12. // 实时监听 state 值的最新状态,注意这里的 getters 可以理解为计算属性
  13. const getters = {
  14. // 在组件中是通过 this.$store.getters.getUser 来获取
  15. getUser(state) {
  16. return state.user;
  17. }
  18. };
  19. // 定义改变 state 初始值的方法,这里是唯一可以改变 state 的地方,缺点是只能同步执行
  20. const mutations = {
  21. // 在组件中是通过 this.$store.commit('updateUser', user); 方法来调用 mutations
  22. updateUser(state, user) {
  23. state.user = user;
  24. }
  25. };
  26. // 定义触发 mutations 里函数的方法,可以异步执行 mutations 里的函数
  27. const actions = {
  28. // 在组件中是通过 this.$store.dispatch('asyncUpdateUser', user); 来调用 actions
  29. asyncUpdateUser(context, user) {
  30. context.commit('updateUser', user);
  31. }
  32. };
  33. export default new Vuex.Store({
  34. state,
  35. getters,
  36. mutations,
  37. actions
  38. });

修改 main.js 增加刚才配置的 store/index.js,关键代码如下:

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. import store from './store'
  4. Vue.use(Vuex);
  5. new Vue({
  6. el: '#app',
  7. store
  8. });

15.3.2 解决浏览器刷新后 Vuex 数据消失问题
  • 问题描述

Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。但是有一个问题就是:vuex 的存储的数据只是在页面的中,相当于我们定义的全局变量,刷新之后,里边的数据就会恢复到初始化状态。但是这个情况有时候并不是我们所希望的。

  • 解决方案

监听页面是否刷新,如果页面刷新了,将 state 对象存入到 sessionStorage 中。页面打开之后,判断 sessionStorage 中是否存在 state 对象,如果存在,则说明页面是被刷新过的,将 sessionStorage 中存的数据取出来给 vuex 中的 state 赋值。如果不存在,说明是第一次打开,则取 vuex 中定义的 state 初始值。

  • 修改代码

在 App.vue 中增加监听刷新事件

  1. export default {
  2. name: 'App',
  3. mounted() {
  4. window.addEventListener('unload', this.saveState);
  5. },
  6. methods: {
  7. saveState() {
  8. sessionStorage.setItem('state', JSON.stringify(this.$store.state));
  9. }
  10. }
  11. }

修改 store/index.js 中的 state

  1. const state = sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')) : {
  2. user: {
  3. name: ''
  4. }
  5. };