🍑Vue-Study
视频地址:https://www.bilibili.com/video/BV17h41137i4
课件链接:https://pan.baidu.com/s/1OQJdnVR74_SBr7BqUYQAbw?pwd=8023
Vue2官网:https://v2.cn.vuejs.org/
Vue3官网:https://cn.vuejs.org/
代码:
Vue-基础部分
前言:环境准备
VsCode插件:
- techer.open-in-browser
- ritwickdey.LiveServer
Chrom浏览器插件:
1.1、Vue程序初体验
1.1.1、第一个Vue程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--安装vue -->
<script src="../js/vue.js"></script>
<div id="app"></div>
<script>
let myVue = new Vue({
template: "<h1>hello Vue</h1>"
});
myVue.$mount('#app');
</script>
</body>
</html>

1.1.2、模板引擎数据来源
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模板引擎数据来源</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 模板语句的数据来源:
1. 谁可以给模板语句提供数据支持呢?data选项。
2. data选项的类型是什么?Object | Function (对象或者函数)
3. data配置项的专业叫法:Vue 实例的数据对象.(data实际上是给整个Vue实例提供数据来源的。)
4. 如果data是对象的话,对象必须是纯粹的对象 (含有零个或多个的 key/value 对)
5. data数据如何插入到模板语句当中?
{{}} 这是Vue框架自己搞的一套语法,别的框架看不懂的,浏览器也是不能够识别的。
Vue框架自己是能够看懂的。这种语法在Vue框架中被称为:模板语法中的插值语法。(有的人把他叫做胡子语法。)
怎么用?
{{data的key}}
插值语法的小细节:
{这里不能有其它字符包括空格{
}这里不能有其它字符包括空格}
-->
<div id="app"></div>
<script>
let myVue = new Vue({
template:
`
<h1>{{name}},年龄是:{{age}},兴趣爱好是:{{hoppy[0]}}、{{hoppy[1]}}、{{hoppy[2]}}</h1>
`,
data: {
name: "zhangsan",
"age": "23",
"hoppy": [
"抽烟", "喝酒", "烫头"
]
}
});
myVue.$mount("#app");
</script>
</body>
</html>

1.1.3、template配置项详解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>template配置项详解</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<h1>姓名:{{name}}</h1>
<h1>年龄:{{age}}</h1>
</div>
</div>
<script>
// Vue.config是Vue的全局配置对象。
// productionTip属性可以设置是否生成生产提示信息。
// 默认值:true。如果是false则表示阻止生成提示信息。
//Vue.config.productionTip = false
// 或者直接在vue.js全局搜索:productionTip,将它设置为false
/*
一 、关于template配置项:
1.template后面指定的是模板语句,但是模板语句中只能有一个根节点。
2.只要data中的数据发生变化,模板语句一定会重新编译。(只要data变,template就会重新编译,重新渲染)
3.如果使用template配置项的话,指定挂载位置的元素会被替换。
4.好消息:目前我们可以不使用template来编写模板语句。这些模板语句可以直接写到html标签中。Vue框架能够找到并编译,然后渲染。
5.如果直接将模板语句编写到HTML标签中,指定的挂载位置就不会被替换了。
二、关于$mount('#app')?
也可以不使用$mount('#app')的方式进行挂载了。
在Vue中有一个配置项:el
el配置项和$mount()可以达到同样的效果。
el配置项的作用?
告诉Vue实例去接管哪个容器。
el : '#app',表示让Vue实例去接管id='app'的容器。
el其实是element的缩写。被翻译为元素。
*/
new Vue({
data: {
name: "zhangzhixi",
age: "23"
},
// 替换掉 .$mount()
el: "#app"
});
</script>
</body>
</html>

1.1.4、Vue实例和容器的关系
<!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>Vue实例 和 容器 的关系是:一夫一妻制</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 准备容器 -->
<div class="app">
<h1>{{msg}}</h1>
</div>
<div class="app">
<h1>{{msg}}</h1>
</div>
<!-- 准备容器 -->
<div id="app2">
<h1>{{name}}</h1>
</div>
<!-- vue程序 -->
<script>
/*
验证:一个Vue实例可以接管多个容器吗?
不能。一个Vue实例只能接管一个容器。一旦接管到容器之后,即使后面有相同的容器,Vue也是不管的。因为Vue实例已经“娶到媳妇”了。
*/
new Vue({
el : '.app',
data : {
msg : 'Hello Vue!'
}
})
new Vue({
el : '#app2',
data : {
name : 'zhangsan'
}
})
// 这个Vue实例想去接管 id='app2'的容器,但是这个容器已经被上面那个Vue接管了。他只能“打光棍”了。
new Vue({
el : '#app2',
data : {
name : 'jackson'
}
})
</script>
</body>
</html>

1.2、核心技术
1.2.1、模板语法之插值语法
<!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>模板语法之插值语法{{}}</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!--
主要研究:{{这里可以写什么}}
1. 在data中声明的变量、函数等都可以。
2. 常量都可以。
3. 只要是合法的javascript表达式,都可以。
4. 模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 等。
'Infinity,undefined,NaN,isFinite,isNaN,'
'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,'
'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,'
'require'
-->
<!-- 准备容器 -->
<div id="app">
<!-- 在data中声明的 -->
<!-- 这里就可以看做在使用msg变量。 -->
<h1>{{msg}}</h1>
<h1>{{sayHello()}}</h1>
<!-- <h1>{{i}}</h1> -->
<!-- <h1>{{sum()}}</h1> -->
<!-- 常量 -->
<h1>{{100}}</h1>
<h1>{{'hello vue!'}}</h1>
<h1>{{3.14}}</h1>
<!-- javascript表达式 -->
<h1>{{1 + 1}}</h1>
<h1>{{'hello' + 'vue'}}</h1>
<h1>{{msg + 1}}</h1>
<h1>{{'msg' + 1}}</h1>
<h1>{{gender ? '男' : '女'}}</h1>
<h1>{{number + 1}}</h1>
<h1>{{'number' + 1}}</h1>
<h1>{{msg.split('').reverse().join('')}}</h1>
<!-- 错误的:不是表达式,这是语句。 -->
<!-- <h1>{{var i = 100}}</h1> -->
<!-- 在白名单里面的 -->
<h1>{{Date}}</h1>
<h1>{{Date.now()}}</h1>
<h1>{{Math}}</h1>
<h1>{{Math.ceil(3.14)}}</h1>
</div>
<!-- vue程序 -->
<script>
// 用户自定义的一个全局变量
var i = 100
// 用户自定义的一个全局函数
function sum(){
console.log('sum.....');
}
new Vue({
el : '#app',
data : {
number : 1,
gender : true,
msg : 'abcdef', // 为了方便沟通,以后我们把msg叫做变量。(这行代码就可以看做是变量的声明。)
sayHello : function(){
console.log('hello vue!');
}
}
})
</script>
</body>
</html>

1.2.2、模板语法之指令:v-once(只执行一次)
<!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>模板语法之指令语法 v-??? </title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!--
指令语法:
1. 什么是指令?有什么作用?
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
2. Vue框架中的所有指令的名字都以“v-”开始。
3. 插值是写在标签体当中的,那么指令写在哪里呢?
Vue框架中所有的指令都是以HTML标签的属性形式存在的,例如:
<span 指令是写在这里的>{{这里是插值语法的位置}}</span>
注意:虽然指令是写在标签的属性位置上,但是这个指令浏览器是无法直接看懂的。
是需要先让Vue框架进行编译的,编译之后的内容浏览器是可以看懂的。
4. 指令的语法规则:
指令的一个完整的语法格式:
<HTML标签 v-指令名:参数="javascript表达式"></HTML标签>
表达式:
之前在插值语法中{{这里可以写什么}},那么指令中的表达式就可以写什么。实际上是一样的。
但是需要注意的是:在指令中的表达式位置不能外层再添加一个{{}}
不是所有的指令都有参数和表达式:
有的指令,不需要参数,也不需要表达式,例如:v-once
有的指令,不需要参数,但是需要表达式,例如:v-if="表达式"
有的指令,既需要参数,又需要表达式,例如:v-bind:参数="表达式"
5. v-once 指令
作用:只渲染元素一次。随后的重新渲染,元素及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
6. v-if="表达式" 指令
作用:表达式的执行结果需要是一个布尔类型的数据:true或者false
true:这个指令所在的标签,会被渲染到浏览器当中。
false:这个指令所在的标签,不会被渲染到浏览器当中。
-->
<!-- 准备一个容器 -->
<div id="app">
<h1>{{msg}}</h1>
<h1 v-once>{{msg}}</h1>
<h1 v-if="a > b">v-if测试:{{msg}}</h1>
</div>
<!-- vue程序 -->
<script>
new Vue({
el: '#app',
data: {
msg: 'Hello Vue!',
a: 12,
b: 11
}
})
</script>
</body>
</html>

用Vue插件,修改文本

