时间:2021-05-26
>初学ssr入坑
初学vue服务端渲染疑惑非常多,我们大部分前端都是半路出家,上手都是前后端分离,对服务端并不了解,不说java、php语言了,连node服务都还没搞明白,理解服务端渲染还是有些困难的;
网上有非常多的vue服务渲染的入门案例,但看了很久,很多,还是一头雾水,搞不明白这些文件和关键字的联系和意思:
这篇内容会按照 基础服务端渲染--vue实例渲染--加入vueRouter--加入vueX的顺序入坑,后续应该还有--开发模式--seo优化--部分渲染,这里先不挖那么多坑了;
>基础服务端渲染
顾名思义,得启个服务:(建个新项目,不要用vue-cli)
启动 node server.js
再看页面 正常,这就是最基础的服务端渲染
其实就是一个get请求,返回一个字符串,浏览器默认展示返回结果;
然而对于这个字符串的解析还不明确,什么意思,比如:
去掉这句话,页面就成了这样,原因不深究,自己百度
>加入vue实例
跳过官网说的built-server-bundle.js应用,意思就是不用管这个文件了,只是一个过渡文件,项目中也不会用到。直接使用createBundleRenderer方法,直接用vue-ssr-server-bundle.json;
看下现在的目录结构:
新增了5个文件;有关客户端的配置entry-client.js不是必须的,这里先不管;
app.js是用来创建vue实例的;
entry-server.js是用来创建生成vue-ssr-server-bundle.json(需要用到app.js)所需的配置配件;是给webpack.server.config.js用的;
webpack.server.config.js是用来生成vue-ssr-server-bundle.json的;
vue-ssr-server-bundle.json是给server.js中的createBundleRenderer用的。
//app.js import Vue from 'vue'import Vue from './App.vue'//这里一定要写上.vue,不然会匹配到app.js,require不区分大小写0.0export default createApp=function(){return new Vue({ render:h => h(App)})}一个createApp生成一个vue实例;
//App.vue<template><div id='app'> 这是个app</div></template><script>export default {}</script>还没用到<router-view>
//weback-base.config.jsconst path = require('path')const VueLoaderPlugin = require('vue-loader/lib/plugin')module.exports = {output:{ path:path.resolve(__dirname,'./dist'), filename:'build.js',},module: { rules: [ { test:/\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } }, exclude:[/node_modules/,/assets/] }, { test:/\.vue$/, use:['vue-loader'] } ]},resolve: { alias:{ '@':path.resolve(__dirname,'../') }, extensions:['.js','.vue','.json']},plugins:[ new VueLoaderPlugin()]}有关webpack配置不啰嗦
//webpack.server.config.js用来生成vue-ssr-server-bundle.jsonconst merge = require('webpack-merge')const baseConfig = require('./webpack.base.js')const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')module.exports = merge(baseConfig, { entry: './entry-server.js', // 这允许 webpack 以 Node 适用方式(Node-appropriate fashion)处理动态导入(dynamic import), // 并且还会在编译 Vue 组件时, // 告知 `vue-loader` 输送面向服务器代码(server-oriented code)。 target: 'node', // 对 bundle renderer 提供 source map 支持 devtool: 'source-map', // 此处告知 server bundle 使用 Node 风格导出模块(Node-style exports) output: { libraryTarget: 'commonjs2' }, // 这是将服务器的整个输出 // 构建为单个 JSON 文件的插件。 // 默认文件名为 `vue-ssr-server-bundle.json` plugins: [ new VueSSRServerPlugin() ]})这个配置哪都能找到,重点是VueSSRServerPlugin这个插件,生成vue-ssr-server-bundle.json全靠它,去掉的话生成的是built-server-bundle.js;关于merge插件,libraryTarget,target配置问题自己百度webpack去0.0;
//entry-server.jsimport { createApp } from './src/app'export default context => { return createApp()}固定写法,返回一个函数供createBundleRenderer使用;
生成vue-ssr-server-bundle.json
到目前为止安装的插件有:
自己手动一个一个装就行了。
生成vue-ssr-server-bundle.json,使用webpack命令
一切都手动,熟悉webpack;
修改server.js
const express = require('express');const chalk = require('chalk');const server = express();const serverBundle = require('./dist/vue-ssr-server-bundle.json')//**新增**//const renderer = require('vue-server-renderer').createBundleRenderer(serverBundle,{ runInNewContext: false, // 看名字也知道是生成某个新的Context对象,默认是true,改成false理解为某种缓存机制,提高服务器效率 template: require('fs').readFileSync('./index.html', 'utf-8'), })//**新增**//server.get('*', (req, res) => { //res.set('content-type', "text/html"); //res.end(` //<!DOCTYPE html> //<html lang="en"> // <head><title>Hello</title></head> // <body > // <div style='color:red'>你好</div> // </body> // </html> //改成下面这样 const context = {//这里的参数现在还没用,但这个对象还是得用,要做renderToString的参数 url:req.url } renderer.renderToString(context, (err, html) => { if (err) { res.status(500).end('Internal Server Error') return } else { res.end(html) } }) `) })server.listen(8080,function(){ let ip = getIPAdress(); console.log(`服务器开在:http://${chalk.green(ip)}:${chalk.yellow(8080)}`)})function getIPAdress(){//node下的os模块可以拿到启动该文件的服务端的部分信息,细节自己去node上面查 var interfaces = require('os').networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = 0; i < iface.length; i++) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { return alias.address; } } }}试一蛤:node server.js
正常,箭头指的地方官网有解释。别忘了inde.html中加入一行注释:
后续修改title,meta头部都是通过类似的注释方式,原理就是正则匹配替换字符串-。-;
>加入路由vue-router
新增几个文件
需要修改的文件有:
App.vue//加个router-view就行
//app.jsimport Vue from 'vue'import App from './App.vue'import router from './router'export function createApp(){ const app = new Vue({ router, render:h => h(App) }) return {app,router}}把app实例和router都抛出去,给entry-server.js用
// entry-server.jsimport { createApp } from './src/app'export default context => { //这里用promise的原因有很多,其中有一个就是下面这个onReady方法是异步的。createBundleRenderer支持promise return new Promise((resolve, reject) => { const { app, router } = createApp() router.push(context.url) router.onReady(() => {//onReady方法还有getMatchedComponents方法还是需要了解一下 const matchedComponents = router.getMatchedComponents() if (!matchedComponents.length) { return reject({ code: 404 }) } resolve(app) }, reject) })}最后看一下router.js
//router.js import Vue from 'vue' import VueRouter from 'vue-router'//页面要先声明后使用,不要问为什么import home from './pages/home'import store from './pages/store'Vue.use(VueRouter)export default new VueRouter({ mode: 'history', routes:[ {path:'/',name:'home',component:home}, {path:'/store',name:'store',component:store}, ]})再看一下两个页面的代码;
//store.vue <template> <div>this is store</div> </template> <script> export default {} </script>改的差不多了,试一哈:
重新打个包webpack --config webpack.server.js
启动node server
>entry-client.js是干啥的
到目前为止还没用到entry-client.js叫客户端配置,不着急使用,先做个测试,写点逻辑试试:
修改下store.vue
看这个样子页面最终展示的结果应该是this is mounted,然而结果是这样的:
很好解释,服务端对于钩子函数的理解也是很正确的,created会在页面返回之前执行,而mounted是在vue实例成型之后执行,就是页面渲染后,这个是要在客户端才会执行,可是为什么页面出来了没有执行mounted,而且run的点击事件没有生效;
看看页面:
一个js文件都没加载,怎么执行逻辑,就是个静态页面0.0;
这时候entry-client.js就出场了
新增两个文件
//entry-client.js import { createApp } from './src/app.js';const { app } = createApp();app.$mount('#app');基本配置;
//webpack.client.config.jsconst merge = require('webpack-merge')const baseConfig = require('./webpack.base.config.js')const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')module.exports = merge(baseConfig, { entry: './entry-client.js', optimization:{ runtimeChunk:true }, plugins: [ // 此插件在输出目录中 // 生成 `vue-ssr-client-manifest.json`。 new VueSSRClientPlugin(), ]})这个地方重点除了VueSSRClientPlugin生成vue-ssr-client-manifest.json外,optimization是webpack4产物,用来分离生成共公chunk,配置还算复杂,可以看下这里webpack4 optimization总结
修改下server.js
打包下:webpack --config webpack.client.config.js
node server 一下,看看页面
js有了,可是为什么还不行,不能点0.0;
看看。奥报错了
读取不到静态文件;
修改server.js加个静态文件托管:
再看看
事件也有了,页面没变化,console一下,发现值其实已经变了,只是失去了响应式;这就是为什么要用vuex的缘故;
>加入vuex
开始想在页面中用this.$set方法,然而行不通,而且不可能给每个值都重新写一个这个方法;
加个sotre.js
// store.jsimport Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex) export default new Vuex.Store({ state: { msg: '' }, actions: { setMsg ({ commit }, val) { commit('setMsg', val) } }, mutations: { setMsg (state, val) { Vue.set(state, 'msg', val)//关键 } } })很基础的逻辑,关键在Vue.set这个方法,重新增加了响应式;
修改下app.js
store.vue改成这样
<template> <div @click='run'>{{msg}}</div></template><script> export default { data(){}, created(){ this.$store.dispatch('setMsg','this is created') }, computed:{ msg(){ return this.$store.state.msg; } }, mounted(){ this.$store.dispatch('setMsg','this is mounted') }, methods: { run(){ alert('this is methods') } } }</script>重新打个包,想一下,修改页面的话只需要重新打包client,如果修改了app.js两个就要都重新打包了;
node server 一下
这回总算完成了;
>总结
服务端渲染东西还是挺多的,涉及领域也非常广,比如vue,webpack,node,它们的生态圈都大的可怕,需要学习东西非常多,
坑又多,又大,又深,后面还有很多问题要解决:
还有很多疑惑:
比如为什么会失去响应式,webpack到底该怎么配置。。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
服务端渲染(SSR)将一个Vue组件在服务端渲染成HTML字符串并发送到浏览器,最后将这些静态标记“激活”为可交互应用程序的过程就叫服务端渲染(SSR)服务器渲
Nuxt.js是一个基于Vue.js的通用应用框架。通过对客户端/服务端基础架构的抽象组织,Nuxt.js主要关注的是应用的UI渲染。SSR,即服务器渲染,就是
本文主要介绍了Vue2SSR渲染根据不同页面修改meta,分享给大家,具体如下:注意:经过测试,vue-meta会导致内存泄漏,请慎用…以现在vue2的服务端渲
对于React,Vue构建的单页面应用老说,SEO是一个众所周知的问题。服务端渲染(SSR-serverSideRender)是目前看来最好的解决办法。Reac
本文实例讲述了vue服务端渲染操作。分享给大家供大家参考,具体如下:想到要学习vue-ssr的同学,自不必多说,一定是熟悉了vue,并且多多少少做过几个项目。然