什么是: 第三方开发的渐进式的基于MVVM设计模式的纯前端js框架
第三方: 下载
渐进式: 可逐步在项目中引入Vue的各项功能
且可与其他技术混用——今后,极力不推荐
基于MVVM设计模式: ?
纯前端: 没有后端技术,Vue照样执行!
js框架: 已经具有核心功能的半成品项目代码。
开发人员只需要根据具体业务的需求,添加定制自己个性化的那一部分内容即可!
原生DOM vs 函数库 vs 框架
原生DOM: 优点: 兼容性各个平台
缺点: 繁琐
函数库: 优点: 简化原生DOM中的每一步操作的代码长度。
缺点: 没有减少步骤!
框架: 优点: 彻底简化了开发的步骤!
缺点: 不兼容旧浏览器
需要开发人员彻底转变思路!
何时: 今后所有以数据为主的项目,都可用Vue框架
2种:
1. 下载独立的vue.js文件,用<script>引入网页中
初学者使用,学习单项Vue中的技能
版本: 2.5
2个版本:
1. 开发版:未压缩,有完备的错误提示
2. 生产版:压缩,去掉了完备的错误提示
2. 安装脚手架代码: 3.x
今后熟练之后,开发项目,都用脚手架
准备: 引入vue.js
<script src="js/vue.js">
1. 编写界面:
1. 所有界面内容必须包裹在一个唯一的父元素内
2. 找到页面中所有可能变化的位置用{{变量名}}方式临时占位
2. 编写自定义js程序:
1. 定义data对象,其中包含所有页面所需的变量及其初始值。页面需要几个变量,data中就要定义几个属性变量
2. 创建new Vue对象,将界面和保存所有变量的data对象绑定起来:
var vm=new Vue({
el:"#app", //el:element的缩写
//el后跟的是要监控的部分的父元素的选择器
//选择找到那个父元素,哪个父元素下的内容就被new Vue()实时监控
data, //让new Vue同时监控data中变量的变化
})
结果:
1. new Vue会自动将data中的初始值替换界面中所有{{变量}}的位置
2. 只要data中的变量值发生变化,new Vue会自动将新的变量值,更新到页面中{{变量}}位置。
3. 添加交互行为:
DOM 4步:
1. 查找触发事件的元素: 不用程序找,用肉眼找到就行
2. 绑定事件处理函数:
1. 不用js绑定,而是手动在界面中的要点击的元素上添加: <button @click="处理函数名">。
2. Vue规定所有事件处理函数需要集中定义在new Vue()中的methods:{内}
new Vue({
... ...,
methods:{
处理函数名(){
想操作data中的变量,必须加this.变量
}
}
})
3. 查找要修改的元素:彻底不用自己找
4. 修改元素: vue中都是通过遥控方式修改元素的: 修改data中的变量值,new Vue可自动更新界面中的元素内容。——绑定
传统Web开发分为三部分组成:
1. HTML:专门编写网页内容的静态语言
2. CSS:专门编写网页样式的静态语言
3. JS:专门为网页添加交互行为的程序语言
问题: HTML和CSS都是静态语言,缺少编程语言必须的要素,比如: 变量,分支,循环...。太弱了!
导致js必须承担所有查找,修改操作!
现代Web前端开发重新划分了三部分:
1. 视图/界面(View):
1. HTML+CSS
2. 增强了HTML和CSS的功能,比如提供了变量,分支,循环等这些程序必备功能
因为,HTML和CSS功能增强了,所以大大减轻了JS的负担!
2. 模型数据(Model): 其实就是用一个data对象,统一保存这个界面所需的所有变量。
因为数据集中保存,所以及其便于维护!
3. 控制器(ViewModel): 自动实现模型数据data中的内容和界面(View)中的内容,实时同步!
控制器已经封装了传统的增删改查的功能,所以几乎避免了一切重复操作!
比如: 今后vue中几乎没有任何查找操作了!
都是以遥控方式代替手工查找元素
只要修改data中的变量,界面中的内容自动更新。
new Vue()中其实包含两大子系统:
1. 响应系统:
什么是: 实时监控data中变量的变化,并能在变量发生变化时,自动发出通知。
原理: 将data放入new Vue()中后,new Vue()自动为每个变量添加了访问器属性。
这些访问器属性,直接隶属于new Vue()对象,保护/监控data中的变量。
而且所有访问器属性中的set方法中,都内置了通知机制。只要试图修改data中的变量,
只能经过访问器属性,自然就会自动触发通知:xx变量值变了!
2. 虚拟DOM树:
什么是: Vue内存中仅保存可能变化的DOM元素和可能变化的属性的简化版DOM树
为什么: 真实DOM树,无关的数据太多!遍历和查找速度太慢!
虚拟DOM树的优点:
1. 小,仅包含可能变化的元素和可能变化的属性
2. 快,遍历快
3. 自动,虚拟DOM树已经封装了增删改查的操作。
4. 效率高,仅修改个别可能受影响的元素和可能受影响的属性。多数无关的元素和属性,不收影响。
原理:
形成:
首次new Vue()时,传入了el:"#app"参数
然后new Vue()找到#app这个父元素,扫描其内部的子元素
变扫描,变创建虚拟DOM树,仅保存可能变化的元素和可能变化的属性
首次将data中的变量,替换DOM树中的{{}}语法
当变量变化时:
首先触发这个变量的访问器set方法,发出通知,通知虚拟DOM树,哪个变量发生变化
然后new Vue()遍历虚拟DOM树,找到可能发生变化的元素和属性
最后,利用已经封装好的DOM的增删改查方法,找到发生变化的元素和属性,修改属性
什么是: 在页面中使用{{变量}}标记哪里可能发生变化
学名: 插值语法: Interpolation
何时: 只要页面中一个位置的内容,可能根据变量的变化而自动变化时,就都用{{变量}}来占位
结果:
1. 所有标有{{变量}}的元素,都被加入虚拟DOM树中
2. 当变量变化时,根据{{}}中的变量名,判断当前元素内容是否受影响。如果受影响,则自动更新该元素的内容。
如何: {{}}除了可以写死一个变量外,还可编写任意正确的有返回值的js表达式——用法同模板字符串中的${...}
比如: {{算术计算}} {{比较运算}} {{三目运算}} {{调用函数}} {{创建对象/访问对象的属性}} {{访问数组的元素}}
不能放程序结构: if else while do while for
什么是: 为HTML添加增强功能的特殊属性
为什么: 传统的HTML缺少程序必须的要素,比如: 变量,函数,分支,循环等,导致js重复操作量巨大。
何时: 今后在vue中,想在HTML里也实现比如分支,循环等功能时
包括: 共13个
什么是: 专门绑定元素的属性值
为什么: {{}}只能绑定元素内容,不能绑定元素属性值
何时: 只要元素的属性值需要根据变量自动变化时,都用v-bind。
如何: <元素 v-bind:属性="js表达式"
其实可简写为 ":属性"
强调: 1. 加了:的属性,值中不用再加{{}}
2. 加了:的属性,""就是{{}}的作用,""中不但可以写值/变量,而且也可以写js表达式
1. 控制一个元素的显示隐藏:
<元素 v-show="条件表达式"
结果:
1. 如果条件表达式执行结果为true,则当前元素显示——会去掉display:none
2. 如果条件表达式执行结果为false,则当前元素隐藏——会自动将v-show替换为display:none
2. 控制两个元素二选一显示隐藏
问题: v-show如果控制多个元素多选一显示隐藏,必须把判断条件在每个元素上都重复写一遍,代码繁琐!
解决: v-if v-else
如何:
<元素1 v-if="判断条件">
<元素2 v-else>
结果:
如果判断条件返回true,就显示第一个元素,删除第二个元素
如果判断条件返回false,就显示第二个元素,删除第一个元素
强调:
1. 两个元素之间不能插入其他元素
2. v-else不需要属性值
3. 控制多个元素多选一显示隐藏
用v-else-if
<元素1 v-if="条件1">
<元素2 v-else-if="条件2">
...
<元素n v-else>
原理:
从上向下一次判断每个v-if或v-else-if后的条件。
只要条件不满足就删除该元素
只有满足条件的元素才会保留下来用于显示。
如果判断走到了下一个元素上,隐含着上一个元素的条件肯定不满足
强调:
1. 多个元素之间不能插入其他元素
2. v-else不需要属性值
总结: v-show vs v-if
v-show采用display:none 隐藏元素 - 效率高,因为未改变DOM树结构
v-if 采用直接删除元素隐藏元素 - 效率略低
1. 一个条件,2件事,二选一执行:
条件?操作1:操作2
2. 多个条件,多选一执行:
条件1?操作1:
条件2?操作2:
条件3?操作3:
操作4
其实: ?代替的是if :代替的是else
if(条件1)
操作1
else if(条件2)
操作2
else if(条件3)
操作3
else
操作4
1. 只要绑定元素内容: {{}}
2. 只要绑定元素属性值: :
3. 只要控制一个元素的显示隐藏: v-show
4. 只要控制两个元素,二选一显示隐藏: v-if v-else
5. 只要控制多个元素,多选一显示隐藏: v-if v-else-if v-else
1. 前提: 一定有一个数组或一个数值,让你可参照着反复生成元素内容
2. 如何:
<要反复生成的元素 v-for="(item,i) of 数组或数值">
在<子元素>中可以将item和i用于绑定值
...
</要反复生成的元素>
3. 原理:
数组中有几个元素,vue在扫描时,就会重复创建几个相同结果的元素。
每遍历数组中一个元素,就可以获得两个值: 第一个变量item获得数组中当前元素的值,第二个变量i获得当前正在遍历的数组元素的下标位置。
每创建一个元素,就会将重复结构中的绑定语法,替换为本次遍历获得的item或index值
4. 强调:
1. 想重复生成哪个元素,v-for就放在哪个元素的开始标签中!
2. v-for=" of "原理和js中的for of完全一样,只不过js中的for of只能获得元素的内容,无法获得元素的下标。而vue中的v-for of即可获得元素内容,又可获得下标。
5. 坑: vue中要修改数组中某个元素,让页面自动更新,决不能用下标修改。必须用API修改。因为vue不监控下标,只监控API调用。
比如: this.tasks[1]="吃饭" 界面上不会变,因为vue不知道1位置的值被修改了。
换成用替换API: this.tasks.splice(1,1,"吃饭"),界面就会自动跟着变化,因为vue始终监控着数组API的调用(其实是因为Vue用一批自己定义的数组API,把原数组API给替换了。而Vue偷梁换柱的数组API中,都带通知机制。所以,调用数组API,vue会发出通知。而下标,vue是换不了的,所以通过下标修改元素值,vue无法发出通知)
6. :key : 每个v-for的元素中,都强烈绑定一个key属性,值为当前元素的下标位置index。
<要反复生成的元素 v-for="(item,i) of 数组或数值" :key="i">
在<子元素>中可以将item和i用于绑定值
...
</要反复生成的元素>
不加:key,每个<li>最终除了内容,在属性上是毫无差别的,当数组中某个位置的元素发生更改时,vue只能用最笨的方法,遍历所有li,把所有li的内容重新更新一遍。
加:key, 每个<li>就变成: <li key="0"><li key="1">...,
就都有了一个唯一的标识。当数组中某个元素发生改变时,
vue就可根据修改元素的下标位置,对应找到相同key值的元素,
只更新这个位置相同的元素,别的元素保持不变。
7. 问题: 没有数组,只有数量,也想反复生成多个相同结构的元素
解决: vue中的v-for of后不但可以写数组,而且可以写数字!——js的for of肯定不行!
如何: <li v-for="i of 整数" :key="i">
结果: <li>1</li><li>2</li>...
何时: 分页按钮
<元素 v-on:事件名="处理函数"
可简写为@事件名="处理函数"
四种情况:
1. 如果处理函数不需要参数,可省略()
2. 如果处理函数需要参数,也可加(实参值, ...)
3. 如果处理函数需要获得事件对象
1. 不需要在绑定时传参
2. 只在methods中定义函数时添加第一个形参e即可。
3. e获得的对象和DOM中事件对象e,完全一样。
4. 如果处理函数即需要获得参数,又需要获得事件对象时:
问题:
如果在绑定时传入实参值,就会顶替事件对象参数的位置。所以,如果想获得事件对象e,又不能添加其它实参值。
解决: 就要在绑定时,即提供实参值,又提供$event。
其中$event是vue中独有的关键词,代表DOM中的事件对象。
问题: {{}}不能用于绑定html片段。如果{{}}绑定的是HTML片段,不会解析其中的内嵌标签和特殊符号,而是尽量保持原样显示
解决: 今后,凡是绑定HTML片段,都用v-html
强调: v-html是指令,应该放在开始标签内!
问题: 如果vue加载慢,可能导致用户短暂看到{{}}语法
解决:
1. v-cloak指令: 专门用于在new Vue加载完之前,临时隐藏个别元素
如何: 2步:
1. 问题: 因为v-cloak自己不带隐藏样式,所以,要先在<style>中用属性选择器为有v-cloak属性的元素,添加隐藏样式。让标有v-cloak的元素先隐藏。等vue在加载完之后,会自动移除所有元素上的v-cloak属性。这些元素就自动恢复显示
2. 在要隐藏的元素上,添加v-cloak属性,不要给属性值。
适用于暂时隐藏大片内容结构复杂的元素
问题: v-cloak是彻底隐藏,连结构和样式也看不见,用户体验不好。
2. 如果希望保持其它样式和结构,仅内容文本暂时隐藏:
<元素 v-text="表达式"></元素>
原理:
如果vue没有加载完,v-text=""属性,反正浏览器也不认识,自然元素中就没有任何显示。
当Vue加载完,解析到v-text时,才为当前元素填充文本内容。
总结: 今后,尽量用v-text代替{{}},绑定普通文本内容
如果字符串需要动态生成,且拼接逻辑比较复杂,首选{{}}+v-cloak隐藏
如果父元素中子元素内容结构复杂,想全部隐藏这部分元素及其子元素时,首选v-cloak;
原理: 只在首次扫描真实DOM树时,替换一次内容,且并没有将这个元素添加到虚拟DOM树。所以,今后变量变化,不会再扫描到这个元素。
何时: 刚巧内容正文中有不希望被替换的{{}}
v-pre
单向绑定: 只能从模型到界面的绑定(M->V)
双向绑定: 即能从模型到界面的绑定(M->V),又能将界面的修改,自动更新回模型中(V->M)
何时: 只要绑定表单元素的值时,都用双向绑定。因为只有表单元素才让用户手动修改内容。
如何:
<input v-model:value="变量">
1. value是提前写死的! 选中不选中,这个value值都是固定不变的,所以v-model不能写在value上
2. 因为选中不选中时修改的是checked属性,所以v-mode绑定的就是checked属性
<input type="radio" value="1" v-model:checked="变量">男
<input type="radio" value="0" v-model:checked="变量" >女
M->V绑定时: 用变量的值和每个value做比较
如果变量的值等于当前radio的value值,则当前radio选中!
如果变量的值不等于当前radio的value值,则当前radio不选中!
V-M绑定时: 当选中项改变时,会用当前选中的radio的value值,更新到data中变量中。
因为复选框在选中或取消选中时,改的是checked属性,所以v-model也要绑定在checked属性上
因为复选框单用,没有value的,所以选中时,会自动将true,更新到变量中。如果没选中,会将false更新到变量
M->V绑定时,也是绑定的bool值
<input type="checkbox" v-model:checked="变量">同意
1. 每一项的value都是写死在每个option上的,所以v-model一定不会写在每个option上
2. 选中项切换时,改的是value属性,所以v-model应该写在select的value上
<select v-mode:value="变量">
<option value="写死的value">
<option value="写死的value">
<option value="写死的value">
当M->V绑定时,用变量的值和每个option的值做比较,如果变量的值等于哪个option的值,那么这个option就被选中。
当选中项改变时,将当前选中的option的value值,更新回变量中
简写: 其实,之前所有v-model:属性名="变量"中的":属性名"都可省略。v-model可自动根据所在元素的不同,自动决定绑定哪个属性。
比如: v-model="变量"
如果出现在文本框中:
<input v-model="变量">
自动绑定的就是value属性
如果出现在checkbox中:
<input type="checkbox" v-model="变量">
自动绑定的就是checked属性
... ...
原理:
凡是带有v-model:的元素,都自动添加事件处理函数: oninput或onchange。在自动绑定的事件处理函数中,内置了自动修改data中变量的方法。——信任
3.
监视函数:
什么是: 当一个变量值发生改变时,自动触发的函数
何时: 如果希望当一个变量值改变时,立刻自动执行一个操作。
如何:
new Vue({
el:"选择器",
data:{ 变量 },
methods:{ 函数 },
watch:{
变量名(){ ... }
}
})
强调: 要监视哪个变量的变化,就要将监视函数的名字起的和那个要监视的变量名一样!
触发: watch中的函数,会在data中同名变量的值发生改变时,自动触发。
何时: 如果程序需要频繁单独调整一个元素的单个css属性时,才绑定style属性。
比如: 游戏中,控制一个元素的上下左右位置
如何: 2种:
1. 将元素的style属性当做一个字符串值来绑定
<元素 :style="变量">
data:{
变量:"left:100px; top:200px;"
}
问题: css属性都混在一个字符串中,不便于操作单个css属性
2. 将style作为一个对象来绑定:
<元素 :style="变量">
data:{
变量:{ left:"100px", top:"200px" }
}
结果: vue会自动将变量中的对象翻译为style所需的字符串: "left:100px; top:200px;"
强调: 数值属性必须带单位
另一种写法:
<元素 :style="{ left: popLeft, top: popTop}">
data:{
popLeft:"100px",
popTop:"200px"
}
结果,也是自动将变量的值放入对象中,并翻译为一个字符串整体,赋值给style属性。
问题: 增多了data中的变量,不便于维护,强烈不推荐。
说明: 如果元素中包含部分不需要变化的内联样式,:style和style其实可并存。不变的css属性放在style中,变化的css属性放在:style中
比如: <div style="position:fixed" :style="popStyle">
结果: 两个style合为一处
<div style="position:fixed; left:100px; top:200px;"
何时: 如果批量应用一批样式时,首选绑定class
如何:
1. 将整个class作为一个大的字符串绑定
问题: 不便于操作其中一个class
2. 将class属性看做一个对象结构来绑定:
<元素 :class="变量">
data:{
变量:{
class1: true/false
class2: true/false
class3: true/false
}
}
结果: 所有值为true的class,最后会拼成一个完整的字符串,赋值给class属性
说明: 如果元素中包含部分不需要变化的class,:class和class其实可并存。不变的class放在普通的class属性中,变化的class放在:class中绑定。
比如: <a class="btn" :class="aClass">
data:{
aClass:{
"btn-success":false,
"btn-danger":true
}
}
结果: 两个class合为一处
<div class="btn btn-danger"
1. 元素内容变化: {{}}
2. 元素属性值变化: v-bind 或 :
3. 一个元素显示隐藏: v-show
4. 两个元素显示隐藏: v-if v-else
5. 多个元素显示隐藏: v-if v-else-if v-else
6. 根据数组或整数,反复生成多个相同结构的元素: v-for :key
7. 绑定事件: v-on 或 @
8. 只要绑定html片段: v-html
9. 暂时隐藏一部分元素,避免用户短暂看到{{}}时: v-cloak
10. 暂时隐藏一个元素的内容,但保留结构和样式时: v-text
1. 先做静态页面:元素内容和需要切换的class先写死
2. 定义或获取数据保存在data中:
1. 如果ajax请求来的数据,就直接放在data中
2. 如果除了ajax请求来的数据,界面还需要其他数据,就在data中补充个别变量。
3. 修改静态页面:
1. 内容可能变化,就用{{}}
2. 属性那可能变化就用:
3. 为写死的class或显示隐藏效果添加绑定条件。比如:class="三目"或v-show="条件"或v-if="条件"等
4. 先不要绑定事件,现在控制台,手工修改data中变量的值,测试界面动态效果是否正常
/***************************************************/
5. 如果第4步页面效果切换正常,才开始:
查找触发事件的元素,添加事件绑定
也可以利用事件委托,原理和DOM中利用冒泡一样。
在事件处理函数中,不需要查找要修改的元素,都是通过只修改data中的变量,来自动更新页面的。
何时: 当一个常用的DOM操作,Vue没有提供对应的指令时
如何:
1. 向vue中添加自定义指令
Vue.directive("指令名",{
inserted(domElem){
对domElem执行常用的DOM操作
}
})
强调: 定义指令时,指令名不要加v-前缀
结果:
1. 在vue类型中就多出一种新的指令
2. 将来运行时,当vue扫描到哪个DOM元素上带有这个自定义指令,就自动触发内部的inserted函数,自动获得当前dom元素对象,然后在inserted函数内,对当前dom元素执行想要的操作。
2. 使用自定义指令: 和使用官方指令是一样的,只不过,不用带参数。
<元素 v-指令名>
强调: 使用指令时,必须加v-前缀
什么是: 不实际保存属性值,仅通过其他属性计算获得属性值
何时: 只要一个属性的值不是现成的,而需要通过其他属性计算,才能获得!
如何: 2步:
new Vue({
el:"选择器",
data:{ 变量 },
methods:{ 函数 },
watch:{ 监视函数 },
computed:{
属性名(){ //计算属性本质是一个函数,但是,函数名却是名词!
通过data中其它变量,计算获得统计结果。
return 计算结果
}
}
})
计算属性的用法,和data中普通变量完全一样!
强调: 不要加()
vs methods
methods中的函数,反复调用几次,就需要重复计算几次,如果反复使用效率低
computed中的计算属性,只在首次使用时,计算一次。之后,即使重复使用计算属性,也不会重复计算,效率高!Vue会缓存计算属性的结算结果。只有计算属性依赖的变量值发生变化,才重新一次,并重新缓存计算结果,反复使用。
总结:
1. 如果你仅希望使用计算结果这个值时,首选计算属性
2. 更希望执行一系列操作流程时,首先函数
什么是: 专门对原始类型的值进行加工后再显示的函数
为什么: 因为有些数据库中保存的原始值,人看不懂,所以需要加工后,再给人看。
比如: 性别! 日期和时间! 状态!
何时: 只要原始属性,必须经过加工后,才能给人看时
如何:
1. 添加过滤器
Vue.filter("过滤器名",function(oldVal){
return 处理后的新值
})
2. 使用过滤器:
在绑定语法中: {{变量 | 过滤器}}
过滤器还可以加参数:
1. 添加过滤器时,就要额外多写几个形参
Vue.filter("过滤器名",function(oldVal, 其它形参...){
return 根据不同的实参值返回不同的结果
})
2. 使用过滤器时,可加()传入实参值:
在绑定语法中: {{变量 | 过滤器(实参值, )}}
强调: 第一个实参值,就写在实参列表的第一个位置,也不会覆盖过滤器函数定义时的第一个形参oldVal。而是自动传给过滤器函数定义时的第二个形参变量。过滤器第一个形参,永远接到的是变量的原始值。
多个过滤器可以连用:
使用过滤器时,多个过滤器可用|连接
强调: 后一个过滤器收到的不一定是原始值,而是前一个过滤器处理后的中间产物
什么是: 专门发送ajax请求的js函数库
为什么: 发送ajax请求:
1. 编写原生ajax四步: 繁琐!
2. 自己封装ajax函数: 功能弱!
3. jQuery中有ajax函数: $.ajax()
在Vue通常不会用到jQuery库,所以,仅仅为了一个ajax函数,将整个jQuery库引入项目,小题大做!
4. Vue官方提供了vue-resource模块: 已不再更新
5. Vue官方推荐使用第三方函数库: axios
何时: 今后只要在vue中发送ajax请求,都可用axios
说明: axios其实是第三方独立的函数库,并不仅仅局限于vue中!
如何:
1. 发get请求:
axios.get(
"服务端接口地址",
{
params:{
参数:值,
... : ...
}
}
).then(result=>{
result.data 才是服务器端返回的结果!
})
2. 发post请求:
补: 如果服务端给出的接口是post类型,可用postman软件先测试一下。
axios.post(
"服务端接口地址",
//本来需要"查询字符串"
//比如:"uname=dingding&upwd=123456"
//不推荐
//推荐: 对象语法
//但是必须借助一个工具叫qs.min.js
//<script src="js/qs.min.js">
//Qs的stringify函数可将对象转为查询字符串格式
Qs.stringify({
变量1:值1,
变量2:值2
})
//转换之后: "变量1=值1&变量2=值2"
).then(result=>{
result.data 才是服务器端返回的结果!
})
什么是: 拥有专属的HTML,CSS,JS和数据的可重用页面独立区域
为什么: 重用!
何时: 只要发现一个功能区域需要反复使用,都要先封装为组件。
如何:
1. 创建一个组件:
Vue.component("组件名",{
//每个组件内都是一个完整的小社会
//new Vue中能写什么,组件中就能写什么!
el:"选择器",
template:`HTML片段, 必须包在一个唯一的父元素内`,——因为组件的HTML片段不是只用一次,而是会被反复复制到页面上,用了几次,就复制几次。所以,这最初的一次HTML片段定义,就称为今后的模板
data:{ 变量 },
data(){
return { //=new Vue中的data
变量:值,
... : ...
}
},——因为每重用一次组件,都需要一个新创建的专属的数据,而不能和之前创建的混用!所以,每使用一次组件时,都自动调用data()函数,返回一个为本次组件专门创建的专属的新对象
//其它和new Vue()完全一样!
methods:{ 函数 },
watch:{ 监视函数 },
computed:{ 计算属性 }
})
2. 反复使用组件:
组件其实就是一个可重复使用的自定义的HTML标签而已 。<组件名></组件名> 尽量用双标记
强调: 如果组件名由多个单词组成:
不要用驼峰命名: 因为组件名就是标签名,而HTML不区分大小写,所以拼写相同,但是大小写不同,是不能区分不同标签的。
所以,如果组件名由多个单词组成,可用-分割多个单词: <my-header> <my-counter>
原理:
1. new Vue()在扫描真实DOM树时,发现组件标签
2. 在Vue家里找到组件标签同名的组件类型
3. 用组件类型中的template HTML片段,代替界面中组件标签的位置
4. 为这个位置的组件标签,创建一个专属的data{}对象
5. new Vue()发现几处组件标签,就用组件中的HTML模板片段代替几处组件标签,就反复调用几次data()重复创建几个相同的data对象给每处组件,专属使用,互不干扰。
什么是组件化开发: 将来所有的页面,都是由组件组成的。拿到页面后,第一件事是划分组件
为什么: 1. 便于分工和协作
2. 松耦合
何时: 今后几乎所有前端项目都是组件化开发
如何:
1. 将一个页面划分成多个组件,并创建多个组件类型
2. 在一个html中,将分头编写的多个组件,再组合起来!
原理:
1. new Vue()会扫描主页面,只要碰到组件标签,就会用组件的模板片段代替标签所在的位置
2. 替换后,为防止有更子一级组件,还会重复扫描更替换上来的组件模板的内容,如果发现更子一级组件标签,则继续替换。
问题: Vue.component()创建的是全局组件!没有使用限制
解决:组件的分类:
1. 根组件: new Vue()
2. 全局组件: 可以用在任何页面的任何位置,没有限制
3. 子组件: 仅能在一个指定的父组件内使用的组件
如何: 2步:
1. 创建子组件:
1. 内部结构和全局组件是完全一样的
2. 但是要将全局组件降级为普通js对象
且子组件的对象名必须用驼峰命名
2. 为父组件添加components:
父组件{
template:`...`,
data(){ return {} },
...
components:{
子组件对象名,比如: todoAdd, todoList, todoItem
}
Vue会自动将todoAdd -> <todo-add>
todoList -> <todo-list>
todoItem -> <todo-item>
自动翻译
}
1. 父->子:
问题: 父组件中的成员,子组件默认不允许随意访问!因为,每个组件中的数据,归当前组件专属独有!
解决:
1. 父组件中:
<子组件标签 :自定义属性名="父的变量"
2. 子组件中:
子组件={
template:`html片段`,
data(){ return { } },
props:[ "要接收的自定义属性名" ],
...
}
结果:
父组件中用到了一个子组件:
Vue.component("parent",{
template:`<div>
<h1>父组件</h1>
<child v-for="(item,i) of arr"
:key="i" ></child>
</div>`,
data(){
return { arr:[1,2,3] }
},
methods:{
handlerTest(item,e){
console.log(item);
console.log(e);
}
},
components:{
child
}
})
子组件中通过触发自定义事件向父组件传递一个数据:
var child={
template:`<div>
<h2>子组件</h2>
<button @click="handler">click</button>
</div>`,
methods:{
handler(){
this.$emit("test","something");
}
}
}
要求: 父组件的函数handlerTest中如何即输出item,又输出子组件传入的"something"字符串?
答: <child ... @test="handlerTest(item, $event)">
首先,子组件给父组件传参,需要用到自定义事件
2步:
1. 父元素在自己的<template>中的子元素标签<child>上用@自定义事件名="父组件处理函数"方式,将父组件的处理函数绑定在子组件的自定义事件上。
2. 子组件在单击按钮时,通过this.$emit("自定义事件名",参数值)方式,触发父组件绑在自己身上的自定义事件,并将参数值沿着事件触发的路线,顺便传递给父组件提前绑定的事件处理函数。
结果: 即使父组件中为子组件绑定自定义事件的处理函数后没有加(),子组件传的参数值,也会默认放在VUE框架自动生成的$event关键字中,作为事件处理函数的第一个参数默认传入父组件处理函数内。
父组件处理函数只需要在定义时,添加一个形参等着接就行了。
比如: 子组件中: 触发名为test的自定义事件,自动传递数据"something"到父组件处理函数
this.$emit("test", "something")
父组件中:
<child @test="handlerTest">
methods:{
handlerTest(val){
console.log(val)
}
}
结果: handlerTest中的形参val自动接到的就是VUE在事件发生时自动创建的$event关键字,其中保存的就是子组件传来的"something"字符串。
问题: 这个题: 事件处理函数需要两个参数,第一个参数是来自于v-for的item变量,第二个参数才是子组件传来的事件参数值。
解决: 因为默认$event是作为第一个参数传入的。如果第一个参数不是$event,就不能用默认方式传值。而应该在绑定自定义事件时,显式写明每个参数的来源,比如:
@test="handlerTest(item,$event)"
这就是说,当事件发生时,处理函数的两个参数分别来自于item变量和VUE内置对象$event,而$event会自动收到子组件触发事件时,传来的参数值。
什么是: 一个应用程序中,只有一个完整的HTML页面,其它所谓的页面,
其实都是一些较大的组件而已。所谓的页面跳转,其实是更换不同的组件加载而已。
1. 请求次数:
多页面应用,每更换一次页面都需要重新请求服务器,重新下载网页文件——增加了请求次数,增大了服务器的负担,同时也增加了用户等待
单页面应用,在首次请求页面时,就把所有的组件一起下载下来。今后,切换页面其实就是在客户端本地切换不同组件显示而已,不需要反复请求服务器——减少了请求次数,减轻了服务器的负担,避免了切换页面等待的事件,提高了用户体验。
2. 加载效率:
多页面应用,每更换一次页面都需要重建整棵DOM树,效率低!
单页面应用,因为只替换页面中部分内容,不需要重建整棵DOM树,所以效率高!
3. 公共资源: bootstrap.css bootstrap.js jquery.js
多页面应用,因为每个页面都包含<link><script>,所以,每更换一次页面都重新请求这些公共资源——也增加了请求次数,增大了服务器的负担,也增加了用户等待的时间
单页面应用,因为唯一完整的index.html文件只在首次记载时,下载一次,<link>和<script>也仅在首次加载时下载一次。之后更换页面组件,不会重新加载<link>和<script>——减少了请求次数,减轻了服务器的负担,避免了切换页面等待的时间,提高了用户体验。
4. 页面过渡效果:
多页面应用不可能实现页面过渡效果,因为两个.html文件不可能并存在一个浏览器窗口内!
单页面应用,因为所有组件都是以元素方式,保存在客户端,所以,完全可以通过定位,CSS3变换的方式,实现页面组件之间的切换效果。
5. 首屏加载效率: ——SPA的缺点
多页面应用,首屏只需要下载第一个页面的内容,其它页面内容暂时不需要下载,所以,首屏加载快!
单页面应用,首屏就要把所有组件内容都下载下载,所以首屏加载慢——已解决: 配合打包工具webpack,让组件的js文件异步延迟加载: 先加载首屏组件用着,其它组件js在后台异步下载,不影响首屏下载速度。
1. 创建一个唯一完整的HTML页面,应该包含new Vue()
引入: <script src="js/vue.js">
<script src="js/vue-router.js">
在<div id="app">内: 添加占位符
<router-view/>
2. 创建多个组件:
需求中有几个页面,就创建几个子组件来包含页面的内容。
多个页面共用的组件,才创建为全局组件,并在各个页面中需要的位置引入全局组件标签。
3. 创建路由字典和路由器对象:
路由字典: 保存一组相对路径和组件名对应关系的数组:
var routes=[
{path:"/", component: index}, //默认路径
{path:"/index", component: index},
{path:"/details", component: details},
{path:"/login", component: login},
{path:"/*", component:{
template:`<h2>404:Not Found</h2>`
}}
]
问题: routes只是一个字典,无法完成替换组件的动作:
路由器对象:
创建路由器对象,将路由字典装入路由器对象中。
var router=new VueRouter({ routes });
原理:
路由器对象始终监视地址栏中的url地址后的#锚点地址变化。(Vue客户端路由采用的是锚点地址)
只要锚点地址变化,立刻从路由字典中找到对应的组件,替换唯一完整的HTML中的<router-view>区域
4. 将所有页面组件、路由器都引入唯一完整的HTML中,还要将路由器对象,装入new Vue()中
//<script src="全局组件.js">
//<script src="2_index.js">
//相当于引入了var index={}
//<script src="2_details.js">
//相当于引入了var details={}
//<script src="2_router.js">
//相当于引入了var router=new VueRouter({routes})
new Vue({
... ,
router
})
1. 在HTML中:
不要再用a
<router-link to="/相对路径">文本</router-link>
自动翻译成
<a href="#/相对路径">文本</router-link>
2. js中:
this.$router.push("/相对路径")
路由器
1. 接收参数的页面的路由字典中就要配置参数:
{path:"/details/:lid", component:details, props:true}
变量名
其中: props:true,让url中参数自动成为props中的自定义属性
2. 在要接收参数的目标组件中,添加与路由参数同名的自定义属性变量:
var details={
...
props:["lid"], 这个lid就可自动接到url中的值,在details页面内,像普通变量一样使用
}
3. 跳转时: 应该在相对路径之后加"/参数值"
强调:
1. 不要有?
2. 不要写参数名=
3. /直接跟参数值
什么是脚手架: 已经包含标准项目文件夹结构和核心功能代码的半成品项目
为什么:
1. 简化开发:
2. 标准化开发:
何时: 今后几乎所有项目的创建,都是用脚手架代码,比如: nodejs express, vue, 微信小程序 , ng, react。所以今后,每学一项新技术,首先要找脚手架代码!
1. 全局安装生成脚手架代码的工具:
npm i -g @vue/cli
2. 用脚手架生成工具为本次项目生成一套脚手架结构代码:
vue create 项目名
结果:
1. 当前目录下多出一个项目名文件夹
2. 进入文件夹: cd 项目名,
3. 运行脚手架半成品项目: npm run serve
1. 打开vscode->文件->打开文件夹->选项目文件夹
2. 右键单击package.json文件,选在终端中打开
3. 在终端中输入npm run serve
4. 按住ctrl点链接:http://localhost:8080
结果自动打开浏览器,显示正在调试的页面
public文件夹:
不需要编译和打包的文件,都要放在public文件夹下。
包括:
1. 唯一的index.html页面
2. 图片文件夹
3. 从第三方下载的已经压缩过的通用的库:
bootstrap.min.css
jquery-1.11.3.min.js
bootstrap.min.js
4. 在唯一完整的index.html开头,引入公共的css或js库
src文件夹:
一切我们自己编写的内容都要放在src文件夹下
包括:
页面组件都要放在views文件夹下
全局组件/子组件都要放在components文件夹下
自己编写的公共的css和js库都要放在assets下。比如: base.css
App.vue相当于之前的<div id="app">
main.js相当于之前的new Vue({})
很多配置,都需要在main.js中的new Vue()之前定义好,才能new Vue()
比如:
1. 全局组件的创建
2. axios的配置和引用
router.js相当于之前的路由字典和路由器
当添加了新页面组件,或删除旧的页面组件,都要在router.js中配置路由字典
1. 一个.vue文件就是一个组件,要创建几个组件,就要添加几个.vue文件
2. 每个vue文件中,都包含3样东西:
1. HTML:
每个组件的HTML片段,必须写在一个<template></template>内——相当于之前组件对象中的template:`HTML片段`属性,且不再放在字符串中,有了提示和颜色显示。
2. 组件对象的js:
export default {
除了没有template外,其余和之前完全一样!
}
但是,vue脚手架采用的是模块化开发!
回顾: node中的模块:
1. 亮哥封装了一个pool模块,其中包含一个query函数。如果亮哥想让别人使用这个模块的内容,必须使用module.exports将内容以对象形式抛出
function query(...){...}
module.exports={ query }
2. 别人想用亮哥的pool模块中的query,就要用require()引入亮哥抛出的模块,然后才能调用模块对象中的方法
var pool=require("../pool.js")
pool.query()
Vue脚手架里的模块化开发要求:
1. 每个组件其实都是一个模块对象,组件对象定义好后,都要用 export default { ... } 抛出。
export default代替了module.exports=
2. 任何想使用这个组件对象的地方,都要用import 变量 from "路径/组件.vue" 先引入组件对象,并起别名,才能使用。
import 变量 from 代替了const 变量=require("...")
3. CSS: 写在专门的<style></style>标签内。
1. 可直接将css代码写在<style scoped></style>内部
scoped 可自动为每个选择器加一个统一的随机的前缀!与其他组件的即使同名的class自动区分。比如: .fade{ ... } 会被scoped翻译为: .fade[随机父组件名]{ ... }
问题: css和HTML,js混搭不便于维护。
2. 在assets/css文件夹中独立编写css文件,再在<style></style>标签内,用@import url(... )引入!
问题: scoped失效,只能靠自己的书写习惯来避免组件间样式冲突。
每个组件(根组件, 全局组件, 子组件)都有自己的一套加载过程——称为组件的生命周期
1. 创建create:仅创建了new Vue()或组件对象,同时创建了组件对象中的data对象。
2. 挂载mount:开始扫描真实DOM树,形成虚拟DOM树,并首次替换绑定内容为data中的变量值。
3. 更新update:在任何位置修改了data中的变量值,都会触发更新阶段
4. 销毁destroy:只在调用$destroy()函数,销毁组件时,才触发销毁阶段
为什么: 有些事儿,必须在指定的阶段,自动被调用执行
比如: 当组件对象创建后,自动发送ajax请求,加载数据
如何:
vue中每个生命周期的执行阶段前后,都各有一个钩子函数。
包括:
问题: 一个带参数的路由地址,仅修改参数值,不修改相对路径名,整个页面不刷新的,无法加载新参数值对应的内容。
因为是单页面应用!
解决方案一: 地址栏中参数值变化,说明props中的变量值跟着变了!可以在watch中,监视变量,只要props变量变化,就重新ajax请求数据,修改data中的变量,引发更新页面。
解决方案二: 监视地址栏对象$route
DOM中有location对象代表地址栏,可获得地址栏中的信息。
Vue中有$route代表地址栏!
比如: 不用props获得参数lid
也可以this.$route.params.lid
$route vs $router
$router 是路由器对象,专门执行跳转动作的对象 $route 是封装地址栏信息的对象,是保存数据的,不执行操作。
问题: 服务器端部署位置的地址,经常发生变化。如果客户端每个.vue文件中都要写完整的域名/相对地址,那么每次服务端地址修改,都要修改所有.vue中的请求地址——相当麻烦!
解决: main.js中
axios.defaults.baseURL="http://服务器地址"
其它.vue中:
this.axios.get("/相对地址",参数).then(...)
结果: 将来运行时,会将baseURL+/相对地址,形成完整地址
而如果服务端地址变化,则只需要修改main.js中baseURL一处,就可切换不同服务端!