初识Vue
el:选定元素,可以用css选择器,也可以直接使用dom对象
data():解析vue模板时所需的数据,是vue管理的函数/对象
method:保存vue管理的各种函数的对象

如果有多个容器绑定了一个vue对象,vue对象只会绑定从上往下第一个容器
如果一个容器绑定多个vue对象,则只有第一个vue对象会被绑定
如果vue对象的data内有相同的属性名,则以最后的属性名来修改容器内的值
可以把data内数据设置成多级对象的结构来避免混淆:

一些特殊的东西
debugger:在程序运行时代替断点,用于调试

template标签:能够作为模版的标签,但本身不会被渲染到页面上

中转变量:在非data的配置项内使用this.xxx可快速设置全局变量
添加变量:使用$set
由于直接编辑对象里的变量值可能不会引起vue重新渲染,在添加变量值时最好不要使用直接赋值
错误写法,没有getter/setter
应写成如下形式:
自动生成getter/setter
在使用过一次$set之后,就可以按之前那样的直接赋值的写法去编辑变量了,没有必要再次重新$set
延迟解析:$nextTick
像窗口聚焦失焦这类事件,需要在窗口出现后才有效。而由于有时窗口聚焦失焦的事件回调是跟编辑窗口是否出现写在同一函数内的,而Vue解析时会等待函数完全执行完再重新解析,可能会导致这些事件失效,因此需要使用定时器或者$nextTick


更新数据时防止属性丢失

{…this.info, …dataObj}的写法,意为以dataobj的所有存在的值去给info所有属性赋值,从而合并对象,不存在于dataobj的值以info存在的值作为默认值
对象内属性的顺序是可以乱的,但最好按顺序修改
模板语法
插值语法(双大括号表达式)与指令语法(v-开头)

只有v-bind:可以简写为冒号:

serve与build
vue serve指令是用来运行测试项目的,而在发布项目时要进行打包发布,使用vue build把vue文件转成原生html文件,会生成在dist文件夹。并且生成的文件必须在部署服务器后才能正常显示页面
数据绑定


单向数据绑定:v-bind:xxx/:xxx
vue对象单向绑定容器的值,在vue插件的分支内修改vue对象会直接影响到容器,而容器内数据不影响vue对象的数据
单向绑定可以绑定大部分容器
双向数据绑定(可实时检测数据值):v-model: value = / v-model =
vue对象与容器双向绑定,无论修改哪个都会修改到另一个的值。
如果修改容器,则会同步修改vue对象的值,若这个值还单向绑定了其他地方,则其他单向绑定的表达式也会同步变化
v-model只能绑定表单元素的输入类元素(含value),并且可以简写为v-model属性,因为默认绑定了value
el与data的两种写法

el创建时挂载与创建后挂载(哪种都可)

以$开头的属性是供用户自行使用的,剩下的是vue使用的
$mount属性来自v的对象原型(可以通过__proto__属性查看)
data的对象式写法与函数式写法(建议只用普通函数式)

使用函数时,vue实例对象会调用这个函数并返回给data,若使用箭头函数则没法找到这个vue实例,因此建议用普通函数
data的写法进一步简化如下:

MVVM架构模型



因此一般用vm表示vue实例
Model(data)里的数据会被代理到vue实例对象(vm)身上,而容器(View,或称视图)可以看到vue实例对象的所有属性,包括__proto__等被嵌套的对象的属性
在容器内使用这些属性时不需要再写vm.前缀,会自动调用
嵌套的对象的属性也不需要前缀(如__proto__里的$emit函数)

Object . defineProperty( obj, property, descriptor )

使用该函数只定义value的话,则不可被枚举、修改、删除和配置
数据描述符
此处意为不能包含get/set

存取描述符

getter在读取属性值时自动触发,且返回属性值
setter在设置属性值时自动触发,且能获得设置的属性值(无需返回)
(注:闭包特性,内部域可以修改外部域变量的值并造成实质变化)


两种描述符的区别及注意事项



注:enumerable和configurable在两种修饰符情况下都能使用

案例:如何令内部属性与外部属性同步

如果直接在person里赋值age
如果修改时触发的setter不修改number的值,则修改age后再读取时仍会读取到原本的number值,修改不成功;需要在setter里把getter返回时指向的变量改掉
数据代理

如图,只需通过defineProperty函数在obj2设置getter和setter,并分别关联到obj.x即可实现最简单的代理

vm实现的数据代理


vm首先将data进行数据劫持并绑定到自身,将其名称改为_data,随后将其内部的属性(vue._data.name)与外部属性(vue.name)进行双向数据代理,因此在容器内使用vue语法时无需添加_data前缀。因此容器内{{_data.name}}与{{name}}是都能使用的
函数methods

methods与data同级,并且也会被挂载在vm身上
methods内的函数不能用对象式进行配置,因此也不存在默认调用等功能
总而言之,methods只用于保管函数数据供vue使用。类似data
事件处理v-on:事件名=’函数’/@:事件名=’函数’
v-on
通过v-on
该回调函数必须写在该vm内,作为属性methods的值,并且有一个参数event表示事件对象(在容器内设置参数会导致事件对象丢失,这时可以添加一个**$event**参数来重新添加事件对象)
methods内配置的函数包含的this指针由vm配置为指向vm本身,因此methods内配置的函数想使用data数据时只需直接使用this.name。函数会被vm挂载在自己身上
如果函数只执行简单的语句,可以直接写在标签里。如下
此时也不再需要this指针,并且语句之间必须使用分号隔开
而由于vm只会在自身上寻找对象,不能使用太复杂的语句(如要使用window自带的函数,则必须把window写进data,太麻烦)


事件修饰符

