前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >进阶| Vue 2.x + Webpack 3.x + Nodejs 多页面项目框架(下篇)

进阶| Vue 2.x + Webpack 3.x + Nodejs 多页面项目框架(下篇)

作者头像
用户1097444
发布2022-06-29 15:55:20
9350
发布2022-06-29 15:55:20
举报
文章被收录于专栏:腾讯IMWeb前端团队

前端爱好者的知识盛宴

导语

这是Vue多页面框架系列文章的第二篇,上一篇中,我们尝试从webpack-simple原型项目改造为一个多页面的Vue项目。而这里,我们继续往前,尝试把Vue多页面改造为Nodejs直出。由于步骤较多,所以本文片幅较长。

上一篇:纯前端Vue多页面改造

本文源代码:

https://github.com/kenkozheng/HTML5_research/tree/master/Vue-SSR-Multipages-Webpack3

正文

1 认识原理

稍微详细的信息,大家可以参考:

官网:https://ssr.vuejs.org/zh/ 还有官方的例子:https://github.com/vuejs/vue-hackernews-2.0 不过,文档写得并不详细,也没看到文档对应的代码在哪里;而例子呢,下载后无法运行(2017年12月上旬),也是有点麻烦。

我总结一下大概的运行步骤:

•Nodejs运行vue组件输出html片段:这一步,可以理解为虚拟dom运行在Nodejs环境,换算出html的字符串,很好理解。

•Nodejs把html片段拼接到整个HTML上:这里跟客户端版本略有不同,上一篇文章中,我们针对多页面生成了多个html,而这里因为有了Nodejs的动态输出能力,就没必要生成多个html了,只需要每次把动态部分拼接到模版html上即可。

•对HTML注入数据:上一步有了HTML,但这个html只是死的字符串,到了浏览器解析后只能是普通的dom,无法启动vue还原为虚拟dom。那么就需要原始的数据,好让客户端重建对应的虚拟dom。

•浏览器运行vue重建虚拟dom:这一步跟之前纯前端的vue架构类似,不同的是,vue会识别到div已经是服务器渲染好的,并不需要重新渲染dom结构,只需要重建虚拟dom,备好数据,绑定事件即可。

那么从已有的多页面Vue框架出发,要做成多页面nodejs直出,我们需要解决几个问题。

1、怎么打包为Nodejs支持的js?

2、在这个情况下,客户端部分是否要特殊打包?怎么打包?

3、使用什么方式运行打包后的两部分代码,并生成最终的HTML?

4、怎么注入数据?客户端又怎么获取数据作用于Vue?

5、如何启动项目?热更新还能有效吗? 接下来就带着这几个问题,学习官方资料,看如何实现Vue的SSR。

2 Nodejs和浏览器分别打包

从之前的纯浏览器运行建模+渲染,到现在拆分两个过程:Nodejs输出结构、浏览器端重建虚拟dom和绑定事件,这里必然需要修改已有的webpack打包配置。

官方提供了vue-server-renderer组件。

这个组件分为client-plugin和server-plugin,分别用于客户端和Nodejs部分的打包。针对这个情况,我们需要把webpack文件修改一下,把基础部分抽离出来,把多余部分去除(例如生成html的HtmlWebpackPlugin)。

简单看看webpack.base.config.js

跟webpack-simple原型项目的配置没什么差异。主要是去掉了entry的配置,因为针对nodejs和客户端将有新的入口文件。

然后,看看Nodejs端怎么处理。 首先,需要新建一个新的app和entry文件。

app.js

原来客户端渲染是直接new Vue(),而这里改为export一个工厂方法,好让后续服务器和客户端分别用各自的方式创建。这里有个题外话,import css不能写在这了,会导致nodejs运行时缺少document对象而报错,需要写到vue文件中。

然后是server-entry.js

就是简单创建Vue实例,然后返回。这个函数接受context参数,是vue-server-renderer传入的,往context中塞数据,可以作用于最终生成的HTML,例如注入数据,这个稍后再说明。

接着再看webpack的配置。

配置不多,利用webpack-merge工具,便于合并前后两份配置。 有几个关键点:

•target: ‘node’。这个让webpack针对nodejs的module做处理。

•output的libraryTarget:设置module的具体引用方式。

•plugins中加入VueSSRServerPlugin:这个插件会让文件最后打包为一个json,用于后续运行时读入到Vue的vue-server-renderer中

再看看客户端的修改。

client-entry.js

跟服务器的略有不同,这个是针对浏览器运行的代码,创建Vue实例后,就手工挂载到已存在的节点#app上。

webpack的配置也要相应处理:

这里做了几个关键事情:

entry指向客户端打包入口

•利用chunkPlugin生成vendor.js抽离部分库文件

•生成manifest文件,记录文件名

VueSSRClientPlugin,这个插件生成vue-ssr-client-manifest.json,记录页面所有依赖文件列表,在生成最终HTML时方便注入相应的js链接和css链接

3 服务器运行

Nodejs端,我们需要引入vue-server-renderer

主要代码如下:

详细代码请查github: https://github.com/kenkozheng/HTML5_research/blob/master/Vue-SSR-Single-Page-Webpack3/server/server.js

