实例方法是挂载在Vue的原型上
而全局API是直接在Vue上挂载方法。
实例方法
事件相关
在eventsMixin函数中挂载到Vue原型上的
vm.$on和emit
发布订阅模式
vm.$on( event, callback )
vm.$on('test', function (msg) {
console.log(msg)
})
vm.$emit('test', 'hi')
//该方法接收的第一个参数是要触发的事件名,之后的附加参数都会传给被触发事件的回调函数。
所有绑定在这个实例上的事件都会存储在实例的_events属性中。输出该属性看一看:
原生事件绑定是通过addEventListener绑定给真实元素的,组件事件绑定是通过Vue自定义的$on实现的。而on是在new Vue()初始化时候挂载到vue原型上的
vm.$off
如果没有提供参数,则移除所有的事件监听器;
如果只提供了事件,则移除该事件所有的监听器;
如果同时提供了事件与回调,则只移除这个回调的监听器。
vm.$off( [event, callback] )
vm.$once
监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。
Vue.prototype.$once = function (event, fn) {
const vm: Component = this
function on () {
vm.$off(event, on);//取消监听之后执行回调
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
生命周期相关
初始化:内部属性watcher…和外部属性
初始化methods中方法时候会检验是否合法
在beforeMount对数据进行更改,不会触发updated。
数据处理一般放在created和mounted
接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created中。
mounted阶段真实dom渲染完成,可以操作dom,
SSR:服务器获取数据之后绑定视图,直接生成html返回前端。SEO性能好
vue.$options.created其实是一个数组,保存多个钩子函数
$mount
手动挂载 一个未挂载的实例
如果 Vue 实例在实例化时没有收到 el 选项,则它停滞处于“未挂载”状态,没有关联的 DOM 元素。
vm.$mount( [elementOrSelector] )//返回实例自身,因而可以链式调用其它实例方法。
如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API把它插入文档中。
不同构建版本的vue.js的差异主要在于对mount方法的实现
$forceUpdate
迫使vue实例重新渲染
vm.$forceUpdate()
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
$nextTick()
vm.$nextTick( [callback] )
方法存在的意义
Vue中对DOM的更新策略:
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个事件队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到事件队列中一次。这样去除重复数据,避免不必要的计算和 DOM 操作。(所以nextTick很必要)
nextTick(),是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数,callback应该放置 对修改后DOM进行操作的 js代码
注意还有全局Vue.nextTick
区别在于:$nextTick 中回调的 this 绑定在调用它的实例上
$destroy
当调用了实例的$destroy方法之后,当前实例就进入了销毁阶段
完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
触发 beforeDestroy 和 destroyed 的钩子。
vm.$destroy()
全局API
Vue.extend
创建一个继承自Vue类的子类,可接收的参数是一个包含组件选项的对象。
注意:data 选项在 Vue.extend() 中它必须是函数
为什么呢
一个组件被复用多次意味着创建多个实例。本质上,这些实例用的都是同一个构造函数。
如果data是对象的话,对象属于引用类型,被所有的实例共用。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。
Vue.extend( options )
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
//data(){
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('##mount-point')
注册获取全局指令、过滤器、组件
涉及全局,Vue.options属性很重要
options的五类属性
数据: data, props, propsData, computed, Watch;
DOM: el, template, render, renderError;
声明周期钩子: beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、activated、deactivated、beforeDestroy、destroyed、errorCaptured;
资源: directives、filters、components;
组合: parent、mixins、extends、provide、inject;
Vue.directive
用来注册或获取全局指令的
注册指令是将定义好的指令存放在某个位置,获取指令是根据指令id从存放指令的位置来读取指令。
Vue.directive( id, [definition] )
源码实现:
在Vue类上创建了options属性,其属性值为一个空对象,并且在options属性中添加了directives属性,其值也是一个空对象,这个directives属性就是用来存放指令的位置。
自定义指令
Vue对于自定义指令定义对象提供了几个钩子函数,这几个钩子函数分别对应着指令的几种状态,一个指令从第一次被绑定到元素上到最终与被绑定的元素解绑,它会经过以下几种状态:
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
源码内部是监听虚拟DOM渲染更新的create、update、destory阶段都得处理指令逻辑
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
Vue.filter
注册或获取全局过滤器。
Vue.filter( id, [definition] )
// 注册
Vue.filter('my-filter', function (value) {
// 返回处理后的值
})
// getter,返回已注册的过滤器
var myFilter = Vue.filter('my-filter')
源码实现:
同全局指令一样,Vue.options[‘filters’]是用来存放全局过滤器的地方。
过滤器(纯函数,无this)
内部工作原理,就是将用户写在模板中的过滤器通过模板编译,编译成_f函数的调用字符串,之后在执行渲染函数的时候会执行_f函数,从而使过滤器生效。
构建
//可以在一个组件的选项中定义本地的过滤器:
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
//也可以在创建vue实例之后全局
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
过滤器有两种使用方式,分别是在双花括号插值中和在 v-bind 表达式。
解析过滤器:将过滤器字符串转化成_f(“capitalize”)(message)
v-bind 表达式中的过滤器它属于存在于标签属性中,在HTML解析器parseHTML函数中负责处理标签属性的函数是processAttrs,所以会在processAttrs函数中调用过滤器解析器parseFilters函数对写在该处的过滤器进行解析。
在双花括号中的过滤器它属于存在于标签文本中,在HTML解析器parseHTML函数中,当遇到文本信息时会调用parseHTML函数的chars钩子函数,在chars钩子函数内部又会调用文本解析器parseText函数对文本进行解析,所以会在文本解析器parseText函数中调用过滤器解析器parseFilters函数对写在该处的过滤器进行解析。
使用
//可以串联使用
{{ message | filterA | filterB }}
//过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
<div v-bind:id="rawId | formatId"></div>
Vue.component
注意全局和局部注册两种方式
//全局
Vue.component( id, [definition] )
Vue.component('tab-title',{
props:['title'],
template:'<li v-on:click="$emit(\'change\')">{{title}}</li>'
})
//补充局部
var app=new Vue({
el: '#app',
components: {
'tab-title': {
props:['info'],//接受父元素传递的参数
template:'<li v-on:click="$emit(\'addactive\')">{{info}}</li>'//点击时传递通过$emit子元素传递给父元素调用 addactive方法(不能使用驼峰写法)
}
},
methods:{
},
data:{
}
});
源码实现:
同全局指令一样,Vue.options[‘components’]是用来存放全局组件的地方。
Vue.use安装插件
写一个vue插件
如果插件是一个对象,必须提供 install 方法。
如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。
该方法需要在调用 new Vue() 之前被调用。
import dlg from 'vue-dialog-rexsheng';
Vue.use(dlg);
Vue.mixin
Vue.mixin( mixin )
该API是用来向全局注册一个混入,即可以修改Vue.options属性,并且会影响之后的所有Vue实例,这个API虽然在日常的业务开发中几乎用不到,但是在编写Vue插件时用处非常大。
Vue.observable
Vue.observable( object )
让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生改变时触发相应的更新。也可以作为最小化的跨组件状态存储器
源码内部是调用了observe方法
Vue.compile
Vue.compile( template )
在 render 函数中编译模板字符串。只在独立构建时有效
源码内部是调用了compileToFunctions方法
Vue.version
用法如下:提供字符串形式的 Vue 安装版本号
Vue.version
建立自己的全局变量
只读的全局变量
定义在其他模块然后导入,OnlyRead作为全局对象来调用
读写的全局变量
在main.js中的data定义,然后添加到原型对象prototype上,每个对象都可以this.引用/修改
import Vue from 'vue';
let OnlyRead = new Vue({
methods: {
deleteCookie: function (cname) {
let d = new Date();
let expires = "expires=" + d.toGMTString();
document.cookie = cname + "=; " + expires;
}
})
export default OnlyRead;
import OnlyRead from "./components/common/comm";
//look at there
OnlyRead.deleteCookie('ms_username')
import Vue from 'vue';
let ReadWrite = new Vue({
methods: {
deleteCookie: function (cname) {
let d = new Date();
let expires = "expires=" + d.toGMTString();
document.cookie = cname + "=; " + expires;
}
})
export default ReadWrite;
Vue.prototype.$ReadWrite = MyComm;
//项目中任何地方都可如此引用
this.$ReadWrite.deleteCookie('ms_username')
内置组件
keep-alive
组件的定义位于源码的 src/core/components/keep-alive.js 文件
keep-alive的中还运用了LRU(Least Recently Used)算法。
是Vue中内置的一个抽象组件,它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。把组件保存在内存中,而不是直接的销毁,这样做可以保留组件的状态或避免多次重新渲染,以提高页面性能。
<keep-alive>不常变动的组件或者需要缓存的组件用</keep-alive>
还有两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态。
源码分析:
组件一旦被 < keep-alive> 缓存,那么再次渲染的时候就不会执行 created、mounted 等钩子函数,但是我们很多业务场景都是希望在我们被缓存的组件再次被渲染的时候做一些事情,好在Vue 提供了 activated和deactivated 两个钩子函数,它的执行时机是 < keep-alive> 包裹的组件激活时调用和停用时调用
应用:当用户再次切回组件时保留切走之前的组件状态。
组件可接收三个属性:
include/exclude- 字符串或正则表达式,允许组件有条件的进行缓存,前者匹配缓存,后者反之。
max - 数字。最多可以缓存多少组件实例。
<html>
<head>
<title>VueJS</title>
</head>
<div id="app">
<button @click="switchComp('child1')">组件1</button>
<button @click="switchComp('child2')">组件2</button>
<keep-alive>
<component :is="chooseComponent"></component>
</keep-alive>
</div>
<body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var child1 = {
template: '<div>组件1:<input type="text"/></div>',
}
var child2 = {
template: '<div>组件2:<input type="text"/></div>'
}
var vm = new Vue({
el: '#app',
components: {
child1,
child2,
},
data() {
return {
chooseComponent: 'child1',
}
},
methods: {
switchComp(component) {
this.chooseComponent = component;
}
}
})
</script>
</body>
</html>