vue服务端渲染 项目改造优化

vue ssr 项目改造优化

这篇文章距离上一篇文章更新接近俩月了,主要记录下遇到的一些问题和解决办法

首先留个链接,上一篇文章:https://www.cnblogs.com/wangyongping/p/10961587.html

这个文章借鉴了   vue-hackernews-2.0-master和vue ssr官网,自行百度下。

问题1

         还是element的事情,针对上一篇文章,关于element引入我用的是通过判断是否为浏览器端还是服务端引入,但是这个引入虽然不报错,却会出现好多问题,页面加载闪一下,提示服务端和浏览器端加载不一样,所以需要解决问题,毕竟element现在2.11.0出了很多新功能,真心不错。

         按需引入,没错,将element按需引入     

首先,安装 babel-plugin-component:    npm install babel-plugin-component -D

然后,将 .babelrc 修改为:

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}
这么引入,在main.js正常按需导入所用插件。
重点来了,我们还要配置 webpack.base.config.js,

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');////重点!!!
const webpack = require('webpack');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  devtool: isProd
    ? false
    : '#source-map',
  output: {
    path: path.resolve(__dirname, '../dist'),
    publicPath: '/dist/',
    filename: '[name].[chunkhash].js'
  },
  resolve: {
    alias: {
      'static': path.resolve(__dirname, '../static'),
    }
  },
  module: {
    noParse: /es6-promise\.js$/,
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          extractCSS: isProd
        }
      },
//////重点!!!!!!! { test:
/\.js$/, loader: 'babel-loader', exclude: /node_modules/, options:{ plugins:['syntax-dynamic-import'] }, }, { test: /\.(png|jpg|gif|svg)$/, loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]?[hash]' } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', query: { limit: 10000, name: 'fonts/[name].[hash:7].[ext]' } },
//////重点!!!!!!!! { test:
/\.css$/, use: isProd ? ExtractTextPlugin.extract({ use: 'css-loader', fallback: 'vue-style-loader' }) : ['vue-style-loader', 'css-loader'] }, ] }, plugins: isProd ? [ new VueLoaderPlugin(),/////重点 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, sourceMap: true, parallel: true, }), new webpack.optimize.ModuleConcatenationPlugin(), new ExtractTextPlugin({ filename: 'common.[chunkhash].css' }) ] : [ new VueLoaderPlugin(),/////重点 new FriendlyErrorsPlugin() ], performance: { hints: process.env.NODE_ENV === 'production' ? 'warning' : false, maxAssetSize: 30000000, maxEntrypointSize: 50000000, assetFilter: function(assetFilename) { return assetFilename.endsWith('.css') || assetFilename.endsWith('.js'); } } };

这段代码里有几个重点:一个是css的引入,一个是js的引入,这两个引入错了,按需引入element也会行不通的,

看好所用的插件,缺啥下啥,还有一个就是  vue-loader  引入,插件版本不一样,引入的方式也不一样,这里的版本是15.7.0  ,之前有个低版本的,这么引入报错,不记得错误什么样了,自行测试吧。

 

问题2
          
这个错误从一开始就犯了,导致研究了好久,最后发现是路由的错。
错误现象:首次加载不出来,刷新就好了;刷新500。
错误码:
Cannot read property '__esModule' of undefined
原来项目改造前用这种懒加载方式:
{path: '/my',name: 'my',
     component: resolve => require(['../page/my/my.vue'], resolve),
 },

苦苦找了好久这个问题,竟然是路由。包括子组件都不能这么引入。

正确引入,官网:

 { path: '/', name: 'index',
        component: () => import('../views/index.vue')},

问题3
       
关于 entry-client.js,这个是浏览器端入口。但是发现一个问题,用这种方式的时候有一个bug,在首次加载
或者刷新的时候出现数据后退,回到上一个路由的数据,造成页面不美,用户体验不好。基本再刷新时候出现,在网上
有人做个判断路由,试了一下,有点麻烦,不知道怎么解决好。上代码:
import Vue from 'vue'
import { createApp } from './main'
import 'es6-promise/auto'
const { app, router, store } = createApp();
import { Loading } from 'element-ui';

 Vue.mixin({
   beforeRouteUpdate (to, from, next) {
     const { asyncData } = this.$options;
     if (asyncData) {
       asyncData({
        store: this.$store,
       route: to
       }).then(next).catch(next)
} else {
     next()
    }
 }
});


if (window.__INITIAL_STATE__) {
  store.replaceState(window.__INITIAL_STATE__)
}

router.onReady(() => {
    router.beforeResolve((to, from, next) => {
      const matched = router.getMatchedComponents(to);
      const prevMatched = router.getMatchedComponents(from);
      let diffed = false;
      const activated = matched.filter((c, i) => {
        return diffed || (diffed = (prevMatched[i] !== c))
      });
      const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _);
      if (!asyncDataHooks.length) {
        return next()
      }
      let loadingInstance = Loading.service({
        background:'rgba(220,220,220,0.5)',
        fullscreen: true,
        text: '加载中...',
      });
      Promise.all(asyncDataHooks.map(hook => hook({
        store,
        route: to})))
        .then(() => {
        loadingInstance.close();
          next()
        }).catch(next)
    });
    app.$mount('#app');
});

