├── readme.md ├── src ├── content │ ├── docs │ │ ├── vue │ │ │ ├── test │ │ │ │ └── index.mdx │ │ │ ├── typescript │ │ │ │ └── index.mdx │ │ │ ├── pinia │ │ │ │ └── index.mdx │ │ │ ├── others │ │ │ │ └── index.mdx │ │ │ ├── hello-world │ │ │ │ ├── eslint.mdx │ │ │ │ ├── scss.mdx │ │ │ │ ├── vite.mdx │ │ │ │ ├── hello-world.mdx │ │ │ │ └── structure.mdx │ │ │ ├── component │ │ │ │ ├── api.mdx │ │ │ │ ├── template │ │ │ │ │ ├── show-if-else.mdx │ │ │ │ │ ├── html.mdx │ │ │ │ │ ├── bind.mdx │ │ │ │ │ ├── event.mdx │ │ │ │ │ ├── for.mdx │ │ │ │ │ └── form.mdx │ │ │ │ ├── sfc.mdx │ │ │ │ ├── index.mdx │ │ │ │ └── style.mdx │ │ │ ├── func │ │ │ │ ├── lifecycle.mdx │ │ │ │ ├── methods.mdx │ │ │ │ └── watch.mdx │ │ │ ├── index.mdx │ │ │ ├── data │ │ │ │ ├── reactivity.mdx │ │ │ │ ├── data.mdx │ │ │ │ ├── computed.mdx │ │ │ │ └── props.mdx │ │ │ ├── app │ │ │ │ └── index.mdx │ │ │ ├── data-transfer │ │ │ │ ├── attrs.mdx │ │ │ │ ├── emit.mdx │ │ │ │ ├── provide-inject.mdx │ │ │ │ └── slot.mdx │ │ │ ├── ref │ │ │ │ └── index.mdx │ │ │ └── router │ │ │ │ └── index.mdx │ │ ├── react │ │ │ └── index.mdx │ │ ├── angular │ │ │ └── index.mdx │ │ ├── start │ │ │ ├── knowledge.mdx │ │ │ ├── env.mdx │ │ │ └── index.mdx │ │ ├── topic │ │ │ ├── component │ │ │ │ ├── style.mdx │ │ │ │ ├── func.mdx │ │ │ │ ├── data.mdx │ │ │ │ ├── index.mdx │ │ │ │ └── template.mdx │ │ │ ├── app │ │ │ │ └── index.mdx │ │ │ ├── ref │ │ │ │ └── index.mdx │ │ │ ├── hello-world │ │ │ │ └── index.mdx │ │ │ └── data-transfer │ │ │ │ └── index.mdx │ │ ├── coding-style-guides │ │ │ ├── index.mdx │ │ │ ├── common.mdx │ │ │ ├── javascript.mdx │ │ │ ├── html.mdx │ │ │ └── css.mdx │ │ └── index.mdx │ └── config.ts ├── assets │ └── all.jpg └── env.d.ts ├── tsconfig.json ├── .vscode ├── extensions.json └── launch.json ├── .gitignore ├── package.json ├── public └── favicon.svg ├── LICENSE └── astro.config.mjs /readme.md: -------------------------------------------------------------------------------- 1 | # Vue、React 和 Angular 一起学怎么样? 2 | 3 | Why not. 4 | -------------------------------------------------------------------------------- /src/content/docs/vue/test/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 测试 3 | --- -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict" 3 | } -------------------------------------------------------------------------------- /src/content/docs/react/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: React 3 | --- 4 | -------------------------------------------------------------------------------- /src/content/docs/angular/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Angular 3 | --- 4 | -------------------------------------------------------------------------------- /src/content/docs/vue/typescript/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: TypeScript 3 | --- 4 | -------------------------------------------------------------------------------- /src/assets/all.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LearnShare/learn-VRA/HEAD/src/assets/all.jpg -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /src/content/docs/vue/pinia/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pinia - 状态管理 3 | --- 4 | 5 | :::note 6 | Vuex 已经是过去式了,Pinia 是 Vue 当前推荐的状态管理库。 7 | ::: 8 | -------------------------------------------------------------------------------- /src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineCollection, 3 | } from 'astro:content'; 4 | import { 5 | docsSchema, 6 | } from '@astrojs/starlight/schema'; 7 | 8 | export const collections = { 9 | docs: defineCollection({ schema: docsSchema() }), 10 | }; 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/content/docs/vue/others/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 其他相关内容 3 | --- 4 | 5 | ## UI 组件库 6 | 7 | + [Element Plus](https://element-plus.org/zh-CN/) 8 | 9 | ## Jamestack 10 | 11 | + [VitePress](https://vitepress.dev/zh/) 12 | 13 | ## 开发框架 14 | 15 | + [Nuxt](https://nuxt.com/) 16 | -------------------------------------------------------------------------------- /src/content/docs/start/knowledge.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 开始之前 3 | sidebar: 4 | order: 1 5 | --- 6 | 7 | 在开始学习和练习之前,建议您先熟悉前端开发相关的知识,并具有良好的操作技能和学习环境。 8 | 9 | ## 环境和技能 10 | 11 | + 个人电脑:配置开发环境,保存代码和资料 12 | + 通畅的网络:获取信息,下载在线资源 13 | + 英文:阅读文档,查找资料 14 | 15 | ## 前端知识 16 | 17 | + HTML 18 | + CSS 19 | + JavaScript 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /src/content/docs/start/env.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 开发环境 3 | sidebar: 4 | order: 2 5 | --- 6 | 7 | ## 开发工具 8 | 9 | + Node.js 10 | + pnpm 11 | + Visual Studio Code 12 | + Google Chrome 13 | + Git、Github 和 Sourcetree 14 | 15 | ## 库和框架 16 | 17 | + Vue 18 | + React 19 | + Angular 20 | 21 | ## 其他 22 | 23 | + SCSS + dart-sass 24 | + TypeScript 25 | + ESLint 26 | -------------------------------------------------------------------------------- /src/content/docs/start/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 简介 3 | sidebar: 4 | order: 0 5 | --- 6 | 7 | ## 目标 8 | 9 | + 同时学习 Vue、React 和 Angular 10 | + 同步学习三者相同/相关的知识 11 | + 全面切换为 TypeScript 12 | + 融合以往的项目经验 13 | + 尽量涉及更多、更广的内容 14 | 15 | ## 成果记录 16 | 17 | 1. 将学习内容记录到文档中(编写为 Markdown 存放在本项目中) 18 | 2. 将练习代码保存下来(存放在 Github 中,按主题独立分支) 19 | 20 | ## 交流讨论 21 | 22 | + learnshare.hjq#gmail 23 | + [Discord: Learn-VRA](https://discord.gg/vzfFh54322) 24 | -------------------------------------------------------------------------------- /src/content/docs/topic/component/style.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 样式 3 | sidebar: 4 | order: 2 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 |
主题VueReactAngular
样式 20 | 样式 21 |
25 | -------------------------------------------------------------------------------- /src/content/docs/topic/app/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 应用配置和启动 3 | --- 4 | 5 | + 创建应用实例 6 | + 将根组件渲染到 HTML 中 7 | + 通过插件等模块扩展应用 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 |
主题VueReactAngular
应用配置和启动 22 | Vue 应用 23 |
27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "learn-vra", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "private": true, 6 | "scripts": { 7 | "dev": "astro dev", 8 | "start": "astro dev", 9 | "build": "astro check && astro build", 10 | "preview": "astro preview", 11 | "astro": "astro" 12 | }, 13 | "dependencies": { 14 | "@astrojs/starlight": "^0.24.2", 15 | "astro": "^4.10.2", 16 | "sharp": "^0.32.5", 17 | "@astrojs/check": "^0.7.0", 18 | "typescript": "^5.4.5" 19 | } 20 | } -------------------------------------------------------------------------------- /src/content/docs/vue/hello-world/eslint.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: ESLint 3 | --- 4 | 5 | 创建项目时应当已经选择并支持了 ESLint。 6 | 7 | ## 安装 Airbnb 规则 8 | 9 | ``` sh 10 | pnpm i -D eslint-config-airbnb-base 11 | ``` 12 | 13 | ## 更新配置文件 14 | 15 | ``` js title=".eslintrc.cjs" ins={2} {5-6} 16 | extends: [ 17 | 'airbnb-base', 18 | ], 19 | rules: { // 根据项目风格,设置合适的规则 20 | 'import/no-unresolved': 'off', 21 | 'import/no-extraneous-dependencies': 'off', 22 | }, 23 | ``` 24 | 25 | ## 运行 ESLint 26 | 27 | 运行 `pnpm run lint`,并根据运行结果修改代码中的问题,或调整 ESLint 规则。 28 | 29 | 可以运行 `pnpm run lint --fix` 自动修复部分问题。 30 | -------------------------------------------------------------------------------- /src/content/docs/coding-style-guides/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 概述 3 | sidebar: 4 | order: 0 5 | --- 6 | 7 | 代码风格(个人总结,仅供参考)。 8 | 9 | ## 目标 10 | 11 | 1. 统一代码风格,提高代码质量 12 | 2. 保证代码易于编写、阅读和维护 13 | 3. 自动化 lint、format 14 | 15 | ## 目录 16 | 17 | 参考资料: 18 | 19 | + [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 20 | + [Google Style Guides](http://google.github.io/styleguide/) 21 | 22 | 通用规则: 23 | 24 | + [通用规则](./common) 25 | + HTTP 和 API 26 | 27 | 工具: 28 | 29 | + [EditorConfig](./editor-config) 30 | + JSDoc 31 | + TSDoc 32 | + ESLint 33 | 34 | 按语言: 35 | 36 | + [HTML](./html) 37 | + [CSS](./css) 38 | + [JavaScript](./javascript) 39 | -------------------------------------------------------------------------------- /src/content/docs/topic/ref/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: ref 和 DOM 3 | --- 4 | 5 | 虽然在现代框架中应当尽量避免 DOM 操作,但 DOM 操作是前端开发中无法避免的问题。常见的 DOM 操作场景有: 6 | 7 | + 表单元素的焦点操作 8 | + video/audio 元素的播放控制 9 | + 使用 canvas 元素进行绘图 10 | + 与 jQuery 等 DOM 操作库混合使用 11 | 12 | Vue、React 和 Angular 均提供了引用 DOM 元素的功能,方便进行 DOM 操作。 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 |
主题VueReactAngular
ref 和 DOM 27 | 模板引用 28 |
32 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/content/docs/vue/component/api.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: API 风格 3 | --- 4 | 5 | Vue 当前版本提供了两种完全不同的 [API](https://cn.vuejs.org/api/) [风格](https://cn.vuejs.org/guide/introduction.html#api-styles): 6 | 7 | + 选项式 API 8 | + 组合式 API 9 | 10 | ## 选项式 (Options) API 11 | 12 | >自 Vue 诞生时就存在的 API 风格,当前版本(3.*)仍然支持,但选择组合式风格的用户会更多一些。 13 | 14 | 组件的数据和逻辑(甚至是模板)都是选项对象里的参数,它们会暴露在 `this`(指向当前组件实例) 上,可以在模板及函数内使用。 15 | 16 | 参考 [组件 - 组件配置](/vue/component/#组件配置)。 17 | 18 | :::note 19 | 本教程的前半部分仍然以选项式 API 编写,后半部分会完全切换为组合式风格。 20 | ::: 21 | 22 | ## 组合式 (Composition) API 23 | 24 | Vue 3 中推出的全新 API 风格([Vue 2.7 也支持了同样的 API](https://v2.cn.vuejs.org/v2/guide/migration-vue-2-7.html)) 25 | 26 | 参考 [组合式 API]。 27 | 28 | ## 选择 29 | -------------------------------------------------------------------------------- /src/content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vue、React 和 Angular 一起学怎么样? 3 | description: Get started building your docs site with Starlight. 4 | template: splash 5 | hero: 6 | tagline: Why not. 7 | image: 8 | file: ../../assets/all.jpg 9 | --- 10 | 11 | import { 12 | CardGrid, 13 | LinkCard, 14 | Icon, 15 | } from '@astrojs/starlight/components'; 16 | 17 | ## 开始学习 18 | 19 | 20 | 23 | 26 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /src/content/docs/topic/component/func.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 逻辑 3 | sidebar: 4 | order: 4 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 27 | 28 | 29 | 32 | 33 | 34 |
主题VueReactAngular
逻辑 20 | methods 21 |
25 | watch 26 |
30 | 生命周期 31 |
35 | -------------------------------------------------------------------------------- /src/content/docs/vue/hello-world/scss.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: SCSS 3 | --- 4 | 5 | import { 6 | Steps, 7 | } from '@astrojs/starlight/components'; 8 | 9 | Vite 已经内置了对 [CSS 预处理器](https://cn.vitejs.dev/guide/features.html#css-pre-processors)的支持,只需要安装相关的依赖即可: 10 | 11 | 12 | 1. 安装 [Dart Sass](https://www.npmjs.com/package/sass): 13 | 14 | ``` sh 15 | pnpm i -D sass 16 | ``` 17 | 18 | 2. 添加文件 `components/hello-world.scss`: 19 | 20 | ``` scss title="components/hello-world.scss" 21 | h1 { 22 | color: red; 23 | } 24 | ``` 25 | 26 | 3. 在 `hello-world.vue` 末尾添加下面的代码: 27 | 28 | ``` vue title="hello-world.vue" ins={1-4} 29 | 33 | ``` 34 | 35 | 4. 运行并检查页面,文字应当显示为**红色**。 36 | 37 | -------------------------------------------------------------------------------- /src/content/docs/vue/func/lifecycle.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 生命周期 3 | --- 4 | 5 | 参考内容: 6 | 7 | + [Vue Guide: 生命周期图示](https://v3.cn.vuejs.org/guide/instance.html#生命周期图示) 8 | + [Vue API: 生命周期钩子](https://v3.cn.vuejs.org/api/options-lifecycle-hooks.html) 9 | 10 | 生命周期指的是组件运行过程中的特殊时间点,如组件创建、挂载、重新渲染和卸载。 11 | 12 | ## 生命周期钩子 13 | 14 | 生命周期钩子指的是具有特定名称,且会在组件运行的特殊时间点自动执行的方法。 15 | 16 | ``` ts 17 | { 18 | name: 'DemoLifeCycle', 19 | 20 | created() { 21 | // 组件创建并完成初始化 22 | // 可以在这里执行数据初始化,或接口调用 23 | }, 24 | mounted() { 25 | // 组件已挂载,但子组件可能没有渲染完毕 26 | // 可以在这里使用 ref 进行 DOM 操作 27 | }, 28 | updated() { 29 | // 组件已更新,但子组件可能没有渲染完毕 30 | }, 31 | beforeUnmount() { 32 | // 组件卸载之前 33 | // 可以在这里清除定时器和 $watch 34 | }, 35 | errorCaptured(error, component, info) { 36 | // 内部组件发生错误 37 | }, 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /src/content/docs/topic/component/data.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 数据 3 | sidebar: 4 | order: 3 5 | --- 6 | 7 | 数据是组件的核心功能之一。数据具有响应性,它的变化会影响模板的渲染。 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 | 41 |
主题VueReactAngular
数据 22 | 数据的响应性 23 |
27 | data 28 |
32 | props 33 |
37 | computed 38 |
42 | -------------------------------------------------------------------------------- /src/content/docs/coding-style-guides/common.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 通用规则 3 | sidebar: 4 | order: 1 5 | --- 6 | 7 | ## 文件和路径 8 | 9 | :::note[DO] 10 | + 项目中的文件存放在同一个目录中 11 | + 使用小写字母和数字 12 | + 使用 `.` 或 `-` 作为单词的连接符 13 | + 路径中始终包含扩展名 14 | ::: 15 | 16 | :::danger[DON'T] 17 | + 使用本地绝对路径 18 | ::: 19 | 20 | ## 编码 21 | 22 | 使用 UTF-8(no BOM)。 23 | 24 | ## 缩进 25 | 26 | :::note[DO] 27 | + 使用两个或四个空格缩进 28 | ::: 29 | 30 | :::danger[DON'T] 31 | + 使用 `Tab` 缩进 32 | + 混合多种缩进方式 33 | ::: 34 | 35 | ## 每行字符数 36 | 37 | 每行尽量保持在 80 字符以内,不超过 100 字符。 38 | 39 | ## 行尾空白符 40 | 41 | 行尾不使用任何空白符。 42 | 43 | ## 换行 44 | 45 | + 以保证代码易读为**首要目标** 46 | + 通过缩进表现多行内容的关系 47 | + 使用 Unix [换行](https://zh.wikipedia.org/wiki/%E6%8F%9B%E8%A1%8C)符 LF `\n` 48 | 49 | ## 文件末尾空行 50 | 51 | 文件末尾保留一个空行。 52 | 53 | ## 文件行数 54 | 55 | 每个文件尽量保持在 500 行以内,不超过 1000 行。 56 | 57 | ## 注释 58 | 59 | + 在需要的地方添加注释 60 | + 编写简洁、明确的注释内容 61 | -------------------------------------------------------------------------------- /src/content/docs/vue/hello-world/vite.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vite 3 | --- 4 | 5 | [Vite](https://cn.vitejs.dev/) 是官方推荐的开发工具,它的特点有: 6 | 7 | + [广泛支持](https://github.com/vitejs/vite/tree/main/packages/create-vite) Vue、React 等框架和库 8 | + 基于 ESM,更快的冷启动和热更新 9 | 10 | :::caution 11 | Vue CLI 已经处于维护状态,不推荐在新项目中继续使用。 12 | ::: 13 | 14 | ## 创建项目 15 | 16 | ``` sh 17 | pnpm create vue 18 | 19 | # 或使用 Vite 创建 20 | # pnpm create vite 21 | ``` 22 | 23 | 根据提示,按需求选择项目配置: 24 | 25 | 1. 项目名称: `vra-vue` 26 | 2. 是否使用 TypeScript: `Yes` 27 | 3. 是否添加 JSX 支持: `No` 28 | 4. 是否添加 Vue Router: `Yes` 29 | 5. 是否使用 Pinia 作为状态管理库: `Yes` 30 | 6. 是否使用 Vitest 作为单元测试库: `Yes` 31 | 7. 选择一个端到端测试方案: `Playwrite` 32 | 8. 是否添加 Vue 开发工具: `Yes` 33 | 34 | ## 运行和预览 35 | 36 | ``` sh 37 | cd vra-vue 38 | pnpm install 39 | pnpm run dev 40 | ``` 41 | 42 | 在浏览器中访问 http://localhost:5173/ 即可。 43 | 44 | ## 项目配置文件 45 | 46 | 参考 [配置 Vite](https://cn.vitejs.dev/config/)。 47 | -------------------------------------------------------------------------------- /src/content/docs/topic/component/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 组件 3 | sidebar: 4 | order: 0 5 | --- 6 | 7 | 组件是可复用的模块。它们类似于 HTML 元素,一般包含了下面几个部分: 8 | 9 | + 模板: 包含 HTML 元素或自定义组件,最终渲染为 HTML 10 | + 样式: 最终编译为 CSS,这部分是可选的 11 | + 数据和逻辑: 处理组件数据和交互逻辑的代码 12 | 13 | 关于组件: 14 | 15 | + 组件是应用的基本组成单位。 16 | + 组件内可以包含其他组件,构成组件树。 17 | + 最先渲染到页面中的组件称为**根组件**。 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 | 41 | 44 | 45 | 46 |
主题VueReactAngular
组件 32 | 创建、注册和使用组件 33 |
37 | 单文件组件 38 |
42 | API 风格 43 |
47 | -------------------------------------------------------------------------------- /src/content/docs/vue/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vue 3 | --- 4 | 5 | 核心概念: 6 | 7 | + 开始 8 | + 使用方式 9 | + Vite 10 | + ESLint 11 | + sass 12 | + Hello World 13 | + 发布 14 | + Nuxt 15 | + VitePress 16 | + 应用 17 | + 组件 18 | + 根组件 19 | + 单文件组件 20 | + API 风格 21 | + 选项式 22 | + 组合式 23 | + 组件 24 | + 模板 25 | + HTML 和插值 26 | + 指令 v-* 27 | + 特殊属性 28 | + key 29 | + is 30 | + ref 31 | + 内置元素和组件 32 | + 属性绑定 33 | + 显示或隐藏元素 34 | + 循环 35 | + 事件 36 | + 修饰符 37 | + 表单和双向绑定 38 | + 修饰符 39 | + 样式 40 | + class 41 | + style 42 | + scoped 43 | + CSS Modules 44 | + 数据 45 | + 响应性 46 | + data 47 | + props 48 | + computed 49 | + 逻辑 50 | + methods 51 | + watch 52 | + 生命周期 53 | + ref 和 DOM 54 | + 跨组件数据传递 55 | + 组合式 API 56 | + setup() 57 | + ref()/reactive() 58 | + computed() 59 | + watch() 60 | + 生命周期 61 | + useAttrs() 62 | + 宏/define* 63 | + 复用 64 | + 相关库 65 | + UI 组件 66 | + 状态管理 67 | + 路由 68 | + 测试 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Hu Junqing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/content/docs/topic/hello-world/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello World 3 | --- 4 | 5 | + 了解和安装 cli 6 | + 创建和配置项目 7 | + 认识项目结构 8 | + 编写最小的 Hello World 9 | + 运行和查看 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 50 | 51 | 52 |
主题VueReactAngular
创建和运行项目 24 | Vite 25 |
项目结构和配置 30 | 项目结构 31 |
ESLint + Airbnb 36 | ESLint + Airbnb 37 |
SCSS + dart-sass 42 | SCSS + dart-sass 43 |
Hello World 48 | Hello World 49 |
53 | -------------------------------------------------------------------------------- /src/content/docs/topic/component/template.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 模板 3 | sidebar: 4 | order: 1 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 52 | 53 | 54 |
主题VueReactAngular
HTML 和插值 20 | HTML 和插值 21 |
属性绑定 26 | 属性绑定 27 |
显示或隐藏元素 32 | 显示或隐藏元素 33 |
循环 38 | 循环 39 |
事件 44 | 事件 45 |
表单 50 | 表单和双向绑定 51 |
55 | -------------------------------------------------------------------------------- /src/content/docs/vue/component/template/show-if-else.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 显示或隐藏元素 3 | --- 4 | 5 | Vue 提供了用于控制元素显示或隐藏的指令。 6 | 7 | ``` vue 8 | 27 | 28 | 40 | ``` 41 | 42 | 渲染出的 HTML: 43 | 44 | ``` html 45 |
46 | A 47 |
48 | B not exist 49 |
50 | D 51 |
52 | ``` 53 | 54 | ## v-show 55 | 56 | 从上面的例子可以看出,`v-show` 指令控制了元素是否包含行内样式 `display: none;`,从而控制元素是否显示在页面中。 57 | 58 | ## v-if/v-else-if/v-else 59 | 60 | 与 JavaScript 中的 if/else-if/else 一致,但它们控制元素是否渲染到 HTML 中。 61 | 62 | 当需要通过隐藏元素来控制用户的操作权限时,建议使用 v-if/v-else-if/v-else 从 HTML 中完全移除相应元素。 63 | -------------------------------------------------------------------------------- /src/content/docs/vue/component/sfc.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 单文件组件 3 | --- 4 | 5 | 单文件组件(Single-File Components, SFC)允许在一处编写模板、样式和脚本代码: 6 | 7 | ``` vue title="src/components/hello-world.vue" 8 | 11 | 12 | 20 | 21 | 26 | ``` 27 | 28 | 单文件组件的扩展名通常是 `.vue`,它最终会被编译为 JavaScript 代码,以及额外的 CSS 代码。 29 | 30 | 文件中可以包含三个部分: 31 | 32 | ``` vue 33 | 36 | 37 | 40 | 41 | 44 | ``` 45 | 46 | ## \ 47 | 48 | 脚本代码通常是 `JavaScript`,但也可以使用 `TypeScript`: 49 | 50 | ``` vue 51 | 54 | ``` 55 | 56 | 在使用其他脚本语言时,需要通过 `lang` 属性指定语言的代号。 57 | 58 | ## \ 59 | 60 | 关于样式部分,参考 [样式](./style.md)。 61 | 62 | ## 分离 JavaScript 和 CSS 63 | 64 | 如果模板、脚本和样式堆在一起会让你抓狂,或者一个文件中包含了几百上千行代码,也可以把 JavaScript 和 CSS 分离到单独的文件中: 65 | 66 | ``` vue 67 | 70 | 71 | 73 | 74 | 76 | ``` 77 | -------------------------------------------------------------------------------- /src/content/docs/vue/component/template/html.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: HTML 和插值 3 | --- 4 | 5 | 在 Vue 模板中,几乎可以编写一切符合语法的 HTML 内容。 6 | 7 | 除此之外,Vue 还提供了许多扩展语法。 8 | 9 | ## 插值 10 | 11 | 插值用于将文本内容添加到模板中。 12 | 13 | ``` vue 14 | 24 | 25 | 38 | ``` 39 | 40 | 渲染出的 HTML: 41 | 42 | ``` html 43 |

1. HTML

44 |

value from data

45 |

a + b = 3

46 |

value from data

47 |

<strong>html content</strong>

48 |

html content

49 | ``` 50 | 51 | 其中,`{{ msg }}` 将 data.msg 输出为文本内容,并插入到 HTML 中,`v-text="msg"` 的作用是完全一致的。 52 | 53 | 也可以在 `{{ }}` 中编写 JavaScript 语句,语句将完成执行,并输出文本内容。 54 | 55 | 但是无法将包含 HTML 代码的文本直接作为代码插入,它们最终也会输出为文本(`<>` 会被转换为字符实体)。 56 | 57 | ## `v-html` 指令 58 | 59 | 可以使用 `v-html` 指令将 HTML 代码插入到元素中,但需要特别注意这么做是否安全。 60 | 61 | :::note[概念] 62 | [**指令**](https://cn.vuejs.org/api/built-in-directives.html) 是 Vue 提供的特殊属性(attribute),用于在模板中实现诸多功能,通常以 `v-` 作为前缀。 63 | ::: 64 | -------------------------------------------------------------------------------- /src/content/docs/vue/hello-world/hello-world.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello World 3 | --- 4 | 5 | import { 6 | Steps, 7 | } from '@astrojs/starlight/components'; 8 | 9 | ## 编写最小化的 Hello World 10 | 11 | 在上一节完成的代码基础上进行修改: 12 | 13 | 14 | 1. 精简 `hello-world.vue` 15 | 16 | ``` vue title="src/components/hello-world.vue" 17 | 20 | 21 | 29 | ``` 30 | 31 | 2. 修改 `app.vue` 32 | 33 | ``` vue title="src/app.vue" 34 | 37 | 38 | 48 | ``` 49 | 50 | 3. 精简 `main.ts` 51 | 52 | ``` ts title="src/main.ts" 53 | import { createApp } from 'vue'; 54 | 55 | import App from './app.vue'; 56 | 57 | const app = createApp(App); 58 | 59 | app.mount('#app'); 60 | ``` 61 | 62 | 4. 删除不相关的文件和目录 63 | 64 | ``` 65 | public/ 66 | src/components/ (hello-world.vue 以外的文件) 67 | src/assets/ 68 | src/router/ 69 | src/store/ 70 | src/views/ 71 | ``` 72 | -------------------------------------------------------------------------------- /src/content/docs/topic/data-transfer/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 跨组件数据传递 3 | --- 4 | 5 | 前面已经了解了跨组件传递数据的几种方式: 6 | 7 | + `props`:可以从父组件向子组件传递各种类型的数据,甚至是方法和组件。 8 | + `$emit`:可以通过事件从子组件向父组件传递数据 9 | + `ref`:可以从父组件中访问子组件的数据或方法。 10 | 11 | 跨组件传递数据的场景有很多: 12 | 13 | + 父组件 \<=> 子组件 14 | + 祖先组件 \<=> 后代组件(跨越多层父子关系) 15 | + 组件 \<=> 兄弟组件(位于同一父组件内) 16 | + 组件树上的任意组件之间(拥有共同的祖先组件) 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 63 | 64 | 65 |
主题VueReactAngular
props 31 | props 32 |
attrs 37 | attrs 38 |
slot 43 | slot 44 |
自定义事件 49 | $emit 50 |
局部共享 55 | Provide/Inject 56 |
状态管理库 61 | Pinia 62 |
66 | -------------------------------------------------------------------------------- /src/content/docs/coding-style-guides/javascript.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: JavaScript 3 | --- 4 | 5 | ## 语法特性 6 | 7 | + 优先使用 TypeScript 编写代码 8 | + 优先使用最新的 ECMAScript 语法和特性 9 | 10 | ## 名称 11 | 12 | + 使用有意义的英文单词 13 | + 尽量避免使用数字、空格、连字符(如 `-`、`_`)、缩写、无法阅读和识别的组合(如 `danJia`)、非英文字符(如 `🎶`、`价格`)等 14 | + 变量和字段名称使用 'camelCase' 方式命名 15 | + 模块和类名称使用 'CamelCase' 方式命名 16 | 17 | ## 注释 18 | 19 | + 使用 [JSDoc](https://jsdoc.app/) 或 [TSDoc](https://tsdoc.org/) 语法编写注释内容 20 | + 编写尽量详细的注释 21 | 22 | ## 空格、换行、缩进和标点符号 23 | 24 | + 每行的首尾均不能是空格(以空格缩进的行首除外) 25 | + 关键词、运算符与变量、常量和字面量之间保留一个空格 26 | + 数组和对象 27 | + 尽量将数组和对象拆分为多行书写 28 | + 每行一个成员或属性,以 `,` 结尾,并根据层级向内缩进对应的数量 29 | + 每个 `[` 和 `{` 与之前的内容保持在同一行,在其后换行 30 | + 每个 `]` 和 `}` 在其前换行,并于之后的内容保持在同一行 31 | + 函数 32 | + 声明时,`function funcName() {}` 函数名称与 `()` 之间没有空格 33 | + 匿名函数不论是否有参数,都需要保留 `()` 34 | + 函数参数较多时,可以分为多行编写 35 | + 三目运算符使用换行和缩进区分两个分支 36 | + 连续调用和有多个判断条件时,可以使用缩进区分不同的操作对象和数据比较 37 | 38 | ## 变量、常量和作用域 39 | 40 | + 始终使用 `let` 声明变量,避免使用任何 `var` 41 | + 始终在声明变量时指定初始值 42 | + 仅能将变量赋值为同一类型的数据 43 | + 始终使用 `const` 声明常量 44 | + 每条语句仅能声明一个变量或常量 45 | + 仅在需要的作用域中声明和使用变量和常量 46 | + 尽量避免设置和使用全局变量或常量 47 | + 始终在作用域的顶部完成变量和常量的声明和初始化 48 | + 禁止在同一作用域内重复声明同名的变量或常量 49 | 50 | ## 数据计算和类型比较 51 | 52 | + 仅能计算和比较相同类型的数据 53 | + 多个数据的运算可以使用括号明确优先级 54 | + 禁止使用 `==` 比较数据是否相等 55 | + 比较语句应当是幂等的,禁止任何有副作用的操作 56 | 57 | ## 模块 58 | 59 | + 始终选择 ESM 方案 60 | + 优先导出和使用默认模块 61 | 62 | ## 错误处理 63 | 64 | + 始终编写错误处理逻辑 65 | + 对错误可以选择忽略、重试、备用方案,或者打印日志、提交给统计接口 66 | + 无法处理的错误可以继续抛出,交给其他相关逻辑处理 67 | 68 | ## 格式检查与格式化 69 | 70 | + 应当使用 [ESLint](https://eslint.org/) 进行代码格式检查 71 | + 选择合适且完善的 ESLint 规则 72 | + 尽量避免大规模的自动格式化 73 | -------------------------------------------------------------------------------- /src/content/docs/vue/func/methods.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: methods - 方法 3 | --- 4 | 5 | 在前面的例子中,我们已经体验了在 Vue 组件中声明和调用方法(或函数): 6 | 7 | ``` ts 8 | import { 9 | defineComponent, 10 | } from 'vue/dist/vue.esm-bundler'; 11 | 12 | const ToggleButton = defineComponent({ 13 | name: 'toggle-button', 14 | template: ` 15 | 17 | `, 18 | data() { 19 | return { 20 | checked: false, 21 | }; 22 | }, 23 | methods: { 24 | toggleChecked() { 25 | this.checked = !this.checked; 26 | }, 27 | }, 28 | }); 29 | 30 | export default ToggleButton; 31 | ``` 32 | 33 | ## 声明方法 34 | 35 | Vue 中的一般方法和事件处理方法都需要写在组件的 `methods` 选项中: 36 | 37 | ``` ts 38 | methods: { 39 | func1() { 40 | this.func2('arg'); 41 | }, 42 | func2(arg) { 43 | console.log(arg); 44 | }, 45 | toggleChecked() { 46 | this.checked = !this.checked; 47 | }, 48 | }, 49 | ``` 50 | 51 | + `methods` 中的方法会自动绑定到组件实例中,通过 `this.funcName()` 访问 52 | + 方法中的 `this` 也指向组件实例 53 | + Vue 不建议在 `methods` 中定义箭头函数 `func3 = () => {}`,这可能会影响 `this` 的指向 54 | 55 | ## 调用方法 56 | 57 | Vue 中调用/使用方法的方式有很多: 58 | 59 | + 作为文本插值 60 | 61 | ``` vue 62 | {{ formatTime(item.createdAt) }} 63 | ``` 64 | 65 | + 作为事件处理方法 66 | 67 | ``` vue 68 | 17 | `, 18 | data() { 19 | return { 20 | checked: false, 21 | }; 22 | }, 23 | methods: { 24 | toggleChecked() { 25 | this.checked = !this.checked; 26 | }, 27 | }, 28 | }); 29 | 30 | export default ToggleButton; 31 | ``` 32 | 33 | ## data 选项 34 | 35 | `data` 选项应当是一个方法,该方法返回一个数据对象: 36 | 37 | ``` ts 38 | data() { 39 | return { 40 | // 41 | }; 42 | } 43 | ``` 44 | 45 | ## 定义数据 46 | 47 | ``` ts 48 | data() { 49 | return { 50 | a: 1, 51 | b: 2, 52 | c: { 53 | x: '1', 54 | y: '2', 55 | }, 56 | d: ['A', 'B'], 57 | now: new Date(), 58 | }; 59 | } 60 | ``` 61 | 62 | **注意**:为了保证数据的响应性,需要在 `data` 选项中完成所有数据的定义。 63 | 64 | ## 访问和修改数据 65 | 66 | 在模板中访问时,可以直接直接使用各个属性 `key`: 67 | 68 | ``` vue 69 | 73 | ``` 74 | 75 | 在 JavaScript 逻辑中访问时,直接访问和修改 `this.key`: 76 | 77 | ``` ts 78 | { 79 | computed: { 80 | sum() { 81 | return this.a + this.b; 82 | }, 83 | }, 84 | methods: { 85 | addOne() { 86 | this.a += 1; 87 | }, 88 | addItem(item) { 89 | this.d.push(item); 90 | }, 91 | }, 92 | } 93 | ``` 94 | 95 | :::note 96 | `data` 选项中定义的数据仅能在当前组件的模板和逻辑中访问。可以进一步了解 [跨组件数据传递]。 97 | ::: 98 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | } from 'astro/config'; 4 | import starlight from '@astrojs/starlight'; 5 | 6 | // https://astro.build/config 7 | export default defineConfig({ 8 | integrations: [ 9 | starlight({ 10 | title: 'learn-VRA', 11 | social: { 12 | github: 'https://github.com/LearnShare/learn-VRA', 13 | }, 14 | sidebar: [ 15 | { 16 | label: '开始', 17 | collapsed: true, 18 | autogenerate: { 19 | directory: 'start', 20 | }, 21 | }, 22 | { 23 | label: '代码风格', 24 | collapsed: true, 25 | autogenerate: { 26 | directory: 'coding-style-guides', 27 | }, 28 | }, 29 | { 30 | label: '主题', 31 | collapsed: true, 32 | items: [ 33 | { 34 | label: '1. Hello World', 35 | link: '/topic/hello-world/', 36 | }, 37 | { 38 | label: '2. 应用配置和启动', 39 | link: '/topic/app/', 40 | }, 41 | { 42 | label: '3. 组件', 43 | autogenerate: { 44 | directory: '/topic/component/', 45 | }, 46 | }, 47 | { 48 | label: '4. ref 和 DOM', 49 | link: '/topic/ref/', 50 | }, 51 | { 52 | label: '5. 跨组件数据传递', 53 | link: '/topic/data-transfer/', 54 | }, 55 | ], 56 | }, 57 | { 58 | label: '库和框架', 59 | collapsed: true, 60 | items: [ 61 | { 62 | label: 'Vue', 63 | link: '/vue', 64 | }, 65 | ], 66 | }, 67 | ], 68 | }), 69 | ], 70 | }); 71 | -------------------------------------------------------------------------------- /src/content/docs/coding-style-guides/html.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: HTML 3 | sidebar: 4 | order: 2 5 | --- 6 | 7 | + https://www.w3.org/TR/html/ 8 | + https://html.spec.whatwg.org/ 9 | 10 | ## 语义化 11 | 12 | + 标签名、属性名和属性值的使用需要符合其语义 13 | + 使用有意义的 id 和 class 名称(英文单词为主),并尽量简短(避免使用 `ad` 等关键词,会被去广告插件屏蔽) 14 | 15 | ## 可访问性 16 | 17 | [TODO] 18 | 19 | ## DOCTYPE 20 | 21 | ``` html 22 | 23 | ``` 24 | 25 | ## lang 26 | 27 | 为 html 元素提供 lang 属性,表明当前内容的语言。 28 | 29 | ``` html 30 | 31 | ``` 32 | 33 | ## meta 34 | 35 | + charset="UTF-8" 36 | 37 | ``` html 38 | 39 | ``` 40 | 41 | ## 元素 42 | 43 | + 使用小写标签名 44 | + 编写成对标签的结束标签 45 | + 省略空元素的结束标记 `/` 46 | + 正确嵌套成对的标签 47 | + 减少不必要的元素和嵌套层级 48 | 49 | ``` html 50 |

51 | 52 | 53 | 54 |
    55 |
  • 1
  • 56 |
  • 2
  • 57 |
58 | ``` 59 | 60 | ## 换行和缩进 61 | 62 | + 每个块级元素、列表项和表格元素独占一行 63 | + 包含子元素的元素,其开始标签和结束标签均独占一行 64 | + 每级子元素缩进两个空格 65 | + 行内文本元素可以和其他文本放在同一行 66 | 67 | ``` html 68 |
69 |

title

70 |

content

71 |
    72 |
  • 1
  • 73 |
  • 2
  • 74 |
75 |
76 | 77 |

注意这里

78 | ``` 79 | 80 | ## 属性 81 | 82 | + 使用小写字母作为属性名 83 | + 使用双引号 `""` 包含属性值 84 | + 属性值关键词使用小写字母 85 | + 使用 `-` 连接属性名、id 和 class 名称中的单词 86 | 87 | ``` html 88 | 89 | 90 | 91 | ``` 92 | 93 | 当属性较多时,使用换行和缩进提高可读性。 94 | 95 | 1. 在开始标签名后换行 96 | 2. 使用四个空格缩进后续内容 97 | 3. 将开始标签的 `>` 放在最后一个属性之后 98 | 99 | ``` html 100 | 104 | ``` 105 | 106 | *注意*:不要在文本内容中使用换行。 107 | 108 | ## 超链接 109 | 110 | + 使用超链接实现页面和锚点跳转,而非其他元素的点击事件 111 | + 禁止阻止超链接的点击事件 112 | -------------------------------------------------------------------------------- /src/content/docs/vue/hello-world/structure.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 项目结构 3 | --- 4 | 5 | import { 6 | Steps, 7 | } from '@astrojs/starlight/components'; 8 | 9 | ## 项目结构 10 | 11 | 在[上一节](../vite/)中,我们创建并运行了 `vra-vue` 项目。 12 | 13 | 来看一下项目的结构: 14 | 15 | ``` 16 | vra-vue/ 17 | .vscode/ // vscode 相关配置 18 | .e2e/ // 端到端(Playwrite)相关配置和测试脚本 19 | node_modules/ 20 | public/ // 静态文件目录 21 | src/ 22 | assets/ // 图片及 CSS 资源 23 | components/ // 组件及测试代码 24 | router/ // Vue Router 路由配置 25 | store/ // Pinia 状态管理配置 26 | views/ // 页面 27 | App.vue // Vue 根组件 28 | main.js // 应用配置和启动代码 29 | .eslintrc.js // ESLint 配置 30 | .gitignore 31 | env.d.ts 32 | index.html // 页面入口 33 | package.json 34 | playwrite.config.ts // Playwrite 配置文件 35 | pnpm-lock.yaml 36 | README.md 37 | tsconfig*.json // TypeScript 配置文件 38 | vite.config.ts // Vite 配置文件 39 | vitest.config.ts // Vitest 配置文件 40 | ``` 41 | 42 | ## 路径别名 43 | 44 | Vite 已经提供了名为 `@` 的路径别名,并将它映射到 `src` 路径: 45 | 46 | ``` ts title="vite.config.ts" 47 | alias: { 48 | '@': fileURLToPath(new URL('./src', import.meta.url)), 49 | }, 50 | ``` 51 | 52 | 因此,我们可以使用路径别名简化路径的书写,例如: 53 | 54 | ``` ts 55 | // 使用别名 56 | import Button from '@/components/button/index.vue'; 57 | ``` 58 | 59 | ## 代码风格调整 60 | 61 | 为了符合 [代码风格](/coding-style-guides/) 的要求,进行如下调整: 62 | 63 | 64 | 1. 修改文件名及相关代码 65 | 66 | ``` 67 | 1. 路径及文件名使用小写字母和 `-` 68 | 2. 省略路径和文件名重复的部分(如 `views/HomeView.vue` -> `views/home.vue`) 69 | ``` 70 | 71 | 2. 在根目录创建 `.editorconfig` 文件 72 | 73 | ``` editorconfig title=".editorconfig" 74 | root = true 75 | 76 | [*.{js,ts,vue,html,css,scss}] 77 | charset = utf-8 78 | indent_style = space 79 | indent_size = 2 80 | tab_width = 2 81 | end_of_line = lf 82 | trim_trailing_whitespace = true 83 | insert_final_newline = true 84 | max_line_length = 80 85 | ``` 86 | 87 | -------------------------------------------------------------------------------- /src/content/docs/vue/app/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vue 应用 3 | --- 4 | 5 | 回顾一下 Hello World 的代码: 6 | 7 | ``` html title="index.html" 8 |
9 | ``` 10 | 11 | ``` vue title="src/app.vue" 12 | 15 | 16 | 26 | ``` 27 | 28 | ``` ts title="src/main.ts" "createApp" "mount" 29 | import { createApp } from 'vue'; 30 | 31 | import App from './app.vue'; 32 | 33 | const app = createApp(App); 34 | 35 | app.mount('#app'); 36 | ``` 37 | 38 | ## createApp() - 创建应用实例 39 | 40 | 参考 [Vue API: createApp](https://cn.vuejs.org/api/application.html#createapp)。 41 | 42 | ``` ts 43 | const app = createApp(rootComponent, rootProps?); 44 | ``` 45 | 46 | 参数: 47 | 48 | + `rootComponent`: 需要渲染的根组件 49 | + `rootProps`: 传给根组件的 props 参数(可选) 50 | 51 | 返回值为 Vue 应用实例,可以进行后续操作: 52 | 53 | ``` ts 54 | import { createApp } from 'vue'; 55 | import root from './app.vue'; 56 | 57 | const app = createApp(root); 58 | 59 | // 获取 Vue 的版本号 60 | app.version 61 | 62 | // 注册或检索全局组件 63 | app.component('toggle-button', { 64 | // 组件配置 65 | }); 66 | 67 | // 应用配置 68 | app.config.* 69 | 70 | // 注册或检索全局指令 71 | app.directive('datetime', { 72 | // 指令配置 73 | }); 74 | 75 | // 注册全局 mixin 76 | app.mixin({ 77 | // mixin 配置 78 | }); 79 | // 建议使用组合式函数 80 | 81 | // 向后代共享课注入的数据 82 | app.provide('key', value); 83 | // 参考 Provide/Inject 84 | ``` 85 | 86 | ## mount() - 将根元素挂载到容器元素中 87 | 88 | ``` ts 89 | app.mount(rootContainer); 90 | ``` 91 | 92 | 参数: 93 | 94 | + `rootContainer`: DOM 元素或 CSS 选择器 95 | 96 | ## use() - 使用插件 97 | 98 | ``` ts 99 | app.use(plugin, ...options?) 100 | ``` 101 | 102 | 插件的类型很广泛,通常有: 103 | 104 | + 添加全局方法或属性 105 | + 添加全局指令、mixin 106 | + 添加全局实例方法 107 | + 路由、UI 组件等第三方库 108 | -------------------------------------------------------------------------------- /src/content/docs/vue/component/template/bind.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 属性绑定 3 | --- 4 | 5 | 虽然可以在模板中使用 `{{ }}` 语法使用数据变量或 JavaScript 表达式,但该语法并不能在元素/组件属性中使用。 6 | 7 | 要完成这个操作,需要使用属性绑定指令。 8 | 9 | ## v-bind:attr (:attr) 10 | 11 | ``` vue 12 | 26 | 27 | 49 | ``` 50 | 51 | 渲染出的 HTML: 52 | 53 | ``` html 54 |

2. attr

55 | 56 | 57 | 58 |
59 | ``` 60 | 61 | `v-bind` 的语法: 62 | 63 | ``` 64 | // 绑定组件数据 65 | v-bind:href="imgUrl" 66 | v-bind:disabled="btnDisabled" 67 | 68 | // 绑定表达式 69 | v-bind:title="user.name" 70 | v-bind:tabindex="`item-${index}`" 71 | 72 | // 绑定动态属性 73 | v-bind:[key]="value" 74 | 75 | // 可以简写为 76 | :attr="value" 77 | :[key]="value" 78 | 79 | // 绑定 class 80 | :class="['button', 'primary']" 81 | :class="{ 82 | large: isLarge, 83 | }" 84 | :class="['button', { 85 | large: isLarge, 86 | }]" 87 | :class="btnClasses" 88 | 89 | // 绑定 style 90 | :style="{ 91 | width: '100px', 92 | fontSize: '16px', 93 | }" 94 | :style="styleObject" 95 | :style="[styleA, styleB]" 96 | ``` 97 | 98 | :::note 99 | + 如果绑定的值为 `null` 或 `undefined`,该属性将不会出现在渲染结果中。 100 | + 当在组件上绑定 class 属性时,绑定的值会和组件根元素的 class 属性合并。 101 | ::: 102 | -------------------------------------------------------------------------------- /src/content/docs/vue/component/template/event.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 事件 3 | --- 4 | 5 | Vue 提供了处理事件的 `v-on` 指令,以及创建和触发自定义事件的能力。 6 | 7 | ``` vue 8 | 16 | 17 | 27 | ``` 28 | 29 | ## v-on 30 | 31 | `v-on` 指令用于监听 DOM 事件或组件自定义事件。 32 | 33 | 类似于 `v-bind`,`v-on` 之后需要指定监听的事件名称,属性值可以是表达式、方法名称或方法调用语句: 34 | 35 | ``` 36 | // 直接执行表达式 37 | v-on:click="count += 1" 38 | 39 | // 调用事件处理方法 40 | v-on:input="eventListener" 41 | v-on:input="eventListener($event)" 42 | 43 | // 简写为 44 | @click="count += 1" 45 | @input="eventListener" 46 | ``` 47 | 48 | ## 事件处理方法 49 | 50 | 事件处理方法是定义在组件 `methods` 属性内的方法。 51 | 52 | 如果未在模板中指定参数,事件处理方法可以没有参数,或者一个默认参数(`$event`)。 53 | 54 | 也可以在模板中指定传给事件处理方法的参数,它们可以是: 55 | 56 | + `$event`: 代表了原始的 DOM 事件,或自定义事件的数据 57 | + 任意值 58 | + 任意变量 59 | 60 | ``` vue 61 | 76 | 77 | 100 | ``` 101 | 102 | ## 事件修饰符 103 | 104 | 事件修饰符为事件绑定提供了更多扩展功能或限定条件: 105 | 106 | ``` vue 107 | 108 | 111 | 112 | 113 | 116 | ``` 117 | 118 | 参考:[Vue 事件处理: 事件修饰符](https://cn.vuejs.org/guide/essentials/event-handling.html#event-modifiers) 119 | 120 | ## 自定义事件 121 | 122 | 在模板中,`v-on` 对 HTML 元素和自定义组件的语法和功能是基本一致的。 123 | 124 | 参考 [跨组件数据传递 - 自定义事件]()。 125 | -------------------------------------------------------------------------------- /src/content/docs/vue/data-transfer/attrs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 透传 Attribute 3 | --- 4 | 5 | 如果向组件传递一个属性,但它没有对应 props 或自定义事件,那该属性就是非 props 的 attribute。常见的例子有 `id` `class` 和 `style` 属性。 6 | 7 | ## Attribute 继承 8 | 9 | 如果组件返回单个根节点,非 props 的 attribute 属性将自动应用到根节点上。其中: 10 | 11 | + 传入的 `class` 和 `style` 值会和根节点已有的值合并。 12 | + 绑定的事件处理方法也会应用到根节点元素上。 13 | + 如果根节点已经绑定了对应的事件处理方法,两处都会被触发。 14 | 15 | ``` vue title="single-root.vue" 16 | 19 | 20 | 28 | ``` 29 | 30 | ``` vue title="demo-attrs.vue" 31 | 40 | 41 | 57 | 58 | ``` 59 | 60 | 渲染出的 HTML: 61 | 62 | ``` html 63 |
single-root-component
68 | ``` 69 | 70 | 可以看到: 71 | 72 | 1. `attrA` 没有渲染出来,因为它是 props 属性 73 | 2. `attrB` 渲染为 `attrb`,因为 HTML 属性名不区分大小写 74 | 3. 传入的 class 属性与根节点的 class 属性合并 75 | 4. 点击事件的目标是子组件的根节点 76 | 77 | ### $attrs 78 | 79 | 在组件中,可以使用 `this.$attrs` 访问传入的非 props attribute: 80 | 81 | ``` ts 82 | mounted() { 83 | console.log(this.$attrs); 84 | }, 85 | 86 | /* 87 | { 88 | attrB: "B", 89 | class: "y", 90 | id: "x", 91 | onClick: ƒ (), 92 | style: { 93 | color: 'red' 94 | } 95 | } 96 | */ 97 | ``` 98 | 99 | ### 禁用继承 100 | 101 | 可以在组件选项中关闭非 props attribute 的继承: 102 | 103 | ``` ts 104 | { 105 | inheritAttrs: false, 106 | } 107 | ``` 108 | 109 | ## 多根节点组件的处理方式 110 | 111 | 如果组件返回多个根节点,非 props 的 attribute 将不会自动应用,而且会收到类似下面的警告信息: 112 | 113 | >[Vue warn]: Extraneous non-props attributes (class) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. 114 | 115 | 这种情况下,需要手动将 `$attrs` 绑定到节点: 116 | 117 | ``` vue title="multiple-root.vue" 118 | 125 | ``` 126 | 127 | `v-bind="object"` 会将一个对象的所有属性都作为 attribute 应用到元素或组件上。 128 | -------------------------------------------------------------------------------- /src/content/docs/vue/data-transfer/emit.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 自定义事件 3 | --- 4 | 5 | 我们已经在 [事件](../../component/template/event) 一节了解过处理 DOM 事件的方法。 6 | 7 | Vue 也提供了注册和触发自定义事件的功能。 8 | 9 | ## emits 选项 10 | 11 | 在触发自定义事件之前,需要在组件的 `emits` 选项中注册所有事件的名称。 12 | 13 | ``` ts 14 | // 1. 仅注册名称 15 | emits: [ 16 | 'change', 17 | 'statusUpdated', 18 | ], 19 | 20 | // 2. 使用验证方法 21 | emits: { 22 | change: (data) => { 23 | if (data.id 24 | && data.value) { 25 | return true; 26 | } else { 27 | throw(new Error('invalid event data')); 28 | return false; 29 | } 30 | }, 31 | }, 32 | ``` 33 | 34 | 关于事件名称,建议在注册时使用 `eventName` 形式,绑定时使用 `event-name` 形式。 35 | 36 | ## $emit 37 | 38 | 组件内部提供了 `$emit` 方法,用于触发事件,并提供附加数据(附加数据是可选的)。 39 | 40 | ``` ts 41 | this.$emit('eventName', eventData); 42 | ``` 43 | 44 | 绑定事件处理方法的语法与原生事件基本一致,但 `$event` 不再是 DOM 事件,而是 `$emit` 中提供的 `eventData`。 45 | 46 | ``` vue title="toggle-button.vue" "change" 47 | 51 | 52 | 75 | ``` 76 | 77 | ``` vue title="app.vue" "@change" 78 | 80 | 81 | buttonOnChange(data) { 82 | console.log(data); // { checked: true } | { checked: false } 83 | }, 84 | 85 | ``` 86 | 87 | ## 双向绑定 88 | 89 | 当使用 `propName` 作为 props 属性名,且使用 `update:propName` 作为事件名时,该属性即可使用 `v-model` 实现数据的双向绑定。 90 | 91 | ``` vue title="volumn-bar.vue" "update:volumn" 92 | 101 | 102 | 123 | ``` 124 | 125 | ``` vue title="app.vue" "v-model:volumn" 126 | 128 | 129 | data() { 130 | return { 131 | value: 40, 132 | }; 133 | }, 134 | ``` 135 | 136 | 注意绑定的语法应当是 `v-model:propName="value"`。 137 | -------------------------------------------------------------------------------- /src/content/docs/vue/data/computed.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: computed 3 | --- 4 | 5 | `computed` 用于完成数据的自动跟踪和处理,如加减运算或列表过滤。 6 | 7 | ``` vue 8 | 33 | 34 | 95 | ``` 96 | 97 | ## computed 选项 98 | 99 | `computed` 是包含一个或多个方法的对象: 100 | 101 | ``` ts 102 | computed: { 103 | // functions 104 | }, 105 | ``` 106 | 107 | 其中的方法没有参数,但必须有返回值。方法中可以使用 `data` `props` 或其他 `computed` 的值,且方法会在值变化后自动重新执行。 108 | 109 | :::note 110 | `computed` 中的方法应当只包含数据处理逻辑,不应该包含任何副作用。当依赖的值一样时,执行的结果也应当一样。而且每次执行都不会改变其他数据,也不能执行接口调用等操作。 111 | ::: 112 | 113 | ## computed 值 114 | 115 | `computed` 中的方法可以像 `data` 和 `props` 一样作为值,在模板或 JavaScript 逻辑中使用。 116 | 117 | ## getter/setter 118 | 119 | `computed` 也可以写作 getter/setter 组合的值: 120 | 121 | ``` ts 122 | { 123 | data() { 124 | return { 125 | value: 1, 126 | }; 127 | }, 128 | computed: { 129 | double: { 130 | get() { 131 | return this.value * 2; 132 | }, 133 | set(value) { 134 | this.value = value / 2; 135 | }, 136 | }, 137 | }, 138 | } 139 | ``` 140 | -------------------------------------------------------------------------------- /src/content/docs/vue/func/watch.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: watch 3 | --- 4 | 5 | 参考内容: 6 | 7 | + [Vue API: watch](https://v3.cn.vuejs.org/api/options-data.html#watch) 8 | + [Vue API: $watch](https://v3.cn.vuejs.org/api/instance-methods.html#watch) 9 | 10 | Vue 提供了 [computed](../../data/computed),用于完成数据的自动跟踪和处理。但它是自动执行的,而且不能有副作用。 11 | 12 | `watch` 提供了监听数据变化和执行自定义操作的功能。 13 | 14 | ``` vue "number" 15 | 28 | 29 | 55 | ``` 56 | 57 | 在这个例子中,我们监听了 `number` 的变化。如果它的值 `>= 3`,就呈现红色的提示文字。 58 | 59 | ## watch - 定义数据监听 60 | 61 | `watch` 类似于 `methods` 选项,它是一个包含监听方法的对象。 62 | 63 | 可以监听 `data` `props` 和 `computed` 中的数据,当数据发生变化时执行对应的方法: 64 | 65 | ``` ts 66 | watch: { 67 | // 监听单个数据 68 | // newValue: 变化后的值 69 | // oldValue: 变化前的值 70 | key1(newValue, oldValue) { 71 | // 72 | }, 73 | // 监听对象属性 74 | 'key2.subKey'(newValue, oldValue) { 75 | // 76 | }, 77 | // 执行指定方法 78 | key3: funName, 79 | // 执行多个方法 80 | key4: [ 81 | funcName, 82 | (newValue, oldValue) { 83 | // 84 | }, 85 | ], 86 | // 更多配置选项 87 | key5: { 88 | handler(newValue, oldValue) { 89 | // 90 | }, 91 | // 是否深入检查对象属性或数组成员的变化 92 | deep: false, 93 | // 是否立即执行 94 | immediate: false, 95 | // 选择执行的时机 96 | // pre: 渲染前执行 97 | // post: 渲染后执行 98 | // sync: 同步执行 99 | flush: 'pre', 100 | }, 101 | }, 102 | ``` 103 | 104 | **注意**:默认情况下,`watch` 方法无法监听对象属性或数组成员的变化,需要启用 `deep` 选项。 105 | 106 | ## $watch() - 设置和取消监听 107 | 108 | Vue 提供了组件实例方法 `this.$watch()`,也可以用来定义数据监听方法。 109 | 110 | 与 `watch` 选项不同的是,`$watch()` 方法可以灵活地设置和取消监听。 111 | 112 | ``` ts 113 | // 监听单个数据 114 | this.$watch('key1', 115 | (newValue, oldValue) { 116 | // 117 | }, { 118 | // 配置选项 119 | }, 120 | ); 121 | // 监听对象属性 122 | this.$watch( 123 | () => this.key2.subKey, 124 | (newValue, oldValue) { 125 | // 126 | }, 127 | ); 128 | // 监听表达式 129 | this.$watch( 130 | () => this.key3 + this.key4, 131 | (newValue, oldValue) { 132 | // 133 | }, 134 | ); 135 | ``` 136 | 137 | `$watch()` 方法会返回一个的方法,可以用来取消这个监听: 138 | 139 | ``` ts 140 | const unwatch = this.$watch(); 141 | 142 | unwatch(); 143 | ``` 144 | 145 | ## 真的需要手动监听吗? 146 | 147 | 对于上面的例子,我们可以通过 `computed` 方法自动完成: 148 | 149 | ``` ts "number" 150 | export default { 151 | name: 'DemoFunc', 152 | data() { 153 | return { 154 | number: 0, 155 | }; 156 | }, 157 | computed: { 158 | alert() { 159 | return this.number >= 3; 160 | }, 161 | }, 162 | methods: { 163 | order(value) { 164 | if (value === -1 165 | && this.number <= 0) { 166 | return; 167 | } 168 | 169 | this.number += value; 170 | }, 171 | }, 172 | }; 173 | ``` 174 | 175 | 对于能够自动完成的数据处理逻辑,使用 `computed` 会更合适。`watch` 更适合执行一些副作用,如触发事件或调用接口。 176 | -------------------------------------------------------------------------------- /src/content/docs/vue/data-transfer/provide-inject.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Provide/Inject 3 | --- 4 | 5 | `props` 可以将数据从父组件传递给子组件,子组件也可以继续把数据传递给它的子组件,但这会导致组件彼此关联,每个组件也会包含重复的代码。 6 | 7 | 考虑如下的组件树: 8 | 9 | ``` 10 | music-player - 音乐播放器 11 | music-list - 播放列表 12 | music-item - 歌曲信息及状态 13 | item-index - 序号 14 | play-status - 播放状态 15 | liked-status - 收藏状态 16 | ... 17 | music-item 18 | ... 19 | music-info - 正在播放的音乐信息 20 | music-lyrics - 歌词 21 | player-control - 播放器控制 22 | ``` 23 | 24 | 如果要将正在播放和已收藏的歌曲信息在多个组件之间共享,将会需要在一层层组件间传递许多数据。 25 | 26 | Vue 提供了 Provide/Inject 功能,用于向组件树内的其他节点共享数据: 27 | 28 | ``` vue title="music-player.vue" "provide" 29 | 34 | 35 | 77 | ``` 78 | 79 | ``` vue title="music-list.vue" "inject" 80 | 85 | 86 | 92 | ``` 93 | 94 | ``` vue title="music-item.vue" "inject" 95 | 106 | 107 | 120 | ``` 121 | 122 | ## provide 123 | 124 | 组件的 `provide` 选项用于向组件树内的其他节点共享数据: 125 | 126 | ``` ts 127 | // 共享静态数据 128 | provide: { 129 | appName: 'MusicPlayer', 130 | }, 131 | 132 | // 共享组件内部数据(必须是返回对象的方法) 133 | provide() { 134 | return { 135 | // 来自 data 的数据 136 | musicList: this.list, 137 | current: this.current, 138 | // 方法 139 | play: this.play, 140 | toggleLiked: this.toggleLiked, 141 | }; 142 | }, 143 | ``` 144 | 145 | :::caution[注意] 146 | 1. 提供共享数据的组件并不知道哪些子组件使用了这些数据。 147 | 2. 默认情况下,数据是**非相应式的**。 148 | ::: 149 | 150 | ## inject 151 | 152 | 组件的 `inject` 选项用于注入某个根节点共享的数据: 153 | 154 | ``` ts 155 | inject: [ 156 | 'musicList', 157 | 'current', 158 | ], 159 | ``` 160 | 161 | 组件并不会知道注入的数据由哪个节点共享,只是不断向上层组件查找,直到找到共享的数据为止。 162 | 163 | 如果未找到共享的数据,会收到类似的警告: 164 | 165 | >[Vue warn]: injection "musicList" not found. 166 | 167 | ## 响应式的数据共享 168 | 169 | 由于 Provide/Inject 模式传递的数据并不是响应式的,所以在上面的例子中,双击列表播放其他曲目时,音乐列表中的“playing”状态并不会更新。 170 | 171 | 我们会在 [组合式 API] 部分学习响应式的数据共享。 172 | -------------------------------------------------------------------------------- /src/content/docs/vue/component/template/for.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 循环 3 | --- 4 | 5 | Vue 也提供了用于循环的指令,可以基于数组等数据重复渲染部分模板。 6 | 7 | ``` vue 8 | 30 | 31 | 66 | ``` 67 | 68 | 渲染出的 HTML: 69 | 70 | ``` html 71 |

4. for/key

72 |
    73 |
  • 1
  • 74 |
  • 2
  • 75 |
  • 3
  • 76 |
77 |
    78 |
  1. A
  2. 79 |
  3. B
  4. 80 |
  5. C
  6. 81 |
82 |
83 | x: X 84 |
85 | y: Y 86 |
87 | z: Z 88 | 89 |
90 | ``` 91 | 92 | ## v-for 93 | 94 | `v-for` 用于重复渲染部分模板内容,其数据可以是数组、对象、数值、文本或其他可迭代数据: 95 | 96 | ``` 97 | // 数值 98 | v-for="number in 3" // 1 2 3 99 | 100 | // 文本 101 | v-for="char in 'abcd'" // a b c d 102 | 103 | // 数组 104 | // item: 数组成员 105 | // index: 成员序号 106 | v-for="item in list" 107 | v-for="item of list" 108 | v-for="(item, index) in list" 109 | 110 | // 对象 111 | // value: 对象中的值 112 | // key: 对象中的 key 113 | // index: 循环的序号(由于浏览器引擎的差异,输出的顺序可能存在差异) 114 | v-for="value in dataObject" 115 | v-for="(value, key) in dataObject" 116 | v-for="(value, key, index) in dataObject" 117 | ``` 118 | 119 | `v-for` 中声明的局部变量可以在当前元素/组件,以及子元素/组件中使用: 120 | 121 | ``` vue 122 | 129 | ``` 130 | 131 | ## :key 132 | 133 | 为了让 Vue 有效地跟踪 `v-for` 渲染出的每个元素,需要为元素添加 `key` 属性: 134 | 135 | ``` vue 136 |
  • {{ x }}
  • 139 | 140 |
  • {{ item.label }}
  • 143 | ``` 144 | 145 | `key` 的值一般是数字或字符串,它可以来自: 146 | 147 | + 循环的值 148 | + 值的特殊字段,如 item.id 149 | + 循环的 key 150 | + 循环的序号 151 | 152 | :::note 153 | 通常应该避免使用循环序号,因为它可能会随着数组元素的增加、删除或排序发生变化,从而导致无法预料的逻辑错误。 154 | ::: 155 | 156 | ## \ 157 | 158 | 可以使用 `