《Vue入门到精通》最强Vue教程,附带经典案例,万字详解,干货十足!

目录:

一、前言二、Vue是什么?1、简介2、发展史3、为什么要学习Vue?4、jQuery、javascript、Vue的区别5、Vue框架构造

三、Vue的使用1、Vue环境搭建2、插值表达式3、指令(Directives)1、什么是指令2、指令API1、v-cloak 解决插值闪动2、v-text 填充纯文本3、v-html :填充HTML片段4、v-pre 填充原始信息5、v-once 只编译一次6、v-model 双向数据绑定7、v-on 事件绑定8、v-bind 属性绑定9、v-if 分支结构v-ifv-elsev-else-if

10、v-show 元素的显示与隐藏11、v-for 遍历12、自定义指令

3、案例1、计算机案例(实现简单的加法运算)2、原淘宝选项卡案例

4、表单输入绑定(1)单行文本 和 多行文本(2)单选按钮(3)复选按钮(4)下拉菜单示例:

5、计算属性(computed)(1)为什么需要计算属性?(2)计算属性的用法(3)计算属性与方法的区别

6、侦听器(1)为什么需要侦听器?(2)侦听器的使用案例:验证用户名是否存在

7、过滤器(1)过滤器的作用是什么?(2)自定义过滤器案例:使用过滤器格式化日期

8、选项/生命周期钩子函数(1)主要阶段(2)Vue实例的产生过程

9、Vue响应式数据(1)数组响应式数据(2)Vue响应式数据A、使用B、说明C、注意D、案例

10、组件(1)组件是什么?(2)组件的复用(3)data 必须是一个函数(4)组件的组织和注册(5)组件通信1、父组件向子组件传值2、子组件向父组件传值3、兄弟组件传值

11、插槽(1)插槽是什么?(2)插槽的基本使用(3)具名插槽(4)作用域插槽

四、Vue Router(路由)1、什么是路由?(1)后端路由(2)前端路由

2、SPA(1)前后端渲染的缺点(2)什么是SPA(3)实现简易的前端路由

3、Vue Router(1)简介(2)基本使用(3)路由重定向(4)嵌套路由1、简介:2、实现:

(5)动态路由匹配1、基本用法2、路由组件传递参数3、vue-router命名路由4、编程式导航(1)页面导航的两种方式(2)常用的编程式导航 API 如下:(3)编程式导航参数规则

五、Vue CLI (脚手架)1、简介2、安装3、基于3.x版本的脚手架创建vue项目4、Vue 脚手架生成的项目结构分析5、Vue 脚手架的自定义配置

六、Vux 集中状态管理1、回顾Vue组件通信2、Vuex 是什么3、什么样的数据适合存储到Vuex中?4、Vuex 的基本使用5、Vuex 的核心概念(1)State(状态)(2)Mutation(变化)1、简介2、使用:3、在组件中调用mutations中的方法

(3)Action1、简介2、使用3、组件中调用

(4)Getter1、简介2、使用3、在组件中使用

七、Element-UI八、经典案例1、图书管理系统2、购物车3、ToDoList待办事项列表4、简易计算器

九、总结

一、前言

在初步学习前端课程的时候,相信大家都知道Vue这个名字,至于Vue到底是什么却不得而知,又或者知道Vue是前端的一大流行框架,那么它到底是用来干啥的呢,对于其他框架而言Vue又有什么优势和特点,学习前端的人为什么都要去学习Vue呢?

对于这些答案,相信大家都想去亲自揭开它的谜底,但是如果你未学过JavaScript和jQuery那么,请先不要学习Vue,因为没有根基是非常晦涩难学的。反之学习的时候会特别轻松,所以希望大家学习Vue的时候,尽量都有javascript和jQuery库类的基础。

二、Vue是什么?

1、简介

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

什么是渐进式框架?

说白了,就是框架分层。那是如何分层的呢?就像《功夫》里面黄圣依手里拿的棒棒糖一样:最核心的是视图层渲染,然后往外是组件机制,在此基础上再加入路由机制,再加入状态管理,最外层是构建工具,vue和react都是如此。

Vue分层:声明式渲染>组件系统>客户端路由>集中式状态管理>项目构建

2、发展史

创始人:尤雨溪

照 片:

官 网:https://cn.vuejs.org/

安 装:https://cn.vuejs.org/v2/guide/installation.html =》点击开发版本

2013年,在 Google 工作的尤雨溪,受到 Angular 的启发,开发出了一款轻量框架,最初命名为 Seed 。2013年12月,更名为 Vue,图标颜色是代表勃勃生机的绿色,版本号是 0.6.0。2014.01.24,Vue 正式对外发布,版本号是 0.8.0。2014.02.25,0.9.0 发布,有了自己的代号:Animatrix,此后,重要的版本都会有自己的代号。2015.06.13,0.12.0,代号Dragon Ball,Laravel 社区(一款流行的 PHP 框架的社区)首次使用 Vue,Vue 在 JS 社区也打响了知名度。2015.10.26,1.0.0 Evangelion 是 Vue 历史上的第一个里程碑。同年,vue-router、vuex、vue-cli 相继发布,标志着 Vue从一个视图层库发展为一个渐进式框架。2016.10.01,2.0.0 是第二个重要的里程碑,它吸收了 React 的虚拟 Dom 方案,还支持服务端渲染。自从Vue 2.0 发布之后,Vue 就成了前端领域的热门话题。2019.02.05,Vue 发布了 2.6.0 ,这是一个承前启后的版本,在它之后,将推出 3.0.0。2019.12.05,在万众期待中,尤雨溪公布了 Vue 3 源代码,目前 Vue 3 处于 Alpha 版本。

3、为什么要学习Vue?

易用:熟悉 HTML 、 CSS 、 JavaScript 知识后,可快速上手 Vue灵活:在一个库和一套完整框架之间自如伸缩高效: 20kB 运行大小,超快虚拟 DOM

4、jQuery、javascript、Vue的区别

Vue是框架而jQuery顶多算个库类

​ 在Vue还未诞生之前,jQuey可以说是一个前端的流行框架,操作DOM元素非常方便,缺点是要大量的获取和操作DOM元素。在 React、Vue、Angular前端三大主流框架出现后,jQuey就被降级了,所以顶多算个库类。而在使用Vue等其他主流框架的时候,我们不需要一直关注DOM元素,因为在Vue中DOM是虚拟的,我们不需要去获取,这样就减轻了前端开发人员的工作量。

前端渲染方式

原生js字符串拼接ES6新语法……等,显得极为麻烦使用 template-web.js 模板引擎,但没有提供专门的事件机制Vue提供了一套极为标准的模板,包括插值、指令、事件、属性、样式、分支循环……等,可以说我们只使用Vue就可以搭建并完成一整套的网页应用