若要阻止默认行为,本应在事件函数内添加e.preventDefault,但可以用@click.prevent=’showinfo’的形式实现相同效果,此处的**.prevent**就是事件修饰符
同理,阻止事件冒泡,本应用e.stopPropagation,此处可用**.stop**
次此外常用的还有只触发一次事件,用.once
.capture令事件在捕获阶段而不是冒泡阶段被处理
(捕获触发的层级顺序与冒泡相反)
.self使event.target是当前操作的元素时才触发事件(也阻止冒泡)
.passive立刻执行事件的默认行为,无需等待事件回调执行完毕
例如wheel事件会检测鼠标滚轮,此时若还有页面滚动条的默认事件要执行(页面往下滚动),则触发了滚轮之后不会立刻执行,而是先执行wheel绑定的回调函数,回调函数完成后再执行默认事件
事件修饰符可以连着写,如@click.prevent.stop=”func”(有先后)

键盘按键keyup.enter / keydown.enter
正常情况下,需要判断回车事件时得自己写函数,用事件对象的keyCode去判断是否为回车键才继续执行程序

而vue内可以用事件修饰符.enter来简便地进行判断
形如“CapsLock”这种两个单词的,使用“caps-lock”的方式命名
如果需要组合键,特别是对于系统修饰键,可以通过连续修饰来检测。如@keyup.ctrl.y

计算属性computed
使用插值语法实现需求(复杂)

使用函数实现同样的需求(相对简洁)
data的每次变化都会引起vm对容器的实时更新,更新的时候若检测到函数则直接会重新调用一次。因此可以在绑定容器的标签内调用一个函数并返回一个值,来用函数进行值的计算

计算属性的写法(更好的办法)
在vm内,data用来保存属性,而computed才用来保存计算属性。
computed里存放的计算属性以对象形式存在,且必须包含一个getter用于在被读取时返回计算属性的值
与data类似,computed里的计算属性会被vm计算后直接挂载到vm身上,可以省略vm.去使用,如{{fullname}}。但vm身上的vm是计算好的值,而不是对象,更没有get方法
getter里的this指向被vm设置成了vm自己本身,可以避免繁琐的跨域访问,直接return this. name。也因此不能使用箭头函数
调用计算属性的getter之后vm会进行缓存,因此容器内多次使用同一个计算属性只会在初次读取时调用一次getter。而为了防止缓存引发数据滞后,在所依赖的数据(数据必须在vm内)发生变化时vm也会再调用一次getter

setter不是必须项,但是若计算属性会被修改,则必须添加setter
同样,要实现双向同步,setter里面要修改getter返回值的来源数据
而且虽然vm身上fullname变化了,但是data和getter的数据源没变化,容器包括里面的fullname并不会被更新


计算属性的简写(只读的情况,不包含setter)
不需要在把计算属性写成配置对象形式,只需要写成函数,且默认会作为getter。注意使用时不加括号,仍为{{fullname}}

开发者工具的坑
如果页面没有用到数据,但是有修改数据的操作,则开发者工具不会正常显示数据被修改,但是数据实际上已经被修改了。
监视属性watch


注:计算属性也可以被监视
属性可选配置项:deep、immediate
watch属性

需要监视的属性以对象形式保存,其中包含一个handler函数,这个函数有两个可选参数,分别表示新值和旧值
还有一个可选的属性immediate,用于控制是否立刻执行一次
如果被监视的属性不存在,不会报错,因此必须要确认存在再监视
在vm建立之后进行监视:vm.$watch(‘被监视的属性’,配置对象)
注意一定要加引号,因为key/value的实际存在形式是字符串/数字

深度监视
如果numbers作key,data里的numbers里包含a、b两个属性,则作为key的numbers的value值是包含a、b的结构体的地址值而与a和b的值无关,a和b的值变化不会被vue监视到
监视多级结构中某个属性的变化,需要把被监视的属性从data下开始逐级定位,最后写成字符串形式作为watch的key(key本来就该有引号,只是平常可以简写成不带引号)
vue中的watch默认不监视子级属性

另一种办法,在被监视的对象里添加配置项deep:true,监视所有子属性的变化

监视的简写形式
如果不需要immediate和deep,可以直接把被监视的属性作为handler函数进行配置

如果是在vm外进行配置,则配置项改为传入一个回调函数作为参数
注意不允许传入箭头函数

其他情况:

watch跟computed的区别

watch可以用于执行异步任务,而computed只能执行同步任务。而在执行同步任务时,watch还必须监视每一个可能会造成改变的因素造成写法上的繁琐,computed虽然无法异步但是写法简单。根据情况watch和computed各有优劣

如图,watch还需要自定义一个fullname在data里,并在配置监视时进行修改的函数
注意,此处定时器函数setTimeout函数是vue管理的函数,而回调的无名函数不是,因此一定要用箭头函数来使用定时器函数的this来指向vm对象,否则此处的this会指向window(因为回调函数是事件循环时window调用)
被vue管理的函数要写成普通函数,不被管理的写成箭头函数
vue绑定样式
绑定class样式
字符串写法

正常的样式正常写,变化的样式用绑定的方式写(v-bind简写)
正常的多个class会只使用一个忽略其他,而绑定的会被合并
同样的,class名要出现在data里,修改class名即修改data

数组写法
如果使用数组名,则会一并显示数组内元素
若数组内元素为变量的情况“
![]()
”
在devtools里使用shift和push可以方便的进行修改class

对象写法
对于固定的类名,通过对象属性值为true/false进行切换是否应用

绑定style
对象写法

注意单位要用字符串拼接写
fontSize由css的font-size得来,注意写法
若key与val重名,可简写为这种写法
对象数组写法

条件渲染

切换频率高建议v-show,频率低建议v-if
v-show /false
效果是给style添加display
可以用v-show:“a”,通过修改a来调整显示情况
v-if与v-else-if、v-else
**注意:**if、elseif与else之间不能被分割,否则后续失效
v-if
v-if会直接删除整个节点,变为空注释



如图可实现简单的切换
v-else-if

v-else-if的逻辑与正常的else if完全相同,会跳过
v-else
v-else的逻辑与正常的else完全相同,且无需表达式

使用template进行临时打包

template模版标签会在最终渲染的时候去除而只留下内容部分
template标签只能配合v-if,不能配合v-show
列表渲染:使用v-for

persons为数组或对象,p为元素,类似for i in range的写法
v-for应当与**


获取元素与索引的写法
括号可以去掉但不建议,in可换成of但也不建议

