English
uni-app
has supported vue 3.0
development, see https://ask.dcloud.net.cn/article/37834
uni-app
officially provides simple and easy-to-use SSR support based on vue 3.0 & uniCloud
.
news.dcloud.io is a news system based on uni-app & uniCloud
. You can view the source code through a browser, which is an example of a server-side rendering (SSR) site.
By default, uni-app outputs Vue components in the client to generate DOM and operate DOM. However, it is also possible to render the same component as HTML strings on the server side, send them directly to the browser, and finally "activate" these static markers as fully interactive applications on the client.
The uni-app application rendered by the server can also be considered as "homogeneous" or "universal", because most of the application code can run on the server and the clients.
The advantages of server-side rendering (SSR) over traditional SPA (Single-Page Application) mainly lie in:
Better SEO, searching engine crawling tool can directly view the fully rendered page.
Faster time-to-content, especially for slow network conditions or slow running devices. No need to wait for all JavaScript to be downloaded and executed before displaying the marker rendered by the server, so your users will see the fully rendered page more quickly. Generally, it can produce better user experience, and as far as those applications where "time-to-content is directly related to conversion rate" are concerned, server-side rendering (SSR) is of great significance.
There are some trade-offs when using server-side rendering (SSR):
Limited by development conditions. Browser-specific code can only be used in some lifecycle hook functions. Some external library may require special handling to run in the server rendering application.
More requirements related to build settings and deployment. Unlike the fully static single page application (SPA) that can be deployed on any static file server, the server rendering application needs to be in the Node.js server running environment.
More server-side load. The fully rendered application in Node.js obviously takes up more CPU resources (CPU-intensive) than the server that only provides static files. Therefore, if you expect to use it in a high traffic environment, please prepare the corresponding server load and use the caching strategy wisely.
幸运的是,以上问题,uniCloud 均为您提供了解决方案
Before further introduction, let's take a moment to discuss the constraints when writing "generic" code - i.e., the code running on the server and the clients. Because of the differences between use cases and platform APIs, our code will not be exactly identical when running in different environments. So here we will lay out the key points you need to understand.
In the client-only apps, each user will use a new application instance in their respective browser. For server-side rendering, we also hope that each request should be a brand-new and independent application instance, so that there will be no cross-request state pollution caused by cross-requests.
Because the actual rendering process requires certainty, we will also "pre-fetch" data on the server - this means our application has already finished parsing its state by the time we start rendering. In other words, it is unnecessary to make the data responsive on the server, so that it is disabled by default. Disabling responsive data can also avoid the performance overhead of converting "data" into "responsive objects".
Since there is no dynamic update, among all lifecycle hook functions, only beforeCreate and created will be called in the process of server-side rendering (SSR). That is to say, the code in any other lifecycle hook functions (such as beforeMount or mounted) will only be executed on the client.
In addition, it is noteworthy that you should avoid the code that generates global side effects during the lifecycles of beforeCreate and created, such as using setInterval to set the timer in them. In the pure client-side only code, we can set a timer and then destroy it during the lifecycle of beforeUnmount or unmounted. However, considering that the destroy hook function will not be called during SSR, the timer will remain forever. To avoid this situation, please move the side-effect code to the beforeMount or mounted lifecycle.
Common code cannot accept the APIs of specific platform, so if the browser-only global variables like window or document are directly applied in your code, errors may be thrown when executed in Node.js, and vice versa.
For browsers-only APIs, the usual way is to lazily access them in the "client-only" lifecycle hook functions.
Please note that it can be tricky to integrate a third-party library into the server-rendered application if it is not written in the common usage above. You may have to mock some global variables to make it work, but this is just what hack does and may interfere with the environment detection codes of other libraries.
During server-side rendering (SSR), we are essentially rendering a "snapshot" of our application, so if the application depends on some asynchronous data, we need to prefetch and parse these data before starting the rendering process.
Another concern is that on the client, it is necessary to get the same data as the server-side application before mounting to the client application - otherwise, the mixing may fail because the client application uses a different state from the server-side application.
To solve this problem, the obtained data needs to be located outside the view component, i.e., being placed in a special prefetch data store or "state container". First, on the server side, we can prefetch data before rendering and fill the data into the store. In addition, we will serialize and inline the states in HTML. In this way, before mounting to the client application, the inline status can be obtained directly from the store.
ssrRef
(equivalent to ref of vue3) and shallowSsrRef
(equivalent to shallowRef of vue3)) provided by @dcloudio/uni-app
to ensure the consistency between server data and client data.
ssrRef
and shallowSsrRef
in the non-component lifecycle, the data will be stored globallyssrRef
and shallowSsrRef
support the second parameter as the key of the dataExample:
import { ssrRef } from '@dcloudio/uni-app'
const categories = ssrRef([], 'categories'); // 在非组件生命周期中使用时,为全局数据,可以跨组件跨页面使用
export default {
setup (){
const items = ssrRef([]); // 在生命周期中使用时,为组件级别作用域,不指定第二个参数key的情况下,编译器会自动补充(默认,以文件+函数位置做base64生成key)
return { items, categories }
}
}
Example:
import { createStore } from 'vuex'
// Simulate the interface to acquire data
function fetchItem(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id,
title: 'title' + id,
})
}, 300)
})
}
export default () => {
return createStore({
state() {
return {
items: {},
}
},
actions: {
fetchItem({ commit }, id) {
return fetchItem(id).then((item) => {
commit('setItem', { id, item })
})
},
},
mutations: {
setItem(state, { id, item }) {
state.items[id] = item
},
},
})
}
Then modify main.js
import { createSSRApp } from 'vue'
import App from './App.vue'
import createStore from './store'
export function createApp() {
const app = createSSRApp(App)
const store = createStore() // 创建 store
app.use(store)
return {
app,
store,// 必须返回 store
}
}
Use in pages or components
<template>
<text v-if="item">{{ item.title }}</text>
<text v-else>...</text>
</template>
<script>
const id = 1;// 模拟ID
export default {
computed: {
item() {
return this.$store.state.items[id]
}
},
serverPrefetch() {// 服务端预取数据的生命周期
return this.fetchItem()
},
mounted() { // 仅客户端执行的生命周期
if (!this.item) { // 判断服务端是否已正常获取,若未获取,重新调用加载数据
this.fetchItem()
}
},
methods: {
fetchItem() {
return this.$store.dispatch('fetchItem', id)
}
}
}
</script>
For the project created by HBuilderX, check Enable SSR
in the run menu and run it to the browser.
The project created by cli can be run with npm run dev:h5:ssr
Releasing ssr will get two parts: the cloud part and the static resource part. To deploy in uniCloud needs to deploy the cloud part into the cloud function, and the static resource part into the front-end web hosting.
uniCloud
Pre-step
Be sure to complete the pre-step before proceeding with the subsequent operation
cloudfunctions/uni-ssr/package.json
内容,将uni-app相关依赖的版本调整为发行项目是的依赖版本Compile and release
Use HBuilderX to release and deploy automatically
Requires HBuilderX version 3.5.1
and above, supports deployment to Alibaba Cloud and Tencent Cloud previously only supported automatic deployment to Alibaba Cloud
Configure base
in vite.config.js
to be the address of front-end web hosting
import {
defineConfig
} from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
// https://vitejs.dev/config/
export default defineConfig({
base: 'https://static-xxxx.bspapp.com/', // uniCloud 前端网页托管资源地址(主要是应用编译后的js,图片等静态资源,可以配置为二级目录)
plugins: [
uni(),
],
ssr: {
format: 'cjs'
}
})
Through HBuilderX
Issuance Menu -> Website PC-Web or Mobile H5
, check ssr
, check Deploy compiled resources on uniCloud front-end web hosting
配置uni-ssr
的云函数URL化路径,请参考文档:云函数URL化
Manual release and deployment
Configure base
in vite.config.js
as Front-end website hosting
address
import {
defineConfig
} from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
// https://vitejs.dev/config/
export default defineConfig({
base: 'https://static-xxxx.bspapp.com/', // uniCloud 前端网页托管资源地址(主要是应用编译后的js,图片等静态资源,可以配置为二级目录)
plugins: [
uni(),
],
ssr: {
format: 'cjs'
}
})
编译:
cli工程:npm run build:h5:ssr
或通过HBuilderX 3.1.16及以上版本
的发行菜单->网站 PC-Web或手机H5
、勾选ssr
非cli工程:通过HBuilderX 3.1.16及以上版本
的发行菜单->网站 PC-Web或手机H5
、勾选ssr
Upload the compiled resources in dist/build/h5/client
to the front-end web hosting. The free Alibaba Cloud service space is recommended.
uni-ssr
cloud functionCopy the compiled dist/build/h5/server
directory to the uni-ssr
cloud function root directory and upload it.
uni-ssr
的云函数URL化路径,请参考文档:云函数URL化ssrRef
[Vue warn]: Hydration node mismatch:
- Client ***
- Server ***
Hydration completed but contains mismatches