一、原生DOM vs 函数库 vs 框架 |
|||||||||||||||
原生DOM: 优点: 跨平台好 缺点: 繁琐 函数库jQuery: 优点: 对原生DOM的每个API进行了简化 缺点: 并没有简化开发的步骤 框架Vue,AngularJS,React: 优点: 彻底简化了开发的步骤 缺点: 需要转变观念 |
|||||||||||||||
二、概述 |
|||||||||||||||
什么是框架:已经是一个半成品的项目,封装了大量重复性劳动,人只要提供个性化定制即可。 1. 什么是Vue:Vue是基于MVVM设计模式的渐进式的前端js框架 渐进式: 可以有选择的逐步使用框架中的组件 vs"全家桶": 必须全盘使用所有组件 前端js框架: 不需要nodejs,仅靠浏览器就可独立运行 为什么后续需要nodejs?将Vue框架中浏览器不认识的新技术翻译为浏览器认识的ES5的对等标准——已经封装好了 |
|||||||||||||||
2.何时:vue侧重于以数据操作为主的前端项目开发(增删改查加事件绑定) |
|||||||||||||||
3. 如何使用Vue:下载: cn.vuejs.org 最新稳定版: 2.6 开发版: 未压缩的,包含完备注释和错误提示信息 优: 可读性好 缺: 体积大,不便于传输 生产版: 代码经过压缩,删除了所有注释和错误提示信息 优: 体积最小化,便于传输 缺: 不便于学习和阅读 使用Vue 2种方式: 1). 下载独立的vue.js,在网页中引入: 2). 用脚手架代码: 版本:2.5 开发版:未压缩版本,有完备的错误提示 生产版:压缩版本,删除了错误提示 |
|||||||||||||||
三、MVVM框架 |
|||||||||||||||
原理: MVVM设计模式: 1.旧的前端代码划分:html: 定义网页的内容,专门保存网页内容和结构的文档 css: 定义网页的样式 js: 为网页添加交互行为(增删改查,事件绑定) 问题:HTML和css太蠢,不会动态变化一切交互都只能靠js添加。 导致js中大量重复的代码和重复的步骤 2.MVVM设计模式将前端内容重新划分: (步骤)1). 界面(View):增强版的html+css,可根据数据自动变化 为HTML添加了动态功能: 变量,if else, for 2). 模型数据(Model): 所有页面上需要的/可能发生变化的数据。集中定义在data={ 数据1:值1, 数据2:值2, ... } 往往模型数据都是ajax从服务端请求来的。 3). 控制器(ViewModel):将视图View和模型数据(Model)绑定在一起, 绑定: 监控视图和模型,始终保持模型数据与页面自动同步 3.控制器ViewModel中(new Vue()对象)包含两大子系统:1). 响应系统: 监控模型中每个变量的变化,为每个模型自动添加get()和set() 只要有变量发生变化,都会自动经过set()和get()中会触发通知:xx变量变为xx新值 本质: 将data中每个属性都提升为new Vue对象的访问器属性。只要修改new Vue对象的访问器属性,就可修改data中的变量。但是,同时会发出通知! 通知会发给虚拟DOM树 2). 虚拟DOM树: 什么是: Vue临时生成的仅保存可能变化的元素和属性的DOM树(创建new vue时,通过扫描完整DOM树,仅提取可能变化的元素和属性,组成的一颗及精 简的虚拟DOM树) 何时生成: new Vue()边扫描受监控的页面元素,边创建虚拟DOM树,仅保留可能发生变化的元素 何时使用: 响应系统通知某个变量被改变时,告知虚拟DOM树。虚拟DOM树快速遍历自己,找到受影响的DOM元素,仅修改受影响的DOM元素 总结: 虚拟DOM树: 1). 内容少,遍历极快 2). 封装了重复性的增删改查DOM操作//仅修改受影响的DOM元素的属性或内容 |
|||||||||||||||
四、 绑定语法: |
|||||||||||||||
学名:插值语法 Interpolation == {{变量名}} 1. 什么是:让HTML可以自动找到程序中的变量的特殊语法 2. 为什么:因为传统的HTML是静态的,缺少动态变化的能力。导致js当中要想操作HTML,需要大量重复的代码。 3.何时用:只要HTML中某个位置的数据,需要根据程序中的一个变量动态变化!都要用绑定语法! 如何:2步 1).先找页面中所有可能发生变化的地方有几处 2).再在模型数据中定义相同数量的变量: new Vue({ el:“#app”, data:{ 变量名:值, ... : ... , } }) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <!--2.在定义界面--> <div id="app"> <h1>用户名:{{uname}}</h1> <h1>性别1:{{sex1==1?"男":"女"}}</h1> <h1>性别2:{{sex2==1?"男":"女"}}</h1> <h1>订单时间:{{new Date(orderDate).toLocaleString()}}</h1> </div> <script> //3.定义new Vue()对象,其中包含俩部分:el:"#app"和data:{...} new Vue({ el:"#app", data:{ uname:"dingding", sex1:1, sex2:0, orderDate:1559349035000, } }) </script> </body> </html> {{}} 强调:HTML中有几处变化,data对象中就要有几个变量与之对应。 3).在HTML中,可能发生变化的位置用绑定语法定义变量:{{变量或表达式}} 强调:其实{{}}的用法和模板字符串中${}的用法完全一样! 能写:变量,算术计算,关系/逻辑运算,函数调用,访问数组元素,三目一一凡是有返回值的js表达式都能 结果:运行时,HTML中的所有{{}}会自动去data中找同名的变量使用。且内存中的data中的变量值发生变化,HTML中的{{}}的值自动变化!--节省 了大量 重复的查找和修改操作。--多亏了MVVM中的viewModel中的两大子系统:响应系统和虚拟DOM树。 |
|||||||||||||||
五. 指令directive |
|||||||||||||||
1. 什么是:为HTML添加更多新功能的Vue预知的自定义属性 2.为什么:因为原来HTML缺少动态原因所需的要素: 运算, 分支,循环等功能。所以只能依靠js中反复查找,反复修改来控制HTML元素的内容和状态。 3.如何使用指令:13种1).v-bind属性: 什么是: v-bind属性专门动态绑定元素的属性值的指令 为什么:要绑定属性值,不能用{{}},只能用v-bind或 :简写、 如何: <ANY v-bind:属性名="js表达式"> <img :src="pm25<100?'img/1.png':
pm25<200?'img/2.png':
pm25<300?'img/3.png':
'img/4.png'">
去{{}}换v-bind:
其实可简写<img v-bind:src="...
去{{}}换:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <img :src="pm25<100?'img/1.png': pm25<200?'img/2.png': pm25<300?'img/3.png': 'img/4.png'"> <!--pm25<100 就显示1.png pm25<200 就显示2.png pm25<300 就显示3.png pm25<400 就显示1.png--> </div> <script> new Vue({ el:"#app", data:{ pm25:65 }, //凡是data里的,都是直接属于new vue的 //在vue中任何位置想访问变量this.变量 created(){//当new Vue创建完自动执行 setInterval(()=>{ this.pm25=Math.random()*400 },2000) } }) </script> </body> </html> v-bind 2). 事件绑定: v-on 如何: HTML中: <ANY v-on:事件名="处理函数名"> 其实: v-on: 可简写为@, 所以,今后绑定事件都用@事件名="处理函数" 强调: 2.1).处理函数必须定义在: <script>中 new Vue({ el:"#app", data:{ 模型变量 }, methods:{ 处理函数 (){ this.模型变量 } } })中的methods:{}结构中 2.2).其实可以传参:@事件名=“处理函数(实参值)” 2.3).也可以用事件对象e:用法和DOM中完全一样 获得e:methods:{ 后续可以:获取目标元素,实现事件委托 e.target 取消冒泡e.stopPropagation() 阻止默认行为e.default(); 键盘事件中获得键盘号:e.keyCode 获取鼠标坐标位置:e.screenX,e.screenY e.clientX, e.clientY. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <!--<div id="app"> <button @click="change(-1)">-</button> <span>{{n}}</span> <button @click="change(+1)">+</button> </div> <script> new Vue({ el:"#app", data:{n:1}, methods:{ change(i){this.n+=i;} } }) </script>--> <div id="app"> <div @click="change"> <button>-</button> <span>{{n}}</span> <button>+</button> </div> </div> <script> new Vue({ el:"#app", data:{n:1}, methods:{ change(e){ var tar=e.target; if(tar.nodeName==="BUTTON"){ this.n+=tar.innerHTML=="+"?1:-1; } } } }) </script> </body> </html> @ 3). v-if: 为HTML添加分支结构 v-if 控制一个元素的显示隐藏 <ANY v-if="条件表达式" v-if v-else 可控制两个元素二选一显示 <ANY v-if="条件表达式" <ANY v-else v-if v-else-if v-else 可控制多个元素多选一显示 <ANY v-if="条件表达式" <ANY v-else-if="条件表达式" <ANY v-else 原理:Vue会自动判断每个条件,哪个条件符合, v-else的元素 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!--如果未登录,则显示第一个h1,隐藏第二个h1--> <h1 v-if="isLogin==false"><a href="javascript:;">登录</a>|<a href="javascript:;">注册</a></h1> <!--否则,隐藏第一个h1,显示第二个h1--> <h1 v-else>Welcome dingding, <a href="javascript:;">注销</a></h1> <hr> {{pageNo}}/{{pageCount}} <button @click="next" v-if="pageNo<pageCount">下一页</button> </div> <script> var vm=new Vue({ el:"#app", data:{ isLogin:false, pageNo:1, pageCount:3 }, methods:{ next(){ this.pageNo++; } } }) </script> </body> </html> v-if <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!--如果是未登录的,就显示第一个h1--> <h1 v-if="isLogin==true"> <a href="javascript:;"@click="login"> 登录</a> | 注册</h1> <!--否则就显示第二个h1--> <h1 v-else>Welcome {{uname}}, <a href="javascript:;" @click="logout">注销</a></h1> </div> <script> new Vue({ el:"#app", data:{ isLogin:false,//开始时是未登录 uname:"dingding" }, methods:{ login(){ this.inLogin=true }, logout(){ this.isLogin=false} } }) </script> </body> </html> v-if 4). v-show: 控制一个元素显示隐藏 用法同v-if: <ANY v-show="条件表达式" 笔试题:v-if vs v-show区别本质: 用display:none隐藏元素 v-if的本质: 用删除元素节点的方式,隐藏元素 如果一个元素频繁需要显示隐藏,v-show 的效率高 5). v-for: 根据数组反复生成多个相同结构的html元素:其实就是为HTML添加循环功能: 如何: 语法: <要反复生成的元素 v-for=“(elem,i) of 数组”:key="i"> 强调:特例:v-for中的循环变量可被v-for自己或子元素用于绑定! 用法: <ANY v-for="elem of 数组"> //elem可当做模型变量使用,用作绑定 </ANY> of会依次读取数组中每个元素 每读取一个元素就创建一个<ANY> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="(elem,i) of tasks" :key="i"> {{i+1}} - {{elem}}</li> </ul> </div> <script> new Vue({ el:"#app", data:{ tasks:["吃饭","睡觉","学习"] } }) </script> </body> </html> v-for <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="(score,i) of scores" :key="i"> <span v-if="score>=60">及格</span> <span v-else>不及格</span> </li> </ul> </div> <script> new Vue({ el:"#app", data:{ scores:[69,77,5,35,88] } }) </script> </body> </html> v-for 6). 绑定html片段: 问题: {{}}无法绑定HTML片段,它会努力保持HTML片段原样显示, 解决: v-text="变量" 不行,v-text等效于{{}} v-html="变量" 可以,会被浏览器解析为正文 总结: 只要绑定的内容是HTML片段,都用v-html 如何: <ANY v-htm|="变量"></ANY> 7). v-cloak: 在vue加载完之前,短暂隐藏{{}}语法 如何: 7.1). 在界面中想隐藏的元素/父元素上添加v-cloak自定义属性 7.2). 在页面的<style>中用属性选择器找到所有有v-cloak属性的元素,设置隐藏: <style>[v-cloak]{ display:none }</style> 强调:v-cloak没有属性值! 问题:v-cloak空有属性名,没有配套样式 解决:只能自己手写!用属性选择器: [v-cloak]{display:none} v-cloak不能改名,改名就找不到了 结果: 当vue对象加载完成,会自动查找所有v-cloak属性,自动移除。所有元素就重新显示出来其实,除了v-cloak外,还可用v-text代替{{}}绑定元素内容,避免短暂看到{{}} 8).<ANY v-text="`js模板字符串语法,用${变量}动态生成内容`" 比如: 原理: v-text 在未绑定时,是一个浏览器不认识的属性,所以显示不出来。直到new Vue加载完,认出v-text, 才用V-text的内容代替元素的innerHTML内容 v-cloak VS v-text: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> <style> [v-cloak]{display:none} </style> </head> <body> <div id="app"> <h1 v-cloak>{{uname}}</h1> <h1 v-cloak>性别:{{sex==1?"男":"女"}}</h1> </div> <script> setTimeout(function(){ new Vue({ el:"#app", data:{ uname:"dingding", sex:1 } }) },2000) </script> </body> </html> v-cloak 8). v-pre: 保护内容中碰巧出现的{{}}不被VUE编译,保持原样显示
9). v-once: 控制一个变量只在开始时绑定一次,之后即使模型变量变化,也不再更新
强调: v-once没有任何属性值,写在元素中,就起作用。 原理:在构建虚拟DOM树时,会扫描到v-once的元素,所以首次绑定,能绑定v-once的元素的内容,首次绑定后,v-once的元素会从虚拟DOM树中移除。从 此, 再出发变量改变时,不会再修改v-once的元素。 |
|||||||||||||||
总结: 1).如果元素内容要变化:用 {{变量}} 绑定也可以用v-text="`变量或表达式`" 2).如果元素的属性值要变化:用 :属性名=“变量名或表达式” 绑定。 3).一个元素控制显示隐藏:v-show=“条件” 4).多个元素选其一显示:v-if="条件" v-else-if="条件" v-else 5).反复生成多个相同结构的元素时:v-for="(elem,i) of 数组":key=i
6).只要绑定事件处理函数都用:@事件名="处理函数"
7).只要不希望用户短暂看到{{}}语法:v-cloakI或V-text. 9).只要希望只在页面加载之初绑定--次,之后不再变化,就用v-once |
|||||||||||||||
六. 双向绑定: |
|||||||||||||||
单向绑定: 将内存中模型数据中的值绑定到界面上(Model->View) 问题: 无法绑定表单元素的值表单元素值的修改,无法自动映射到内存中 解决: 今后只要绑定表单元素,都用双向绑定 1.什么是双向绑定:即能将Model中的数据绑定到界面上(Model->View),又能自动将界面上的修改,映射到内存中(View->Model) 2.何时:只要希望通过表单元素的修改,来更改程序的内容时。——只要绑定表单元素,都要用双向绑定 3.如何:不要用:,要用v-model:属性名=“变量” 哪个属性会被改变,v-model就自动绑定哪个属性 <表单元素 v-model:value="模型变量"> 简写: 1). <input type="text" <textarea> <select> 比如 文本框和文本域: <input type="text" v-model[:value]="变量"> <textarea v-model[:value]="变量">
都可简写为: v-model="模型变量" 省略:value,默认绑定value属性 原理: v-model其实就是自动为表单元素绑定了oninput或onchange事件。在这些内容更改事件中,自动修改当前Vue 对象中的模型变量。 普通绑定为什么不能更新程序中的数据:因为没有自动添加事件,导致界面.上更改, Vue框架不知道,自然就无法更新模型变量。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <input v-model="kword"><button @click="search">百度一下</button> <br> <input type="checkbox" v-model="isAgree">同意 <br> <input type="radio" name="sex" value="1" v-model="sex">男 <input type="radio" name="sex" value="0" v-model="sex">女 </div> <script> var vm=new Vue({ el:"#app", data:{ kword:"输入搜索关键词", isAgree:false, sex:1 }, methods:{ search(){ console.log(`查询 ${this.kword} 相关的消息`); } } }) </script> </body> </html> v-model 2). <input type="checkbox"> 可简写为: v-model="模型变量" 但默认绑定的是checked *).单选按钮radio:要绑定checked.不绑定value,因为radio中的value是写死的,固定不变的。等着被选择。绑定checked:因为是否选中radio,取决于 checked属性的值。用户更改选中项,改的也是checked属性值。一一radio 只能绑定v-model:checked="变量“ 绑定时: 注意:v-model在radio中,会自动用checked后绑定的变量和当前radio写死的value值做比较,返回booll值决定当前radio 是否选中。不用自己在checked后 写完整条件,只写一个变量名即可! 改变选中状态时,v-model会将选中的radio的写死的value值更新回程序中绑定的变量上, *). <input type="radio"> 可简写为: v-model="模型变量" 但用模型变量和radio的value做比较 如果模型变量的值=radio的value时,就选中 如果模型变量的值!=radio的value时,就不选中 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <h1>性别:<label><input type="radio" name="sex" value="1" v-model:checked="sex">男</label> <label><input type="radio" name="sex" value="0" v-model:checked="sex">女</label> </h1> <h1>sex:{{sex}}</h1> </div> <script> new Vue({ el:"#app", data:{ sex:1 } }) </script> </body> </html> v-radio 3). 复选框: 单独使用: <input type="checkbox" v-model="isAgree">同意 v-mode:checked 且isAgree是bool类型
4). select元素: <select v-model="orderStatus"> value=30 <option value="10" >未付款</option> <option value="20" >已付款</option> <option value="30" >已发货</option> <option value="40" selected >已签收</option> select元素绑定:绑定的是select元素整体的value属性.因为,无论选择了哪一个option, 当前选中项的值都变成select元素的value属性值。 原理: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> 订单状态: <!--<select v-model:value="orderstatus">--> <select v-model="orderstatus"> <option value="10">未付款</option> <option value="20">已付款</option> <option value="30">已发货</option> <option value="40">已签收</option> </select> <h1>orderstatus:{{orderstatus}}</h1> </div> <script> new Vue({ el:"#app", data:{ orderstatus:30 } }) </script> </body> </html> select
watch监控机制:什么是:Vue中的watch监控机制可监控vue所有模型变量的变化。只要模型变量变化,就自动触发一个同名的函数。在函数中可调试变量的值或执行实时操作 如何: new Vue({ 变量1:值1 },//所有页面需要的模型变量 watch 监控: 当某个模型变量被修改时,自动触发的函数 watch:{//定义监控变量的函数 变量1(){ //会在受监控的变量被双向绑定修改时自动执行 } } }) |
|||||||||||||||
六. 绑定样式 |
|||||||||||||||
绑定样式:class和style 2种: 1.动态绑定单个css属性:(也分2种) 绑定style: 1). 其实style也是一个普通的字符串属性,也可以: <ANY :style="变量" data:{ 缺点:在字符串中不便于修改某一个css属性 2). 将style看做一个对象绑定: <ANY :style="变量" data:{ 变量:{ css属性:值,//必须写px css属性:值 } //绑定时,被动翻译为字符串: //“top:50px;left:50px” }//css属性都要去横线变驼峰! 强调:固定的style和动态绑定的style可同时存在 运行时,结果:动态绑定的style会和写死的style合并为一个style: 2.绑定class1). 其实class属性当做一个字符串绑定,也可以: 问题:不便于控制一个class 的启用或禁用 <ANY :class="拼接字符串" 2). 都用对象方式绑定: <ANY :class="对象名" //"success" data:{ 对象名:{ hide: false, success: true, fail:false } } 用翻译好的class 字符串和元素上规定的class=",合并为一个class。如果想用程序切换使用/不使用某个样式类,只要修改类名对应的true/false就行。 值为true,就是应用该样式类 值为false,就是不应用该样式类 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="js/vue.js"></script> <style> span.hide{ display:none } span.success{ background:green; color:#fff } span.fail{ background:red; color:#fff } span.vali{ padding-left:5px; font-size:12px; } </style> </head> <body> <div id="app"> <input :placeholder="placeholder" v-model="phone"> <span class="vali" :class="phoneClass">{{phoneMsg}}</span> </div> <script> var vm=new Vue({ el:"#app", data:{ placeholder:"输入手机号", phone:"", phoneClass:{ hide:true, success:false, fail:false },//class="hide" phoneMsg:"" }, watch:{ phone(){ if(/^1[3-8]\d{9}$/.test(this.phone)){ this.phoneClass={ hide:false,success:true,fail:false } this.phoneMsg="手机号验证通过!" }else{ this.phoneClass={ hide:false,success:false,fail:true } this.phoneMsg="手机号格式不正确!" } } } }) </script> </body> </html> class |
|||||||||||||||
八. 计算属性: |
|||||||||||||||
1.什么是:自己不保存值,每次访问都要根据其他属性计算出来 2.何时:要显示的值,需要根据其他属性动态计算才能获得时 3.如何:1). 定义计算属性: computed:{ 计算属性名(){ 根据其他属性,返回计算结果 return ... ... } } 特点: vs函数:也可以实现计算的效果 使用计算属性:计算属性虽然定义成函数,但是,用法和普通函数模型变量完全一样!不要加() <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <p>总价:¥{{total.toFixed(2)}}</p> <ul> <li v-for="(p,i) of cart" :key="i"> {{p.pid}} | ¥{{p.price.toFixed(2)}} | {{p.count}} | ¥{{(p.price*p.count).toFixed(2)}} | <input type="checkbox" v-model="p.checked"> </li> </ul> <p>总价:¥{{total.toFixed(2)}}</p> </div> <script> new Vue({ el:"#app", data:{ cart:[ {pid:1,price:12.5, count:5,checked:true}, {pid:2,price:10, count:3,checked:false}, {pid:3,price:20, count:1,checked:true}, {pid:1,price:25, count:5,checked:false}, ] }, computed:{ //虽然是函数,但是当普通变量一样绑定 total(){ var sum=0; for(var p of this.cart){ if(p.checked==true){ sum+=p.price*p.count; } } return sum; } } }) </script> </body> </html> computed 4. 绑定时:和普通属性完全一样,不要加() vs methods: methods: 调用时,必须加(),调用几次,就重复计算几次 computed: 绑定时,不加(),即使多次绑定,也只计算一次。vue会缓存计算属性的计算结果 总结: 如果更关心值/结果,优先选计算属性 如果更关心操作/执行过程,优先选方法 |
|||||||||||||||
九. 自定义指令: |
|||||||||||||||
如果希望元素自动执行一件事,13种指令不够用,可自定义指令: 1.如何:1). 定义指令: Vue.directive( "指令名",//不写v-前缀{ inserted(elem){//当拥有该指令的元素被添加到DOM树后自动执行 //elem: 接住当前标有该指令的DOM元素对象 对elem执行各种想要的DOM操作 } }) 强调:应该定义在不属于任何new Vue()的外部全局.将来所有new Vue()都可以共用这个自定义指令 2). 使用指令: <ANY v-指令名> 当该元素被加载到DOM树上后,自动执行指令中的inserted 但是,使用指令时,必须加v-前缀 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="js/vue.js"></script> <script> Vue.directive( "focus", { inserted(elem){ //DOM的天下 elem.focus(); } }) </script> </head> <body> <div id="app"> <input type="text" v-focus><button>百度一下</button> </div> <script> var vm=new Vue({ el:"#app", data:{ } }) </script> </body> </html> 自定义 |
|||||||||||||||
十. 过滤器: |
|||||||||||||||
1.什么是:对从模型中绑定来的原始值,进行再加工后,再显示 2.何时:如果模型中的原始值不能直接使用 3. 如何:1). 定义过滤器: Vue.filter("过滤器名", function(value,形参1,...){ //value会接住要处理的原始值 return 改造后的新值 }) 2). 使用过滤器: {{原始模型变量|过滤器(实参1,...)|下一个过滤器 }} :属性="原始模型变量|过滤器(实参1,...)|下一个过滤器" 在绑定时: 在绑定语法中都可用"|"平将过滤器连接在变量之后。运行时:原始变量值->过滤器->value参数->return过滤后的新值->给人看。 过滤器参数: function(value,lang="cn"){ 2).使用过滤器时传入实参. {{sex2 |sexFilter(''en"}} 强调:定义时添加过滤器形参,从第二个形参开始添加。但是使用过滤器时,自定义实参值,却从第一个实参传入。但是还不会出错!因为过滤器函数在调用时,自动回将原始值作为第一个实参传入。我们添加的自定义实参,只能排在第二 其实程序中的过滤器也可以用|串联多个: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="js/vue.js"></script> <script> Vue.filter("sexFilter",function(value,lang){ //value: 1 0 ; lang: cn en if(lang=="cn"){ return value==1?"男":"女" }else{ return value==1?"Male":"Female" } }); Vue.filter("sexIcon",function(value){ return value=="男"||value=="Male"?value+"♂":value+"♀" }) Vue.filter("dateFilter",function(value){ //value: ms return new Date(value).toLocaleString() }) </script> </head> <body> <div id="app"> <h1>性别:{{sex | sexFilter("en") | sexIcon}}</h1> <h1>下单时间: {{orderDate | dateFilter}}</h1> </div> <script> var vm=new Vue({ el:"#app", data:{ sex:0, orderDate:1550160000000 } }) </script> </body> </html> filter |
|||||||||||||||
十 一 . axios: |
|||||||||||||||
1.什么是:专门发送ajax请求的基于Promise的函数库. 2. 何时:今后在vue中发送ajax请求首选axios 3.为什么:1). 手写ajax四步/封装自定义函数 2). jQuery: $.ajax() —— Vue中没有jQuery 3). Vue官方曾经提供了专门发送ajax请求的组件:vue-resource ——官方不推荐使用,已停止维护 4). Vue官方推荐axios作为Vue中标准的发送ajax请求的组件 4.如何:1). 下载并引入:axios.min.js 内存中,就多出一个axios对象,有两个方法: get() post() 2). 发送get请求: axios.get("url",{ params:{//相当于$ajax中的data:{...} uname: "dingding", upwd:"123456" }//运行时params中的参数会被翻译为字符串,拼接到url的结尾 }).then((result)=>{ this//这里的this如果不写成箭头函数默认指window,为this保持和外部VUe一致,必须用箭头函数. //res是经过axios再封装的响应对象,不仅是result //result: res.data才是从前的result(result返回的不只是服务端响应结果,真正的响应结果包含在result.data中) }) 发送post请求: axios.post("url","uname=dingding&upwd=123456") |
|||||||||||||||
十二. 组件: |
|||||||||||||||
1.什么是:拥有专属的HTML,CSS,js和数据的独立页面区域 为什么: 重用! 2.何时:只要一个页面功能,需要反复使用时 3. 如何:Vue中的组件其实是一个包含html,数 据和功能的对象 1). 创建:每一个组件都是一个缩微的new Vue() Vue.component( "组件名",{ //不要用驼峰命名,而用"单词-单词" el:"选择器", template:`<div>//组件模板必须用唯一 的父元素包裹 <button>-</button><span>1</span><button>+</button> </div>`, //找到要反复使用的HTML片段(被反复复制的多个一模一样的片段,所以将这一份标准的片段称为模板) data:{...}, data:function(){//每次重复创建组件时,都自动调用data函数,为本次组件创建一个专属的模型对象 return { //模型数据 }//类似于以前的data }, methods:{ 方法/事件处理函数 }, computed:{ 计算属性 }, watch:{ 监听函数 } } ) HTML片段都要保存在<template>元素中并其一个唯一的id new Vue()要不要写? 答:必须写! <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="js/vue.js"></script> <script> Vue.component("my-counter",{ template:"#tplCounter", //data:function(){ data(){ return {//相当于以前的data n:1 } }, methods:{ change(i){ this.n+=i; } } }) </script> </head> <body> <div id="app"> <my-counter></my-counter> <my-counter></my-counter> <my-counter></my-counter> </div> <!--<div id="tplCounter" style="display:none">--> <template id="tplCounter"><!--HTML5中新增--> <div> <!--Template--> <button @click="change(-1)">-</button><span>{{n}}</span><button @click="change(+1)">+</button> </div> </template> <script> var vm=new Vue({ el:"#app", data:{ } }) </script> </body> </html> component 2). 使用组件: 其实vue中的一个组件,就是一个可重复使用的自定义标签而已。组件名其实就是一个标签名: <组件名></组件名> 会被template的HTML片段代替! |
|||||||||||||||
十三.newVue的生命周期: |
|||||||||||||||
1.什么是:一个newVue()的加载过程: 2.何时:如果希望在加载过程某个阶段,自动执行一项任务时 3.如何:newVue()的声明周期包括4个阶段,8个函数 钩子函数—其实就是在不同阶段自动执行的回调函数
前:beforeCreate(){...} 1).创建(create)阶段:创建new Vue()对象和data()对象 已经有data对象了,但是没有虚拟DOM树。可以发送ajax请求。因为没有虚拟DOM树,所以暂时不能用DOM操作 后:created(){...}//重点 前:beforeMount(){...} 即有data对象,又有虚拟DOM树。 即可发送ajax请求,又可执行DOM操作。 后:mounted(){...} //后两个阶段不是必须: 前:beforeUpdae(){...} 3).更新(update)阶段:当data 中的变量值被改变时才触发 后:updated(){...} 前:beforeDestroy(){...} 4).销毁(destroy)阶段:当调用专门的$destroy函数销毁一个组件时才触发 后:destroyed(){...} |
|||||||||||||||
十四.组件化开发**** |
|||||||||||||||
1.什么是组件化开发:今后一个网页都是由组件组成的 拿到网页,先划分组件,再分组件开发,最后再拼接成一个网页 2.为什么:1). 团队协作开发 2). 松耦合 3.如何:1).将页面划分成多个功能区域-组件 2).创建组件.js,其中定义组件对象:2种 4.自定义组件1).全局组件: Vue.component() 什么是:可以在页面任何位置使用的组件 何时:只要页面中一个区域,可能在多个页面或者多个不同位置随意使用时 问题:不受约束,随处可用。 什么是:必须在指定父组件内才能使用的组件 何时:如果希望限制一个组件只能在其父组件内使用时,就必须定义子组件 2.1). 如何: 创建子组件: 强调: {}中必须和Vue.component()中的{}内容格式一致! 变量名必须用驼峰命名。 2.2).在父组件中用components属性包裹所有子组件: Vue.component("父组件",{ 2.3).在主界面或父组件中,使用子组件标签,引入子组件的内容。 结果:浏览时,所有组件(父和子),最终都合并为一个整体。不再包含不认识的标签。 3). 区别: 3.1). 根组件: 一个页面中只有一个的组件: new Vue({ el:"#app", data:{ ... } }) 3.2). 全局组件: 可出现在任意位置的组件 Vue.component("组件名",{ template:"#tplxxx", data(){ return { ... } } }) 3.3). 子组件: 只能出现在规定父组件内的组件 先将Vue.component降级为普通的js对象 //Vue.component("todo-add",{ var todoAdd={ template:"#tplTodoAdd", data(){ return { ... } } } 再在父组件的{}中的components下添加子组件对象: Vue.component("todo",{ template:"#tplTodo", data(){ return { ... } }, components:{ todoAdd } //vue会自动将todoAdd -> <todo-add> })
问题:父组件中的公共数据,子组件可以直接使用吗?。答:不行!因为每个组件都是专属的独立的数据。互相之间没有关系。即使是在位置上是父子关系的组件之间,也没
4).*****组件间传参:2步: 问题: 父组件的成员,子组件无权直接使用 解决: 2步 4.1).子组件定义自定义属性,准备接受父组件的数据 4.2).父组件中使用子组件标签的位置,开始标签中通过 :绑定的方式,将自己data中的值赋值给子组件自定义的属性 <子组件:自定义属性="父组件的变量" 强调: props中的自定义属性等效于data中的模型变量。也可以在子组件内绑定使用 强调:子组件中可以修改父组件传来的数据。如果传来的是引用类型的对象:数组或对象,则在子元素中修改变量等效于直接修改父元素中的原对象或数组。 因为父子组件间传递引用类型的对象,实际传递的都是地址值,父子组件用的还是同一个数据对象,任何一方修改,另一方都会受影响
|
|||||||||||||||
十五. *****SPA |
|||||||||||||||
1.Single Page Application(单页面应用)1). 什么是: 整个应用程序只有一个完整的HTML页面文件 单页面应用中所谓的“页面”,其实就是组件 由一个路由器根据URL地址中的锚点地址不同,选择不同的组件替换 2).为什么:vs 多页面应用:
3).何时:今后,基本上都是单页面应用。 4).如何 实现单页面应用: 4.1). 先有一个完整的home.html文件,home.html中主体部分被用特殊标签占位,暂时为空 比如: index.js、details.js、products.js 、login.js. { path:"/login", component: login },] 4.4).创建一个路由器对象: |
|||||||||||||||
十六.脚手架: |
|||||||||||||||
1.什么是:已经具备核心功能的半成品项目 2.为什么:标准化项目的开发,便于分工协作。 3.何时:今后只要使用Vue框架做开发,都要先创建Vue框架的脚手架项目代码 4.如何:2步:1).先安装一个能创建脚手架项目的命令行工具: npm i -g @vue/cli 2).用vue命令,创建一个项目的脚手架代码文件夹vue create项目文件夹名 第一步:Your connection to the default npm 第二步: ? Please pick a preset: (Use arrow keys). 第三步: ? Check the features needed for your 脚手架代码中采用了模块化开发: 因为不是所有浏览器都认识ES6,所以,需要Babel最后将所有ES6的代码翻译为多数浏览器都认识的ES5代码。 () TypeScript//微软出的严格的script—不选 () Progressive Web App (PWA) Support. (*) Router //必选 ( )CSS Pre-processors. () Linter / Formatter //千万不要选!是代码质量检查工具。即使代码没错,格式不规范,也报错! ( ) Unit Testing 第四步: Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)n(公司里都是启用的因为必须要服务端支持,所以才不选。默认Vue的地址栏中都是用#/路径来跳转。但是#可能和你的锚点地址冲突。如果即想用vue,又想要锚点地址,只能启用history模式。启用后,地址栏中就没有#了。而是直接用"/路径"。) 第五步: Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys) 第六步: Save this as a preset for future projects? 脚手架文件夹创建成功后: 🎉 Successfully created project xz_vue. $ cd xz_vue //先进入项目文件夹(vue create后的那个文件夹一XZ_ vue ) $ npm run serve//=运行Live server+Babel的效果。是先将脚手架中浏览器不懂的代码,翻译为浏览器能懂的代 码。然后,再将翻译后的页面运行在一个临时的开发服务器中。强调:npm run serve必须在项目文件夹内运行。 保险起见是在vs code中打开XZ_ _vue ,然后在package.json文件.上右键单击在命令行打开,然后运行: 2件事: 强调: . 1.不用总是npm run serve,因为npm run serve可以自动感知代码的修改,并自动重新编译,重新运行。 2.服务端nodejs的cors: 要改为http://localhost:8080/如果不想总是改。可改为cors:*。允许所有客户端都能跨域 3.分析脚手架文件夹的结构: public/将来所有不需要编译,就可直接使用的js和css,以及图片都放在public下。放在public 下的文件,都不会被 放在public下的不需要编译的css和js,都要在唯一完整的页面index.html中引入。 src/ 放的是所有自己写的代码。() /components 放全局组件或多个页面共用的子组件(比如页头组件 页脚组件header.html/footer.html) /views 放所有的页面组件,有几个页面,就在views 下创建几个组件文件。比如:首页,详情页,商品列表 app.vue 整个网站所有页面公共的容器组件.
main.js 相当于以前的newVue()。整个网站只有一个new Vue() 。main.js还承担了配置Vue项目的责任。 比如: axios: 1.用npm i -save axios为脚手架项目添加axios模块。 2.希望在Vue框架中所有组件里都能用axios。所以,应该讲axios添加到Vue整个类型的原型对象中。 2步: 在main.js中。 import axios from "axios" 结果:所有组件中都可用this.axios.get()发送ajax请求
router.js 是整个网站的路由器和路由字典,保存着所有路径和组件的对应关系。 .vue文件:是脚手架专用的组件/页面文件。 今后,只要新建一个组件,或新建一个页面,都新建.vue文件. 内容包括:3大部分: <script>
//组件本质就是一个对象,对象需要抛出到外部被别人使用
export default{
//没有template和el了
data(){
return {
}
},
created(){},
methods:{}
computed:{}
watch:{}
}
</script>
|
|||||||||||||||
十七.Vue-router插件 |
|||||||||||||||
1.vue-router使用步骤1). 先创建一个唯一的完整的index.html 引入vue-router.js 在div#app中添加一个<router-view>标签,用于为将来要加载的"页面"组件占位 2). 分别创建多个“页面”组件对象和模板 “页面”组件对象必须以子组件形式创建 3). 定义路由字典: 路由字典:保存一组路径与组件对象对应关系的数组 var routes=[ {path:"/相对路径名", component: "页面"组件对象 } ] 4). 定义路由器对象,将路由字典放入路由器中: 才是真正负责导航/替换页面内容的对象 var router=new VueRouter({ routes }) 5). 将路由器对象,加入当前页面的根组件new Vue中 new Vue({ ..., router, ... }) 2. 嵌套路由:何时: 多个页面中包含相同的区域(页头,页脚,导航)时 如何: 2步: 1). 将多个页面共用的部分,保存在一个新创建的组件中 新组件同时包含<router-view> 2). 定义路由字典: //home: 是保存相同部分的组件 {path:"/",component:home,children:[ {path:"/",component:index},//默认路径 / {path:"/index",component:index},// /index {path:"/details",component:details} // /details ]}, //login和之前页面没有相同部分 {path:"/login",component:login} 加载时: #/ /=="/" 先加载home页头 /=="/" 再加载index替换home中的<router-view> #/details /=="/" 先加载home页头 /details="/details" 再加载details替换home中的<router-view> #/login /=="/" 先加载home页头 没有/login 撤掉home页头 /login="/login" 只加载login 3. 路由跳转:HTML中: 所有a换成<router-link to="路径"></router-link> 运行时,自动翻译为<a href="#/路径"></a> js中: this.$router.push("路径") $router就是var router=new VueRouter()路由器对象 要执行跳转这个动作,只能借助$router 凡是以$开头的,都是Vue内置的对象 4. 路由传参:2步: 1). 在路由字典中配置支持参数的相对路径: {path:"/details/:lid",component:Details,props:true} /:lid: 将来请求details页面时必须携带一个lid参数 比如: /details/5 在details页面中获取参数: this.$route.params.lid 如果定义了props:true,lid参数会自动传递给props中同名的属性: props:["lid"] this.lid=5 2). 跳转时: <router-link to="/details/5"
|