1.2.2、v-bind指令详解
<!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>v-bind指令详解(它是一个负责动态绑定的指令)</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!--
v-bind指令详解
1. 这个指令是干啥的?
它可以让HTML标签的某个属性的值产生动态的效果。
2. v-bind指令的语法格式:
<HTML标签 v-bind:参数="表达式"></HTML标签>
3. v-bind指令的编译原理?
编译前:
<HTML标签 v-bind:参数="表达式"></HTML标签>
编译后:
<HTML标签 参数="表达式的执行结果"></HTML标签>
注意两项:
第一:在编译的时候v-bind后面的“参数名”会被编译为HTML标签的“属性名”
第二:表达式会关联data,当data发生改变之后,表达式的执行结果就会发生变化。
所以,连带的就会产生动态效果。
4. v-bind因为很常用,所以Vue框架对该指令提供了一种简写方式:
只是针对v-bind提供了以下简写方式:
<img :src="imgPath">
5. 什么时候使用插值语法?什么时候使用指令?
凡是标签体当中的内容要想动态,需要使用插值语法。
只要向让HTML标签的属性动态,需要使用指令语法。
-->
<!-- 准备一个容器 -->
<div id="app">
<!-- 注意:以下代码中 msg 是变量名。 -->
<!-- 注意:原则上v-bind指令后面的这个参数名可以随便写。 -->
<!-- 虽然可以随便写,但大部分情况下,这个参数名还是需要写成该HTML标签支持的属性名。这样才会有意义。 -->
<span v-bind:xyz="msg"></span>
<!-- 这个表达式带有单引号,这个'msg'就不是变量了,是常量。 -->
<span v-bind:xyz="'msg'"></span>
<!-- v-bind实战 -->
<img src="../img/1.jpg"> <br>
<img v-bind:src="imgPath"> <br>
<!-- v-bind简写形式 -->
<img :src="imgPath"> <br>
<!-- 这是一个普通的文本框 -->
<input type="text" name="username" value="zhangsan"> <br>
<!-- 以下文本框可以让value这个数据变成动态的:这个就是典型的动态数据绑定。 -->
<input type="text" name="username" :value="username"> <br>
<!-- 使用v-bind也可以让超链接的地址动态 -->
<a href="https://www.baidu.com">走起</a> <br>
<a :href="url">走起2</a> <br>
<!-- 不能采用以下写法吗? -->
<!--
不能这样,报错了,信息如下:
Interpolation inside attributes has been removed.
Use v-bind or the colon shorthand instead. For example,
instead of <div id="{{ val }}">, use <div :id="val">
属性内部插值这种语法已经被移除了。(可能Vue在以前的版本中是支持这种写法的,但是现在不允许了。)
请使用v-bind或冒号速记来代替。
请使用 <div :id="val"> 来代替 <div id="{{ val }}">
-->
<!-- <a href="{{url}}">走起3</a> -->
<h1>{{msg}}</h1>
</div>
<!-- vue程序 -->
<script>
// 赋值的过程就可以看做是一种绑定的过程。
//let i = 100
new Vue({
el : '#app',
data : {
msg : 'Hello Vue!',
imgPath : '../img/1.jpg',
username : 'jackson',
url : 'https://www.baidu.com'
}
})
</script>
</body>
</html>

1.2.3、v-model指令详解
<!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>v-model指令详解</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!--
v-bind和v-model的区别和联系
1. v-bind和v-model这两个指令都可以完成数据绑定。
2. v-bind是单向数据绑定。
data ===> 视图
3. v-model是双向数据绑定。
data <===> 视图
4. v-bind可以使用在任何HTML标签当中。v-model只能使用在表单类元素上,例如:
input标签、select标签、textarea标签。
为什么v-model的使用会有这个限制呢?
因为表单类的元素才能给用户提供交互输入的界面。
v-model指令通常也是用在value属性上面的。
5. v-bind和v-model都有简写方式:
v-bind简写方式:
v-bind:参数="表达式" 简写为 :参数="表达式"
v-model简写方式:
v-model:value="表达式" 简写为 v-model="表达式"
-->
<!-- 准备一个容器 -->
<div id="app">
v-bind指令:<input type="text" v-bind:value="name1"><br>
v-model指令:<input type="text" v-model:value="name2"><br>
<!-- 以下报错了,因为v-model不能使用在这种元素上。 -->
<!-- <a v-model:href="url">百度</a> -->
v-bind指令:<input type="text" :value="name1"><br>
v-model指令:<input type="text" v-model="name2"><br>
消息1:<input type="text" :value="msg"><br>
消息2:<input type="text" v-model="msg"><br>
</div>
<!-- vue程序 -->
<script>
new Vue({
el : '#app',
data : {
name1 : 'zhangsan',
name2 : 'wangwu',
url : 'https://www.baidu.com',
msg : 'Hello Vue!'
}
})
</script>
</body>
</html>

1.2.4、MVVM分层思想
<!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>初识MVVM分层思想</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
1. MVVM是什么?
M:Model(模型/数据)
V:View(视图)
VM:ViewModel(视图模型):VM是MVVM中的核心部分。(它起到一个核心的非常重要的作用。)
MVVM是目前前端开发领域当中非常流行的开发思想。(一种架构模式。)
目前前端的大部分主流框架都实现了这个MVVM思想,例如Vue,React等。
2. Vue框架遵循MVVM吗?
虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。
Vue框架基本上也是符合MVVM思想的。
3. MVVM模型当中倡导了Model和View进行了分离,为什么要分离?
假如Model和View不分离,使用最原始的原生的javascript代码写项目:
如果数据发生任意的改动,接下来我们需要编写大篇幅的操作DOM元素的JS代码。
将Model和View分离之后,出现了一个VM核心,这个VM把所有的脏活累活给做了,
也就是说,当Model发生改变之后,VM自动去更新View。当View发生改动之后,
VM自动去更新Model。我们再也不需要编写操作DOM的JS代码了。开发效率提高了很多。
-->
<!-- 准备容器 -->
<!-- View V-->
<div id="app">
姓名:<input type="text" v-model="name">
</div>
<!-- vue程序 -->
<script>
// ViewModel VM
const vm = new Vue({
el : '#app',
// Model M
data : {
name : 'zhangsan'
}
})
</script>
</body>
</html>
1.2.5、Vue数据代理机制对属性名的要求
<!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>Vue数据代理机制对属性名的要求</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!--
1. Vue实例不会给以_和$开始的属性名做数据代理。
2. 为什么?
如果允许给_或$开始的属性名做数据代理的话。
vm这个Vue实例上可能会出现_xxx或$xxx属性,
而这个属性名可能会和Vue框架自身的属性名冲突。
3. 在Vue当中,给data对象的属性名命名的时候,不能以_或$开始。
-->
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
</div>
<!-- vue程序 -->
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Hello Vue!',
/* 不能被识别 */
_name : 'zhangsan',
$age : 20
}
})
</script>
</body>
</html>

1.2.6、解读Vue框架源代码
<!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>解读Vue框架源代码</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!--
Vue框架源代码中关键性代码:
1. var data = vm.$options.data;
注意:这是获取data。程序执行到这里的时候vm上还没有 _data 属性。
2. data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
程序执行完这个代码之后,vm对象上多了一个_data这样的属性。
通过以上源码解读,可以得知data不一定是一个{},也可以是一个函数。
代码含义:
如果data是函数,则调用getData(data, vm)来获取data。
如果data不是函数,则直接将data返回,给data变量。并且同时将data赋值给vm._data属性了。
有一个疑问?
程序执行到这里,为什么要给vm扩展一个_data属性呢?
_data属性,以"_"开始,足以说明,这个属性是人家Vue框架底层需要访问的。
Vue框架底层它使用vm._data这个属性干啥呢?
vm._data是啥?
vm._data 是:{
name : 'jackson',
age : 35
}
vm._data 这个属性直接指向了底层真实的data对象。通过_data访问的name和age是不会走数据代理机制的。
通过vm._data方式获取name和age的时候,是不会走getter和setter方法的。
注意:对于Vue实例vm来说,不仅有_data这个属性,还有一个$data这个属性。
_data 是框架内部使用的,可以看做私有的。
$data 这是Vue框架对外公开的一个属性,是给我们程序员使用。
3. 重点函数:
function isReserved(str) {
var c = (str + '').charCodeAt(0);
return c === 0x24 || c === 0x5f;
}
这个函数是用来判断字符串是否以 _ 和 $ 开始的。
true表示以_或$开始的。
false表示不是以_或$开始的。
4. proxy(vm, "_data", key);
通过这行代码直接进入代理机制(数据代理)。
5. 重点函数proxy
function proxy(target, sourceKey, key) { // target是vm,sourceKey是"_data",key是"age"
sharedPropertyDefinition.get = function proxyGetter() {
return this["_data"]["age"];
};
sharedPropertyDefinition.set = function proxySetter(val) {
this["_data"]["age"] = val;
};
Object.defineProperty(vm, 'age', sharedPropertyDefinition);
}
-->
<!-- 容器 -->
<div id="app">
<h1>姓名:{{name}}</h1>
<h1>年龄:{{age}}岁</h1>
</div>
<!-- vue代码 -->
<script>
function isReserved(str) {
var c = (str + '').charCodeAt(0);
return c === 0x24 || c === 0x5f;
}
const vm = new Vue({
el: '#app',
data: {
name: 'jackson',
age: 35
}
})
// 如果我们程序员不想走代理的方式读取data,想直接读取data当中的数据,可以通过_data和$data属性来访问。
// 建议使用$data这个属性。
console.log('name = ' + vm.$data.name)
console.log('age = ' + vm.$data.age)
</script>
</body>
</html>
1.2.7、data也可以是一个函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>data也可以是一个函数</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
</div>
<script>
new Vue({
el: "#app",
data() {
return {
msg: "zhangsan"
};
}
});
// 关于配置项:enumerable、configurable
let phone = {
name: "IphoneX"
}
Object.defineProperty(phone, "color", {
value: "red",
// true表示可以进行遍历:Object.keys(phone)
enumerable: true,
// true表示是可以被删除的:delete phone.color
configurable: true
});
</script>
</body>
</html>

