DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/jquery-1.11.3.js">script>
head>
<body>
<button id="btnMinus">-button>
<span>0span>
<button id="btnAdd">+button>
<script>
//先实现+
//DOM 4步
//1. 查找触发事件的元素
$("#btnAdd")
//2. 绑定事件处理函数
.click(function(){
//3. 查找要修改的
var $span=$(this).prev();
//4. 修改元素
//4.1 先取出span中现在的数量,转为整数
var n=parseInt($span.html());
//4.2 将n+1
n++
//4.3 将新的n再放回去:
$span.html(n);
})
//再实现-
//DOM 4步
//1. 查找触发事件的元素
$("#btnMinus")
//2. 绑定事件处理函数
.click(function(){
//3. 查找要修改的
var $span=$(this).next();
//4. 修改元素
//4.1 先取出span中现在的数量,转为整数
var n=parseInt($span.html());
//4.2 如果n>0,才能n-1
if(n>0){
n--
}
//4.3 将新的n再放回去:
$span.html(n);
})
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<button @click="minus">-button>
<span>{{n}}span>
<button @click="add">+button>
div>
<script>
//2. 创建一个new Vue()对象,来监控div所包含的区域。
var vm=new Vue({
//vue对象中,必须用el属性,告诉new Vue()要监控的区域是哪里: (el其实是element的缩写)
// id选择器
el:"#app",
//3. 定义模型对象,来保存界面中所需的所有变量和事件处理函数
//什么是模型对象: 就是专门替界面保存变量和事件处理函数的特殊的对象
//3.1 先创建一个data:{}来保存界面中所需的所有变量和初始值
//本例中: 因为界面上只需要一个变量n,所以data中才需要1个变量n
data:{
n:0
},
//3.2 再创建一个methods:{}来保存界面中所需的所有事件处理函数
//本例中: 因为界面中需要2个事件处理函数,所以methods中应该定义两个函数
methods:{
add(){
this.n++;
},
minus(){
if(this.n>0){
this.n--;
}
}
}
//强调:
//3.2.1 methods中的事件处理函数中,如果要操作data中的变量,必须加this.
//3.2.2 methods中的事件处理函数中,根本不用考虑如何从界面取值,也不用考虑如何将新值放回界面,只需要专门考虑如何把data中的变量值修改正确即可!
//new Vue()会自动保持界面中变量n和data中变量n同步:
//开局时,data中n是几,new Vue()就把n的值送到页面上,对应位置显示给人看
//当methods中修改了n的值,new Vue()会自动把n的新值自动更新到界面中n所在的位置给人看
});
console.log(vm);
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3>Welcome {{uname}}h3>
<h3>性别:{{sex==1?"男":"女"}}h3>
<h3>小计:¥{{(price*count).toFixed(2)}}h3>
<h3>下单时间: {{new Date(orderTime).toLocaleString()}}h3>
<h3>今天星期{{week[day]}}h3>
div>
<script>
new Vue({
el:"#app",
data:{
uname:"dingding",
sex:1,
price:12.5,
count:5,
orderTime:1614158191101,
week:["日","一","二","三","四","五","六"],
day:new Date().getDay()
}
})
script>
body>
html>
a. 什么是: 专门用来标记属性值变化的特殊指令
b. 如何:
1). 标准: <元素 v-bind:属性名=“变量或js表达式”>
html地盘 | js的地盘 |
2). 简写: <元素 :属性名=“变量或js表达式”>
c. 原理: new Vue()只要扫描到":",就会先执行=右侧"“中的js变量或表达式。然后用变量值或js表达式的运行结果,作为当前属性的属性值!
d. 强调:
1). 一旦属性上加了:,则不要再加{{}}了!”"就起到了{{}}的作用。
2). ""之内变成了js的语法天下!必须遵守js的语法!
e. 示例: 根据pm25数值,显示不同的图片
4_v-bind.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>根据程序中PM25的数值显示不同的表情title>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<img :src="pm25<100?'img/1.png':pm25<200?'img/2.png':pm25<300?'img/3.png':'img/4.png'">
<h3>{{pm25<100?'img/1.png':pm25<200?'img/2.png':pm25<300?'img/3.png':'img/4.png'}}h3>
div>
<script>
var vm=new Vue({
el:"#app",
data:{
//程序中只保存pm2.5的数值,不保存图片路径
pm25:160
}
})
//运行后,F12打开控制台,console,输入vm.pm25=数值,观察页面上图片的变化.
script>
body>
html>
a. 什么是: 专门根据条件控制一个元素的显示隐藏
b. 如何: <元素 v-show=“变量或js表达式”>
c. 原理: new Vue()只要扫描到v-show,就会先执行=右边""中的变量或js表达式:
1). 如果=右边的变量或js表达式结果为true,则当前元素正常显示
2). 如果=右边的变量或js表达式结果为false,则当前元素隐藏(new Vue()自动将v-show替换为display:none)!
d. 示例: 点按钮弹出框
5_v-show.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
#pop{
position:fixed;
top:50%;
left:50%;
width:300px;
height:100px;
margin-left:-150px;
margin-top:-50px;
background-color:lightBlue
}
#pop>a{
float:right;
margin-right:20px;
text-decoration:none;
}
style>
head>
<body>
<div id="app">
<button @click="show">click mebutton>
<div v-show="visible" id="pop">
<a href="javascript:;" @click="hide">×a>
div>
div>
<script>
//2. 创建new Vue()对象,监控id为app的区域
new Vue({
el:"#app",
//3. 创建模型对象:
//3.1 创建data对象
//本例中: 只用到一个变量visible,所以
data:{
visible:false, //专门用来保存弹出框的显示隐藏状态。开局,因为弹出框隐藏,所以变量值为false。
},
//3.2 创建methods对象
//本例中: 因为界面上需要2个事件处理函数,所以,methods中也应该保存2个事件处理函数
methods:{
show(){
this.visible=true;
},
hide(){
this.visible=false;
}
}
})
script>
body>
html>
a. 什么是: 专门根据条件在两个元素之间二选一显示隐藏
b. 如何:
<元素1 v-if=“变量或js表达式”>
<元素2 v-else>
强调: v-if和v-else两个元素之间必须紧挨着写,不能插入任何其他元素.
c. 原理: new Vue()扫描到v-if时,总是先执行=右边""中的变量和js表达式:
1). 如果v-if=右边的变量或js表达式返回true,则显示v-if所在的元素,而删除v-else所在元素
2). 如果v-if=右边的变量或js表达式返回false,则删除v-if所在的元素,而保存v-else所在的元素
d. 问题:为什么已经删除的元素,后来还能回到页面上
原理: 每次删除的指示真实DOM树中的元素对象。而虚拟DOM树中始终同时保存着v-if和v-else两个元素对象。每次变量发生变化时,并不是直接扫描真实DOM树,而是扫描虚拟DOM树中两个元素对象,决定这次派谁上场!
e. v-if和v-show的差别:
1). v-show: 用display:none隐藏元素
2). v-if: 用删除元素方式来达到隐藏的目的
f. 示例: 切换登录状态:
6_v-if_v-else.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<div v-if="isLogin">
<h3>Welcome dingding <a href="javascript:;" @click="logout">注销a>h3>
div>
<div v-else>
<a href="javascript:;" @click="login">登录a> |
<a href="javascript:;">注册a>
div>
div>
<script>
//2. 创建new Vue()
new Vue({
el:"#app",
//3. 创建模型对象
//3.1 创建data对象
//本例中:因为界面上需要1个变量,所以
data:{
isLogin:false,//记录用户是否登录。开局,用户暂时未登录,所以初始值为false
},
//3.2 创建methods对象
//本例中:因为界面中需要2个函数,所以
methods:{
login(){
//让用户变成已登录状态
this.isLogin=true;
},
logout(){
//让用户变回未登录状态
this.isLogin=false;
}
}
})
script>
body>
html>
a. 什么是: 和v-if v-else配合来控制多个元素多选一显示的特殊指令
b. 如何:
<元素1 v-if=“条件1”>
<元素2 v-else-if=“条件2”>
… …
<元素n v-else>
c. 原理: new Vue()扫描到v-if时,先执行=右边的条件1。
1). 如果条件1为true,则显示v-if所在的元素,删除之后所有v-else-if和v-else
2). 如果条件1为false,则先删除v-if所在的元素,依次判断v-else-if后的条件。只要有任何一个v-else-if条件满足,则只保留这一个v-else-if所在的元素,删除其余元素
3). 如果所有v-if 和v-else-if的条件都不满足,则删除所有v-if和v-else-if所在元素,只保留v-else
d. 示例: 根据pm25值不同,显示不同表情
7_v-else-if.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<img v-if="pm25<100" src="img/1.png" alt="">
<img v-else-if="pm25<200" src="img/2.png" alt="">
<img v-else-if="pm25<300" src="img/3.png" alt="">
<img v-else src="img/4.png" alt="">
div>
<script>
var vm=new Vue({
el:"#app",
data:{
pm25:160
}
})
script>
body>
html>
a. 什么是: 专门根据一个数组的内容反复生成多个相同结构的HTML元素的特殊指令
b. 何时: 今后,只要希望根据一个数组中的元素内容,来生成相应个数的HTML元素,都要用v-for。
c. 如何:
<要反复生成的元素 v-for="(元素值, 下标i) of 数组">
强调: of前的()中第一个变量必须接住值,第二个变量必须接住下标/属性名。变量名可以随便改,但是顺序改不了。
d. 原理: new Vue()只要扫描到v-for,就会自动遍历of后的数组中每个元素。每遍历一个元素,就自动返回元素的内容和下标,保存到of前的两个变量中。并且,每遍历一个元素, v-for都自动创建一个当前HTML元素的副本。遍历了数组中的几个元素,最终就会反复创建几个相同结构的HTML元素副本。
e. v-for还能遍历对象中每个属性:
1). 其实vue中的v-for统一了js中for of和for in的功能
2). 其实v-for="(元素值, 下标i) in 数组/对象"
3). 其实写in和写of完全一样!都是既能遍历索引数组,又能遍历对象中的属性。
f. VUE2的两个著名的坑:
1). 如果使用"数组[下标]=新值"方式来修改数组中某个位置的元素,则即使数组内容可以修改成功,那么页面也不会跟着自动变化
原因: vue中并没有给数组中的数字下标添加任何访问器属性,所以vue中所有数字下标,都是不受监控的!
临时解决: 所有对数组的修改,不要用下标来做。用数组类型提供的那些API函数来实现。
比如: 想修改数组中1位置的元素值为新值:
错误: 数组[1]=新值
正确: 数组.splice(1,1,新值)
从1位置删除1个元素
再将新值插入到1位置——完成替换
将来解决: 升级到VUE3,自动解决了.
2). 在运行时,为对象添加新属性,则新属性不会自动更新界面。
原因: VUE2的new Vue()只能对开局时对象中现有的属性自动添加访问器属性。而,将来在运行时,由程序动态添加的属性,都无法实时自动获得访问器属性。所以,又会出现不受监控的属性
解决: 只能升级VUE3
g. v-for的问题:
1). v-for很蠢,即使我们在程序中只修改了一个元素值,因为v-for无法区分页面上的HTML元素,所以,只能删除整个列表,重建整个列表。——效率低
2). 根本原因: v-for反复生成的多个HTML元素副本,没有差别!
3). 解决: vue规定,今后只要使用v-for,都必须为元素同时绑定:key属性,且属性值应该是一个不重复的下标或属性名。
4). 如何:
<元素 v-for="(元素值, 下标i) of 数组" :key=“下标i”>
5). 原理:
i. 今后,v-for每创建一个HTML元素副本,都会绑定一个key属性,属性值为数组中一个不重复的下标
ii. 今后,当更新数组中某一个元素时,v-for就可根据HTML元素上绑定的唯一的key属性值,只找到对应的一个HTML元素,修改其内容即可。不会更新列表——效率高!
6). 总结: v-for为什么必须带:key
为每个元素添加唯一标识,避免更新一个元素时重建整个列表。
h. 示例: 使用v-for遍历索引数组和对象
8_v-for.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3>遍历数组中每个元素h3>
<ul>
<li v-for="(str,i) of arr" :key="i">
{{i+1}} - {{str}}
li>
ul>
<h3>遍历对象中每个属性h3>
<ul>
<li v-for="(属性值, 属性名) of lilei" :key="属性名">{{属性名}} - {{属性值}}li>
ul>
div>
<script>
var vm=new Vue({
el:"#app",
data:{
arr:["亮亮","楠楠","东东"],
lilei:{
sname:"Li Lei",
sage:11,
class:"初一2班"
}
}
})
//1. vm.arr[1]="涛涛" //修改不成功
//2. vm.arr.splice(1,1,"涛涛") //成功
//3. vm.lilei.money=100 //没成功
script>
body>
html>
运行结果:
i. 其实v-for也可以根据一个整数反复生成指定个数的HTML元素.
1). <元素 v-for=“i of 整数” :key=“i”>
2). 原理: v-for会自动从1开始,连续数数,数到给定的整数结束。每数一个数,就自动创建当前HTML元素的一个副本。
3). 示例: 生成分页按钮
9_v-for_pages.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
ul{
list-style: none;
}
ul>li{
float:left;
width:40px;
height:40px;
text-align:center;
line-height:40px;
border:1px solid #555;
border-radius: 5px;
margin-left:5px;
}
ul>li:hover{
cursor:pointer;
background-color:lightskyblue;
color:#fff;
border:0;
}
style>
head>
<body>
<div id="app">
<ul>
<li v-for="i of pageCount" :key="i">{{i}}li>
ul>
div>
<script>
var vm=new Vue({
el:"#app",
data:{
pageCount:10 //共6页
}
})
script>
body>
html>
(1). 什么是: 专门为元素绑定事件处理函数的指令
(2). 如何: 2步:
a. 第一步:
1). 标准: <元素 v-on:事件名=“事件处理函数(实参,…)”>
2). 简写:
i. @事件名=“事件处理函数(实参,…)”
ii. 如果不需要传实参值,则可以省略()
@事件名=“事件处理函数名”
b. 第二步: 页面中所需的所有事件处理函数必须在methods对象中集中定义。
methods:{
事件处理函数(形参变量, …){ … }
}
(3). 如何获得事件对象:
a. 只获得事件对象,不传其它自定义实参值:
<元素 @事件名=“事件处理函数”> //不要加()
methods:{
//当事件发生时
//浏览器自动创建event对象
// ↓
事件处理函数( e ){
}
}
b. 如何既获得事件对象,又传自定义实参
//浏览器自动创建的 event对象
// ↓
<元素 @事件名="处理函数($event, 自定义实参,...)"
//今后,vue中凡是$开头的都是有特殊意义的关键字,不能改名!
//$event 专门用于在vue事件绑定时获得事件对象。
//今后,每当触发事件时,$event都会抢先获得浏览器自动创建的event对象,保存起来备用。
methods:{
事件处理函数(e, 自定义形参,...){
e->$event对象->浏览器的event对象
自定义形参->自定义实参
}
}
(3). 示例: 点谁,谁喊疼:
[email protected]
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
img{ width:250px; }
style>
head>
<body>
<div id="app">
<img src="img/liang.jpg" @click="say($event,'liang')">
<img src="img/tao.jpg" @click="say($event,'tao')">
div>
<script>
new Vue({
el:"#app",
methods:{
say(e, name){
if(e.offsetY<120){
console.log(`${name} 头疼!`)
}else if(e.offsetY<240){
console.log(`${name} 胸疼!`)
}else{
console.log(`${name} 肚子疼!`)
}
}
}
})
script>
body>
html>
6_@_e.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
img{ width:250px; }
style>
head>
<body>
<div id="app">
<img src="img/liang.jpg" @click="say">
<img src="img/tao.jpg" @click="say">
div>
<script>
new Vue({
el:"#app",
methods:{
say(e){
if(e.offsetY<120){
console.log(`头疼!`)
}else if(e.offsetY<240){
console.log(`胸疼!`)
}else{
console.log(`肚子疼!`)
}
}
}
})
script>
body>
html>
7_@_$event.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
img{ width:250px; }
style>
head>
<body>
<div id="app">
<img src="img/liang.jpg" @click="say($event,'liang')">
<img src="img/tao.jpg" @click="say($event,'tao')">
div>
<script>
new Vue({
el:"#app",
methods:{
say(e, name){
if(e.offsetY<120){
console.log(`${name} 头疼!`)
}else if(e.offsetY<240){
console.log(`${name} 胸疼!`)
}else{
console.log(`${name} 肚子疼!`)
}
}
}
})
script>
body>
html>
(1). 问题: 如果要绑定的字符串内容中包含内嵌的HTML标签或特殊符号,如果使用{{}}绑定,则不会交给浏览器解析,而是原样显示HTML代码——不是我们想要的
(2). 原因: {{}}底层本质是textContent。DOM中的textContent特点就是如果修改内容时,内容中包含html代码,则不交给浏览器解析,而是直接显示。
(3). 解决:
a. DOM中: 只要要修改的新内容中包含HTML片段,都要用innerHTML来修改。因为innerHTML会先把HTML片段交给浏览器解析。然后将解析后的结果显示给人看。
b. vue中: v-html指令等效于innerHTML
(4). 今后,只要要绑定的内容中包含HTML片段,都要用v-html指令来绑定。不要用{{}}
(5). 如何: <元素 v-html=“变量”> 元素>
(6). 原理: v-html会先将变量中的字符串内容交给浏览器解析。然后将解析后的内容覆盖掉当前元素开始标签到结束标签之间的旧内容。
(7). 示例: 使用v-html绑定HTML内容
8_v-html.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3>{{msg}}h3>
<h3 v-html="msg">h3>
div>
<script>
new Vue({
el:"#app",
data:{
msg:`来自<<新华社>>的消息`
}
})
script>
body>
html>
(1). 问题: 当网络状况不好时,new Vue()对象所在的js文件下载可能会延迟加载。导致用户短暂看到页面上的{{}}
(2). 解决: 2种:
a. v-cloak:
1). 什么是: 在new Vue()加载之前,暂时隐藏元素的特殊指令
2). 如何: 2步:
i. 必须手工在样式表中添加:
属性选择器
选择所有带有v-cloak属性的元素
[v-cloak]{ display:none }
斗篷/幕布
ii. 在要暂时隐藏的元素上,添加v-cloak指令.
3). 原理: 当new Vue()加载完之后,new Vue()会扫描页面中所有v-cloak属性,并自动移除所有v-cloak属性。原来暂时隐藏的元素,就都显示出来了。
b. v-text:
1). 什么是: 专门用来代替{{}}绑定元素内容的特殊指令。
2). 如何: <元素 v-text=“变量”> 元素>
3). 说明: v-text和{{}}原理一样,底层都相当于textContent。
4). 原理: v-text暂时不会把变量值放入元素开始标签到结束标签之间。只有当new Vue()加载完,解析到v-text指令时,才临时读取变量值,放入元素开始标签到结束标签之间。
5). 强调: 如果v-text中的内容是由部分写死的字符串和变量拼接而成,则必须在"“内用``定义模板字符串。”"范围内必须符合js的语法规定。
(3). 示例:分别使用v-cloak和v-text避免用户短暂看到{{}}
9_v-cloak.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
[v-cloak]{ display:none }
style>
head>
<body>
<div id="app">
<h3 v-cloak>Welcome {{uname}}h3>
div>
<script>
setTimeout(function(){
new Vue({
el:"#app",
data:{
uname:"dingding"
}
})
},2000);
script>
body>
html>
10_v-text.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3 v-text="`Welcome ${uname}`">h3>
div>
<script>
setTimeout(function(){
new Vue({
el:"#app",
data:{
uname:"dingding"
}
})
},2000)
script>
body>
html>
(1). 什么是: 专门标记一个元素只在首次加在时更新一次内容。之后,即使变量发生变化,元素内容也不更新.
(2). 如何: <元素 v-once>
(3). 原理: new Vue()扫描到v-once后,只更新一次v-once所在元素的内容。并且不会将v-once加入虚拟DOM树。今后,即使变量值发生变化,也无法通知到这个v-once所在的元素。自然不会更新元素的内容。
(4). 优化: 今后如果发现个别元素只需要在首次页面加载时更新一次,之后不会再更新!都必须用v-once标记。进一步减少虚拟DOM树中元素的个数,进一步提高虚拟DOM树的效率。
(5). 示例: 使用v-once标记一个元素只在首次加载时更新内容
11_v-once.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3 v-once>上线时间: {{time}}h3>
<h3>当前系统时间: {{time}}h3>
div>
<script>
var vm=new Vue({
el:"#app",
data:{
time:new Date().toLocaleString()
}
});
setInterval(function(){
vm.time=new Date().toLocaleString();
},1000)
script>
body>
html>
(1). 什么是: 专门保护元素内容中的{{}}不被new Vue()编译的特殊指令
(2). 如何: <元素 v-pre>
(3). 示例: 阻止内容中的{{}}被编译:
12_v-pre.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h1 v-pre>vue框架采用{{自定义变量名}}方式标记页面中可能发生变化的位置h1>
div>
<script>
new Vue({
el:"#app"
})
script>
body>
html>
问题: 使用传统的:value绑定表单元素的内容时,虽然界面中更新了表单元素的内容,但是程序中的变量里却无法获得新的表单元素内容。
原因: 单向绑定: 只能将程序中的变量值,更新到页面中显示。无法实时将页面中的修改的新值,反向更新回程序中的变量里。(只能从Model->View,不会从View->Model)比如: :value就是一种单向绑定.
解决: vue中其实提供了双向绑定的方式:
1). 什么是双向绑定: 既能将程序中的变量值,更新到页面中显示。又能实时将页面中的修改的新值,反向更新回程序中的变量里。(既能从Model->View,又能从View->Model)
2). 何时: 今后,只要想实时获得/收集用户在界面上输入或选择的表单元素的新值时,都要用双向绑定。
3). 如何:
<表单元素 v-model=“变量”>
view->model
4). 结果: 只要界面上用户修改了表单元素的值,v-model就会立刻自动将新表单元素值更新回程序中data中的变量里保存。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<input v-model="str">
<button @click="search">百度一下button>
div>
<script>
//2. 创建new Vue()对象, 监视id为app的区域
var vm=new Vue({
el:"#app",
//3. 创建模型对象:
//3.1 创建data对象
//本例中: 因为界面中只需要一个变量,所以
data:{
str:"" //保存用户在界面上文本框中输入的内容。开局,内容为""
},
//3.2 创建methods对象
//本例中: 因为界面中只需要一个事件处理函数,所以
methods:{
search(){
if(this.str!==""){
console.log(`搜索${this.str}相关的内容...`);
}
}
}
})
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<input :value="str" @input="input">
<button @click="search">百度一下button>
div>
<script>
//2. 创建new Vue()对象, 监视id为app的区域
var vm=new Vue({
el:"#app",
//3. 创建模型对象:
//3.1 创建data对象
//本例中: 因为界面中只需要一个变量,所以
data:{
str:"" //保存用户在界面上文本框中输入的内容。开局,内容为""
},
//3.2 创建methods对象
//本例中: 因为界面中只需要一个事件处理函数,所以
methods:{
input(e){
//DOM中:
//当前文本框的内容
//赋值给
//data中的str
this.str=e.target .value
},
search(){
if(this.str!==""){
console.log(`搜索${this.str}相关的内容...`);
}
}
}
})
script>
body>
html>
new Vue({
el:"#app",
data:{ 变量名: 值, ... },
methods:{},
watch:{
变量名(){
}
}
})
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<input v-model="str" @keyup.13="search">
<button @click="search">百度一下button>
div>
<script>
//2. 创建new Vue()对象, 监视id为app的区域
var vm=new Vue({
el:"#app",
//3. 创建模型对象:
//3.1 创建data对象
//本例中: 因为界面中只需要一个变量,所以
data:{
str:"" //保存用户在界面上文本框中输入的内容。开局,内容为""
},
//3.2 创建methods对象
//本例中: 因为界面中只需要一个事件处理函数,所以
methods:{
search(){
if(this.str!==""){
console.log(`搜索${this.str}相关的内容...`);
}
}
},
//因为希望str变量值一变,立刻自动执行搜索操作
watch:{
str(){
this.search();
}
}
})
script>
body>
html>
<input type="radio" name="sex" value="1" v-model="sex">男
<input type="radio" name="sex" value="0" v-model="sex">女
c. 原理:
1). html中,一组相同name名的radio,只能选一个
2). v-model每次只会把选中的一个radio的value值自动更新回程序中变量里。
3). 如果程序中的变量值改变,也会影响页面上radio的选中状态。v-model会取回变量值和当前radio的value值做比较。哪个radio的value值与变量值相等,哪个radio就选中。
d. 示例: 选择性别:
16_v-model_radio.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
性别: <input type="radio" name="sex" value="1" v-model="sex">男
<input type="radio" name="sex" value="0" v-model="sex">女
<br/>
<h3>您选择的性别是:{{sex}}h3>
div>
<script>
new Vue({
el:"#app",
data:{
sex:1
}
})
script>
body>
html>
(3). select:
a. 特点:
1). 一个select元素,包含多个option元素
2). select元素中每个value值也都是在每个option上写死的!
b. 如何: v-model只写在select元素上一份即可!
<select v-model="变量">
<option value="备选值1">xxx</option>
<option value="备选值2">xxx</option>
<option value="备选值3">xxx</option>
</select>
c. 原理:
1). 首次加载页面时: v-model读取出变量的当前值和每个option的value值作比较。哪个option的value值与变量值相等,select就选中哪个option
2). 每当用户切换了select下的选中项时,v-model只会将选中的一个option的value值自动更新回程序中data中变量里保存。
d. 示例:选择城市,显示城市图片
17_v-model_select.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
请选择城市: <select id="sel" v-model="src">
<option value="img/bj.jpg">北京option>
<option value="img/sh.jpg">上海option>
<option value="img/hz.jpg">杭州option>
select><br/>
<br/>
<br/>
<img style="width:300px" :src="src">
div>
<script>
//2. 创建new Vue()对象
new Vue({
el:"#app",
//3. 创建模型对象
//本例中: 界面中虽然有两处变化,但是,两处变化用的是同一个变量。
data:{
src:"img/bj.jpg"
}
})
script>
body>
html>
运行结果:
(4). checkbox单用:
a. 特点: 用户选中与不选中改的是checked属性。
b. 如何:
c. 原理:
1). v-model直接操作和读取checked属性
2). 首次加载页面时,v-model读取变量值。如果变量值为true,则当前checkbox选中。如果变量值为false,则当前checkbox不选中
3). 当用户切换了选中状态,则v-model会直接将checked属性值更新回程序中data中变量里。且值还是bool类型的true或false。
d. 示例: 点同意,启用元素:
18_v-model_checkbox.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<br/>
用户名:<input :disabled="!agree"><br/>
<br/>
密码:<input :disabled="!agree" type="password"><br/>
<br/>
<input type="checkbox" v-model="agree">同意<br/>
<br/>
<button :disabled="!agree">注册button>
div>
<script>
//2. 创建new Vue()对象
new Vue({
el:"#app",
//3. 创建模型对象
//本例中: 界面上只需要一个变量agree
data:{
agree:false, //表示用户是否同意,开局,默认为不同意
}
})
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
img{
position:fixed;
}
style>
head>
<body>
<div id="app">
<img :style="{ left:left, top:top }" src="img/p3.png" >
div>
<script>
var vm=new Vue({
el:"#app",
data:{
left:"200px", //加单位
top:"100px" //加单位
}
});
//在f12 console控制台:
//vm.left="300px" 让飞机水平向右飞100px
//vm.top="50px" 让飞机水平向上飞50px
//只要在整个窗口中任何位置按下键盘时,都能飞行
window.onkeydown=function(e){
console.log(e.keyCode);
//左37, 上38, 右39, 下40
if(e.keyCode==37){
var left=parseInt(vm.left);
left-=10;//速度
vm.left=left+"px";
}else if(e.keyCode==39){
var left=parseInt(vm.left);
left+=10;//速度
vm.left=left+"px";
}else if(e.keyCode==38){
var top=parseInt(vm.top);
top-=10;
vm.top=top+"px";
}else if(e.keyCode==40){
var top=parseInt(vm.top);
top+=10;
vm.top=top+"px";
}
}
script>
body>
html>
(4). 多个元素都要绑定css属性:
a. 将style对象保存到一个变量中: 2步:
<元素1 :style="变量名1">
<元素2 :style="变量名2">
data:{
变量名1:{
css属性名: 属性值,
left : ...
},
变量名2:{
css属性名: 属性值,
left : ...
},
}
优点: 即使不同的变量内部,包含相同的css属性名,也因为分属于不同的对象范围内,而不会发生冲突
结果: new Vue()会先将变量保存的对象,翻译为style字符串,再用翻译之后的字符串作为style的最终属性值。
(5). 有些css属性是固定的,而有些css属性是变化的:
a. 其实固定不变的内联样式css属性和变化的css属性可以同时并存。
<元素 style=“固定不变的css属性” :style=“变化的css属性”
b. 运行时: new Vue()会先把:style的内容编译为字符串,然后,再将:style字符串与不带:的style的字符串拼接,形成最终一个完整的style字符串.
(6). 示例: 控制两架飞机飞行:
19_style2.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<img style="position:fixed" :style="p3Style" src="img/p3.png" >
<img style="position:fixed" :style="p5Style" src="img/p5.png" >
div>
<script>
var vm=new Vue({
el:"#app",
data:{
p3Style:{
left:"200px",
bottom:"100px"
},
p5Style:{
left:"500px",
bottom:"150px"
}
}
});
//只要在整个窗口中任何位置按下键盘时,都能飞行
window.onkeydown=function(e){
console.log(e.keyCode);
//左37, 上38, 右39, 下40
if(e.keyCode==37){
var left=parseInt(vm.p3Style.left);
left-=10;//速度
if(left<0){left=0};
vm.p3Style.left=left+"px";
}else if(e.keyCode==39){
var left=parseInt(vm.p3Style.left);
left+=10;//速度
if(left>window.innerWidth-200){
left=window.innerWidth-200;
}
vm.p3Style.left=left+"px";
}else if(e.keyCode==38){
var bottom=parseInt(vm.p3Style.bottom);
bottom+=10;
if(bottom>window.innerHeight-100){
bottom=window.innerHeight-100
}
vm.p3Style.bottom=bottom+"px";
}else if(e.keyCode==40){
var bottom=parseInt(vm.p3Style.bottom);
bottom-=10;
if(bottom<0){bottom=0}
vm.p3Style.bottom=bottom+"px";
}
//左65, 上87, 右68, 下83
if(e.keyCode==65){
var left=parseInt(vm.p5Style.left);
left-=10;//速度
vm.p5Style.left=left+"px";
}else if(e.keyCode==68){
var left=parseInt(vm.p5Style.left);
left+=10;//速度
vm.p5Style.left=left+"px";
}else if(e.keyCode==87){
var bottom=parseInt(vm.p5Style.bottom);
bottom+=10;
vm.p5Style.bottom=bottom+"px";
}else if(e.keyCode==83){
var bottom=parseInt(vm.p5Style.bottom);
bottom-=10;
vm.p5Style.bottom=bottom+"px";
}
}
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
.success{
background-color:lightGreen;
color:green;
border:green 1px solid ;
}
.fail{
background-color:pink;
color:red;
border:red 1px solid;
}
style>
head>
<body>
<div id="app">
手机号:<input type="text" v-model="phone">
<span :class="{ success:success , fail:fail }">{{msg}}span>
div>
<script>
//2. 创建new Vue()对象
new Vue({
el:"#app",
//3. 创建模型对象:
//3.1 因为界面中共需要4个变量,所以
data:{
phone:"", //保存文本框中用户输入的内容
success:false, //控制是否启用验证通过的样式类
fail:false, //控制是否启用验证失败的样式类
msg:"", //控制错误提示信息
},
//3.2 因为希望手机号一变,立刻执行查询,应该监控phone变量
watch:{
phone(){
//定义正则表达式规定手机号的规则
var reg=/^1[3-9]\d{9}$/;
//用正则表达式验证手机号变量的值
var result=reg.test(this.phone);
//如果验证通过
if(result==true){
//修改span的class,启用success,禁用fail
this.success=true;
this.fail=false;
//修改span的内容为"手机号格式正确"
this.msg="手机号格式正确"
}else{
//修改span的class,禁用success,启用fail
this.success=false;
this.fail=true;
//修改span的内容为"手机号格式不正确"
this.msg="手机号格式不正确"
}
}
}
})
script>
body>
html>
运行结果:
(3). 多个元素都要绑定相同的class
a. 问题: 如果多个元素都要绑定相同的class,则使用相同的变量名一定有冲突
b. 解决: 要将class的绑定对象定义在data中的一个变量里。不要定义在html元素上。
c. 如何: 2步:
1). <元素1 :class=“变量名1”>
<元素2 :class=“变量名2”>
2). data:{
变量名1:{
class名1: true或false,
class名2: true或false
},
变量名2:{
class名1: true或false,
class名2: true或false
},
d. 示例: 验证手机号和密码
5_class2.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<style>
.success{
background-color:lightGreen;
color:green;
border:green 1px solid ;
}
.fail{
background-color:pink;
color:red;
border:red 1px solid;
}
style>
head>
<body>
<div id="app">
手机号:<input type="text" v-model="phone">
<span :class="phoneSpan">{{phoneSpan.msg}}span><br>
密码:<input type="password" v-model="pwd">
<span :class="pwdSpan">{{pwdSpan.msg}}span>
div>
<script>
//2. 创建new Vue()对象
new Vue({
el:"#app",
data:{
phone:"",
phoneSpan:{
success:false,
fail:false,
msg:""
},
pwd:"",
pwdSpan:{
success:false,
fail:false,
msg:""
}
},
watch:{
phone(){
var reg=/^1[3-9]\d{9}$/;
var result=reg.test(this.phone);
if(result==true){
this.phoneSpan={
success:true,
fail:false,
msg:"手机号格式正确"
}
}else{
this.phoneSpan={
success:false,
fail:true,
msg:"手机号格式不正确"
}
}
},
pwd(){
var reg=/^\d{6}$/;
var result=reg.test(this.pwd);
if(result==true){
this.pwdSpan={
success:true,
fail:false,
msg:"密码格式正确"
}
}else{
this.pwdSpan={
success:false,
fail:true,
msg:"密码格式不正确"
}
}
}
}
})
script>
body>
html>
运行结果:
(4). 固定不变的class和动态变化的class也可以并存
a. <元素 class=“固定不变的class” :class=“变化的class”>
b. 结果: 两个class中的所有class,会拼接为一个大的class字符串,最终作用域当前元素上
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<input id="txt" v-my-focus><button>搜索button>
div>
<script>
//先定义自定义指令:
Vue.directive("my-focus",{
// 自己改名
inserted(原生DOM元素对象){
/******原生DOM的地盘**********/
//想让当前DOM元素自动获得焦点
//原生DOM中,已经提供了一个方法
原生DOM元素对象.focus();
/******原生DOM的地盘**********/
}
})
new Vue({
el:"#app"
})
script>
body>
html>
new Vue({
el:"#app",
data:{},
methods:{},
watch:{},
computed:{ //专门定义计算属性的区域
//计算属性虽然称为"属性",但是,因为要包含复杂的计算过程,所以,本质上却是一个函数。
属性名(){
//复杂的结算过程
return 结算结果
}
}
})
(2). 在界面中使用计算属性: 计算属性和用法和data中普通变量的用法完全一样!
<元素>{{计算属性名}}元素>
强调: 不要加()
4. 原理:
(1). new Vue()扫描到一个不认识的变量时,都会先去data中查找。如果data中没有,再自动去computed中查找是否有同名的计算属性。
(2). 自动执行计算属性的函数,并将返回值替换界面中计算属性名所在的位置。
5. 计算属性 vs methods中方法 区别
(1). methods中的方法: 反复调用几次,就重复执行几次,每次执行结果,不缓存执行结果,不能为下一次服务。
(2). 计算属性:
a. 只在第一次使用时,计算一次。然后, new Vue()就会立刻将计算结果缓存起来。
b. 之后如果使用相同的计算属性时,则不用重新计算,直接取出缓存中的值,反复使用。——无需重复计算,效率高!
c. 除非当计算属性内部依赖的其它普通变量值发生了改变时,才重新计算计算属性的新值,并立刻将新值重新缓存起来。
6. 总结:
(1). 今后,如果更侧重于执行一项操作,而不是侧重返回一个值时,就首选methods。比如: 登录、格式验证等
(2). 今后,如果更侧重计算一个结果值时,就首选computed。比如:计算总价。
7. 示例: 定义计算属性,计算购物车总价:
7_computed.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3>总价: ¥{{total.toFixed(2)}}h3>
<ul>
<li v-for="(p,i) of cart" :key="i">
{{p.pid}} | {{p.pname}} | ¥{{p.price.toFixed(2)}} | {{p.count}} | 小计: ¥{{(p.price*p.count).toFixed(2)}}
li>
ul>
<h3>总价: ¥{{total.toFixed(2)}}h3>
div>
<script>
new Vue({
el:"#app",
data:{
//购物车数组
cart:[
//商品对象
{pid:1, pname:"华为", price:5588, count:2},
//商品对象
{pid:2, pname:"苹果", price:9588, count:1},
//商品对象
{pid:3, pname:"小米", price:3588, count:3}
]
},
methods:{
},
computed:{
//专门计算购物车总价的计算属性
total(){
console.log(`自动调用了一次计算属性total()`);
var result=0;
for(var p of this.cart){
result+=p.price*p.count;
}
return result;
}
}
})
script>
body>
html>
(1). 定义过滤器:
//在Vue类型中
//添加一个过滤器 ↓
Vue.filter("过滤器名", function(原值){
对变量的原值进行加工
← return 加工后的结果
})
(2). 使用过滤器: 在绑定语法中:
<元素>{{变量 | 过滤器名}}元素>
连接
(3). 因为过滤器名称将来会用在{{}}绑定语法内。又因为{{}}内是js的地盘,所以过滤器名如果遇到多个单词必须用驼峰。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
||
<h3>性别: {{sex | sexFilter}}h3>
div>
<script>
//先定义过滤器: 1 或 0
Vue.filter("sexFilter",function(变量的原值){
if(变量的原值==1){
return "男"
}else{
return "女"
}
})
new Vue({
el:"#app",
data:{
sex:1
}
})
script>
body>
html>
运行结果:
性别: 男
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3>性别: {{sex | sexFilter}}h3>
<h3>性别: {{sex | sexFilter("en")}}h3>
<h3>性别: {{sex | sexFilter("cn")}}h3>
div>
<script>
Vue.filter("sexFilter",function(原值, 语言){
if(语言=="en"){
return 原值==1?"Male":"Female"
}else{
return 原值==1?"男":"女"
}
})
new Vue({
el:"#app",
data:{
sex:1
}
})
script>
body>
html>
运行结果:
性别: 男
性别: Male
性别: 男
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<h3>性别: {{sex | sexIcon}}h3>
<h3>性别: {{sex | sexFilter | sexIcon}}h3>
<h3>性别: {{sex | sexFilter("en") | sexIcon}}h3>
<h3>性别: {{sex | sexFilter("cn") | sexIcon}}h3>
div>
<script>
Vue.filter("sexFilter",function(原值, 语言){
if(语言=="en"){
return 原值==1?"Male":"Female"
}else{
return 原值==1?"男":"女"
}
})
// 1,0,男,女,Male,Female
Vue.filter("sexIcon",function(原值){
if(原值==1){
return "♂"
}else if(原值==0){
return "♀"
}else if(原值=="男"||原值=="Male"){
return 原值+"♂"
}else if(原值=="女"||原值=="Female"){
return 原值+"♀"
}
})
new Vue({
el:"#app",
data:{
sex:0
}
})
script>
body>
html>
运行结果:
性别: ♀
性别: 女♀
性别: Female♀
性别: 女♀
(0). 先配置服务器端接口地址的基础url部分:
axios.defaults.baseURL="http://xzserver.applinzi.com"
执行时,axios会将baseURL和相对url地址拼接在一起形成完整的服务器端接口地址。
(1). 发送get请求:
axios.get("服务器端接口的相对url地址",{
params:{
变量名: 变量值, 变量名: 变量值
}
}).then((result)=>{ //回调函数,请求响应成功后自动执行
//result里放的不直接是返回结果
//result.data里放的才是服务器端真实的返回结果
}).catch((err)=>{ //回调函数,请求响应失败后自动执行
//err错误提示信息
})
axios自动调用JSON.parse,将返回的json字符串转为js内存中的数组或对象。不用我们写任何东西。
(2). 发送post请求:
axios.post(
"服务器端接口的相对url地址",
"变量名=变量值&变量名=变量值&..."
).then((result)=>{
result.data是服务器端返回的结果
})
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/axios.min.js">script>
head>
<body>
<script>
axios.defaults.baseURL="http://xzserver.applinzi.com";
//向东哥新浪云服务器发送请求
//1. 请求首页6个商品对象
axios.get(
"index")
.then((result)=>{
console.log(result.data);
})
//2. 查询5号商品的详细信息
axios.get(
"details",
{
params:{ lid:5 }
}
).then((result)=>{
console.log(result.data);
})
//3. 登录验证
//(正确的用户名:dingding, 正确的密码:123456)
//乱写验证不通过
axios.post(
"users/signin",
"uname=dingding&upwd=123456"
).then((result)=>{
console.log(result.data);
})
script>
body>
html>
什么是: 可反复使用的页面中独立的功能区域。
为什么: 重用
何时: 今后,只要项目中,一块独立的功能区域,可能被反复使用,都要定义成组件
如何: 2步
(1). 定义组件,包含HTML+JS内容:
//在Vue类型中
// 添加一个新组件
Vue.component(“组件名”,{
//每个组件其实都是缩微的new Vue()对象
//功能和new Vue()完全一样
//只不过监控的区域小点儿
//第一步,还是做界面,只不过界面要在Vue.component()内部来做,必须写在template:``属性中,代替原来的el:"#app"
el:"#app", //去查找id为app的div所在的区域
//模板
template:<唯一父元素>
组件的HTML片段内容——和以前界面的定义完全一样
唯一父元素>`
data:{ … },
data(){ //从今往后data一律是函数!
return { //new Object()
界面所需的所有变量
}
}
methods:{ … },
watch:{ … },
computed:{ … },
mounted(){ … },
… …
})
(2). 在页面中使用组件: vue中的组件在使用上,其实就是一个可重用的自定义HTML标签而已!组件名叫什么,标签名就叫什么.强调: 因为组件名就是将来的标签名,又因为HTML语言不区分大小写!所以如果组件名使用驼峰命名,到了HTML中是区分不出来的!所以,组件名中如果包含多个英文单词,应该使用-分隔,而不应该用驼峰命名!比如: 在HTML中
原理: 每当new Vue()扫描页面时发现不认识的自定义HTML标签时,就会去Vue类型中查找是否有同名的自定义组件。只要找到同名的自定义组件,就自动做3件事:
(1). 用组件的template中代码片段代替页面上<自定义组件>标签的位置
(2). 自动调用data(),为当前组件副本创建一个专属的新的data对象,保存变量。
(3). 为当前组件副本区域创建一个缩微版的new Vue()对象,监控组件小范围内的所有数据变化和功能
为什么data要变成一个函数:
便于反复调用,创建新对象,避免组件间数据冲突.
示例: 自定义计数器组件
12_component.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
head>
<body>
<div id="app">
<ul>
<li><my-counter>my-counter>li>
<li><my-counter>my-counter>li>
<li><my-counter>my-counter>li>
ul>
div>
<script>
//做一个组件和做new Vue()完全一样:3步
//和前三天所讲的内容完全一致
Vue.component("my-counter",{
//1. 做界面
//1.1 唯一父元素
//1.2 查找可能发生变化的位置
//1.3 触发事件的元素
template:`
{{n}}
`,
//2. 创建模型对象
//2.1 创建data
data(){//可反复调用的函数
return { //相当于以前的data对象
n:0
}
},
/*****以下和new Vue()完全一样*********/
//2.2 创建methods
methods:{
add(){
this.n++;
},
minus(){
if(this.n>0){
this.n--
}
}
}
})
new Vue({
el:"#app"
})
script>
body>
html>
html片段
,xxx
,Vue.component("todo",{
template:`
待办事项列表
`,
components:{ todoAdd, todoList },
beforeCreate(){
console.log(`父组件beforeCreate`);
},
created(){
console.log(`父组件created`);
},
beforeMount(){
console.log(`父组件beforeMount`);
},
mounted(){
console.log(`父组件mounted`);
},
})
1_todo/todoList.js
var todoList={
template:`
`,
components:{ todoItem }
}
1_todo/todoItem.js
var todoItem={
template:`
1 - 吃饭 ×
`
}
1_todo/index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<script src="todoAdd.js">script>
<script src="todoItem.js">script>
<script src="todoList.js">script>
<script src="todo.js">script>
head>
<body>
<div id="app">
<todo>todo>
div>
<script>
new Vue({
el: "#app"
})
script>
body>
html>
Vue.component("todo",{
template:`
待办事项列表
`,
data(){
return {
tasks:[ "吃饭","睡觉","打亮亮" ]
}
},
components:{ todoAdd, todoList },
beforeCreate(){
console.log(`父组件beforeCreate`);
},
created(){
console.log(`父组件created`);
},
beforeMount(){
console.log(`父组件beforeMount`);
},
mounted(){
console.log(`父组件mounted`);
},
})
2_todo/todoAdd.js
var todoAdd={
props:["tasks"],
template:`
`,
data(){
return { t:"" }
},
methods:{
add(){
if(this.t!==""){
this.tasks.push(this.t);
//添加成功,清除t的内容
//清除t,等效于清除文本框内容
this.t="";
}
}
},
beforeCreate(){
console.log(`子组件beforeCreate`);
},
created(){
console.log(`子组件created`);
},
beforeMount(){
console.log(`子组件beforeMount`);
},
mounted(){
console.log(`子组件mounted`);
},
}
2_todo/todoList.js
var todoList={
//从自己身上的属性中获取指定属性名的属性值
props:["tasks"],
template:`
-
`,
components:{ todoItem }
}
2_todo/todoItem.js
var todoItem={
//从自己身上的t属性和i属性中取出属性值
props:["t", "i", "tasks"],
template:`
{{i+1}} - {{t}} ×
`,
methods:{
del(){
this.tasks.splice(this.i,1);
}
}
}
2_todo/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/vue.js"></script>
<!--因为加载父组件时,就要用到子组件的内容了,所以先引入子组件,然后再引入父组件-->
<script src="todoAdd.js"></script>
<script src="todoItem.js"></script>
<script src="todoList.js"></script>
<script src="todo.js"></script>
</head>
<body>
<div id="app">
<todo></todo>
<!-- <todo-item></todo-item> -->
</div>
<script>
new Vue({
el: "#app"
})
</script>
</body>
</html>
单 页面 应用
多页面 | 单页面 | |
---|---|---|
请求次数 | 每访问一个页面,哪怕是曾经访问过的页面,都需要重新向服务器发送请求。——请求次数多 | 只在首次加载时,将唯一完整的HTML页面和所有页面组件都下载到客户端本地。今后,每次切换"页面",只不过是在客户端本地一个html文件中切换不同的组件对象显示而已。无需重复向服务器发送请求。——请求次数少 |
多个页面共用的公共资源 | 每请求一个页面,都要重新下载页面头部引入的css和js文件——请求次数多 | 每次切换页面时,唯一完整的HTML文件是不动的。只更新文件中局部的内容。所以,不会重新加载/请求HTML文件头部引入的css和js。所有css和js也只是在首次加载时下载一次即可。——请求次数少 |
页面加载效率 | 每加载一个新页面,都要删除原来的整棵DOM树,重建整棵DOM树。——效率低 | 每次切换页面时,只是局部更新页面中组件片段的内容,所以不会删除整棵DOM树,只需要修改原DOM中个别元素对象节点即可!——效率高! |
切面切换动画 | 几乎不可能实现页面切换动画。因为每次切换页面总是先彻底删除原页面内容,再重新下载新页面内容。新旧两个页面不可能并存。 | 有可能实现页面过去动画。因为所有组件都已经在客户端了。完全有可能让前后两个页面组件同时显示,来实现过渡动画效果。 |
单页面应用的缺点: 首屏加载慢——因为不但下载首页,还要下载其他页面的组件备用。
早就被解决了: 懒加载
何时: 今后几乎所以移动端,以及大部分PC端项目都使用单页面应用开发。
如何: 3步
(1). 先创建一个唯一完整的HTML页面
a. 拥有基本的vue页面结构的页面
b. 引入vue-router.js路由器组件文件
c. 在唯一完整的页面中使用标签为将来的页面组件设置预留区域
(2). 为每个"页面"创建对应的页面组件(就是子组件,只不过当做页面来用)
将所有页面组件的文件,引入唯一完整的HTML页面中
(3). 创建路由器对象和路由字典
a. 什么是路由器对象: 可自动侦测浏览器地址栏的url变化,根据用户输入的不同地址,自动找到对应的组件对象,帮我们替换到唯一完整的HTML页面中区域
b. 什么是路由字典: 集中保存不同地址与组件对象之间对应关系的数组
c. 为什么要使用路由字典: 路由器对象侦测到地址栏变化时,必须去路由字典中找到对应的路径,才能进一步找到对应的组件对象。
d. 如何创建路由器对象: 3步: 创建独立的router.js文件中
1). 先创建路由字典数组:
var routes=[
{ path:"/", component: 首页组件对象 },
{ path:"/其它路径", component: 其它页面组件对象 },
… …
]
2). 创建路由器对象,并将路由字典装入路由器对象中
var router=new VueRouter({ routes })
3). 将路由器对象引入new Vue()中: 回到唯一完整的HTML页面中:
先将router.js文件引入唯一完整的HTML页面中,
再new Vue({ el:"#app", router })
公共的页头组件:
(1). 创建独立的页头组件文件,将页头组件创建为全局组件,因为页头不属于任何一个页面,可用在任意页面中
Vue.component(“my-header”, { … })
(2). 在唯一完整的HTML页面中引入页头组件文件
(3). 问题: 标签放在那儿?2种:
a. 如果所有页面都有统一的页头,则可以将放在唯一完整的HTML页面中外的上方!
b. 如果个别页面不希望有页头!则也可以将标签只放在需要页头的页面组件中!
如果用户路径错误: 自制一个友好的404页面
(1). 创建一个子组件保存404页面的内容
(2). 去路由字典中加入404页面组件对应的路径
{ path:"", component: 404页面组件对象 }
强调: path:""必须写在其它路由最后!*代表一切路径!
(3). 将404页面组件引入到唯一完整的HTML页面中
原理:
(1).路由器router对象被加入到唯一html页面内的new Vue()中,它就会监视当前html页面所在路径。
(2).一旦浏览器地址栏中路径发送变化,router就会立刻获得当前新路径,自动去路由字典routers中查找是否有匹配的路径。
(3).只要找到路由字典中和当前地址栏中匹配的路径,就自动去内存中找对应的组件对象。
(4).只要找到组件对象,就会用组件对象的内容,替换html页中占位符的位置。
(5).最后用户就看到了对应组件的内容。
为什么使用#/作为客户端导航的标志
(1).如果在浏览器地址栏输入新地址,默认浏览器都会向服务器发送请求。
(2).但是,vue spa应用中,因为早就把所有的页面的组件都下载到了客户端本地。所以,当修改地址栏路径时,不应该再想服务器
发送请求。而应该交给网页本地的路由器对象,解析新地址,加载对应的组件。
(3).url标准中只有一种地址是客户端导航–锚点地址。当在浏览器内切换锚点地址时,浏览器是不会向服务器发送请求的,而是在
页面内跳转。
(4)所以,Vue单页面应用为了避免浏览器向服务器发送请求,才用的"#/相对路径"锚点地址方式作为客户端导航。
示例: 使用单页面应用程序实现一个网站,包含三个页面一个页头
spa/js/index.js
var Index={
template:`
这里是首页
`
}
spa/js/details.js
var Details={
template:`
这里是详情页
`
}
spa/js/not-found.js
var NotFound={
template:`
404: 你迷路了!
`
}
spa/js/my-header.js
Vue.component("my-header",{
template:`
这里是页头
`
})
spa/js/router.js
var routes=[
{path:"/", component: Index},
{path:"/details", component: Details},
{path:"*", component: NotFound}
];
var router=new VueRouter({ routes })
spa/index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<script src="js/vue-router.js">script>
<script src="js/my-header.js">
//Vue.component("my-header")
script>
<script src="js/index.js">
//var Index={}
script>
<script src="js/details.js">
//var Details={}
script>
<script src="js/not-found.js">
//var NotFound={}
script>
<script src="js/router.js">
//var routes=[
//用到了Index和Details两个对象名
//]
//var router=new VueRouter({ routes })
script>
head>
<body>
<div id="app">
<router-view>
router-view>
div>
<script>
new Vue({
el:"#app",
router
})
script>
body>
html>
页面跳转: 2种 固定套路!记住!
(1). 在页面上写死一个标签:
a. 不要用
b. 改为用
c. 原理:
(2). 在js程序中跳转:
this.$router.push("/相对路径")
路由器对象
路由传参:
(1). 需求: 实际项目中,前一个页面,跳转到后一个页面时,前一个页面有时会传递一个数据到后一个页面中继续使用!
(2). 多页面应用: 使用url中search查询字符串方式传递:
a. 前一个页面:
b. 下一个页面:
1). location.search 可获得 “?变量名=变量值”
2). location.search.split("=")[1] 可获得传过来的变量值
(3). vue单页面应用程序中:
a. 修改路由字典中的项目,允许携带参数:
{ path:"/相对路径/:自定义变量名", component: 页面组件, props:true }
允许携带一个参数变量
props:true, 让参数自定义变量自动成为下一个页面的props中的属性。在下个页面中就可直接使用自定义变量名获取传过来的参数值
b. 跳转时,路径携带参数值:
this.$router.push("/相对路径/参数值")
c. 在下一个组件中如何接:
1). 先props:[ “自定义变量” ]
2). 在下一个组件中就可以像使用自己的data中的变量一样,使用页面跳转时出来的参数值
(4). vue中规定: 如果一个路径要求携带参数,每次跳转时都必须携带参数值!
否则如果将来跳转时不携带参数,就不允许跳转!而是转向404页面!
示例: 为上一个单页面应用示例加上跳转页面功能,以及在跳转时传参
spa/js/index.js
var Index={
template:`
这里是首页
查看5号商品的详情
`,
methods:{
goto(lid){
this.$router.push(`/details/${lid}`);
}
}
}
spa/js/details.js
var Details={
//因为路由字典中写了props:true
//所以lid参数变量会自动成为props中的属性
props:["lid"], //和data中的变量用法完全一样,只不过来源不同而已。props来自于外部,data是自己创建的。
template:`
这里是详情页
这里是{{lid}}号商品的详细信息...
`,
methods:{
back(){
this.$router.push("/");
}
}
}
spa/js/not-found.js
var NotFound={
template:`
404: 你迷路了!
`
}
spa/js/my-header.js
Vue.component("my-header",{
template:`
这里是页头
首页
详情页
`
})
spa/js/router.js
var routes=[
{path:"/", component: Index},
//允许/details路径携带一个变量名为lid
//props:true意为,让lid变量自动成为details组件中的props属性
{path:"/details/:lid", component: Details, props:true},
{path:"*", component: NotFound}
];
var router=new VueRouter({ routes })
spa/index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="js/vue.js">script>
<script src="js/vue-router.js">script>
<script src="js/my-header.js">
//Vue.component("my-header")
script>
<script src="js/index.js">
//var Index={}
script>
<script src="js/details.js">
//var Details={}
script>
<script src="js/not-found.js">
//var NotFound={}
script>
<script src="js/router.js">
//var routes=[
//用到了Index和Details两个对象名
//]
//var router=new VueRouter({ routes })
script>
head>
<body>
<div id="app">
<router-view>
router-view>
div>
<script>
new Vue({
el:"#app",
router
})
script>
body>
html>
运行结果:
(1). 先安装能够反复生成脚手架的命令行工具: (老母鸡)
npm config set registry https://registry.npm.taobao.org
npm i -g @vue/cli
结果: + @vue/[email protected] 说明装完。
(2). 用工具为不同项目反复生成多套脚手架: (老母鸡下的蛋)
今后,每开发一个新项目,都要用脚手架命令行工具,创建一套新的标准化的脚手架项目结构:
在想创建项目文件夹的路径中,地址栏输入cmd,回车
vue create 自定义项目名
a. ? Please pick a preset: (Use arrow keys)
Default ([Vue 2] babel, eslint)
Default (Vue 3) ([Vue 3] babel, eslint)
> Manually select features
b. ? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
( ) Choose Vue version
(*) Babel //可将时髦的ES6/7等语法翻译为大多数浏览器都支持的ES5代码。
( ) TypeScript
( ) Progressive Web App (PWA) Support
(*) Router //SPA应用的核心
( ) Vuex
( ) CSS Pre-processors //sass/scss
( ) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
c. ? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) N
1). Vue的路由导航方式有2种:
a. hash方式: #/相对路径
问题: 万一,页面中也用到#锚点地址在页面内跳转位置。
b. history方式: /相对路径
问题: 浏览器不知道该把请求发给服务器端接口,还是发给客户端的VueRouter()
解决: 后端工程师采用首页重定向技术解决。
d. ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrowkeys)
In dedicated config files
> In package.json
e. Save this as a preset for future projects? (y/N) N
f. 结果: 看到: Successfully created project xzvue. 说明安装成功
g. 在我们希望创建脚手架项目的位置,多出一个xzvue文件夹。
h. 删除新生成的xzvue文件夹内的.git文件夹
在vs code中运行脚手架项目:
(1). 用vs code打开xzvue文件夹。(不要打开xzvue的上级文件夹)
(2). 右键单击package.json文件,选择"在集成终端中打开"
(3). 在终端中启动脚手架项目: 输入npm run serve
!!!结尾没有r
看到:App running at:
- Local: http://localhost:8080/
启动成功
(4). (如果上一步powershell不能用, 才需要)在终点上方的+图标右侧有一个下拉箭头,打开,选择Command Prompt。
(5). 打开浏览器,地址栏中输入http://localhost:8080
看到: 脚手架项目的示例页面
脚手架的文件夹结构: SPA 4部分
(1). 唯一完整的HTML页面: 被一分为三:
a. HTML的基础结构保存在了public/index.html
b.