🍑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})