专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

vue的两种服务器端渲染方案

  作者:京东零售姜欣
  关于服务器端渲染方案,之前只接触了基于react的Next。js,最近业务开发vue用的比较多,所以调研了一下vue的服务器端渲染方案。首先:长文预警,下文包括了两种方案的实践,没有耐心的小伙伴可以直接跳到方案标题下,down代码体验一下。前置知识:1、什么是服务器端渲染(ssr)?
  简单来说就是用户第一次请求页面时,页面上的内容是通过服务器端渲染生成的,浏览器直接显示服务端返回的完整html就可以,加快首屏显示速度。
  举个栗子:
  当我们访问一个商品列表时,如果使用客户端渲染(csr),浏览器会加载空白的页面,然后下载js文件,通过js在客户端请求数据并渲染页面。如果使用服务器端渲染(ssr),在请求商品列表页面时,服务器会获取所需数据并将渲染后的HTML发送给浏览器,浏览器一步到位直接展示,而不用等待数据加载和渲染,提高用户的首屏体验。2、服务器端渲染的优缺点
  优点:
  (1)更好的seo:抓取工具可以直接查看完全渲染的页面。现在比较常用的交互是页面初始展示loading菊花图,然后通过异步请求获取内容,但是但抓取工具并不会等待异步完成后再行抓取页面内容。
  (2)内容到达更快:不用等待所有的js都完成下载并执行,所以用户会更快速地看到完整渲染的页面。
  缺点:
  (1)服务器渲染应用程序,需要处于Node。jsserver运行环境
  (2)开发成本比较高
  总结:
  总得来说,决定是否使用服务器端渲染,取决于具体的业务场景和需求。对于具有大量静态内容的简单页面,客户端渲染更合适一些,因为它可以更快地加载页面。但是对于需要从服务器动态加载数据的复杂页面,服务器端渲染可能是一个更好的选择,因为他可以提高用户的首屏体验和搜索引擎优化。
  下面进入正文方案一:vue插件vueserverrender
  git示例demo地址
  结论前置:不建议用,配置成本高
  官网地址:https:v2。ssr。vuejs。orgzh
  首先要吐槽一下官网,按官网教程比较难搞,目录安排的不太合理,一顿操作项目都没起来。。。
  并且官网示例的构建配置代码是webpack4的,现在初始化项目后基本安装的都是webpack5,有一些语法不同(1)首先,先初始化一个npm项目,然后安装依赖得到一个基础项目。(此处要注意vueserverrenderer和vue必须匹配版本)npminityyarnaddvuevueserverrendererSyarnaddexpressSyarnaddwebpackwebpackclifriendlyerrorswebpackpluginvueloaderbabelloaderbabelcoreurlloaderfileloadervuestyleloadercssloadersassloadersasswebpackmergewebpacknodeexternalsDyarnaddcleanwebpackpluginbabelpresetenvDyarnaddrimraf模拟linx的删除命令,在build时先删除distyarnaddwebpackdevmiddlewarewebpackhotmiddlewareDyarnaddchokidarD监听变化yarnaddmemoryfsDyarnaddnodemonD。。。实在太多,如有缺失可以在package。json中查找另外:我现在用的vueloader:15。9。0版本,之前用的是vueloader:17。0。1,报了一个styles的错(2)配置app。js,entryclient。js,entryserver。js,将官网参考中的示例代码拷贝至对应文件。
  app。jsimportVuefromvueimportAppfrom。App。vueimport{createRouter}from。routerimport{createStore}from。storeimport{sync}fromvuexroutersync导出一个工厂函数,用于创建新的应用程序、router和store实例exportfunctioncreateApp(){创建router和store实例constroutercreateRouter()conststorecreateStore()sync(store,router)constappnewVue({router,store,render:hh(App)})return{app,router,store}}
  entryclient。jsimportVuefromvueimport{createApp}from。appVue。mixin({beforeMount(){const{asyncData}this。optionsif(asyncData){this。dataPromiseasyncData({store:this。store,route:this。route})}}})const{app,router,store}createApp()if(window。INITIALSTATE){store。replaceState(window。INITIALSTATE)}router。onReady((){在初始路由resolve后执行,使用router。beforeResolve(),以便确保所有异步组件都resolve。router。beforeResolve((to,from,next){constmatchedrouter。getMatchedComponents(to)constprevMatchedrouter。getMatchedComponents(from)找出两个匹配列表的差异组件letdiffedfalseconstactivatedmatched。filter((c,i){returndiffed(diffed(prevMatched〔i〕!c))})if(!activated。length){returnnext()}Promise。all(activated。map(c{if(c。asyncData){returnc。asyncData({store,route:to})}}))。then((){next()})。catch(next)})app。mount(app)})
  entryserver。jsimport{createApp}from。appexportdefaultcontext{返回一个promise,服务器能够等待所有的内容在渲染前,已经准备就绪,returnnewPromise((resolve,reject){const{app,router,store}createApp()router。push(context。url)router。onReady((){constmatchedComponentsrouter。getMatchedComponents()if(!matchedComponents。length){returnreject({code:404})}对所有匹配的路由组件调用asyncData()Promise。all(matchedComponents。map(Component{if(Component。asyncData){returnComponent。asyncData({store,route:router。currentRoute})}}))。then((){context。statestore。stateresolve(app)})。catch(reject)},reject)})}(3)在根目录下创建server。js文件
  其中一个非常重要的api:createBundleRenderer,这个api上面有一个方法renderToString将代码转化成html字符串,主要功能就是把用webpack把打包后的服务端代码渲染出来。具体了解可看官网bundlerenderer指引。server。jsconstapprequire(express)()const{createBundleRenderer}require(vueserverrenderer)constfsrequire(fs)constpathrequire(path)constresolvefilepath。resolve(dirname,file)constisProdprocess。env。NODEENEproductionconstcreateRenderer(bundle,options){returncreateBundleRenderer(bundle,Object。assign(options,{basedir:resolve(。dist),runInNewContext:false,}))}letrenderer,readyPromiseconsttemplatePathresolve(。srcindex。template。html)if(isProd){constbundlerequire(。distvuessrserverbundle。json)constclientManifestrequire(。distvuessrclientmanifest。json)consttemplatefs。readFileSync(templatePath,utf8)renderercreateRenderer(bundle,{推荐template,(可选)页面模板clientManifest(可选)客户端构建manifest})}else{开发模式readyPromiserequire(。configsetupdevserver)(app,templatePath,(bundle,options){renderercreateRenderer(bundle,options)})}constrender(req,res){constcontext{title:hellossrwithwebpack,meta:metacharsetUTF8metanameviewportcontentwidthdevicewidth,initialscale1。0metahttpequivXUACompatiblecontentieedge,url:req。url}renderer。renderToString(context,(err,html){if(err){if(err。code404){res。status(404)。end(Pagenotfound)}else{res。status(500)。end(InternalServerError)}}else{res。end(html)}})}在服务器处理函数中app。get(,isProd?render:(req,res){readyPromise。then(()render(req,res))})app。listen(8080)监听的是8080端口(4)接下来是config配置
  在根目录新增config文件夹,然后新增四个配置文件:webpack。base。config,webpack。client。config,webpack。server。config,setupdevserver(此方法是一个封装,为了配置个热加载,差点没搞明白,参考了好多)(官网传送门:构建配置)
  大部分官网有示例代码,但是要在基础上进行一些更改
  webpack。base。configwebpack。base。configconstpathrequire(path)用来处理后缀为。vue的文件const{VueLoaderPlugin}require(vueloader)constFriendlyErrorsWebpackPluginrequire(friendlyerrorswebpackplugin)定位到根目录constresolve(dir)path。join(path。resolve(dirname,。。),dir)打包时会先清除一下const{CleanWebpackPlugin}require(cleanwebpackplugin)constisProdprocess。env。NODEENVproductionmodule。exports{mode:isProd?production:development,output:{path:resolve(dist),publicPath:dist,filename:〔name〕。〔chunkhash〕。js},resolve:{alias:{public:resolve(public)}},module:{noParse:es6promise。js,rules:〔{test:。vue,loader:vueloader,options:{compilerOptions:{preserveWhiteSpace:false}}},{test:。js,loader:babelloader,exclude:nodemodules},{test:。(pngjpggifsvg),loader:urlloader,options:{limit:10000,name:〔name〕。〔ext〕?〔hash〕}},{test:。s(ac)ss?,use:〔vuestyleloader,cssloader,sassloader〕}〕},performance:{hints:false},plugins:〔newVueLoaderPlugin(),编译后的友好提示,比如编译完成或者编译有错误newFriendlyErrorsWebpackPlugin(),打包时会先清除一下newCleanWebpackPlugin()〕}
  webpack。client。configwebpack。client。configconst{merge}require(webpackmerge)constbaseConfigrequire(。webpack。base。config。js)constVueSSRClientPluginrequire(vueserverrendererclientplugin)module。exportsmerge(baseConfig,{entry:{app:。srcentryclient。js},optimization:{重要信息:这将webpack运行时分离到一个引导chunk中,以便可以在之后正确注入异步chunk。这也为你的应用程序vendor代码提供了更好的缓存。splitChunks:{name:manifest,minChunks:Infinity}},plugins:〔此插件在输出目录中生成vuessrclientmanifest。json。newVueSSRClientPlugin()〕})
  webpack。server。configwebpack。server。configconst{merge}require(webpackmerge)constnodeExternalsrequire(webpacknodeexternals)webpack的基础配置,比如sass,less预编译等constbaseConfigrequire(。webpack。base。config。js)constVueSSRServerPluginrequire(vueserverrendererserverplugin)module。exportsmerge(baseConfig,{将entry指向应用程序的serverentry文件entry:。srcentryserver。js,target:node,对bundlerenderer提供sourcemap支持devtool:sourcemap,此处告知serverbundle使用Node风格导出模块(Nodestyleexports)output:{libraryTarget:commonjs2},https:webpack。js。orgconfigurationexternalsfunctionhttps:github。comliadywebpacknodeexternals外置化应用程序依赖模块。可以使服务器构建速度更快,并生成较小的bundle文件。externals:nodeExternals({不要外置化webpack需要处理的依赖模块。你可以在这里添加更多的文件类型。例如,未处理。vue原始文件,你还应该将修改global(例如polyfill)的依赖模块列入白名单allowlist:。css}),这是将服务器的整个输出构建为单个JSON文件的插件。默认文件名为vuessrserverbundle。jsonplugins:〔newVueSSRServerPlugin()〕})
  setupdevserver:封装createRenderer方法constwebpackrequire(webpack)constfsrequire(fs)constpathrequire(path)constchokidarrequire(chokidar)constmiddlewarerequire(webpackdevmiddleware)constHMRrequire(webpackhotmiddleware)constMFSrequire(memoryfs)constclientConfigrequire(。webpack。client。config)constserverConfigrequire(。webpack。server。config)constreadFile(fs,file){try{returnfs。readFileSync(path。join(clientConfig。output。path,file),utf8)}catch(error){}}constsetupServer(app,templatePath,cb){letbundleletclientManifestlettemplateletreadyconstreadyPromisenewPromise(rreadyr)templatefs。readFileSync(templatePath,utf8)constupdate(){if(bundleclientManifest){通知server进行渲染执行createRendererRenderToStringready()cb(bundle,{template,clientManifest})}}webpackentryserverbundleconstmfsnewMFS();constserverCompilerwebpack(serverConfig);serverCompiler。outputFileSystemmfs;serverCompiler。watch({},(err,stats){if(err)throwerr之后读取输出:statsstats。toJson()stats。errors。forEach(errconsole。error(err))stats。warnings。forEach(errconsole。warn(err))if(stats。errors。length)returnbundleJSON。parse(readFile(mfs,vuessrserverbundle。json))update()});clientConfig。plugins。push(newwebpack。HotModuleReplacementPlugin())clientConfig。entry。app〔webpackhotmiddlewareclient,clientConfig。entry。app〕clientConfig。output。filename〔name〕。jsconstclientCompilerwebpack(clientConfig);constdevMiddlewaremiddleware(clientCompiler,{noInfo:true,publicPath:clientConfig。output。publicPath,logLevel:silent})app。use(devMiddleware);app。use(HMR(clientCompiler));clientCompiler。hooks。done。tap(clientsBuild,stats{statsstats。toJson()stats。errors。forEach(errconsole。error(err))stats。warnings。forEach(errconsole。warn(err))if(stats。errors。length)returnclientManifestJSON。parse(readFile(devMiddleware。fileSystem,vuessrclientmanifest。json))update()})fstemplatePathtemplatechokidar。watch(templatePath)。on(change,(){templatefs。readFileSync(templatePath,utf8)console。log(templateisupdated);update()})returnreadyPromise}module。exportssetupServer(5)配置搞完了接下来是代码渲染
  在src目录下,新增index。template。html文件,将官网中的例子(地址:使用一个页面模板)复制,并进行一些更改htmlhead!使用双花括号(doublemustache)进行HTML转义插值(HTMLescapedinterpolation)title{{title}}title!使用三花括号(triplemustache)进行HTML不转义插值(nonHTMLescapedinterpolation){{{meta}}}headbody!这个是告诉我们在哪里插入正文的内容!vuessroutletbodyhtml(6)再搞个store和api模拟一下数据请求
  这里介绍一下一个很重要的东西asyncData预取数据,预取数据是在vue挂载前,所以下文这里用了上下文来获取store而不是thisasyncData:({store}){returnstore。dispatch(getDataAction)},
  在src下创建api文件夹,并在下面创建data。js文件data。jsconstgetData()newPromise((resolve){setTimeout((){resolve(〔{id:1,item:测试1},{id:2,item:测试2},〕)},1000)})export{getData}
  在src下创建store文件夹,并在下面创建index。js文件store。jsimportVuefromvueimportVuexfromvuexVue。use(Vuex)import{getData}from。。apidataexportfunctioncreateStore(){returnnewVuex。Store({state:{lists:〔〕},actions:{getDataAction({commit}){returngetData()。then((res){commit(setData,res)})}},mutations:{setData(state,data){state。listsdata}}})}(7)编写组件,在srccomponents文件夹下写两个组件,在app。vue中引用一下,用上刚写的模拟数据
  Hello。vuetemplate这里是测试页面一p{{item}}routerlinktohello1链接到测试页面二routerlinktemplatestylelangscssscopedstyle
  Hello1。vuetemplate这里是测试页面二{{item}}templatestylelangscssscopedstyle(8)配置路由并在app。vue使用路由
  router。jsimportVuefromvueimportRouterfromvuerouterVue。use(Router)exportfunctioncreateRouter(){returnnewRouter({mode:history,routes:〔{path:hello,component:()import(。componentsHello。vue)},{path:hello1,component:()import(。componentsHello1。vue)},〕})}
  app。vuetemplaterouterviewrouterviewtemplatestylelangscssscopedstyle(9)根目录下创建一个。babelrc,进行配置{presets:〔〔babelpresetenv,{modules:false}〕〕}(10)改写package。json执行命令dev:nodemonserver。js,build:rimrafdistnpmrunbuild:clientnpmrunbuild:server,build:client:webpackconfigconfigwebpack。client。config。js,build:server:webpackconfigconfigwebpack。server。config。js
  大搞告成,执行一下dev命令,可以通过访问localhost:8080端口看到页面,记得带上路由哦~
  执行build命令可看到,最后dist文件下共有三个文件:main。〔chunkhash〕。js,vuessrclientmanifest。json,vuessrserverbundle。json
  附上文件整体目录结构
  方案二:基于vue的nuxt。js通用应用框架
  git示例demo地址
  一对比,这个就显得丝滑多了~官网地址:nuxt。js
  先对比一下两种方案的差别1。vue初始化虽然有cli,但是nuxt。js的cli更加完备2。nuxt有更合理的工程化目录,vue过于简洁,比如一些component,api文件夹都是要手动创建的3。路由配置:传统应用需要自己来配置,nuxt。js自动生成4。没有统一配置,需手动创建。nuxt。js会生成nuxt。config。js5。传统不易与管理底层框架逻辑(nuxt支持中间件管理,虽然我还没探索过这里)
  显而易见这个上手就快多了,也不需要安装一大堆依赖,如果用了sass需要安装sass和sassloader,反正我是用了(1)创建一个项目可选npm,npx,yarn,具体看官方文档npminitnuxtappprojectname(2)pages下面创建几个文件
  nuxt是通过pages页面形成动态的路由,不用手动配置路由。比如在pages下面新增了个文件about。vue,那么这个页面对应的路由就是about
  其实这个时候运行npmrundev就可以看到简单的页面了(3)模拟接口
  这里介绍一个插件,可以快速创建一个服务npmijsonserver
  安装完后,在根目录新增db。json文件,模拟几个接口{post:〔{id:1,title:jsonserver,author:jx}〕,comments:〔{id:1,body:somecomment,postId:1}〕,profile:{name:typicode}}
  运行命令jsonserverwatchdb。jsonport8000(不加会端口冲突),就可以看到
  因为是get请求,可以直接点击访问可以看到mock的数据已经返回了
  (4)页面调用
  先配置一下axios,推荐使用nuxt。js封装的axios:nuxtjsaxios:5。13。6,然后再在nuxt。config。js文件中modules下面配置一下就可以使用了modules:〔nuxtjsaxios〕,
  随便找个接口调用一下template这里是测试页面一接口返回数据:{{posts}}template
  刷新下页面就可以看到效果了,这里注意axios有两个get方法,一个axios。get还会返回头部等信息,另一个axios。get只返回结果
  总结:
  从页面篇幅上应该也能看到哪个容易上手了,nuxt相对于插件来说限定了文件夹的结构,并通过此预定了一些功能,更好上手。预设了利用vue。js开发服务端渲染所需要的各种配置,并且提供了提供了静态站点,异步数据加载,中间件支持,布局支持等

