I am working on a translate google chrome extension using Vue 3 right now. I define the translate UI as a public independent component that could reuse in many places. This is part of the public component code looks like(the translate UI was defined in the template.html):
import '../fontello/css/selection-translator.css';
import './style.scss';
import { defineComponent } from 'vue';
import widgetMixin from './vue-st';
import chromeCall from 'chrome-call';
import locales from '../locales';
import template from './template.html';
// const request = require('superagent');
const translateLocales = [];
locales.forEach( locale => {
const {localeId} = locale;
if ( !localeId.includes( '-' ) || ( localeId === 'zh-CN' || localeId == 'zh-TW' || localeId == 'zh-HK' ) ) {
translateLocales.push( locale );
}
} );
const resolvedEmptyPromise = Promise.resolve() ,
noop = ()=> {};
export const BaseTranslation = defineComponent( {
template ,
data : ()=>({
access_token: '',
locales : translateLocales ,
showForm : false ,
query : {
text : '' ,
from : '' ,
to : '' ,
api : ''
} ,
result : {
error : '' ,
phonetic : '' ,
dict : [] ,
result : [] ,
link : '',
}
}) ,
created() {
this.$options.client.on( 'disconnect' , ()=> {
alert("disconnect");
this.result = {
error : 'index连接到翻译引擎时发生了错误,请刷新网页或重启浏览器后再试。'
}
} );
} ,
computed : {
apiName() {
return {
YouDao: '有道翻译',
Google: '谷歌翻译',
GoogleCN: '谷歌翻译(国内)',
BaiDu: '百度翻译',
Reddwarf: '红矮星翻译'
}[this.query.api] || ''
}
},
methods : {
/**
* 翻译快捷键:Ctrl + Enter
* @param event
*/
ctrlEnter( event ) {
if ( event.ctrlKey ) {
this.safeTranslate();
}
} ,
/**
* 仅当有文本时才翻译
*/
safeTranslate() {
if ( this.query.text.trim() ) {
this.translate();
}
} ,
/**
* 从后台网页获取查询结果
* @returns {Promise}
*/
getResult() {
if ( this.$options.client.disconnected ) {
alert("disconnectedddd");
return resolvedEmptyPromise;
}
return this.$options.client
.send( 'get translate result' , this.query , true )
.then( resultObj => {
debugger;
if ("200" !==resultObj.response.statusCode||"200" !== resultObj.response.resultCode) {
let errMsg = {
NETWORK_ERROR: '网络错误,请检查你的网络设置。',
API_SERVER_ERROR: '接口返回了错误的数据,请稍候重试。',
UNSUPPORTED_LANG: '不支持的语种,请使用谷歌翻译重试。',
NETWORK_TIMEOUT: '查询超时:5 秒内没有查询到翻译结果,已中断查询。'
}[resultObj.code]
if (resultObj.error) {
errMsg += resultObj.error
}
this.result = {error: errMsg}
} else {
const {phonetic} = resultObj;
this.result = resultObj;
this.result.error = '';
this.result.phonetic = resultObj.response.result.translation;
}
} , noop );
// 只有在一种特殊情况下才会走进 catch 分支:
// 消息发送出去后但还没得到响应时就被后台断开了连接.
// 不过出现这种情况的可能性极低.
} ,
/**
* 交换源语种与目标语种
*/
exchangeLocale() {
const {to,from} = this.query;
this.query.to = from;
this.query.from = to;
} ,
/**
* 打开设置页
*/
openOptions() {
this.$options.client.send( 'open options' );
} ,
/**
* 复制文本
* @param {String|String[]} textOrTextArray
* @param {MouseEvent} event
*/
copy( textOrTextArray , event ) {
if ( Array.isArray( textOrTextArray ) ) {
textOrTextArray = textOrTextArray.join( 'n' );
}
this.$options.client.send( 'copy' , textOrTextArray );
const {target} = event ,
original = target.textContent;
target.textContent = '已复制';
setTimeout( ()=> target.textContent = original , 2000 );
} ,
/**
* 添加单词
* @param {String|String[]} textOrTextArray
* @param {MouseEvent} event
*/
addWord(text, event) {
chromeCall('storage.local.get', ['access_token'])
.then((res) => {
if (res.access_token) {
this.access_token = res.access_token;
this.queryWord(text, event);
} else {
alert('未绑定扇贝账号,请授权绑定')
this.gotoAccessToken();
}
});
},
/**
* 添加单词
* @param {String|String[]} textOrTextArray
* @param {MouseEvent} event
*/
addGlossary(text, event) {
chromeCall('storage.local.get', ['reddwarf_access_token'])
.then((res) => {
if (res.access_token) {
this.access_token = res.access_token;
this.queryWord(text, event);
} else {
alert('未绑定红矮星账号,请授权绑定')
this.gotoReddwarfAccessToken();
}
});
},
gotoAccessToken() {
chrome.runtime.sendMessage({ action: 'shanbay_authorize' })
},
gotoReddwarfAccessToken() {
chrome.runtime.sendMessage({ action: 'reddwarf_authorize' })
},
queryWord(text, event) {
let params = { word: text, access_token: this.access_token }
request.get('https://api.shanbay.com/bdc/search/')
.query(params)
.end((err, res) => {
switch (res.status) {
case 200:
let info = res.body
if (info.status_code == 0) {
this.realAddWord(info.data.id, event);
} else {
alert(`查词错误, ${info.msg}`)
}
break;
case 401:
alert('token 失效,请重新授权')
this.gotoAccessToken()
break;
case 429:
alert('今日请求次数过多')
break;
default:
alert(`未知错误, ${err}`)
break;
}
})
},
realAddWord(id, event) {
let params = { id: id, access_token: this.access_token }
request.post('https://api.shanbay.com/bdc/learning/')
.type('form')
.send(params)
.end((err, res) => {
switch (res.status) {
case 200:
let info = res.body
if (info.status_code == 0) {
const { target } = event;
let original = target.textContent;
target.textContent = '已添加';
setTimeout(() => target.textContent = original, 2000);
} else {
alert(`添加单词发生错误, ${info.msg}`)
}
break;
default:
alert(`添加单词发生错误, ${err}`)
break;
}
})
},
/**
* 播放语音
* @param {String|String[]} textOrTextArray
* @param {String} [lang] - 文本的语种
*/
play( textOrTextArray , lang ) {
if ( Array.isArray( textOrTextArray ) ) {
textOrTextArray = textOrTextArray.join( 'n' );
}
this.$options.client.send( 'play' , {
text : textOrTextArray ,
api : this.query.api ,
from : lang
} );
}
} ,
mixins : [ widgetMixin ]
} );
what I am trying to do is that reuse the component in the google chrome popup UI. when user click the extension, shows the public component that define the translation UI. this is the component that popup page would load when clicked:
import client from './client';
import getOptions from '../public/default-options';
import {read} from '../public/clipboard';
import { defineComponent,h } from 'vue';
import BaseTranslation from '../public/widget/index';
export default defineComponent( {
client ,
async compiled() {
this.inline = true;
this.showForm = true;
const {defaultApi , autoClipboard} = await getOptions( [ 'defaultApi' , 'autoClipboard' ] );
this.query.api = defaultApi;
if ( autoClipboard ) {
this.query.text = read();
this.safeTranslate();
}
} ,
ready() {
setTimeout( ()=> this.$els.textarea.focus() , 200 );
},
render() {
return BaseTranslation;
}
});
as you can see, I am tried to return the public BaseTranslation
component which was import from the base component I am previously defined. seems did not work, I did not know how to resue the public component right now. what should I do to return the public component. In Vue 2, it could use the Vue.extend
, I Vue 3, what is the right way to reuse this component in the render function?