当前位置: 首页 > news >正文

山西省建设厅网站 孙涛东营最新通知

山西省建设厅网站 孙涛,东营最新通知,wordpress当前分类id,找工程承包本文和个人博客同步发表 更多优质文章查看个人博客 前言 手动搭建 vue ssr 一直是一些前端开发者的噩梦#xff0c;因为其中牵扯到很多依赖包之间的配置以及webpack在node中的使用。就拿webpack配置来说#xff0c;很多前端开发者还是喜欢用webpack-cli脚手架搭建项目。导致…本文和个人博客同步发表 更多优质文章查看个人博客 前言 手动搭建 vue ssr 一直是一些前端开发者的噩梦因为其中牵扯到很多依赖包之间的配置以及webpack在node中的使用。就拿webpack配置来说很多前端开发者还是喜欢用webpack-cli脚手架搭建项目。导致这样的原因之一无外乎学习成本高软件复杂等。这也是有些前端开发者直接拥抱nuxt.js的部分原因。这篇博客使用vue2以步骤为主来展示如何创建完整ssr开发环境。 主要参考文章 vue2 ssr 中文官网webpack 中文官网webpack-dev-middlewarewebpack-hot-middlewarewebpack tapablememory-fsexpressvue2 ssr 官方参考案例 构建ssr所需依赖包 {devDependencies: {chokidar: ^3.5.3,css-loader: ^6.7.3,memory-fs: ^0.5.0,vue-loader: ^15.9.8,vue-style-loader: ^4.1.3,webpack: ^5.54.0,webpack-dev-middleware: ^5.2.1,webpack-hot-middleware: ^2.25.1,webpack-node-externals: ^3.0.0},scripts: {dev: node ./server/index.cjs},dependencies: {express: ^4.18.2,vue: ^2.6.14,vue-router: ^3.5.2,vue-server-renderer: ^2.6.14,vue-template-compiler: ^2.6.14,vuex: ^3.6.2,vuex-router-sync: ^5.0.0} } 目录结构 需要创建如下图的目录结构方能进行后面的代码编写 ssr是如何生成的 了解完目录架构之后首先需要知道ssr是如何生成的是至关重要的只有这样我们才了解后续通过什么样的操作来构建ssr。首先看一张官方给出的构建图。 从图中可以看出想要实现ssr必选通过webpack构建生成的服务端bundle文件和客户端bundle文件服务端bundle在bundle renderer的作用下生成html字符串并发送给浏览器端并且和客户端bundle一起作用下激活最终实现ssr。代码中创建html字符串和发送到浏览器的实现如下所示 let devServerPromise devServer((serverBundle, options) {// 服务端生成的bundele和客户端生成的clientManifest结合并返回rendererrenderer createBundleRenderer(serverBundle, Object.assign(options, {runInNewContext: false,})) });ROUTER.get(*, (req, res) {const context {url: req.url}devServerPromise.then((random) {// 将 Vue 实例渲染为字符串发送给客户端renderer.renderToString(context).then(html {res.send(html)}).catch(err {console.log(err,req.url,err)})}) })其实在构建图中还少一个关键点如下所示 webpack在编译客户端时会生成客户端构建清单(clientManifest)清单里面的内容其实是当html字符串在浏览器中解析时要获取的资源内容也可简单理解为client bundle当解析html时根据内容去请求client bundle。 好到目前为止实现vue ssr思路已经很清晰了。在这里简单梳理一下。 #mermaid-svg-HbCR2MFq9PeOUqVK {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-HbCR2MFq9PeOUqVK .error-icon{fill:#552222;}#mermaid-svg-HbCR2MFq9PeOUqVK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HbCR2MFq9PeOUqVK .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-HbCR2MFq9PeOUqVK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HbCR2MFq9PeOUqVK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HbCR2MFq9PeOUqVK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HbCR2MFq9PeOUqVK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HbCR2MFq9PeOUqVK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HbCR2MFq9PeOUqVK .marker.cross{stroke:#333333;}#mermaid-svg-HbCR2MFq9PeOUqVK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HbCR2MFq9PeOUqVK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HbCR2MFq9PeOUqVK .cluster-label text{fill:#333;}#mermaid-svg-HbCR2MFq9PeOUqVK .cluster-label span{color:#333;}#mermaid-svg-HbCR2MFq9PeOUqVK .label text,#mermaid-svg-HbCR2MFq9PeOUqVK span{fill:#333;color:#333;}#mermaid-svg-HbCR2MFq9PeOUqVK .node rect,#mermaid-svg-HbCR2MFq9PeOUqVK .node circle,#mermaid-svg-HbCR2MFq9PeOUqVK .node ellipse,#mermaid-svg-HbCR2MFq9PeOUqVK .node polygon,#mermaid-svg-HbCR2MFq9PeOUqVK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HbCR2MFq9PeOUqVK .node .label{text-align:center;}#mermaid-svg-HbCR2MFq9PeOUqVK .node.clickable{cursor:pointer;}#mermaid-svg-HbCR2MFq9PeOUqVK .arrowheadPath{fill:#333333;}#mermaid-svg-HbCR2MFq9PeOUqVK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HbCR2MFq9PeOUqVK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HbCR2MFq9PeOUqVK .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-HbCR2MFq9PeOUqVK .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-HbCR2MFq9PeOUqVK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HbCR2MFq9PeOUqVK .cluster text{fill:#333;}#mermaid-svg-HbCR2MFq9PeOUqVK .cluster span{color:#333;}#mermaid-svg-HbCR2MFq9PeOUqVK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HbCR2MFq9PeOUqVK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}需要需要依赖依赖生成生成生成生成html字符串webapckclientBundlerenderToString函数serverBundleclientManifest根据上面的图可知想要实现ssr就是需要serverBundle、clientBundle、clientManifest文件。而这三种文件又是通过webpack生成的。所以现在的要面临的问题就是配置webpack生成这三种文件然后通过renderToString函数实现ssr。话不多说开始配置。 配置 webpack 在配置webpack之前需要先创建要打包的代码、app.js、entry-client.js和entry-server.js。创建这些内容的作用是为后续webpack打包做准备。 1. 创建UAC(Universal Application Code)相关文件 在ssr-demo文件夹下的client文件夹中分别创建router、store和views文件夹和相关内容。 1.1 router文件夹和相关内容 // 路径 client/router/index.js import Vue from vue import Router from vue-routerVue.use(Router);export function createRouter(){return new Router({mode:history,routes:[{path:/,component: () import(../views/index.vue)},]}) }1.2 store文件夹和相关内容 // 路径 client/store/index.js import Vue from vue import Vuex from vuexVue.use(Vuex);export function createStore(){return new Vuex.Store({state:{},actions:{},mutations:{}}) }1.3 view文件夹和相关内容 !-- 路径 client/views/index.vue -- templatedivkinghiee ssr test/div /templatescript export default {} /scriptstyle/style2. 创建app.js文件 // 路径 client/app.js import Vue from vue; import App from ./App.vue; import { createRouter } from ./router/index; import { createStore } from ./store; import { sync } from vuex-router-sync;// 简单工厂模式创建vue实例 export function createApp() {const router createRouter();const store createStore();sync(store, router);const app new Vue({router,store,render: h h(App),})return {app,router,store} };app.js文件的作用: 使用简单工厂模式创建vue实例为每个请求创建新的应用程序实例避免状态单例。更加详细解释请查看官网; 3. 创建entry-client.js文件 // 路径 client/entry-client.js import { createApp } from ./app; import Vue from vue;const { app, router, store } createApp();Vue.mixin({/*** 路由更新触发组件内异步获取数据方法* param {*} to* param {*} from* param {*} next*/beforeRouteUpdate(to, from, next) {const { asyncData } this.$optionsif (asyncData) {asyncData({store: this.$store,route: to}).then(next).catch(next)} else {next()}} });if (window.__INITIAL_STATE__) {store.replaceState(window.__INITIAL_STATE__); }router.onReady(() {router.beforeResolve((to, from, next) {const matched router.getMatchedComponents(to);const prevMatched router.getMatchedComponents(from);let diffed false;const activated matched.filter((c, i) {return diffed || (diffed (prevMatched[i] ! c))})if (!activated.length) {return next();}Promise.all(activated.map(c {if (c.asyncData) {return c.asyncData({ store, route: to })}})).then(() {next();}).catch(next)})app.$mount(#app) }) 4. 创建entry-server.js文件 // 路径 client/entry-server.js import { createApp } from ./appexport default context {return new Promise((resolve, reject) {const { app, router, store } createApp()router.push(context.url)router.onReady(() {const matchedComponents router.getMatchedComponents()if (!matchedComponents.length) {return reject({ code: 404 })}// 对所有匹配的路由组件调用 asyncData()Promise.all(matchedComponents.map(Component {if (Component.asyncData) {return Component.asyncData({store,route: router.currentRoute})}})).then(() {// 在所有预取钩子(preFetch hook) resolve 后// 我们的 store 现在已经填充入渲染应用程序所需的状态。// 当我们将状态附加到上下文// 并且 template 选项用于 renderer 时// 状态将自动序列化为 window.__INITIAL_STATE__并注入 HTML。context.state store.stateresolve(app)}).catch(reject)}, reject)}) }注entry-client.js和entry-server.js文件以及文件中为什么这么写在官网中都能找的到这里只说明搭建步骤 官网 相关代码创建完毕之后client文件夹内容大致如下 到此为止webpack打包前期的准备工作已经结束。接下来开始配置webpack打包 5. webpack配置 在ssr webpack打包配置中分为客户端和服务端配置。客户端打包配置主要生成后面用到的客户端bundle和clientManifest而服务端打包配置主要生成后面用到的服务端bundle这三种文件正是ssr所需的关键文件。 5.1 客户端打包配置 // 路径 build/webpack.client.dev.js const { resolve: RESOLVE } require(path); const WEBPACK require(webpack); const { VueLoaderPlugin: VUELOADERPLUGIN } require(vue-loader); const VUESSRCLIENTPLUGIN require(vue-server-renderer/client-plugin)module.exports {mode: development,entry: { app: RESOLVE(__dirname, ../client/entry-client.js) },output: {path: RESOLVE(__dirname, ../dist),filename: src/[name].[contenthash:6].js,publicPath: /dist/},module: {rules: [{test: /\.vue$/,loader: vue-loader},{test: /\.css$/i,use: [vue-style-loader, css-loader],},]},resolve: {extensions: [.js, .ts, .vue, .json],alias: {client:RESOLVE(__dirname,../client)}},plugins: [new VUELOADERPLUGIN(),new VUESSRCLIENTPLUGIN({filename: src/vue-ssr-client-manifest.json})] } 5.2 服务端打包配置 // 路径 build/webpack.server.dev.js const { resolve: RESOLVE } require(path); const { VueLoaderPlugin: VUELOADERPLUGIN } require(vue-loader); const VUESERVERPlUGINSSR require(vue-server-renderer/server-plugin) const NODEEETERNALS require(webpack-node-externals);module.exports {target: node,devtool: eval-cheap-source-map,entry: RESOLVE(__dirname, ../client/entry-server.js),output: {path: RESOLVE(__dirname, ../dist),filename: server-bundle.js,libraryTarget: commonjs2},module: {rules: [{test: /\.vue$/,loader: vue-loader},{test: /\.css$/i,use: [vue-style-loader, css-loader],},]},externalsPresets: { node: true }, // in order to ignore built-in modules like path, fs, etc.externals: [NODEEETERNALS()], // in order to ignore all modules in node_modules folderplugins: [new VUELOADERPLUGIN(),new VUESERVERPlUGINSSR({filename: src/vue-ssr-server-bundle.json})] }webpack 客户端和服务端都已配置好了那如何生成相应的三种文件呐其实生成这三种文件需要在webpack编译阶段而对于配置开发环境来说一般还用到热更新和webpack dev中间件所以webpack编译和热更新常在一起出现。目前为止该准备的都已经到位了现在就可以开始webpack编译和配置热更新操作了。 webpack编译和配置热更新 在server文件夹内创建如下文件夹和文件 1. webpack编译 本博客给出的代码示例和官方的示例组织上有不同的地方但功能上一样。本博客按照功能的不同对代码进行了合理的拆分和封装而不是把全部功能写到一个函数下面。这样做的目的是关注点单一、功能单一、便于开发和维护。 1.1 编译客户端 // 路径 dev/clientCompile.cjs let webpack require(webpack); let path require(path);module.exports function clientCompile(clientConfig, clientManifestCb) {// 向客户端 webpack 修改配置clientConfig.entry.app [ webpack-hot-middleware/client?path/__webpack_hmrtimeout2000reloadtrue, clientConfig.entry.app ];clientConfig.output.filename [name].[contenthash:6].js;clientConfig.plugins.push(new webpack.HotModuleReplacementPlugin(),new webpack.NoEmitOnErrorsPlugin(),); // 编译客户端 webpack 配置let clientCompiler webpack(clientConfig); // 获取 compiler 实例let devMiddleware require(webpack-dev-middleware)(clientCompiler, {publicPath: clientConfig.output.publicPath,serverSideRender: true,stats: {//可选colors: true,modules: true,},});// done是AsyncSeriesHook类型钩子clientCompiler.hooks.done.tap(done, stats {stats stats.toJson({stats:errors-warnings});// 如果客户端编译完毕有错误或者警告会打印到控制台stats.errors.forEach(err console.error(err));stats.warnings.forEach(err console.warn(err));// 有错误后续不生成 manifest 文件if (stats.errors.length) return;console.log(\n客户端更新...\n);let manifestContent devMiddleware.context.outputFileSystem.readFileSync(path.resolve(clientConfig.output.path, src/vue-ssr-client-manifest.json),utf-8);clientManifestCb(JSON.parse(manifestContent));});let hotMiddleware require(webpack-hot-middleware)(clientCompiler); return {devMiddleware,hotMiddleware} }如上代码所示把编译客户端的代码写到一个文件内并导处客户端编译函数在其他地方用。 1.2 编译服务端 // 路径 dev/serverCompile.cjs let webpack require(webpack); let path require(path); const MFS require(memory-fs);module.exports function serverCompile(serverConfig,serverBundleCb ) {let serverCompiler webpack(serverConfig); let mfs new MFS();serverCompiler.outputFileSystem mfs; // 把 webpack 默认的普通文件系统更换为内存文件系统serverCompiler.watch({ ignored: /node_modules/, }, (err, stats) {if (err) throw err;stats stats.toJson();// 有错误后续不执行if (stats.errors.length) return;console.log(\n服务端更新...\n);// 获取服务端bundle文件路径let bundlePath path.resolve(serverConfig.output.path,src/vue-ssr-server-bundle.json);serverBundleCb(JSON.parse(mfs.readFileSync(bundlePath, utf-8)))}); }写完客户端和服务端编译后需要把函数导出来在后面的地方使用。代码如下 2. webpack编译和热更新配置 dev.cjs文件如下 // 路由 router/dev.cjs const SERVER require(express); const ROUTER SERVER.Router(); const FS require(fs); const PATH require(path); let clientConfig require(../../build/webpack.client.dev); let serverConfig require(../../build/webpack.server.dev); let templatePath PATH.resolve(__dirname, ../server.template.html); let { createBundleRenderer } require(vue-server-renderer) let serverCompile require(../dev/serverCompile.cjs); let clientCompile require(../dev/clientCompile.cjs); let tempWatch require(../dev/tempWatch.cjs);let renderer;const devServer (cb) {let clientManifest, serverBundle, readyResolve, templateContent;templateContent FS.readFileSync(templatePath, utf-8);let readyPromise new Promise(resolve readyResolve resolve );// 更新客户端和服务端内容let updateClientAndServer () {// 只有构建清单文件都存在时执行更新操作if(clientManifest serverBundle) {readyResolve(); // 把promise resolve掉cb(serverBundle, {template: templateContent,clientManifest})}};// 监听模板文件tempWatch(templatePath, () {updateClientAndServer();});// 客户端 编译let { devMiddleware, hotMiddleware} clientCompile(clientConfig, (clientManifestContent) {clientManifest clientManifestContent;updateClientAndServer();})ROUTER.use(devMiddleware);ROUTER.use(hotMiddleware); // 服务端 编译serverCompile(serverConfig, (serverBundleContent) {serverBundle serverBundleContent;updateClientAndServer();})return readyPromise; }let devServerPromise devServer((serverBundle, options) {renderer createBundleRenderer(serverBundle, Object.assign(options, {runInNewContext: false,})) });在devServer函数中分别使用clientCompile函数编译客户端。在回调函数把生成的clientManifestContent内容赋值给clientManifest然后通知updateClientAndServer函数完成后续内容。同时clientCompile函数也返回了两个中间件并放入use函数中完成后续的热更新和webpack dev server. 和clientCompile函数类似serverCompile函数它也是在回调函数中把生成的serverBundleContent赋值给serverBundle并通知updateClientAndServer函数完成其他内容。在updateClientAndServer函数中当clientManifest和serverBundle内容都有时就可以把promise resolve掉进而可以调用renderToString函数生成html字符串发送给浏览器最后实现ssr。 在devServer函数中还是用了tempWatch该函数的作用是当模板文件发生变化时更新相关内容。代码如下 // 路径 dev/tempWatch.cjs let fs require(fs); let chokidar require(chokidar);module.exports function tempWatch(templatePath, watchCb) {// 监听模板html文件 changechokidar.watch(templatePath).on(change, () {console.log(模板更新中...);templateContent fs.readFileSync(templatePath, utf-8);console.log(模板更新成功!);// 更新模块watchCb();}); }到此为止vue ssr的配置和生成基本结束。但是现在通过浏览器还是访问不了还需要最后一步配置服务器 配置服务器提供访问 在dev.cjs中添加路由配置然后导处路由在程序入口处使用。 // 路径 router/dev.cjs const SERVER require(express); const ROUTER SERVER.Router(); const FS require(fs); const PATH require(path); let clientConfig require(../../build/webpack.client.dev); let serverConfig require(../../build/webpack.server.dev); let templatePath PATH.resolve(__dirname, ../server.template.html); let { createBundleRenderer } require(vue-server-renderer) let serverCompile require(../dev/serverCompile.cjs); let clientCompile require(../dev/clientCompile.cjs); let tempWatch require(../dev/tempWatch.cjs);let renderer;const devServer (cb) {let clientManifest, serverBundle, readyResolve, templateContent;templateContent FS.readFileSync(templatePath, utf-8);let readyPromise new Promise(resolve readyResolve resolve );// 更新客户端和服务端内容let updateClientAndServer () {// 只有构建清单文件都存在时执行更新操作if(clientManifest serverBundle) {readyResolve(); // 把promise resolve掉cb(serverBundle, {template: templateContent,clientManifest})}};// 监听模板文件tempWatch(templatePath, () {updateClientAndServer();});// 客户端 编译let { devMiddleware, hotMiddleware} clientCompile(clientConfig, (clientManifestContent) {clientManifest clientManifestContent;updateClientAndServer();})ROUTER.use(devMiddleware);ROUTER.use(hotMiddleware); // 服务端 编译serverCompile(serverConfig, (serverBundleContent) {serverBundle serverBundleContent;updateClientAndServer();})return readyPromise; }let devServerPromise devServer((serverBundle, options) {renderer createBundleRenderer(serverBundle, Object.assign(options, {runInNewContext: false,})) });ROUTER.get(*, (req, res) {const context {url: req.url}devServerPromise.then(() {renderer.renderToString(context).then(html {res.send(html)}).catch(err {console.log(err,req.url,err)})}) })module.exports ROUTER;在程序入口处使用该路由 // 路径 server/index.cjs const SERVER require(express)(); const SSRROUTER require(./router/dev.cjs); const PORT 8000;SERVER.use(SSRROUTER);SERVER.listen(PORT,() {console.log(app listening at port ${PORT}); });最后输入npm run dev启动项目,结果如下 注: 配置ssr的过程有点繁琐如果途中有配置错的地方可以查看我的github ssr demo 如果博客中有什么不理解的或者错误内容欢迎指出及时更正
http://www.dnsts.com.cn/news/124894.html