当使用对象的时候,两个值分别是值value和属性key


字符串也算作对象或数组,因此也可以使用,但较少使用
遍历指定次数,较少使用。如下为从1遍历到5

遍历列表时key的作用与问题

vue在实现时先生成虚拟node再生成真实node,而用户在真实node上的操作不会被vue检测到
vue在实现真实node时使用对比算法,而遍历列表时vue使用key作为对比的标识。若key不同则直接生成,key相同则对比两者内容,只改变变化的部分,因此真实node上的修改会被保留
未使用key时,默认使用index作为key

index作为key时,若在列表头部插入节点,则对比时仅修改不同的部分,相同的部分会直接使用之前渲染的真实节点且造成大量渲染

使用唯一标识作key时,则头部被插入的节点不会因为key相同而进行对比保留。剩余节点在对比过后进行复用
列表过滤
利用indexOf和filter函数,结合监视/计算属性实现对数组的过滤
arr.indexOf(‘a’)返回字符a的索引,不包含则返回-1
arr.filter((p)=>{
…
return 条件true时的表达式
})
对每个参数进行判断,返回过滤后的新数组,不更改原数组
首先要使用v-model双向绑定搜索关键字

注:要实现搜索功能一定不能更改原数组
所有字符串默认包含空字符,且默认其索引为0
为了使页面立刻渲染一次,需要使用immediate
用计算属性实现

由于watch是对keyWord进行监视因此传参传val即可,而computed要使用this指向keyWord来判断
外部的return为返回过滤后的filPerson,内部的return为filter的过滤条件
列表排序
用变量定义排序类型,再用arr.sort回调

Vue监测数据的原理

如果在data里直接写getter和setter,则getter和setter返回时会引起无限递归的问题。vue的做法是创建构造函数observer,并使用defineProperty函数去在监视器内设置getter和setter,再用这个构造函数去构造一个修改过的data。并且vue通过递归实现了深度监视
如图为模拟表层监视data的方法,vue实现的是深度监视

Vue.set / vm.$set (target, key, val)
源代码内的undefined值会被vue解析为不输出
输出data内的对象的不存在的属性,不会报错并显示为无输出
在创建真实节点时,data内的数据会被vue自动设置响应式设计,而此时若手动添加数据到data,由于是在真实节点上添加新数据的操作,不会被vue检测到因而不会反馈到真实节点,也不会获得getter和setter。
Vue提供Vue.set()和vm.$set()来实现实时设置响应式数据
由于数据代理,不需要写vm._data.student,写vm.student即可
注意vm与_data都无法作为target,
操作对象时,key、val都要用字符串

在如methods内应用时,应当使用this

Vue监测数组

vue不直接监测数组,而通过监测修改数组的方法实现响应式
在vm身上的数据调用的这些数组方法是经过vue包装过的
push 末尾插入
pop 末尾删除
unshift 头部插入
shift 头部删除
splice 任意位置和长度替换
sort 排序
reverse 反转
Vue.set可用于数组,key即为索引

通过如unshift对数组插入对象数据时,对象里的属性也是响应式的
因此虽然直接修改数组元素不被vue监视,修改对象类型的数组元素的值却是能被监视到的
收集表单数据


单选框可以使用相同的name来配置单选,但value值应该不同
复选框的v-model默认收集ckecked,配置了value,并且v-model绑定数组才会收集value
input的type可以控制输入类型,而输入值要用如v-model.number控制
过滤器



过滤器函数保存在filters对象内,函数的形参即为val,前面的val为第一个形参,返回值为过滤之后用于使用的值
使用val丨fliter的格式来使用过滤器
也可使用val1丨fliter(val2)的格式使用过滤器,对应多个函数形参
也可使用val1丨fliter1丨fliter2来多次过滤
过滤器不改变原本值,返回过滤后的值
写在vm内使用filters,与data、methods平级时,为局部过滤器

使用Vue.filter函数,传入过滤器名与回调函数来使全局过滤器
过滤器支持插值语法和v-bind,不支持v-model
指令
内置指令
常用的指令,指令使用方式在前面

v-text 向其所在标签插入文本


v-text会替换掉整个标签内的文本,而且无法解析标签,会将所有内容当做字符串去解析
v-html 支持结构解析的插入文本(存在安全性问题)
用如图方式使用v-html并在a标签的href内写JavaScript代码进行跳转到未知服务器并携带cookie会造成cookie外泄,除非cookie支持HttpOnly使该cookie只能被http协议解析

v-cloak 会被vue加载时删除的无值属性



v-once 只渲染一次数据的无值属性


v-pre 禁止vue解析该节点,提升静态节点的渲染速度


自定义指令 directives

函数式配置
函数式配置指令时,指令函数接收的第一个元素element为指令所在的真实dom节点。第二个元素binding为由指令绑定的表达式相关的配置
当指令与元素成功绑定时会调用指令,当指令所在的容器发生更新时,会立刻调用一次

第二个元素的详细内容如上,其中expression为v-xx=之后的表达式,value则是表达式的值,name是指令的名称,rawName是用户实际使用时的名称
![]()

对象式配置
由于函数式配置只会在指令绑定时、指令所在模版更新时调用函数,因此需要使用对象式配置来控制在元素被插入页面时调用函数
focus函数只有在元素存在时被调用才起作用,指令绑定时元素还未被创建因此用函数式配置无法默认调用focus

对象式配置内含有bind、inserted、update三个函数,函数式配置默认实现了bind和update函数的功能
注意事项
1.对指令进行命名时不能使用小驼峰式命名法,因为vue不区分指令名的大小写,对多单词的命名应该使用v-a-b去命名指令,而在配置指令时使用‘a-b’:function(){}或者‘-a-b’(){}
2.directions内的this一律指向windows而不是vm,需要的各种数据在函数的形参binding内有给出
3.指令分为全局指令和局部指令,
在vm内配置的局部指令只能在vm绑定的容器上起作用,用到的是vm.directives配置对象;
在Vue上可以配置全局指令,用到的是Vue.directive【此处没有s】,接收的第一个参数是指令名,第二个参数是配置对象或者回调函数
生命周期



