原本想这个部分用两天就搞定了,结果还是剩下一小部分不得不放在今天,插件和过滤器(本来想和复用和插件(1)合并在一起的,但忘记了-_-)。
1 插件
插件通常用来为 Vue 添加全局功能,插件的功能范围没有严格的限制——一般有下面几种:
- 添加全局方法或者 DOM 属性,如:vue-custom-element
- 添加全局资源:指令/过滤器/过渡等,如 vue-touch
- 通过全局混入来添加一些组件选项,如 vue-router
- 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如vue-router
1.1 使用
通过全局方法 Vue.use() 使用插件,它需要在调用 new Vue() 启动应用之前完成:
// 调用 MyPlugin.install(Vue)
Vue.use(MyPlugin);
new Vue({
// ...组件选项
});
也可以传入一个可选的选项对象:
Vue.use(MyPlugin, { someOption: true });
Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。
Vue.js 官方提供的一些插件 (例如 vue-router) 在检测到 Vue 是可访问的全局变量时,会自动调用 Vue.use(),然而在像 CommonJS 这样的模块环境中,我们需要始终显式地调用 Vue.use():
// 用 Browserify 或 webpack 提供的 CommonJS 模块环境时
let Vue = require('vue');
let VueRouter = require('vue-router');
// 不要忘了调用此方法
Vue.use(VueRouter);
awesome-vue 集合了大量由社区贡献的插件和库。
1.2 使用范例
下面是一个比较基础的在 Node.js 当中使用 vue-router 的范例:
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const Home = { template: '<router-view></router-view>' };
const Default = { template: '<div>default</div>' };
const Foo = { template: '<div>foo</div>' };
const Bar = { template: '<div>bar</div>' };
const Baz = { template: '<div>baz</div>' };
const WithParams = { template: '<div>{{ $route.params.id }}</div>' };
const Foobar = { template: '<div>foobar</div>' };
const FooBar = { template: '<div>FooBar</div>' };
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/', component: Home,
children: [
{ path: '', component: Default },
{ path: 'foo', component: Foo },
{ path: 'bar', component: Bar },
{ path: 'baz', name: 'baz', component: Baz },
{ path: 'with-params/:id', component: WithParams },
// relative redirect to a sibling route
{ path: 'relative-redirect', redirect: 'foo' }
]
},
// absolute redirect
{ path: '/absolute-redirect', redirect: '/bar' },
// dynamic redirect, note that the target route 'to' is available for the redirect function
{ path: '/dynamic-redirect/:id?',
redirect: to => {
const { hash, params, query } = to;
if (query.to === 'foo') {
return { path: '/foo', query: null };
}
if (hash === '#baz') {
return { name: 'baz', hash: '' };
}
if (params.id) {
return '/with-params/:id';
} else {
return '/bar';
}
}
},
// named redirect
{ path: '/named-redirect', redirect: { name: 'baz' }},
// redirect with params
{ path: '/redirect-with-params/:id', redirect: '/with-params/:id' },
// redirect with caseSensitive
{ path: '/foobar', component: Foobar, caseSensitive: true },
// redirect with pathToRegexpOptions
{ path: '/FooBar', component: FooBar, pathToRegexpOptions: { sensitive: true }},
// catch all redirect
{ path: '*', redirect: '/' }
]
})
new Vue({
router,
template: `
<div id="app">
<h1>Redirect</h1>
<ul>
<li><router-link to="/relative-redirect">
/relative-redirect (redirects to /foo)
</router-link></li>
<li><router-link to="/relative-redirect?foo=bar">
/relative-redirect?foo=bar (redirects to /foo?foo=bar)
</router-link></li>
<li><router-link to="/absolute-redirect">
/absolute-redirect (redirects to /bar)
</router-link></li>
<li><router-link to="/dynamic-redirect">
/dynamic-redirect (redirects to /bar)
</router-link></li>
<li><router-link to="/dynamic-redirect/123">
/dynamic-redirect/123 (redirects to /with-params/123)
</router-link></li>
<li><router-link to="/dynamic-redirect?to=foo">
/dynamic-redirect?to=foo (redirects to /foo)
</router-link></li>
<li><router-link to="/dynamic-redirect#baz">
/dynamic-redirect#baz (redirects to /baz)
</router-link></li>
<li><router-link to="/named-redirect">
/named-redirect (redirects to /baz)
</router-link></li>
<li><router-link to="/redirect-with-params/123">
/redirect-with-params/123 (redirects to /with-params/123)
</router-link></li>
<li><router-link to="/foobar">
/foobar
</router-link></li>
<li><router-link to="/FooBar">
/FooBar
</router-link></li>
<li><router-link to="/not-found">
/not-found (redirects to /)
</router-link></li>
</ul>
<router-view class="view"></router-view>
</div>
`
}).$mount('#app')
当然,因为 vue-router 是一个很重要的插件,几乎可以说是 Vue.js 密不可分的一部分,所以后面会专门来看看它的,现在就仅仅是个演示。
1.3 开发
Vue.js 的插件应该暴露一个 install 方法,这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或 property
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
剩余的部分,靠的就是我们的想象力和对原生 JavaScript 以及 Vue 的熟悉程度了,这方面,可以参考一些简单的 Vue.js 插件源代码来看看。
这里我找到一个非常简单的插件:gocanto/lazy-vue(作者gocanto),用来实现图片懒加载的一个插件,其源代码如下(未做任何改动),分为两个 .js 文件:
image.js:
/**
* Export the directive core code.
*
* @type {Object}
*/
module.exports = {
bind: (el, binding) => {
let img = new Image();
img.src = binding.value;
img.onload = () => {
el.src = img.src;
fadeIn(el);
}
}
};
/**
* Apply a fade in effect to a given element.
*
* @param {Object} el
* @return {Void}
*/
function fadeIn(el)
{
el.style.opacity = 0;
el.style.display = "block";
(function fadeIn() {
let val = parseFloat(el.style.opacity);
if (! ((val += .1) > 1)) {
el.style.opacity = val;
setTimeout(fadeIn, 40);
}
})();
}
lazy.js:
/**
* The global directive.
* @author Gustavo Ocanto <gustavoocanto@gmail.com>
* @license https://github.com/gocanto/lazy-vue/blob/master/LICENSE
*/
import Vue from 'vue';
Vue.directive(
'lazy',
require('./image')
);
从上面的代码可以看得出来,如果我们需要开发插件,需要的仅仅是遵循官方的指引 + 足够的技术能力 + 自己的想法。
2 过滤器
Vue.js 允许我们自定义过滤器,可被用于一些常见的文本格式化。
过滤器可以用在两个地方:双花括号插值和 v-bind 表达式,过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号(|)指示:
<!-- 在双花括号中 -->
{{ 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 (value) {
if (!value) return '';
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
});
new Vue({
// ...
});
局部过滤器的优先级更高,所以当全局过滤器和局部过滤器重名时,会采用局部过滤器。
下面这个例子用到了 capitalize 过滤器:
<!DOCTYPE html>
<html>
<head>
<title>hello</title>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="app" class="demo">
<input type="text" v-model="message">
<p>{{ message | capitalize }}</p>
</div>
<script>
new Vue({
el: '#app',
data: function () {
return {
message: 'Michael'
}
},
filters: {
capitalize: function (value) {
if (!value) return '';
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
}
}
});
</script>
</body>
</html>
过滤器函数总接收表达式的值 (之前的操作链的结果) 作为第一个参数,在上面的例子中,capitalize 过滤器函数将会收到 message 的值作为第一个参数。
过滤器还可以串联:
{{ message | filterA | filterB }}
在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中;然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。
过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
这里,filterA 被定义为接收三个参数的过滤器函数,其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。
因为过滤器相对来说更简单,所以就不多做赘述了。
我们可以通过这三个章节看到 Vue 为了实现复用,开放了很多自定义的方法给开发者,这样在确保不丢失掉其易用性的前提下,也给了我们足够的开放性和自由,来对 Vue.js 进行自己理想当中的自定义,从而获得更强的能力,能更好地适应自己的项目。