При разработке веб-приложений на это всегда влияет длительное время загрузки первого экрана.Основным решением является отображение эффекта диаграммы загрузки до завершения загрузки, и некоторые крупные компании настраивают архитектуру рендеринга на стороне сервера для решения этой проблемы. . . . Учитывая ряд проблем, которые необходимо решить с помощью ssr, все больше и больше приложений используют метод «скелетного экрана» для улучшения взаимодействия с пользователем.
Торговый центр Сяоми:
Сначала проанализируйте процесс загрузки контента на странице Vue.
Запись index.html в проекте vue имеет только простое содержимое:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
</head>
<body>
<div id="root">
</div>
<script type="text/javascript" src="bundle.js"></script></body>
</body>
</html>
Когда js выполняется, дом, отображаемый vue, будетdiv#rootполностью заменен.
мы вdiv#rootДобавьте смоделированный экран-скелет в инструмент разработчика Chrome и настройте скорость сети:
<div id="root">
这里是骨架屏
</div>
Из этого видно, что содержимое каркасного экрана непосредственно вставляется вdiv#rootСкелетный экран может быть реализован в формате .
2. Используйте vue-server-renderer для реализации каркасного экрана
Нам нужно, чтобы каркасный экран тоже был отдельным.vueфайл, поэтому нам нужно использоватьvue-server-renderer. Учащиеся, знакомые с рендерингом vue на стороне сервера, должны знать, что этот плагин может упаковать проект vue в пакет на стороне узла, а затем пакет генерирует соответствующий HTML-код.
Первый — сгенерировать проект:
.
├── build
│ ├── webpack.config.client.js
│ └── webpack.config.server.js
├── src
│ └── views
│ ├── index
│ │ └── index.vue
│ ├── skeleton
│ │ └── skeleton.vue
│ ├── app.vue
│ ├── index.js
│ └── skeleton-entry.js
├── index.html
└── skeleton.js
└── package.json
Рендеринг Server vue обычно используетvue-server-rendererУпакуйте весь проект в бандл на стороне узла, а здесь нам нужен только html со скелетным экраном, поэтому будет отдельный файл входа скелетного экранаskeleton-entry.js, скелетный экран, упаковывающий конфигурацию веб-пакетаwebpack.config.server.js,а такжеskeleton.jsФункция состоит в том, чтобы записать пакет, упакованный webpack, вindex.htmlсередина.
//skeleton-entry.js
import Vue from 'vue'
import Skeleton from './views/skeleton/skeleton.vue'
export default new Vue({
components: {
Skeleton
},
template: '<skeleton />'
})
//webpack.config.server.js
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
module.exports = {
mode: process.env.NODE_ENV,
target: 'node',
entry: path.join(__dirname, '../src/skeleton-entry.js'),
output: {
path: path.join(__dirname, '../server-dist'),
filename: 'server.bundle.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}
]
},
externals: Object.keys(require('../package.json').dependencies),
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins: [
new VueLoaderPlugin(),
new VueSSRServerPlugin({
filename: 'skeleton.json'
})
]
}
Конфигурация веб-пакета скелетного экрана — это сторона узла, поэтому ее необходимоtarget: 'node' libraryTarget: 'commonjs2'. существуетVueSSRServerPlugin, указывает имя выходного файла json. При выполнении webpack будет создан файл в каталоге /server-distskeleton.jsonфайл, этот файл записывает содержимое и стиль экрана скелета и будет предоставленvue-server-rendererиспользовать.
//skeleton.js
const fs = require('fs')
const path = require('path')
const createBundleRenderer = require('vue-server-renderer').createBundleRenderer
// 读取`skeleton.json`,以`index.html`为模板写入内容
const renderer = createBundleRenderer(path.join(__dirname, './server-dist/skeleton.json'), {
template: fs.readFileSync(path.join(__dirname, './index.html'), 'utf-8')
})
// 把上一步模板完成的内容写入(替换)`index.html`
renderer.renderToString({}, (err, html) => {
fs.writeFileSync('index.html', html, 'utf-8')
})
Обратите внимание, что в качестве html-файла шаблона вам нужно добавить заполнитель , где записывается содержимое.В этом примере напишите его в div#root:
<div id="root"> <!--vue-ssr-outlet--> </div>
окончательное исполнениеnode skeletonСкелетный экран vue может быть реализован.
окончательныйindex.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Document</title>
<style data-vue-ssr-id="a7049cb4:0">
.skeleton[data-v-61761ff8] {
position: relative;
height: 100%;
overflow: hidden;
padding: 15px;
box-sizing: border-box;
background: #fff;
}
.skeleton-nav[data-v-61761ff8] {
height: 45px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-swiper[data-v-61761ff8] {
height: 160px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-tabs[data-v-61761ff8] {
list-style: none;
padding: 0;
margin: 0 -15px;
display: flex;
flex-wrap: wrap;
}
.skeleton-tabs-item[data-v-61761ff8] {
width: 25%;
height: 55px;
box-sizing: border-box;
text-align: center;
margin-bottom: 15px;
}
.skeleton-tabs-item span[data-v-61761ff8] {
display: inline-block;
width: 55px;
height: 55px;
border-radius: 55px;
background: #eee;
}
.skeleton-banner[data-v-61761ff8] {
height: 60px;
background: #eee;
margin-bottom: 15px;
}
.skeleton-productions[data-v-61761ff8] {
height: 20px;
margin-bottom: 15px;
background: #eee;
}
</style></head>
<body>
<div id="root">
<div data-server-rendered="true" class="skeleton page" data-v-61761ff8><div class="skeleton-nav" data-v-61761ff8></div> <div class="skeleton-swiper" data-v-61761ff8></div> <ul class="skeleton-tabs" data-v-61761ff8><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li><li class="skeleton-tabs-item" data-v-61761ff8><span data-v-61761ff8></span></li></ul> <div class="skeleton-banner" data-v-61761ff8></div> <div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div><div class="skeleton-productions" data-v-61761ff8></div></div>
</div>
</body>
</html>
Взгляните на эффект:
Эффект по-прежнему широкий.
конец
В начале статьи мобильная страница Xiaomi Mall использует этот метод, разница в том, что ее скелетный экран представляет собой изображение base64.
больше оvue-server-rendererПожалуйста, нажмите на содержаниеvue-ssr
Код статьи был синхронизирован сGithub, добро пожаловать, чтобы проверить ~