生命周期函数与methods同级,且内部的this默认指向vm或组件实例,vue会在特定的时期执行这些函数
完整的生命周期
1.首先:new Vue,创建一个vm(此时vm已经存在)
beforeCreate():
此时初始化生命周期、事件,但是未进行数据代理,data和methods还未挂载到vm上因此无法访问
created():
此时数据监测、数据代理被初始化,data和methods被挂载到vm,可以访问
2.其次:判断vue是否绑定了el去挂载
如果没有:等待vm.$mount(el)执行去绑定el
如果有:判断vm是否有template属性
如果有:将template属性内的东西交给渲染函数(template内必须拥有一个根标签作为容器),这个函数在将来的mount阶段会将整个el容器替换掉
如果没有:将el包括容器本身当做template
beforeMount():
Vue尚未将自身的内容进行编译和挂载,由于此处已经获取过页面的容器当做template,因此在此时对dom的操作最终都不奏效,会在之后的mounted阶段被vue替换掉
mounted():
vue创建为虚拟dom,这份虚拟dom会被保留以进行对比算法,随后创建真实dom,然后替换掉真实的el(vm.$el)插入页面。
此时页面呈现的是经过vue编译的dom,对dom的操作均有效,一般进行初始化操作如:开启定时器、发送网络请求、订阅消息、绑定自定义事件等
3.其次,当data发生变化时:
beforeUpdate():
此时vm的数据已经发生更新,但页面尚未与数据同步更新
updated():
根据新数据生成新的虚拟dom,与之前的虚拟dom作比较以更新真实dom(vm.$el),完成了Model->View的更新
此时页面是最新的
4.最后,当调用了vm.$destroy()去销毁vm时

注:此处的指令指v-之类
此处的事件为自定义事件,click等原生事件不在此列
beforeDestroy():
此时vm内所有的data、methods、指令等均可获取(但指令不生效),但是vue不再进行数据监测,删除自定义事件监听器(如click等原生事件不在此列),因此页面数据不会发生更新,一般进行收尾操作如关闭定时器、取消订阅信息、解绑自定义事件等
注:vm被销毁后定时器仍然会存在,所以要手动关闭
destroyed()

如果使用template配置项,则template只能含有一个根标签且会把之前的root标签完全替换掉,且不能使用template标签作为根标签,不推荐
非单文件组件
基本使用 配置-注册-编写组件标签

基本步骤:配置-注册-编写组件标签
非单文件组件与单文件组件

使用Vue.extend(配置项)创建与配置组件,作为一个常量

配置项与new Vue时的配置项基本相同,都是对象,区别如下:
-
不能含有el,应使用template配置项,需要注意只含一个根元素
-
data使用对象式写法,避免data被全局修改。
每个组件每次使用data()都会返回一个新对象,因此每个组件内部对返回的新对象修改不会修改原data的数据,避免组件相互影响
在vm内配置components/使用Vue.component注册组件
1.在vm内配置components局部注册组件

如果创建组件时使用的名字与要配置的key值相同,可用简写
2.使用Vue.component(‘组件名’,组件名)全局注册组件

在html上编写组件标签(并绑定容器),使用组件
在vm绑定的容器内,用vm的配置项components的key作为标签的名字,编写组件标签

注意点

组件名:
-
一个单词:全小写或开头大写
-
多个单词:全小写、使用-隔开或者每个单词开头使用大写
使用短横连接时,在使用key时需要放在引号内
使用开头大写时,必须配合脚手架使用
在组件内配置name:’名称’可单独修改在开发者工具中的名字,同时应该避免使用HTML的元素名称,无论大小写
组件标签写成单标签:
必须在使用脚手架的情况下才能使用
创建组件时简写:
const s = {配置项}
省略Vue.extend(),但实际上在vm.component内发现组件名并使用时仍然会调用Vue.extend
组件嵌套
在创建组件时使用components配置项配置嵌套的内组件进行注册,使用时需要在组件的template里面写内组件标签进行使用
一般使用一个app组件用于集成子组件的功能



也可以在vm内配置template替换容器


VueComponent构造函数

使用Vue.extend注册的每个组件都独自是一个构造函数,名为VueComponent,这个构造函数内的this指向构造函数的实例对象,每次使用组件标签时,Vue都会调用这个函数去new一个实例
在vm上创建组件时,VueComponent被挂载在vm.$children里,同时被嵌套的组件也会被挂载在VueComponent的实例对象上
**组件是可复用的Vue实例,**vm与vc的大部分内容相同,但是vm可绑定容器,但vc必须在vm上使用
一个重要的内置关系:Vue对原型链的修改
VueComponent.prototype.__proto__===Vue.prototype,使得vc可以访问到Vue原型
每个实例对象的__proto__属性都指向其构造函数的原型对象,即其构造函数的prototype,而__proto__属性指向的原型对象里的__proto__则指向更早的原型对象,例如Vue的原型对象的__proto__指向Object的原型对象
原型链:
vc的__proto__ -> VueComponent的原型对象
VueComponent的原型对象的__proto__ -> Vue的原型对象的__proto__(本应指向Object的原型对象,但是Vue修改了)
Vue的原型对象的__proto__ -> Object的原型对象的__proto__
Object的原型对象的__proto__ -> NULL
单文件组件 xxx.vue
命名
单名词:纯小写或首字母大写,一般选择首字母大写
多名词:短横-隔开或者每个单词首字母大写,一般选择每个单词首字母大写
基本结构

vue支持template、script、style标签,分别对应html、js、css,其中三种不同的标签对应的注释样式也不同
同时,由于需要由外部文件进行引入,所以需要使用export进行暴露,一般选择默认暴露
如果下载了vetur插件,可以输入<v和按下tab来快速生成

template标签的写法
template标签内必须含有根元素
script标签的写法+暴露组件的写法
由于Vue.extend可以省略,采用默认暴露时使用如下形式:

style标签的写法
与正常style标签相同,也可省略该标签(如果没有任何样式)
ES6的三种暴露方式
分别暴露:

统一暴露:

