16 Commits
88 changed files with 18369 additions and 65 deletions
+50
View File
@@ -0,0 +1,50 @@
name: GitHub Actions Build and Deploy to Tencent
on:
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
# using the `master` branch as the default branch.
push:
branches:
- tencent
permissions:
contents: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest #指定服务器的运行环境:最新版本ubuntu
steps:
# 使用actions/checkout@v4 库拉取代码到 ubuntu 上
- name: Checkout
uses: actions/checkout@v4
with:
# 根据网上资料查询此处可以设置为 false。https://github.com/actions/checkout
persist-credentials: false
# 安装 pnpm
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
# 设置node的版本
- name: Use Node.js
# 使用 actions/setup-node@v3 库安装 nodejswith 提供了一个参数 node-version 表示要安装的 nodejs 版本
uses: actions/setup-node@v3
with:
node-version: "18.x"
cache: "pnpm"
# 打包成静态文件
- name: Build
run: pnpm install && pnpm run docs:build
- name: upload file
uses: kostyaten/ssh-server-deploy@v4
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: 22
scp_source: "books/.vitepress/dist/*"
scp_target: ${{ secrets.TARGET }}
+2
View File
@@ -0,0 +1,2 @@
pnpm run changelog
git add CHANGELOG.md
+17
View File
@@ -0,0 +1,17 @@
{
"cSpell.words": [
"commitlint",
"leafer",
"linkify",
"nuxt",
"nuxtjs",
"pinia",
"preinstall",
"rushstack",
"Shiki",
"shikijs",
"tailwindcss",
"unplugin",
"vitesse"
]
}
+249
View File
@@ -0,0 +1,249 @@
# 1.0.0 (2024-09-25)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加gray-matter说明文档 ([0b4414e](https://github.com/CoderLambert/lz-space/commit/0b4414e8d041809eccf3ec58e8e1b5056e604ca9))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加nuxt demo ([37a4606](https://github.com/CoderLambert/lz-space/commit/37a4606a508a3cdd2b0f76a82dd2cc9cfe9c30b2))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
* add nuxt blog project ([bbbf99f](https://github.com/CoderLambert/lz-space/commit/bbbf99f41f89519f1a3fa4e27b8fec0ee2994a52))
* nodejs slash ([92fcc53](https://github.com/CoderLambert/lz-space/commit/92fcc53c12692efc274389670238d77a87ac79b0))
* vue中 markdown 使用 vue 组件,展示内容 ([c2db264](https://github.com/CoderLambert/lz-space/commit/c2db26458275b687635942f2f70c647262708a58))
# 1.0.0 (2024-09-25)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加gray-matter说明文档 ([0b4414e](https://github.com/CoderLambert/lz-space/commit/0b4414e8d041809eccf3ec58e8e1b5056e604ca9))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
* add nuxt blog project ([bbbf99f](https://github.com/CoderLambert/lz-space/commit/bbbf99f41f89519f1a3fa4e27b8fec0ee2994a52))
* nodejs slash ([92fcc53](https://github.com/CoderLambert/lz-space/commit/92fcc53c12692efc274389670238d77a87ac79b0))
* vue中 markdown 使用 vue 组件,展示内容 ([c2db264](https://github.com/CoderLambert/lz-space/commit/c2db26458275b687635942f2f70c647262708a58))
# 1.0.0 (2024-09-18)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加gray-matter说明文档 ([0b4414e](https://github.com/CoderLambert/lz-space/commit/0b4414e8d041809eccf3ec58e8e1b5056e604ca9))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
* nodejs slash ([92fcc53](https://github.com/CoderLambert/lz-space/commit/92fcc53c12692efc274389670238d77a87ac79b0))
* vue中 markdown 使用 vue 组件,展示内容 ([c2db264](https://github.com/CoderLambert/lz-space/commit/c2db26458275b687635942f2f70c647262708a58))
# 1.0.0 (2024-08-20)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加gray-matter说明文档 ([0b4414e](https://github.com/CoderLambert/lz-space/commit/0b4414e8d041809eccf3ec58e8e1b5056e604ca9))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
* nodejs slash ([92fcc53](https://github.com/CoderLambert/lz-space/commit/92fcc53c12692efc274389670238d77a87ac79b0))
# 1.0.0 (2024-08-20)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加gray-matter说明文档 ([0b4414e](https://github.com/CoderLambert/lz-space/commit/0b4414e8d041809eccf3ec58e8e1b5056e604ca9))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
* nodejs slash ([92fcc53](https://github.com/CoderLambert/lz-space/commit/92fcc53c12692efc274389670238d77a87ac79b0))
# 1.0.0 (2024-08-19)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加gray-matter说明文档 ([0b4414e](https://github.com/CoderLambert/lz-space/commit/0b4414e8d041809eccf3ec58e8e1b5056e604ca9))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
# 1.0.0 (2024-08-19)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
# 1.0.0 (2024-08-16)
### Bug Fixes
* 编译目录修改 ([aabca47](https://github.com/CoderLambert/lz-space/commit/aabca4793d7a4d50781244ae0fb9731fb46b3bce))
* 忽略 deadLink导致的编译错误 ([b216478](https://github.com/CoderLambert/lz-space/commit/b216478895734462e45f24ed737645f09f22c765))
* deploy 无权限问题 ([b87cb25](https://github.com/CoderLambert/lz-space/commit/b87cb2592367934622b04bd8f04ac434aab82d27))
* github page 样式展示异常 ([e006e9f](https://github.com/CoderLambert/lz-space/commit/e006e9f635bf135ba488073f13e1e010e2efc164))
### Features
* 30s代码片段链接 ([2af68a3](https://github.com/CoderLambert/lz-space/commit/2af68a3433788c263e8c8353ddd0236a831005db))
* 初始化 ([87d013a](https://github.com/CoderLambert/lz-space/commit/87d013ae27e21049346649a5bab434326c9d0d81))
* 初始化 ([fc33266](https://github.com/CoderLambert/lz-space/commit/fc332660805f61788051c8769a6925c6ec37784d))
* 添加 chokidar.md ([5eb5bba](https://github.com/CoderLambert/lz-space/commit/5eb5bbac8f572b7fb555683fd46bb92476335b38))
* 添加 execa 、semver 文档 ([8fe17b9](https://github.com/CoderLambert/lz-space/commit/8fe17b9a56eb13494876cef717198aec727fc6b2))
* 添加编译打包books命令 ([c6aa6a9](https://github.com/CoderLambert/lz-space/commit/c6aa6a9d8ed0c6e5600e7d04372061f972312a6f))
* 添加部署到腾讯工作流 ([0bbe75f](https://github.com/CoderLambert/lz-space/commit/0bbe75fe3e80a572b16f1fe728e7d3fe8ad692f9))
* 添加同步到个人服务器工作流步骤 ([7341322](https://github.com/CoderLambert/lz-space/commit/73413220b61aaf1f447100258bd336cbddc8ba62))
* 添加主页、笔记布局 ([bfcbb0f](https://github.com/CoderLambert/lz-space/commit/bfcbb0f614244632102f4f7fac04e3b626576326))
* 添加自动生成 changelog 命令 ([7089eba](https://github.com/CoderLambert/lz-space/commit/7089eba069200aef447b3da656a8c471d40c14eb))
* 添加husky钩子 ([be3152b](https://github.com/CoderLambert/lz-space/commit/be3152b74e734231e07823111748b1286e3c7ac3))
* 添加prompt 库使用介绍 ([8c40363](https://github.com/CoderLambert/lz-space/commit/8c40363c7ea0ac8c769cdf8b54e6321c935ad940))
* 修改工作流名称 ([58c5394](https://github.com/CoderLambert/lz-space/commit/58c53940415291439e262267afce3ca9afb77bcf))
+12 -12
View File
@@ -1,49 +1,49 @@
{
"hash": "3043a3a4",
"configHash": "33e5bd4c",
"lockfileHash": "5441c587",
"browserHash": "ca4cc7bc",
"hash": "057f71a2",
"configHash": "6e29879d",
"lockfileHash": "b4426dad",
"browserHash": "a3cde256",
"optimized": {
"vue": {
"src": "../../../../node_modules/.pnpm/vue@3.4.37_typescript@5.5.4/node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "010c3b5d",
"fileHash": "c704382e",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
"src": "../../../../node_modules/.pnpm/@vue+devtools-api@7.3.8/node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js",
"fileHash": "23d68367",
"fileHash": "744110df",
"needsInterop": false
},
"vitepress > @vueuse/core": {
"src": "../../../../node_modules/.pnpm/@vueuse+core@10.11.1_vue@3.4.37_typescript@5.5.4_/node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js",
"fileHash": "ac973a09",
"fileHash": "d3ec95e6",
"needsInterop": false
},
"vitepress > @vueuse/integrations/useFocusTrap": {
"src": "../../../../node_modules/.pnpm/@vueuse+integrations@10.11.1_focus-trap@7.5.4_vue@3.4.37_typescript@5.5.4_/node_modules/@vueuse/integrations/useFocusTrap.mjs",
"file": "vitepress___@vueuse_integrations_useFocusTrap.js",
"fileHash": "ce3e21ae",
"fileHash": "96e3540c",
"needsInterop": false
},
"vitepress > mark.js/src/vanilla.js": {
"src": "../../../../node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/vanilla.js",
"file": "vitepress___mark__js_src_vanilla__js.js",
"fileHash": "ea25cf40",
"fileHash": "b23411db",
"needsInterop": false
},
"vitepress > minisearch": {
"src": "../../../../node_modules/.pnpm/minisearch@7.1.0/node_modules/minisearch/dist/es/index.js",
"file": "vitepress___minisearch.js",
"fileHash": "f477f570",
"fileHash": "2b26e46a",
"needsInterop": false
}
},
"chunks": {
"chunk-Y4BZYF5F": {
"file": "chunk-Y4BZYF5F.js"
"chunk-ICHF43NU": {
"file": "chunk-ICHF43NU.js"
},
"chunk-NNKZBCFC": {
"file": "chunk-NNKZBCFC.js"
+1 -1
View File
@@ -273,7 +273,7 @@ import {
watchTriggerable,
watchWithFilter,
whenever
} from "./chunk-Y4BZYF5F.js";
} from "./chunk-ICHF43NU.js";
import "./chunk-NNKZBCFC.js";
export {
DefaultMagicKeysAliasMap,
+16
View File
@@ -0,0 +1,16 @@
// VitePress技术笔记左侧导航栏
export const gitNavItem = [
{
text: "git",
items: [
{
text: "git",
link: "/note/git/github",
},
{
text: "gitlab",
link: "/note/git/gitlab",
},
],
},
];
+8
View File
@@ -18,6 +18,14 @@ export const nodejsNavItem = [
text: "chokidar(文件监视工具)",
link: "/note/nodejs/chokidar",
},
{
text: "gray-matter(md文件解析元数据)",
link: "/note/nodejs/gray-matter",
},
{
text: "slash (将 Windows 反斜杠路径转换为斜杠路径)",
link: "/note/nodejs/slash",
},
],
},
];
+11
View File
@@ -0,0 +1,11 @@
export const viteNavItem = [
{
text: "vite",
items: [
{
text: "插件构建",
link: "/note/vite/plugins",
},
],
},
];
-1
View File
@@ -1,7 +1,6 @@
import topNav from "./topNav/index";
import { sideBar } from "./sideBar";
const config = {
base: "/lz-space/",
ignoreDeadLinks: true,
description: "个人学习、分享、记录",
markdown: {
+4 -1
View File
@@ -1,8 +1,11 @@
import { vitePress } from "./note/vitePress.ts";
import { nodejs } from "./note/nodejs.ts";
import { markdown } from "../topNav/markdown.ts";
import { vite } from "./note/vite.ts";
export const sideBar = {
"/components": [{ text: "Button 按钮", link: "/components/button/" }],
"/note/vitePress": vitePress,
"/note/nodejs": nodejs,
"/note/markdown": markdown,
"/note/vite": vite,
};
+12
View File
@@ -0,0 +1,12 @@
// VitePress技术笔记左侧导航栏
export const markdown = [
{
text: "markdown",
items: [
{
text: "1. markdown-it",
link: "/note/markdown/",
},
],
},
];
+3
View File
@@ -0,0 +1,3 @@
// VitePress技术笔记左侧导航栏
import { viteNavItem } from "../../common/viteNavItem.ts";
export const vite = [...viteNavItem];
+7 -15
View File
@@ -1,27 +1,19 @@
import { nodejsNavItem } from "../common/nodejsNavItem.ts";
import { viteNavItem } from "../common/viteNavItem.ts";
import { markdown } from "./markdown.ts";
import { gitNavItem } from "../common/gitNavItem.ts";
export default [
{
text: "组件展示",
items: [{ text: "按钮", link: "/components/button" }],
},
{
text: "前端网聚",
items: [
{ text: "前端综合", link: "/site/page" },
{ text: "HTML/CSS", link: "/site/html-css" },
{ text: "框架组件", link: "/site/framework" },
],
},
{
text: "技术笔记",
items: [{ text: "VitePress", link: "/note/vitePress" }],
items: [...markdown, { text: "VitePress", link: "/note/vitePress" }],
},
...gitNavItem,
...nodejsNavItem,
{
text: "关于我们",
link: "/about/page",
},
{ text: "更新日志", link: "" },
...viteNavItem,
// { text: "更新日志", link: "" },
];
+7
View File
@@ -0,0 +1,7 @@
// VitePress技术笔记左侧导航栏
export const markdown = [
{
text: "markdown-it",
link: "/note/markdown/",
},
];
+74
View File
@@ -0,0 +1,74 @@
# git action 使用
1. ssh 上传文件步骤
```yml
name: GitHub Actions Build and Deploy to Tencent
on:
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
# using the `master` branch as the default branch.
push:
branches:
- tencent
permissions:
contents: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest #指定服务器的运行环境:最新版本ubuntu
steps:
# 使用actions/checkout@v4 库拉取代码到 ubuntu 上
- name: Checkout
uses: actions/checkout@v4
with:
# 根据网上资料查询此处可以设置为 false。https://github.com/actions/checkout
persist-credentials: false
# 安装 pnpm
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
# 设置node的版本
- name: Use Node.js
# 使用 actions/setup-node@v3 库安装 nodejswith 提供了一个参数 node-version 表示要安装的 nodejs 版本
uses: actions/setup-node@v3
with:
node-version: "18.x"
cache: "pnpm"
# 打包成静态文件
- name: Build
run: pnpm install && pnpm run docs:build
- name: upload file
uses: kostyaten/ssh-server-deploy@v4
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: 22
scp_source: "books/.vitepress/dist/*"
scp_target: ${{ secrets.TARGET }}
```
注意 scp 上传时会有权限问题,需要在服务器上配置一下
```bash
ls -ld secrets.TARGET
drwxr-xr-x 2
root root
# 如果用户不是root 需要将当前用户添加到文件夹所属组,此外还需给当前用户添加目录写权限
sudo chown root:ubuntu .
sudo usermod -aG ubuntu ubuntu
sudo chmod -R 775 index/
# 最后检查下文件夹权限
ls -ld secrets.TARGET
drwxrwxr-x 5 root ubuntu 4096 Sep 25 17:58 index/
```
+14
View File
@@ -0,0 +1,14 @@
# gitLab CI/CD
要使用 GitLab CI/CD
1. 确保您有可用于运行作业的运行器。如果您没有运行器,请安装 GitLab Runner 并为您的实例、项目或组注册运行器。
2. 在仓库的根目录下创建一个 .gitlab-ci.yml 文件。此文件是您定义 CI/CD 作业的位置。
3. 当您将文件提交到存储库时,运行器将运行您的作业。作业结果显示在管道中。
安装使用 gitlab runner 执行命令, [参考](https://docs.gitlab.com/runner/install/linux-repository.html)
- 参考
[还在用Jenkins?试试Gitlab的CI/CD功能吧](https://segmentfault.com/a/1190000040852485)
+9
View File
@@ -0,0 +1,9 @@
# 前端markdown-it
[一些强大的 markdown-it 插件](https://mdit-plugins.github.io/zh/)
[一个具有强大功能的 vuepress 主题✨](<https://theme-hope.vuejs.press/zh/get-started/>)
[Markdown 与 Vue SFC](https://vuejs.press/zh/advanced/cookbook/markdown-and-vue-sfc.html)
[内置 Markdown 拓展](https://theme-hope.vuejs.press/zh/cookbook/vuepress/markdown.html#%E7%BB%84%E4%BB%B6)
+577
View File
@@ -0,0 +1,577 @@
# 一文彻底搞懂Webpack v5 Module Federation(模块联邦)
## 前言
本篇开始,一起讨论一下,基于Module Federation的微前端架构的架设方案。内容主要涉及到以下几个方面:
把单体应用,拆分为多个微应用,不可能一拆了之。微前端解决单体应用痛点的同时,也会带来很多挑战。这些挑战主要体现在各个微前端应用之间,如何共享状态和通信?以及什么是最符合微前端架构的工作流程?无论是项目结构、路由管理、数据状态管理、还是git工作流,都发生了巨大的转变。
我们的工作,主要围绕,探寻拆分和共享之间存在哪些问题?进而找到解决问题的最佳方案而展开。
本篇文章的内容,将围绕相关概念、主要工具、基础项目结构、远程模块集成展开讨论。
## 导读
- 阐述什么是微前端?有哪些微前端的实现方案?以及微前端有优缺点?
- 讨论微前端项目最佳管理方案monorepos,以及monorepos的优缺点。
- 引入pnpm、turborepo等工具,解决monorepos带来的一些痛点
- 安装配置module federation,集成动态模块
## 一、微前端
先来看一下,什么是微前端?只有理解了什么是微前端,了解微前端的价值所在,才有可能搭建出优秀微前端架构。
## 1、什么是微前端?
微前端是一种多个团队,通过独立发布功能的方式,来共同构建现代化 web 应用的技术手段及方法策略。
- 首先,微前端不是新技术,是构建web应用的策略和方法,也就是前端架构管理的一种模式;
- 其次,微前端鼓励团队拆分,各司其职,各个团队负责的模块可独立开发、构建、测试和部署;
- 另外,微前端是将体量大、复杂难以管理的单体应用,拆分成多个微型前端应用,且这些微应用功能上是相互关联的,最终重新聚合成一个统一应用。
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
总之,微前端架构,借鉴了微服务的架构理念,将Web应用由单一的单体应用,转变为多个小型前端应用聚合为一的一种手段,一种面向垂直划分系统的前端集成。
可能大家依然心存疑惑,为什么要拆分应用,再重新聚合?这么做的意义在哪里?换句话说,微前端到底能解决应用开发和管理中那些痛点呢?
## 2、微前端有哪些优点?
微前端的优点有很多,大多数都是针对单体巨石应用的痛点。
### A、构建性能效率提升
单体应用,随着代码量、和文件数量日益增加,项目变得越来沉重。无论开发启动、还是修改刷新,速度都会变得越来越缓慢。如果说,单次启动需要花费10多分钟,还可以忍受,每次修改刷新等待10几秒,是无论如何都无法忍受的事情。
微前端的思路是,把巨石应用拆分成多个微型应用,每个微应用都有自己独立的构建体系。任务并行执行,最大限度的使用线程,以空间换时间,构建速度的问题自然就解决了。
### B、微应用各个应用独立自治
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
单体应用是一个整体,即使很小的调整,都要整体打包,无法实现增量打包和部署。长年累月堆积了无数技术债,在这种牵一发而动全身的模式下,变的越来越无法管理。
而微前端模型下的微应用彼此隔离,每个微应用都具备独立的构建体系、测试体系、部署流程、运行服务。所以具备以下优点:
- 每个微应用是独立完备,且彼此隔离,所以可以突破技术栈限制,选择更加灵活;
- 微应用无需每次都构建部署所有应用,可以增量按需独立构建部署;
- 微应用都有自己的运行服务,彼此之间影响较小;
- 微应用隔离特性,有利于应用重构升级;
- 松散耦合的架构体系,更易于维护和扩展;
## 3、微前端有哪些缺点?
把应用拆分成,十数个、甚至数十个彼此独立的微型应用,可以解决巨石应用构建打包效率低效的问题;但随着应用数量的急速增加,相应的也会带来一些问题:
- 重复构建,部署频次增加,增加了生产发布风险
- 每次启动多个开发服务,会带来内存压力
- 重复命令行执行动作,效率低,易出错
- 应用版本升级管理复杂、繁琐
- 生态工具,缺乏对应的支持,比如router和store
但相对比,巨石应用带来的超低开发效率和管理困难问题,这些问题相对来说更容易解决。微应用方案收益也很明显。
## 4、微前端常用的实现方案
前面我们讨论了,微前端的概念和优缺点。下面我们一起看一下,目前流行的几种微前端架构的实现方案。
我们的重点是module federation,所以这里对实现方案,不做深入讨论,简单了解一下。
- 乾坤(qiankun):蚂蚁金融科技孵化项目,方案基于 single-spa,采用路由劫持和沙箱隔离,html entry实现方案
- micro-app 是基于 webcomponent + qiankun sandbox 的微前端方案。不同于乾坤,micro-app采用组件的方式加载子应用。
- 无界(wujie):微前端方案基于 webcomponent 容器 + iframe 沙箱,能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等用户的核心诉求
- module federationwebpack提供的一种拆分和加载远程模块的机制,可用于实现微前端。
我们方案是module federation,我们将借助module federation实现微前端应用集成。下面我们先了解一下module federation,方便后面为后面集成微应用,理清楚思路。
## 二、什么是Module Federation 模块联邦
简单说,module federation(下面简称MF)是一种拆分和加载远程模块的机制。MF出现之前,webpack模块只有本地模块,只区分同步加载和异步加载的模块。但随着微服务、全栈开发、服务端渲染的发展日渐成熟,远程模块的需求愈加迫切。MF便是为了弥补远程模块这一块缺失而产生的。
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
事实上,弥补了远程模块这块缺失,也为实现拆分微型应用提供技术支持。MF把模块区分为本地模块和远程模块。本地模块是当前构建的一部分,而远程模块则不然,远程模块属于异步加载操作,是远程构建的一部分。
MF把每个构建看做容器,每个容器都可以对外暴露本地模块,也可以加载其他容器暴露的远程模块。因此,容器都具备双重角色,即是消费者,也可以是生产者。生产者称之为Host容器,消费者称之为Remote容器。
MF解决了拆分和加载远程微模块的问题。但是如何组织管理微模块,却是个复杂的问题。按照什么维度拆分应用?如何提取管理共享模块?如何更有效的组织代码库?如何更有效的减少多应用带来的重复流程?
简而言之,我们首先如何高效的集中管理拆分出来的多个微应用?这些都是接下来我们将要讨论的monorepo功能范畴。
## 三、Monorepo应用架构模型
## 1、monorepo概念
单体应用架构模型叫做monolithic,所有业务代码放在统一的代码库中,共享一个单独的构建部署流程。
单体应用,搭建简单,易于管理,易于构建、测试、部署。大多数情况下,单体应用架构模型,就是理想的项目架构模型。
但是单体应用,容易受到规模效应的影响。容易累积成巨石应用,管理效率会越来越低。而且构建部署在同一个应用里面,业务模块耦合度比较高,容易影响到整体应用。难以重构,容易积累技术债。
一旦积累成巨石应用,最终势必会面临,重新拆解成多个微型的应用。
管理多个微型应用,比较流行的架构模型主要有两种:multirepo 和 monorepo
multirepo架构模型也叫polyrepo,这种架构模型下,每个子应用都有独立的git代码库、独立的构建和应用独立部署。项目之间天然隔离,拥有充分的自由度,项目内部高度自治。易于重构,进而享受新技术红利。
但是这种高度隔离模式也会带来很多缺陷:
- 重复流程、低效率、易出错、维护成本高;
- 共享成本比较高,提取共享模块困难;
- 不能直观反馈项目之间依赖关系,容易出现生产bug;
另外一种架构模型,所有的子应用,共用一个git代码库,但子应用保留独立的构建和部署,这种应用架构模型就叫做monorepo。
从架构模型的演变过程角度,monorepo 可以看做前面两种架构模型的变体。
multirepo 汲取 monolithic 的单一代码库的优点,把所有应用代码集中放置,保留独立构建的同时,兼备了易共享的特性,形成了新的应用架构模型monorepo。
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
## 2、monorepo优点
monorepo架构模型,保留单体应用的易共享的特性;同时还兼顾了multirepo模式下,应用之间高度隔离的特性。
monorepo兼备两种模型的优点:
- 共享时间线,迫使团队之间积极交流,每个人都得努力跟上变化
- 容易提取共享模块,版本维护简单
- 容易共享配置,统一代码质量标准和代码风格的一致性
- 微应用之间保持隔离性,可以使用不同的技术栈,易于项目重构
- 独立启动服务、独立构建,容易实现增量发布
从特性上来看,multirepo 适合关联度不太紧密的项目拆分,但高度隔离性,使得提取共享模块困难,重复搭建和配置,调整、构建、测试、部署流程几何倍增加;而 monorepo 更适合关联性比较紧密的项目拆分,保留了单体应用的统一管理优势的同时,解决了单体应用体量过大造成的低效问题。monorepo 更适合微应用架构。
## 3、monorepo缺点
微应用背后的设计思路,其实是空间换时间。多个应用并行执行,占用了更大的内存和更多进程,但提高了编译速度,节省了编译时间,解决了巨石应用造成的开发效率问题。
但微应用数量急速增加同时,也带来了一些问题:
- 依赖包管理,程数量级增加
- task执行,程数量级增加
- 应用版本管理变得更加复杂
- 代码质量和统一代码风格,变得尤为重要
- 整体应用编译时间较长,需要并行编译和编译缓存
- 对开发人员的整体素质要求更高
为了解决上述问题,我们把微应用,统一放置在同一个workspace下。只有集中放置微应用,才能借助适合的worksapce工具,实现统一管理依赖、task和应用版本的目标。只有解决掉这些痛点,monorepo架构模型的价值,才能真正的体现出来。
## 4、创建monorepo workspace
我们简单的按照集成方式,把monorepo中的模块划分为两种:
- 一种是可以在本地打包集成的模块叫做静态模块,静态模块通过npm模式加载
- 另外一种是runtime运行时,远程加载集成的模块叫做动态模块,动态模块通过http server(module federation)模式加载
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
创建项目microfe
```
<span></span><code>mkdir&nbsp;microfe&nbsp;&amp;&amp;&nbsp;<span>cd</span>&nbsp;microfe<br></code>
```
创建apps和packages文件夹,apps目录下放置应用(动态远程模块),packages下放置共享模块()。
```
<span></span><code>mkdir&nbsp;apps&nbsp;packages<br></code>
```
创建微应用目录
```
<span></span><code>mkdir&nbsp;apps/main-app&nbsp;apps/book-app&nbsp;apps/movie-app&nbsp;app/tv-app&nbsp;app/music-app<br>mkdir&nbsp;packages/pc-react-ui&nbsp;packages/pc-vue-ui&nbsp;packages/utils<br></code>
```
- main-app 为主应用,集成加载展示其他应用的入口
- apps/main-app apps/book-app apps/movie-app 是react应用
- app/tv-app app/music-app 是vue应用
- packages/pc-react-ui 是react组件库
- packages/pc-vue-ui 是vue组件库
- packages/utils 是通用js工具库
**创建主应用**,目录结构如下
```
<span></span><code><span>#&nbsp;apps/main-app</span><br>|--src<br>&nbsp;&nbsp;&nbsp;&nbsp;|--index.js<br>&nbsp;&nbsp;&nbsp;&nbsp;|--App.js<br>&nbsp;&nbsp;&nbsp;&nbsp;|--subApps<br></code>
```
其他应用,将通过module federation暴露应用入口
**创建子应用**,目录结构如下基本相同
react应用
```
<span></span><code><span>#&nbsp;apps/movie-app&nbsp;app/book-app</span><br>|--src<br>&nbsp;&nbsp;&nbsp;&nbsp;|--index.js<br>&nbsp;&nbsp;&nbsp;&nbsp;|--bootstarp.js<br>&nbsp;&nbsp;&nbsp;&nbsp;|--App.vue<br></code>
```
Vue应用
```
<span></span><code><span>#&nbsp;app/music-app&nbsp;app/tv-app</span><br>|--src<br>&nbsp;&nbsp;&nbsp;&nbsp;|--index.js<br>&nbsp;&nbsp;&nbsp;&nbsp;|--bootstarp.js<br>&nbsp;&nbsp;&nbsp;&nbsp;|--App.vue<br></code>
```
目录结构
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
在实现应用代码之前,我们还需要解决一个棘手的问题。那就是批量安装和卸载依赖,以及批量执行task的问题。
管理十几个甚至数十个微应用、微模块。手动安装和卸载或者升级依赖,几乎是一个不可能完成的任务。我们需要支持workspace的包管理器,来解决这个问题。
## 四、选择合适的包管理工具
目前流行的前端包管理工具npm、yarn、pnpm都支持workspace。综合对比后,我们选择pnpm,作为包管理工具。
那么pnpm相对npm和yarn有哪些优势呢?
## 1、为什么选择pnpm?
- **安装速度提升**,pnpm安装速度至少是npm、yarn的2倍;
- **大幅度节省磁盘空间**,pnpm包安装在一个地方,并通过链接的方式实现共享,不会在每个依赖的地方重复安装,这相对npm和yarn每个应用都要安装组件的依赖,安装大幅度节省了磁盘空间;
- **解决了幽灵依赖问题**,npm和yarn依赖包扁平化,造成了一个问题,未在package.json里显性声明的,依赖包B,由于被A依赖,扁平化依赖结构时,B被提升到了node\_modules的根目录下。因此可以在项目中import或者require到B。但是一旦A被移除或者升级,就会造成B可能被移除,这时候就会报错。pnpm的依赖结构是嵌套式的,不存在这个问题;
- **支持monorepos**pnpm内置更加完善的monorepo支持;
## 2、安装和使用pnpm
### A、pnpm是什么?
pnpm全称是 “Performant NPM”,即高性能的 npm。它结合软硬链接与新的依赖组织方式,大大提升了包管理的效率,也同时解决了 “幻影依赖” 的问题,让包管理更加规范,减少潜在风险发生的可能性。
### B、安装pnpm
```
<span></span><code>npm&nbsp;install&nbsp;-g&nbsp;pnpm<br></code>
```
或者
```
<span></span><code>npm&nbsp;install&nbsp;-g&nbsp;@pnpm/exe<br></code>
```
如果安装了包管理器HomeBrew,则可以使用以下命令安装 pnpm:
```
<span></span><code>brew&nbsp;install&nbsp;pnpm<br></code>
```
### C、配置pnpm workspace
根目录下创建pnpm-workspace.yaml文件
```
<span></span><code>touch&nbsp;pnpm-workspace.yaml<br></code>
```
输入配置
```
<span></span><code>packages:<br>&nbsp;&nbsp;<span>#&nbsp;放置共用模块与应用</span><br>&nbsp;&nbsp;-&nbsp;<span>'packages/*'</span><br>&nbsp;&nbsp;<span>#&nbsp;放置微应用</span><br>&nbsp;&nbsp;-&nbsp;<span>'apps/*'</span><br><br>&nbsp;&nbsp;<span>#&nbsp;忽略目录</span><br>&nbsp;&nbsp;-&nbsp;<span>'!**/test/**'</span><br>&nbsp;&nbsp;-&nbsp;<span>'!**/__test__/**'</span><br></code>
```
切换到根目录,初始化package.json
```
<span></span><code>pnpm&nbsp;init<br></code>
```
调整配置如下,private是为了防止误发布
```
<span></span><code>{<br>&nbsp;&nbsp;<span>"name"</span>:&nbsp;<span>"microfe"</span>,<br>&nbsp;&nbsp;<span>"version"</span>:&nbsp;<span>"1.0.0"</span>,<br>&nbsp;&nbsp;<span>"private"</span>:&nbsp;<span>true</span>,<br>&nbsp;&nbsp;<span>"description"</span>:&nbsp;<span>"micro&nbsp;front&nbsp;end&nbsp;apps"</span>,<br>&nbsp;&nbsp;<span>"main"</span>:&nbsp;<span>"index.js"</span>,<br>&nbsp;&nbsp;<span>"scripts"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"test"</span>:&nbsp;<span>"echo&nbsp;\"Error:&nbsp;no&nbsp;test&nbsp;specified\"&nbsp;&amp;&amp;&nbsp;exit&nbsp;1"</span><br>&nbsp;&nbsp;},<br>&nbsp;&nbsp;<span>"keywords"</span>:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"micro&nbsp;front&nbsp;end"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"monorepo"</span><br>&nbsp;&nbsp;],<br>&nbsp;&nbsp;<span>"author"</span>:&nbsp;<span>"yangguangzaifei"</span>,<br>&nbsp;&nbsp;<span>"license"</span>:&nbsp;<span>"MIT"</span><br>}<br></code>
```
**初始化子应用**,分别在上述创建的目录中执行命令,创建package.json
```
<span></span><code><span>cd</span>&nbsp;apps/main-app<br>pnpm&nbsp;init<br></code>
```
并调整name为@microfe/main-app,其他调整如下:
```
<span></span><code>{<br>&nbsp;&nbsp;<span>"name"</span>:&nbsp;<span>"@microfe/main-app"</span>,<br>&nbsp;&nbsp;<span>"version"</span>:&nbsp;<span>"0.0.0"</span>,<br>&nbsp;&nbsp;<span>"private"</span>:&nbsp;<span>true</span>,<br>&nbsp;&nbsp;<span>"sideEffects"</span>:&nbsp;<span>false</span>,<br>&nbsp;&nbsp;<span>"description"</span>:&nbsp;<span>"book&nbsp;app"</span>,<br>&nbsp;&nbsp;<span>"main"</span>:&nbsp;<span>"index.js"</span>,<br>&nbsp;&nbsp;<span>"scripts"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"test"</span>:&nbsp;<span>"echo&nbsp;\"Error:&nbsp;no&nbsp;test&nbsp;specified\"&nbsp;&amp;&amp;&nbsp;exit&nbsp;1"</span><br>&nbsp;&nbsp;},<br>&nbsp;&nbsp;<span>"keywords"</span>:&nbsp;[],<br>&nbsp;&nbsp;<span>"author"</span>:&nbsp;<span>"yangguangzaifei"</span>,<br>&nbsp;&nbsp;<span>"license"</span>:&nbsp;<span>"ISC"</span><br>}<br></code>
```
其他目录 apps/book-app、apps/music-app、apps/book-app、apps/tv-app、packages/p-react-ui、packages/pc-vue-uipackages/utils 重复上述动作
这里我们把应用名称,统一设置为类似 @microfe/\* 格式,方便后面批量执行命令。
### D、锁定pnpm
为了统一,我们锁定 pnpm 为唯一包管理工具
安装only-allow,并package.json添加 preinstall hook
```
<span></span><code>pnpm&nbsp;add&nbsp;only-allow&nbsp;-wD<br></code>
```
```
<span></span><code>{<br>&nbsp;&nbsp;&nbsp;&nbsp;scripts:{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"preinstall"</span>:&nbsp;<span>"npx&nbsp;only-allow&nbsp;pnpm"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code>
```
如果尝试使用yarn安装依赖,将提示下述内容:
```
<span></span><code>yarn&nbsp;add&nbsp;lodash<br></code>
```
```
<span></span><code>Use&nbsp;<span>"pnpm&nbsp;install"</span>&nbsp;<span>for</span>&nbsp;installation&nbsp;<span>in</span>&nbsp;this&nbsp;project.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>If&nbsp;you&nbsp;don<span>'t&nbsp;have&nbsp;pnpm,&nbsp;install&nbsp;it&nbsp;via&nbsp;"npm&nbsp;i&nbsp;-g&nbsp;pnpm".&nbsp;<br>For&nbsp;more&nbsp;details,&nbsp;go&nbsp;to&nbsp;https://pnpm.js.org/<br></span></code>
```
### E、使用pnpm
由于我们定义了 pnpm 的workspace,安装时,需要遵循workspace规范。必须明确指明,安装依赖的位置目录,否则会出现异常警告:
```
<span></span><code>ERR_PNPM_ADDING_TO_ROOT&nbsp;&nbsp;Running&nbsp;this&nbsp;<span>command</span>&nbsp;will&nbsp;add&nbsp;the&nbsp;dependency&nbsp;to&nbsp;the&nbsp;<br>workspace&nbsp;root,&nbsp;<span>which</span>&nbsp;might&nbsp;not&nbsp;be&nbsp;what&nbsp;you&nbsp;want&nbsp;-&nbsp;<span>if</span>&nbsp;you&nbsp;really&nbsp;meant&nbsp;it,&nbsp;<br>make&nbsp;it&nbsp;explicit&nbsp;by&nbsp;running&nbsp;this&nbsp;<span>command</span>&nbsp;again&nbsp;with&nbsp;the&nbsp;-w&nbsp;flag&nbsp;(or&nbsp;--workspace-root).&nbsp;<br>If&nbsp;you&nbsp;don<span>'t&nbsp;want&nbsp;to&nbsp;see&nbsp;this&nbsp;warning&nbsp;anymore,&nbsp;you&nbsp;may&nbsp;set&nbsp;the&nbsp;ignore-workspace-root-check&nbsp;setting&nbsp;to&nbsp;true.<br></span></code>
```
这意味着,安装、卸载、升级依赖,必须带有workspace相关参数。pnpm 有关workspace的命令参数最主要是以下几个:
```
<span></span><code>--recursive(-r):&nbsp;所有应用目录执行,不包括根目录<br>--workspace-root(-w):&nbsp;根目录执行<br>--filter(-F):&nbsp;匹配目录执行<br></code>
```
根目录下管理依赖
```
<span></span><code>pnpm&nbsp;add&nbsp;xxx&nbsp;-w<br>pnpm&nbsp;update&nbsp;xxx&nbsp;-w<br>pnpm&nbsp;remove&nbsp;xxx&nbsp;-w<br></code>
```
workspace中定义的子应用下管理依赖
```
<span></span><code>pnpm&nbsp;add&nbsp;xxx&nbsp;-r<br>pnpm&nbsp;update&nbsp;xxx&nbsp;-r<br>pnpm&nbsp;remove&nbsp;xxx&nbsp;-r<br></code>
```
仅仅apps目录下面的应用管理依赖
```
<span></span><code>pnpm&nbsp;add&nbsp;xxx&nbsp;-F&nbsp;./apps<br>pnpm&nbsp;update&nbsp;xxx&nbsp;-F&nbsp;./apps<br>pnpm&nbsp;remove&nbsp;xxx&nbsp;-F&nbsp;./apps<br></code>
```
也支持glob模糊匹配应用名称
```
<span></span><code>pnpm&nbsp;add&nbsp;xxx&nbsp;-F&nbsp;@microfe/*<br>pnpm&nbsp;update&nbsp;xxx&nbsp;-F&nbsp;@microfe/*<br>pnpm&nbsp;remove&nbsp;xxx&nbsp;-F&nbsp;@microfe/*<br></code>
```
需要注意的是,上述的参数不仅仅支持依赖管理,同样支持pnpm的其他命令。比如run,可以批量执行workspace下匹配的应用下相同的命令
```
<span></span><code>pnpm&nbsp;run&nbsp;dev&nbsp;-r<br>pnpm&nbsp;-F&nbsp;@microfe/*&nbsp;rum&nbsp;lint<br></code>
```
这样我们就可以很方便的管理多个应用的安装、启动、打包、测试等等。否则手动管理,如此繁琐的命令操作,会大大降低工作效率。
## 五、集成远程模块(module federation
安装配置完pnpm,我们就可以尝试集成module federation,把momorepo中的模块整合起来,跑一下整体应用了。
## 1、安装依赖
首先安装webpack及其相关插件,由于属于公用依赖,我们安装在根目录
```
<span></span><code>pnpm&nbsp;add&nbsp;webpack&nbsp;webpack-cli&nbsp;&nbsp;-wD<br><br>pnpm&nbsp;add&nbsp;webpack-manifest-plugin&nbsp;webpack-dev-server&nbsp;webpack-merge&nbsp;html-webpack-plugin&nbsp;-wD<br><br>pnpm&nbsp;add&nbsp;css-loader&nbsp;babel-loader&nbsp;style-loader&nbsp;sass-loader&nbsp;-wD<br></code>
```
安装babel相关配置,同样安装在根目录
```
<span></span><code>pnpm&nbsp;add&nbsp;@babel/core&nbsp;@babel/preset-env&nbsp;@babel/preset-react&nbsp;@babel/plugin-transform-runtime&nbsp;-wD<br></code>
```
安装react
```
<span></span><code>pnpm&nbsp;-F=<span>'./apps/{main,movie,book}-app'</span>&nbsp;-F=<span>'./packages/{pc-react-ui,utils}'</span>&nbsp;add&nbsp;react&nbsp;react-dom&nbsp;-D<br></code>
```
安装vue
```
<span></span><code>pnpm&nbsp;-F=<span>'./apps/{tv,music}-app'</span>&nbsp;-F=<span>'./packages/{utils,pc-vue-ui}'</span>&nbsp;add&nbsp;vue<br><br>pnpm&nbsp;-F=<span>'./apps/{tv,music}-app'</span>&nbsp;-F=<span>'./packages/{utils,pc-vue-ui}'</span>&nbsp;add&nbsp;@vue/compiler-sfc&nbsp;-D<br></code>
```
## 2、使用 module federation集成远程模块
以 book-app子应用为例:
### A、完善子应用
App应用入口,声明跟普通应用一致。
App.js
```
<span></span><code>import&nbsp;React&nbsp;from&nbsp;<span>'react'</span><br><br><span>function</span>&nbsp;<span><span>BookApp</span></span>()&nbsp;{<br>&nbsp;&nbsp;<span>return</span>&nbsp;(<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;Welcome&nbsp;to&nbsp;book&nbsp;app&lt;/h1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;<br>&nbsp;&nbsp;)<br>}<br><br><span>export</span>&nbsp;default&nbsp;BookApp<br></code>
```
App.vue
```
<span></span><code>&lt;template&gt;<br>&nbsp;&nbsp;&lt;div&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;{{&nbsp;message&nbsp;}}&lt;/h1&gt;<br>&nbsp;&nbsp;&lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&nbsp;<span>type</span>=<span>"module"</span>&gt;<br>import&nbsp;{&nbsp;ref,&nbsp;defineComponent&nbsp;}&nbsp;from&nbsp;<span>'vue'</span><br><br><span>export</span>&nbsp;default&nbsp;defineComponent({<br>&nbsp;&nbsp;<span><span>setup</span></span>()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;message&nbsp;=&nbsp;ref(<span>'Welcome&nbsp;to&nbsp;music&nbsp;app'</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>return</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>})<br>&lt;/script&gt;<br></code>
```
bootstrap是应用挂载动作,不同的是挂载的目标dom,是作为参数动态传入的,由使用场景决定。bootstrap会暴露给远程应用,用于挂载启动App应用。
react bootstrap.js
```
<span></span><code>import&nbsp;React&nbsp;from&nbsp;<span>'react'</span><br>import&nbsp;{&nbsp;createRoot&nbsp;}&nbsp;from&nbsp;<span>'react-dom/client'</span><br>import&nbsp;BookApp&nbsp;from&nbsp;<span>'./App'</span><br><br><span>export</span>&nbsp;const&nbsp;mount&nbsp;=&nbsp;({&nbsp;rootDom&nbsp;})&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;const&nbsp;root&nbsp;=&nbsp;createRoot(rootDom)<br>&nbsp;&nbsp;root.render(&lt;BookApp&nbsp;/&gt;)<br>&nbsp;&nbsp;<span>return</span>&nbsp;()&nbsp;=&gt;&nbsp;queueMicrotask(()&nbsp;=&gt;&nbsp;root.unmount())<br>}<br></code>
```
vue bootstrap.js
```
<span></span><code>import&nbsp;{&nbsp;createApp&nbsp;}&nbsp;from&nbsp;<span>'vue'</span><br>import&nbsp;MusicApp&nbsp;from&nbsp;<span>'./App.vue'</span><br><br><span>export</span>&nbsp;const&nbsp;mount&nbsp;=&nbsp;({&nbsp;rootDom&nbsp;})&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;const&nbsp;root&nbsp;=&nbsp;createApp(MusicApp)<br>&nbsp;&nbsp;root.mount(rootDom)<br>&nbsp;&nbsp;<span>return</span>&nbsp;()&nbsp;=&gt;&nbsp;queueMicrotask(()&nbsp;=&gt;&nbsp;root.unmount())<br>}<br><br><span>export</span>&nbsp;default&nbsp;mount<br></code>
```
### B、对外暴露远程模块
book-app目录下创建build目录,并创建webpack.config.js文件
启动4002本地服务,并通过ModuleFederationPlugin插件,对外暴露本地模块microfe\_book\_app的bootstrap
```
<span></span><code>{<br>&nbsp;&nbsp;&nbsp;&nbsp;//....<br>&nbsp;&nbsp;output:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;publicPath:&nbsp;<span>'http://localhost:4002/'</span><br>&nbsp;&nbsp;},<br>&nbsp;&nbsp;devServer:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;port:&nbsp;4002<br>&nbsp;&nbsp;},<br>&nbsp;&nbsp;plugins:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;ModuleFederationPlugin({<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;<span>'microfe_book_app'</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filename:&nbsp;<span>'remoteEntry.js'</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exposes:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>'./bootstrap'</span>:&nbsp;<span>'./src/bootstrap'</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;]<br>&nbsp;&nbsp;//...<br>}<br></code>
```
### C、加载远程模块
main-app中同样创建build/webpack.config.js
启动4001本地服务,并通过ModuleFederationPlugin插件,microfe\_book\_app为bookApp
```
<span></span><code>{<br>//....<br>entry:&nbsp;<span>'./src/index.js'</span>,<br>&nbsp;&nbsp;output:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;publicPath:&nbsp;<span>'http://localhost:4001/'</span><br>&nbsp;&nbsp;},<br>&nbsp;&nbsp;devServer:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;open:&nbsp;<span>true</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;port:&nbsp;4001<br>&nbsp;&nbsp;},<br>&nbsp;&nbsp;plugins:&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;ModuleFederationPlugin({<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;<span>'microfe_main_app'</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remotes:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bookApp:&nbsp;<span>'microfe_book_app@http://localhost:4002/remoteEntry.js'</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;]<br>&nbsp;&nbsp;//...<br>}<br></code>
```
### D、使用远程模块
在main-app/src/subApps/bookApp.js文件中,通过bookApp/bootstrap,访问到microfe\_book\_app暴露的bootstrap。
然后,通过ref挂载到main-app
```
<span></span><code>import&nbsp;React,&nbsp;{&nbsp;useRef,&nbsp;useEffect&nbsp;}&nbsp;from&nbsp;<span>'react'</span><br>import&nbsp;{&nbsp;mount&nbsp;}&nbsp;from&nbsp;<span>'bookApp/bootstrap'</span><br><br><span>export</span>&nbsp;default&nbsp;()&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;const&nbsp;wrapperRef&nbsp;=&nbsp;useRef(null)<br>&nbsp;&nbsp;const&nbsp;isFirstRunRef&nbsp;=&nbsp;useRef(<span>true</span>)<br>&nbsp;&nbsp;const&nbsp;isFirstRef&nbsp;=&nbsp;useRef(<span>true</span>)<br>&nbsp;&nbsp;const&nbsp;unmountRef&nbsp;=&nbsp;useRef(()&nbsp;=&gt;&nbsp;{})<br><br>&nbsp;&nbsp;//&nbsp;Mount&nbsp;book&nbsp;MFE<br>&nbsp;&nbsp;useEffect(()&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if</span>&nbsp;(isFirstRunRef.current)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isFirstRunRef.current&nbsp;=&nbsp;<span>false</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unmountRef.current&nbsp;=&nbsp;mount({<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rootDom:&nbsp;wrapperRef.current<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;},&nbsp;[])<br><br>&nbsp;&nbsp;useEffect(()&nbsp;=&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>if</span>&nbsp;(isFirstRef.current)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isFirstRef.current&nbsp;=&nbsp;<span>false</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return</span>&nbsp;unmountRef.current<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;},&nbsp;[])<br><br>&nbsp;&nbsp;<span>return</span>&nbsp;&lt;div&nbsp;ref={wrapperRef}&nbsp;id=<span>"book-sub-app"</span>&nbsp;/&gt;<br>}<br></code>
```
其他子应用,暴露加载模块过程,基本跟上述一致,这里就不一一展示了
### E、定义scripts任务
根目录,package.json中配置scripts task
\--parallel忽略并发性和拓扑排序,dev server 默认启动 watch 命令,是会长时间运行监听文件变更,进程不会自动退出(除了报错或者手动退出),因此需要加上 **\--parallel 告诉 pnpm 运行该脚本时完全忽略并发和拓扑排序**。
另外,可能会出现启动服务数量将受cpu限制。
```
<span></span><code>&nbsp;&nbsp;<span>"scripts"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;....<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"dev"</span>:&nbsp;<span>"pnpm&nbsp;--parallel&nbsp;-r&nbsp;run&nbsp;dev"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"build"</span>:&nbsp;<span>"pnpm&nbsp;--parallel&nbsp;-r&nbsp;run&nbsp;build"</span><br>&nbsp;&nbsp;}<br></code>
```
apps子应用,package.json中配置scripts task
```
<span></span><code><span>"scripts"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"dev"</span>:&nbsp;<span>"npx&nbsp;webpack&nbsp;serve&nbsp;--mode&nbsp;development&nbsp;--config&nbsp;./build/webpack.config.js"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"build"</span>:&nbsp;<span>"npx&nbsp;webpack&nbsp;--mode&nbsp;production&nbsp;--config&nbsp;./build/webpack.config.js"</span><br>}<br></code>
```
执行下面命令,启动开发服务
```
<span></span><code>pnpm&nbsp;dev<br></code>
```
打开本地服务:<http://localhost:4001>
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
## 六、选择更合适的任务管理器
虽然,pnpm支持批量任务管理,解决了手动执行任务的低效问题。但是同时启动多个开发服务和文件实时监听编译,带来内存和线程管理压力问题,需要借助合适的monorepo工具,才能解决。
目前最流行的Monorepo工具是Lerna、Turborepo和Rush,其中lerna重新更新版本后,需要配合Nx使用。
## 1、对比选择
- **性能**turborepo和Rush相当,Lerna+Nx是最好的,大概相当于前面两者的5倍;
- **功能**:Rush功能最完备齐全,其次是Lerna+Nx,但是turborepo发展趋势最迅速,赶超前面两个是迟早的事情
- **设置**turborepo设置最简单,其次是Lerna+NxRush最复杂
- **缓存**:Rush缓存功能处于实验性阶段,Lerna+Nx远程云缓存收费,turborepo远程云缓存免费,且支持开源搭建自己的远程缓存
- **学习成本**Rush和Lerna+Nx学习曲线相对陡峭,turborepo上手比较简单
虽然turborepo不是性能最好,也不是功能最完备的monorepo工具。但是turborepo 通过「智能缓存」与「任务调度」,极大的提升了构建速度,节省了计算资源。并且 turbo 配置非常简单,侵入性小,可以渐进式的采用。可以很容易和已有的项目整合。
另外,turborepo已经被Vercel收购,而且从1.7版本开始,从go向rust迁移,其发展潜力是巨大的。功能会逐渐完备,性能也会进一步提升,再加上其无与伦比的生态优势。turborepo迟早会成为monorepo领域,最优异的选择。
## 2、turborepo优点
**工欲善其事必先利其器**。下面一起看一下,turborepo都有哪些优点。在此基础上,我们可以更加清晰直观的理解turborepo的设计思路,进而理清楚我们集成和使用turborepo的思路。
## 3、安装turborepo
全局安装
```
<span></span><code>{<br>&nbsp;&nbsp;<span>"<span>$schema</span>"</span>:&nbsp;<span>"https://turbo.build/schema.json"</span>,<br>&nbsp;&nbsp;&nbsp;<span>"pipeline"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"topo"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"dependsOn"</span>:&nbsp;[<span>"^topo"</span>]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"your-task"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"dependsOn"</span>:&nbsp;[<span>"topo"</span>]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;}<br></code>
```
```
<span></span><code>pnpm&nbsp;install&nbsp;turbo&nbsp;--global<br></code>
```
本地根目录安装
```
<span></span><code>pnpm&nbsp;add&nbsp;turbo&nbsp;-wD<br></code>
```
## 4、配置turborepo
根目录下创建turbo.json,输入以下内容,pipeline..outputs中指定缓存目录。不同于pnpmtask依赖关系是在dependsOn中显式指定的,如果dependsOn为空,表明任何时候都可以执行。turbo依照依赖关系,最大限度的利用cpu,调度执行task。
```
<span></span><code>{<br>&nbsp;&nbsp;<span>"<span>$schema</span>"</span>:&nbsp;<span>"https://turbo.build/schema.json"</span>,<br>&nbsp;&nbsp;<span>"pipeline"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"build"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"dependsOn"</span>:&nbsp;[<span>"^build"</span>],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"outputs"</span>:&nbsp;[<span>"dist/**"</span>]<br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"deploy"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"dependsOn"</span>:&nbsp;[<span>"build"</span>,&nbsp;<span>"test"</span>,&nbsp;<span>"lint"</span>]<br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"test"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"dependsOn"</span>:&nbsp;[<span>"build"</span>],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"inputs"</span>:&nbsp;[<span>"src/**/*.jsx"</span>,&nbsp;<span>"src/**/*.js"</span>,&nbsp;<span>"test/**/*.js"</span>,&nbsp;<span>"test/**/*.jsx"</span>]<br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"lint"</span>:&nbsp;{},<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"dev"</span>:&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"cache"</span>:&nbsp;<span>false</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>"persistent"</span>:&nbsp;<span>true</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>}<br></code>
```
## 5、调整scripts命令
调整根目录package.json中scripts命令
```
<span></span><code>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"dev"</span>:&nbsp;<span>"npx&nbsp;turbo&nbsp;run&nbsp;dev"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;<span>"build"</span>:&nbsp;<span>"npx&nbsp;turbo&nbsp;run&nbsp;build"</span><br>}<br></code>
```
启动开发服务
```
<span></span><code>pnpm&nbsp;dev<br></code>
```
打开本地服务:<http://localhost:4001>
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
![图片](data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='1px' height='1px' viewBox='0 0 1 1' version='1.1' xmlns='<http://www.w3.org/2000/svg>' xmlns:xlink='<http://www.w3.org/1999/xlink'%3E%3Ctitle%3E%3C/title%3E%3Cg> stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' fill-opacity='0'%3E%3Cg transform='translate(-249.000000, -126.000000)' fill='%23FFFFFF'%3E%3Crect x='249' y='126' width='1' height='1'%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E)
初次编译
```
<span></span><code>@microfe/music-app:dev:&nbsp;webpack&nbsp;5.88.1&nbsp;compiled&nbsp;successfully&nbsp;<span>in</span>&nbsp;10567&nbsp;ms<br></code>
```
二次编译
```
<span></span><code>@microfe/music-app:dev:&nbsp;webpack&nbsp;5.88.1&nbsp;compiled&nbsp;successfully&nbsp;<span>in</span>&nbsp;520&nbsp;ms<br></code>
```
## 七、总结
上面我们一起阐述了微前端的概念和优势;进而讨论了微前端项目管理的架构模式,最终选择了monorepo。并且探讨了monorepo的最佳实践,着手搭建了monorepo基础架构。我们把monorepo的微模块,分成远程模块和静态模块。静态模块,本地安装打包;远程模块本身具备独立服务,需要远程加载,异步集成。我们选择module federation实现远程模块的集成。我们还讨论了monorepo本身的痛点,并且集成安装了pnpm和turborepo解决了部分痛点。
> 作者:你看那儿有阳光在飞
> 链接:<https://juejin.cn/post/7265524106817503295>
+565
View File
@@ -0,0 +1,565 @@
# gray-matter [![NPM version](https://img.shields.io/npm/v/gray-matter.svg?style=flat)](https://www.npmjs.com/package/gray-matter) [![NPM monthly downloads](https://img.shields.io/npm/dm/gray-matter.svg?style=flat)](https://npmjs.org/package/gray-matter) [![NPM total downloads](https://img.shields.io/npm/dt/gray-matter.svg?style=flat)](https://npmjs.org/package/gray-matter) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/gray-matter.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/gray-matter)
> Parse front-matter from a string or file. Fast, reliable and easy to use. Parses YAML front matter by default, but also has support for YAML, JSON, TOML or Coffee Front-Matter, with options to set custom delimiters. Used by metalsmith, assemble, verb and many other projects.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
npm install --save gray-matter
```
## Heads up
Please see the [changelog](CHANGELOG.md) to learn about breaking changes that were made in v3.0.
## What does this do?
<details>
<summary><strong>Run this example</strong></summary>
Add the HTML in the following example to `example.html`, then add the following code to `example.js` and run `$ node example` (without the `$`):
```js
const fs = require('fs');
const matter = require('gray-matter');
const str = fs.readFileSync('example.html', 'utf8');
console.log(matter(str));
```
</details>
Converts a string with front-matter, like this:
```handlebars
---
title: Hello
slug: home
---
<h1>Hello world!</h1>
```
Into an object like this:
```js
{
content: '<h1>Hello world!</h1>',
data: {
title: 'Hello',
slug: 'home'
}
}
```
## Why use gray-matter?
* **simple**: main function takes a string and returns an object
* **accurate**: better at catching and handling edge cases than front-matter parsers that rely on regex for parsing
* **fast**: faster than other front-matter parsers that use regex for parsing
* **flexible**: By default, gray-matter is capable of parsing [YAML](https://github.com/nodeca/js-yaml), [JSON](http://en.wikipedia.org/wiki/Json) and JavaScript front-matter. But other [engines](#optionsengines) may be added.
* **extensible**: Use [custom delimiters](#optionsdelimiters), or add support for [any language](#optionsengines), like [TOML](http://github.com/mojombo/toml), [CoffeeScript](http://coffeescript.org), or [CSON](https://github.com/bevry/cson)
* **battle-tested**: used by [assemble](https://github.com/assemble/assemble), [metalsmith](https://github.com/segmentio/metalsmith), [phenomic](https://github.com/phenomic/phenomic), [verb](https://github.com/assemble/verb), [generate](https://github.com/generate/generate), [update](https://github.com/update/update) and many others.
<details>
<summary><strong>Rationale</strong></summary>
**Why did we create gray-matter in the first place?**
We created gray-matter after trying out other libraries that failed to meet our standards and requirements.
Some libraries met most of the requirements, but _none met all of them_.
**Here are the most important**:
* Be usable, if not simple
* Use a dependable and well-supported library for parsing YAML
* Support other languages besides YAML
* Support stringifying back to YAML or another language
* Don't fail when no content exists
* Don't fail when no front matter exists
* Don't use regex for parsing. This is a relatively simple parsing operation, and regex is the slowest and most error-prone way to do it.
* Have no problem reading YAML files directly
* Have no problem with complex content, including **non-front-matter** fenced code blocks that contain examples of YAML front matter. Other parsers fail on this.
* Support stringifying back to front-matter. This is useful for linting, updating properties, etc.
* Allow custom delimiters, when it's necessary for avoiding delimiter collision.
* Should return an object with at least these three properties:
* `data`: the parsed YAML front matter, as a JSON object
* `content`: the contents as a string, without the front matter
* `orig`: the "original" content (for debugging)
</details>
## Usage
Using Node's `require()` system:
```js
const matter = require('gray-matter');
```
Or with [typescript](https://www.typescriptlang.org)
```js
import matter = require('gray-matter');
// OR
import * as matter from 'gray-matter';
```
Pass a string and [options](#options) to gray-matter:
```js
console.log(matter('---\ntitle: Front Matter\n---\nThis is content.'));
```
Returns:
```js
{
content: '\nThis is content.',
data: {
title: 'Front Matter'
}
}
```
More about the returned object in the following section.
***
## Returned object
gray-matter returns a `file` object with the following properties.
**Enumerable**
* `file.data` **{Object}**: the object created by parsing front-matter
* `file.content` **{String}**: the input string, with `matter` stripped
* `file.excerpt` **{String}**: an excerpt, if [defined on the options](#optionsexcerpt)
* `file.empty` **{String}**: when the front-matter is "empty" (either all whitespace, nothing at all, or just comments and no data), the original string is set on this property. See [#65](https://github.com/jonschlinkert/gray-matter/issues/65) for details regarding use case.
* `file.isEmpty` **{Boolean}**: true if front-matter is empty.
**Non-enumerable**
In addition, the following non-enumberable properties are added to the object to help with debugging.
* `file.orig` **{Buffer}**: the original input string (or buffer)
* `file.language` **{String}**: the front-matter language that was parsed. `yaml` is the default
* `file.matter` **{String}**: the _raw_, un-parsed front-matter string
* `file.stringify` **{Function}**: [stringify](#stringify) the file by converting `file.data` to a string in the given language, wrapping it in delimiters and prepending it to `file.content`.
## Run the examples
If you'd like to test-drive the examples, first clone gray-matter into `my-project` (or wherever you want):
```sh
git clone https://github.com/jonschlinkert/gray-matter my-project
```
CD into `my-project` and install dependencies:
```sh
cd my-project && npm install
```
Then run any of the [examples](./examples) to see how gray-matter works:
```sh
node examples/<example_name>
```
**Links to examples**
* [coffee](examples/coffee.js)
* [excerpt-separator](examples/excerpt-separator.js)
* [excerpt-stringify](examples/excerpt-stringify.js)
* [excerpt](examples/excerpt.js)
* [javascript](examples/javascript.js)
* [json-stringify](examples/json-stringify.js)
* [json](examples/json.js)
* [restore-empty](examples/restore-empty.js)
* [sections-excerpt](examples/sections-excerpt.js)
* [sections](examples/sections.js)
* [toml](examples/toml.js)
* [yaml-stringify](examples/yaml-stringify.js)
* [yaml](examples/yaml.js)
## API
### [matter](index.js#L29)
Takes a string or object with `content` property, extracts and parses front-matter from the string, then returns an object with `data`, `content` and other [useful properties](#returned-object).
**Params**
* `input` **{Object|String}**: String, or object with `content` string
* `options` **{Object}**
* `returns` **{Object}**
**Example**
```js
const matter = require('gray-matter');
console.log(matter('---\ntitle: Home\n---\nOther stuff'));
//=> { data: { title: 'Home'}, content: 'Other stuff' }
```
### [.stringify](index.js#L160)
Stringify an object to YAML or the specified language, and append it to the given string. By default, only YAML and JSON can be stringified. See the [engines](#engines) section to learn how to stringify other languages.
**Params**
* `file` **{String|Object}**: The content string to append to stringified front-matter, or a file object with `file.content` string.
* `data` **{Object}**: Front matter to stringify.
* `options` **{Object}**: [Options](#options) to pass to gray-matter and [js-yaml](https://github.com/nodeca/js-yaml).
* `returns` **{String}**: Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string.
**Example**
```js
console.log(matter.stringify('foo bar baz', {title: 'Home'}));
// results in:
// ---
// title: Home
// ---
// foo bar baz
```
### [.read](index.js#L178)
Synchronously read a file from the file system and parse front matter. Returns the same object as the [main function](#matter).
**Params**
* `filepath` **{String}**: file path of the file to read.
* `options` **{Object}**: [Options](#options) to pass to gray-matter.
* `returns` **{Object}**: Returns [an object](#returned-object) with `data` and `content`
**Example**
```js
const file = matter.read('./content/blog-post.md');
```
### [.test](index.js#L193)
Returns true if the given `string` has front matter.
**Params**
* `string` **{String}**
* `options` **{Object}**
* `returns` **{Boolean}**: True if front matter exists.
## Options
### options.excerpt
**Type**: `Boolean|Function`
**Default**: `undefined`
Extract an excerpt that directly follows front-matter, or is the first thing in the string if no front-matter exists.
If set to `excerpt: true`, it will look for the frontmatter delimiter, `---` by default and grab everything leading up to it.
**Example**
```js
const str = '---\nfoo: bar\n---\nThis is an excerpt.\n---\nThis is content';
const file = matter(str, { excerpt: true });
```
Results in:
```js
{
content: 'This is an excerpt.\n---\nThis is content',
data: { foo: 'bar' },
excerpt: 'This is an excerpt.\n'
}
```
You can also set `excerpt` to a function. This function uses the 'file' and 'options' that were initially passed to gray-matter as parameters, so you can control how the excerpt is extracted from the content.
**Example**
```js
// returns the first 4 lines of the contents
function firstFourLines(file, options) {
file.excerpt = file.content.split('\n').slice(0, 4).join(' ');
}
const file = matter([
'---',
'foo: bar',
'---',
'Only this',
'will be',
'in the',
'excerpt',
'but not this...'
].join('\n'), {excerpt: firstFourLines});
```
Results in:
```js
{
content: 'Only this\nwill be\nin the\nexcerpt\nbut not this...',
data: { foo: 'bar' },
excerpt: 'Only this will be in the excerpt'
}
```
### options.excerpt_separator
**Type**: `String`
**Default**: `undefined`
Define a custom separator to use for excerpts.
```js
console.log(matter(string, {excerpt_separator: '<!-- end -->'}));
```
**Example**
The following HTML string:
```html
---
title: Blog
---
My awesome blog.
<!-- end -->
<h1>Hello world</h1>
```
Results in:
```js
{
data: { title: 'Blog'},
excerpt: 'My awesome blog.',
content: 'My awesome blog.\n<!-- end -->\n<h1>Hello world</h1>'
}
```
### options.engines
Define custom engines for parsing and/or stringifying front-matter.
**Type**: `Object` Object of engines
**Default**: `JSON`, `YAML` and `JavaScript` are already handled by default.
**Engine format**
Engines may either be an object with `parse` and (optionally) `stringify` methods, or a function that will be used for parsing only.
**Examples**
```js
const toml = require('toml');
/**
* defined as a function
*/
const file = matter(str, {
engines: {
toml: toml.parse.bind(toml),
}
});
/**
* Or as an object
*/
const file = matter(str, {
engines: {
toml: {
parse: toml.parse.bind(toml),
// example of throwing an error to let users know stringifying is
// not supported (a TOML stringifier might exist, this is just an example)
stringify: function() {
throw new Error('cannot stringify to TOML');
}
}
}
});
console.log(file);
```
### options.language
**Type**: `String`
**Default**: `yaml`
Define the engine to use for parsing front-matter.
```js
console.log(matter(string, {language: 'toml'}));
```
**Example**
The following HTML string:
```html
---
title = "TOML"
description = "Front matter"
categories = "front matter toml"
---
This is content
```
Results in:
```js
{ content: 'This is content',
excerpt: '',
data:
{ title: 'TOML',
description: 'Front matter',
categories: 'front matter toml' } }
```
**Dynamic language detection**
Instead of defining the language on the options, gray-matter will automatically detect the language defined after the first delimiter and select the correct engine to use for parsing.
```html
---toml
title = "TOML"
description = "Front matter"
categories = "front matter toml"
---
This is content
```
### options.delimiters
**Type**: `String`
**Default**: `---`
Open and close delimiters can be passed in as an array of strings.
**Example:**
```js
// format delims as a string
matter.read('file.md', {delims: '~~~'});
// or an array (open/close)
matter.read('file.md', {delims: ['~~~', '~~~']});
```
would parse:
```html
~~~
title: Home
~~~
This is the {{title}} page.
```
## Deprecated options
### options.lang
Decrecated, please use [options.language](#optionslanguage) instead.
### options.delims
Decrecated, please use [options.delimiters](#optionsdelimiters) instead.
### options.parsers
Decrecated, please use [options.engines](#optionsengines) instead.
## About
<details>
<summary><strong>Contributing</strong></summary>
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
</details>
<details>
<summary><strong>Running Tests</strong></summary>
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
npm install && npm test
```
</details>
<details>
<summary><strong>Building docs</strong></summary>
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
npm install -g verbose/verb#dev verb-generate-readme && verb
```
</details>
### Related projects
You might also be interested in these projects:
* [assemble](https://www.npmjs.com/package/assemble): Get the rocks out of your socks! Assemble makes you fast at creating web projects… [more](https://github.com/assemble/assemble) | [homepage](https://github.com/assemble/assemble "Get the rocks out of your socks! Assemble makes you fast at creating web projects. Assemble is used by thousands of projects for rapid prototyping, creating themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websit")
* [metalsmith](https://www.npmjs.com/package/metalsmith): An extremely simple, pluggable static site generator. | [homepage](https://github.com/segmentio/metalsmith#readme "An extremely simple, pluggable static site generator.")
* [verb](https://www.npmjs.com/package/verb): Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used… [more](https://github.com/verbose/verb) | [homepage](https://github.com/verbose/verb "Documentation generator for GitHub projects. Verb is extremely powerful, easy to use, and is used on hundreds of projects of all sizes to generate everything from API docs to readmes.")
* [gray-matter-loader](https://github.com/atlassian/gray-matter-loader): A webpack loader for gray-matter. [homepage](https://github.com/atlassian/gray-matter-loader#gray-matter-loader)
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 174 | [jonschlinkert](https://github.com/jonschlinkert) |
| 7 | [RobLoach](https://github.com/RobLoach) |
| 5 | [heymind](https://github.com/heymind) |
| 4 | [doowb](https://github.com/doowb) |
| 3 | [aljopro](https://github.com/aljopro) |
| 2 | [reccanti](https://github.com/reccanti) |
| 2 | [onokumus](https://github.com/onokumus) |
| 2 | [moozzyk](https://github.com/moozzyk) |
| 1 | [Ajedi32](https://github.com/Ajedi32) |
| 1 | [caesar](https://github.com/caesar) |
| 1 | [ianstormtaylor](https://github.com/ianstormtaylor) |
| 1 | [qm3ster](https://github.com/qm3ster) |
| 1 | [zachwhaley](https://github.com/zachwhaley) |
### Author
**Jon Schlinkert**
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
### License
Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on April 01, 2018._
+36
View File
@@ -0,0 +1,36 @@
# slash
> Convert Windows backslash paths to slash paths: `foo\\bar``foo/bar`
[Forward-slash paths can be used in Windows](http://superuser.com/a/176395/6877) as long as they're not extended-length paths.
This was created since the `path` methods in Node.js outputs `\\` paths on Windows.
## Install
```sh
npm install slash
```
## Usage
```js
import path from 'node:path';
import slash from 'slash';
const string = path.join('foo', 'bar');
// Unix => foo/bar
// Windows => foo\\bar
slash(string);
// Unix => foo/bar
// Windows => foo/bar
```
## API
### slash(path)
Type: `string`
Accepts a Windows backslash path and returns a path with forward slashes.
+1
View File
@@ -0,0 +1 @@
# vite 插件 api 官方地址 <https://cn.vitejs.dev/guide/api-plugin.html>
@@ -0,0 +1,36 @@
# prefetch 定义 [mdn]([https://](https://developer.mozilla.org/zh-CN/docs/Glossary/Prefetch)) preload定义 [mdn](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload)
1. 在资讯星球中使用时发现,prefetch 与 modulepreload 加载同一个资源,会重复加载,影响性能。
2. preload 在火狐中也会重复加载,但preload不会重复
3. [modulePreload]([https://](https://cn.vitejs.dev/config/build-options)) 浏览器 esm 模块预加载机制,仅对 js 文件有效
## preload预加载 和 prefetch预提取
二者应用场景:
preload:预加载,比如图片资源、字体元素等需要提前加载,比如 强制将字体请求放到css资源请求之前
```html
<head>
...
<link rel="prefetch" href="static/img/ticket_bg.a5bb7c33.png">
...
</head>
```
prefetch预提取: 比如页面缓存,在生命周期或者页面加载完成之后去加载大概率会访问到的资源,比如 loading 图片
```html
<head>
...
<link rel="preload" as="font" href="<%= require('/assets/fonts/AvenirNextLTPro-Demi.otf') %>" crossorigin>
<link rel="preload" as="font" href="<%= require('/assets/fonts/AvenirNextLTPro-Regular.otf') %>" crossorigin>
...
</head>
```
## 参考文档
[瞒不住了!Prefetch 是一个大忽悠]([https://](https://juejin.cn/post/7217750941853171770))
[vite 打包中 <link rel="modulepreload">的深入理解](https://juejin.cn/post/6961609946832044045)
+11 -1
View File
@@ -14,7 +14,10 @@
"commit": "cz",
"commitlint": "commitlint --config commitlint.config.js -e -V",
"docs:build": "pnpm run --filter=books docs:build",
"docs:dev": "pnpm run --filter=books docs:dev"
"docs:dev": "pnpm run --filter=books docs:dev",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"prepare": "husky",
"blog:dev": "pnpm run --filter=lambert-nuxt-blog dev"
},
"keywords": [],
"author": "",
@@ -28,8 +31,15 @@
"@vitejs/plugin-vue": "^5.1.2",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"commitizen": "^4.3.0",
"conventional-changelog": "^6.0.0",
"conventional-changelog-cli": "^5.0.0",
"cz-conventional-changelog": "^3.3.0",
"husky": "^9.1.4",
"vite": "^5.4.0"
},
"husky": {
"hooks": {
"pre-commit": "pnpm run changelog && git add -A ."
}
}
}
+24
View File
@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example
+75
View File
@@ -0,0 +1,75 @@
# Nuxt 3 Minimal Starter
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install the dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm run dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm run build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm run preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
+1
View File
@@ -0,0 +1 @@
<template><NuxtPage></NuxtPage></template>
Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>Component: TheHeader</div>
</template>
<style scoped></style>
+33
View File
@@ -0,0 +1,33 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
// app: {
// head: {
// charset: "utf-8",
// viewport: "width=device-width, initial-scale=1",
// },
// },
compatibilityDate: "2024-04-03",
devtools: { enabled: true },
modules: ["@nuxtjs/mdc", "@nuxt/image", "@nuxtjs/tailwindcss"],
mdc: {
remarkPlugins: {
emoji: {
src: "remark-emoji",
},
},
},
image: {
dir: "assets/images",
format: ["webp"],
screens: {
xs: 320,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
xxl: 1536,
"2xl": 1536,
},
},
});
+24
View File
@@ -0,0 +1,24 @@
{
"name": "@lz-space/lambert-nuxt-blog",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxt/image": "^1.8.0",
"@nuxtjs/mdc": "^0.9.0",
"nuxt": "^3.13.0",
"remark-emoji": "^5.0.1",
"unhead": "^1.11.6",
"vue": "latest",
"vue-router": "latest"
},
"devDependencies": {
"@nuxtjs/tailwindcss": "^6.12.1"
}
}
+25
View File
@@ -0,0 +1,25 @@
<template>
<main
class="grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8"
>
<div class="text-center">
<p class="text-base font-semibold text-indigo-600">404</p>
<h1
class="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"
>
Page not found
</h1>
<p class="mt-6 text-base leading-7 text-gray-600">
Sorry, we couldnt find the page youre looking for.
</p>
<div class="mt-10 flex items-center justify-center gap-x-6">
<NuxtLink
to="/"
is="a"
class="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>Go back home</NuxtLink
>
</div>
</div>
</main>
</template>
@@ -0,0 +1,10 @@
<script setup lang="ts">
const md = `
# Hello MDC
`;
</script>
<template>
<MDC :value="md" tag="article" />
</template>
<!-- An error occurred after installation #183 -->
@@ -0,0 +1,7 @@
<template>
<h1>home</h1>
</template>
<script setup lang="ts"></script>
<style></style>
@@ -0,0 +1,9 @@
<template>
{{ route.params.id }}
</template>
<script setup lang="ts">
const route = useRoute();
// 访/posts/1route.params.id1
console.log(route.params.id);
</script>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@@ -0,0 +1 @@
@@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}
+4
View File
@@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}
+8 -2
View File
@@ -4,12 +4,18 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"gray-matter": "node .\\src\\gray_matter_demo.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"prompts": "^2.4.2"
"@lobehub/tts": "^1.25.1",
"flextree": "^1.0.1",
"flextree-sqlite-adapter": "^1.0.1",
"gray-matter": "^4.0.3",
"prompts": "^2.4.2",
"slash": "^5.1.0"
}
}
@@ -0,0 +1,80 @@
---
title: My Document
date: 2023-09-13
description: This is a document about...
---
# @mdit-vue/plugin-component
[![npm](https://badgen.net/npm/v/@mdit-vue/plugin-component)](https://www.npmjs.com/package/@mdit-vue/plugin-component)
[![license](https://badgen.net/github/license/mdit-vue/mdit-vue)](https://github.com/mdit-vue/mdit-vue/blob/main/LICENSE)
A [markdown-it](https://github.com/markdown-it/markdown-it) plugin to allow Vue components in markdown.
- Treats [vue built-in components](https://vuejs.org/api/built-in-components.html) and unknown HTML tags as vue components (markdown-it would treat them as inline tags by default).
- Allows vue [`@` directive](https://vuejs.org/api/built-in-directives.html#v-on) on native HTML tags.
## Install
```sh
npm i @mdit-vue/plugin-component
```
## Usage
This plugin will only take effects when the `html` option of markdown-it is enabled:
```ts
import { componentPlugin } from '@mdit-vue/plugin-component';
import MarkdownIt from 'markdown-it';
const md = MarkdownIt({ html: true }).use(componentPlugin, {
// options
});
const rendered = md.render(
`\
<!-- @ shorthand is supported -->
<Foo @click="onClick" />
<!-- multi-line syntax won't be wrapped with <p> -->
<Foo
class="foo"
:bar="bar"
/>
`,
);
```
## Options
### blockTags
- Type: `string[]`
- Default: `[]`
- Details:
Extra tags to be treated as block tags.
By default, all standard [HTML inline elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements) will be treated as inline tags (excluding [Vue built-in special elements](https://vuejs.org/api/built-in-special-elements.html)). All unknown elements will be assumed as Vue components, and will be treated as block tags (with slight differences).
In some cases (should be rare though) you might want to force some tags to behave like block tags, then you can use this option to specify the tag names.
Notice that this option is case-sensitive, and has higher priority than the [inlineTags](#inlinetags) option.
### inlineTags
- Type: `string[]`
- Default: `[]`
- Details:
Extra tags to be treated as inline tags.
By default, only standard [HTML inline elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements) will be treated as inline tags (excluding [Vue built-in special elements](https://vuejs.org/api/built-in-special-elements.html)). All unknown elements will be assumed as Vue components, and will be treated as block tags (with slight differences).
Treating Vue components like block tags would work as expected in most cases. However, in some cases you might want to force some tags to behave like inline tags, then you can use this option to specify the tag names.
Notice that this option is case-sensitive, and has lower priority than the [blockTags](#blocktags) option.
+204
View File
@@ -0,0 +1,204 @@
// // 1. 定义数据库
// // import SqliteAdapter from 'flextree-sqlite-adapter';
// // 定义一个异步函数来处理数据库的打开和SQL执行
// // async function initDatabase() {
// // const sqliteAdapter = new SqliteAdapter("org.db");
// // // 打开数据库连接
// // await sqliteAdapter.open();
// // // 执行SQL语句
// // try {
// // await sqliteAdapter.exec(`
// // CREATE TABLE IF NOT EXISTS org (
// // id INTEGER PRIMARY KEY AUTOINCREMENT,
// // name VARCHAR(60),
// // level INTEGER,
// // leftValue INTEGER,
// // rightValue INTEGER
// // );
// // `); // 注意:SQL语句末尾加上了分号
// // } catch (error) {
// // console.error('Failed to execute SQL:', error);
// // }
// // }
// // // 调用初始化函数
// // initDatabase().catch(console.error);
// // 第2步:创建树管理器
// import { FlexTreeManager } from 'flextree';
// import SqliteAdapter from 'flextree-sqlite-adapter';
// async function getNodeDetail() {
// const sqliteAdapter = new SqliteAdapter("org.db")
// await sqliteAdapter.open()
// const orgManager = await new FlexTreeManager("org",{
// adapter: sqliteAdapter
// })
// // 第3步:添加树节点
// // 创建一个根节点
// await orgManager.createRoot({
// name: "A公司"
// })
// // 添加组织架构的一级部门子节点
// await orgManager.addNodes([
// { name: "行政中心" },
// { name: "市场中心" },
// { name: "研发中心"}
// ])
// // 添加行政中心的部门子节点.
// const node1 = await orgManager.findNode({name:"行政中心"})
// await orgManager.addNodes( [
// { name: "总裁办" },
// { name: "人力资源部" },
// { name: "财务部" },
// { name: "行政部" },
// { name: "法务部" },
// { name: "审计部" }
// ],node1) // 添加为node的子节点
// // 获取所有节点
// await orgManager.getNodes()
// // 限定层级获取节点,仅获取第1-3层节点,不包含第4层及以下节点
// await orgManager.getNodes(3)
// // 根据id获取节点
// await orgManager.getNode(1)
// // 获取树根节点
// await orgManager.getRoot()
// const node = await orgManager.findNode({name:"行政中心"})
// // 获取name=行政中心的节点
// // 获取节点<行政中心>的子节点集
// await orgManager.getChildren(node)
// // 获取节点<行政中心>的所有后代节点集
// await orgManager.getDescendants(node)
// // 获取节点<行政中心>的所有后代节点集,包括自身
// await orgManager.getDescendants(node,{includeSelf:true})
// // 获取节点<行政中心>的所有后代节点集,包括限定层级
// await orgManager.getDescendants(node,{level:2})
// // 获取节点<行政中心>的子节点集,level=1相当于只获取直接子节点
// await orgManager.getDescendants(node,{level:1})
// // 获取节点<行政中心>的所有祖先节点集
// await orgManager.getAncestors(node)
// // 获取节点<行政中心>的父节点
// await orgManager.getParent(node)
// // 获取节点<行政中心>的所有兄弟节点集
// await orgManager.getSiblings(node)
// // 获取节点<行政中心>的所有兄弟节点集,包括自身
// await orgManager.getSiblings(node,{includeSelf:true})
// // 获取节点<行政中心>的前一个兄弟节点
// await orgManager.getNextSibling(node)
// // 获取节点<行政中心>的后一个兄弟节点
// await orgManager.getPrevSibling(node)
// }
// getNodeDetail().catch(console.error);
import { FlexTreeManager } from 'flextree';
import SqliteAdapter from 'flextree-sqlite-adapter';
async function getNodeDetail() {
try {
const sqliteAdapter = new SqliteAdapter("org.db");
await sqliteAdapter.open();
// 创建表(如果尚未创建)
await sqliteAdapter.exec(`
CREATE TABLE IF NOT EXISTS org (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(60),
level INTEGER,
leftValue INTEGER,
rightValue INTEGER
);
`);
const orgManager = new FlexTreeManager("org", {
adapter: sqliteAdapter
});
// 使用 write 方法包裹所有写操作
// await orgManager.write(async () => {
// // 创建一个根节点
// await orgManager.createRoot({
// name: "A公司"
// });
// // 添加组织架构的一级部门子节点
// await orgManager.addNodes([
// { name: "行政中心" },
// { name: "市场中心" },
// { name: "研发中心" }
// ]);
// // 添加行政中心的部门子节点.
// const node1 = await orgManager.findNode({ name: "行政中心" });
// await orgManager.addNodes([
// { name: "总裁办" },
// { name: "人力资源部" },
// { name: "财务部" },
// { name: "行政部" },
// { name: "法务部" },
// { name: "审计部" }
// ], node1); // 添加为node的子节点
// });
// 获取所有节点
console.log(await orgManager.getNodes());
// 限定层级获取节点,仅获取第1-3层节点,不包含第4层及以下节点
await orgManager.getNodes(3);
// 根据id获取节点
await orgManager.getNode(1);
// 获取树根节点
await orgManager.getRoot();
const node = await orgManager.findNode({ name: "行政中心" });
// 获取name=行政中心的节点
// 获取节点<行政中心>的子节点集
await orgManager.getChildren(node);
// 获取节点<行政中心>的所有后代节点集
await orgManager.getDescendants(node);
// 获取节点<行政中心>的所有后代节点集,包括自身
console.log(await orgManager.getDescendants(node, { includeSelf: true }));
// 获取节点<行政中心>的所有后代节点集,包括限定层级
await orgManager.getDescendants(node, { level: 2 });
// 获取节点<行政中心>的子节点集, level=1相当于只获取直接子节点
await orgManager.getDescendants(node, { level: 1 });
// 获取节点<行政中心>的所有祖先节点集
console.log(await orgManager.getAncestors(node));
// 获取节点<行政中心>的父节点
await orgManager.getParent(node);
// 获取节点<行政中心>的所有兄弟节点集
await orgManager.getSiblings(node);
// 获取节点<行政中心>的所有兄弟节点集,包括自身
await orgManager.getSiblings(node, { includeSelf: true });
// 获取节点<行政中心>的前一个兄弟节点
await orgManager.getNextSibling(node);
// 获取节点<行政中心>的后一个兄弟节点
// await orgManager.getPrevSibling(node);
} catch (error) {
console.error('An error occurred:', error);
}
}
// 异步调用 getNodeDetail 函数
getNodeDetail().catch(console.error);
@@ -0,0 +1,7 @@
const fs = require('fs');
const matter = require('gray-matter');
const fileContent = fs.readFileSync('./assets/gray_matter_demo.md', 'utf-8');
const {data, content} = matter(fileContent);
console.log(data);
// console.log(content);
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
import path from 'node:path';
import slash from 'slash';
const string = path.join('foo', 'bar');
// Unix => foo/bar
// Windows => foo\\bar
// slash(string);
// Unix => foo/bar
// Windows => foo/bar
console.log(string)
console.log(slash(string));
+15
View File
@@ -0,0 +1,15 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting'
],
parserOptions: {
ecmaVersion: 'latest'
}
}
+30
View File
@@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}
+7
View File
@@ -0,0 +1,7 @@
{
"recommendations": [
"Vue.volar",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
+45
View File
@@ -0,0 +1,45 @@
# vue-project
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
pnpm install
```
### Compile and Hot-Reload for Development
```sh
pnpm dev
```
### Type-Check, Compile and Minify for Production
```sh
pnpm build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
pnpm test:unit
```
### Lint with [ESLint](https://eslint.org/)
```sh
pnpm lint
```
+21
View File
@@ -0,0 +1,21 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
}
}
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+55
View File
@@ -0,0 +1,55 @@
{
"name": "@lz-space/vue-project",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"@types/markdown-it": "^14.1.2",
"github-markdown-css": "^5.6.1",
"leafer-editor": "^1.0.2",
"leafer-ui": "^1.0.2",
"markdown-it": "^14.1.0",
"pinia": "^2.1.7",
"vue": "^3.4.29",
"vue-router": "^4.3.3"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.8.0",
"@shikijs/markdown-it": "^1.14.1",
"@tsconfig/node20": "^20.1.4",
"@types/jsdom": "^21.1.7",
"@types/node": "^20.14.5",
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
"jsdom": "^24.1.0",
"less": "^4.2.0",
"npm-run-all2": "^6.2.0",
"postcss": "^8.4.41",
"prettier": "^3.2.5",
"tailwindcss": "^3.4.10",
"typescript": "~5.4.0",
"unplugin-vue-components": "^0.27.4",
"vite": "^5.3.1",
"vite-plugin-md": "^0.21.5",
"vite-plugin-vue-devtools": "^7.3.1",
"vitest": "^1.6.0",
"vue-tsc": "^2.0.21"
}
}
+7
View File
@@ -0,0 +1,7 @@
export default {
plugins: {
'postcss-import': {},
tailwindcss: {},
autoprefixer: {},
},
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+11
View File
@@ -0,0 +1,11 @@
declare module '*.vue' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
declare module '*.md' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}
+9
View File
@@ -0,0 +1,9 @@
<script setup lang="ts">
import { RouterView } from 'vue-router'
</script>
<template>
<RouterView />
</template>
<style scoped></style>
+59
View File
@@ -0,0 +1,59 @@
# vue-project
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
pnpm install
```
### Compile and Hot-Reload for Development
```sh
pnpm dev
```
### Type-Check, Compile and Minify for Production
```sh
pnpm build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
pnpm test:unit
```
### Lint with [ESLint](https://eslint.org/)
```sh
pnpm lint
```
```js
import MarkdownIt from 'markdown-it'
import Shiki from '@shikijs/markdown-it'
const md = MarkdownIt()
md.use(await Shiki({
themes: {
light: 'vitesse-light',
dark: 'vitesse-dark',
}
}))
```
+1
View File
@@ -0,0 +1 @@
# {{ name }}
+86
View File
@@ -0,0 +1,86 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

+35
View File
@@ -0,0 +1,35 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}
@@ -0,0 +1,41 @@
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
@@ -0,0 +1,88 @@
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
you need to test your components and web pages, check out
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
>Cypress Component Testing</a
>.
<br />
More instructions are available in <code>README.md</code>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
Discord server, or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also subscribe to
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
the official
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
twitter account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>
@@ -0,0 +1,87 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>
@@ -0,0 +1,11 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import HelloWorld from '../HelloWorld.vue'
describe('HelloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
expect(wrapper.text()).toContain('Hello Vitest')
})
})
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>
@@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+11
View File
@@ -0,0 +1,11 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import HelloWorld from './components/HelloWorld.vue'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.component('HelloWorld', HelloWorld)
app.mount('#app')
+31
View File
@@ -0,0 +1,31 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
},
{
path: '/leafer',
name: 'leafer',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/LeaferView.vue')
}
]
})
export default router
@@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
@@ -0,0 +1,7 @@
<template>
<Test name="lambert" />
</template>
<script setup>
import Test from './Test.md'
</script>
@@ -0,0 +1,63 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import MarkdownIt from 'markdown-it'
import Readme from '../assets/README.md?raw'
import Test from './Test.md'
import Shiki from '@shikijs/markdown-it'
import 'github-markdown-css'
// Readme
const markdownItOptions = {
html: true,
linkify: true,
typographer: true
}
const content = ref('')
const markdownIt = new MarkdownIt(markdownItOptions)
const md = computed(() => markdownIt.render(content.value))
onMounted(async () => {
markdownIt.use(
await Shiki({
themes: {
light: 'vitesse-light',
dark: 'github-dark'
}
})
)
content.value = Readme
})
</script>
<template>
<main>
<textarea v-model="content" class="text-input"> </textarea>
<div class="markdown-box">
<!-- <div class="md-raw" :v-pre="md"></div> -->
<div class="md-preview">
<div class="md-preview-box" v-html="md"></div>
</div>
</div>
</main>
<Test name="lambert" />
</template>
<style lang="less" scoped>
main {
display: flex;
width: 100%;
height: 100vh;
gap: 20px;
> .text-input {
width: 40%;
&::focus {
outline: none;
}
}
> .markdown-box {
flex: 1 0 0;
}
}
</style>
@@ -0,0 +1,35 @@
<template>
<h1>leafer UI</h1>
</template>
<script setup lang="ts">
import { Leafer, Rect, PointerEvent } from 'leafer-ui'
const leafer = new Leafer({ view: window })
const rect = new Rect({
x: 100,
y: 100,
width: 200,
height: 200,
fill: '#32cd79', //
stroke: 'black', //
strokeWidth: 2, //
draggable: true
})
leafer.add(rect)
function onEnter(e: PointerEvent) {
;(e.current as Rect).fill = '#42dd89'
}
function onLeave(e: PointerEvent) {
;(e.current as Rect).fill = '#32cd79'
}
rect.on(PointerEvent.ENTER, onEnter)
rect.on(PointerEvent.LEAVE, onLeave)
</script>
<style></style>
+3
View File
@@ -0,0 +1,3 @@
# 你好
<HelloWorld></HelloWorld>
+11
View File
@@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
+22
View File
@@ -0,0 +1,22 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": [
"env.d.ts",
"src/**/*",
"src/**/*.vue",
"shims.d.ts"
],
"exclude": [
"src/**/__tests__/*"
],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.vitest.json"
}
]
}
+19
View File
@@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.app.json",
"exclude": [],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
"lib": [],
"types": ["node", "jsdom"]
}
}
+27
View File
@@ -0,0 +1,27 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vueDevTools from 'vite-plugin-vue-devtools'
import Markdown from 'vite-plugin-md'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
include: [/\.vue$/, /\.md$/] // <--
}),
Markdown({
builders: []
})
// vueJsx(),
// vueDevTools()
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
+14
View File
@@ -0,0 +1,14 @@
import { fileURLToPath } from 'node:url'
import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(
viteConfig,
defineConfig({
test: {
environment: 'jsdom',
exclude: [...configDefaults.exclude, 'e2e/**'],
root: fileURLToPath(new URL('./', import.meta.url))
}
})
)
+15152 -32
View File
File diff suppressed because it is too large Load Diff