Appearance
使用 vue-loader 在 html 中引入单页面组件
背景
在很多业务需求下,我们没办法使用工程化环境,例如架构已定的 electron项目、SSR 框架的视图中、一些没必要起脚手架的小项目、一些没办法起脚手架的老框架等。这时如果还想要编写 .vue
后缀名的单页面组件,就需要绕一些弯路来实现。
vue3-sfc-loader
官方示例
html
<html>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
<script>
const options = {
moduleCache: {
vue: Vue
},
async getFile(url) {
const res = await fetch(url);
if ( !res.ok )
throw Object.assign(new Error(res.statusText + ' ' + url), { res });
return {
getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
}
},
addStyle(textContent) {
const style = Object.assign(document.createElement('style'), { textContent });
const ref = document.head.getElementsByTagName('style')[0] || null;
document.head.insertBefore(style, ref);
},
}
const { loadModule } = window['vue3-sfc-loader'];
const app = Vue.createApp({
components: {
'my-component': Vue.defineAsyncComponent( () => loadModule('./myComponent.vue', options) )
},
template: '<my-component></my-component>'
});
app.mount('#app');
</script>
</body>
</html>
使用 Vue2 或 Vue3
封装 initApp 方法
js
/**
* 初始化 Vue 3 实例并挂载
* @param {*} el 挂载 Vue 实例的元素,id 选择器,以 # 开头
* @param {*} rootComponentPath 根组件路径,相对于 src 目录,以 ./ 开头
* @param {*} componentName 组件名称,默认 'app-page-vue'
*/
initApp: (el, rootComponentPath, componentName = 'app-page-vue') => {
const options = {
moduleCache: {
vue: Vue
},
async getFile(url) {
try {
const response = await fetch(url);
if (!response.ok)
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
return {
getContentData: (asBinary) => asBinary ? response.arrayBuffer() : response.text()
}
} catch (e) {
console.error(e);
throw e;
}
},
addStyle(textContent) {
const style = Object.assign(document.createElement('style'), { textContent });
const ref = document.head.getElementsByTagName('style')[0] || null;
document.head.insertBefore(style, ref);
},
handleModule: async function (type, getContentData, path, options) {
switch (type) {
case '.svg':
return getContentData(false);
}
},
log(type, ...args) {
console[type](...args);
}
}
// 创建 Vue 应用
const app = Vue.createApp({
template: `<${componentName} />`,
components: {
[componentName]: Vue.defineAsyncComponent(() => {
return loadModule(
rootComponentPath,
options
).catch(err => {
console.error('Failed to load component:', err);
throw err;
});
})
}
});
// 挂载应用
app.mount(el);
return app
}
调用
js
initApp('#myPage', './myPage.vue', 'my-page-vue')
浏览器版本问题
一些低版本浏览器(老 Electron、IE)中会遇到如下报错:
sh
TypeError: Cannot destructure property `resolveComponent` of 'undefined' or
此时需要下载源码,自己按需求来编译。