我目前的公司通常在我们客户的网页中提供嵌入式应用程序。
注意:虽然下面的例子是用.ts写的,但在.js几乎是一样的。
在测试了各种方法(Vue 相当灵活)之后,我们最终做了以下工作:
该方法的灵感来自mapbox-gl的模型,适用于Vue。
基本原理很简单:将整个应用导出为干净的 JS 类(通过单个脚本导入)并运行
new SomeClass(selector, config);
为此,我通常会创建一个someClass.ts 文件(它通常会完成main.ts 的工作),但会导出一个类:
import Vue from 'vue';
import App from './App.vue';
import store from './store';
import i18n from '@/plugins/i18n';
import VA from 'vue-axios';
import axios from 'axios';
import store from './store';
...
Vue.use(VA, axios);
Vue.config.productionTip = false;
const Instance = new Vue({
store,
i18n,
components: { App },
data: () => ({ config: {} }),
render: function(createElement) {
return createElement('app', {
props: { config: this.config }
});
}
});
export default class SomeClass {
constructor(selector: string, config: SomeClassConfig) {
Vue.set(Instance, 'config', {
...config,
// id is optional. but allows setting the app's `id` dynamically to same `id`
// as the context page placeholder (which app replaces), for consistency
// for this, you have to assign `config.id` to App's `$el` in its `mounted()`
id: selector.replace('#', '')
// or document.querySelector(selector).id || 'someClassApp'
});
Instance.$mount(selector);
return Instance;
}
}
// optional exports, used for type inheritance throughout the app
export const { $http, $t, $store } = Instance;
注意:明确这一点:没有上述导入是必需的(即:axios、i18n 等... - 这只是一个例子)。做你的main.ts(或main.js)通常做的任何事情。然后创建Instance,注入配置,从Instance 导出您可能需要的类和任何命名导出。
上面已经完成了工作(如果您使用下面指定的build 命令构建它),但您还必须调整main.ts 以导入此类并在服务时呈现应用程序:
import SomeClass from '@/someClass';
export default new SomeClass('#devhook', {
// config object
});
const app = document.querySelector('#devhook');
if (app instanceof HTMLElement) {
app.addEventListener('some-event', function(e) {
if (e instanceof CustomEvent) {
console.log(e.detail);
// you can test events emitted for context app here...
}
});
}
而public/index.html 看起来像这样:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="./favicon.ico">
<!-- VueJS -->
<script src="https://cdn.jsdelivr.net/npm/vue@latest/dist/vue.min.js"></script>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="devhook"></div>
<!-- built files will be auto injected -->
</body>
</html>
构建命令是:
vue-cli-service build --target lib --name SomeClass --inline-css --inline-vue src/someClass.ts
请注意,我们正在导出 src/someClass.ts。您最终会得到一个名为 SomeClass.umd.js 的文件。
另外,将此设置添加到config.vue.js:
configureWebpack: {
output: {
libraryExport: 'default'
}
}
当您在上下文页面中导入它时,将使您能够在没有.default() 的情况下初始化该类。如果没有上面的 webpack 选项,你需要像这样初始化它:
new Someclass.default(selector, config);
其他一切都很标准。
而现在上下文页面只需要导入.umd.js,使用实例化app
new SomeClass(selector, config)
...并在呈现的元素上添加一个或多个侦听器。
.umd.js 导出可以从外部服务器提供服务,并且显然可以从 npm 包中导入。
如果单独加载它们更有意义(即:Vue 可能已经存在于上下文页面中),您不必--inline-css 和--inline-vue。另外,我经常依赖上下文页面中已经存在的库(您希望尽可能多地重用库),使用vue.config.js:
configureWebpack: {
externals: {
lodash: 'window._'
}
}
执行此操作时,请记住在 public/index.html 中添加指向 lodash/jquery/whatever cdn 的脚本,以便在服务/开发时加载它,就像在上下文页面中一样。
最后,demo.html(用于展示产品构建)如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Some Class App Demo</title>
<link rel="shortcut icon" href="./favicon.ico"/>
<!-- also works when served from a different domain -->
<script src="./SomeClass.umd.min.js"></script>
<link rel="stylesheet" href="./SomeClass.css">
</head>
<body>
<div id="SomeClassApp"></div>
<script>
const config = {
// whatever
};
const app = new SomeClass('#SomeClassApp', config);
if (app.$el instanceof HTMLElement) {
app.$el.addEventListener('some-event', function(e) {
if (e instanceof CustomEvent) {
console.log(e.detail);
}
});
}
</script>
</body>
当然,您必须在应用脚本 (./SomeClass.umd.min.js) 之前添加所有外部组件(如果您使用了任何组件)。不要defer他们。该应用程序希望它们在启动时加载到window。
至于冲突,你不应该有任何冲突。 Vue 在未成年人之间高度兼容,因此您可以简单地使用上下文页面的 Vue(而不是内联它)。此外,Vue 2 语法应该与 Vue 3 完全兼容(例如,您可以使用 Vue 3 运行 Vue 2 应用程序而无需对其进行任何更改 - 我们将拭目以待)。