Skip to content

nbnatcom/Vue3-SFC-Loader-Demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vue3 SFC Loader Demo

这是一个使用 vue3-sfc-loader 动态加载 Vue 单文件组件的示例项目,无需构建步骤即可运行 Vue3 应用。

项目特点

  • 使用 Vue3 + Vue Router + Pinia 构建
  • 无需构建工具,直接在浏览器中运行
  • 动态加载 .vue 文件
  • 支持 ES6 模块导入语法
  • 包含登录和主页示例

项目结构

.
├── api/                 # API 模块
│   └── login.js         # 登录相关 API
├── router/              # 路由配置
│   └── index.js         # 路由定义和守卫
├── stores/              # Pinia 状态存储
│   └── auth.js          # 认证状态管理
├── utils/               # 工具函数
│   └── helpers.js       # 辅助函数
├── views/               # Vue 视图组件
│   ├── login/index.vue  # 登录页面
│   └── home/index.vue   # 主页
├── styles/              # 样式文件
│   └── main.css         # 全局样式
├── static/              # 静态资源
│   └── vue3-sfc-loader/ # vue3-sfc-loader 源码
├── index.html           # 主页面
├── main.js              # 应用入口
└── README.md            # 项目说明

运行方式

直接在浏览器中打开 index.html 即可运行项目。

默认登录账号密码:

  • 账号:admin
  • 密码:password

核心技术说明

vue3-sfc-loader 配置选项说明

vue3-sfc-loader 是本项目的核心组件,它允许我们在浏览器中直接加载和运行 .vue 单文件组件。以下是对配置选项的详细说明:

additionalBabelParserPlugins

  • 类型: babel_ParserPlugin[]
  • 说明: 额外的 Babel 解析器插件,用于支持特殊的 JavaScript 语法特性

additionalBabelPlugins

  • 类型: Record<string, any>
  • 说明: 额外的 Babel 插件,用于代码转换和处理

compiledCache

  • 类型: Cache
  • 说明: 编译缓存配置,用于缓存已编译的组件以提高性能

delimiters

  • 类型: [string, string]
  • 说明: 自定义文本插值分隔符,用于避免与服务端模板引擎冲突

devMode

  • 类型: boolean
  • 说明: 开发模式开关,启用后会提供更详细的错误信息和警告

getPathname

  • 类型: (path: string) => string
  • 说明: 路径处理函数,用于从路径中提取文件名

handleModule

  • 类型: ModuleHandler
  • 说明: 自定义模块处理器,用于处理特殊类型的模块(如 .css、.json 等)

isCustomElement

  • 类型: (tag: string) => boolean | undefined
  • 说明: 自定义元素识别函数,用于识别原生自定义元素

moduleCache

  • 类型: Record<ModuleCacheId, LoadingType<ModuleExport> | ModuleExport>
  • 说明: 模块缓存,用于存储已加载的模块,避免重复加载。这是实现模块导入的关键配置

pathResolve

  • 类型: PathResolve
  • 说明: 路径解析函数,用于解析模块的绝对路径

whitespace

  • 类型: "preserve" | "condense"
  • 说明: 空白字符处理策略,控制模板中空白字符的处理方式

addStyle

  • 类型: (style: string, scopeId: string) => void
  • 说明: 样式添加函数,用于将编译后的 CSS 样式添加到页面中

createCJSModule

  • 类型: (refPath: AbstractPath, source: string, options: Options) => Module
  • 说明: CommonJS 模块创建函数,用于创建 CommonJS 格式的模块

customBlockHandler

  • 类型: (block: CustomBlock, filename: AbstractPath, options: Options) => Promise<CustomBlockCallback>
  • 说明: 自定义块处理器,用于处理 .vue 文件中的自定义块

getFile

  • 类型: (path: AbstractPath) => Promise<File | ContentData>
  • 说明: 文件获取函数,用于获取指定路径的文件内容

