当初的利用开发,基本上都是前后端拆散的,前端支流框架有 SPA、MPA 等,那么解决页面渲染、白屏工夫成为首要关注的点
webpack 能够按需加载,减小首屏须要加载代码的体积;
应用 CDN 技术、动态代码等缓存技术,能够减小加载渲染的时长
问题:然而首页仍然存在加载、渲染期待时长的问题。那么如何从视觉效果上减小首屏白屏的工夫呢?
骨架屏:举个例子:其实就是在模版文件中 id=app 容器上面写想要展现的成果,在 new Vue(option)之后,该 id 下的内容就被替换了(这时候,可能 Vue 编译生成的内容还没有挂载。因为 new Vue 的时候会进行一系列的初始化,这也须要消耗工夫的)。这样就能够从视觉上减小白屏的工夫
骨架屏的实现形式
1、间接在模版文件 id=app 容器上面,写进想要展现的成果 html
2、间接在模板文件 id=app 容器上面,用图片展现
3、应用 vue *提供的 webpack 插件
4、主动生成并且主动插入动态骨架屏
形式 1 和形式 2 存在的缺点:针对不同入口,展现的成果都一样,导致不能灵便的针对不同的入口,展现不同的款式
形式 3 能够针对不同的入口展现不同的成果。(本质也是先通过 * 生成一个 json 文件,而后将 json 文件内容注入到模板文件的 id=app 容器下)
计划一、间接在模版文件 id=app 容器上面,写进想要展现的成果 html
在根目录的模版文件内写进内容,如红色圈进去的中央
在浏览器关上我的项目
在调用 new Vue 之前的展现成果(只是做了个简略成果,不喜勿喷):
能够看到 elements 中 id=app 的容器下内容,就是咱们写进的骨架屏成果内容
在看下调了 new Vue 之后的成果,id=app 容器下的内容被 vue 编译生成的内容替换了
计划二、间接在模板文件 id=app 容器上面,用图片展现(这个就不做展现了)
计划三、应用 vue *提供的 webpack 插件:即用.vue 文件实现骨架屏
在计划一的根底上,将骨架屏的代码抽离进去,不在模版文件外面书写代码,而是在 vue 文件外面书写游戏成果代码,这样便于保护
1、在根目录下建一个 skeleton 文件夹,在该目录下创立文件 App.vue 文件(根组件,相似 Vue 我的项目的 App.vue)、home.skeleton.vue(首页骨架屏展现成果的代码,相似 Vue 我的项目写的路由页面)、skeleton-entry.js(入口文件相似 Vue 我的项目的入口文件)、plugin/server-plugin.js(vue-server-renderer 包提供了 server-plugin 插件,从外面将代码拷贝进去)
home.skeleton.vue(首页骨架屏展现成果的代码)
<template>
<div class=”skeleton-home”>
<div> 加载中 …</div>
</div>
</template>
<style>
.skeleton-home {
width: 100vw;
height: 100vh;
}
</style>
App.vue(根组件)
<template>
<div id=”app”>
<!– 根组件 –>
<home style=”display:none” id=”homeSkeleton”></home>
</div>
</template>
<script>
import home from ‘./home.skeleton.vue’
export default{
components: {
home
}
}
</script>
<style>
#app {
font-family: ‘Avenir’, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
*{
padding: 0;
margin: 0;
}
<
var prefix = “[vue-server-renderer-webpack-plugin]”; var prefix = www.sangpi.comvar warn = exports.warn = function (msg) {return console.error(red((prefix + ” ” + msg + “\n”))); };
var tip = exports.tip = function (msg) {return console.log(yellow((prefix + ” ” + msg + “\n”))); };
var validate = function (compiler) {
if (compiler.options.target !== ‘node’) {
warn(‘webpack config target
should be “node”.’);
}
if (compiler.options.output && compiler.options. !== ‘commonjs2’) {
warn(‘webpack config “ should be “commonjs2”.’);
}
if (!compiler.options.externals) {
tip(
’It is recommended to externalize dependencies in the server build for ‘ +
’better build performance.’
);
}
};
var VueServerPlugin = function VueServerPlugin (options) {
if (options === void 0) options = {};
this.options = Object.assign({
filename: ‘vue-*-server-bundle.json’
}, options);
};
Vue*ServerPlugin.prototype.apply = function apply (compiler) {
var this$1 = this;
validate(compiler);
compiler.plugin(’emit’, function (compilation, cb) {
var stats = compilation.getStats().toJson();
var entryName = Object.keys(stats.entrypoints)[0];
var entryAssets = stats.entrypoints[entryName].assets.filter(isJS);
if (entryAssets.length > 1) {
throw new Error(
”Server-side bundle should have one single entry file. ” +
”Avoid using CommonsChunkPlugin in the server config.”
)
}
var entry = entryAssets[0];
if (!entry || typeof entry !== ‘string’) {
throw new Error(
(“Entry \”” + entryName + “\” not found. Did you specify the correct entry option?”)
)
}
var bundle = {
entry: entry,
files: {},
maps: {}
};
stats.assets.forEach(function (asset) {
if (asset.name.match(/.js$/)) {
bundle.files[asset.name] = compilation.assets[asset.name].source();
} else if (asset.name.match(/.js.map$/)) {
bundle.maps[asset.name.replace(/.map$/, ”)] = JSON.parse(compilation.assets[asset.name].source());
}
// do not emit anything else for server
delete compilation.assets[asset.name];
});
var json = JSON.stringify(bundle, null, 2);
var filename = this$1.options.filename;
compilation.assets[filename] = {
source: function () { return json;},
size: function () { return json.length;}
};
cb();
});
};
module.exports = Vue*ServerPlugin;
2、新建一个骨架屏构建配置文件:build/webpack.skeleton.conf.js,这个文件配合 vue-server-renderer 插件,将 App.vue 内容构建成单个 json 格局的文件
’use strict’
const path = require(‘path’)
const nodeExternals = require(‘webpack-node-externals’)
const Vue*ServerPlugin = require(‘../skeleton/plugin/server-plugin’)
module.exports = {
// 这容许 webpack 以 Node 实用形式 (Node-appropriate fashion) 解决动静导入(dynamic import),
// 并且还会在编译 Vue 组件时,
// 告知 vue-loader
输送面向服务器代码(server-oriented code)。
// 对 bundle renderer 提供 source map 反对
devtool: ‘source-map’,
// 将 entry 指向应用程序的 server entry 文件
entry: path.resolve(__dirname, ‘../skeleton/skeleton-entry.js’),
output: {
path: path.resolve(__dirname, ‘../skeleton’), // 生成的文件的目录
publicPath: ‘/skeleton/’,
filename: ‘[name].js’,
},
module: {
rules: [
{
test: /.vue$/,
loader: ‘vue-loader’,
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
test: /.css$/,
use: [‘vue-style-loader’, ‘css-loader’]
}
]
},
performance: {
hints: false
},
//
//
// 外置化应用程序依赖模块。能够使服务器构建速度更快,
// 并生成较小的 bundle 文件。
externals: nodeExternals({
// 不要外置化 webpack 须要解决的依赖模块。
// 你能够在这里增加更多的文件类型。例如,未解决 *.vue 原始文件,
// 你还应该将批改 global
(例如 polyfill)的依赖模块列入白名单
allowlist: /.css$/
}),
// 这是将服务器的整个输入
// 构建为单个 JSON 文件的插件。
// 不配置 filename,则默认文件名为 vue-***-server-bundle.json
plugins: [
new Vue*ServerPlugin({
filename: ‘skeleton.json’
})
]
}
3、应用 webpack-cli 运行文件 webpack.skeleton.conf.js,生成 skeleton.json 文件,搁置在文件夹 skeleton 下
在 package.json 文件外面书写运行命令:create-skeleton
”scripts”: {
”create-skeleton”: “webpack –progress –config build/webpack.skeleton.conf.js”,
”fill-skeleton”: “node ./skeleton/skeleton.js”
}
在管制台上运行命令:
npm run create-skeleton
文件夹 skeleton 下就会多出 skelleton.json 文件
4、将生成的 skeleton.json 内容注入到根目录下的 index.html(模版文件)
1)在文件夹 skeleton 下新建 skeleton.js
// 将生成的 skeleton.json 的内容填充到模板文件中
const fs = require(‘fs’)
const {resolve} = require(‘path’)
const createBundleRenderer = require(‘vue-server-renderer’).createBundleRenderer
// 读取 skeleton.json,以 skeleton/index.html 为模版写入内容
const renderer = createBundleRenderer(resolve(__dirname, ‘../skeleton/skeleton.json’), {
template: fs.readFileSync(resolve(__dirname, ‘../skeleton/index.html’), ‘utf-8’)
})
// 把上一步模版实现的内容写入根目录下的模版文件 ’index.html’
renderer.renderToString({}, (err, html) => {
if (err) {
return console.log(err)
}
console.log(‘render complete!’)
fs.writeFileSync(‘index.html’, html, ‘utf-8’)
})
2)增加运行命令:fill-skeleton
”fill-skeleton”: “node ./skeleton/skeleton.js”
3)在管制台上运行该命令,则 skeleton.json 文件内容被填充至根目录下的模板文件 index.html 了