Lodash, Moment, Axios, Async…这些都是非常有用的 Javascript 库,而且你会希望使用在你的各种 Vue.js 应用中。
但是随着你的项目的增长,你会想要将代码分离为单一的组件文件跟模块文件。同时,你可能也会想要让你的应用能运行在不同的环境下,比如可以通过服务端渲染。
除非你找到一个简单且健壮的方法去引入这些 Javascript 库到你的组件文件与模块文件中,不然他们将会成为你项目中的一个累赘!
_注意:这篇文章原载于the Vue.js Developers blog 2017/04/22_
最直接添加一个类库到你的项目中的方法,是让这个类库作为一个全局变量挂载在 window
对象上
entry.js
window._ = require('lodash');
MyComponent.vue
export default {
created() {
console.log(_.isEmpty() ? 'Lodash everywhere!' : 'Uh oh..');
}
}
关于反对使用 window 全局变量是一个十分悠久的话题,但是,在具体到这篇文章中,是因为这样不支持服务器渲染,当这个应用跑在服务端,window
对象将不复存在,因此会导致尝试访问这个原型的时候会抛出一个错误并终止它。
另一个二流方法是在每一个文件中都把类库文件引入进去。
MyComponent.vue
import _ from 'lodash';
export default {
created() {
console.log(_.isEmpty() ? 'Lodash is available here!' : 'Uh oh..');
}
}
这当然行得通,然而这并不 DRY 并且这基本上就是一种惩罚游戏:你不得不记住要在每个文件中都引入这些些类库,以及当你不再需要使用它以后也要记得移除它。
与此同时,如果你没有正确的设置好你的构建工具,你很有可能会在你最终构建出来的文件中会有一个类库的多个副本在其中。
在 Vue 项目中使用一个 Javascript 库的最干净且最健壮的方法是将他代理为 Vue 原型对象的属性。 让我们来试试通过这种方式把 Moment date 和 time 库添加到我们的项目中来:
entry.js
import moment from 'moment';
Object.definePrototype(Vue.prototype, '$moment', { value: moment });
由于所有的组件都会从 Vue 原型对象中继承其所有方法,这让 Moment 自然而然的传递到任何或者说所有的组件当中,并且无需全局变量或者是手动引入什么东西(到组件中)。 只通过 this.$moment
就可以在任意的实例/组件中快速简单地访问到 Moment。
MyNewComponent.vue
export default {
created() {
console.log('The time is ' . this.$moment().format("HH:mm"));
}
}
现在,让我们花点时间来搞清楚这是为什么。
我们通常会这样设置一个对象属性:
Vue.prototype.$moment = moment;
在这个示例中你确实可以这样做,但如果用Object.defineProperty
来定义,我们就可以同时定义属性的描述符。描述符允许我们设置一些底层的细节信息,例如我们的属性是否是可写的,又或者是 for
循环中是否是可枚举的等等。
我们通常不会对此感兴趣,因为在我们日复一日的 Javascript 时间里,其中 99% 的时间我们都不需要了解属性赋值的这一低层级细节信息。
不过在这里它可以给我们明显的好处:通过描述符去创建的属性默认是只读的。
这意味着那些一时失了智的开发者(或者就是你自己)不会有机会干出一些类似下面这个组件中那样的蠢事,进而搞坏整锅粥。
this.$http = 'Assign some random thing to the instance method';
this.$http.get('/'); // TypeError: this.$http.get is not a function
相对的,我们的只读实例方法保护了我们的类库不被修改,假如你尝试去重写它,你就只会得到一个“TypeError: Cannot assign to read only property”。
你应该会注意到代理我们的类库到一个属性上的时候,属性名前补充了一个美元符号“$”的前缀。你也大概已经见过其他一些属性或者方法比如 $refs
,$on
,$mount
等等也都有同样的前缀。
虽然不是必须的,这个前缀加到这些属性上也是为了提醒那些失了智的开发者(不用看,还是你),这是一个公共 API 属性或者方法,欢迎使用,不像其他的实例属性可能只是给 Vue 的内部使用。
作为一个基于原型链的语言,Javascript 没有(真正意义上的)类,因此也没有所谓的 “私有” 和 “公共” 变量或者是 “静态” 方法。
而这种($ 符号)约定俗成实际上是一种柔和的提醒,我认为这是值得去遵循的。
由于类库现在是一个实例方法,所以通过 this.libraryName
来使用类库不会是一件值得惊讶的事。
这样做的一个后果是,与使用全局变量不一样,你必须确保使用类库时处于一个正确的作用于中。 比如在内部回调函数中你就不能访问的到 this
上的类库。
对此,箭头回调函数会是一个不错的解决方案,它会确保你在正确的作用域中
this.$http.get('/').then(res => {
if (res.status !== 200) {
this.$http.get('/') // etc
// Only works in a fat arrow callback.
}
});
如果你计划在多个 Vue 项目中使用同一个类库,又或者你想要把它分享给全世界,那么你其实可以去构建一个属于你自己的插件。 一个插件可以把复杂的操作抽象出来,从而允许你通过如下面所展示,十分简单的方式去把你所选的类库添加到一个项目中。
import MyLibraryPlugin from 'my-library-plugin';
Vue.use(MyLibraryPlugin);
仅靠这样两行代码,我们就可以在任意的组件中使用我们的类库,就像我们使用 Vue Router,Vuex 又或者其他可以通过 Vue.use
使用的插件那样。
首先,为你的插件创建一个文件。在这个例子中我将会写一个把 Axios 添加到你所有的 Vue 示例和组件的插件,因此我将文件命名为 axios.js。
其中最需要搞清的事情是,插件需要暴露一个 install
方法,该方法中会将 Vue 的构造函数作为第一个参数。
axios.js
export default {
install: function(Vue) {
// Do stuff
}
}
现在我们可以使用我们私有的方法去把类库添加到原型对象:
axios.js
import axios from 'axios';
export default {
install: function(Vue,) {
Object.defineProperty(Vue.prototype, '$http', { value: axios });
}
}
对于添加类库到一个项目中作业这个目标来说,use
这个实例方法就是我们现在所需要知道的一切。 举个例子,要把 Axios 添加到项目中,我们只需要像下面那样操作,非常简单:
entry.js
import AxiosPlugin from './axios.js';
Vue.use(AxiosPlugin);
new Vue({
created() {
console.log(this.$http ? 'Axios works!' : 'Uh oh..');
}
})
你的插件的 install 方法还可以带上可选参数。一些开发者可能不会喜欢命名他们的 Axios 实例方法为 $http
,这是由于 Vue Resource 通常会使用这个名字,
那么,让我们通过可选参数来允许他们可以改用任何他们喜欢的名字吧:
axios.js
import axios from 'axios';
export default {
install: function(Vue, name = '$http') {
Object.defineProperty(Vue.prototype, name, { value: axios });
}
}
entry.js
import AxiosPlugin from './axios.js';
Vue.use(AxiosPlugin, '$axios');
new Vue({
created() {
console.log(this.$axios ? 'Axios works!' : 'Uh oh..');
}
})
往期精选文章 |
---|
怎样避免开发中的深坑? |
全栈工程师技能大全 |
配置一个简单实用的JavaScript开发环境 |
推翻JavaScript中的三座大山:作用域篇 |
掌握Chrome开发工具:新一代前端开发技术 |
WEB前端性能优化常见方法 |
在 Vue 中创建自定义输入 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
2017年前端开发技术栈 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。