1.2.8、事件绑定:v-on,简写为:@
<!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>Vue的事件绑定</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!--
Vue事件处理:
1.指令的语法格式:
<标签 v-指令名:参数名="表达式">{{插值语法}}</标签>
“表达式”位置都可以写什么?
常量、JS表达式、Vue实例所管理的XXX
2. 在Vue当中完成事件绑定需要哪个指令呢?
v-on指令。
语法格式:
v-on:事件名="表达式"
例如:
v-on:click="表达式" 表示当发生鼠标单击事件之后,执行表达式。
v-on:keydown="表达式" 表示当发生键盘按下事件之后,执行表达式。
3. 在Vue当中,所有事件所关联的回调函数,需要在Vue实例的配置项methods中进行定义。
methods是一个对象:{}
在这个methods对象中可以定义多个回调函数。
4. v-on指令也有简写形式
v-on:click 简写为 @click
v-on:keydown 简写为 @keydown
v-on:mouseover 简写为 @mouseover
....
5. 绑定的回调函数,如果函数调用时不需要传递任何参数,小括号()可以省略。
6. Vue在调用回调函数的时候,会自动给回调函数传递一个对象,这个对象是:当前发生的事件对象。
7. 在绑定回调函数的时候,可以在回调函数的参数上使用 $event 占位符,Vue框架看到这个 $event 占位符之后,会自动将当前事件以对象的形式传过去。
-->
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
<!-- 使用javascript原生代码如何完成事件绑定。 -->
<button onclick="alert('hello')">hello</button>
<!-- 使用Vue来完成事件绑定 -->
<!-- 以下是错误的,因为alert()并没有被Vue实例管理。 -->
<!-- <button v-on:click="alert('hello')">hello</button> -->
<!-- 以下是错误的,因为sayHello()并没有被Vue实例管理。 -->
<!-- <button v-on:click="sayHello()">hello</button> -->
<!-- 正确的写法 -->
<button v-on:click="sayHello()">hello</button>
<!-- v-on指令的简写形式 -->
<button @click="sayHi()">hi button</button>
<button @click="sayHi($event, 'jack')">hi button2</button>
<!-- 绑定的回调函数,如果不需要传任何参数,小括号() 可以省略 -->
<button @click="sayWhat($event)">what button</button>
</div>
<!-- vue代码 -->
<script>
// 自定义一个函数
// function sayHello(){
// alert('hello')
// }
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue的事件绑定'
},
methods : {
// 回调函数
// sayHello : function(){
// alert('hello')
// }
// : function 可以省略
sayHello(){
alert('hello2')
},
sayHi(event, name){
console.log(name, event)
//alert("hi " + name)
},
// 参数是事件对象
sayWhat(event){
console.log(event)
console.log(event.target)
// 拿到按钮文本
console.log(event.target.innerText)
}
}
})
</script>
</body>
</html>
1.2.9、关于事件回调中的this,计数器例子(this就是Vue对象)
<!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>关于事件回调函数中的this</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
<h1>计数器:{{counter}}</h1>
<button @click="counter++">点击我加1-第一种实现</button>
<button @click="add()">点击我加1-第二种实现</button>
<button @click="add2()">点击我加1-(箭头函数)</button>
</div>
<!-- vue代码 -->
<script>
const vm = new Vue({
el: '#app',
data: {
msg: '关于事件回调函数中的this',
counter: 0
},
// 1.methods对象中的方法可以通过vm去访问吗?可以。
// 2.methods对象中的方法有没有做数据代理呢?没有。
methods: {
add() {
//counter++; // 错误的。
// 在这里需要操作counter变量?怎么办?
//console.log(vm === this) :true
this.counter++;
},
add2: () => {
//this.counter++;
//console.log(this === vm)
//箭头函数中没有this,箭头函数中的this是从父级作用域当中继承过来的。
//对于当前程序来说,父级作用域是全局作用域:window
console.log(this)
},
sayHi() {
alert('hi...')
}
}
})
</script>
</body>
</html>

1.2.10、事件修饰符
<!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>事件修饰符</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
<style>
.divList{
width: 300px;
height: 200px;
background-color: aquamarine;
overflow: auto;
}
.item{
width: 300px;
height: 200px;
}
</style>
</head>
<body>
<!--
Vue当中提供的事件修饰符:
.stop : 停止事件冒泡,等同于 event.stopPropagation()。
.prevent : 等同于 event.preventDefault() 阻止事件的默认行为。
.capture :添加事件监听器时使用事件捕获模式
添加事件监听器包括两种不同的方式:
一种是从内到外添加。(事件冒泡模式)
一种是从外到内添加。(事件捕获模式)
.self :这个事件如果是“我自己元素”上发生的事件,这个事件不是别人给我传递过来的事件,则执行对应的程序。
.once : 事件只发生一次
.passive :passive翻译为顺从/不抵抗。无需等待,直接继续(立即)执行事件的默认行为。
.passive 和 .prevent 修饰符是对立的。不可以共存。(如果一起用,就会报错。)
.prevent:阻止事件的默认行为。
.passive:解除阻止。
-->
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
<!-- 阻止事件的默认行为 -->
<a href="https://www.baidu.com" @click.prevent="yi">百度</a> <br><br>
<!-- 停止事件冒泡 -->
<div @click="san">
<div @click.stop="er">
<button @click="yi">事件冒泡</button>
</div>
</div>
<br><br>
<!-- 添加事件监听器时使用事件捕获模式 -->
<div @click.capture="san">
<!-- 这里没有添加.capture修饰符,以下这个元素,以及这个元素的子元素,都会默认采用冒泡模式。 -->
<div @click="er">
<button @click="yi">添加事件监听器的时候采用事件捕获模式</button>
</div>
</div>
<br>
<br>
<!-- .self修饰符 -->
<div @click="san">
<!-- 此处是button按钮向上冒泡传递古来的事件,所以不执行er -->
<div @click.self="er">
<button @click="yi">self修饰符</button>
</div>
</div>
<br>
<br>
<!-- 在Vue当中,事件修饰符是可以多个联合使用的。
但是需要注意:
@click.self.stop:先.self,再.stop
@click.stop.self:先.stop,再.self
-->
<div @click="san">
<div @click="er">
<button @click.self.stop="yi">self修饰符</button>
</div>
</div>
<br>
<br>
<!-- .once修饰符:事件只发生一次 -->
<button @click.once="yi">事件只发生一次</button>
<!-- .passive修饰符 -->
<div class="divList" @wheel.passive="testPassive">
<div class="item">div1</div>
<div class="item">div2</div>
<div class="item">div3</div>
</div>
</div>
<!-- vue代码 -->
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '事件修饰符'
},
methods : {
yi(event){
//alert('去百度!!!!!!')
// 手动调用事件对象的preventDefault()方法,可以阻止事件的默认行为。
// 在Vue当中,这种事件的默认行为可以不采用手动调用DOM的方式来完成,可以使用事件修饰符:prevent。
//event.preventDefault();
alert(1)
},
er(){
alert(2)
},
san(){
alert(3)
},
testPassive(event){
for(let i = 0; i < 100000; i++){
console.log('test passive')
}
// 阻止事件的默认行为
//event.preventDefault()
}
}
})
</script>
</body>
</html>

1.2.11、按键修饰符
<!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>按键修饰符</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
9个比较常用的按键修饰符:
.enter
.tab (必须配合keydown事件使用。)
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
怎么获取某个键的按键修饰符?
第一步:通过event.key获取这个键的真实名字。
第二步:将这个真实名字以kebab-case风格进行命名。
PageDown是真实名字。经过命名之后:page-down
按键修饰符是可以自定义的?
通过Vue的全局配置对象config来进行按键修饰符的自定义。
语法规则:
Vue.config.keyCodes.按键修饰符的名字 = 键值
系统修饰键:4个比较特殊的键
ctrl、alt、shift、meta
对于keydown事件来说:只要按下ctrl键,keydown事件就会触发。
对于keyup事件来说:需要按下ctrl键,并且加上按下组合键,然后松开组合键之后,keyup事件才能触发。
-->
<div id="app">
<h1>{{msg}}</h1>
回车键:<input type="text" @keyup.enter="getInfo"><br>
回车键(键值):<input type="text" @keyup.13="getInfo"><br>
delete键:<input type="text" @keyup.delete="getInfo"><br>
esc键:<input type="text" @keyup.esc="getInfo"><br>
space键:<input type="text" @keyup.space="getInfo"><br>
up键:<input type="text" @keyup.up="getInfo"><br>
down键:<input type="text" @keyup.down="getInfo"><br>
left键:<input type="text" @keyup.left="getInfo"><br>
right键:<input type="text" @keyup.right="getInfo"><br>
<!-- tab键无法触发keyup事件。只能触发keydown事件。 -->
tab键: <input type="text" @keyup.tab="getInfo"><br>
tab键(keydown): <input type="text" @keydown.tab="getInfo"><br>
PageDown键: <input type="text" @keyup.page-down="getInfo"><br>
huiche键: <input type="text" @keyup.huiche="getInfo"><br>
ctrl键(keydown): <input type="text" @keydown.ctrl="getInfo"><br>
ctrl键(keyup): <input type="text" @keyup.ctrl="getInfo"><br>
ctrl键(keyup): <input type="text" @keyup.ctrl.i="getInfo"><br>
</div>
<script>
// 自定义了一个按键修饰符:.huiche 。代表回车键。
Vue.config.keyCodes.huiche = 13
const vm = new Vue({
el : '#app',
data : {
msg : '按键修饰符'
},
methods : {
getInfo(event){
// 当用户键入回车键的时候,获取用户输入的信息。
//if(event.keyCode === 13){
console.log(event.target.value)
//}
console.log(event.key)
}
}
})
</script>
</body>
</html>

