碎碎念
上一篇文章,我們介紹瞭如何構建一個 react 外掛,今天我們說說如何構建 vue 外掛
準備工作
由於與上一篇 react 外掛文章使用的是相同的結構,程式碼測試、持續整合及釋出 npm 包也都是一個套路,這裡就不再敖述。 下面主要說下不同的地方,let's start ?
- 開發依賴包
{
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"chai": "^4.2.0",
"coveralls": "^3.0.2",
"css-loader": "^1.0.0",
"jest": "^23.6.0",
"style-loader": "^0.23.1",
"vue": "^2.5.21",
"vue-loader": "^15.5.0", // 解析 SFC 檔案
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.5.21", // vue-loader 的同步依賴
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
},
}
複製程式碼
- webpack 配置
const path = require('path');
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
mode: "production", // 生產模式
entry: { // 入口
"YanProgress": path.resolve(__dirname, './src/index.js')
},
output: { // 出口
path: path.resolve(__dirname, './dist'),
filename: '[name].min.js',
publicPath: "./dist/",
libraryTarget: 'umd', // 按 UMD 模式打包
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// 模板編譯過程中,編譯器可以將某些特性轉換為 require 呼叫
transformAssetUrls: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href' // SVG
}
},
// 只命中src目錄裡的js檔案,加快 Webpack 搜尋速度
include: path.resolve(__dirname, "./src"),
},
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
],
// 只命中src目錄裡的js檔案,加快 Webpack 搜尋速度
include: path.resolve(__dirname, "./src/"),
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
}
]
},
plugins: [
// vue-loader **這個外掛是必須的!**它的職責是將你定義過的其它規則複製並應用到 .vue 檔案裡相應語言的塊。
new VueLoaderPlugin
],
resolve: { // 省略檔案字尾時,預設按下面的配置取
extensions: ['.js', '.vue'],
},
externals: { // 不把 vue 打包進去
vue: 'vue',
}
};
複製程式碼
編寫外掛
寫 vue 外掛稍微複雜一點 ?,根據官網的案例,我們需要提供一個包含 install 方法的物件或者一個函式(傳送門),供 Vue.use 呼叫註冊你的外掛
- 寫法一
import Component from './YanProgress.vue'; // 這個就是你平時寫的 SFC 元件
// 這裡要匯出一個包含 install 方法的物件
let plugin = { // 這裡要匯出一個 install 方法
install(Vue,options) {
// 這裡寫你的程式碼,你可以全域性註冊元件,也可以寫全域性指令,也可以擴充套件 Vue 的方法
// 1. 全域性元件
Vue.component('yan-progress',Component);
// 2. 全域性方法或屬性
Vue.myGlobalMethod = function () {
// 邏輯...
}
// 3. 全域性指令
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 邏輯...
}
})
// 4. 注入元件
Vue.mixin({
created: function () {
// 邏輯...
}
})
// 5. 新增例項方法
Vue.prototype.$myMethod = function (methodOptions) {
// 邏輯...
}
}
};
if (window && window.Vue) { // 如果是漸進式開發(script 引入簡單粗暴的開發方式),需要自動註冊你的外掛
window.Vue.use(plugin);
}
export default plugin;
複製程式碼
- 寫法二
import Component from './YanProgress.vue'; // 這個就是你平時寫的 SFC 元件
// 或者這裡也可以寫成函式
function plugin(Vue,options) {
// 這裡寫你的程式碼,你可以全域性註冊元件,也可以寫全域性指令,也可以擴充套件 Vue 的方法
Vue.component('yan-progress',Component);
}
};
if (window && window.Vue) { // 如果是漸進式開發(script 引入簡單粗暴的開發方式),需要自動註冊你的外掛
window.Vue.use(plugin);
}
export default plugin;
複製程式碼
這樣寫的原因是,下面原始碼伺候?
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) { // 在這裡哦,可以傳物件,也可以傳函式
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) { // 避免重複註冊外掛
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') { // 如果是帶有 install 方法的物件
plugin.install.apply(plugin, args) // 不改變外掛的 this(這裡的 this 還是指向外掛物件本身)
} else if (typeof plugin === 'function') { // 如果是函式
plugin.apply(null, args) // 不改變外掛的 this(這裡應該是指向window,在瀏覽器非嚴格模式下)
}
installedPlugins.push(plugin)
return this
}
}
複製程式碼
開源貢獻
擁抱開源,這樣才能讓社群,乃至行業發展更有動力,哎,似曾相識的趕腳,?
注:例如,你的 star 是對我最大的鼓勵,是支援我繼續開源的動力,?
- awesome-vue 社群 awesome-vue
- 其他社群,可以到
Github
探索
完結撒花?
? 再次歡迎大家一起和我搞 ji 由(Github)?
- 專案地址 github.com/Yangfan2016…
- 部落格原文 yangfan2016.github.io
- react-yan-progress github.com/Yangfan2016…
- vue-yan-progress github.com/Yangfan2016…