文章目录
- Vue生命周期
- Vue 组件化编程 - .vue文件
- 非单文件组件
- 组件的注意点
- 组件嵌套
- Vue实例对象和VueComponent实例对象
- Js对象原型与原型链
- Vue与VueComponent的重要内置关系
- 应用单文件组件构建
- Vue脚手架 - vue.cli
- 项目文件结构
- 组件相关高级属性
- 引用名 - ref
- 数据接入 - props
- 混入 - mixin
- 插槽 - slot
- 默认插槽
- 具名插槽
- 插件
- 存储
- 浏览器存储 - Window.sessionStorage与 Window.localStorage
- 组件事件
- 触发自定义事件 - emit()
- 解绑自定义事件 - off() / destroy()
- 自定义事件注意事项 - this指向与native
- 全局事件总线 - 实现任意组件间通信
- 消息订阅与发布模型 - 基于pubsub实现
- 事件触发更新 - nextTick
- 过度与动画
- 配置代理解决跨域问题
- 什么是跨域
- 出现跨域的原因
- Vue对于跨域问题的解决 - 配置代理
- 方式一
- 方式二
Vue生命周期
- 1、beforeCreate():数据代理尚未开始,此时无法通过vm访问到data中的数据,methods中的方法。
- 2、created():数据监测和数据代理完成了,可以通过vm访问到data中的数据,methods中的方法。
- 3、beforeMount():页面呈现的是未经Vue编译的DOM结构,所有对DOM的操作,最终都不奏效。
- 4、mounted():页面中呈现的是经过Vue编译的DOM,此时对DOM的操作均有效,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作。
1-4步骤构成了挂载的主要流程
- 5、beforeUpdate():此时数据是新的,但是页面是旧的,即新的数据尚未更新到页面上。
- 6、updated():此时,页面的显示的是新数据,页面和数据同步完毕。
- 7、beforeDestroy():此时vm中所有的data、methods、指令等等,都处于可用状态,马上要执行销毁的过程,一般在此阶段:关闭定时器、取消订阅消息,解绑自定义事件等收尾操作。
- 8、destroyed():销毁完毕。可以使用
this.$destroy()
,让vue实例自己销毁,Vue实例销毁后,自定义事件会失效,但原生DOM事件依然有效。
Vue 组件化编程 - .vue文件
组件的定义——实现应用中局部功能代码和资源的集合。
非单文件组件
一个组件文件中由多个其他组件构成,称为非单文件组件。
Vue使用组件的三大步骤:
- 1、定义组件(创建组件)
- 2、注册组件(Vue的
components
局部注册或者使用Vue.component(‘组件名’, 组件))全局注册。 - 3、使用组件(写组件标签)
1、创建组件
创建组件时,需要注意:
- 不需要写
el
标签:el标签是将Vue实例与DOM元素绑定的,当你创建组件时,你并不知道需要将其用在哪里,因此不需要写el
标签。 - 组件的
data
属性需要写成函数式的:因为一个组件可能在多个地方使用,如果写为对象式的,各个组件之间的数据变化会相互影响。 - 使用
Vue.extend({})
创建组件,在template
属性中,使用反引号包裹HTML标签。
2、注册组件
局部注册:在Vue实例的components
属性中,标注上构建的组件。
使用`Vue.componet(‘组件名’, 组件);可以注册全局组件,即所有的Vue实例,尽管在components属性中没声明该组件,也可直接使用。
3、使用组件,写组件标签。
组件的注意点
组件嵌套
组件可以嵌套使用。注册给谁的组件,就可以在它的template
属性中应用这个组件标签。
Vue实例对象和VueComponent实例对象
以定义的school组件为例:
- 1、当我们使用school标签时,本质上是调用了
VueComponent
构造函数,该构造函数是Vue.extend
生成的。 - 2、当使用school标签时,Vue框架解析时会帮助创建school组件的实例对象,方式就是执行
new VueComponent(options)
- 3、特别注意,每次调用
Vue.extend
,返回的都有一个全新的VueComponent对象。 - 4、关于
this
指向,组件配置中:data函数、methods中的函数,watch、computed函数,它们的this均是VueComponent实例对象
。而在new Vue(options)配置中,data函数、methods中的函数,watch、computed函数,它们的this均是Vue实例对象
- 5、根组件Vue实例对象会有
$children
属性,其中就是VueComponent实例对象
数组。 - 6、Vue实例对象和VueComponent实例对象在创建时接收的options选项,除了Vue实例可以使用
el
绑定具体的DOM标签外,其余都一样。
Js对象原型与原型链
JavaScript 是一种基于原型的语言 (prototype-based language),每个对象拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性,这些属性和方法定义在对象的构造器函数的 prototype 属性上,而非对象实例本身。
每个Js对象都会有一个名为__proto__
的内部属性,该属性指向当前对象的原型对象。在Js中,当我们访问一个对象的某个属性时,如果该对象没有这个属性,Js语法引擎就会沿着该对象的原型链(在对象的__proto__
上找)向上查找,直到找到该属性为止。在查找对象属性时,通过__proto__
查找原型的属性这个过程,就用到原型链。
当然在Js对象中,根原型为Object,而Object的__proto__
是为null的。
原型属性又可分为显示原型属性
和隐式原型属性
。
- 显示原型属性访问:
构造函数名.prototype
- 隐式原型属性访问:
实例对象名.__proto__
- 由这两种方式访问到的原型对象是等价的,即指向的是同一个原型对象。
构造函数名.prototype
===实例对象名.__proto__
- 由原型链的属性访问方式可知,如果给原型对象添加一个属性x = 99,那么可以由实例对象d的原型对象属性访问它,即
d.__proto__.x
,实际上对于__proto__
的访问是Js执行引擎自动完成的,因此d.__proto__.x
访问的方式,可以等价于使用d.x
Vue与VueComponent的重要内置关系
VueComponent(Vue组件)的原型对象的原型对象是Vue的原型对象
应用单文件组件构建
1、构建一个单文件组件
一个单.vue文件,基本结构如下;
定义好单文件组件是为了提供给 别的文件使用,因此需要使用export
将该文件进行暴露。
export default {
……
}
实际上是一种简写,等价于:
const vc = Vue.extend(
{
……
}
)
export vc
2、入口Vue文件:App.vue
一般而言,Vue项目的根组件为App.vue,这里使用其他的组件。
组件化编程三步走:
- 导入
- 注册
- 使用标签
3、入口js文件:main.js
构建的根组件App.vue并没有使用,在main.js中构建App.vue的实例对象,使用el
属性为其绑定DOM对象。
4、入口HTML文件:index.html
Vue脚手架 - vue.cli
项目文件结构
组件相关高级属性
引用名 - ref
ref属性被用来给元素或者子组件注册引用信息,类似id,但是id只能绑定DOM元素标签,而ref还可以绑定vue组件,相当于给组件或DOM元素标签起一个别名,便于在程序中引用,程序中可以通过this.$refs.xxx
获取到Vue组件或DOM元素标签。
数据接入 - props
组件中的数据,在复用时可能各自不同,props属性用于给组件传递数据。
基本使用方法:
step1:在组件的props属性配置数据项的名称接收。
step2:在使用组件标签时传入数据项的数据值。
实际上props数据接收,有三种不同的方式:
- 简单接收
- 限制类型
- 限制类型、必要性、并指定默认值
注意:props中指定数据项名称可能会与Vue组件中的data属性中的数据项名称产生冲突,从优先级上,是props优先级会更高一点。
一般而言,props承接的数据,我们希望将其转为Vue组件的data数据项的数据,那么根据优先级关系,可以将props属性传给data属性。
将props承接的数据传给data,另一方面是由于组件在使用过程中数据可能会更新,而props属性项数据是不允许更改的,只有组件自身的data属性项可更改
混入 - mixin
mixin:混入,可以把多个组件的共用的options配置,抽取出一个混入对象mixin,节约配置。
使用方式:
step1:定义混入:
例如:
{
data() : {……}
methods: {……}
……
}
上述这些公共配置,写成一个js代码文件:mixin.js,然后暴露。
step2:使用混入,例如:
- 全局混入:Vue.mixin(xxx)
- 局部混入:使用mixins属性,mixins: [‘xxx’]
关于mixins属性需要注意:
- 1、如果混入mixin中定义的data属性项名与组件中的已经有的data名重名,则只保留组件已经有的data名。
- 2、mounted()等生命周期方法中的操作,则会与组件已有的内容合并。
插槽 - slot
插槽的提出是为了实现合成组件,假定一个组件中大部分内容是固定的,仅有几个地方是需要变化更改的,我们就可以用插槽来代替这部分需要变化的,然后根据需要写相应的插槽组件
Slot的通俗理解是“占坑”,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中位置),当插槽也就是坑<slot name= ” mySlot ” >有命名时,组件标签中使用属性slot= ” mySlot ” 的元素就会替换该对应位置内容;
默认插槽
下边这个需求,大致上页面都是标题为某个内容的分类,内容呈现上有一些不同,这些不同就可以使用插槽进行抽象占位,那么整个页面就可以使用一个组件来复用。
step1:在每个分类内容组件Catetory中编写相应的DOM结构。
step2:Catetory中编写相应的DOM结构会放在Catetory的slot
占位处,相当于将对应的DOM进行了替换。
具名插槽
当一个父组件需要组装多个动态组件时,就需要多个插槽,使用具名插槽用于区分。
需求:在内容底部,还有一部分超链接。那么就相当于是两部分动态内容,需要安置两个插槽。
所谓具名插槽就是在使用插值挖坑占位时,给坑起个名字,好方便后续根据名字来填坑。
step1:挖坑时,使用name属性,给插槽起名。
step2:填坑时,使用slot
属性,绑定name,进行结构替换。
注意:下边对于slot的绑定使用了两种方式:
- 1、美食与游戏组件都是使用
slot = "name"
进行绑定 - 2、电影里边,使用template标签 +
v-slot:name
进行了绑定。
上边两种方式都可以,只不过 v-slot:name
必须与template结合使用,template这一层标签在渲染时是不存的,仅作间隔占位用。
插件
插件的功能是用于对Vue进行增强。
使用步骤:
step1:
定义plugins.js文件,定义install函数,表示是一个可安装的插件,在其中一般可以定义用于增强Vue的:
- 1、全局过滤器
- 2、全局自定义指令
- 3、全局混入
- 4、Vue原型方法
step2:在main.js中,导入plugins.js并使用Vue.use()
。
step3:在Vue中使用定义的增强功能,比如上边在plugins.js中定义了Vue的原型方法hello
,那么后边就可直接使用了。
存储
浏览器存储 - Window.sessionStorage与 Window.localStorage
组件事件
触发自定义事件 - emit()
以子组件向父组件传递数据为例:
如果不用自定义事件,可以通过父组件给子组件传递函数类型的props实现
step1:给子组件School绑定getSchoolName函数,相当于子组件的School的组件实例会有getSchoolName()这个函数。
step2:用props承接父组件注册的getSchoolName。按钮注册点击事件,调用该函数,并将组件的name参数传递。
如果使用自定义事件来实现:
step1:通过v-on: 给子组件Student绑定自定义事件atguigu
,该事件触发时,调用父组件的getStudentName()方法。
step2:使用this.$emit('atguigu', this.name)
触发子组件Student上的atguigu事件。
除了在子组件标签上使用v-on绑定,也可以通过代码实现:
先给子组件标签起一个引用名,随后在mounted()方法中,拿到该组件,给它绑定事件方法。
如果要保证事件只触发一次:
可以使用:
解绑自定义事件 - off() / destroy()
- 1、
this.$off()
:如果指定参数,则 解绑对应事件(参数指定的),如果不指定参数,默认解绑所有的事件。 - 2、在Vue生命周期中讲过,当组件销毁时,其上绑定的自定义事件以及子组件实例都会被销毁,因此使用
this.$destroy()
生命周期钩子函数,也可以实现自定义事件的解绑。
自定义事件注意事项 - this指向与native
- 1、注意自定义事件触发时的this指向,this可能指向的是事件的产生者,即子组件。
如果想this指向父组件,即该mounted声明周期的组件,要解决这个问题,有两种方式。
(1)使用箭头函数替换,function函数,箭头函数没有this指针,向外寻找mounted()函数的this,就是父组件了。
(2)将function的内容写为父组件的一个methods方法,那么this指向父组件。
- 2、给组件绑定事件,默认绑定的是自定义事件,即便事件名与内置事件如
click、keyup
等同名。如果想为其绑定内置事件,应使用.native
指明。
全局事件总线 - 实现任意组件间通信
如下图事件总线X,是各个组件之间的中介者,如果要实现组件A和D之间通信,可以在A中给X绑定一个自定义事件,那么X中的自定义事件触发时,就会触发A中的回调。此时在D组件中编写触发X该自定义事件的逻辑,并携带参数,那么就实现了D组件向A组件通信。
X应该具备两个特点:
- 1、所有其他组件都可见
- 2、实现
$on、$off、$emit
函数
$on、$off、$emit
函数都是Vue实例或者组件上有的函数,因此上边两个特点,综合一句话就是X是一个所有其他组件都可见的Vue实例或者组件
为了使用所有其他组件都可见,可将X安装在Vue的原型对象上。
X本身可以是Vue实例,不妨直接使用Vue实例本身。
因此一般的事件总线安装方法如下:
在main.js创建Vue实例时,beforeCreate()中,给Vue的原型对象设置$bus = this
.
后续使用时,在mounted()钩子处绑定,在beforeDestroy()钩子处解绑。
消息订阅与发布模型 - 基于pubsub实现
消息订阅与发布模型是一种理念,这里我们使用pubsub - js
库提供的消息订阅与发布函数。
step 1:使用npm下载pubsub - js
npm i pubsub-js
step 2 : 消息订阅
接收数据的组件,使用pubsub订阅消息,指定消息主题名即可。
- 1、import pubsub
- 2、编写定义回调方法demo(),需要注意的时,回调方法中第一个参数为msgName即订阅主题名,而后边才是消息发布时携带的参数。
- 3、在mounted中,指定主题名实现消息订阅,返回订阅id
- 4、在beforeDestroy中,根据订阅时产生的订阅id,取消订阅
step3:消息发布
事件触发更新 - nextTick
语法:this.$nextTick(回调函数)
作用:在下一次DOM更新结束后执行其指定的回调。
使用场景:当事件触发更新视图时,可以放在nextTick()指定的回调函数中,因为事件触发时,Vue会等待触发函数内所有代码执行完毕,才进行DOM更新,如果你想要基于更新后的DOM视图进行操作,应该放在nextTick()指定的回调函数中,这样才可以使得你基于更新后的DOM操作生效。
this.$nextTick(回调函数)
在实际开发中使用特别多。
过度与动画
配置代理解决跨域问题
什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不一同,即跨域
出现跨域的原因
跨域问题出于浏览器的同源策略限制。所谓同源策略(Same-Origin Policy)是一种基于安全考虑的限制,它的主要作用是防止一个网站访问另一个不同来源的网站的数据。同源策略限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互,即一个源的 JavaScript 脚本只能读取同一源的数据,而不能读取其他源的数据。
同源策略主要限制以下几个方面:
-
1、Cookie、LocalStorage 和 IndexDB 等存储器:浏览器只允许网页访问自身网站的 Cookie、LocalStorage 和 IndexDB 等存储器,不能访问其他网站的存储器。
-
2、Ajax 请求:XHR(XMLHttpRequest)对象只能访问同一域下的资源,不能访问其他域的资源。
-
3、DOM 访问:JavaScript 脚本只能访问同一域下的 DOM 对象,不能访问其他域的 DOM 对象。
-
4、Frame 和 iframe:包含不同源页面的 Frame 和 iframe 也受到同源策略的限制。
需要注意的是,同源策略只限制浏览器端的交互,而不限制服务器端的交互。因此,服务器端可以自由地访问和传输不同源的数据。
Vue对于跨域问题的解决 - 配置代理
服务器之间通信不受同源策略影响,即无代理问题,因此,通过将代理服务器启动在前端浏览器进程端口,浏览器请求同端口的代理服务器,代理服务器,再根据浏览器的请求转发给后端服务器,就可以解决请求跨域问题。
方式一
step1:在Vue-Cli中的Vue.config.js中开启Vue代理服务器:
需要注意的时,Vue代理服务器默认运行在前端浏览器同端口的进程,因此这里proxy属性配置的端口地址,实际上为后端服务器端口地址。
step2:向代理服务器发送请求,代理服务器会自动转发到后端服务器端口。
需要说明的是,这种方法有一些缺陷:
- 1、假如要请求的资源,恰好是public中的资源,即该端口中已经有的资源,则就不会走代理服务器。那么假若恰好有与后端路径同名的public文件,则不会请求到后端。
- 2、无法配置多个代理。