前言
这一篇让我们来学习vue API全局相关部分。
1. 全局相关
1.1 Global Config
这部分参考官网会更清楚,这里只记录某些应用场景和使用方法。
silent
Vue.config.silent = false // vue执行过程中如果出现错误,要不要输出到控制台打印
optionMergeStrategies
合并策略,父子组件相同的option如何合并。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue的全局配置和全局API</title>
<!-- 1. 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="root">
{{title}} : {{count}}
</div>
</body>
<script>
// 2. 创建vue实例
// 定义全局配置Vue.config
/**
* 数据的合并策略,是一个对象,对象的key是方法,
* 比如我们想合并组件的data选项,
* parant是父组件的data, child是当前组件的data, vm是vue实例,key是data
*/
Vue.config.optionMergeStrategies.data = function(parent, child, vm, key) {
let count = child.count + 1
return {
title: '我替换了组件的title',
count
}
}
const vm = new Vue({
// el: '#root',
data: {
title: '让我们来学习如何使用vue',
count: 1
},
}).$mount('#root')
</script>
</html>
本来title的值是'让我们来学习如何使用vue',通过合并策略,修改title的值为'我替换了组件的title',修改count的值由1变为2
devtools
Vue.config.devtool = true
配置是否允许 vue-devtools 检查代码。开发版本默认为 true,生产版本默认为 false。生产版本设为 true 可以启用检查。
errorHandler
Vue.config.errorHandler = function(...args) { // err, vm, info
console.log(args, '出现错误')
}
当我在mounted方法打印未定义的title时,
可见这个方法能够捕获生命周期里的错误,其他能够被捕获的错误类型,请参考文档。
warnHandler
Vue.config.warnHandler = function(msg, vm, trace) {
// `trace` 是组件的继承关系追踪
console.log(msg, vm, trace)
}
为 Vue 的运行时警告赋予一个自定义处理函数。注意这只会在开发者环境下生效,在生产环境下它会被忽略。
ignoredElements
比如你现在定义一个组件:
// 定义一个组件,vue实例说白了都是一个对象,组件当然也是一个对象啦
let childPane = {
props: {
title: ''
},
// data: { // 这里不要以对象的形式,不然会出现重名
// count: 10
// },
data() {
return {
count: 10
}
},
template: '<div>{{title}}:{{count}}</div>'
}
然后使用components注册使用
<body>
<div id="root">
{{title}} : {{count}}
<child-pane :title="title"></child-pane>
</div>
</body>
.....
const vm = new Vue({
// el: '#root',
components: {
childPane
},
data: {
title: '让我们来学习如何使用vue',
count: 1
},
}).$mount('#root')
这个时候:
能正常显示,这时候我去注释掉components选项,则控制台会报错,
所以我们就可以配置如下:
Vue.config.ignoredElements = ['child-pane']
这样就不会报上面的错误了。
keyCodes
键盘上的每个字符都有其keycode,我们可以通过原生键盘事件,如onkeypress去获取keycode,从而知道用户按下了哪些键,keycode是一个数值,在vue里可以通过@keyup去监听键盘事件,@keyup.somekeycode可以指定用户按下哪个键而触发事件,比如,v的keyCode是86,我们使用Vue.config.keyCodes可以设置keycode的别名
Vue.config.keyCodes = {
v: 86,
f1: 112,
// camelCase 不可用
mediaPlayPause: 179, // 取而代之的是 kebab-case 且用双引号括起来
"media-play-pause": 179,
up: [38, 87]
}
以下2种方式,无论是使用别名还是keycode,输入v时都会触发test事件。
<input type="text" @keyup.86="test">
<input type="text" @keyup.v="test">
performance
用于性能检测
Vue.config.performance = true
productionTip
设置为 false 以阻止 vue 在启动时生成生产提示。
Vue.config.productionTip = false
ps: Vue.config的默认配置:
说了这么多,才完成一点一点,坚持啊!接下来说说全局API.
1.2 Global API
- Vue.extend
创建
Vue的子构造器,传入一个对象,这个对象跟创建vue实例的传入的options一样
var VueExtend = Vue.extend({
template: '<div>{{content}}</div>',
data() {
return {
content: '我是通过Vue.extend创建的'
}
},
mounted() {
console.log('Vue.extend已经被挂载')
}
})
new VueExtend().$mount('#extend')
Vue.nextTick
当你想操作DOM时,你可能需要用到它。在修改vue的数据时,vue并不会立即更新视图,而是把要更新的视图先放在一个队列里,每次event loop后才进行UI render,Vue.nextTick可以在DOM更新完毕之后执行一个回调,确保我们操作的是更新后的DOM,参考。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue全局API</title>
<!-- 1. 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="root">
{{title}}
</div>
</body>
<script>
const vm = new Vue({
el: '#root',
data: {
title: 'Vue 全局API'
},
mounted() {
// console.log('vue实例已经挂载')
Vue.nextTick(() => {
console.log('nextTick里打印未修改title', document.querySelector('#root')
.innerHTML)
})
Promise.resolve().then(() => {
console.log('then里打印未修改title', document.querySelector('#root')
.innerHTML)
})
this.title = '这是我第一次更改'
Promise.resolve().then(() => {
console.log('then里打印第1次修改title', document.querySelector('#root')
.innerHTML)
})
Vue.nextTick(() => {
console.log('nextTick里打印第一次修改title', document.querySelector('#root')
.innerHTML)
})
console.log('打印第1次修改title', document.querySelector('#root').innerHTML)
this.title = '这是我第二次更改'
console.log('打印第2次修改title', document.querySelector('#root').innerHTML)
Vue.nextTick(() => {
console.log('nextTick里打印第2次修改title', document.querySelector('#root')
.innerHTML)
})
}
})
</script>
</html>
运行结果:
打印结果分析:
Vue里数据发生了变化,视图并没有马上更新,而是先将更新方法放在一个数组
callbacks里,每次执行Vue.nextTick,相当于往这个数组后面添加一个方法,callbacks里面的方法什么时候执行呢?其实是交给异步任务执行的,而我们实现异步的方法有promise,setImmediate, setTimeout,Vue里面实现异步的最高优先级是promise,所以会把callbacks的执行放在异步回调里。
0. Vue里面执行了var p = Promise.resolve();,创建了微任务 p,注册回调函数flushCallbacks。
flushCallbacks长这样:
function flushCallbacks () { // 回调函数调用
pending = false;
var copies = callbacks.slice(0);
callbacks.length = 0;
for (var i = 0; i < copies.length; i++) {
copies[i]();
}
}
- 把修改
title前的nextTick的回调函数n0放进callbacks - 把修改
title前的Promise的回调函数p0放进callbacks - 执行第一次修改
title的同步代码,把watcher视图更新函数放进callbacks, 这个时候还没有进行UI render,于是title打印的结果还是原来的值 - 把第一次修改
title后的Promise的回调函数p1放进队列,注意,这已经是另外一个微任务 - 把第一次修改
title后的nextTick的回调函数n1放进callbacks - 执行第2次修改
title的同步代码,由于前面已经把watcher视图更新函数放进队列,这次不必重复加入,这个时候还没有进行UI render,于是title打印的结果还是原来的值 - 把第2次修改
title后的Promise的回调函数p2放进队列,又一个新的微任务 - 把第2次修改
title后的nextTick的回调函数n2放进callbacks - 同步任务执行完,开始执行异步任务,按照异步任务的执行顺序:
p,p1,p2所以会先执行flushCallbacks,遍历执行callbacks里的方法,再然后是p1的回调函数,p2的回调函数。
Vue.set
用法: Vue.set(target, key, val)。就是将在vue实例上但是没有定义在data里面的数据转为可响应式。
const vm = new Vue({
el: '#root',
data: {
title: 'Vue 全局API',
person: {
name: 'peter'
}
},
mounted() {
Vue.set(this.person, 'age', 12)
}
})
在控制台打印vm.person,发现age会有setter和getter方法,说明它是响应式的。如果直接赋值this.person.age = 12是没有这个效果的。
注意对象不能是Vue实例,或者Vue实例的根数据对象。
Vue.delete
用法:Vue.delete(target, key) 删除属性,源码里是借助JavaScript 的 delete
Vue.directive
定义指令,用法
Vue.directive('directive-name', {
bind: function() {
},
inserted: function() {
},
update: function() {
},
componentUpdated: function() {
},
unbind: function() {
}
})
包含5个钩子函数,分别在指令绑定元素(bind),绑定元素插入根节点(insert),组件更新(update, componentUpdated) 和指定解绑元素(unbind)时执行,通过每个钩子函数的参数我们可以拿到绑定节点,vnode, oldVnode,传进来的参数,基于此我们就可以操作了。比如我想定义一个指令,被绑定的元素的innerHTML变成pigpig,就可以这样定义:
Vue.directive('yh', {
bind: function(el) {
el.innerHTML = 'pigpig'
}
})
使用:
<div v-yh>测试自定义指令</div>
结果:
Vue.filter
过滤器:使用 |将数据交给过滤器处理.
例子:获取<=0的数据
// 定义
Vue.filter('lteZero', (value) => {
if (Array.isArray(value)) {
return value.filter(val => val <= 0)
}
return value
})
// 使用:
{{list | lteZero}}
Vue.component
Vue.component('my-com', { // 默认调用了Vue.extend方法
template: '<div>{{title}}</div>',
data() {
return {
title: '我是子组件'
}
}
})
全局定义的组件使用时不需要重新注册。
Vue.use
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
- 添加全局方法或者 property。如:vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如 vue-touch
- 通过全局混入来添加一些组件选项。如 vue-router
- 添加 Vue 实例方法,通过把它们添加到
Vue.prototype上实现。- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
// 定义插件
const selfPlugin = {
install(Vue, options) {
Vue.component('test-use', {
template: '<div>自定义Vue插件<div>'
})
}
}
// 注册插件
Vue.use(selfPlugin)
...
// 使用
<test-use></test-use>
Vue.mixin
全局混入,每一个组件都会受影响, 不推荐使用。
Vue.mixin({
mounted() {
console.log('每一个组件都会受影响')
}
})
Vue.compile
将模板编译成render函数
var res = Vue.compile('<div><span>{{ msg }}</span></div>')
new Vue({
data: { msg: 'hello' },
render: res.render,
staticRenderFns: res.staticRenderFns
})
Vue.observable
const out = Vue.observable({
name: '这是外面的值'
})
...
// 使用
this.out = out
这个时候即使不使用Vue.set, this.out也是可响应式的。
Vue.version
Vue的版本,可以根据版本做不同的兼容操作。
以上测试代码在github.com/fanqingyun/…