1.2.12、计算属性-反转字符串案例(computed)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算属性-反转字符串</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
执行步骤:
1、v-model绑定的数据发生变化
2、插值语法中的方法会重新执行,执行methods中的方法
3、方法中的this指向的是Vue实例,获取到最新的数据
4、方法中的代码会重新执行,获取到最新的数据,然后返回
-->
<div id="app">
<h1>{{message}}</h1><br>
输入字符串:<input type="text" v-model="reversedMessageInfo"><br>
<br>
<!-- 在插值语法中可以调用方法,小括号不能省略。这个方法需要是Vue实例所管理的。 -->
反转字符串-methods方式:{{reverseMessage_methods()}}<br>
<br>
反转字符串-compared方式:{{reverseMessage_computed}}<br>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
reversedMessageInfo: ''
// 方法也可以直接放在data中,但是不推荐这样做
// reverseMessage_methods(){
// return this.reversedMessageInfo.split('').reverse().join('');
// }
},
methods: {
// 反转字符串的方法
reverseMessage_methods() {
return this.reversedMessageInfo.split('').reverse().join('');
}
},
computed: {
// 反转字符串的方法
reverseMessage_computed() {
return this.reversedMessageInfo.split('').reverse().join('');
}
}
})
</script>
</body>
</html>

1.2.13、侦听属性-比较数字大小案例(watch)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>监听属性变化-数字大小比较案例</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
数字1:<input type="number" v-model="num1"><br>
数字2:<input type="number" v-model="num2"><br>
<hr>
比较数值大小-watch方式: {{compareResult}}
<br>
<!-- 此处需要注意的是,computed中的是计算属性,不是方法,所以不需要加括号。 -->
比较数值大小-computed方式: {{compareResult_computed}}
</div>
<script>
new Vue({
el: "#app",
data: {
msg: "监听属性变化-数值大小比较案例",
num1: 0,
num2: 0,
compareResult: ''
},
computed: {
compareResult_computed() {
let result = this.num1 - this.num2
if (result == 0) {
return this.num1 + ' = ' + this.num2
} else if (result > 0) {
return this.num1 + ' > ' + this.num2
} else {
return this.num1 + ' < ' + this.num2
}
}
},
watch: {
num1: {
// 设置immediate为true,可以在初始化的时候,调用一次handler方法。
immediate: true,
handler(val) {
let result = val - this.num2
if (result == 0) {
this.compareResult = val + ' = ' + this.num2
} else if (result > 0) {
this.compareResult = val + ' > ' + this.num2
} else {
this.compareResult = val + ' < ' + this.num2
}
}
},
num2: {
immediate: true,
handler(val) {
let result = this.num1 - val
if (result == 0) {
this.compareResult = this.num1 + ' = ' + val
} else if (result > 0) {
this.compareResult = this.num1 + ' > ' + val
} else {
this.compareResult = this.num1 + ' < ' + val
}
}
}
}
});
</script>
</body>
</html>

1.2.14、class样式绑定
分别有三种形式:字符串形式、数组形式、对象形式
字符串形式:适用场景(如果确定动态绑定的样式个数只有1个,但是名字不确定)
<!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>Class绑定之字符串形式</title>
<script src="../js/vue.js"></script>
<style>
.static{
border: 1px solid black;
background-color: aquamarine;
}
.big{
width: 200px;
height: 200px;
}
.small{
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- 静态写法 -->
<div class="static small">{{msg}}</div>
<br><br>
<button @click="changeBig()">变大</button>
<button @click="changeSmall()">变小</button>
<!-- 动态写法:动静都有 -->
<!-- 适用场景:如果确定动态绑定的样式个数只有1个,但是名字不确定。 -->
<div class="static" :class="c1">{{msg}}</div>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Class绑定之字符串形式',
c1 : 'small'
},
methods: {
changeBig(){
this.c1 = 'big'
},
changeSmall(){
this.c1 = 'small'
}
},
})
</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>Class绑定之数组形式</title>
<script src="../js/vue.js"></script>
<style>
.static {
border: 1px solid black;
width: 100px;
height: 100px;
}
.active {
background-color: green;
}
.text-danger {
color: red;
}
</style>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- 静态写法 -->
<div class="static active text-danger">{{msg}}</div>
<br>
<!-- 动态写法:动静结合 -->
<div class="static" :class="['active','text-danger']">{{msg}}</div>
<br>
<!-- 数组写法1 -->
<div class="static" :class="[c1, c2]">{{msg}}</div>
<br>
<!-- 数组写法1 -->
<!-- 适用场景:当样式的个数不确定,并且样式的名字也不确定的时候,可以采用数组形式。 -->
<div class="static" :class="classArray">{{msg}}</div>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: 'Class绑定之数组形式',
c1: 'active',
c2: 'text-danger',
classArray: ['active', 'text-danger']
}
})
</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>Class绑定之对象形式</title>
<script src="../js/vue.js"></script>
<style>
.static {
border: 1px solid black;
width: 100px;
height: 100px;
}
.active {
background-color: green;
}
.text-danger {
color: red;
}
</style>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- 动态写法:动静结合 -->
<!-- 对象形式的适用场景:样式的个数是固定的,样式的名字也是固定的,但是需要动态的决定样式用还是不用。 -->
<div class="static" :class="classObj">{{msg}}</div>
<br>
<div class="static" :class="{active:true,'text-danger':false}">{{msg}}</div>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Class绑定之对象形式',
classObj : {
// 该对象中属性的名字必须和样式名一致。
active : false,
'text-danger' : true
}
}
})
</script>
</body>
</html>

1.2.15、style样式绑定
<!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>Style绑定</title>
<script src="../js/vue.js"></script>
<style>
.static {
border: 1px solid black;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- 静态写法 -->
<div class="static" style="background-color: green;">{{msg}}</div>
<br>
<!-- 动态写法:字符串形式 -->
<div class="static" :style="myStyle">{{msg}}</div>
<br>
<!-- 动态写法:对象形式 -->
<div class="static" :style="{backgroundColor: 'gray'}">{{msg}}</div>
<br>
<div class="static" :style="styleObj1">{{msg}}</div>
<br>
<!-- 动态写法:数组形式 -->
<div class="static" :style="styleArray">{{msg}}</div>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: 'Style绑定',
myStyle: 'background-color: gray;',
styleObj1: {
backgroundColor: 'green'
},
styleArray: [
{ backgroundColor: 'green' },
{ color: 'red' }
]
}
})
</script>
</body>
</html>

1.2.16、条件渲染v-if、v-else-if、v-else
<!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>条件渲染</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- ------------------------------------------------------------------------------- -->
<!--
v-if指令的值:true/false
true: 表示该元素会被渲染到页面上。
false: 表示该元素不会被渲染到页面上。(注意:不是修改了CSS样式,是这个元素压根没有加载)
-->
<div v-if="false">{{msg}}</div>
<div v-if="2 === 1">{{msg}}</div>
<hr>
<!-- ------------------------------------------------------------------------------- -->
<!-- ------------------------------------------------------------------------------- -->
<button @click="counter++">点我加1</button>
<h3>{{counter}}</h3>
<!-- 提醒:v-if和v-else之间不能断开。 -->
<img :src="imgPath1" v-if="counter % 2 === 1">
<img :src="imgPath2" v-else>
<hr>
<!-- ------------------------------------------------------------------------------- -->
<!-- ------------------------------------------------------------------------------- -->
温度:<input type="number" v-model="temprature"><br><br>
<!-- 写法一:繁琐 -->
天气:<span v-if="temprature <= 10">寒冷</span>
<span v-if="temprature > 10 && temprature <= 25">凉爽</span>
<span v-if="temprature > 25">炎热</span>
<br>
<!-- 写法一:简洁 -->
天气:<span v-if="temprature <= 10">寒冷</span>
<span v-else-if="temprature <= 25">凉爽</span>
<span v-else>炎热</span>
<br><br><br>
<!-- ------------------------------------------------------------------------------- -->
<!--
v-show指令是通过修改元素的CSS样式的display属性来达到显示和隐藏的。
v-if和v-show应该如何选择?
1. 如果一个元素在页面上被频繁的隐藏和显示,建议使用v-show,因为此时使用v-if开销比较大。
2. v-if的优点:页面加载速度快,提高了页面的渲染效率。
-->
<div v-show="false">你可以看到我吗?</div>
<!-- ------------------------------------------------------------------------------- -->
<!-- ------------------------------------------------------------------------------- -->
<!-- template标签/元素只是起到占位的作用,不会真正的出现在页面上,也不会影响页面的结构。 -->
<template v-if="counter === 10">
<input type="text">
<input type="checkbox">
<input type="radio">
</template>
<!-- ------------------------------------------------------------------------------- -->
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: '条件渲染',
counter: 1,
imgPath1: '../img/1.jpg',
imgPath2: '../img/2.jpg',
// 温度
temprature: 0
}
})
</script>
</body>
</html>