后来尝试改下导航首位方式,问题解决了,但是新的问题出现,那就是页面首次加载,请求后端两次,上代码:

import Vue from 'vue'
import { createApp } from './main'
import 'es6-promise/auto'
const { app, router, store } = createApp();
import { Loading } from 'element-ui';

Vue.mixin({
  beforeMount () {
    const { asyncData } = this.$options;
    if(asyncData){
      let loadingInstance = Loading.service({
        background:'rgba(220,220,220,0.5)',
        fullscreen: true,
        text: '加载中...',
      });
      this.dataPromise = asyncData({
        store: this.$store,
        route: this.$route
      });
      this.dataPromise.then(()=>{
        loadingInstance.close();
      }).catch(e=>{
        loadingInstance.close();
      })
    }
  }
});

if (window.__INITIAL_STATE__) {
  store.replaceState(window.__INITIAL_STATE__)
}

router.onReady(() => {
    app.$mount('#app');
});

这两种方法引入,虽然在直接上没有错误,但是整体上并不完美,并不能解决根本问题,于是仔细思考,这不是路由的问题,是数据缓存的问题,

仔细看代码,恍然大悟。在多模块下,要将vuex代码分离。

 

在*****.vue
import sgnavSM from '../../../store/modules/sgnav';////引用
asyncData ({ store , route }) {
        store.registerModule('sgnav', sgnavSM);////注册
        return Promise.all([
          store.dispatch('gsnavList'),
          store.dispatch('ghnavList'),
          store.dispatch('gxnavList'),
          store.dispatch('getidssgnav',{id:6})
        ]);
      },
computed: {
        navlist() {
          return this.$store.state.sgnav.hidnav;/////使用
        }
      },
destroyed () {
         this.$store.unregisterModule('sgnav');/////消灭
       },

 

彻底解决路由变换,信息后退问题!!!!

 

 


 问题4

          解决404,新建个error.vue,加点内容;修改router/index.js。

 

import Vue from 'vue'
import Router from 'vue-router'
import {router} from "../main";

Vue.use(Router);
export function createRouter () {
  const router = new Router({
    mode: 'history',
    scrollBehavior (to, from, savedPosition) {
      if (savedPosition) {
        return savedPosition
      } else {
        return { x: 0, y: 0 }
      }
    },
    routes: [
      { path: '/', name: 'index',
        component: () => import('../views/index.vue')},
      ////404等错误
      {path: '/error', name: 'error',
        component: () => import('../views/error.vue')},
    ],
  });
  router.beforeEach((to, from, next) => {
    if (to.matched.length === 0) {
      from.name ? next({
        name: from.name
      }) : next('/error');
    } else {
      next();
    }
  });
  return router

}

 

 

 

 


 问题5 

         cookie的使用。

在   /server.js

引入   const cookieParser = require('cookie-parser');   当然提前下好插件。

使用  app.use(cookieParser());

传出去:

 const context = {
    title:'',
    keywords:'',
    description:'' ,
    author:'星涑',
    cookies:req.cookies,
    url: req.url,
  };

  // const stream = renderer.renderToStream(context);
  // stream.on('error', errorHandler);
  // stream.pipe(res)
  renderer.renderToString(context, (err, html) => {
    if (err) {
      return errorHandler(err)
    }
    res.send(html);
    if (!isProd) {
      console.log('>>>>>|success|<<<<<')
    }
    res.end();
  })

 

修改  entry-server.js   ,将cookie存到vuex

 

if (context.cookies) {
store.commit('SET_TOKEN', context.cookies)
}


 

问题6   
       
这个涉及到接收json数据,如果从后端接收过来的数据有点多。使页面比较乱可以处理下。
          const olddata=接收的json;
          const newdata =[];
          olddata.list.forEach(function(item){
            newdata.push({
              id:item.id,
              title:item.title,
              stitle:item.stitle,
              createTime:item.createTime,
              userImg:item.userImg,
              readNum:item.readNum,
            })
          })
          const wandata ={
            pages: olddata.pages,
            total: olddata.total,
            pageNum:olddata.pageNum,
            pageSize:olddata.pageSize,
            list:newdata,
          };
          commit('SET_ARTICLE', wandata);
        }
 
思路就是把有用的展示到前端。当然也可以后端处理。

问题7 
这个问题也挺重要的,就是细节问题,在测试时候代码请求数据请求一次,渲染页面,但是打包上线后就请求了两次
,研究了一下发现,在代码添加vue-ssr-client-manifest.json之后,需要将webpack.client.config.js的 prod 的HtmlWebpackPlugin 去除,否则就会请求多次
,造成数据压力增大,页面变慢。

今天就更新这些,希望对你有帮助,给个推荐吧。

还是墨迹那句话,希望有更多的人支持我,点个关注。我会努力发表更好的文章

QQ:1763907618------备注:博客园

posted @ 2019-07-28 21:53  星涑  阅读(260)  评论(5编辑  收藏  举报