从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 相关文章推荐
js点击页面其它地方将某个显示的DIV隐藏
Jul 12 Javascript
向当前style sheet中插入一个新的style实现方法
Apr 01 Javascript
jQuery 遍历- 关于closest() 的方法介绍以及与parents()的方法区别分析
Apr 26 Javascript
js 剪切板应用clipboardData详细解析
Dec 17 Javascript
JavaScript、tab切换完整版(自动切换、鼠标移入停止、移开运行)
Jan 05 Javascript
javascript中arguments,callee,caller详解
Mar 16 Javascript
js实现纯前端的图片预览
Apr 27 Javascript
jQuery序列化后的表单值转换成Json
Jun 16 jQuery
VUE的history模式下除了index外其他路由404报错解决办法
Aug 21 Javascript
Vue组件为什么data必须是一个函数
Jun 11 Javascript
ReactRouter的实现方法
Jan 25 Javascript
vue中axios封装使用的完整教程
Mar 03 Vue.js
从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
一个odbc连mssql分页的类
2006/10/09 PHP
编写Smarty插件在模板中直接加载数据的详细介绍
2013/06/26 PHP
PHP base64编码后解码乱码的解决办法
2014/06/19 PHP
小程序微信退款功能实现方法详解【基于thinkPHP】
2019/05/05 PHP
Laravel修改验证提示信息为中文的示例
2019/10/23 PHP
url 特殊字符 传递参数解决方法
2010/01/01 Javascript
javascript中的self和this用法小结
2014/02/08 Javascript
js实现DOM走马灯特效的方法
2015/01/21 Javascript
实例讲解避免javascript冲突的方法
2016/01/03 Javascript
jQuery的promise与deferred对象在异步回调中的作用
2016/05/03 Javascript
javascript淘宝主图放大镜功能
2016/10/20 Javascript
Vuex之理解state的用法实例
2017/04/19 Javascript
Bootstrap Table实现定时刷新数据的方法
2018/08/13 Javascript
解决vue.js 数据渲染成功仍报错的问题
2018/08/25 Javascript
Vue 框架之动态绑定 css 样式实例分析
2018/11/14 Javascript
详细分析vue响应式原理
2020/06/22 Javascript
Vite和Vue CLI的优劣
2021/01/30 Vue.js
Python使用ntplib库同步校准当地时间的方法
2016/07/02 Python
python+selenium实现自动化百度搜索关键词
2019/06/03 Python
django框架模板中定义变量(set variable in django template)的方法分析
2019/06/24 Python
Django单元测试工具test client使用详解
2019/08/02 Python
django从后台返回html代码的实例
2020/03/11 Python
python的help函数如何使用
2020/06/11 Python
python os.listdir()乱码解决方案
2021/01/31 Python
Python中生成ndarray实例讲解
2021/02/22 Python
澳大利亚正品化妆品之家:Cosmetic Capital
2017/07/03 全球购物
Ariat官网:美国马靴和服装品牌
2019/12/16 全球购物
大学毕业生自荐书怎么写?
2014/01/06 职场文书
二年级数学教学反思
2014/01/21 职场文书
最新会计专业求职信范文
2014/01/28 职场文书
大二法学专业职业生涯规划范文
2014/02/12 职场文书
安全生产工作汇报
2014/10/28 职场文书
初三毕业评语
2014/12/26 职场文书
2016年学习贯彻十八届五中全会精神心得体会
2016/01/05 职场文书
【DOTA2】高能暴走TK秀!PSG LGD vs ASTER - DPC 2022 WINTER TOUR CN
2022/04/02 DOTA
SpringCloud Function SpEL注入漏洞分析及环境搭建
2022/04/08 Java/Android