相关文章:

  • 静态网站开发语言有哪些朱子网站建设
  • 六 网站建设方案.云南高端网站建设公司
  • 链接网站logo兰州正规seo整站优化
  • 建网站啦WordPress如何快速排名
  • me域名网站c2c平台是什么意思
  • cdn网站网站建设技术服务清单
  • 长春做公司网站织梦建站教程视频
  • 做购物网站需不需要交税费枣庄网站建设 网站设计 网站制作
  • asp access网站开发实例精讲网站怎样做优化网页
  • 内蒙古建设住房与城乡厅官方网站时尚大气网站
  • 酒店网站策划书禁用wordpress裁剪
  • 大城网站建设wordpress 微信采集器
  • 嘉兴专业自助建站免费咨询seoul
  • 厦门做网站 厦门专业做网站的公司 我想做网站遵化市城乡建设规划局网站
  • 经营性质的网站wordpress咋样
  • 汕头网站建设制作报价wordpress siren主题
  • 九江网站建设张旭wordpress导航改哪个php文件夹
  • 七牛怎么做网站服务器珠海高端网站建设
  • 广东建设信息网站首页谷歌搜索引擎为什么打不开
  • 网站全面详细创建步骤南宁自助模板建站
  • 网站建设进度表怎么做坪山商城网站建设哪家效益快
  • 焦作网站建设jz518小黄豆crm
  • 如何建设局域网内部网站wordpress 慢的上天
  • 一个公司做多个网站做百度推广需要自己有个网站吗
  • 重庆做网站公司排名app模板素材下载
  • 网站建设公司利润分配做网站还是小程序
  • 能看人与动物做的网站农林网站建设公司
  • 研究网站建设城市建设与管理网站
  • 六一儿童节网站制作公司网站建设教程
  • 用电脑做兼职的网站比较好10黄页网站建设