默认暴露:

通过APP.vue集成所有组件
在template标签内使用组件名

在script标签内使用import引入组件,在components内注册

创建main.js入口文件和index.html通过app.vue来生成vm
index.html应该包含id为root的标签,
并且必须要引入vue.js和main.js,且要先引入vue

main.js内创建vm

如果不想在index内写app标签,可以增加templpate标签

注:浏览器不能识别.vue文件,以上所有内容都要在脚手架里运行
Vue脚手架(CLI, command line interface)
创建与加载


一些文件的功能
index.html存档在public文件夹里,作为主页面和容器所在的页面
src文件夹存放脚本,App.vue、main.js直接存放在该目录下,子文件夹components存放其他子vue文件,assets存放图片等资源
App.vue集成了components里的vue文件
main.js作为入口文件,由脚手架引入,无需在index.html里引入


render函数
vue脚手架默认引入的是缺乏模板解析器的vue,无法解析vm里的template模板,必须使用render函数
(.vue后缀文件里的template不受影响,有专用的解析器)
render函数包含一个函数类型的参数createElement,
并且render函数必须有返回值

createElement函数可以解析模板,返回模板后作为render的返回值渲染到页面上
create函数的参数:
渲染vue组件时,包含一个参数即可,即这个组件的名字
渲染html时要包含html标签和内容,且都必须加引号
关于不同版本的vue

脚手架的配置
vue隐藏了webpack配置项,可以通过指令输出output.js查看
红色框内容为使用默认配置时不可改动的部分

若要修改脚手架配置,则要在与package.json同级的默认目录下创建vue.config.js去配置,如果不修改配置则一定不能创建

通过ref进行调用vc上的真实dom元素
原生调用dom元素的方法是使用document.getElementsByXX函数,Vue中可以使用ref,通过对真实dom加上ref属性,在vc的$refs里找到对应元素,


ref的值对应$refs里的key名,该名对应的值为真实dom
同理,可以在组件标签上添加ref,会返回组件标签
此方法避免了使用id属性,而使用id无法获取组件标签,只能获取渲染后的最终标签

props配置项

props用于对外暴露数据,便于复用组件时进行数据修改
注:必须加引号,且默认是Sring类型
如果要传入数值,要使用数据绑定,即添加冒号

props接受的值是无法修改的(实际上Vue是浅监视),如果要使用可修改的数据,应当在data内利用props的数据配置一次

props的对象式写法
props的对象式写可以配置类型限制、默认限制 、必要限制
如果不希望使用冒号进行数据绑定来设置数值类型,则要修改props的接收方式,使用对象写法并写明数据类型

对象式写法可以有其他配置项,如默认值、必要项等

mixin混合/混入:多个组件共享一个配置

共享的配置单独写在一个js文件内,并且要暴露出来,一般使用分别暴露,因为可以引入多个配置。
如果混合配置内的数据与原文件的配置有冲突,则根据类型决定:
data和methods等数据的冲突,以vue文件本身为主
生命周期钩子存在冲突,全部执行,且混合配置先执行

使用时,将其在.vue文件内import进去,引入的方式要对应暴露的方式,随后配置maxins配置项,内容为配置组成的数组

全局混合
如果在main.js内引入了配置项并应用,则创建vm时会应用到所有vc上

插件

插件是一个对象,但必须包含一个install函数,一般写在plugins.js文件内,且必须暴露,一般使用默认暴露
install函数的形参是Vue本身,因此可以进行很多修改
install第二个以及之后的参数是插件使用者传递的数据,对应在Vue.use(plugins, data1, data2, …)
在main.js内引入该文件并命名为plugins,用Vue.use(plugins)调用
可以重复使用Vue.use来开启多个插件


scoped局部样式

在style标签上加上scoped属性,能使样式只在组件内局部生效
如果在app.vue里的style标签上写,则会全局生效

实际原理为给组件所在标签加上一个随机tag
在style标签上加上lang属性,可以设置标签内使用less等语言,但是要下载npm包和注意版本兼容性

组件化编码流程


- 实现静态组件,将需求明确化,只关注结构和样式,拆分要合理

根据拆分的结构,搭建vue文件和完成引入、注册等动作
兄弟组件间的数据交流
由于只有父子组件可以使用props进行交流,兄弟组件的交流受限
将数据定义在共同的父级

props只能让父级传递给子级,因此Header得采用其他方式传递数据给父级
在父级内定义函数,将函数交给子级。
虽然传递的函数子级无法修改,但是可以调用,传递数据给父级


v-model进行的双向数据绑定可以跨组件通信
对v-model的值在任意组件内的修改都可以在所有组件内观察到
若一个多选框类型的input标签上存在值为布尔类型的v-model属性,则该属性会代替checked去决定多选框的状态。因此在Item组件内使用v-model,当多选框状态变化时todo.done会发生变化,并且该变化可以在App组件内观察接收到。
该方法本质上是直接修改了props内的值,不推荐
原理是vue对该处传递的props值仅使用了浅监视,检测不到内部
组件自定义事件 this . $emit(‘action’,传递的其他形参)

click、keyup等内置事件是相对html元素而言的,而组件自定义事件是相对组件而言的
事件被绑定在所触发组件的实例对象上,因此自定义事件要在所触发组件内进行配置事件行为
自定义事件是由子组件定义和触发,而由父组件去使用的
用this.$destroy销毁后,自定义事件皆被销毁
运行逻辑是,子组件使用父组给的事件名配置自定义事件,当子组件上的该事件被触发,父组件做出反应
子:

父:

父组件回调函数的形参,与子组件emit函数的第二个形参是一致的
通过该方式实现了子组件向父组件的数据传递
在父组件内配置子组件的定义和触发
使用refs获得子实例,在子实例上使用on方法配置触发事件
此时第二个参数为父组件内触发之后执行的函数