1.2.17、列表渲染:v-for
<!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>列表渲染</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<h2>遍历对象的属性</h2>
<ul>
<li v-for="(value, propertyName) of user">
{{propertyName}},{{value}}
</li>
</ul>
<h2>遍历字符串</h2>
<ul>
<li v-for="(c,index) of str">
{{index}},{{c}}
</li>
</ul>
<h2>遍历指定的次数</h2>
<ul>
<li v-for="(num,index) of counter">
{{index}}, {{num}}
</li>
</ul>
<h2>遍历数组</h2>
<!-- 静态列表 -->
<ul>
<li>张三</li>
<li>李四</li>
<li>王五</li>
</ul>
<!-- 动态列表 -->
<ul>
<!--
1. v-for要写在循环项上。
2. v-for的语法规则:
v-for="(变量名,index) in/of 数组"
变量名 代表了 数组中的每一个元素
-->
<li v-for="fdsafds in names">
{{fdsafds}}
</li>
</ul>
<ul>
<li v-for="name of names">
{{name}}
</li>
</ul>
<ul>
<li v-for="(name,index) of names">
{{name}}-{{index}}
</li>
</ul>
<ul>
<li v-for="(vip,index) of vips">
会员名:{{vip.name}},年龄:{{vip.age}}岁
</li>
</ul>
<table>
<tr>
<th>序号</th>
<th>会员名</th>
<th>年龄</th>
<th>选择</th>
</tr>
<tr v-for="(vip,index) in vips">
<td>{{index+1}}</td>
<td>{{vip.name}}</td>
<td>{{vip.age}}</td>
<td><input type="checkbox"></td>
</tr>
</table>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '列表渲染',
names : ['jack','lucy','james'],
vips : [
{id:'111',name:'jack',age:20},
{id:'222',name:'lucy',age:30},
{id:'333',name:'james',age:40}
],
user : {
id : '111',
name : '张三',
gender : '男'
},
str : '动力节点',
counter : 10
}
})
</script>
</body>
</html>

1.2.18、列表过滤(文本检索):computed、watch

computed
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表过滤</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<input type="text" v-model="keyword" placeholder="请输入想要查找的姓名,">
<table>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>地址</th>
</tr>
<tr v-for="user in filterUser" ::key="user.id">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<td>{{user.address}}</td>
</tr>
</table>
</div>
<script>
new Vue({
el: '#app',
data: {
keyword: '',
msg: '列表过滤',
users: [
{ id: '100', name: '张三', age: '23', address: '北京丰台' },
{ id: '200', name: '李四', age: '24', address: '北京朝阳' },
{ id: '300', name: '王五', age: '25', address: '北京海淀' },
{ id: '400', name: '赵六', age: '26', address: '北京昌平' },
{ id: '500', name: '田七', age: '27', address: '北京大兴' },
{ id: '600', name: '张小三', age: '28', address: '北京大兴' }
]
},
// 字符串搜索之computed计算属性实现
computed: {
filterUser() {
return this.users.filter((user) => {
return user.name.indexOf(this.keyword) >= 0
})
}
}
});
</script>
</body>
</html>
watch
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表过滤</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<input type="text" v-model="keyword" placeholder="请输入想要查找的姓名">
<table>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>地址</th>
</tr>
<tr v-for="user in filterUser" ::key="user.id">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.age}}</td>
<td>{{user.address}}</td>
</tr>
</table>
</div>
<script>
new Vue({
el: '#app',
data: {
keyword: '',
msg: '列表过滤',
users: [
{ id: '100', name: '张三', age: '23', address: '北京丰台' },
{ id: '200', name: '李四', age: '24', address: '北京朝阳' },
{ id: '300', name: '王五', age: '25', address: '北京海淀' },
{ id: '400', name: '赵六', age: '26', address: '北京昌平' },
{ id: '500', name: '田七', age: '27', address: '北京大兴' },
{ id: '600', name: '张小三', age: '28', address: '北京大兴' }
],
filterUser:[]
},
// 字符串搜索之watch监听属性实现
watch: {
// 监听keyword的变化
keyword: {
immediate: true,
handler(val) {
this.filterUser = this.users.filter((user) => {
return user.name.indexOf(val) >= 0
})
}
}
}
});
</script>
</body>
</html>
1.2.19、表单数据收集

<!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>表单数据的收集</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- 阻止表单的默认提交行为,第一种:@submit.prevent -->
<form @submit.prevent="send">
用户名:<input type="text" v-model.trim="user.username"><br><br>
密码:<input type="password" v-model="user.password"><br><br>
年龄:<input type="number" v-model.number="user.age"><br><br>
性别:
男<input type="radio" name="gender" value="1" v-model="user.gender">
女<input type="radio" name="gender" value="0" v-model="user.gender"><br><br>
爱好:
<!-- 注意:对于checkbox来说,如果没有手动指定value,那么会拿这个标签的checked属性的值作为value -->
旅游<input type="checkbox" v-model="user.interest" value="travel">
运动<input type="checkbox" v-model="user.interest" value="sport">
唱歌<input type="checkbox" v-model="user.interest" value="sing"><br><br>
学历:
<select v-model="user.grade">
<option value="">请选择学历</option>
<option value="zk">专科</option>
<option value="bk">本科</option>
<option value="ss">硕士</option>
</select><br><br>
简介:
<!-- -->
<textarea cols="50" rows="15" v-model.lazy="user.introduce"></textarea><br><br>
<input type="checkbox" v-model="user.accept">阅读并接受协议<br><br>
<!-- 阻止表单的默认提交行为,第二种:@click.prevent -->
<!-- <button @click.prevent="send">注册</button> -->
<button>注册</button>
</form>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
user: {
username: '',
password: '',
age: '',
gender: '1',
interest: ['travel'],
grade: '',
introduce: '',
accept: ''
},
msg: '表单数据的收集'
},
methods: {
send() {
// 将数据收集好,发送给服务器。
console.log(JSON.stringify(this.$data))
console.log(JSON.stringify(this.user))
}
}
})
</script>
</body>
</html>
1.2.20、过滤器(在Vue3被弃用)
<!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>过滤器</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
需求:
从服务器端返回了一个商品的价格price,这个price的值可能是这几种情况:''、null、undefined、60.5
要求:
如果是''、null、undefined ,页面上统一显示为 -
如果不是 ''、null、undefined,则页面上显示真实的数字即可。
在Vue3当中,已经将过滤器语法废弃了。
-->
<div id="app">
<h1>{{msg}}</h1>
<h2>商品价格:{{formatPrice}}</h2>
<h2>商品价格:{{formatPrice2()}}</h2>
<h2>商品价格:{{price | filterA | filterB(3)}}</h2>
<input type="text" :value="price | filterA | filterB(3)">
</div>
<hr>
<div id="app2">
<h2>商品价格:{{price | filterA | filterB(3)}}</h2>
</div>
<script>
// 配置全局的过滤器。
Vue.filter('filterA', function(val){
if(val === null || val === undefined || val === ''){
return '-'
}
return val
})
Vue.filter('filterB', function(val, number){
return val.toFixed(number)
})
const vm2 = new Vue({
el : '#app2',
data : {
price : 20.3
}
})
const vm = new Vue({
el : '#app',
data : {
msg : '过滤器',
price : 50.6
},
methods: {
formatPrice2(){
if(this.price === '' || this.price === undefined || this.price === null){
return '-'
}
return this.price
}
},
computed : {
formatPrice(){
if(this.price === '' || this.price === undefined || this.price === null){
return '-'
}
return this.price
}
},
/* filters : {
// 局部过滤器
filterA(val){
if(val === null || val === undefined || val === ''){
return '-'
}
return val
},
filterB(val, number){
// 确保传递过来的数据val,保留两位小数。
return val.toFixed(number)
}
} */
})
</script>
</body>
</html>

1.2.21、Vue的其他指令(v-text、v-html、v-once)
<!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>Vue的其它指令</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}},test</h1>
<!--
v-text指令:
可以将指令的内容拿出来填充到标签体当中。和JS的innerText一样。
这种填充是以覆盖的形式进行的。先清空标签体当中原有的内容,填充新的内容。
即使内容是一段HTML代码,这种方式也不会将HTML代码解析并执行。只会当做普通
文本来处理。
-->
<h1 v-text="msg">test</h1>
<h1 v-text="name">test</h1>
<h1 v-text="s1"></h1>
<!--
v-html指令:
和v-text一样,也是填充标签体内容。也是采用覆盖的形式进行。
只不过v-html会将内容当做一段HTML代码解析并执行。
-->
<h1 v-html="s1"></h1>
<br><br>
使用了v-once指令,那么这个元素只会渲染一次。
<ul>
<li v-for="user,index of users" :key="index" v-once>
{{user}}
</li>
</ul>
<ul>
<li v-for="user,index of users" :key="index">
{{user}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue的其它指令',
name : 'jack',
s1 : '<h1>欢迎大家学习Vue!</h1>',
users : ['jack', 'lucy', 'james']
}
})
</script>
</body>
</html>

