Skip to content

Commit 240a1cf

Browse files
authored
Migrate from Vue 2 to Vue 3 (#2622)
Migrate from Vue 2 to Vue 3 Vue 2 has reached end-of-life in 2023, limiting maintainability, extensibility of MarkBind. This commit migrates the codebase (core, CLI, and vue-components) to Vue 3.3.11. The migration includes major updates to server-side rendering logic, hydration, component syntax, bundling setup (Webpack), and test configurations. Vue 2 dependencies and deprecated APIs have been replaced accordingly. Snapshot tests and test site output have also been updated to reflect new Vue version. This migration improves future extensibility and compatibility with modern tooling and Vue ecosystem packages.
1 parent e3b6dd4 commit 240a1cf

File tree

335 files changed

+23956
-21019
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

335 files changed

+23956
-21019
lines changed

docs/_markbind/layouts/devGuide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* Design :expanded:
2525
* [Project Structure]({{baseUrl}}/devGuide/design/projectStructure.html)
2626
* [Architecture]({{baseUrl}}/devGuide/design/architecture.html)
27+
* [Vue Integration]({{baseUrl}}/devGuide/design/vueIntegration.html)
2728
* [Server Side Rendering]({{baseUrl}}/devGuide/design/serverSideRendering.html)
2829
* GitHub Actions
2930
* [Overview]({{baseUrl}}/devGuide/githubActions/overview.html)

docs/devGuide/design/projectStructure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ We forked it from the original [yuche/vue-strap](https://github.com/yuche/vue-st
100100

101101
**The key dependencies used are:**
102102

103-
* [Vue.js](http://vuejs.org/) (required ^v2.x.x, test with v2.6.14).
103+
* [Vue.js](http://vuejs.org/) (required ^v3.x.x, test with v3.3.11).
104104

105105
* [Bootstrap CSS](http://getbootstrap.com/) (required 5.x.x, test with 5.1.3). MarkBind's Vue components doesn't depend on a very precise version of Bootstrap.
106106

docs/devGuide/design/serverSideRendering.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ Here is a short list of questions to check your understanding of Vue:
3939

4040
If there are any doubts regarding the questions above, here are some good resources to refer to:
4141

42-
* [Vue Official Documentation](https://vuejs.org/v2/guide)
42+
* [Vue 3 Official Documentation](https://vuejs.org/guide/introduction)
43+
* [Vue Server-Side Rendering (SSR)](https://vuejs.org/guide/scaling-up/ssr.html#server-side-rendering-ssr)
4344
* [Evan You - Inside Vue Components - Laracon EU 2017](https://www.youtube.com/watch?v=wZN_FtZRYC8&ab_channel=LaraconEU)
4445
</box>
4546

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
{% set title = "Vue Integration" %}
2+
<span id="title" class="d-none">{{ title }}</span>
3+
4+
<frontmatter>
5+
title: "{{ title }}"
6+
layout: devGuide.md
7+
pageNav: default
8+
</frontmatter>
9+
10+
# {{ title }}
11+
12+
<div class="lead mb-2">
13+
This page provides an overview of how Vue is integrated into MarkBind's architecture, including its usage in Server-side Rendering (SSR), setup, Client-side Hydration, and the abstraction layers involved in rendering content.
14+
</div>
15+
16+
## Overview
17+
18+
Vue is the frontend framework used to power MarkBind’s dynamic UI components and client-side interactivity. Vue components are rendered both on the server and browser depending on the context:
19+
20+
* **Server-side Rendering (SSR)**: Used for better performance, SEO, and to reduce Flash-of-Unstyled-Content (FOUC).
21+
* **Client-side Hydration**: After the server-rendered HTML is delivered to the browser, the Vue app takes over to hydrate the static HTML. This process involves attaching event listeners and enabling interactivity, ensuring the page becomes fully interactive.
22+
* **Client-side Rendering (CSR)**: Used to load additional content dynamically, enabling interactivity after the page loads. For example, components like `<panel>` with `preload='false'` are dynamically loaded via CSR.
23+
24+
<box type="info">
25+
26+
For more details on how MarkBind uses Vue for server-side rendering and hydration (and the common caveats developers should be aware of, including hydration issues), refer to the [Server Side Rendering](serverSideRendering.md) page.
27+
</box>
28+
29+
## Custom Vue Components
30+
31+
MarkBind uses a library of custom Vue components, which are a mix of Bootstrap-based components and those tailored for educational websites. These components are designed to meet the specific needs of MarkBind's use cases. For a list of some components and directives added for MarkBind's use, refer to the [UI components library](projectStructure.md#ui-components-library).
32+
33+
Vue components are particularly useful for implementing complex features and interactivity that go beyond what static HTML and Markdown can provide. For details implementing a MarkBind component as a Vue component, refer [here](../development/writingComponents.md#vue-components).
34+
35+
### What Vue is Used For
36+
37+
Vue components in MarkBind are used for:
38+
39+
* **Encapsulating Complex Behavior**: Vue components allow for reusable, self-contained logic and interactivity.
40+
41+
* **Dynamic Content Loading**: Components like `<panel>` with `preload='false'` use Vue to dynamically load content after the page is rendered.
42+
43+
* **Interactive Features**: Components like modals, quizzes, and tooltips rely on Vue for their interactive behavior.
44+
45+
* **Integration with External Libraries**: Vue components can wrap external libraries (e.g., Vue Final Modal for modals) to provide a seamless user experience.
46+
47+
## Custom Vue Directives
48+
49+
In addition to custom Vue components, MarkBind also makes use of custom Vue directives to provide enhanced interactivity and DOM manipulation behaviors. These directives encapsulate logic that operates directly on the DOM elements, complementing the declarative nature of Vue templates.
50+
51+
* For example, the `v-closeable` directive, used here to <a tags="environment--combined" href="/userGuide/reusingContents.html#allowing-users-to-remove-some-contents">remove content</a><a tags="environment--dg" href="https://markbind.org/userGuide/reusingContents.html#allowing-users-to-remove-some-contents">remove content</a>, provides functionality to toggle the visibility of content sections. It dynamically wraps the directive’s target content in a container, adds a close button and show label, and sets up the necessary event handlers.
52+
53+
## Markbind's Server-Side Rendering (SSR) using Vue
54+
55+
MarkBind leverages [Server-side Rendering (SSR)](https://vuejs.org/guide/scaling-up/ssr.html#server-side-rendering-ssr) to pre-render Vue components into static HTML during the build process. This pre-rendered HTML is served to the client, reducing the time required for the page to become interactive. It also ensures that the content is visible immediately, even before JavaScript is executed, eliminating the Flash-of-Unstyled-Content (FOUC).
56+
57+
### SSR Workflow Overview
58+
59+
MarkBind uses Server-side Rendering (SSR) to pre-render page content into static HTML during the build process. Here is an overview of the workflow during the build process:
60+
61+
1. **Server-side Compilation of Render Function**: MarkBind takes the final HTML page content (product of [Content Processing Flow](architecture.md#content-processing-flow)) and compiles it into a render function using the `compileTemplate` function from [`vue/compiler-sfc`](https://www.npmjs.com/package/@vue/compiler-sfc). This render function is saved as a JavaScript file (e.g., `<page-name>.page-vue-render.js`) in the same directory as the HTML file. This script file is served alongside the static HTML and injected into the final page template, for the purpose of hydration. It is also directly used in SSR to generate the static HTML.
62+
63+
64+
1. **Server-side Render of HTML**: On the server, MarkBind initializes a separate Vue instance solely for SSR. It registers the custom Vue components and directives bundled by core-web onto this Vue instance. The Vue instance uses the render function previously compiled to generate static HTML via the `renderToString` function from [`vue/server-renderer`](https://www.npmjs.com/package/@vue/server-renderer).
65+
66+
67+
1. **Client-side Hydration**: On the client side (e.g., a web browser), the HTML is served with the injected render function. The Vue app is initialized using `createSSRApp` in index.js. Thereafter, the Vue instance takes over and hydrates the static HTML. For more details on hydration and potential hydration issues, refer to [Client-side Hydration](serverSideRendering.md#client-side-hydration).
68+
69+
70+
71+
## Dynamic Content Loading (Client-Side Rendering)
72+
73+
In specific cases, MarkBind uses **Client-side Rendering (CSR)** exclusively to dynamically load content. A key example is the `<panel>` component. When the `src` attribute is set to a remote page to be loaded as the content, the `preload` attribute is `false` by default. As the content is not preloaded and loaded dynamically after the page is rendered, it wil conduct CSR, where:
74+
* **Generated Separately**: Content is generated outside the main content flow.
75+
* **Mounted as CSR-Only Vue Apps**: Content is mounted as a separate Vue app after being loaded into the browser.
76+
77+
## Detailed Vue App Setup and Execution Flow
78+
79+
<box type="tip" seamless>
80+
81+
This section provides a deeper look into how MarkBind sets up and initializes the Vue application on the client side. It is intended for developers who are interested in understanding the underlying execution flow in more detail.
82+
</box>
83+
84+
The core Vue app is initialized in `core-web/src/index.js`, which serves as the entry point for the client-side Vue application. This file is part of the `packages/core-web/` library — a client-side bundle generated by MarkBind. It contains initialization scripts, the bundled UI components library, and shared logic used across all rendered sites. This bundle is included in the final site HTML and executed in the web browser when a user visits a generated MarkBind site.
85+
86+
### **Initialization Flow**
87+
1. **Loading the Core-Web Bundle**:
88+
* The SSR-generated HTML includes a `<script>` tag that loads the `core-web` bundle.
89+
* When this script runs in the browser, it imports and executes `core-web/src/index.js`.
90+
91+
2. **Setting Up the Vue App**:
92+
* The entry file (`index.js`) defines the `setup()` function, which is exported and later called in the HTML template (`page.njk`).
93+
* Inside `setup()`, the Vue app is initialized using `createSSRApp()`, where the `render` function was previously attached as a global variable, generated during SSR and served in `<page-name>.page-vue-render.js`. (injected via SSR in `PageVueServerRenderer.ts`).
94+
95+
3. **Registering the custom MarkBind Vue Plugin**:
96+
* The global MarkBind plugin is registered, which sets up **Global Vue components** (e.g., `<modal>`, `<tooltip>`), **Custom directives** (e.g., `v-tooltip`, `v-closeable` and **Global configuration** (e.g., `$vfm` for modals).
97+
98+
4. **Mounting the Vue App**:
99+
* The Vue app is mounted onto the pre-rendered SSR DOM element (typically `#app`) using `app.mount('#app')`.
100+
* This step hydrates the server-rendered HTML into an interactive client-side app.
101+
102+
5. **Running Client-Only Setup Routines**:
103+
* After the app is mounted, the mounted() lifecycle hook executes client-only setup routines, such as:
104+
* **Header style detection**: Detects and applies styles for sticky headers.
105+
* **Scrolling to anchor headings**: Ensures the page scrolls to the correct section based on the URL hash.
106+
* **Restoring bypassed `<style>` tags**: Replaces placeholder `<script>` tags with their original `<style>` tags.
107+
* **Dynamic search setup**: Fetches and initializes search data from `siteData.json`.
108+
109+
This architecture allows MarkBind to combine fast server-side initial rendering with interactive client-side behavior, ensuring a seamless and performant experience.

docs/devGuide/development/writingComponents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ It is important to consider reactivity when implementing a component that may ha
115115

116116
Components should be compatible with SSR (Server-Side Rendering).
117117
Minimally, there should be no SSR issues (viewable from the browser console), though a lack of warnings does **not** mean that there are no SSR problems.
118-
A guide on SSR for MarkBind can be found [here]({{baseUrl}}/devGuide/design/serverSideRendering.html).
118+
For more information on SSR in MarkBind, refer to the [Vue Integration guide]({{baseUrl}}/devGuide/design/vueIntegration.html) and [Server-Side Rendering design guide]({{baseUrl}}/devGuide/design/serverSideRendering.html).
119119

120120
Vue-specific tips for resolving SSR issues:
121121
* The `mount` and `beforeMount` lifecycle hooks will only be executed on the client, not the server

0 commit comments

Comments
 (0)