仍需子组件配合使用$emit设置函数的形参
getStudentName在App里写,则函数内的this指向App
getStudentName若在on()里写成function()形式的回调,则里面的this指向Student,因此要写成箭头函数才会指向App。谁触发了事件则指向谁
注:Vue3废弃了$on,$on的作用是父组件给子组件标签绑定事件
也可以用once等触发方式
解绑自定义事件this,$off
this.$off(‘事件名’) //解绑单个事件
this.$off( [‘事件名1’, ‘事件名2’] ) //解绑多个事件
this.$off() //解绑所有事件
组件绑定html原生事件
使用@click=‘’的形式会被解析成自定义事件
使用@click.native=’’的形式才可以解析成原生事件
除此之外,$emit也可以绑定原生事件
全局事件总线:实现任意组件间通信

使用一个额外的X,独立于所有组件之外,A组件要与B组件通信时,在A组件内触发X的事件并传递参数,X组件执行B组件的回调并传递参数
为了让这个X被所有组件可见,需要将其挂载在Vue.prototype上
同时,为了让这个X拥有$on、$offh、$emit方法,需要让X是一个vm或vc
使用VueComponent

由于Vue.extend方法创造的vc构造函数还没有实例对象,所以需要执行一次new,再挂载到Vue的prototype上
使用VM
在new Vue前设置X,则此时vm还未创建,
在new Vue后设置X,则此时vm已经执行完毕并销毁
因此必须在new Vue的内部去写X

组件的销毁要伴随事件的解绑:beforeDestroy

一定要在销毁前解绑该组件配置在bus身上的事件,
否则可能引起周期函数持续执行
一定要传参,否则表示将全部事件解绑
总结步骤
在main.js内使用beforeCreate钩子,可以在Vue创建但未完全加载的时候将X挂载到vue原型上,使其对其他组件可见,X命名为$bus
语句:
Vue.prototype.$bus = this
在App.vue内使用mounted钩子,进行事件的命名与绑定
this.$bus.$on(‘changeTodo’,this.changeTodo)
在App.vue内使用beforeDestroy钩子,进行事件的解绑
this.$bus.$off(‘changeTodo’)
消息订阅与发布:实现全局组件通信
A组件需要C组件的数据,则A组件向C组件订阅消息。A组件提供消息名,C组件依据消息名提供消息内容
使用第三方工具:
npm i pubsub-js
import pubsub from pubsub-js//pubsub是一个对象
//A的mounted内:
this.pubId = pubsub.subscribe(‘消息名’, ( msg, data )=>{
//A订阅了名为XX的消息,当有人发布名为XX的消息时,执行该回调函数。
//该回调的两个参数,第一个为消息名,第二个为数据
//该方法返回一个pubId作为订阅的消息ID,用于解绑
//这个方法内的this不受vue管控,因此必须写成箭头函数。或者直接将这个参数从现写的函数改成**methods内配置的函数。**如果不需要第一个参数,随便写一个占位符
})
//A的beforeDestroy内:
pubsub.unsubscribe(pubId)//A销毁前解绑消息
//C的mounted内:
pubsub.publish(‘消息名’,data) // C发布消息

动画过渡效果

注:vue3的起点加上了from
transition标签与动画的使用
具体的样式需要自己去写,但vue提供了方便使用样式的工具
将要使用动画的单个标签添加到<transition> </transition>内

在style标签里写**.v-enter-active .v-leave-active**两个样式
分别表示进入时激活的样式和退出时激活的样式


动画相关代码(自己编写):

值的变化是优先于动画的,值变化之后动画才变化
自定义transition标签名
.v-enter-active 与.v-leave-active的v可被替换为transition标签的name值


默认播放动画
页面渲染完成时不会播放出现动画,需要配置appear属性为真

注意还要绑定成动态否则会报错,或使用如下形式

默认是真值,有该属性即可
不使用动画,使用过渡效果
.v-enter是进入起点,.v-enter-to是进入终点,离开相同

由于不使用动画,播放速度、是否匀速等要在其他地方配置
如图在h1的transition属性配置过渡效果

由于进入的起点=离开的终点,离开的起点=进入的终点,合并配置
动画相关配置可放在active内,该标签应用到全过程

多个元素过渡
要配置多个标签的动画,得使用transition-group标签,并且要配置不同的key值
优点是可以实现互斥效果,用一个div将两个标签包起来无法实现

使用第三方动画库
网站:animate.style
控制台:npm i animate.css
引入:import ‘animate.css’
配置name属性、enter-active-class和leave-active-class

配置代理
浏览器直接与服务器用ajax交互,会引起跨域问题。服务器确实收到并返回了数据,但是浏览器会报错拒绝访问,原因是端口号不一致,例如主机端口号可能是8080,服务器是5000
通过代理服务器解决该问题,代理服务器的端口号与主机一致,主机与代理服务器之间的交互不引起ajax跨域问题,而代理服务器与服务器的交互不使用XMLHttpRequest,而是基础Http协议,不会引起跨域问题
使用vue脚手架开启代理服务器
方式一

4000表示从主机转发的目标服务器端口号,自行配置
同时,前端代码的axios内要修改请求url为自己的主机端口号

方式一注意事项
当请求的资源是代理服务器自身拥有的,不转发直接返回,可能会造成资源的请求出现问题
同时,代理服务器只能向一个服务器转发,不能转发多个服务器
在vue脚手架内,资源的位置在public文件夹内,public为根目录

方式二

在proxy内配置多个属性,属性名为‘/api’,作为请求头
该方法配置的请求,主机发送请求时要添加‘/api’请求头,并且确实会被导航到/api的目录下

如果要避免导航到/api路径,要导航到根目录,需要配置pathRewrite属性,值为如上的对象
这个对象的内部,属性名为正则表达式,值为替换字,意为将匹配到的字符修改为值内的内容
上方的属性名意为‘将所有以/atguigu开头的字符串替换为空’
通过该方式实现了导航到根路径
ws属性表示启用websocket,changeOrigin表示是否修改请求来源,即后端请求的host。这两个值默认都为true启用,除了react内的changeOrigin默认不启用

要导航到多个服务器,只需要在proxy内配置多个‘/api’属性

vue-resource插件
npm i vue-resource
import vueResource from ‘vue-resource’
Vue.use(vueResource)
用法与axios相同,将axios前缀改成this.$http
slot插槽