1.2.22、自定义指令(directives)
例:
- text-danger:将属性值设置为红色
- bind-blue:设置属性的父级元素背景色
关于自定义指令的区别:
- 函数式:功能简洁
- 对象式:功能更加细粒化
<!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>自定义指令</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>自定义指令</h1>
<div v-text="msg"></div>
<div v-text-danger="msg"></div>
用户名:<input type="text" v-bind:value="username">
<!--
需要一个指令,可以和v-bind指令完成相同的功能,同时将该元素的父级元素的背景色设置为蓝色。
-->
<div>
用户名:<input type="text" v-bind-blue="username">
</div>
</div>
<hr>
<hr>
<div id="app2">
<div v-text-danger="msg"></div>
<div>
用户名:<input type="text" v-bind-blue="username">
</div>
</div>
<script>
// 定义全局的指令
// 函数式
Vue.directive('text-danger', function (element, binding) {
//对于自定义指令来说,函数体当中的this是window,而不是vue实例。
console.log(this)
element.innerText = binding.value
element.style.color = 'red'
})
// 对象式
Vue.directive('bind-blue', {
// 元素与指令初次绑定的时候,自动调用bind
bind(element, binding) {
element.value = binding.value
console.log(this)
},
// 元素被插入到页面之后,这个函数自动被调用。
inserted(element, binding) {
element.parentNode.style.backgroundColor = 'skyblue'
console.log(this)
},
// 当模板重新解析的时候,这个函数会被自动调用。
update(element, binding) {
element.value = binding.value
console.log(this)
}
})
const vm2 = new Vue({
el: '#app2',
data: {
msg: '欢迎学习Vue框架!',
username: 'lucy'
}
})
const vm = new Vue({
el: '#app',
data: {
msg: '自定义指令',
username: 'jackson'
},
directives: {
// 指令1
// 指令2
// ...
// 关于指令的名字:1. v- 不需要写。 2. Vue官方建议指令的名字要全部小写。如果是多个单词的话,请使用 - 进行衔接。
// 这个回调函数的执行时机包括两个:第一个:标签和指令第一次绑定的时候。第二个:模板被重新解析的时候。
// 这个回调函数有两个参数:第一个参数是真实的dom元素。 第二个参数是标签与指令之间绑定关系的对象。
// 这种方式属于函数式方式。
/* 'text-danger' : function(element, binding){
console.log('@')
element.innerText = binding.value
element.style.color = 'red'
}, */
/* 'bind-blue' : function(element, binding){
element.value = binding.value
console.log(element)
// 为什么是null,原因是这个函数在执行的时候,指令和元素完成了绑定,但是只是在内存当中完成了绑定,元素还没有被插入到页面当中。
console.log(element.parentNode)
element.parentNode.style.backgroundColor = 'blue'
} */
// 对象式
/* 'bind-blue' : {
// 这个对象中三个方法的名字不能随便写。
// 这三个函数将来都会被自动调用。
// 元素与指令初次绑定的时候,自动调用bind
// 注意:在特定的时间节点调用特定的函数,这种被调用的函数称为钩子函数。
bind(element, binding){
element.value = binding.value
},
// 元素被插入到页面之后,这个函数自动被调用。
inserted(element, binding){
element.parentNode.style.backgroundColor = 'blue'
},
// 当模板重新解析的时候,这个函数会被自动调用。
update(element, binding){
element.value = binding.value
}
} */
}
})
</script>
</body>
</html>

1.2.23、响应式与数据劫持
<!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>响应式与数据劫持</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<div>姓名:{{name}}</div>
<div>年龄:{{age}}岁</div>
<div>数字:{{a.b.c.e}}</div>
<div>邮箱:{{a.email}}</div>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '响应式与数据劫持',
name : 'jackson',
age : 20,
a : {
b : {
c : {
e : 1
}
}
}
}
})
// 测试:后期给Vue实例动态的追加的一些属性,会添加响应式处理吗?
// 目前来看,通过这种方式后期给vm追加的属性并没有添加响应式处理。
//vm.$data.a.email = 'jack@126.com'
// 如果你想给后期追加的属性添加响应式处理的话,调用以下两个方法都可以:
// Vue.set() 、 vm.$set()
//Vue.set(目标对象, 属性名, 属性值)
//Vue.set(vm.$data.a, 'email', 'jack@126.com')
//Vue.set(vm.a, 'email', 'jack@123.com')
vm.$set(vm.a, 'email', 'jack@456.com')
// 避免在运行时向Vue实例或其根$data添加响应式
// 不能直接给vm / vm.$data 追加响应式属性。只能在声明时提前定义好。
//Vue.set(vm, 'x', '1')
//Vue.set(vm.$data, 'x', '1')
</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>数组的响应式处理</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
1. 通过数组的下标去修改数组中的元素,默认情况下是没有添加响应式处理的。怎么解决?
2. 第一种方案:
vm.$set(数组对象, 下标, 值)
Vue.set(数组对象, 下标, 值)
3. 第二种方案:
push():在数组的末尾添加一个或多个元素,并返回新的长度。
vm.users.push('jackson')
pop():删除数组的最后一个元素,并返回该元素。
vm.users.pop()
reverse():颠倒数组中元素的顺序。
vm.users.reverse()
splice():从数组中替换元素。
vm.users.splice(下标, 替换的数量, 替换的元素),
vm.users.splice(1, 1, 'jackson')
shift():删除数组的第一个元素,并返回该元素。
vm.users.shift()
unshift():在数组的开头添加一个或多个元素,并返回新的长度。
vm.users.unshift('jackson')
sort():对数组的元素进行排序,按照字母或数字的顺序。
vm.users.sort()
在Vue当中,通过以上的7个方法来给数组添加响应式处理。
-->
<div id="app">
<h1>{{msg}}</h1>
<ul>
<li v-for="user in users">
{{user}}
</li>
</ul>
<ul>
<li v-for="vip in vips" :key="vip.id">
{{vip.name}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '数组的响应式处理',
users : ['jack', 'lucy', 'james'],
vips : [
{id:'111', name:'zhangsan'},
{id:'222', name:'lisi'}
]
}
})
</script>
</body>
</html>

1.2.24、Vue生命周期
<!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>Vue的生命周期</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<h3>计数器:{{counter}}</h3>
<h3 v-text="counter"></h3>
<button @click="add()">点我加1</button>
<button @click="destroy()">点我销毁</button>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
methods: {
add(){
console.log('add....')
this.counter++
},
destroy(){
// 销毁vm
this.$destroy()
},
/* m(){
console.log('m....')
} */
},
watch : {
counter(){
console.log('counter被监视一次!')
}
},
/*
1.初始阶段
el有,template也有,最终编译template模板语句。
el有,template没有,最终编译el模板语句。
el没有的时候,需要手动调用 vm.$mount(el) 进行手动挂载,然后流程才能继续。此时如果template有,最终编译template模板语句。
el没有的时候,需要手动调用 vm.$mount(el) 进行手动挂载,然后流程才能继续。此时如果没有template,最终编译el模板语句。
结论:
流程要想继续:el必须存在。
el和template同时存在,优先选择template。如果没有template,才会选择el。
*/
beforeCreate() {
// 创建前
// 创建前指的是:数据代理和数据监测的创建前。
// 此时还无法访问data当中的数据。包括methods也是无法访问的。
console.log('beforeCreate', this.counter)
// 调用methods报错了,不存在。
//this.m()
},
created() {
// 创建后
// 创建后表示数据代理和数据监测创建完毕,可以访问data中的数据了。
console.log('created', this.counter)
// 可以访问methods了。
//this.m()
},
// 2.挂载阶段
beforeMount() {
// 挂载前
console.log('beforeMount')
},
mounted() {
// 挂载后
console.log('mounted')
console.log(this.$el)
console.log(this.$el instanceof HTMLElement)
},
// 3.更新阶段
beforeUpdate() {
// 更新前
console.log('beforeUpdate')
},
updated() {
// 更新后
console.log('updated')
},
// 4.销毁阶段
beforeDestroy() {
// 销毁前
console.log('beforeDestroy')
console.log(this)
this.counter = 1000
},
destroyed() {
// 销毁后
console.log('destroyed')
console.log(this)
},
})
</script>
</body>
</html>

1.3、Vue组件化开发
1.3.1、第一个组件程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>第一个组件程序</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<!-- 3、使用组件 -->
<my-component></my-component>
<userlist></userlist>
<userlogin></userlogin>
</div>
<hr>
<div id="app2">
<userlogin></userlogin>
</div>
<script>
// ##########################1、创建组件###########################################
/* let myComponent = Vue.extend(
{
template: `
<ul>
<li v-for="user,index in users" :key="user.id">
{{user.id}}--{{user.name}}--{{user.age}}
</li>
</ul>
`,
data() {
return {
users: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 },
]
}
}
}
); */
// 创建组件简写
let myComponent = {
template: `
<ul>
<li v-for="user,index in users" :key="user.id">
{{user.id}}--{{user.name}}--{{user.age}}
</li>
</ul>
`,
data() {
return {
users: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 },
]
}
}
}
let myUserLogin = Vue.extend({
template: `
<div>
<h1>用户登录</h1>
<form @submit.prevent="login">
用户名:<input type="text" v-model="username"> <br>
密码:<input type="password" v-model="password" autocomplete><br>
<input type="submit" value="登录">
</form>
</div>
`,
data() {
return {
username: '',
password: ''
}
},
methods: {
// 阻止表单默认提交
login() {
alert(this.username + "," + this.password)
}
}
});
// ##########################创建组件###########################################
// 2、注册组件(全局),写在组件创建之后
Vue.component('userlogin', myUserLogin)
// ##########################创建Vue实例###########################################
let myVue = new Vue({
el: '#app',
data: {
msg: '第一个组件程序',
users: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 },
]
},
// 2、注册组件(局部)
components: {
myComponent,
// 起别名
userlist: myComponent
}
})
// 创建Vue实例
let myVue2 = new Vue({
el: '#app2'
})
// ##########################创建Vue实例###########################################
</script>
</body>
</html>

