关于前端:在vue项目中使用骨架屏

37次阅读

共计 6149 个字符,预计需要花费 16 分钟才能阅读完成。

当初的利用开发,基本上都是前后端拆散的,前端支流框架有 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 了

正文完
 0