小言_互联网的博客

在vue项目中使用骨架屏

350人阅读  评论(0)

现在的应用开发,基本上都是前后端分离的,前端主流框架有SPA、MPA等,那么解决页面渲染、白屏时间成为首要关注的点

webpack可以按需加载,减小首屏需要加载代码的体积;

使用CDN技术、静态代码等缓存技术,可以减小加载渲染的时长

问题:但是首页依然存在加载、渲染等待时长的问题。那么如何从视觉效果上减小首屏白屏的时间呢?

骨架屏:举个例子:其实就是在模版文件中id=app容器下面写想要展示的效果,在new Vue(option)之后,该id下的内容就被替换了( 这时候,可能Vue编译生成的内容还没有挂载。因为new Vue的时候会进行一系列的初始化,这也需要耗费时间的)。这样就可以从视觉上减小白屏的时间

骨架屏的实现方式

1、直接在模版文件id=app容器下面,写进想要展示的效果html

2、直接在模板文件id=app容器下面,用图片展示

3、使用vue ssr提供的webpack插件

4、自动生成并且自动插入静态骨架屏

方式1和方式2存在的缺陷:针对不同入口,展示的效果都一样,导致不能灵活的针对不同的入口,展示不同的样式

方式3可以针对不同的入口展示不同的效果。(实质也是先通过ssr生成一个json文件,然后将json文件内容注入到模板文件的id=app容器下)

 

方案一、直接在模版文件id=app容器下面,写进想要展示的效果html

在根目录的模版文件内写进内容,如红色圈出来的地方

在浏览器打开项目

在调用new Vue之前的展示效果(只是做了个简单效果,不喜勿喷):

可以看到elements中id=app的容器下内容,就是我们写进的骨架屏效果内容

在看下调了new Vue之后的效果,id=app容器下的内容被vue编译生成的内容替换了

方案二、直接在模板文件id=app容器下面,用图片展示(这个就不做展示了)

方案三、使用vue ssr提供的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(首页骨架屏展示效果的代码)


  
  1. <template>
  2. <div class="skeleton-home">
  3. <div>加载中... </div>
  4. </div>
  5. </template>
  6. <style>
  7. .skeleton-home {
  8. width: 100vw;
  9. height: 100vh;
  10. background-color: #eaeaea;
  11. }
  12. </style>

App.vue(根组件)


  
  1. <template>
  2. <div id="app">
  3. <!-- 根组件 -->
  4. <home style="display:none" id="homeSkeleton"> </home>
  5. </div>
  6. </template>
  7. <script>
  8. import home from './home.skeleton.vue'
  9. export default{
  10. components: {
  11. home
  12. }
  13. }
  14. </script>
  15. <style>
  16. #app {
  17. font-family: 'Avenir', Helvetica, Arial, sans-serif;
  18. -webkit-font-smoothing: antialiased;
  19. -moz-osx-font-smoothing: grayscale;
  20. text-align: center;
  21. color: #2c3e50;
  22. }
  23. *{
  24. padding: 0;
  25. margin: 0;
  26. }
  27. </style>

skeleton-entry.js(入口文件)


  
  1. // 入口文件
  2. import Vue from 'vue'
  3. import App from './App.vue'
  4. let skeleton = new Vue({
  5. render(h) {
  6. return h(App)
  7. }
  8. })
  9. export default skeleton

plugin/server-plugin.js(vue-server-renderer包提供了server-plugin插件)


  
  1. 'use strict';
  2. /* */
  3. var isJS = function (file) { return /\.js(\?[^.]+)?$/.test(file); };
  4. var ref = require( 'chalk');
  5. var red = ref.red;
  6. var yellow = ref.yellow;
  7. var prefix = "[vue-server-renderer-webpack-plugin]";
  8. var warn = exports.warn = function (msg) { return console.error(red((prefix + " " + msg + "\n"))); };
  9. var tip = exports.tip = function (msg) { return console.log(yellow((prefix + " " + msg + "\n"))); };
  10. var validate = function (compiler) {
  11. if (compiler.options.target !== 'node') {
  12. warn( 'webpack config `target` should be "node".');
  13. }
  14. if (compiler.options.output && compiler.options.output.libraryTarget !== 'commonjs2') {
  15. warn( 'webpack config `output.libraryTarget` should be "commonjs2".');
  16. }
  17. if (!compiler.options.externals) {
  18. tip(
  19. 'It is recommended to externalize dependencies in the server build for ' +
  20. 'better build performance.'
  21. );
  22. }
  23. };
  24. var VueSSRServerPlugin = function VueSSRServerPlugin (options) {
  25. if ( options === void 0 ) options = {};
  26. this.options = Object.assign({
  27. filename: 'vue-ssr-server-bundle.json'
  28. }, options);
  29. };
  30. VueSSRServerPlugin.prototype.apply = function apply (compiler) {
  31. var this$ 1 = this;
  32. validate(compiler);
  33. compiler.plugin( 'emit', function (compilation, cb) {
  34. var stats = compilation.getStats().toJson();
  35. var entryName = Object.keys(stats.entrypoints)[ 0];
  36. var entryAssets = stats.entrypoints[entryName].assets.filter(isJS);
  37. if (entryAssets.length > 1) {
  38. throw new Error(
  39. "Server-side bundle should have one single entry file. " +
  40. "Avoid using CommonsChunkPlugin in the server config."
  41. )
  42. }
  43. var entry = entryAssets[ 0];
  44. if (!entry || typeof entry !== 'string') {
  45. throw new Error(
  46. ( "Entry \"" + entryName + "\" not found. Did you specify the correct entry option?")
  47. )
  48. }
  49. var bundle = {
  50. entry: entry,
  51. files: {},
  52. maps: {}
  53. };
  54. stats.assets.forEach( function (asset) {
  55. if (asset.name.match( /\.js$/)) {
  56. bundle.files[asset.name] = compilation.assets[asset.name].source();
  57. } else if (asset.name.match( /\.js\.map$/)) {
  58. bundle.maps[asset.name.replace( /\.map$/, '')] = JSON.parse(compilation.assets[asset.name].source());
  59. }
  60. // do not emit anything else for server
  61. delete compilation.assets[asset.name];
  62. });
  63. var json = JSON.stringify(bundle, null, 2);
  64. var filename = this$ 1.options.filename;
  65. compilation.assets[filename] = {
  66. source: function () { return json; },
  67. size: function () { return json.length; }
  68. };
  69. cb();
  70. });
  71. };
  72. module.exports = VueSSRServerPlugin;

 

