从0到1构建vueSSR项目之node以及vue-cli3的配置


Posted in Javascript onMarch 07, 2019

前言

上一次做了路由的相关配置,原本计划今天要做vuex部分,但是想了想,发现vuex单独的客户端部分穿插解释起来很麻烦,所以今天改做服务端部分。

服务端部分做完,再去做vuex的部分,这样就会很清晰。

vue ssr是分两个端,一个是客户端,一个是服务端。

所以要做两个cli3的配置。

那么下面就直接开始做吧。

修改package.json的命令

//package.json :client代表客户端 :server代表服务端
//使用VUE_NODE来作为运行环境是node的标识
//cli3内置 命令 --no-clean 不会清除dist文件
  "scripts": {
    "serve:client": " vue-cli-service serve",
    "build":"npm run build:server -- --silent && npm run build:client -- --no-clean --silent",
    "build:client": "vue-cli-service build",
    "build:server": "cross-env VUE_NODE=node vue-cli-service build",
    "start:server": "cross-env NODE_ENV=production nodemon nodeScript/index"
  }

修改vue.config.js配置

添加完相关脚本命令之后,我们开始改造cli3配置。

首先要require('vue-server-renderer')

然后再根据VUE_NODE环境变量来决定编译的走向以及生成不同的环境清单

先做cli3服务端的入口文件

// src/entry/server.js
import {
  createApp
} from '../main.js'

export default context => {

  return new Promise((resolve, reject) => {
    const {
      app,
      router
    } = createApp(context.data)
    //根据node传过来的路由 来调用router路由的指向
    router.push(context.url)

    router.onReady(() => {
      //获取当前路由匹配的组件数组。
      const matchedComponents = router.getMatchedComponents()
      //长度为0就是没找到该路由所匹配的组件
      //可以路由设置重定向或者传回node node来操作也可以
      if (!matchedComponents.length) {

        return reject({
          code: 404
        })
      }

      resolve(app)

    }, reject)
  })
}

这里是cli3的配置

//vue.config.js

const ServerPlugin = require('vue-server-renderer/server-plugin'),//生成服务端清单
   ClientPlugin = require('vue-server-renderer/client-plugin'),//生成客户端清单
   nodeExternals = require('webpack-node-externals'),//忽略node_modules文件夹中的所有模块
   VUE_NODE = process.env.VUE_NODE === 'node',
   entry = VUE_NODE ? 'server' : 'client';//根据环境变量来指向入口

module.exports = {
  css: {
    extract: false//关闭提取css,不关闭 node渲染会报错
  },
  configureWebpack: () => ({
    entry: `./src/entry/${entry}`,
    output: {
      filename: 'js/[name].js',
      chunkFilename: 'js/[name].js',
      libraryTarget: VUE_NODE ? 'commonjs2' : undefined
    },
    target: VUE_NODE ? 'node' : 'web',
    externals: VUE_NODE ? nodeExternals({
      //设置白名单
      whitelist: /\.css$/
    }) : undefined,
    plugins: [//根据环境来生成不同的清单。
      VUE_NODE ? new ServerPlugin() : new ClientPlugin()
    ]
  }),
  chainWebpack: config => {
    config.resolve
      .alias
        .set('vue$', 'vue/dist/vue.esm.js')
    config.module
      .rule('vue')
        .use('vue-loader')
          .tap(options => {
            options.optimizeSSR = false;
            return options;
          });
    config.module
      .rule('images')
        .use('url-loader')
          .tap(options => {
            options = {
              limit: 1024,
              fallback:'file-loader?name=img/[path][name].[ext]'
            }
            return options;
          });
  }
}

node相关配置

用于node渲染 必然要拦截get请求的。然后根据get请求地址来进行要渲染的页面。

官方提供了vue-server-renderer插件

大概的方式就是 node拦截所有的get请求,然后将获取到的路由地址,传给前台,然后使用router实例进行push

再往下面看之前 先看一下官方文档

创建BundleRenderer
createBundleRenderer

将 Vue 实例渲染为字符串。
renderToString

渲染应用程序的模板
template

生成所需要的客户端或服务端清单
clientManifest

先创建 服务端所需要的模板

//public/index.nodeTempalte.html
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="/favicon.ico" rel="external nofollow" >
    <meta charset="utf-8">
    <title>vuessr</title>
  </head>
  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>

node部分

先创建三个文件
index.js //入口
proxy.js //代理
server.js //主要配置