默认插槽

在组件标签内可以写标签,这个标签会被加载到组件的slot标签内


插槽内可以写默认值,当组件标签没有内容时会被加载
由于各组件需要的样式各不相同,可以统一写到app的style标签下,独自写在各组件内也可以
具名插槽

想要使用多个插槽,需要使用具名插槽,否则每个默认插槽都会被加载一次组件标签体的整个内容
给多个插槽加上name属性,在组件标签体内的**子标签加上slot属性,值为插槽的name,**以加载对应的内容
可以加载多个slot相同的标签,会一并加载

如果要将标签一并打包,并打上slot属性,不要用多余的div,要使用template标签,避免多余的标签
只有使用template时可使用v-slot : value
注意v-slot
作用域插槽


要从组件里获取数据并对组件进行自定义使用,需要用作用域插槽
类似于props,在设计子组件的时候把数据写在slot标签里
作用域插槽也可以有名字

使用时,必须增加template标签将结构打包,并增加scope属性

scope属性值的名字可以随意定义,该对象内部即为所需的数据集
同时,支持结构赋值将该对象解构,可以直接取数据

另一个写法:使用slot-scope(与直接使用scope相同)

VueX



全局事件总线实现多组件共享数据可能会造成太复杂的交流,而且可能会导致脏读等情况

使用vuex,将共享的数据存到vuex,不共享的数据存在自身

状态即数据

组件使用dispatch函数说明要对数据执行的操作,dispatch函数被关联到action对象,在该对象上寻找操作名称对应的函数,然后执行该函数;
这个函数内部又包含一个commit函数,commit函数被关联到mutations对象,再次去这个对象上寻找操作名对应的函数,随之执行该函数,此时执行的函数才真正进行了提交修改,并且vue会自动再次渲染组件
actions的存在使得对数据的操作可以被共享,即可以多个对象共同去决定如何执行一个动作。例如让一个对象提供值,另一个对象决定动作,而不会因直接使用commit造成错误
如果操作是规范的,可以在组件内直接调用commit
store用于管理以上三个部分
下载与使用



vuex可以在开发者工具内查看过程
方法一:在src下创建vuex/store.js

方法二(官方推荐写法):在src下创建store/index.js


使用时需import,如果不写清楚路径,默认引用index

由于vue脚手架会将所有import语句移动到文件头部运行,如果先引入store文件再执行new vuex会引发错误,应当是先new再执行store文件,所以干脆在store文件内引入vue和vuex去创建实例
组件调用dispatch

组件直接调用commit

配置action

第一个参数为上下文对象,类似迷你版的store,拥有commit方法和dispatch方法、state对象

这个参数里存在的dispatch使得action拥有的函数可以相互调用,对一个动作分多个函数去执行
action里的函数可以直接修改state里的数据而不引起错误,但是无法被开发者工具捕获操作
配置mutations
mutations里的操作是原子操作,不会引发脏读


第一个参数为state对象,该对象会把你配置的state加上数据监测
配置的state

getters配置项:实现计算属性

记得配置
使用:不用state,用getters

mapState与mapGetters
先引入这两个工具

重复写$store.state.xxx稍显麻烦
从vuex上引入mapState实现数据映射,computed也能实现该功能
computed写法:

mapState
对象写法:
在computed里使用该写法,可以直接展开所有数据
注意原state里的属性名到此处是字符串,因此不能简写
数组写法:
将原state里的属性名一律映射成相同的名字
mapGetters
与mapState相同
mapActions跟mapMutations
调用dispatch跟commit的时候也可以映射为简写

mapMutations:帮使用commit的函数映射

对象写法:
在methods内:
注意绑定事件的函数要传参,否则传参会被默认成事件对象
会变成下面这种情况

正确的写法,加上了(n)
数组写法:

mapActions 帮使用dispatch的函数映射
使用方法和问题同mutations
Vuex的模块化编码+namespaced


共享的数据太多会造成维护混乱,使用模块化编码将共享的数据进行分类
命名空间要设置为true,否则无法被mapState第一个参数识别


此时不能按照原来的方式配置store了,要改成在modules配置

mapState/mapGetters的修改
State:
此时state里的属性不是直接的数据,而是modules里的属性和值
使用mapState时,映射的也是modules里的配置对象
如果要映射成具体数据,需要修改为如下配置
在前面增加一个参数即配置对象名
要使用这种配置,必须将配置对象内的命名空间配置为true

Getters:
getters同理,里面的属性不是直接的数据,但是是按照一定格式修改了属性名

直接使用时,要按照该格式去调用
由于无法在调用时直接使用/,改为使用数组式

使用mapGetters则与mapState类似:
mapMutations/mapActions时的修改
由于store存在多个配置的mutations,mapMutations也发生变化
修改方式相似,需要配置命名空间和使用时添加配置对象名
mapActions:
直接调用commit时的修改
注意,与其他修改方式不同,这里是将参数修改为xxx/xxx的格式
类似于直接调用getters
直接调用dispatch时与actions交互时的修改
同上,要用XXX/XXX的格式
路由:管理单页面内部导航到多页面

下载Vue2对应的版本3



创建与使用




创建与配置文件
在src下创建文件夹router,创建index.js,类似store


需要引入组件、VueRouter插件
暴露时要new VueRouter,配置项内的routes是对象数组,里面的每个路径对应一个组件
使用router-link替换a标签
用router-link标签替换a标签进行跳转,to属性替换href属性

router-link最终会转换成a标签,a标签的样式对router-link有效
使用active-class属性,可以在激活时自动使用属性值的样式
router-link的replace属性
replace将浏览器显示的当前页面,对页签最近的历史记录进行替换

使用router-view指定组件的显示位置

一些注意点
将路由组件放在pages文件夹下,一般组件放在components文件夹下
被切走的组件,底层上是已经被销毁了
路由组件多了两个属性:$route(路由规则)和$router(路由器)

注意:
路由器是唯一的,不同组件的router都相同,路由协议route不同

嵌套/多级路由