2、新建一个骨架屏构建配置文件:build/webpack.skeleton.conf.js,这个文件配合vue-server-renderer插件,将App.vue内容构建成单个json格式的文件


  
  1. 'use strict'
  2. const path = require( 'path')
  3. const nodeExternals = require( 'webpack-node-externals')
  4. const VueSSRServerPlugin = require( '../skeleton/plugin/server-plugin')
  5. module.exports = {
  6. // 这允许 webpack 以 Node 适用方式(Node-appropriate fashion)处理动态导入(dynamic import),
  7. // 并且还会在编译 Vue 组件时,
  8. // 告知 `vue-loader` 输送面向服务器代码(server-oriented code)。
  9. target: 'node',
  10. // 对 bundle renderer 提供 source map 支持
  11. devtool: 'source-map',
  12. // 将 entry 指向应用程序的 server entry 文件
  13. entry: path.resolve(__dirname, '../skeleton/skeleton-entry.js'),
  14. output: {
  15. path: path.resolve(__dirname, '../skeleton'), // 生成的文件的目录
  16. publicPath: '/skeleton/',
  17. filename: '[name].js',
  18. libraryTarget: 'commonjs2' // 此处告知 server bundle 使用 Node 风格导出模块(Node-style exports)
  19. },
  20. module: {
  21. rules: [
  22. {
  23. test: /\.vue$/,
  24. loader: 'vue-loader',
  25. options: {
  26. compilerOptions: {
  27. preserveWhitespace: false
  28. }
  29. }
  30. },
  31. {
  32. test: /\.css$/,
  33. use: [ 'vue-style-loader', 'css-loader']
  34. }
  35. ]
  36. },
  37. performance: {
  38. hints: false
  39. },
  40. // https://webpack.js.org/configuration/externals/#function
  41. // https://github.com/liady/webpack-node-externals
  42. // 外置化应用程序依赖模块。可以使服务器构建速度更快,
  43. // 并生成较小的 bundle 文件。
  44. externals: nodeExternals({
  45. // 不要外置化 webpack 需要处理的依赖模块。
  46. // 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,
  47. // 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单
  48. allowlist: /\.css$/
  49. }),
  50. // 这是将服务器的整个输出
  51. // 构建为单个 JSON 文件的插件。
  52. // 不配置filename,则默认文件名为 `vue-ssr-server-bundle.json`
  53. plugins: [
  54. new VueSSRServerPlugin({
  55. filename: 'skeleton.json'
  56. })
  57. ]
  58. }

 

3、使用webpack-cli运行文件webpack.skeleton.conf.js,生成skeleton.json文件,放置在文件夹skeleton下

在package.json文件里面书写运行命令:create-skeleton


  
  1. "scripts": {
  2. "create-skeleton": "webpack --progress --config build/webpack.skeleton.conf.js",
  3. "fill-skeleton": "node ./skeleton/skeleton.js"
  4. }

在控制台上运行命令:

npm run create-skeleton

文件夹skeleton下就会多出skelleton.json文件

 

4、将生成的skeleton.json内容注入到根目录下的index.html(模版文件)

1)在文件夹skeleton下新建skeleton.js


  
  1. // 将生成的skeleton.json的内容填充到模板文件中
  2. const fs = require( 'fs')
  3. const { resolve } = require( 'path')
  4. const createBundleRenderer = require( 'vue-server-renderer').createBundleRenderer
  5. // 读取skeleton.json,以skeleton/index.html为模版写入内容
  6. const renderer = createBundleRenderer(resolve(__dirname, '../skeleton/skeleton.json'), {
  7. template: fs.readFileSync(resolve(__dirname, '../skeleton/index.html'), 'utf-8')
  8. })
  9. // 把上一步模版完成的内容写入根目录下的模版文件'index.html'
  10. renderer.renderToString({}, (err, html) => {
  11. if (err) {
  12. return console.log(err)
  13. }
  14. console.log( 'render complete!')
  15. fs.writeFileSync( 'index.html', html, 'utf-8')
  16. })

2)添加运行命令:fill-skeleton

"fill-skeleton": "node ./skeleton/skeleton.js"

3)在控制台上运行该命令,则skeleton.json文件内容被填充至根目录下的模板文件index.html了

 

参考文章:

利用Vue SSR 做骨架屏注入:https://www.cnblogs.com/goloving/p/11397371.html

在Vue中实现骨架屏:http://www.360doc.com/content/20/0709/11/21412_923150401.shtml

Vue ssr渲染踩过的坑:https://blog.csdn.net/chen801090/article/details/105974987/


转载:https://blog.csdn.net/tangxiujiang/article/details/116832585
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场