//server.js
const fs = require('fs');
const { resolve } = require('path');
const express = require('express');
const app = express();
const proxy = require('./proxy');
const { createBundleRenderer } = require('vue-server-renderer')

//模板地址
const templatePath = resolve(__dirname, '../public/index.nodeTempalte.html')
//客户端渲染清单
const clientManifest = require('../dist/vue-ssr-client-manifest.json')
//服务端渲染清单
const bundle = require('../dist/vue-ssr-server-bundle.json')
//读取模板
const template = fs.readFileSync(templatePath, 'utf-8')
const renderer = createBundleRenderer(bundle,{
  template,
  clientManifest,
  runInNewContext: false
})


//代理相关
proxy(app);
//请求静态资源相关配置
app.use('/js', express.static(resolve(__dirname, '../dist/js')))
app.use('/css', express.static(resolve(__dirname, '../dist/css')))
app.use('/font', express.static(resolve(__dirname, '../dist/font')))
app.use('/img', express.static(resolve(__dirname, '../dist/img')))
app.use('*.ico', express.static(resolve(__dirname, '../dist')))


//路由请求
app.get('*', (req, res) => {
  res.setHeader("Content-Type", "text/html")
  //传入路由 src/entry/server.js会接收到 使用vueRouter实例进行push
  const context = {
    url: req.url
  }

  renderer.renderToString(context, (err, html) => {
    if (err) {
      if (err.url) {
        res.redirect(err.url)
      } else {
        res.status(500).end('500 | 服务器错误');
        console.error(`${req.url}: 渲染错误 `);
        console.error(err.stack)
      }
    }
    res.status(context.HTTPStatus || 200)
    res.send(html)
  })
})
module.exports = app;



//proxy.js

const proxy = require('http-proxy-middleware');

function proxyConfig(obj){
  return {
    target:'localhost:8081',
    changeOrigin:true,
    ...obj
  }
}
module.exports = (app) => {
  //代理开发环境
  if (process.env.NODE_ENV !== 'production') {
    app.use('/js/main*', proxy(proxyConfig()));
    app.use('/*hot-update*',proxy(proxyConfig()));
    app.use('/sockjs-node',proxy(proxyConfig({ws:true})));
  }
}


//index.js
const app = require('./server')

app.listen(8080, () => {
 console.log('\033[42;37m DONE \033[40;33m localhost:8080 服务已启动\033[0m')
})

做完这一步之后,就可以预览基本的服务渲染了。

后面就只差开发环境的配置,以及到node数据的传递(vuex)

npm run build
npm run start:server
打开localhost:8080
F12 - Network - Doc 就可以看到内容

从0到1构建vueSSR项目之node以及vue-cli3的配置

最终目录结构