getResource

  • 类型: (pathCx: PathContext, options: Options) => Resource
  • 说明: 资源获取函数,用于获取指定路径的资源

loadModule

  • 类型: (path: AbstractPath, options: Options) => Promise<{}>
  • 说明: 模块加载函数,用于加载指定路径的模块

log

  • 类型: (type: string, ...data: any[]) => void
  • 说明: 日志记录函数,用于输出编译过程中的日志信息

processStyles

  • 类型: (srcRaw: string, lang: string, filename: AbstractPath, options: Options) => Promise<string>
  • 说明: 样式处理函数,用于处理不同语言的样式代码

为什么使用 CJS 格式的 IIFE

本项目中的工具函数、API 模块和 Pinia store 都采用 CJS (CommonJS) 格式的 IIFE (Immediately Invoked Function Expression) 定义,主要原因如下:

1. 兼容性考虑

  • 浏览器原生支持:IIFE 是立即执行的函数表达式,可以直接在浏览器中运行,无需额外的模块加载器
  • vue3-sfc-loader 集成:vue3-sfc-loader 可以很好地与这种格式集成,通过 moduleCache 映射模块

2. 模块系统统一

  • CJS 格式:CommonJS 是 Node.js 使用的模块系统,具有良好的生态系统和工具支持
  • UMD 兼容性:通过 IIFE 包装,我们的模块既可以在浏览器中作为全局变量使用,也可以在 Node.js 环境中作为 CommonJS 模块使用

3. 作用域隔离

  • 避免全局污染:IIFE 创建了一个独立的作用域,防止变量泄漏到全局命名空间
  • 命名冲突避免:内部变量和函数不会与页面其他脚本产生冲突

4. 简化开发流程

  • 无需构建步骤:直接在浏览器中运行,无需 Webpack、Vite 等构建工具
  • 快速原型开发:适合快速开发和测试,降低项目初始化复杂度

示例格式:

// 使用 IIFE 包装确保作用域隔离
(function() {
    // 严格模式确保代码质量
    'use strict';
    
    // 模块实现
    var moduleName = {
        // 模块方法
        method: function() {
            // 方法实现
        }
    };
    
    // 多环境支持导出
    if (typeof module !== 'undefined' && module.exports) {
        // CommonJS/Node.js 环境
        module.exports = moduleName;
    } else {
        // 浏览器环境
        window.moduleName = moduleName;
    }
})();

Window 对象命名规范

在编写 JavaScript 模块时,必须注意避免重复定义 window 对象属性,以防止模块冲突:

  1. 唯一性原则:每个模块应使用唯一的名称挂载到 window 对象上
  2. 语义化命名:使用具有明确含义的名称,如 window.helpersUtilwindow.authStore
  3. 避免冲突:确保新模块的名称不与已存在的模块或浏览器内置对象冲突
  4. 命名规范:推荐使用驼峰命名法,保持命名风格一致

错误示例:

// 错误:重复定义同一个window属性
window.utils = moduleA;
window.utils = moduleB; // 覆盖了moduleA

正确示例:

// 正确:使用不同的属性名
window.helpersUtil = moduleA;
window.authStore = moduleB;

index.html 中的 JavaScript 加载时序

index.html 中 JavaScript 文件的加载顺序非常重要,必须遵循严格的依赖关系:

<!-- 1. 加载核心依赖库 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue-router@4/dist/vue-router.global.js"></script>
<script src="https://unpkg.com/pinia@2/dist/pinia.iife.js"></script>

<!-- 2. 加载vue3-sfc-loader -->
<script src="https://unpkg.com/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>

<!-- 3. 加载自定义模块 -->
<script src="./utils/helpers.js"></script>
<script src="./stores/auth.js"></script>
<script src="./router/index.js"></script>

<!-- 4. 启动应用 -->
<script src="./main.js"></script>

