摘要:簡(jiǎn)介搭建多頁(yè)面服務(wù)端渲染技術(shù)點(diǎn)搭建服務(wù)創(chuàng)建頁(yè)面路由模板引擎組合打包多頁(yè)面端異步請(qǐng)求服務(wù)端日志打印項(xiàng)目源碼運(yùn)行一現(xiàn)代服務(wù)端渲染的由來(lái)服務(wù)端渲染概念是指,瀏覽器向服務(wù)器發(fā)出請(qǐng)求頁(yè)面,服務(wù)端將準(zhǔn)備好的模板和數(shù)據(jù)組裝成完整的返回給瀏覽器展示前端后
簡(jiǎn)介
nodejs搭建多頁(yè)面服務(wù)端渲染
技術(shù)點(diǎn)
koa 搭建服務(wù)
koa-router 創(chuàng)建頁(yè)面路由
nunjucks 模板引擎組合html
webpack打包多頁(yè)面
node端異步請(qǐng)求
服務(wù)端日志打印
項(xiàng)目源碼 git clone https://gitee.com/wjj0720/nod...
運(yùn)行
npm i
npm start
一、 現(xiàn)代服務(wù)端渲染的由來(lái)服務(wù)端渲染概念: 是指,瀏覽器向服務(wù)器發(fā)出請(qǐng)求頁(yè)面,服務(wù)端將準(zhǔn)備好的模板和數(shù)據(jù)組裝成完整的HTML返回給瀏覽器展示
1、前端后端分離
早在七八年前,幾乎所有網(wǎng)站都使用 ASP、Java、PHP做后端渲染,隨著網(wǎng)絡(luò)的加快,客戶(hù)端性能提高以及js本身的性能提高,我們開(kāi)始往客戶(hù)端增加更多的功能邏輯和交互,前端不再是簡(jiǎn)單的html+css更多的是交互,前端頁(yè)在這是從后端分離出來(lái)「前后端正式分家」
2、客戶(hù)端渲染
隨著ajax技術(shù)的普及以及前端框架的崛起(jq、Angular、React、Vue) 框架的崛起,開(kāi)始轉(zhuǎn)向了前端渲染,使用 JS 來(lái)渲染頁(yè)面大部分內(nèi)容達(dá)到局部刷新的作用
優(yōu)勢(shì)
局部刷新,用戶(hù)體驗(yàn)優(yōu)
富交互
節(jié)約服務(wù)器成本
缺點(diǎn)
不利于SEO(爬蟲(chóng)無(wú)法爬取ajax)請(qǐng)求回來(lái)的數(shù)據(jù)
受瀏覽器性能限制、增加手機(jī)端的耗電
首屏渲染需要等js運(yùn)行才能展示數(shù)據(jù)
3、現(xiàn)在服務(wù)端渲染
為了解決上面客戶(hù)端渲染的缺點(diǎn),然前后端分離后必不能合,如果要把前后端部門(mén)合并,拆掉的肯定是前端部門(mén)
現(xiàn)在服務(wù)端渲染的特點(diǎn)
前端開(kāi)發(fā)人員編寫(xiě)html+css模板
node中間服務(wù)負(fù)責(zé)前端模板和后臺(tái)數(shù)據(jù)的組合
數(shù)據(jù)依然由java等前服務(wù)端語(yǔ)言提供
優(yōu)勢(shì)
前后端分工明確
SEO問(wèn)題解決
4、前、后端渲染相關(guān)討論參考
知乎問(wèn)答:為什么現(xiàn)在又流行服務(wù)器端渲染html
精讀前后端渲染之爭(zhēng)
服務(wù)端渲染 vs 客戶(hù)端渲染
二、 項(xiàng)目開(kāi)始確保你安裝node第一步 讓服務(wù)跑起來(lái)
目標(biāo): 創(chuàng)建node服務(wù),通過(guò)瀏覽器訪問(wèn),返回"hello node!"(html頁(yè)面其實(shí)就是一串字符串)
/** 創(chuàng)建項(xiàng)目目錄結(jié)構(gòu)如下 */ │─ package-lock.json │─ package.json │─ README.md ├─bin │─ www.js // 1. 安裝依賴(lài) npm i koa // 2. 修改package.json文件中 scripts 屬性如下 "scripts": { "start": "node bin/www.js" } // 3. www.js寫(xiě)入如下代碼 const Koa = require("koa"); let app = new Koa(); app.use(ctx => { ctx.body = "hello node!" }); app.listen(3000, () => { console.log("服務(wù)器啟動(dòng) http://127.0.0.1:3000"); }); // 4 npm start 瀏覽器訪問(wèn) http://127.0.0.1:3000 查看效果第二步 路由的使用
目標(biāo):使用koa-router根據(jù)不同url返回不同頁(yè)面內(nèi)容
依賴(lài) npm i koa-router
koa-router 更多細(xì)節(jié) 請(qǐng)至npm查看
/** 新增routers文件夾 目錄結(jié)構(gòu)如下 │─.gitignore │─package.json │─README.md ├─bin │ │─www.js ├─node_modules └─routers │─home.js │─index.js │─user.js */ //項(xiàng)目中應(yīng)按照模塊對(duì)路由進(jìn)行劃分,示例簡(jiǎn)單將路由劃分為首頁(yè)(/)和用戶(hù)頁(yè)(/user) 在index中將路由集中管理導(dǎo), 出并在app實(shí)例后掛載到app上
/** router/home.js 文件 */ // 引包 const homeRouter = require("koa-router")() //創(chuàng)建路由規(guī)則 homeRouter.get(["/", "/index.html", "/index", "/home.html", "/home"], (ctx, next) => { ctx.body = "home" }); // 導(dǎo)出路由備用 module.exports = homeRouter /** router/user.js 文件 */ const userRouter = require("koa-router")() userRouter.get("/user", (ctx, next) => { ctx.body = "user" }); module.exports = userRouter
/** router/index.js 文件 */ // 路由集中點(diǎn) const routers = [ require("./home.js"), require("./user.js") ] // 簡(jiǎn)單封裝 module.exports = function (app) { routers.forEach(router => { app.use(router.routes()) }) return routers[0] }
/** www.js 文件改寫(xiě) */ // 引入koa const Koa = require("koa") const Routers = require("../routers/index.js") // 實(shí)例化koa對(duì)象 let app = new Koa() // 掛載路由 app.use((new Routers(app)).allowedMethods()) // 監(jiān)聽(tīng)3000端口 app.listen(3000, () => { console.log("服務(wù)器啟動(dòng) http://127.0.0.1:3000") })第三步 加入模板
目標(biāo):
1.使用nunjucks解析html模板返回頁(yè)面
2.了解koa中間件的使用
依賴(lài) npm i nunjucks
nunjucks中文文檔
/* *我向項(xiàng)目目錄下加入兩個(gè)準(zhǔn)備好的html文件 目錄結(jié)構(gòu)如下 │─.gitignore │─package.json │─README.md ├─bin │ │─www.js │─middlewares //新增中間件目錄 │ ├─nunjucksMiddleware.js //nunjucks模板中間件 ├─node_modules │─routers │ │─home.js │ │─index.js │ │─user.js │─views //新增目錄 作為視圖層 ├─home │ ├─home.html ├─user ├─user.html */
/* nunjucksMiddleware.js 中間件的編寫(xiě) *什么是中間件: 中間件就是在程序執(zhí)行過(guò)程中增加輔助功能 *nunjucksMiddleware作用: 給請(qǐng)求上下文加上render方法 將來(lái)在路由中使用 */ const nunjucks = require("nunjucks") const path = require("path") const moment = require("moment") let nunjucksEVN = new nunjucks.Environment(new nunjucks.FileSystemLoader("views")) // 為nkj加入一個(gè)過(guò)濾器 nunjucksEVN.addFilter("timeFormate", (time, formate) => moment(time).format( formate || "YYYY-MM-DD HH:mm:ss")) // 判斷文件是否有html后綴 let isHtmlReg = /.html$/ let resolvePath = (params = {}, filePath) => { filePath = isHtmlReg.test(filePath) ? filePath : filePath + (params.suffix || ".html") return path.resolve(params.path || "", filePath) } /** * @description nunjucks中間件 添加render到請(qǐng)求上下文 * @param params {} */ module.exports = (params) => { return (ctx, next) => { ctx.render = (filePath, renderData = {}) => { ctx.type = "text/html" ctx.body = nunjucksEVN.render(resolvePath(params, filePath), Object.assign({}, ctx.state, renderData)) } // 中間件本身執(zhí)行完成 需要調(diào)用next去執(zhí)行下一步計(jì)劃 return next() } }
/* 中間件掛載 www.js中增加部分代碼 */ // 頭部引入文件 const nunjucksMiddleware = require("../middlewares/nunjucksMiddleware.js") //在路由之前調(diào)用 因?yàn)槲覀兊闹虚g件是在路由中使用的 故應(yīng)該在路由前加到請(qǐng)求上下文ctx中 app.use(nunjucksMiddleware({ // 指定模板文件夾 path: path.resolve(__dirname, "../views") })
/* 路由中調(diào)用 以routers/home.js 為例 修改代碼如下*/ const homeRouter = require("koa-router")() homeRouter.get(["/", "/index.html", "/index", "/home.html", "/home"], (ctx, next) => { // 渲染頁(yè)面的數(shù)據(jù) ctx.state.todoList = [ {name: "吃飯", time: "2019.1.4 12:00"}, {name: "下午茶", time: "2019.1.4 15:10"}, {name: "下班", time: "2019.1.4 18:30"} ] // 這里的ctx.render方法就是我們通過(guò)nunjucksMiddleware中間件添加的 ctx.render("home/home", { title: "首頁(yè)" }) }) module.exports = homeRouter第四步 抽取公共模板
目標(biāo): 抽取頁(yè)面的公用部分 如導(dǎo)航/底部/html模板等
/**views目錄下增加兩個(gè)文件夾_layout(公用模板) _component(公共組件) 目錄結(jié)構(gòu)如下 │─.gitignore │─package.json │─README.md ├─bin │ │─www.js /koa服務(wù) │─middlewares //中間件目錄 │ ├─nunjucksMiddleware.js //nunjucks模板中間件 ├─node_modules │─routers //服務(wù)路由目錄 │ │─home.js │ │─index.js │ │─user.js │─views //頁(yè)面視圖層 │─_component │ │─nav.html (公用導(dǎo)航) │─_layout │ │─layout.html (公用html框架) ├─home │ ├─home.html ├─user ├─user.html */
{{ title }} {% block content %} {% endblock %}
{% extends "../_layout/layout.html" %} {% block content %} {% include "../_component/nav.html" %}待辦事項(xiàng)
目標(biāo): 處理頁(yè)面jscssimg等資源引入
依賴(lài)
用webpack打包靜態(tài)資源 npm i webpack webpack-cli -D
處理js npm i @babel/core @babel/preset-env babel-loader -D
處理less npm i css-loader less-loader less mini-css-extract-plugin -D
處理文件 npm i file-loader copy-webpack-plugin -D
處理html npm i html-webpack-plugin -D
清理打包文件 npm i clean-webpack-plugin -D
> *相關(guān)插件使用 查看npm相關(guān)文檔*
/* 項(xiàng)目目錄 變更 │ .gitignore │ package.json │ README.md ├─bin │ www.js ├─config //增加webpack配置目錄 │ webpack.config.js ├─middlewares │ nunjucksMiddleware.js ├─routers │ home.js │ index.js │ user.js ├─src │ │─template.html // + html模板 以此模板為每個(gè)入口生成 引入對(duì)應(yīng)js的模板 │ ├─images // +圖資源目錄 │ │ ww.jpg │ ├─js // + js目錄 │ │ ├─home │ │ │ home.js │ │ └─user │ │ user.js │ └─less // + css目錄 │ ├─common │ │ common.less │ │ nav.less │ ├─home │ │ home.less │ └─user │ user.less └─views ├─home │ home.html ├─user │ user.html ├─_component │ nav.html └─_layout // webpac打包后的html模板 ├─home │ home.html └─user user.html */
{{title}} {% block content %} {% endblock %}
/* src/js/home/home.js 一個(gè)入口文件*/ import "../../less/home/home.less" //引入css import img from "../../images/ww.jpg" //引入圖片 console.log(111); let add = (a, b) => a + b; //箭頭函數(shù) let a = 3, b = 4; let c = add(a, b); console.log(c); // 這里只做打包演示代碼 不具任何意義
// 引入公共樣式 @import "../common/common.less"; @import "../common/nav.less"; .list { li { color: rebeccapurple; } } .bg-img { width: 200px; height: 200px; background: url(../../images/ww.jpg); // 背景圖片 margin: 10px 0; }
/* webpack配置 webpack.config.js */ const path = require("path"); const CleanWebpackPlugin = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin"); // 多入口 let entry = { home: "src/js/home/home.js", user: "src/js/user/user.js" } module.exports = evn => ({ mode: evn.production ? "production" : "development", // 給每個(gè)入口 path.reslove entry: Object.keys(entry).reduce((obj, item) => (obj[item] = path.resolve(entry[item])) && obj, {}), output: { publicPath: "/", filename: "js/[name].js", path: path.resolve("dist") }, module: { rules: [ { // bable 根據(jù)需要轉(zhuǎn)換到對(duì)應(yīng)版本 test: /.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"] } } }, { // 轉(zhuǎn)換less 并交給MiniCssExtractPlug插件提取到多帶帶文件 test: /.less$/, loader: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"], exclude: /node_modules/ }, { //將css、js引入的圖片目錄指到dist目錄下的images 保持與頁(yè)面引入的一致 test: /.(png|svg|jpg|gif)$/, use: [{ loader: "file-loader", options: { name: "[name].[ext]", outputPath: "./images", } }] }, { test: /.(woff|woff2|eot|ttf|otf)$/, use: [{ loader: "file-loader", options: { name: "[name].[ext]", outputPath: "./font", } }] } ] }, plugins: [ // 刪除上一次打包目錄(一般來(lái)說(shuō)刪除自己輸出過(guò)的目錄 ) new CleanWebpackPlugin(["dist", "views/_layout"], { // 當(dāng)配置文件與package.json不再同一目錄時(shí)候需要指定根目錄 root: path.resolve() }), new MiniCssExtractPlugin({ filename: "css/[name].css", chunkFilename: "[id].css" }), // 將src下的圖片資源平移到dist目錄 new CopyWebpackPlugin( [{ from: path.resolve("src/images"), to: path.resolve("dist/images") } ]), // HtmlWebpackPlugin 每個(gè)入口生成一個(gè)html 并引入對(duì)應(yīng)打包生產(chǎn)好的js ...Object.keys(entry).map(item => new HtmlWebpackPlugin({ // 模塊名對(duì)應(yīng)入口名稱(chēng) chunks: [item], // 輸入目錄 (可自行定義 這邊輸入到views下面的_layout) filename: path.resolve("views/_layout/" + entry[item].split("/").slice(-2).join("/").replace("js", "html")), // 基準(zhǔn)模板 template: path.resolve("src/template.html") })) ] }); "scripts": { "start": "node bin/www.js", "build": "webpack --env.production --config config/webpack.config.js" } 運(yùn)行 npm run build 后生成 dist views/_layout 兩個(gè)目錄
{{title}} {% block content %} {% endblock %}
{% extends "../_layout/home/home.html" %} {% block content %} {% include "../_component/nav.html" %}待辦事項(xiàng)
/**koa處理靜態(tài)資源 * 依賴(lài) npm i "koa-static */ // www.js 增加 將靜態(tài)資源目錄指向 打包后的dist目錄 app.use(require("koa-static")(path.resolve("dist")))
運(yùn)行
npm run build npm start 瀏覽器訪問(wèn)127.0.0.1:3000 查看頁(yè)面 js css img 效果第六步 監(jiān)聽(tīng)編譯
目標(biāo): 文件發(fā)生改實(shí)時(shí)編譯打包
依賴(lài) npm i pm2 concurrently
/**項(xiàng)目中文件發(fā)生變動(dòng) 需要重啟服務(wù)才能看到效果是一件蛋疼的事,故需要實(shí)時(shí)監(jiān)聽(tīng)變動(dòng) */ "scripts": { // concurrently 監(jiān)聽(tīng)同時(shí)監(jiān)聽(tīng)兩條命令 "start": "concurrently "npm run build:dev" "npm run server:dev"", "dev": "npm start", // 生產(chǎn)環(huán)境 執(zhí)行兩條命令即可 無(wú)監(jiān)聽(tīng) "product": "npm run build:pro && npm run server:pro", // pm2 --watch參數(shù)監(jiān)聽(tīng)服務(wù)的代碼變更 "server:dev": "pm2 start bin/www.js --watch", // 生產(chǎn)不需要用監(jiān)聽(tīng) "server:pro": "pm2 start bin/www.js", // webpack --watch 對(duì)打包文件監(jiān)聽(tīng) "build:dev": "webpack --watch --env.production --config config/webpack.config.js", "build:pro": "webpack --env.production --config config/webpack.config.js" }第七步 數(shù)據(jù)請(qǐng)求
目標(biāo): node請(qǐng)求接口數(shù)據(jù) 填充模板
依賴(lài) npm i node-fetch
/*上面的代碼中routers/home.js首頁(yè)路由中我們向頁(yè)面渲染了下面的一組數(shù)據(jù) */ ctx.state.todoList = [ {name: "吃飯", time: "2019.1.4 12:00"}, {name: "下午茶", time: "2019.1.4 15:10"}, {name: "下班1", time: "2019.1.4 18:30"} ] /*但 數(shù)據(jù)是同步的 項(xiàng)目中我們必然會(huì)向java獲取其他后臺(tái)拿到渲染數(shù)據(jù)再填充頁(yè)面 我們來(lái)看看怎么做*/
/*我們?cè)诟夸浵聞?chuàng)建一個(gè)util的目錄作為工具庫(kù) 并簡(jiǎn)單封裝fetch.js請(qǐng)求數(shù)據(jù)*/ const nodeFetch = require("node-fetch") module.exports = ({url, method, data = {}}) => { // get請(qǐng)求 將參數(shù)拼到url url = method === "get" || !method ? "?" + Object.keys(data).map(item => `${item}=${data[item]}`).join("&") : url; return nodeFetch(url, { method: method || "get", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }).then(res => res.json()) }
/*在根目錄下創(chuàng)建一個(gè)service的目錄作為數(shù)據(jù)層 并創(chuàng)建一個(gè)exampleService.js 作為示例*/ //引入封裝的 請(qǐng)求工具 const fetch = require("../util/fetch.js") module.exports = { getTodoList (params = {}) { return fetch({ url: "https://www.easy-mock.com/mock/5c35a2a2ce7b4303bd93fbda/example/todolist", method: "post", data: params }) }, //... }
/* 將請(qǐng)求加入到路由中 routers/home.js 改寫(xiě) */ const homeRouter = require("koa-router")() let exampleService = require("../service/exampleService.js") // 引入service api //將路由匹配回調(diào) 改成async函數(shù) 并在請(qǐng)時(shí)候 await數(shù)據(jù)回來(lái) 再調(diào)用render homeRouter.get(["/", "/index.html", "/index", "/home.html", "/home"], async (ctx, next) => { // 請(qǐng)求數(shù)據(jù) let todoList = await exampleService.getTodoList({name: "ott"}) // 替換原來(lái)的靜態(tài)數(shù)據(jù) ctx.state.todoList = todoList.data ctx.render("home/home", { title: "首頁(yè)" }) }) // 導(dǎo)出路由備用 module.exports = homeRouter第八步 日志打印
目標(biāo): 使程序運(yùn)行可視
依賴(lài) npm i log4js
/* 在util目錄下創(chuàng)建 logger.js 代碼如下 作簡(jiǎn)單的logger封裝 */ const log4js = require("log4js"); const path = require("path") // 定義log config log4js.configure({ appenders: { // 定義兩個(gè)輸出源 info: { type: "file", filename: path.resolve("log/info.log") }, error: { type: "file", filename: path.resolve("log/error.log") } }, categories: { // 為info/warn/debug 類(lèi)型log調(diào)用info輸出源 error/fatal 調(diào)用error輸出源 default: { appenders: ["info"], level: "info" }, info: { appenders: ["info"], level: "info" }, warn: { appenders: ["info"], level: "warn" }, debug: { appenders: ["info"], level: "debug" }, error: { appenders: ["error"], level: "error" }, fatal: { appenders: ["error"], level: "fatal" }, } }); // 導(dǎo)出5種類(lèi)型的 logger module.exports = { debug: (...params) => log4js.getLogger("debug").debug(...params), info: (...params) => log4js.getLogger("info").info(...params), warn: (...params) => log4js.getLogger("warn").warn(...params), error: (...params) => log4js.getLogger("error").error(...params), fatal: (...params) => log4js.getLogger("fatal").fatal(...params), }
/* 在fetch.js中是喲logger */ const nodeFetch = require("node-fetch") const logger = require("./logger.js") module.exports = ({url, method, data = {}}) => { // 加入請(qǐng)求日志 logger.info("請(qǐng)求url:", url , method||"get", JSON.stringify(data)) // get請(qǐng)求 將參數(shù)拼到url url = method === "get" || !method ? "?" + Object.keys(data).map(item => `${item}=${data[item]}`).join("&") : url; return nodeFetch(url, { method: method || "get", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }).then(res => res.json()) } [2019-01-09T17:34:11.404] [INFO] info - 請(qǐng)求url: https://www.easy-mock.com/mock/5c35a2a2ce7b4303bd93fbda/example/todolist post {"name":"ott"}
注: 僅共學(xué)習(xí)參考,生產(chǎn)配置自行斟酌!轉(zhuǎn)載請(qǐng)備注來(lái)源!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/100863.html
摘要:是一個(gè)基于和的服務(wù)器端和瀏覽器端的的前后端全棧應(yīng)用框架。是的組件,并且會(huì)進(jìn)行數(shù)據(jù)初始化不但可以支持的數(shù)據(jù)初始化,還可以合并和的,使用同一個(gè),和的無(wú)縫結(jié)合。 koa-cola是一個(gè)基于koa和react的服務(wù)器端SSR(server side render)和瀏覽器端的SPA(single page application)的web前后端全棧應(yīng)用框架。 koa-cola使用typescr...
摘要:無(wú)需使用服務(wù)器實(shí)時(shí)動(dòng)態(tài)編譯,而是使用預(yù)渲染方式,在構(gòu)建時(shí)簡(jiǎn)單地生成針對(duì)特定路由的靜態(tài)文件。與可以部署在任何靜態(tài)文件服務(wù)器上的完全靜態(tài)單頁(yè)面應(yīng)用程序不同,服務(wù)器渲染應(yīng)用程序,需要處于運(yùn)行環(huán)境。更多的服務(wù)器端負(fù)載。 目錄結(jié)構(gòu) -no-ssr-demo 未做ssr之前的項(xiàng)目代碼用于對(duì)比 -vuecli2ssr 將vuecli生成的項(xiàng)目轉(zhuǎn)為ssr -prerender-demo 使用prer...
摘要:從零開(kāi)始搭建一個(gè)背景是什么全拼是,服務(wù)端渲染。大家不妨可以打開(kāi)一些頁(yè)面或者一些公司的網(wǎng)站,查看源代碼,你會(huì)發(fā)現(xiàn),也是有這個(gè)標(biāo)記。這時(shí)候,我們發(fā)現(xiàn)頁(yè)面的路由切換生效了,并且不同頁(yè)面的源代碼也不一樣了。從零開(kāi)始搭建一個(gè)下項(xiàng)目源碼 從零開(kāi)始搭建一個(gè)vue-ssr 背景 What?SSR是什么? SSR全拼是Server-Side Rendering,服務(wù)端渲染。 所謂服務(wù)端渲染,指的是把...
摘要:如何去解決這些問(wèn)題前后端分離大部分的互聯(lián)網(wǎng)公司都分成了前端團(tuán)隊(duì)和后端團(tuán)隊(duì)。方案一采用架構(gòu)業(yè)界很多公司會(huì)采用,單頁(yè)應(yīng)用的架構(gòu),這種架構(gòu)是天然的前后端分離的。方案二淘寶的大前端方案中途島上圖是淘寶基于的前后端分離分層,以及的職責(zé)范圍。 我們遇到了什么問(wèn)題? 1.前端無(wú)法調(diào)試后端未完成的 API:如果后端同學(xué)還沒(méi)有完成 API 開(kāi)發(fā),那么前端同學(xué)就不能對(duì)這個(gè) API 進(jìn)行開(kāi)發(fā)。之前我們都是在...
摘要:前言大多數(shù)項(xiàng)目要支持應(yīng)該是為了考慮,畢竟對(duì)于應(yīng)用來(lái)說(shuō),搜索引擎是一個(gè)很大的流量入口。引入是一個(gè)構(gòu)建客戶(hù)端應(yīng)用的框架,即組件是在瀏覽器中進(jìn)行渲染的。由于服務(wù)端渲染要用做中間層,所以部署項(xiàng)目時(shí),需要處于運(yùn)行環(huán)境。 前言 大多數(shù)Vue項(xiàng)目要支持SSR應(yīng)該是為了SEO考慮,畢竟對(duì)于WEB應(yīng)用來(lái)說(shuō),搜索引擎是一個(gè)很大的流量入口。Vue SSR現(xiàn)在已經(jīng)比較成熟了,但是如果是把一個(gè)SPA應(yīng)用改造成S...
閱讀 844·2023-04-25 21:21
閱讀 3226·2021-11-24 09:39
閱讀 3067·2021-09-02 15:41
閱讀 1993·2021-08-26 14:13
閱讀 1827·2019-08-30 11:18
閱讀 2768·2019-08-29 16:25
閱讀 507·2019-08-28 18:27
閱讀 1580·2019-08-28 18:17