想买车再等等!新型电池成本低40,新能源新一波降价潮要来了遥想两年前,特斯拉由于新版车型采用磷酸铁锂电池,降低了成本,于是高调宣布降价3万元,掀起了新能源汽车行业的降价潮。正当我们以为,电动汽车的价格会不断下探,最终和燃油车持平的时候,涨Shell脚本习题打印三角形需求打印一个正三角形,要求用户输入一个数字n,然后打印边长为n个字符的正三角形。需求分析1。正三角形,即三条边长度相同,三个角都为602。脚本需要用户交互,所以使用readp实现,华为P60Pro概念图继续突破,你永远可以相信华为大家都知道,创新和研发是智能手机发展必不可少的,而只有持续的创新和研发,手机厂商才能拥有更好的未来。华为手机作为行业里一个赫赫有名的手机厂商,通过大量人力物力的投入,以至于华为手机华为Mate50Pro对比小米12SUltra,摄影作品华为Mate50Pro小米12SUltra他俩的对比视频上线了,评论区很热闹。视频链接如下华为Mate50Pro对比小米12SUltra如今的旗舰手机都在2022年双十一热门平板PK苹果华为OPPO,怎么选?平板电脑拥有比笔记本更加轻巧便携的特性,以及相比手机更大的屏幕,不仅日常的影音娱乐比手机体验更好,还能应对轻度的办公任务。配合手写笔还可以解锁绘画做笔记等更多场景。在双11到来之际华为运动健康迎来13。0。0。310版本,快去升级吧今天中午,华为应用商城推送了华为运动健康13。0。0。310,也就是说之前的12。1。8。320成为了历史,进去了13版本的更迭,从更新内容上看主要有三个方面1应用图标更新,带来新大疆会成为第二个华为?拆解数据出炉国外芯片技术占八成近段时间,无人机这项前沿科技产品成了国际瞩目的焦点,其实不只是军用,在民用方面,无人机的使用率也在逐渐提升,不管救援灾区投递物资或者是娱乐航拍,无人机都能高效的完成任务。在全球无人更专业的技术服务,华为云大数据解决方案赋能企业高效发展更专业的技术服务,华为云大数据解决方案赋能企业高效发展!对于企业的发展来说,在数字化高度发展的今天,一些企业将信息化与数字化混淆,以至于在进行数字化转型的过程中,成效不显著,让数据中共国务院党组召开会议深入学习贯彻党的二十大精神李克强主持会议并讲话新华社北京10月24日电10月24日,国务院总理党组书记李克强主持召开国务院党组会议,深入学习贯彻党的二十大精神。会议指出,党的二十大回顾总结了过去五年的工作和新时代十年的伟大变革大思政课创建图书阅览空间,以原著阅读强化理论学习习近平总书记指出,共产党人要把读马克思主义经典悟马克思主义原理当作一种生活习惯当作一种精神追求,用经典涵养正气淬炼思想升华境界指导实践。学习理论最有效的办法是读原著学原文悟原理,强爆笑神回复日常扎双马尾入学校,导致在全校出名怎么办?喜欢的小伙伴们可以移动您尊贵的手指点击右上角关注哦,每日必更!你捡过最大的漏是什么?没有技术没有文凭的人做什么工作可以月薪9000左右?来聊,日常扎双马尾入学校,导致在全校出名怎么
又是福建企业家,身家510亿,赶超曹德旺成为新一代玻璃大王众所周知,曹德旺一直都被称之为玻璃大王,除了他传奇的创业经历,他大方的捐款更是受到老百姓的尊敬和爱戴。但是长江后浪推前浪,没有人永远能一直位居榜首,这次玻璃大王的称号换人了,我们今三到五月份可能面临更严峻的考验!提醒大家要提前做好准备进入三月份,随着气温的升高,新冠病毒对人的影响也越来越小了,出行游玩的人也多了起来,甚至大街上还有不戴口罩的,但是不得不提醒大家,新冠病毒并没有完全消失。感染新冠病毒后,身体会产生陈乔恩泰国被偶遇,喝酒破怀孕谣传,丈夫陪度假多月被质疑不工作3月12日,陈乔恩和Alan前往泰国度假,到商场里购物的时候,陈乔恩对镜自拍,调侃坐在沙发上休息的老公累到全身酸痛,但关键是自己还没有买完。陈乔恩身穿连衣裙,小腹高高隆起,不少网友卸下戎装,首批春季入伍士兵退役!作者刘小均徐文峰凡兴涛柯颖达烟花三月陌上花开君子于役圆满归期近日第75集团军某旅隆重举行春季退役士兵仪式送别一年两征后首批春季入伍士兵退役士兵们脱下军装离开军营为军旅生涯献上最后一教育均衡发展能否实现?村镇学校现状调查中国小康网独家专稿文小康中国小康网记者刘建华今年春节前后一个月来,小康杂志中国小康网记者在粤东粤西粤北等地采访调研时发现,现实中的乡村教育远比想象中的情况更严峻,学生不愿学家长随便第十六届福建省戏剧水仙花奖比赛评审结果公示东南网3月14日讯(本网记者陈楠)记者今日获悉,由福建省文学艺术界联合会福建省文化和旅游厅共同主办,福建省戏剧家协会承办的第十六届福建省戏剧水仙花奖决赛于2023年3月1日至12日2023年2月份居民消费价格同比上涨1。0,环比下降0。5根据国家统计局3月15日公布的最新数据,2023年2月份,全国居民消费价格同比上涨1。0。其中,城市上涨1。0,农村上涨1。0食品价格上涨2。6,非食品价格上涨0。6消费品价格上涨央广网评自媒体要留住粉丝首先要自己美近年来,随着互联网的普及和相关应用的升级,网络上涌现出了一大批自媒体账号。几乎每一个网民都可以通过个人账号生产分享传播网络信息,并引起网友的关注。例如,有的旅游博主通过分享旅行中的华创证券看好2023年贵金属表现当前处于中长期黄金标的配置窗口期智通财经APP获悉,华创证券发布研究报告称,看好2023年贵金属表现,当前正处于中长期黄金标的配置窗口期。2月份美国强劲的就业数据再次带动加息预期提升,金价出现阶段性回调,市场过于让乡村领头雁专起来日前召开的基层党建工作重点任务推进会指出,要全面提升乡镇村班子领导力,深入推进抓党建促乡村振兴。村党组织书记作为农村发展的带头人,是推动乡村振兴的火车头。要通过专岗性任职专业化培养撒马尔罕,中亚丝绸之路的十字路口,迷人的建筑亲眼所见此生无憾撒马尔罕Citywalk古里埃米尔陵GuriAmirAksarayMausoleum比比哈內姆清真寺BiBiKhanymMosqueHazratKhizrMosque夏伊辛达Sha
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网