1.3.2、组件嵌套
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件的嵌套</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 3、使用组件 -->
<app></app>
</div>
<script>
let x1 = {
template: `
<div>
<h1>x1组件</h1>
</div>
`
}
let x = {
template: `
<div>
<h1>x组件</h1>
<!--使用组件-->
<x1></x1>
</div>
`,
components: {
x1
}
}
let y1 = {
template: `
<div>
<h1>y1组件</h1>
</div>
`
}
let y = {
template: `
<div>
<h1>y组件</h1>
<!--使用组件-->
<y1></y1>
</div>
`,
components: {
y1
}
}
// 1、创建组件
let app = {
template: `
<div>
<h1>app组件</h1>
<!--使用组件-->
<x></x>
<y></y>
</div>
`,
components: {
x, y
}
};
new Vue({
el: '#root',
// 2、注册组件
components: {
app
}
});
</script>
</body>
</html>

1.4、Vue脚手架
1.4.1、NodeJs安装
https://www.cnblogs.com/zhangzhixi/p/17642325.html
1.4.2、Vue脚手架安装
Vue的脚手架(Vue CLI: Command Line Interface)是Vue官方提供的标准化开发平台。
它可以将我们.vue的代码进行编译生成html css js代码,并且可以将这些代码自动发布到它自带的服务器上,为我们Vue的开发提供了一条龙服务。
脚手架官网地址:https://cli.vuejs.org/zh
安装脚手架
npm install -g @vue/cli
创建项目(自带HelloWorld案例)
vue create vue_pro
这里选择Vue2,
- babel:负责ES6语法转换成ES5。
- eslint:负责语法检查的。
回车之后,就开始创建项目,创建脚手架环境(内置了webpack loader),自动生成HelloWorld案例。


编译Vue程序
cd vue_pro npm run serve

访问程序:
1.4.3、脚手架目录结构
- package.json:包的说明书(包的名字,包的版本,依赖哪些库)。该文件里有webpack的短命令:
- serve(启动内置服务器)
- build命令是最后一次的编译,生成html css js,给后端人员
- lint做语法检查的。

1.4.4、分析脚手架HelloWorld程序

可以看到在index.html中只有一个容器。没有引入vue.js,也没有引入main.js
Vue脚手架可以自动找到main.js文件。(所以main.js文件名不要修改,位置也不要随便移动)

接下来就是将之前写的程序拷贝到脚手架中,进行测试。
需要拷贝过来的是:App.vue、X.vue、Y.vue、X1.vue、Y1.vue。
main.js和index.html都不需要了,因为脚手架中有。

只需要将App.vue中的路径修改一下即可:

