2. vue2的应用: 全局相关

608 阅读2分钟

前言

这一篇让我们来学习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

image.png

devtools

Vue.config.devtool = true 

配置是否允许 vue-devtools 检查代码。开发版本默认为 true,生产版本默认为 false。生产版本设为 true 可以启用检查。

errorHandler

 Vue.config.errorHandler = function(...args) { // err, vm, info
        console.log(args, '出现错误')
    }

当我在mounted方法打印未定义的title时,

image.png 可见这个方法能够捕获生命周期里的错误,其他能够被捕获的错误类型,请参考文档。

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')

这个时候:

image.png 能正常显示,这时候我去注释掉components选项,则控制台会报错, image.png 所以我们就可以配置如下:

Vue.config.ignoredElements = ['child-pane']

这样就不会报上面的错误了。

keyCodes

键盘上的每个字符都有其keycode,我们可以通过原生键盘事件,如onkeypress去获取keycode,从而知道用户按下了哪些键,keycode是一个数值,在vue里可以通过@keyup去监听键盘事件,@keyup.somekeycode可以指定用户按下哪个键而触发事件,比如,vkeyCode是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的默认配置:

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 renderVue.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>

运行结果:

image.png 打印结果分析: Vue里数据发生了变化,视图并没有马上更新,而是先将更新方法放在一个数组callbacks里,每次执行Vue.nextTick,相当于往这个数组后面添加一个方法,callbacks里面的方法什么时候执行呢?其实是交给异步任务执行的,而我们实现异步的方法有promise,setImmediate, setTimeout,Vue里面实现异步的最高优先级是promise,所以会把callbacks的执行放在异步回调里。 0. Vue里面执行了var p = Promise.resolve();,创建了微任务 p,注册回调函数flushCallbacksflushCallbacks长这样:

function flushCallbacks () { // 回调函数调用
    pending = false;
    var copies = callbacks.slice(0);
    callbacks.length = 0;
    for (var i = 0; i < copies.length; i++) {
      copies[i]();
    }
  }
  1. 把修改title前的nextTick的回调函数n0放进callbacks
  2. 把修改title前的Promise的回调函数p0放进callbacks
  3. 执行第一次修改title的同步代码,把watcher视图更新函数放进callbacks, 这个时候还没有进行UI render,于是title打印的结果还是原来的值
  4. 把第一次修改title后的Promise的回调函数p1放进队列,注意,这已经是另外一个微任务
  5. 把第一次修改title后的nextTick的回调函数n1放进callbacks
  6. 执行第2次修改title的同步代码,由于前面已经把watcher视图更新函数放进队列,这次不必重复加入,这个时候还没有进行UI render,于是title打印的结果还是原来的值
  7. 把第2次修改title后的Promise的回调函数p2放进队列,又一个新的微任务
  8. 把第2次修改title后的nextTick的回调函数n2放进callbacks
  9. 同步任务执行完,开始执行异步任务,按照异步任务的执行顺序:p, p1, p2 所以会先执行flushCallbacks,遍历执行callbacks里的方法,再然后是p1的回调函数,p2的回调函数。

image.png

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会有settergetter方法,说明它是响应式的。如果直接赋值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>

结果:

image.png

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 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

  1. 添加全局方法或者 property。如:vue-custom-element
  2. 添加全局资源:指令/过滤器/过渡等。如 vue-touch
  3. 通过全局混入来添加一些组件选项。如 vue-router
  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
  5. 一个库,提供自己的 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/…