好久没写文章,心血来潮发一篇
重点先放三遍
不用 Vuetify 也可以用 vuetify-loader 哦不用 Vuetify 也可以用 vuetify-loader 哦不用 Vuetify 也可以用 vuetify-loader 哦const vuetify = 'vue' + 'buetify'
现在已经有许多基于 Vue 的 UI 框架、元件库都提供方便易用且客製化的方案,
从 Vuetify 的命名可以看到是为了美化 Vue 而存在,美化範围不只限于 UI,也包含程式码和开发体验。
今天主要和大家分享 vuetify-loader
如何提升 SFC
开发体验。
如果你也思考过以下问题,你可以考虑使用 vuetify-loader
来少写一些程式码。
重複且频繁的在专案各处引入相同的元件
举例来说,专案中有一个 BaseButton
这个基本的按钮元件,当我在不同的元件中使用时会是这样:
<template> <base-button>Click me!</base-button></template>
import BaseButton from '@/components/BaseButton'export default { components: { BaseButton }}
这个作法很普通,但是当有十个页面时就需要引入 10 次 BaseButton
,并且加入到 components
物件中
随着这样的 UI 元件慢慢变多,一个页面就需要引入一堆基础元件,牺牲的就是前端工程师的开发体验,下面举个例:
PageB.vue<template> <base-button>Click me!</base-button> <base-tooltip></base-tooltip> <base-input></base-input> <base-alert></base-alert> <base-container> <base-layout> <base-flex> ... </base-flex> </base-layout> </base-container></template>
import BaseButton from '@/components/BaseButton'import BaseTooltip from '@/components/BaseTooltip'import BaseInput from '@/components/BaseInput'import BaseAlert from '@/components/BaseAlert'import BaseContainer from '@/components/BaseContainer'import BaseLayout from '@/components/BaseLayout'import BaseFlex from '@/components/BaseFlex'export default { components: { BaseButton, BaseTooltip, BaseInput, BaseAlert, BaseContainer, BaseLayout, BaseFlex }}
同样的道理,有十个以上的页面,每个页面都要引入这么多基础元件时,牺牲的就是前端工程师的开发体验。
这也衍生一个问题,当我要移除 BaseButton
这个元件时,不只要移除在 template 中的
<base-button>Click me!</base-button>
同时也要移除 script 中的
import BaseButton from '@/components/BaseButton'
components: { BaseButton }
如果我忘了移除 js 中的引用,
此时专案若有使用 Eslint,他可能会好心的提醒你:
The "BaseButton" component has been registered but not used.eslint(vue/no-unused-components)
万一不幸的事情发生了,我没有使用 Eslint,又忘了移除 script 中的引用,就「可能」会在无形中增加了专案的 bundle size。
看到这里有些人可能想到不错的解决办法
将共用元件注册为全域元件,避免不断重複引入元件
时常看到的解决範例:
main.jsimport Vue from 'vue'import BaseButton from '@/components/BaseButton'import BaseTooltip from '@/components/BaseTooltip'import BaseInput from '@/components/BaseInput'import BaseAlert from '@/components/BaseAlert'import BaseContainer from '@/components/BaseContainer'import BaseLayout from '@/components/BaseLayout'import BaseFlex from '@/components/BaseFlex'Vue.component('BaseButton', BaseButton)Vue.component('BaseTooltip', BaseTooltip)Vue.component('BaseInput', BaseInput)Vue.component('BaseAlert', BaseAlert)Vue.component('BaseContainer', BaseContainer)Vue.component('BaseLayout', BaseLayout)Vue.component('BaseFlex', BaseFlex)
这方法相比在各处不断地引用基础元件有更好的开发体验,也让开发专案时可以更多的专注在商业逻辑上
但是这并没有办法主动
避免弃用的元件在建置时不小心被打包的问题
如果 BaseButton
是一个被弃用的元件,我需要在专案中透过全域搜寻 BaseButton
或 base-button
,确认没有任何地方用到这个元件后,回到 main.js 移除以下两行代码:
import BaseButton from '@/components/BaseButton'Vue.component('BaseButton', BaseButton)
同样的问题是如果我忘了移除,那 BaseButton
元件就「肯定」会增加专案的 bundle size,并且不会有 Eslint 的提示。
那有没有方法或工具能同时兼顾开发体验、专注开发商业逻辑,又能避免弃用元件建置时被打包呢?
本文的重点 vuetify-loader
vuetify-loader 是一个 webpack 的套件,用于自动引用 Vuetify 的元件
可以参考 vuetify-loader 文件的 Automatic Imports。
很少有 UI 框架自带 tree shaking 功能的,这也是我喜欢 Vuetify 的原因之一
你可以在设置 webpack plugins new VuetifyLoaderPlugin
时,带入 match
函式,
所有的元件中的每一个 tag 会执行一次 match
函式,参数有:
<base-button>Click me!</base-button>
就会是 base-button
kebabTag : 将 originalTag 转为 kebab-casecamelTag : 将 originalTag 转为 PascalCasepath : SFC 的相对路径component : 该 tag 所在的那个元件被解析后的物件,主要包含 SFC 的三个区块 (template, script, style)可以根据这些参数组合出适合专案的自动引入元件功能
match
需要回传一个长度为 2 的阵列:
第一个元素会被插入在该元件的 components
属性中
第二个元素为自动引用该元件的路径
以下用 Vue CLI 3 建立的专案做为範例:
vue.config.js
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')module.exports = { configureWebpack: { plugins: [ new VuetifyLoaderPlugin({ match (originalTag, { kebabTag, camelTag, path, component }) { if (kebabTag.startsWith('base-')) { return [camelTag, `import ${camelTag} from '@/components/${camelTag}.vue'`] } } }) ] }}
这边解释一下上面这段程式码的效果
我用 BaseButton
做举例:
如果在 PageA.vue 中使用
<template> <BaseButton>Click me!</BaseButton> 或 <base-button>Click me!</base-button></template>
在 script 中不必
再写:
import BaseButton from '@/components/BaseButton'export default { components: { BaseButton }}
在每一个元件中,若有 tag 的使用为 BaseButton
或 base-button
皆符合 kebabTag.startsWith('base-')
的这个条件。
此时回传长度为 2 的阵列
return ['BaseButton', `import BaseButton from '@/components/BaseButton.vue'`]
vuetify-loader
接着自动在 PageA.vue
元件中,引用 BaseButton
当有天 BaseButton
被弃用时,我只需要直接移除 PageA 中的
<BaseButton>Click me!</BaseButton>
或
<base-button>Click me!</base-button>
我甚至不需要意识到 BaseButton
已经被弃用,也不会造成多余的引入
因为删掉 template 中的 tag,同时 vuetify-loader 也移除了对 BaseButton
元件的引入
注意:这个範例的 import 条件判断只有以 PascalCase 方式命名,且放在 @/components/ 底下的元件才会被正确引入,当然你也可以写适合自己专案的客製化条件
总结:
vuetify-loader
的 match
函式提供自动引入元件的功能,伴随两项优点:
最后,很重要说三次
不用 Vuetify 也可以用 vuetify-loader 哦不用 Vuetify 也可以用 vuetify-loader 哦不用 Vuetify 也可以用 vuetify-loader 哦你心动了吗? 快去使用看看 vuetify-loader
欢迎留言提问、补充或一起讨论,
如果我的分享对你有帮助,可以顺手按个讚,我会更有动力。