打开VSCode终端:ctrl + `
在终端中执行:npm run serve
报错了:

导致这个错误的原因是:组件的名字应该由多单词组成。这是eslint进行的es语法检测。
解决这个问题有两种方案:
第一种:把所有组件的名字修改一下。
第二种:在vue.config.js文件中进行脚手架的默认配置。配置如下:

在终端中ctrl + c 两次,终止之前的服务,再次运行命令:npm run serve

1.4.5、props配置项
使用props配置可以接收其他组件传过来的数据,让组件的数据变为动态数据,三种接收方式:
(1) 简单接收
props : [‘name’,’age’,’sex’]
(2) 接收时添加类型限制
props: {
name: String,
age: Number,
sex: String
}
(3) 接收时添加类型限制,必要性限制,默认值
props : {
name : {
type : Number,
required : true
},
age : {
type : Number,
default : 10
},
sex : {
type : String,
default : '男'
}
}
在上面的案例中,通常我们给属性赋值是通过在data中给属性赋值,如下所示:

可以看到上面的程序是有问题,如果我们想要数据是动态的话,这时候需要一个个修改其属性值,比较繁琐麻烦。
有没有可以在标签使用时动态给属性传参呢?这时候就可以通过props配置项:
car.vue:
<template>
<div>
<h3>品牌:{{brand}}</h3>
<h3>价格:{{price}}</h3>
<h3>颜色:{{color}}</h3>
</div>
</template>
<script>
export default {
/* data() {
return {
brand: '奔驰',
price: 1000000,
color: '黑色'
}
} */
// props接收父组件传递过来的数据
props: ['brand', 'price', 'color']
}
</script>
<style>
</style>
App.vue:
<template>
<div>
<h1>我是App组件</h1>
<!-- 传递属性值 -->
<car brand="奔驰" :price="100000" color="黑色"></car>
<hr />
<car brand="宝马" :price="200000" color="白色"></car>
<hr />
<car brand="奥迪" :price="300000" color="红色"></car>
</div>
</template>
<script>
import car from "./components/car.vue";
export default {
components: {
car,
},
};
</script>
<style>
</style>

1.4.6、在父组件中获取子组件信息:ref
比如上个例子,App.vue可以看做父组件、car.vue看作是子组件,想在父组件中获取子组件中的属性值等信息,就可以使用到标签体中的ref属性。
1、打标记
<car ref="benzCar" brand="奔驰" :price="100000" color="黑色"></car>
2、获取值
// 获取子组件 console.log(this.$refs.benzCar); // 获取子组件的属性 console.log(this.$refs.benzCar.brand); // 获取dom元素 console.log(this.$refs.h1Msg.innerText);
App.vue
<template>
<div>
<h1 ref="h1Msg">{{ msg }}</h1>
<!-- 传递属性值 -->
<car ref="benzCar" brand="奔驰" :price="100000" color="黑色"></car>
<hr />
<car ref="bmwCar" brand="宝马" :price="200000" color="白色"></car>
<hr />
<car brand="奥迪" :price="300000" color="红色"></car>
<button @click="printCarInfo()" value="点我获取汽车属性">
点我获取汽车属性
</button>
</div>
</template>
<script>
import car from "./components/car.vue";
export default {
data() {
return {
msg: "我是App组件",
};
},
components: {
car,
},
methods: {
printCarInfo() {
// 获取子组件
console.log(this.$refs.benzCar);
console.log(this.$refs.bmwCar);
// 获取子组件的属性
console.log(this.$refs.benzCar.brand);
console.log(this.$refs.bmwCar.brand);
// 获取dom元素
console.log(this.$refs.h1Msg.innerText);
},
},
};
</script>
<style>
</style>

1.4.7、mixins配置(混入)(代码复用)
如图所示,其中两段方法是一样的,是否可以进行优化,将代码复用呢?

运行效果:

可以看到以上vip.vue和user.vue代码中都有相同的methods,这个代码可以复用吗?可以使用mixins配置进行混入。实现步骤:
第一步:创建混入
单独定义一个mixin.js(一般和main.js在同级目录),代码如下:
// 1、定义一个混入对象
export const mix1 = {
methods: {
printInfo() {
console.log(this.name, this.age);
}
}
}
2、引入和使用混入


全局混入:

1.4.8、局部样式(scoped)
如下所示定义了两个相同名称的class,并分别设置背景颜色为红色和蓝色

这时候在页面上面显示的是红色的背景色,原因是因为在App.vue中先引入了user.vue,如果先引入vip.vue,那么背景色就会都变为蓝色。
如何解决呢?想要每个组件拥有自己的样式:只需要在style中添加scoped标签即可

Vue-高级部分
2.1、Vue与Ajax
2.1.1、回顾发送AJax异步请求的方式
发送AJAX异步请求的常见方式包括:
1. 原生方式,使用浏览器内置的JS对象XMLHttpRequest
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){}
xhr.open()
xhr.send()
2. 原生方式,使用浏览器内置的JS函数fetch
fetch(‘url’, {method : ‘GET’}).then().then()
3. 第三方库方式,JS库jQuery(对XMLHttpRequest进行的封装)
$.get() $.post()
4. 第三方库方式,基于Promise的HTTP库:axios (对XMLHttpRequest进行的封装),axios是Vue官方推荐使用的。
axios.get('').then(
response => {
console.log('服务器响应回来的数据:', response.data)
},
error => {
console.log('错误信息:', error.message)
}
)
2.1.2、回顾AJax跨域
1.什么是跨域访问
(1) 在a页面中想获取b页面中的资源,如果a页面和b页面所处的协议、域名、端口不同(只要有一个不同),所进行的访问行动都是跨域的。
(2) 哪些跨域行为是允许的?
① 直接在浏览器地址栏上输入地址进行访问
② 超链接
③ <img src=”其它网站的图片是允许的”>
④ <link href=”其它网站的css文件是允许的”>
⑤ <script src=”其它网站的js文件是允许的”>
⑥ ......
(3) 哪些跨域行为是不允许的?
① AJAX请求是不允许的
② Cookie、localStorage、IndexedDB等存储性内容是不允许的
③ DOM节点是不允许的
2.AJax无法跨域的原因:同源策略
(1)
同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。
同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
(2)
AJAX请求不允许跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
3.解决AJax跨域的方案包括哪些
(1) CORS方案(工作中常用的)
这种方案主要是后端的一种解决方案,被访问的资源设置响应头,告诉浏览器我这个资源是允许跨域访问的:
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
(2) jsonp方案(面试常问的)
采用的是<script src=””>不受同源策略的限制来实现的,但只能解决GET请求。
(3) 代理服务器方案(工作中常用的)
Nginx反向代理
Node中间件代理
vue-cli(Vue脚手架自带的8080服务器也可以作为代理服务器,需要通过配置vue.config.js来启用这个代理)
(4) postMesssage
(5) websocket
(6) window.name + iframe
(7) location.hash + iframe
(8) document.domain + iframe
(9) ......
2.1.3、演示跨域
Vue脚手架内置服务器的地址:http://localhost:8080
SpringBoot1项目-User地址:http://localhost:8000/getUserList
package com.zhixi.controller; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; /** * @author zhangzhixi * @version 1.0 * @description * @date 2023-09-01 9:10 */ @RestController public class UserController { @GetMapping("/getUserList") public List<User> getUserList(){ List<User> userList = new ArrayList<>(); userList.add(new User("张三", 23, "河南信阳")); userList.add(new User("李四", 24, "河南南阳")); userList.add(new User("王五", 25, "河南濮阳")); return userList; } } @Data @NoArgsConstructor @AllArgsConstructor class User{ private String name; private Integer age; private String address; }
SpringBoot2项目-Vip地址:http://localhost:8001/getVipList
package com.zhixi.controler; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; /** * @author zhangzhixi * @version 1.0 * @description * @date 2023-09-01 9:17 */ @RestController public class VipController { @GetMapping("/getVipList") public List<Vip> getVipList() { List<Vip> vipList = new ArrayList<>(); vipList.add(new Vip("马云", 58, "浙江杭州")); vipList.add(new Vip("刘强东", 49, "江苏宿迁")); vipList.add(new Vip("雷军", 53, "湖北仙桃")); return vipList; } } @Data @NoArgsConstructor @AllArgsConstructor class Vip { private String name; private Integer age; private String address; }

1.安装axios库
npm i axios
2.引入axios库
3.发送ajax请求
import axios from "axios";
mounted() {
// 发送ajax请求
axios.get("http://localhost:8000/getUserList").then(
(response) => {
console.log(response.data);
},
(error) => {
console.log(error.message);
}
);

以上的访问表示:在8080服务器(Vue)中发送AJAX请求访问8000服务器,必然会出现AJAX跨域问题:

2.1.4、解决跨域
在Vue中解决跨域的方法是:启用Vue脚手架内置服务器8080的代理功能。
简单开启:
vue.config.js文件中添加如下配置:
devServer: {
// 后端服务器:协议、IP、端口
// 含义:Vue脚手架内置的8080服务器负责代理访问8000服务器
proxy: 'http://localhost:8000'
}
发送ajax时,地址需要修改为如下:

刷新页面,可以看到成功打印出来了user信息:

如何验证会先走vue项目8080的请求呢?在public下新建一个文件,文件名为getUserList,写入内容随意,然后再次刷新,可以看到走的是vue服务器8080的请求了:

高级开启(常用)
vue.config.js:
devServer: {
proxy: {
// 后端服务,访问前缀
'/api': {
// 后端服务,协议、IP、端口
target: 'http://localhost:8000',
pathRewrite: { '^/api': '' }, // 重写路径,正则表达式去除前缀,因为后端服务器是没有/api访问前缀的
ws: true, // 支持websocket
changeOrigin: true // true表示改变起源(让目标服务器不知道真正的起源)
},
'/abc': {
target: 'http://localhost:8001',
pathRewrite: { '^/abc': '' },
ws: true, // 默认值true
changeOrigin: true // 默认值true
}
}
}
页面:新建按钮,分别访问8000和8001服务器资源:
<button @click="getUserList()">获取普通用户列表信息</button> <br /> <button @click="getVipList()">获取Vip用户列表信息</button>
methods: {
getUserList() {
// 当前按钮这个页面就是在8080服务器上,又去访问8080服务器上的资源,所以http://localhost:8080可以省略。
axios.get("/api/getUserList").then(
(response) => {
console.log("服务器响应回来的数据:", response.data);
},
(error) => {
console.log("错误信息:", error.message);
}
);
},
getVipList() {
axios.get("/abc/getVipList").then(
(response) => {
console.log("服务器响应回来的数据:", response.data);
},
(error) => {
console.log("错误信息:", error.message);
}
);
}
}
访问到资源,成功解决跨域问题:

2.2、实现城市搜索展示天气预报功能
效果:

2.3、vuex
2.3.1、vuex概述
1. vuex是实现数据集中式状态管理的插件。数据由vuex统一管理。其它组件都去使用vuex中的数据。只要有其中一个组件去修改了这个共享的数据,其它组件会同步更新。
一定要注意:全局事件总线和vuex插件的区别:
- (1) 全局事件总线关注点:组件和组件之间数据如何传递,一个绑定$on,一个触发$emit。数据实际上还是在局部的组件当中,并没有真正的让数据共享。只是数据传来传去。
-
(2) vuex插件的关注点:共享数据本身就在vuex上。其中任何一个组件去操作这个数据,其它组件都会同步更新。是真正意义的数据共享。
-
(3) 使用vuex的场景是:多个组件之间依赖于同一状态。来自不同组件的行为需要变更同一状态。

2.3.2、vuex环境搭建
1. 安装vuex
# 因为Vue和vuex版本没有对应上,这里是需要注意的 vue2安装vuex3版本:npm i vuex@3 vue3安装vuex4版本:npm i vuex@4
2. 创建目录和js文件(目录和文件名不是必须叫这个)
(1) 目录:vuex
(2) js文件:store.js
vuex/store.js
// 引入Vue,因为下面使用Vuex插件的时候需要Vue
import Vue from 'vue'
// 引入vuex插件
import Vuex from 'vuex'
// 使用插件
Vue.use(Vuex)
// 创建三个vuex插件的核心对象:actions对象、mutations对象、state对象
const actions = {}
const mutations = {}
const state = {}
// 简写形式
export default new Vuex.Store({actions,mutations,state})
/* // 创建store对象(这个store对象是vuex插件中的老大,最核心的对象,这个对象store是用来管理actions对象、mutations对象、state对象。)
const store = new Vuex.Store({
// 它是一个负责执行某个行为的对象
actions : actions,
// 它是一个负责更新的对象
mutations : mutations,
// 它是一个状态对象
state : state
})
// 导出store对象(暴露之后,别人想用可以使用import进行引入)
export default store */
3.暴漏vuex核心对象store
在store.js文件中创建核心store对象,并暴露
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 引入Vuex插件中的核心对象store
import store from './vuex/store'
new Vue({
el : '#app',
// 一个全新的配置项,以前没有学过:store
// 加上这个配置项之后,vm以及所有的vc对象上都会多一个属性:$store
// 以后通过vm.$store或者vc.$store来获取store对象。
//store : store,
store,
render : h => h(App),
})
2.3.3、vuex实现点我加一功能
<template>
<div>
<h1>数字:{{$store.state.num}}</h1>
<button @click="plusOne()">点我加1</button>
</div>
</template>
<script>
export default {
name : 'App',
data() {
return {
startNum : 0
}
},
methods: {
plusOne(){
// 这里的代码在实际开发中可能会比较复杂。
// 业务逻辑复杂,代码比较多。
// 如果你将这些代码放到这里的话,这些业务逻辑代码无法得到复用。
// 无法在其他组件中使用,在其他组件中使用的时候,你还需要把这些代码再写一遍。
//this.$store.state.num++
// 调用vuex的API。
// dispatch是vuex的API。调用这个方法之后,store对象中的plusOne这个action回调函数会被自动调用。
// dispatch:分发
// 交给plusOne这个action去处理这个事儿。
this.$store.dispatch('plusOne', this.startNum)
}
},
}
</script>
// 引入Vue,因为下面使用Vuex插件的时候需要Vue import Vue from 'vue' // 引入vuex插件 import Vuex from 'vuex' // 使用插件 Vue.use(Vuex) // 创建三个vuex插件的核心对象:actions对象、mutations对象、state对象 const actions = { // N多个action // 每一个action都是一个callback(回调函数) // 在action这种回调函数中编写复杂的业务逻辑 // 有个原则:action是专门用来处理业务逻辑,或者说发送AJAX请求的。 //plusOne : function(){} // 简写 // context参数:context是vuex的上下文(context可以看做是store对象的缩小版) // value参数:传过来的数据 plusOne(context, value){ // 处理业务 value = value + 1 // 调用其它的action这个回调函数 //context.dispatch('otherAction', value) // 业务逻辑处理完成之后,继续向下一个环节走,就轮到了数据的更新。 // 提交上下文环境(所有的事儿都做完了,该最后一步了,更新数据了,怎么办?提交) context.commit('PLUS_ONE', value) }, // 这里可能还会有其它的action // ... /* otherAction(context, value){ console.log(6666) } */ } const mutations = { // N多个mutation // 每一个mutation都是一个callback(回调函数) // 每一个mutation这个回调函数的作用就是:更新state // 只要state一更新,因为是响应式的,所以页面就重新渲染了。 // state参数:状态对象 // value参数:上一环节传过来的数据 PLUS_ONE(state, value){ state.num += value } } // 等同于Vue当中的data(只不过这里我们不叫做数据,叫做状态) // 状态对象(数据对象),已经做了响应式处理的。 const state = { num : 0 } // 简写形式 export default new Vuex.Store({actions,mutations,state})

浙公网安备 33010602011771号