上述代码做的是大概是: 1、读入模版html文件、打包后的两个json,从而生成bundleRenderer 2、创建render函数,接受req和res(例如用于express),使用renderToString方法,简单把整个网页拼装好返回。其中context是作用于模版html的参数对象,用法跟普通的模版引擎类似。

例如填充title

顺带说一句,HTML中需要有特殊标记<!--vue-ssr-outlet-->,用于替换为动态的Vue html片段。 vue-server-renderer会自动向模版填充js和css的外链。这个是默认的行为,如果想要把各种js和css做特殊处理,或输出更多内容,可以参考手工注入: https://ssr.vuejs.org/zh/build-config.html#manual-asset-injection

如果想更进一步,例如css、js打入html中,还可以抛弃template(createRenderer时不传入template),改为自行拼接html,只需要renderer返回vue的html片段。

至此,粗略的SSR就已经完成了。

project.json中加入

先npm run build,然后npm start就可以了。

跟上一篇文章完成的架构不一样,这里不通过webpack-dev-server启动,所以没有热更新的功能。对于实际开发而言,每次修改都要build再run,肯定太麻烦。

4 搭建热更新功能

这里,借鉴了官方例子,可以简单copy setup-dev-server.js。

setup-dev-server.js的代码比较长,就不列出来了。

github:https://github.com/kenkozheng/HTML5_research/blob/master/Vue-SSR-Single-Page-Webpack3/build/setup-dev-server.js

实现原理跟webpack-dev-server是相同的,基于express的服务。做的主要是:

•引入webpack-hot-middleware和webpack-dev-middleware,建立客户端和服务器之间热更新websocket,另外把临时文件生成到内存中

•使用webpack和chokidar,监控vue、js、html等的变化

•实现了异步的编译回调和不断的监控

我们自己主要需要修改server.js,判断是否开发环境。如果是,则使用dev-server特殊的renderer。

devServerSetup每次callback都返回最新的bundle和clientManifest,用于刷新renderer。

那么,使用node server/server就能启动热更新服务器了。

到这里,我们实现了一个没有动态数据的SSR版本,方便初学者对整个概念的理解。

代码:

https://github.com/kenkozheng/HTML5_research/tree/master/Vue-SSR-Single-Page-Webpack3

5 数据注入

接下来,我们在已有基础上,再实现动态数据。这里列出我认为比较简单易懂的两种方式和相应例子,可能实现的方式有更多。

情况1:不使用Vuex

先考虑没有Vuex的情况,只是简单粗暴的组件式从上往下传递数据。这个情况适合一些简单页面,纯粹的展示信息和几个简单的点击处理。

各个文件,我们都稍作修改。

app.vue

vue的写法从原来固定data,改为从父节点传入的props标签(appData)获取数据。

app.js

entry-server.js

server除了像之前那样直接返回app还可以返回promise对象,从而实现异步处理。关键点是把data赋值给context.state。state会被自动注入到html中,作为全局js变量__INITIAL_STATE__

entry-client.js

最后在client的代码中,拿到这个全局对象,并赋值给Vue。。。完成。。。

情况2:使用Vuex

这里建了一个例子,模拟初始化时获取数据,然后再返回给Server去渲染。

先建立一个Store

上述代码使用了page2Data别名,利用webpack的alias功能,可以快速实现一份代码,同时对接浏览器和服务器不同的数据获取方式。这也许就是“同构”的一种思路吧,有利于客户端做一些刷新逻辑时,不需要整个页面重载。

app.vue

app.js

Vue使用store,而不是组件式的传递数据。

entry-server.js

初始化时,调用store的方法,获得数据后再返回渲染。跟不用Vuex类似,数据也是塞到context.state中。

entry-client.js

客户端手工设置store的数据。

运行测试,可以发现两种方式都能正常完成页面渲染

6 多页面并存

上边提到的例子都只针对一个页面,因为webpack后,生成的vue-ssr-client-manifest.json等都只有一份。我们需要做一些优化。

既然是多页面Nodejs,那肯定需要一个路由表。我们可以在路由表中配置访问url(express正则)和代码目录。

例如:router.js

然后根据每个页面,动态生成相应的webpack配置,用于build和dev-server

这里关键点是动态设置entry和设置VueSSRClientPlugin/VueSSRServerPluginfilename。 filename这个字段官方文档是没有的,不过,node_modules基本都能找到源码,可以发现有这个动态设置的办法。 通过上述配置,让浏览器使用的js和服务器打包后的json文件分开,便于设置访问权限,防止服务器信息泄漏。

build之后的dist目录结构如下所示:

相应的,server.js中运行时和build的脚本都需要调整。

server.js

server是express实例,设置路由时,创建闭包,每个处理器都能带上对应的pageKey,从而访问对应的renderer。

build.js

build改为我们自建的js脚本

至此,一个多页面VueSSR就完成了,后续可以根据项目的具体情况添加实际的Vue组件和插件。

这里是IMWEB

一个想为更多的前端人

享知识 

助发展

觅福利

情怀有情调的公众号

欢迎关注转发

更多的前端技友一起学习发展~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯IMWeb前端团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档