5、Vue框架构造

Vue程序结构框架:

Vue.js是典型的MVVM框架,什么是MVVM框架,介绍之前我们先介绍下什么是MVC框架

MVC是后端分层开发的思想 即 Model-View-Controller 的缩写,就是 模型-视图-控制器 , 也就是说一个标准的Web 应用程序是由这三部分组成的:

View 用来把数据以某种方式呈现给用户。

Model 其实就是数据。

Controller 接收并处理来自用户的请求,并将 Model 返回给用户。

MVC框架对于简单的应用处理是可以的,也符合软件架构的分层思想。但随着H5 的不断发展,人们更希望使用H5 开发的应用能和Native 媲美,或者接近于原生App 的体验效果,于是前端应用的复杂程度已不同往日,今非昔比。这时前端开发就暴露出了三个痛点问题:

开发者在代码中大量调用相同的 DOM API, 处理繁琐 ,操作冗余,使得代码难以维护。大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。当 Model 频繁发生变化,开发者需要主动更新到View ;当用户的操作导致 Model 发生变化,开发者同样需要将变化的数据同步到Model 中,这样的工作不仅繁琐,而且很难维护复杂多变的数据状态。

其实,早期jquery 的出现就是为了前端能更简洁的操作DOM 而设计的,但它只解决了第一个问题,另外两个问题始终伴随着前端一直存在。随着智能手机,平板电脑的流行,多终端开始流行。

MVVM框架开始流行,应用场景:

针对具有复杂交互逻辑的前端应用提供基础的架构抽象提供ajax数据持久化,保证前端用户体验

框架数据分层情况:

M-Model:数据模型层,可以在Model中定义数据修改和操作的业务逻辑, Vue中数据层都放在data里面

V-View:视图层,它负责将数据模型转化成UI 展现出来

VM-ViewModel:是控制器,将视图和数据之间建立联系,或者说是一个同步视图和数据的对象,即Vue对象

三、Vue的使用

1、Vue环境搭建

引入Vue.js给HTML元素即视图添加id属性创建Vue对象,并搭建Vue环境

Vue差值表达式

// 导入vue

注意❗:

el 元素的挂载位置(值可以是 CSS 选择器或者 DOM 元素),但一般都使用CSS选择器data :模型数据(值必须是一个对象)

2、插值表达式

与template-web.js模板引擎绑定数据的语法类似,都是使用双大括号的文本插值{{ msg }}

Vue差值表达式

// 从data模型数据中获取

姓名:{{name}}

性别:{{sex}}

注意❗:

插值可以使用变量名的形式也可以使用表达式的形式,即对这个数据进行一些了的操作,假如这个数据是个数组,那么就可以在插值的地方进行数组的分割、合并……等操作在使用插值表达式后,会出现数据闪动的情况,不过可以使用指令的方式修复当然也可以不使用插值表达式的形式进行绑定数据,换成使用指令

3、指令(Directives)

1、什么是指令

指令是一个带有v-前缀的特殊属性,那么指令的本质就是自定义属性指令的格式,以v-开头指令可带参数,也可不带参数,比如v-cloak指令就属于无参数的指令

2、指令API

1、v-cloak 解决插值闪动

不需要表达式

用法:

​ 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

功能:解决插值表达式存在的闪动问题

原理:先隐藏,替换好值之后,再显示最终的值

示例:

Vue差值表达式

姓名:{{name}}

性别:{{sex}}

2、v-text 填充纯文本

预期:string

详细:

更新元素的 文本内容。如果要更新部分的 文本内容,则需要使用 {{ Mustache }} 插值表达式。

示例:

{{msg}}

注意:

相比插值表达式更加简洁指令用于将数据填充到标签中,作用与差值表达式类似,但是其直接没有闪动如果数据中有html标签则会将html标签一并输出此处为单向绑定,数据对象上的值改变,差值会改变,但是差值改变并不会影响数据对象的改变

3、v-html :填充HTML片段

预期:string

详细:

更新元素的 innerHTML。注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译,并且可识别HTML标签

注意:

存在安全问题,一般只在可信任内容上使用v-html。永远不要在用户提交的内容上使用和v-text区别在于v-text输出的是纯文本内容,而v-html会对内容进行html解析然后输出本网站内部数据可以使用,来自第三方的数据不可以用 示例:

4、v-pre 填充原始信息

不需要表达式

用法:

跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。

注意:

显示原始信息,跳过vue编译过程(分析编译过程)一些静态的内容不需要编译加这个指令可以加快渲染效果 示例:

{{ 这将不会被编译 }}

5、v-once 只编译一次

不需要表达式

详细:

只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

注意:

显示内容之后不再具有数据响应式功能什么是数据响应式?

HTML响应式即媒体查询,屏幕尺寸的变化导致样式的改变Vue数据响应式则是数据的变化导致页面内容的变化 示例:

这永远不会改变: {{msg}}

comment

{{msg}}

  • {{i}}

参考:

数据绑定语法- 插值组件 - 对低开销的静态组件使用 v-once

6、v-model 双向数据绑定

预期:随表单控件类型不同而不同。

限制:

5、计算属性(computed)

(1)为什么需要计算属性?

表达式的计算逻辑可能会比较复杂,使用计算属性可以使模板内容更加简洁

(2)计算属性的用法

// 定义计算属性的选项

computed: {

// reversedMessage计算属性的名称,那接下来这个属性就可以像data中的数据一样直接在模板中使用了

reversedMessage: function ()

// 对Vue内部数据进行一系列的操作

return this.msg.split ('').reverse().join('')

}

}

(3)计算属性与方法的区别

计算属性是基于它们的依赖进行缓存的方法不存在缓存

6、侦听器

(1)为什么需要侦听器?

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

(2)侦听器的使用

// 注意:firstName和lastName都是已经存在的属性

watch: {

firstName: function(val){

// val表示变化之后的值

this.fullName = val + this.lastName;

},

lastName: function(val) {

this.fullName = this.firstName + val;

}

}

案例:验证用户名是否存在

通过 v-model 实现数据绑定需要提供提示信息需要侦听器监听输入信息的变化触发后修改值

验证用户名是否存在

用户名:

// 如果不存在则使用这个

// 如果存在则使用这个

7、过滤器

(1)过滤器的作用是什么?

用户输入的一些数据它不符合我们数据的规则,影响视觉,那么就要格式化数据,比如:

将字符串格式化为首字母大写将日期格式化为指定的格式等

(2)自定义过滤器

全局过滤器

// 定义一个全局的过滤器,val就是模板中默认传来的参数