|-- vuessr
  |-- .gitignore
  |-- babel.config.js
  |-- package-lock.json
  |-- package.json
  |-- README.md
  |-- vue.config.js
  |-- nodeScript //node 渲染配置
  |  |-- index.js
  |  |-- proxy.js
  |  |-- server.js
  |-- public//模板文件
  |  |-- favicon.ico
  |  |-- index.html
  |  |-- index.nodeTempalte.html
  |-- src
    |-- App.vue
    |-- main.js
    |-- router.config.js//路由集合
    |-- store.config.js//vuex 集合
    |-- assets//全局静态资源源码
    |  |-- 备注.txt
    |  |-- img
    |    |-- logo.png
    |-- components//全局组件
    |  |-- Head
    |    |-- index.js
    |    |-- index.scss
    |    |-- index.vue
    |    |-- img
    |      |-- logo.png
    |-- entry//cli3入口
    |  |-- client.js
    |  |-- server.js
    |  |-- 备注.txt
    |-- methods//公共方法
    |  |-- 备注.txt
    |  |-- mixin
    |    |-- index.js
    |-- pages//源码目录
    |  |-- home
    |  |  |-- index.js
    |  |  |-- index.scss
    |  |  |-- index.vue
    |  |  |-- img
    |  |  |  |-- flow.png
    |  |  |  |-- head_portrait.jpg
    |  |  |  |-- logo.png
    |  |  |  |-- vuessr.png
    |  |  |-- vue
    |  |  |  |-- index.js
    |  |  |  |-- index.scss
    |  |  |  |-- index.vue
    |  |  |-- vueCli3
    |  |  |  |-- index.js
    |  |  |  |-- index.scss
    |  |  |  |-- index.vue
    |  |  |-- vueSSR
    |  |  |  |-- index.js
    |  |  |  |-- index.scss
    |  |  |  |-- index.vue
    |  |  |-- vuex
    |  |    |-- index.js
    |  |    |-- index.scss
    |  |    |-- index.vue
    |  |-- router//路由配置
    |  |  |-- index.js
    |  |-- store//vuex配置
    |    |-- all.js
    |    |-- gather.js
    |    |-- index.js
    |-- static//cdn资源
      |-- 备注.txt

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
关于JavaScript的一些看法
May 27 Javascript
javascript中hasOwnProperty() 方法使用指南
Mar 09 Javascript
JS实现快速的导航下拉菜单动画效果附源码下载
Nov 01 Javascript
Node.js的特点详解
Feb 03 Javascript
百度地图JavascriptApi Marker平滑移动及车头指向行径方向
Mar 13 Javascript
vue动态路由实现多级嵌套面包屑的思路与方法
Aug 16 Javascript
Bootstrap 模态框(Modal)带参数传值实例
Aug 20 Javascript
canvas绘制爱心的几种方法总结(推荐)
Oct 31 Javascript
JS判断数组里是否有重复元素的方法小结
May 21 Javascript
微信小程序 确认框的实现(附代码)
Jul 23 Javascript
javascript(基于jQuery)实现鼠标获取选中的文字示例【测试可用】
Oct 26 jQuery
electron 安装,调试,打包的具体使用
Nov 06 Javascript
从0到1构建vueSSR项目之路由的构建
Mar 07 #Javascript
bootstrap-table实现表头固定以及列固定的方法示例
Mar 07 #Javascript
js如何获取图片url的Blob值并预览示例代码
Mar 07 #Javascript
mpvue全局引入sass文件的方法步骤
Mar 06 #Javascript
angular6根据environments配置文件更改开发所需要的环境的方法
Mar 06 #Javascript
vue项目前端埋点的实现
Mar 06 #Javascript
vue2.0结合Element-ui实战案例
Mar 06 #Javascript
You might like
php5.2.0内存管理改进
2007/01/22 PHP
php print EOF实现方法
2009/05/21 PHP
PHP通过iconv将字符串从GBK转换为UTF8字符集
2011/07/18 PHP
通过JAVASCRIPT读取ASP设定的COOKIE
2007/02/15 Javascript
javascript 必知必会之closure
2009/09/21 Javascript
js实现网站首页图片滚动显示
2013/02/04 Javascript
js写出遮罩层登陆框和对联广告并自动跟随滚动条滚动
2014/04/29 Javascript
浅谈jQuery中 wrap() wrapAll() 与 wrapInner()的差异
2014/11/12 Javascript
javascript进行四舍五入方法汇总
2014/12/16 Javascript
jquery判断复选框是否选中进行答题提示特效
2015/12/10 Javascript
AngularJS监听路由的变化示例代码
2016/09/23 Javascript
微信小程序  网络请求API详解
2016/10/25 Javascript
微信小程序实现动态改变view标签宽度和高度的方法【附demo源码下载】
2017/12/05 Javascript
vue 中使用 watch 出现了如下的报错的原因分析
2019/05/21 Javascript
vue-cli3项目打包后自动化部署到服务器的方法
2020/09/16 Javascript
[01:50:49]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Aster BO3 第三场 1月24日
2021/03/11 DOTA
Python二叉搜索树与双向链表转换实现方法
2016/04/29 Python
python使用opencv读取图片的实例
2017/08/17 Python
python中numpy的矩阵、多维数组的用法
2018/02/05 Python
python中logging模块的一些简单用法的使用
2019/02/22 Python
超简单的Python HTTP服务
2019/07/22 Python
维多利亚的秘密阿联酋官网:Victoria’s Secret阿联酋
2019/12/07 全球购物
linux比较文件内容的命令是什么
2015/09/23 面试题
法律专业应届生自荐信范文
2014/01/06 职场文书
《会变的花树叶》教学反思
2014/02/10 职场文书
企业职业病防治方案
2014/05/29 职场文书
大学生社会实践活动总结
2014/07/03 职场文书
销售竞赛活动方案
2014/08/23 职场文书
个人三严三实对照检查材料思想汇报
2014/09/22 职场文书
四风问题原因分析及整改措施
2014/10/24 职场文书
2014年学校食堂工作总结
2014/11/25 职场文书
幼儿园大班毕业评语
2014/12/31 职场文书
2015年中学校长工作总结
2015/05/19 职场文书
公司2015年终工作总结
2015/05/26 职场文书
导游词范文之颐和园/重庆/云台山
2019/09/10 职场文书
PyTorch 如何自动计算梯度
2021/05/23 Python