vue API 知识点(1) --- 全局 API 总结
1.Vue.extend(options)
构造器,创建一个 子类 。参数是一个包含组件选项的对象
data 选项是特例,需要注意 在 Vue.extend() 中它必须是一个函数,
<div id="test"></div>
// 创建构造器 let MyTest = Vue.extend({ template: '<p>{{name}} {{age}}</p>', data(){ return { name: 'zhangning', age: '24' } } })
创建 MyTest 实例,并挂载到一个元素上
new MyTest().$mount('#test')
最终结果如下
<p>zhangning 24</p>
上面 extend 创建的是 Vue 构造器,而不是一个具体的组件实例,所以不能够通过 new Vue({components: testExtend}) 来直接使用,需要通过 new MyTest().$mount('#test') 来挂载到指定的元素上
*调用 Vue.extend() 创建的是一个组件构造器
*通常在创建组件构造器时,传入 template 代表我们自定义组件的模板
*该模板就是在使用到组件的地方,要显示的 HTML 代码
*但是,这种写法在vue2之后几乎就不再使用了,都是直接使用语法糖,不过我们还是要学习的,为后面的学习打下基础
现在我们通常不使用 Vue.extend() 来注册组件了,使用语法糖的写法,直接把对象放在 Vue.component() 来注册组件,可以看第 7 个API进行学习
调用Vue.component() 是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名字
所以需要传递两个参数:1.注册组件的标签名 2.组件构造器
组件必须挂在在某个Vue实例下,否则不会生效
示例:封装一个全局提示组件
首先创建一个 message.vue 组件
<template> <transition> <div v-show='show'>{{message}}</div> </transition> </template> <script> export default{ data(){ return { show: false, message: '' } } } </script> <style scoped>
div {
padding: 10px;
color: #ddd;
background: red;
text-align: center;
position: fixed;
top: 30%;
left: 50%;
} </style>
然后在 main.js 中配置
import Message from './components/Message.vue' const MessageM = { install: function(Vue) { // 创建一个 vue 的子类组件 const MessageConstructor = Vue.extend(Message) // 创建子类实例,并挂载到动态创建的元素上,并将元素添加到全局结构中 const inst = new MessageConstructor().$mount(document.createElement('div')) document.body.appendChild(inst.$el) // 在 vue 原型链上注册方法,控制组件 Vue.prototype.$message = (msg, time = 1000) => { inst.message = msg inst.show = true setTimeout(( inst.show = false ), time) } } }
Vue.use(MessageM)
在组件内使用
this.$message('消息提示')
以上就是一个简单的全局消息提示
2.Vue.nextTick
在写词DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM
<template> <div> <div ref='valRef'>{{ val }}</div> <div>{{ val1 }}</div> <div>{{ val2 }}</div> <div>{{ val3 }}</div> <el-button type="primary" @click.once='handleClick'>改变val</el-button> </div> </template> <script> export default { name: 'index', data() { return { val: 'zhangning', val1: '', val2: '', val3: '' } }, methods: { // 点击按钮时,val1和val3获取的是初次加载时候的val的值,因为vue的DOM是异步加载, // 而使用了nextTick的val2的值在msg改变之后,就立刻获取到了val更新之后的值 handleClick() { this.val = '我已改变'; // DOM 未更新,获取不到最新的dom this.val1 = this.$refs.valRef.innerHTML; this.$nextTick(() => { // DOM 已更新,获取最新的dom this.val2 = this.$refs.valRef.innerHTML; }) // DOM 未更新,获取不到最新的dom this.val3 = this.refs.valRef.innerHTML; } } } </script>
官方给出的解释和例子(异步更新队列)
Vue 在更新 DOM 时是异步执行的。只要监听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更,如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然而在下一个的事件循环 tick 中,Vue 刷新队列并执行实际(已去重)工作。Vue 在内部对异步队列尝试使用原生的Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
例如,当设置 vm.someData = 'newVlaue',该组件不会立即重新渲染,当刷新队列时,组件会在下一个事件循环 tick 中更新。多数情况我们不需要关心这个过程,但是如果想基于更新后的 DOM 状态做点什么,这就有点棘手。虽然Vue鼓励我们使用数据驱动的方式思考,避免直接接触 DOM ,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成之后被调用。例如
<div id="example">{{message}}</div>
var vm = new Vue({ el: '#example', data: { message: '123' } }) vm.message = 'new message' // 更改数据 vm.$el.textContent === 'new message' // false Vue.nextTick(function () { vm.$el.textContent === 'new message' // true })
在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue ,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上
Vue.component('example', { template: '<span>{{ message }}</span>', data: function () { return { message: '未更新' } }, methods: { updateMessage: function () { this.message = '已更新' console.log(this.$el.textContent) // => '未更新' this.$nextTick(function () { console.log(this.$el.textContent) // => '已更新' }) } } })
因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的语法 async/await 完成相同的事情
methods: { updateMessage: async function () { this.message = '已更新' console.log(this.$el.textContent) // => '未更新' await this.$nextTick() console.log(this.$el.textContent) // => '已更新' } }
3.Vue.set
Vue.set(target, key, value)
target:要更改的数据源(可以是对象或者数组)
key:要更改的具体数据
value:重新赋的值
由于 js 的限制,Vue 不能检测出数据的改变,但是通过这种方法修改数组中的数据,就会检测到数组的改变,是响应式的
用法:向响应式对象中添加一个 property, 并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 arr[1] = 110)
export default { name: 'index', data() { return { arr: [1,2,3] } }, methods: { // 修改 arr[1] 变为 200 handleChange(){ this.$set(this.arr, 1, 200);// arr: [1, 200, 3] }, // arr 数组添加元素 600 handleAddArr(){ this.$set(this.arr, this.arr.length, 600); } } }
4.Vue.delete()
Vue.delete(target, key, value)
target:要删除的数据源(可以是对象或者数组)
key:要删除的具体数据
用法:删除对象的 property。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到 property 被删除的限制,虽然很少使用,但直到要会用
export default { name: 'index', data() { return { zhangning: { height: 187, name: 'zhangning', sex: '男' } } }, methods: { // 删除 name handleDeleteName() { this.$set(this.zhangning, 'name'); } } }
5.Vue.directive()
Vue.directive(string, Function | Object)
注册或获取全局指令
声明周期:
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定被插入文档中)
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生改变,也可能没有,但是你可以通过比较更新前后的值来忽略不必要的模板更新(详细的钩子函数参数见下)
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用
unbind:只调用一次,指令与元素解绑时调用
钩子函数参数
el:指令所绑定的元素,可以用来直接操作 DOM
binding:一个对象,包含以下 property:
name:指令名,不包括 v- 前缀
value:指令的绑定值,例如:v-my-directive='1+1'中,绑定值为 2
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用,无论值是否改变都可用
expression:字符串形式的指令表达式。例如 v-my-directive='1+1' 中,表达式为 ‘1+1’
arg:传给指令的参数,可选。例如 v-my-directive: foo 中,参数为 'foo'
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 {foo: true, bar: true}
vnode:Vue 编译生成的虚拟节点
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>
Vue.directive('demo', { bind: function (el, binding, vnode) { var s = JSON.stringify el.innerHTML = 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'expression: ' + s(binding.expression) + '<br>' + 'argument: ' + s(binding.arg) + '<br>' + 'modifiers: ' + s(binding.modifiers) + '<br>' + 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#hook-arguments-example', data: { message: 'hello!' } })
页面展示效果如下
name: 'demo'
value: 'hello!'
expression: 'message'
argument: 'foo'
modifiers: {a: true, b: true}
vnode keys: tag,data,children,text,elm,ns,context...
动态指令参数,指令的参数可以是动态的,例如,在 v-mydirective:[argument]='value'中,argument 参数可以根据组件数据进行更新,这可以让我们的自定义指令灵活使用。
创建一个自定义指令,把元素固定在距离顶部 200px 的位置
<div id="test"> <p>Scroll down the page</p> <p v-pin="200">距离顶部200px</p> </div>
Vue.directive('pin', { bind: function(el, binding, vnode){ el.style.position = 'fixed' el.style.top = binding.value + 'px' } }) new Vue({ el: '#test' })
当我们需要固定在左侧而不是顶部的时候,这时候我们就需要用到动态指令
<div id="test"> <p>Scroll down the page</p> <p v-pin:[direction]="200">距离顶部200px</p> </div>
Vue.directive('pin', { bind: function(el, binding, vnode){ el.style.position = 'fixed' let s = binding.arg == 'left' ? 'left' : 'top' el.style[s] = binding.value + 'px' } }) new Vue({ el: '#test' , data(){ return { direction: 'left' } } })
以上这个自定义指令现在的灵活性就足以支持一些不同的用例了
函数简写
很多时候,我们想在 bind 和 update 时触发相同行为,而不关心其他的钩子。
Vue.directive('pin', function(el, binding){ el.style.top = binding.value + 'px' })
如果指令需要多个值,可以传入 js 对象,指令函数能够接收所有合法的 js 表达式
<div v-demo="{ color: 'red', text: 'hello'}"></div>
Vue.directive('demo', function(el, binding){ console.log(binding.value.color) // red console.log(binding.value.text) // hello })
在项目中使用的案例
通过权限控制每个页面的按钮,
先定义一个 directive.js,写上全局指令
import Vue from 'vue'; import store from './store'; Vue.directive('permission', { inserted: (el, binding) => { if (store.getters.userButtons.length) { const buttons = store.getters.userButtons; if (buttons.indexOf(binding.value) < 0) el.parentNode.removeChild(el); } else { store.dispatch('getInfo').then(data => { const { buttons } = data.buttons; if (buttons.indexOf(binding.value) < 0) el.parentNode.removeChild(el); }); } } });
在 main.js 中全局引入
import './directive.js'
在 vue 组件中使用,判断有没有下载按钮权限
<el-button round icon="el-icon-document" type="primary" v-permission="'SJML_SQXZ'" @click="applyForDownload" >下载文档</el-button>
6.Vue.filter()
定义过滤器,可被用于常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式,过滤器应该被添加在 js 表达式的尾部,由管道符号指示:'|'
<!-- 在双花括号中 --> {{ message | capitalize }}
<!-- 在 `v-bind` 中 --> <div v-bind:id="rawId | formatId"></div>
在一个组件的选项中定义本地过滤器
filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } }
在创建 Vue 实例之前全局定义过滤器
Vue.filter('capitalize', function() { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) }) new Vue({ // ... })
注意:当全局过滤器和局部过滤器重名时,会采用局部过滤器
过滤器函数总接收表达式的值作为第一个参数
过滤器可以串联:
{{ message | filterA | filterB }}
这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递给 filterB 中。
过滤器时 js 函数,因此可以接收参数
{{ message | filterA('arg1', arg2) }}
这个例子,filterA 被定义为接收三个参数的过滤器函数,其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数, arg2 作为第三个参数
在项目中使用的案例
先定义一个 filters.js
export { formatBoolean, formatDate, formatNum, formatStatus }; // 布尔值 function formatBoolean(value) { return value ? "是" : "否"; } // 状态 function formatStatus(value) { return value ? "成功" : "失败"; } // 时间戳转换 function formatDate(value) { let date = new Date(parseInt(value)); let Y = date.getFullYear(); let M = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; // let D = date.getDate(); let D = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); let h = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(); let m = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(); let s = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds(); return Y + "." + M + "." + D + " " + h + ":" + m + ":" + s; } // 数值加逗号 function formatNum(num) { num = (num || 0).toString(); let result = ""; while (num.length > 3) { result = "," + num.slice(-3) + result; num = num.slice(0, num.length - 3); } if (num) { result = num + result; } return result; }
然后在 main.js 中进行全局引入
import * as filters from './filters.js' Object.keys(filters).forEach(item => { Vue.filter(item, filters[item]) })
// 这里讲一下 Object.keys() 方法,表示给定对象的所有可枚举属性的字符串数组 let zn = {name: 'zhangning', age: '24', height: '187'} Object.keys(zn);// ['name', 'age', 'height'] 返回可枚举属性组成的数组 // 处理数组,返回索引值数组 let arr = [100, 200, 300, 400, 500] Object.keys(arr);// ['0', '1', '2', '3', '4'] 返回索引值字符串组成的数组 // 处理字符串,返回索引值数组 let str = 'zhang'; Object.keys(str);// ['0', '1', '2', '3', '4'] // 常用技巧 let zn = {name: 'zhangning', age: '25', address: '合肥', getName: function()} Object.keys(person).map(item => { person[item] // 获取到属性对应的值,做一些处理 })
7.Vue.component()
// 定义一个名为 button-counter 的新组件 Vue.component('button-counter', { data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' })
data 必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,如果不是一个函数,就会影响到组件所有实例
在项目中自定义全局组件案例
首先创建一个组件 download.vue
然后创建一个 download.js,在 js 中引入组件
// 引入组件 import DLFLoading from './index.vue'; // 自定义组件对象 const DownloadDialog = { // install 是默认的方法。当外界在 use 这个组件的时候,就会调用本身的 install 方法,同时传一个 Vue 这个类的参数 install: function(Vue) { // Vue.component() 与正常的全局注册组件用法相同,可以理解为通过 install 和 Vue.use()函数注册了全局组件 Vue.component('DownloadDialog', DLFLoading); } } // 导出 export default DownloadDialog;
接着在 main.js 中全局引入组件
// 全局引入自定义下载 loading import DownloadDialog from '@/components/DownloadDialog/download.js'; Vue.use(DownloadDialog);
最后在项目中直接就可以使用
<DownloadDialog @cancelDownload="cancelDownload" :downloadOver="downloadOver" :downloadLoading="downloadLoading" ></DownloadDialog>
8.Vue.use()
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。
该方法需要在 new Vue() 之前被调用
当 install 方法被同一个插件多次调用,插件将只会被安装一次。
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制 一般有以下几种
1.添加全局方法或者property
2.添加全局资源:指令/过滤器/过渡等
3.通过全局混入来添加一些组件选项
4.添加 Vue 实例方法,通过把他们添加到 Vue.prototype 上实现
5.一个库,提供自己的 api,同时提供上面提到的一个或多个功能
使用插件
通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成
// 调用 MyPlugin.install(Vue) Vue.use(MyPlugin) new Vue({ // ...组件选项 })
也可以传入一个可选的选项对象
Vue.use(MyPlugin, {someOption: true})
Vue.use 会自动阻止多次注册相同插件,即使多次调用也只会注册一次该插件
9.Vue.mixin()
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为(官方不推荐使用)
官网全局混入案例。混入也可以进行全局注册,使用时格外小心,一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,它可以用来自定义选项注入处理逻辑。
// 为自定义的选项 myOption 注入一个处理器
Vue.mixin({ created: function(){ var myOption = this.$options.myOption if(myOption) { console.log(myOption) } } }) new Vue({ myOption: 'zhangning' }) // => 'zhangning'
注意:要谨慎使用全局混入,因为它影响每个单独创建的 Vue 实例(包括第三方组件)。大多数情况下,之应当应用于自定义选项,就像上面实例一样,推荐将其作为插件发布,以避免重复应用混入
使用案例
定义一个 mixin.js
let MiXin = { data(){ return { name: 'zhangning' } }, created(){ console.log('这是mixin中的name', this.name) }, mounted(){}, methods: {} } export default MiXin
// 全局引入 import mixin from './mixin' Vue.mixin(mixin) // 在 vue 组件中局部引入 import '@/mixin' export default { mixins: [mixin] }
混入和组件的合并注意事项
1.数据对象 data 在内部进行递归合并,在和组件的数据发生冲突时以组件数据优先
2.同名钩子函数(created,mounted)将混合为一个数组,都将被调用。另外混入对象的钩子将在组件自身钩子之前调用
3.值为对象的选项(methods,components,directives)将被混合为同一个对象,两个对象键名冲突时,去组件对象的键值对
10.Vue.compile() -- 模板渲染
将一个模板字符串编译成 render 函数。旨在完整版时可用
let res = Vue.compile('<div><span>{{ massage }}</span></div>')
new Vue({
data: {
message: 'hello'
},
render: res.render,
staticRenderFns: res.staticRenderFns
})
以上是官网给的一个小实例。
以后深入理解之后,再回来更新
11.Vue.observable() -- 可用于组件间共享数据
让一个对象可响应,Vue 内部会用它来处理 data 函数返回的对象。
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时出发相应的更新。
处理简单的跨组件共享数据状态的问题,可以说是个精简的 vuex
示例
创建store.js
import Vue from 'vue' export const store = Vue.observable({num: 0}) export const mutations = { setNum (num) { store.num = num } }
在组件中使用(所有的说明都没有举例子来的实在,理解的更快)
<template> <div> <span>选择数量</span> <button @click="setNum(num + 1)"> + </button> <span>count</span> <button @click="setNum(num - 1)"> - </button> </div> </template> <script> import { store, mutations } from '@/store/store' export default { name: 'numIndex', computed: { count() { return store.num } }, methods: { setNum: mutations.setNum } } </script>
12.Vue.version()
提供字符串形式的 Vue 安装版本号。这对社区的插件和组件来说非常有用,你可以根据不同的版本号采取不同的策略
let version = Number(Vue.version.split('.')[0])
if (version == 2) {
} else if (version == 1) {
} else {}
就是获取当前使用的 vue 版本号,原理就是读取 package.json 中的 version 字段
以上就是 vue API 全局 API 的所有内容,
宝剑锋从磨砺出,梅花香自苦寒来。