Vue.filter(('过滤器名称', function(val){

//过滤器业务逻辑

})

局部过滤器

// 定义局部的过滤器,val就是模板中默认传来的参数

filters: {

过滤器名称: function(val) {

//过滤器业务逻辑

}

}

带参数的过滤器

​ 一般情况下,过滤器都会默认自动带一个模板中传来的参数,但有些情况下我们需要再传递一个参数,比如传递一个转换格式字符串,这里以全局过滤器为例

// 定义一个全局的过滤器,val就是模板中默认传来的参数,而format是人为传递的参数

Vue.filter(('过滤器名称', function(val,format){

//过滤器业务逻辑

})

过滤器的使用

单个过滤器

{{msg | 过滤器名称}}

多个过滤器

{{msg | 过滤器名称 | 过滤器名称(参数)}}

案例:使用过滤器格式化日期

格式化日期

当前日期:{{time | formatDate("yyyy-MM-dd hh:mm:ss")}}

8、选项/生命周期钩子函数

所有的生命周期钩子自动绑定 this 上下文到实例中,因此你可以访问数据,对 property 和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())。这是因为箭头函数绑定了父上下文(由于this的指向),因此 this 与你期待的 Vue 实例不同,this.fetchTodos 的行为未定义。

生命周期图示:

(1)主要阶段

挂载(初始化相关属性) ①beforeCreate ②created ③beforeMount ④mounted更新(元素或组件的变更操作) ①beforeUpdate ②updated销毁(销毁相关属性) ①beforeDestroy ②destroyed

(2)Vue实例的产生过程

beforeCreate :在实例初始化之后,数据观测 (data observer) 和 event/watcher事件配置之前被调用。created :在实例创建完成后被立即调用。beforeMount :在挂载开始之前被调用。mounted:实例被挂载后调用。这时 el 被新创建的 vm.$el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。beforeUpdate: 数据更新时调用,发生在虚拟 DOM 打补丁之前。updated :由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。beforeDestroy: 实例销毁之前调用。destroyed :实例销毁后调用。

注意❗:

一般情况下我们最注重mounted钩子因为此时实例已经创建好了,但是数据还没渲染到模板中那么在mounted钩子函数中我们就可以从服务器请求页面数据

9、Vue响应式数据

在vue对象中直接修改data对象属性内的对象或数组的值无法触发响应式(只有数据发生了改变,而页面内容不会发生更改,也就是没有及时的渲染)。而变异方法,可以保持数组方法的原有功能不变的前提并且对其功能进行拓展或删减,或者使用Vue内置的数据响应式方法set。

(1)数组响应式数据

变异方法:修改原有数据

push():往数组中的最后面添加一个元素,成功返回当前数组的长度pop():删除数组的最后一个元素,成功返回删除元素的值shift():删除数组的第一个元素,成功返回删除元素的值unshift():往数组的最前面添加一个元素,成功返回当前数组的长度splice():有三个参数,第一个参数是想要删除的元素的下标(必填),第二个参数是想要删除的个数(必填),第三个参数是删除后想要在原位置替换的值(选填)sort():对数组按照字符编码默认从小到大进行排序,成功返回排序后的数组reverse():将数组进行反转,并返回反转之后的数组 非变异方法:不对原有数据操作,而生成新的数据

filter():高阶函数,用于将符合条件的元素返回,并生成一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素concat():用于将多个数组进行连接,不会改变现有数组slice():可以从已有的数组中返回选定的元素,该方法并不会修改数组,而是返回一个子数组

(2)Vue响应式数据

A、使用

全局数据响应

即在没有创建或使用Vue对象,但是导入了Vue.js时使用,所以称之为全局的响应式数据

Vue.set(target,propertyName/index, value);

局部数据响应

即在创建并使用了Vue对象时,在Vue方法中就可以使用该方法,我们称之为局部的响应式数据

vm.$set(target,propertyName/index, value);

//等价于

this.$set(target,propertyName/index, value);

B、说明

参数说明target要处理的数组或对象名称propertyName/index要处理的对象属性或数组索引value要处理的对象或数组的值

C、注意

Vue响应式数据方法可以实现数据的修改,并且还可以实现添加数据的功能。

D、案例

数组编译方法

  • {{item}}

10、组件

(1)组件是什么?

首先,让我们一起来看一个Vue组件的实例:

// 定义一个名为 button-counter 的新组件

Vue.component('button-counter', {

// 组件数据

data: function () {

return {

count: 0

}

},

// 模板,点击后count++

template: ''

})

组件是可复用的Vue实例,且带有一个名字:在这个例子中是,我们可以在一个通过new Vue创建的Vue根实例中,把这个组件作为自定义元素来使用:

// vue环境

// 自定义组件

因为组件是可复用的Vue实例,所以它们与new Vue接收相同的选项,例如:data、computed、watch、methods,以及生命周期钩子函数……等。仅有的例外是el这样跟实例特有的选项。

(2)组件的复用

你可以将组件进行任意多次的复用,因为它们之间的参数是相互独立的,但是内部参数相同。

注意:当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

(3)data 必须是一个函数

当我们定义这个 组件时,你可能会发现它的 data 并不是像new Vue中的data这样直接提供一个对象:

data: {

count: 0

}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {

return {

count: 0

}

}

如果 Vue 没有这条规则,那么点击一个按钮就可能会出现组件中的数据变化是同步的,因为它们用的是一个数据,而不是相互独立的存在。

(4)组件的组织和注册

通常一个应用会以一棵嵌套的组件树的形式来组织:

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。

全局组件:全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

Vue.component(

组件名称,

{

data:组件数据(以对象为返回值的函数),

template:组件模板内容

}

)

//例如 注册一个名为 button-counter 的新组件

Vue.component('button-counter', {

data: function () {

return {count: 0}

},

template: '

})

局部组件:相反,而局部组件只能在当前Vue实例中使用,所以大多数情况下我们注册的都是全局组件

// 因为组件内部本身就是以对象形式存在的,那么我们就可以这样定义一个局部的组件,但是必须在Vue环境中使用

var ComponentA = { /* ... */ }

var ComponentB = { /* ... */ }

var ComponentC = { /* ... */ }

// 当前Vue环境

new Vue({

el: '#app'

// 局部组件

components: {

// 使用局部组件

'component-a': ComponentA,

'component-b': ComponentB,

'component-c': ComponentC,

}

})

用法

// 使用组件

组件注册注意事项

data必须是一个函数

分析函数与普通对象的对比

组件模板内容必须是单个根元素

分析演示实际的效果

组件模板内容可以是模板字符串

模板字符串需要浏览器提供支持( ES6 语法)

组件命名方式

短横线方式

Vue.component('my-component', { /* ... */ })

驼峰方式

Vue.component('MyComponent ', { /* ... */})

​ 注意:不论使用哪种命名法则,在使用的时候只能使用my-component标签名称,所以我建议大家在使用的时候尽量以短横线的方式进行命名。

(5)组件通信

由于组件间存在数据相互依赖的关系,所以学习Vue组件通信是必须的。比如子组件要使用父组件从服务器获取的数据,并对这个值进行处理在自己的模板中使用。又或者子组件修改了父组件从服务器获取的数据,那么为了实现数据响应,就必须将数据传输给父组件,让父组件进行处理。又或者两个子组件之间相互依赖对方的数据,那又该怎么办?

1、父组件向子组件传值

分析:

首先父组件向子组件传递数据是通过子组件的props属性来实现的,那么也就是说子组件必须要有这个props属性。

// 定义一个menu-item组件

Vue.component('menu-item', {

// props属性用于接收父组件传递的数据,该属性的值为一个数字

props: [],

// 模板要使用父组件传递的数据title

template: '

{{ title }}
'

})

那么此时父组件通过自定义属性的方式将数据传递给子组件的props属性,那子组件的props属性中当然要有一个变量来接收

那么此时就可以在子组件的props属性中定义一个包含变量的数组,数组中的值必须为字符串类型。那么数组的元素就是父组件传递过来的值。

Vue.component('menu-item', {

// props数组属性中title变量就是父组件通过自定义属性传来的值title

props: ['title'],

// 模板中使用props中父组件传递的值title

template: '

{{ title }}
'

})

注意:

props中的变量名称不能和data中的名称相同props和data中的数据一样都具有缓存,并且在methods函数中一样可以使用this.属性名的方式来获取和使用,而在template模板中与data的使用方式一样,直接书写名称。props属性名规则

在 props 中使用驼峰形式,HTML模板中需要使用短横线的形式而在template选项的字符串形式的模板中没有这个限制

2、子组件向父组件传值

分析:

父组件并没有像子组件一样的props属性。而Vue规定可以使用自定义事件,那么我们就可以让父组件自定义一个事件,并监听它的触发。

那么此时子组件就要触发父组件创建并监听的事件,并将要传递的数据加上。

// 创建全局的组件

Vue.component('title-get', {

// 组件数据

data:function(){

return {

size: 0.1

}

},

// 字符串模板

template: `

`

});

当子组件触发了父组件监听的事件,那么父组件就可以获取到子组件传递的参数

注意:

当子组件在函数中触发父组件事件的时候,就需要使用this.$emit("自定义事件名称", 参数)的形式,即用this点出来子组件在传递参数时可以一次性传递多个参数,比如数组、对象……等,那么父组件就可以通过结构的方式拿到对应的数据又或者父组件可以在触发事件后挂载一个函数,将子组件传递的参数$event作为函数的参数传递,那么就可以在父组件的函数中对子组件传递的参数进行一系列的复杂操作

3、兄弟组件传值

分析:

要想实现兄弟组件间的通信,则必须要有事件中心,这个事件中心所起到的作用就是触发事件后通过事件中心传值

// 创建一个事件中心

let eventHub = new Vue();

在子组件创建后,但数据和模板还未进行渲染的情况下,使用事件中心的$on方法创建并监听自定义事件

// 2.在组件内部使用mounted钩子函数,因为mounted钩子函数就是在组件创建后,但数据和模板未进行渲染的情况下带调用的

mounted: function() {

// A组件监听自己的事件,val为触发组件传递的数据

eventHub.$on("a-event", (val) => {

})

},

当在某种情况下需要兄弟组件之间通讯时,使用事件中心的$emit()方法触发对方的事件,并将参数传递

// 事件函数

methods: {

// 定义handle函数

handle: function() {

// B组件触发兄弟组件的A的"a-event"事件,并将自己的参数传递

eventHub.$emit("b-event", this.title)

}

}

那么此时兄弟组件监听的事件就会得到数据,并对数据进行操作,如果下次不需要传值了,那么可以在此销毁事件

// 2.在组件内部使用mounted钩子函数,因为mounted钩子函数就是在组件创建后,但数据和模板未进行渲染的情况下带调用的

mounted: function() {

// A组件监听自己的事件,val为触发组件传递的数据

eventHub.$on("a-event", (val) => {

// 将接收的参数赋值给当前组件的属性

this.content = val;

// 销毁自定义事件

eventHub.$off("a-event");

})

},

案例:

兄弟组件之间传递参数

11、插槽

(1)插槽是什么?

插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。

有些时候,我们需要父组件向子组件中填充一些数据,那么假如子组件没有使用插槽,父组件如果需要往子组件中填充模板或者html代码块, 是没办法做到的。

(2)插槽的基本使用

子组件插槽占位符

// 全局的子组件alert-box

Vue.component('alert-box', {

template: `

Error!

`

})

父组件向插槽中填充内容

Something bad happened.

(3)具名插槽

描述

​ 具名插槽其实就是给插槽取个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中。一个不带 name 的 出口会带有隐含的名字“default”。

使用

子组件的模板

给slot标签一个name属性,那么它就是具名插槽,而不带name属性的插槽则为默认插槽

父组件传值

在给具名插槽传值的时候必须使用template标签,并使用v-slot指令冒号后为插槽名称。而默认插槽则以默认的方式传递,或者以defaulte为name值传递。

A paragraph for the main content.

And another one.

注意

父级的填充内容如果指定到子组件的没有对应名字插槽,那么该内容不会被填充到默认插槽中。

如果子组件没有默认插槽,而父级的填充内容指定到默认插槽中,那么该内容就“不会”填充到子组件的任何一个插槽中。

如果子组件有多个默认插槽,而父组件所有指定到默认插槽的填充内容,将“会” “全都”填充到子组件的每个默认插槽中。

(4)作用域插槽

描述

​ 作用域插槽其实就是带数据的插槽,即带参数的插槽,简单的来说就是子组件提供给父组件的参数,该参数仅限于插槽中使用,父组件可根据子组件传过来的插槽数据来进行不同的方式展现和填充插槽内容。

使用

子组件模板

给插槽上绑定一个属性(包含数据),使得父组件可以获取到。绑定在 元素上的 属性 被称为插槽 prop。

  • {{item.name}}

父组件传值

现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:

使用场景

​ 如果子组件中的某一部分的数据,每个父组件都会有自己的一套对该数据的不同的呈现方式,这时就需要用到作用域插槽。

​ 也就是说,要从外部控制模板插槽对应数据的样式的话,就用作用域插槽。

四、Vue Router(路由)

1、什么是路由?

路由是一个比较广义和抽象的概念,路由的本质就是对应关系。而在开发中,路由分为:后端路由 和 前端路由。

(1)后端路由

概念:根据不同的用户 URL 请求,返回不同的内容

本质:URL 请求地址与服务器资源之间的对应关系

(2)前端路由

概念:根据不同的用户事件,显示不同的页面内容

本质:用户事件与事件处理函数之间的对应关系

2、SPA

(1)前后端渲染的缺点

后端渲染:存在性能问题,并且全部代码由服务器生成,浏览器只负责渲染前端渲染:前端通过Ajax技术从服务器拿到数据,并使用模板引擎将数据填充到页面结构中,最终交由浏览器渲染,并且支持页面的局部刷新。前端渲染提高性能,但是不支持浏览器的前进后退操作。

(2)什么是SPA

SPA(Single Page Application)单页面应用程序:整个网站只有一个页面,内容的变化通过Ajax局部更新实现、同时支持浏览器地址栏的前进和后退操作,并且地址栏会更新url,通过这个url就可以来到这个页面,还会刷新局部的内容。

人话:意思就是说前端渲染实现的菜单栏点击后不会局部更新url地址,同时也不会拥有前进和后退的功能,那么前端路由就是为了解决这个问题来诞生的。

(3)实现简易的前端路由

需求:基于UR的hash实现菜单栏(点击菜单的时候改变URL的hash,根据hash的变化控制组件的切换)

原生js实现简易前端路由

3、Vue Router

(1)简介

Vue Router(官网:https://router.vuejs.org/zh/)是 Vue.js 官方的路由管理器。 它和 Vue.js 的核心深度集成,可以非常方便的用于SPA应用程序的开发,可以说Vue Router让构建单页面应用变得易如反掌。

Vue Router 包含的功能有:

支持HTML5 历史模式或 hash 模式支持嵌套路由支持路由参数支持编程式路由支持命名路由

(2)基本使用

引入相关的库文件

添加路由链接

User

Register

添加路由填充位

定义路由组件

var User = { template: '

User
' }

var Register = { template: '

Register
' }

配置路由规则并创建路由实例

// 创建路由实例对象

var router = new VueRouter({

// routes 是路由规则数组

routes: [

// 每个路由规则都是一个配置对象,其中至少包含path和component 两个属性:

// path 表示当前路由规则匹配的 hash 地址

// component 表示当前路由规则对应要展示的组件

{path:'/user',component: User},

{path:'/register',component: Register}

]

})

把路由挂载到 Vue 根实例中

new Vue({

el: '#app',

// 为了能够让路由规则生效,必须把路由对象挂载到 vue 实例对象上

router

});

使用 Vue Router 实现简易的菜单:

Vue路由的基本使用

  • 我的

  • 你的

  • 你们的

(3)路由重定向

路由重定向指:

用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向就比如访问主页的时候就让他跳转到主页所要展示的内容上,而不是说点击超链接以后才修改路由

使用:

// 路由对象

var router = new VueRouter({

routes: [

// 其中,path 表示需要被重定向的原地址,redirect 表示将要被重定向到的新地址

// 也就是说访问/地址的时候,让其强制跳转到/user地址所对应的路由上

{path:'/', redirect: '/user'},

{path:'/user',component: User},

{path:'/register',component: Register}

]

})

(4)嵌套路由

1、简介:

嵌套路由是指:

点击父级路由链接显示模板内容模板内容中又有子级路由链接点击子级路由链接显示子级模板内容

2、实现:

父级路由组件模板

父级路由链接

父组件路由填充位

User

Register

子级路由组件模板

子级路由链接:在父级路由组件的模板中定义

子级路由填充位:在父级路由组件的模板中定义

const Register = {

template: `

Register 组件


Tab1

Tab2

`

}

嵌套路由配置

父级路由通过children属性配置子级路由在路由对象的父级路由中配置子级路由 const router = new VueRouter({

routes: [

{ path: '/user', component: User },

{ path: '/register', component: Register,

// 通过 children 属性,为 /register 添加子路由规则

children: [ {

path: '/register/tab1', component: Tab1 },

{ path: '/register/tab2', component: Tab2 }

]}

]

})

(5)动态路由匹配

1、基本用法

通过动态路由参数的模式进行路由匹配,与后端路由类似,就是给路由传递参数,可以是查询参数,也可以是params参数

var router = new VueRouter({

routes: [

// 动态路径参数 以冒号开头

{ path: '/user/:id', component: User }

]

})

路由组件模板中通过$route.params获取路由参数

const User = {

// 路由组件中通过$route.params获取路由参数

template: '

User {{ $route.params.id }}
'

}

2、路由组件传递参数

$route与对应路由形成高度耦合,不够灵活,所以可以使用props将组件和路由解耦

props的值为布尔类型

const router = new VueRouter({

routes: [

// 如果props被设置为true,那么route.params 将会被设置为组件属性

{ path: '/user/:id', component: User, props: true }

]

})

const User = {

props: ['id'],

// 使用 props 接收路由参数

template: '

用户ID:{{ id }}
' // 使用路由参数

}

props的值为对象类型

const router = new VueRouter({

routes: [{

path: '/user/:id',

component: User,

// 如果 props 是一个对象,它会被按原样设置为组件属性

props: { uname: 'lisi', age: 12 }

}]

})

const User = {

props: ['uname', 'age'],

template: `

用户信息:{{ uname + '---' + age}}
'

}

props的值为函数类型

const router = new VueRouter({

routes: [{

path: '/user/:id',

component: User,

// 如果 props 是一个函数,则这个函数接收 route 对象为自己的形参

props: route => ({

uname: 'zs',

age: 20,

id: route.params.id

})

}]

});

const User = {

props: ['uname', 'age', 'id'],

template: `

用户信息:{{ uname + '---' + age + '---' +'---'id}}
'

}

3、vue-router命名路由

简介:为了更加方便的表示路由的路径,可以给路由规则起一个别名,即为“命名路由”。

在路由对象配置中可以使用name属性给所在的路由起一个名字

const router = new VueRouter({

routes: [{

path: '/user/:id',

// 给所在路由起名字,即为命名式路由

name: 'user',

component: User

}]

})

在路由链接中就可以使用直接使用对象的形式来获取name所代表路由,并传递参数

User

4、编程式导航

(1)页面导航的两种方式

声明式导航:通过点击链接实现导航的方式,叫做声明式导航

例如:普通网页中的 < a> 链接 或 vue 中的 < router-link>< /router-link>

编程式导航:通过调用JavaScript形式的API实现导航的方式,叫做编程式导航

例如:普通网页中的 location.href

(2)常用的编程式导航 API 如下:

this.$router.push('hash地址')this.$router.go(n)

(3)编程式导航参数规则

// 字符串(路径名称)

router.push('/home');

// 对象:命名路由

router.push({ path: '/home' })

// 命名的路由(传递参数)

router.push({ name: '/user', params: { userId: 123 }})

// 带查询参数,变成 /register?uname=lisi

router.push({ path: '/register', query: { uname: 'lisi' }})

五、Vue CLI (脚手架)

1、简介

vue脚手架指的是vue-cli,它是一个专门为单页面应用快速搭建繁杂的脚手架,它可以轻松的创建新的应用程序而且可用于自动生成vue和webpack的项目模板。

2、安装

安装 3.x 版本的 Vue 脚手架:

npm install -g @vue/cli

3、基于3.x版本的脚手架创建vue项目

基于交互式命令行的方式,创建 新版 vue 项目

vue create my-project

基于 图形化界面 的方式,创建 新版 vue 项目

vue ui

基于 2.x 的旧模板,创建 旧版 vue 项目

npm install -g @vue/cli-init

vue init webpack my-project

4、Vue 脚手架生成的项目结构分析

5、Vue 脚手架的自定义配置

通过 package.json 配置项目

// 必须是符合规范的json语法

"vue": {

"devServer": {

"port": "8888",

"open" : true

}

},

注意:不推荐使用这种配置方式。因为 package.json 主要用来管理包的配置信息;为了方便维护,推荐将 vue 脚手架相关的配置,单独定义到 vue.config.js 配置文件中。

通过单独的配置文件配置项目

在项目的跟目录创建文件 vue.config.js

在该文件中进行相关配置,从而覆盖默认配置

// vue.config.js

module.exports = {

devServer: {

port: 8888

}

}

六、Vux 集中状态管理

1、回顾Vue组件通信

父向子传值:v-bind 属性绑定子向父传值:v-on 自定义事件绑定兄弟组件之间共享数据: EventBus(事件中心)

$on 接收数据的那个组件$emit 发送数据的那个组件

分析:试想一下,如果我们在开发中使用这种方法来共享数据未免有点太麻烦了,是不是影响我们的开发效率,而且代码不容易看懂

2、Vuex 是什么

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

人话:Vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。图示如下:

分析:

左图:使用Vue组件通信传递数据,我们发现杂乱无章,不好看懂,数据究竟在哪里?右图:使用Vuex集中管理数据,我们可以很清楚的看到数据是从STORE(状态)集中管理获取的,当前也可以修改,修改后又返回STORE(状态)中

3、什么样的数据适合存储到Vuex中?

一般情况下,只有组件之间共享的数据,才有必要存储到 vuex 中而对于组件中的私有数据,依旧存储在组件自身的 data 中即可比如几个组件的数据之间都互相依赖,那么就可以把这些依赖的数据提取出来统一放到Vuex中进行保管

4、Vuex 的基本使用

安装 vuex 依赖包

npm install vuex --save

导入 vuex 包

import Vuex from 'vuex'

Vue.use(Vuex)

创建 store 对象

const store = new Vuex.Store({

// state 中存放的就是全局共享的数据

state: { count: 0 }

})

将 store 对象挂载到 vue 实例中

new Vue({

el: '#app',

render: h => h(app),

router,

// 将创建的共享数据对象,挂载到 Vue 实例中

// 所有的组件,就可以直接从 store 中获取全局的数据了

store

})

注意:当然,你也可以使用脚手架使用图形化界面来创建项目,并选择安装Vuex,那么你就不需要配置这些东西,直接使用即可

5、Vuex 的核心概念

(1)State(状态)

State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储。

// 创建store数据源,提供唯一公共数据

const store = new Vuex.Store({

state: { count: 0 }

})

组件访问 State 中的数据

方法1:this.$store.state.全局数据名称;

方法2:

按需导入

// 1. 从 vuex 中按需导入 mapState 函数

import { mapState } from 'vuex'

通过刚才导入的 mapState 函数,将当前组件需要的全局数据,映射为当前组件的 computed 计算属性,那么就可以直接使用

// 2. 将全局数据,映射为当前组件的计算属性

computed: {

...mapState(['count'])

}

(2)Mutation(变化)

1、简介

作用:Mutation 用于变更 Store中 的数据。原因:

只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化

2、使用:

在 mutations 属性中 定义修改状态的方法

const store = new Vuex.Store({

state: {

count: 0

},

mutations: {

// 更改state状态的方法,当然也可以传递其他的参数

add(state,...) {

// 变更状态

state.count++

}

}

})

注意:方法中第一个参数必须是state,因为要对其中的内容进行修改注意:当然也可以带有其他的由调用时传递的参数

3、在组件中调用mutations中的方法

方法1:this.$store.commit('addN', 参数1...)

// 触发mutation

methods: {

handle2() {

// 在调用 commit 函数,

// 触发 mutations 时携带参数

this.$store.commit('addN', 3)

}

}

方法2:按需导入

从 vuex 中按需导入 mapMutations 函数

import { mapMutations } from 'vuex'

通过刚才导入的 mapMutations 函数,将需要的 mutations 函数,映射为当前组件的 methods 方法

methods: {

...mapMutations(['add', 'addN'])

}

这样你就可以使用this.方法名的方式去调用它了

(3)Action

1、简介

Action 用于处理异步任务,比如计时器……等。如果通过异步操作变更数据,必须通过 Action,而不能使用 Mutation,但是在 Action 中还是要通过触发Mutation 的方式间接变更数据。

人话:意思就是说 Action 用于处理 Mutation 中带有异步事件的方法,或者是如果你想在 Mutation 中定义带有异步事件的方法,那么你就要在Mutation中定义同步方法,然后在Action中使用异步事件调用即可。

2、使用

在 mutations 属性中 定义修改状态的同步方法

// 定义 Action

const store = new Vuex.Store({

// ...省略其他代码

mutations: {

add(state) {

state.count++

}

}

})

在 Action 中定义一个方法,使用异步事件调用 mutations 中的同步方法

// 定义 Action

const store = new Vuex.Store({

// ...省略其他代码

mutations: {

add(state) {

state.count++

}

},

actions: {

addAsync(context) {

setTimeout(() => {

context.commit('add')

}, 1000)

}

}

})

注意:方法中第一个参数必须是context,因为要通过它调用mutations中定义的方法注意:所有的异步操作必须出现在Action中,而不是mutations注意:当然,它也可以传递参数,与mutations中大同小异

3、组件中调用

方法1:this.$store.dispatch('addAsync')

方法2:按需导入

从 vuex 中按需导入 mapActions 函数

import { mapActions } from 'vuex';

通过刚才导入的 mapActions 函数,将需要的 actions 函数,映射为当前组件的 methods 方法

methods: {

...mapActions(['addASync', 'addNASync'])

}

(4)Getter

1、简介

Getter 用于对 Store 中的数据进行加工处理形成新的数据。

注意:

Getter 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性。Store 中数据发生变化,Getter 的数据也会跟着变化。

2、使用

注意:因为是要对state中的数据进行加工,那么第一个参数必须是state

const store = new Vuex.Store({

state: {

count: 0

},

getters: {

// 创建一个方法,对state中的数据进行加工,并返回一个加工后的数据

showNum: state => {

return '当前最新的数量是【'+ state.count +'】'

}

}

})

3、在组件中使用

在插值表达式中使用:this.$store.getters.名称的方式直接调用

按需导入

从 vuex 中按需导入 mapGetters 函数

import { mapGetters } from 'vuex'

通过刚才导入的 mapGetters 函数,将当前组件需要的全局数据,映射为当前组件的 computed 计算属性

computed: {

...mapGetters(['showNum'])

}

接下来直接使用计算属性的方式调用即可

七、Element-UI

Element-UI:一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。

官网地址:https://element.eleme.cn/#/zh-CN

安装:

基于命令行方式手动安装

安装依赖包

npm i element-ui –S

②导入 Element-UI 相关资源

// 导入组件相关样式

import 'element-ui/lib/theme-chalk/index.css';

// 配置 Vue 插件

Vue.use(ElementUI);

基于图形化界面自动安装

运行 vue ui 命令,打开图形化界面通过 Vue 项目管理器,进入具体的项目配置面板点击 插件 -> 添加插件,进入插件查询面板搜索 vue-cli-plugin-element 并安装配置插件,实现按需导入,从而减少打包后项目的体积

八、经典案例

1、图书管理系统

需求:使用Vue指令、计算属性、侦听器、过滤器……等实现简单的图书管理(增删改)功能,并使用文件操作(读写)来存储数据和获取数据。

步骤:

过滤器(格式化日期)自定义指令(获取表单焦点)计算属性(统计图书数量)侦听器(验证图书存在性)生命周期(图书数据处理)

效果展示:

源码奉上:

前台代码

图书列表

图书管理列表

编号

书名

图书总数:{{total}}

编号 书名 时间 操作
{{item.id}} {{item.name}} {{item.date | format("yyyy-mm-dd hh:mm:ss")}}

删除

|

修改

服务器代码

// 导入express模块

const express = require("express");

// 导入cors模块

const cors = require("cors");

// 导入路由模块

const router = require("./route/actionRoute");

// 创建服务器

const app = express();

// 使用一个自定义中间件

app.use(function(req, res, next) {

// 对send函数进行封装

res.dispatch = function(error, status = 1) {

// 返回数据

res.send({

status: status,

message: (error instanceof Error) ? error.message : error

});

};

next();

});

// 使用跨域请求

app.use(cors());

// 可解析json数据

app.use(express.json());

// 配置可解析表单数据的参数

app.use(express.urlencoded({ extended: false }));

// 使用路由模块

app.use("/api", router);

oldApi();

// 监听并开启服务器

app.listen(8024, "127.0.0.1", function() {

console.log("127.0.0.1:8024 服务器开启成功!");

});

路由接口代码

// 导入express模块

const express = require("express");

// 导入fs模块

const fs = require("fs");

// 导入path模块

const path = require("path");

// 创建路由对象

const router = express.Router();

// 文件路径

let filePath = path.join(__dirname, "../db/data.txt");

// 获取数据模块

router.get("/getData", function(req, res) {

// 调用读取文件的函数,并以响应数据作为参数进行传递

fs.readFile(filePath, { encoding: "utf8" }, (err, val) => {

// 如果err不为空,则文件读取异常

if (err) return res.dispatch(err);

res.send({

status: 0,

message: JSON.parse(val)

});

});

});

// 添加数据

router.post("/addData", function(req, res) {

// 获取传进来的数据

let ele = req.body.ele;

// 读数据

fs.readFile(filePath, { encoding: "utf8" }, (err, val) => {

// 如果err不为空,则文件读取异常

if (err) return res.dispatch(err);

// 将获取的数据转化为数组

let oldData = JSON.parse(val);

// 向数组的末尾添加数据(对象)

oldData.push(ele);

// 对数组中的元素根据id进行排序

sortByArray(oldData);

// 将添加后的数组转化为json字符串

let newData = JSON.stringify(oldData);

// 开始文件写入

write(res, newData, "添加成功!");

});

});

// 修改数据模块

router.put("/upData", function(req, res) {

// 通过解构,获取值

let {

index,

data

} = req.body;

// 文件读取

fs.readFile(filePath, { encoding: "utf8" }, (err, val) => {

// 如果err不为空,则文件读取异常

if (err) return res.dispatch(err);

// 将获取的数据转化为数组

let oldData = JSON.parse(val);

// 向数组中添加数据

oldData.splice(index, 1, data);

// 将删除后的数组转化为json字符串

let newData = JSON.stringify(oldData);

// 开始文件写入

write(res, newData, "修改成功!");

});

});

// 删除数据的路由接口

router.delete("/delData", function(req, res) {

//获取要删除的索引

let index = req.query.index;

// 首先进行文件读取

fs.readFile(filePath, { encoding: "utf8" }, (err, val) => {

// 如果err不为空,则文件读取异常

if (err) return res.dispatch(err);

// 将获取的数据转化为数组

let oldData = JSON.parse(val);

// 删除数组中指定下标的元素

oldData.splice(index, 1);

// 对数组中的元素根据id进行排序

sortByArray(oldData);

// 将删除后的数组转化为json字符串

let newData = JSON.stringify(oldData);

// 开始文件写入

write(res, newData, "删除成功!");

});

});

// 对数组排序,默认为升序:小=》大,第一个参数为数组

function sortByArray(array, model = 0) {

// 小=》大

if (model === 0) {

array.sort((a, b) => {

return a.id - b.id;

});

// 大=》小

} else {

array.sort((a, b) => {

return b.id - a.id;

});

}

}

// 写文件的函数:参数(res:响应对象,data:写入的数据,message:提示信息)

function write(res, data, message) {

fs.writeFile(filePath, data, { encoding: "utf8" }, err => {

// 如果err不为空,则文件读取异常

if (err) return res.dispatch(err);

// 成功后,响应数据

return res.send({

status: 0,

message: message,

data: JSON.parse(data)

});

});

}

// 共享路由对象

module.exports = router;

2、购物车

需求:使用Vue组件化的开发思想实现简单的购物车案例,包含获取商品列表、增加/减少商品的数量,并计算总价、删除商品后总价扣除……等功能

分析:

标题组件(父子组件传值)商品列表组件(父子传值+子父传值)包含有删除数据的功能商品总价+数量(兄弟组件传值)

效果展示:

源码奉上:

购物车案例

3、ToDoList待办事项列表

需求:使用Vue组件化的开发思想、指令……等知识,实现ToDoList案例,包含增加待办、删除待办、修改待办、获取待办以及更改待办状态……等功能。

分析:

搜索框组件正在进行标题和总数组件正在进行列表组件已完成标题和总数组件已完成列表组件删除全部待办的组件

注意:

修改事项时点击p标签,则input文本框出现,并将原有数据显示修改完成,点击Vue父组件,则使用ref直接修改子组件的数据最后将数据保存到父组件中,并将p标签显示,input文本框消失

ref的使用:

ref属性用于父组件访问子组件的实例和数据你可以给子组件上挂载一个ref="名称"的属性那么你在父组件的函数中就可以使用this.$refs.名称的方式将带有ref="名称"属性并且名称一致的的子组件中的实例数据拿到,拿到的是一个对象如果一个页面中有多个this.$refs.名称,那么名称必须不能相同,所以是唯一的如果this.$refs.名称和v-for指令一起使用,那么从父组件拿到的子组件数据将是一个对象数组,因为你的这个组件使用了for遍历,那么也就有多个ref名称相同,所以返回的就为数组那么如果你要在数组中找对应的数据,那么你就要拿索引来操作了

效果展示:

源码奉上:

html和Vue操作

ToDoList

css样式

* {

margin: 0;

padding: 0;

box-sizing: border-box;

}

/* 当设备宽度大于750px时 */

@media screen and (min-width:750px) {

/* html采用75px的字体大小 */

html {

font-size: 75px !important;

}

}

body {

background: #CDCDCD;

}

header {

height: .666667rem;

background: rgba(47, 47, 47, 0.98);

}

header form {

width: 10rem;

margin: 0 auto;

display: flex;

align-items: center;

}

header label {

flex: 1;

width: 1.333333rem;

height: .666667rem;

line-height: .666667rem;

color: #ffffff;

font-size: .32rem;

cursor: pointer;

}

header input {

flex: 1;

width: 4.826667rem;

height: .346667rem;

padding-left: .2rem;

border: none;

border-radius: .066667rem;

box-shadow: 0 1px rgba(255, 255, 255, 0.24), 0 1px 6px rgba(0, 0, 0, 0.45) inset;

font-size: .16rem;

margin-right: 10px;

/* 点击表单后,表单边框消失 */

outline: none;

}

section,

footer {

min-width: 320px;

max-width: 750px;

width: 10rem;

margin: 0 auto;

font-family: Arial, Helvetica;

background: #CDCDCD;

}

section h2 {

height: .413333rem;

margin: .266667rem 0;

font-size: .333333rem;

}

section span {

float: right;

width: .266667rem;

height: .266667rem;

line-height: .266667rem;

text-align: center;

border-radius: 50%;

background: #E6E6FA;

font-size: .186667rem;

color: #666666;

margin-right: .133333rem;

}

ul,

ol {

list-style: none;

}

footer {

color: #666666;

font-size: .186667rem;

text-align: center;

}

footer a {

text-decoration: none;

color: #999999;

}

.li {

height: .426667rem;

background: #ffffff;

margin-bottom: .133333rem;

border-radius: .04rem;

border-left: .066667rem solid #629A9C;

box-shadow: 0 .013333rem .026667rem rgba(0, 0, 0, 0.07);

/* 弹性布局 */

display: flex;

/* 侧轴居中 */

align-items: center;

/* 两边贴边,再平分剩余空间 */

justify-content: space-around;

/* 相对定位 */

position: relative;

cursor: pointer;

}

.li input {

position: absolute;

width: .293333rem;

height: .293333rem;

left: .2rem;

}

.li p {

display: inline-block;

width: 85%;

height: 100%;

line-height: .426667rem;

font-size: .186667rem;

}

.li input[type=text] {

display: inline-block;

width: 80%;

height: 75%;

margin-left: 38px;

padding-left: 5px;

line-height: .426667rem;

font-size: .186667rem;

}

.li a {

font-size: 0.2rem;

position: absolute;

width: .346667rem;

height: .32rem;

line-height: .14rem;

text-align: center;

right: .106667rem;

border-radius: 50%;

border: .08rem double #ffffff;

background: #cccccc;

color: #ffffff;

}

.finish {

opacity: 0.5;

border-left: .066667rem solid #999999;

}

4、简易计算器

1、需求:使用Vue单文件组件、Vuex、Vue脚手架……等知识完成一个可以二目运算的简单计算器

2、需求大体角度分析:

使用Vue脚手架创建项目不需要Router,但是需要Vuex选择单独的文件配置项目配置需要使用组件,并且不止一个

3、组件和Vuex角度分析:

组件强调复用性,所以组件可以是三个:输入框组件、运算符组件、按钮和结果组件(一个)Vuex的状态有:数字1、数字2、运算符号、结果那么只要组件中的值变化了就要修改状态,所有需要watch侦听器来实时监测,并调用Vuex中的方法按钮和结果组件中:按钮需要点击事件,点击以后要调用Vuex中的方法,根据不同的运算符进行相应的运算,并将结果赋值到结果状态中结果的值直接绑定到结果组件的地方由于不需要异步操作和对结果进行加工,那么Vuex中就不需要Action、Getter

4、效果展示

5、源代码

Vuex中的数据

import Vue from 'vue';

import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({

state: { one: 0, two: 0, flag: 0, result: 0 },

mutations: {

// 获取文本框中的值,并将其赋值给公共数据

getValue(state, arr) {

// isFirstNum用来判断是否是第一个数字

if (arr[0]) {

state.one = arr[1];

} else {

state.two = arr[1];

}

},

// 获取下拉菜单中的运算符号,并将其赋值给公共数据

getFlag(state, symbol) {

state.flag = symbol;

},

// 计算运算结果,并将其赋值给公共数据

computation(state) {

// 判断操作符的数字

switch (Number(state.flag)) {

// +

case 0:

state.result = state.one + state.two;

break;

// -

case 1:

state.result = state.one - state.two;

break;

// *

case 2:

state.result = state.one * state.two;

break;

// ÷

case 3:

state.result = state.one / state.two;

break;

// %

case 4:

state.result = state.one % state.two;

}

}

},

actions: {},

modules: {}

});

输入框组件中的数据

运算符组件中的数据

按钮以及结果组件中的数据

九、总结

以上就是Vue框架的全部内容,当然还有很多内容没有延申拓展,因为本篇文章只是说让大家对Vue有一个基本的认识,方便初学者打好基础,那么在接下来的前端生涯中少走弯路。

当然如果本篇文章哪里讲的有不妥的地方,还请各位踊跃发言。那么下次博主将给大家带有React框架的全部内容,希望大家多多支持。

02世界杯韩国队球员现状:多人曾来华执教
方舟生存进化迷失岛水獭分布位置分享 迷失岛水獭在哪
Top