因公司的官網功能遷移,從原來的 SPA 應用遷移至 Nuxt 的 SSR 應用,牽扯到第三方客服腳本的加載,對前端不熟悉的美工特來求助,所以編寫了一個 Vue 插件來救同事於水火之中。前端
const PluginName = 'LoadScriptPlugin'
const prefix = `[${PluginName}] - `
/** @typedef {{name: string, src: string, selector?: string, defer?: boolean, async?: boolean}} ScriptOption */
/**
* 建立並掛載腳本
* @param {ScriptOption} option 掛載腳本選項
* @returns {void}
*/
function createScript(option = {}) {
if (typeof option !== 'object' || option === null) {
option = {}
}
const scriptId = `${prefix}${name}`
const script = document.createElement('script')
const parentEl = document.querySelector(selector) || document.body
script.id = scriptId
script.src = option.src
script.defer = option.defer
script.async = option.async
parentEl.append(script)
}
複製代碼
上面代碼經過 createElement 建立了 script 標籤,並經過接收外部傳遞的參數來設置 script 的屬性實現掛載腳本的過程。vue
爲了方便後面卸載腳本,咱們還需記錄每次掛載腳本的元素及自身元素。添加以下代碼:markdown
/**
* 全部腳本信息
* @description
* parent: 父級元素
* self: 自身元素(腳本元素)
*
* @type {{parent: HTMLElement, self: HTMLElement}}
*/
const scriptMap = {}
function createScript(option = {}) {
// 尾部追加
+ scriptMap[scriptId] = {
+ parent: parentEl,
+ self: script
+ }
}
複製代碼
咱們上面經過 scriptMap 記錄了每次掛載腳本的父級元素和自身元素,下面咱們經過建立接收 name 標識的函數來銷燬腳本。app
/**
* 銷燬腳本
* @param {string} name 腳本名稱
* @returns {string}
*/
function destroyScript(name) {
const scriptId = `${prefix}${name}`
const scriptInfo = scriptMap[scriptId]
scriptInfo.parent.removeChild(scriptInfo.self)
delete scriptMap[scriptId]
}
複製代碼
上面咱們經將插件的基本功能實現完成,可是尚未看到咱們定義的插件在哪裏,接下來須要讓插件具象化,同時爲 Vue.use 暴露安裝接口。async
/**
* Vue 自動掛載三方腳本插件
* @example
* Vue.use(Plugin, {})
* // or
* Vue.use(Plugin, [{}])
*/
const Plugin = {
/**
* 插件名
*/
name: PluginName,
/**
* 安裝記錄
*/
installed: false,
/**
* 安裝行爲
* @param {Vue} Vue
* @param {ScriptOption|ScriptOption[]|null} options
* @returns {void}
*/
install(Vue, options) {
if (Plugin.installed || Vue.$isServer) return
if (options) {
options = Array.isArray(options) ? options : [options]
options.forEach((opt) => createScript(opt))
}
Plugin.installed = true
Vue.prototype.$createScript = createScript
Vue.prototype.$destroyScript = destroyScript
}
}
複製代碼
上面代碼定義咱們插件的基本信息和安裝接口,後面能夠經過默認導出便可使用 Vue.use(LoadScriptPlugin)
進行註冊,而且能夠接收 options 選項來在初始化時進行掛載操做。ide
經過爲 Vue 原型掛載咱們的腳本函數來方便在後續的業務中動態使用掛載和銷燬腳本功能。函數
到這裏咱們的插件基本已經實現完成,可是仍然存在使用隱患,由於咱們開發出來是爲其餘人員提供便利操做的,在不能完整了解的狀況下使用時可能會出現運行時報錯,因此須要對接收參數作嚴格校驗。ui
function log(...messages) {
console.log(prefix, ...messages)
}
function warn(...messages) {
console.warn(prefix, ...messages)
}
/**
* 建立並掛載腳本
* @param {ScriptOption} option 掛載腳本選項
* @returns {void}
*/
function createScript(option = {}) {
if (typeof option !== 'object' || option === null) {
option = {}
}
if (['', null, void 0].includes(option.src)) {
return warn('The src property of the option cannot be falsly value!')
}
if (['', null, void 0].includes(option.name)) {
return warn(
'The name property of the option cannot be falsly value! The name property will be used to identify the current script!'
)
}
const scriptId = getScriptId(option.name)
if (scriptId in scriptMap) {
return warn('Duplicate name attribute, please re-enter!')
}
...
log(`The ${name} script been created!`)
}
/**
* 銷燬腳本
* @param {string} name 腳本名稱
* @returns {string}
*/
function destroyScript(name) {
...
if (!(scriptId in scriptMap) || scriptInfo === undefined) {
return warn(`The script with name as ${name} does not exist!`)
}
...
log(`The ${name} script been destroyed!`)
}
複製代碼
import Vue from 'vue'
const PluginName = 'LoadScriptPlugin'
const prefix = `[${PluginName}] - `
/**
* 全部腳本信息
* @description
* parent: 父級元素
* self: 自身元素(腳本元素)
*
* @type {{parent: HTMLElement, self: HTMLElement}}
*/
const scriptMap = {}
function log(...messages) {
console.log(prefix, ...messages)
}
function warn(...messages) {
console.warn(prefix, ...messages)
}
/**
* 獲取須要掛載的父級元素
* @param {string} selector 選擇器
* @returns {HTMLElement}
*/
function getParentEl(selector) {
let el = null
if (selector) el = document.querySelector(selector)
return el || document.body
}
/**
* 獲取腳本惟一標識
* @param {string} name 腳本名稱
* @returns {string}
*/
function getScriptId(name) {
return `${prefix}${name}`
}
/** @typedef {{name: string, src: string, selector?: string, defer?: boolean, async?: boolean}} ScriptOption */
/**
* 建立並掛載腳本
* @param {ScriptOption} option 掛載腳本選項
* @returns {void}
*/
function createScript(option = {}) {
if (typeof option !== 'object' || option === null) {
option = {}
}
if (['', null, void 0].includes(option.src)) {
return warn('The src property of the option cannot be falsly value!')
}
if (['', null, void 0].includes(option.name)) {
return warn(
'The name property of the option cannot be falsly value! The name property will be used to identify the current script!'
)
}
const scriptId = getScriptId(option.name)
if (scriptId in scriptMap) {
return warn('Duplicate name attribute, please re-enter!')
}
const script = document.createElement('script')
const parentEl = getParentEl(option.selector)
script.id = scriptId
script.src = option.src
script.defer = option.defer
script.async = option.async
parentEl.append(script)
scriptMap[scriptId] = {
parent: parentEl,
self: script
}
log(`The ${name} script been created!`)
}
/**
* 銷燬腳本
* @param {string} name 腳本名稱
* @returns {string}
*/
function destroyScript(name) {
const scriptId = getScriptId(name)
const scriptInfo = scriptMap[scriptId]
if (!(scriptId in scriptMap) || scriptInfo === undefined) {
return warn(`The script with name as ${name} does not exist!`)
}
scriptInfo.parent.removeChild(scriptInfo.self)
delete scriptMap[scriptId]
log(`The ${name} script been destroyed!`)
}
/**
* Vue 自動掛載三方腳本插件
* @example
* Vue.use(Plugin, {})
* // or
* Vue.use(Plugin, [{}])
*/
const Plugin = {
/**
* 插件名
*/
name: PluginName,
/**
* 安裝記錄
*/
installed: false,
/**
* 安裝插件
* @param {Vue} Vue
* @param {ScriptOption|ScriptOption[]|null} options
* @returns {void}
*/
install(Vue, options) {
if (Plugin.installed || Vue.$isServer) return
if (options) {
options = Array.isArray(options) ? options : [options]
options.forEach((opt) => createScript(opt))
}
Plugin.installed = true
Vue.prototype.$createScript = createScript
Vue.prototype.$destroyScript = destroyScript
}
}
// export default Plugin // 導出插件入口
// Nuxt plugin
Vue.use(Plugin, [
/** 加載客服腳本 */
{
name: 'customService',
src: 'xxx
}
])
複製代碼
每個問題的解決,都爲我實現財富自由前進了一步!lua
初次寫文章,不喜勿噴,有問題歡迎評論區留言交流!url