加载时序说明

  1. Vue 核心库优先:Vue 是整个应用的基础,必须首先加载
  2. Vue Router 和 Pinia:这些是 Vue 的插件,依赖于 Vue,需要在 Vue 之后加载
  3. vue3-sfc-loader:这是动态加载 .vue 组件的核心工具,依赖于 Vue
  4. 自定义模块:工具函数、状态管理和路由配置等模块,它们可能依赖于前面加载的库
  5. 应用入口:main.js 是应用的启动文件,需要在所有依赖加载完成后执行

这种加载顺序确保了:

  • 每个模块在使用时其依赖已经存在
  • 避免因依赖缺失导致的运行时错误
  • 正确初始化全局对象和模块引用

moduleCache 用法

moduleCache 是 vue3-sfc-loader 的核心配置之一,用于映射模块标识符到实际模块对象:

// router/index.js
const options = {
    moduleCache: {
        vue: Vue,
        'vue-router': VueRouter,
        pinia: Pinia,
        'utils/helpers': window.helpersUtil,
        'stores/auth': window.authStore,
        'api/login': window.loginApi,
    }
};

这样在 .vue 组件中就可以使用标准的 ES6 导入语法:

<script setup>
import { getToken } from 'utils/helpers';
import { login } from 'api/login';
</script>

不使用 moduleCache 的情况

如果不使用 moduleCache,也可以直接使用相对路径导入:

<script setup>
import { login } from '../../api/login.js';
import { setToken, getToken } from '../../utils/helpers.js';
</script>

两种方式的差别:

  1. 路径处理:使用 moduleCache 可以使用简短的模块标识符,而相对路径需要根据文件位置调整
  2. 模块映射:moduleCache 提供了更清晰的模块映射关系,便于维护
  3. 可读性:moduleCache 方式更接近传统构建工具的使用体验

在 .vue 组件中使用 router、pinia、util

路由 (Router)

<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

// 导航
router.push('/home')

// 访问路由参数
console.log(route.params)
</script>

状态管理 (Pinia)

<script setup>
import { useAuthStore } from 'stores/auth'

const authStore = useAuthStore()

// 访问状态
console.log(authStore.user)

// 调用 actions
authStore.setUser(userInfo)

// 使用 getters
console.log(authStore.displayName)
</script>

工具函数 (Utils)

<script setup>
import { getToken, setToken } from 'utils/helpers'

// 使用工具函数
const token = getToken()
setToken(newToken)
</script>

在组件外使用 router、pinia、util

在组件外部(如路由守卫、工具函数等),我们需要通过 window 对象来访问 router 和 pinia,原因如下:

1. 执行时机问题

  • 组件外的代码(如路由守卫)在 Vue 应用实例创建之前就已经执行
  • 此时通过 ES6 导入的模块还无法访问到 Vue 应用上下文中的 router 和 pinia 实例
  • 通过 window 对象可以确保在应用初始化后访问到这些实例

2. 依赖注入机制

  • 在 index.html 中,通过 <script> 标签加载的模块会自动挂载到 window 对象上
  • 每个模块在 IIFE 中都会自动将自身导出到 window 对象,例如 window.helpersUtilwindow.authStore
  • 这样在任何地方都可以通过 window 对象访问这些全局模块

3. 模块加载顺序

  • Vue 组件中的 ES6 导入在组件加载时解析
  • 组件外的代码需要确保在相关模块加载完成后再访问
  • 通过 window 对象可以避免模块加载顺序问题

模块间的关系

  1. index.html - 主页面,通过 <script> 标签加载所有依赖并自动挂载到 window 对象
  2. router/index.js - 路由配置,通过 moduleCache 映射模块
  3. stores/auth.js - 状态管理,导出 Pinia store
  4. utils/helpers.js - 工具函数集合
  5. api/login.js - API 接口封装
  6. views//*.vue** - Vue 组件,通过 ES6 导入使用其他模块

这种架构实现了模块间的解耦,同时通过 vue3-sfc-loader 和 moduleCache 提供了良好的开发体验。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published