第一个配置项也叫一级路由
在第二个配置项内增加一个属性children,这是一个对象数组,里面的每一个对象是一个子路由配置,注意子路由不加斜杠。
记得引入组件
在进行匹配时,要把to值写成完整的路由路径/home/news
路由传参
为了避免太多嵌套,需要实现路由间参数的传递
query参数 ?a=xxx&b=xxx
query参数,全称是查询参数,是网址(URL)中问号(?)后面携带的键值对信息,用于向服务器发送额外的请求数据。


父组件在使用子组件的时候,在配置路由路径(to属性)的时候传递query参数,子组件在$route.query参数内可以查询到传递的参数

to的字符串写法
使用动态绑定和模板字符串
to的对象写法

仍然需要动态绑定,不同的是to的值是一个对象
包含两个值,path是路由路径,query是携带的参数集对象
在query内配置参数
命名路由
对于每个路由配置项目,可以加上name属性进行命名
命名路由可以简化编码,把对象写法的to的path改成name

路由$route的params参数
params参数是斜杠加参数的形式,直接拼接在尾部,与多级路由的书写部分重叠,因此需要在配置路由时在路径path内声明占位

注意:
使用params参数时,标签配置to属性若使用对象式写法,不能用path,必须用name ,且query属性要改成params属性

路由$route的props配置
组件在接受参数是重复写$route.query.xx太麻烦,在配置路由时使用props,将键值对以props形式传递给组件,组件以props接受

props的三种写法

第一种写法,值为对象,不常用,因为传递的是固定数据
第二种写法,值为布尔值,会把该路由组件的所有params参数以props的形式传递给组件

而组件的props仍然是数组,根据params的参数名决定字符串值
第三种写法,值为函数

该函数返回一个对象,参数是$route,可以获取query和params

可以对参数连续解构赋值进行简写

编程式路由导航:复杂场景下代替router-link

router-link自动转为a,有时要使用其他元素,或需要延迟跳转等
跳转两种方式,一种是push,一种是replace,与router-link相同
router所需的配置对象与router-link的to属性值相同
可以传递给push方法或者replace方法


注意,如果使用replace,不会留下历史记录,每次replace跳转都会把上一次的页面历史删除
通过router实现页面前进/后退
使用router的back和forward方法

使用go方法,传数字参数,正数前负数后,数字大小表示走几步

缓存路由组件:避免切换导致数据销毁

页签切换时组件被销毁,会导致数据丢失,但可以进行缓存
(在父组件内)寻找被销毁组件的router-view标签,在外面配置套上keep-alive标签,并进行配置
如果不进行配置,则会阻止所有标签被销毁

使用include属性进行配置,值为组件名,该组件不会被消耗

有多个组件,要进行动态绑定,因为值为数组

两个新的生命周期钩子activated、deactivated

由于进行缓存时,组件不会进入destroy流程从而被销毁,因此在切换之后会出现定时器仍然在执行的情况
使用activated钩子和deactivated钩子进行清除定时器回调的操作

在激活组件时激活定时器,在切换组件时销毁定时器
路由守卫:页面的条件跳转
在使用路由进行组件跳转时进行数据校验,判断是否进行跳转
在创建路由器时不能直接暴露,要先创建,再条件暴露
先在路由器上配置使用全局前置路由守卫,然后再暴露
全局前置路由守卫beforeEach函数接受一个回调函数,它在初始化的时候被调用、在每次路由切换之前被调用
回调函数包含三个参数:to、from、next
to、from都是对象,是源页面和目标页面的路由协议
next是一个函数,在被调用时允许跳转

首先要判断目标路径是否为条件跳转,然后才进行条件跳转流程
在配置好路由守卫之后,才能进行暴露路由器的操作
简化条件跳转
由于需要先进行一次判断,判断组件是否需要条件跳转,随后才进行条件判断,因此对此进行简化
让每个组件的路由配置都加上一个标识,表示向/从该组件的跳转是否需要进行条件判断
由于配置路由时使用的是配置对象,无法随意往路由器上挂载变量,需要将自定义的标识添加到配置对象的meta属性内
meta称作路由元信息


使用to.meta.isAuth**,判断目标组件的跳转是否需要进行条件判断**
全局后置路由守卫
与前置路由守卫的区别是,在每次切换之后被调用
全局后置路由守卫afterEach函数接受一个回调函数,它在初始化的时候被调用、在每次路由切换之后被调用
回调函数包含两个参数:to、from
由于是切换之后进行调用,所以不需要next
通过全局后置路由守卫进行页面标题切换:
将标题信息加到meta属性里,传递给后置路由守卫

使用后置路由守卫更方便,相当于页面被切换后进行初始化操作

如果用前置路由守卫,会在判断流程里进行重复执行更换标题的指令,还会因为条件判断而书写混乱

独享路由守卫 beforeEnter
在某个路由的配置时,直接配置路由守卫,功能类似前置
独享路由守卫只有前置,没有后置,但与全局后置路由守卫不冲突


组件内路由守卫
beforeRouterEnter与beforeRouteLeave都配置在组件内,使用方式与钩子一致
beforeRouteEnter是在通过路由规则进入该组件时调用,类似前置
beforeRouteLeave是在通过路由规则离开该组件时调用,并非后置
不通过路由规则方式进入的都无法触发这两个方法

beforeRouteLeave与后置不同,回调的参数带next,可以进行条件判断,只有为真时可以离开该组件

history模式与hash模式

#即hash,#之后的所有部分不会作为路径的一部分传给服务器

要改成histroy模式,在路由器内添加mode属性,值为history
history模式下没有#标志,在只有前端路由运行的情况下可以正常运行,而如果刷新页面,则会向后端服务器发送请求,这时就将所有部分都当成路径发给了服务器,导致找不到资源
解决这个问题,需要后端进行正则匹配,或者使用插件
Vue UI组件库


如果引入组件,可能会造成引入所有组件而造成了项目体积过大,必须按需引入,根据组件库的帮助文档进行引入
如下的例子使用了全局注册组件的办法,可以在注册时修改组件名称,从而在使用组件标签时不用默认的名字

样式的部分为自动分析:

如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时















