摘要:單頁架構方案當下雖然很時髦,不過大多數的網站依舊選擇多頁或者單頁多頁的混合架構。在正式環境中直接配置進行轉發補充本文拋磚引玉簡單搭建了一個前后端分離框架,但還有很多不完善的地方。
先上項目SPA(單頁架構)方案當下雖然很時髦,不過大多數的網站依舊選擇多頁或者單頁+多頁的混合架構。使用 express, webpack 本文低成本的實現了包含多頁架構,自動刷新,前后端分離 等概念
git repo
node-pages-webpack-hot
開發
npm install npm install supervisor -g npm run start # 開發環境,配置 hot reload npm run prod # 生產環境 npm run build # 編譯前端生產環境
DEMO
FE目錄:
SERVER目錄:
為了不浪費你的時間,在閱讀以下內容時需要有:
express 基礎知識,以及對 node 簡單了解
webpack 中級了解,本文采用 webpack2 實現
1. FE 端配置前端配置需要實現的功能點:
多頁架構自動生成 entry,并通過 html-webpack-plugin 生成每個頁面的模板,且選擇任意模板引擎需要實現 layout 模板功能(本文使用swig作為模板引擎)
配置各種文件后綴的 loader
使用 HotModuleReplacementPlugin 實現修改自刷新
1.1 自動分析entry規定每個頁面必須有一個同名的 js 文件作為此頁面的 entry ,目錄深度可變,如下圖,分解為兩個 entry:
為實現自動化獲取,使用了 glob 獲取所有 .js 文件,并判斷是否有同名的 .html ,如果有則生成一個 entry,如果是 dev 環境則多增加 hotMiddlewareScript 模塊
// get all js files let files = glob.sync(config.src + "/**/*.js"); let srcLength = config.src.length; let entrys = {}; files.forEach(function (_file) { let file = path.parse(_file); let htmlFile = path.resolve(file.dir, file.name + "." + config.ext); // if has same name template file, it is a entry if (fs.existsSync(htmlFile)) { let pathIndex = file.dir.indexOf(config.src); if (config.dev == "dev") { entrys[config.staticRoot + file.dir.slice(srcLength) + "/" + file.name] = [path.resolve(_file), hotMiddlewareScript]; } else { entrys[config.staticRoot + file.dir.slice(srcLength) + "/" + file.name] = path.resolve(_file); } } }); return entrys;1.2 自動生成 html-webpack-plugin 模板
生成一系列 HtmlWebpackPlugin 的要點如下:
獲取到所有的 .html 后,判斷是否有對應的 entry 文件,若有則創建 HtmlWebpackPlugin
如果頁面為 layout 模板,則需要多注入由 CommonsChunkPlugin 生成的 common 模塊
自動生成 HtmlWebpackPlugin 代碼如下:
let htmls = []; // get all templates let files = glob.sync(config.src + "/**/*." + config.ext); let srcLength = config.src.length; files.forEach(function (_file) { let file = path.parse(_file); let chunks = []; let chunkName = config.staticRoot + file.dir.slice(srcLength) + "/" + file.name; // if has same name entry, create a html plugin let c = entrys[chunkName]; c && chunks.push(chunkName); // layout will contains common chunk if (file.name == config.serverLayoutName) { chunks.push(config.staticRoot + "/common"); } let plugin = new HtmlWebpackPlugin({ filename: config.templateRoot + file.dir.slice(srcLength) + "/" + file.base, template: path.resolve(file.dir, file.base), chunks: chunks, inject: false }); htmls.push(plugin); }); return htmls;
由于引入了模板 extends 支持,需設置 inject=false 便不會自動注入 assets 文件
編寫 webpack 插件,將頁面的 js assets, css assets 分別注入到:
兩個替換文案處,例如頁面模板:
{% extends "../base/base.html" %} {% block title %}My Page{% endblock %} {% block style %}{%endblock%} {% block head %} {% parent %} {% endblock %} {% block content %}This is just an home page!!!
cloudslink page2 {% endblock %} {% block script %}{%endblock%}
編譯后替換后為:
{% extends "../base/base.html" %} {% block title %}My Page{% endblock %} {% block style %}{%endblock%} {% block head %} {% parent %} {% endblock %} {% block content %}1.3 各種 loader 配置,提取頁面 cssThis is just an home page!!!
cloudslink page2 {% endblock %} {% block script %}{%endblock%}
在 dev 環境下由于配置了 webpack-hot-middleware 所以不能對 css 進行提取,否則無法熱更新
樣式相關的 loader 配置如下:
var extractInstance = new ExtractTextPlugin("[name].css"); if (config.env == "dev") { var stylusLoader = [ { loader: "style-loader" }, { loader: "css-loader" }, { loader: "stylus-loader" } ]; var cssLoader = [ { loader: "style-loader" }, { loader: "css-loader" } ]; } else { var stylusLoader = extractInstance.extract(["css-loader", "stylus-loader"]); var cssLoader = extractInstance.extract(["css-loader"]); }
并將所有的 loader 放到同一個文件進行維護:
var rules = [ { test: /.styl$/, exclude: /node_modules/, use: stylusLoader }, { test: /.css$/, exclude: /node_modules/, use: cssLoader }, { test: /.html$/, use: { loader: "html-loader", options: { minimize: false } } }, ...... ...... ]1.4 路徑配置
對生成模板,靜態文件輸出目錄進行統一控制,便于結合各種后端架構
const port = process.env.PORT || 8080; const env = process.env.NODE_ENV || "dev"; const CONFIG_BUILD = { env: env, ext: "html", // tempate ext src: path.resolve(__dirname, "../src"), // source code path path: env == "dev" ? "/" : path.resolve(__dirname, "../dist"), // base output path templateRoot: "templates", // tempate output path staticRoot: "static", // static output path serverLayoutName: "base", // swig layout name , only one file publicPath: env == "dev" ? ("http://localhost:" + port + "/") : "/" }2. SERVER 端配置
server 端搭建了 express 服務,實現的功能點如下:
使用 webpack-dev-middleware 進行 webpack 編譯
使用 webpack-hot-middleware 實現 hot reload
使用 supervisor 服務監聽 node 文件改動并自動重啟
render 模板時將內存中的文件寫入硬盤,以進行渲染
2.1 webpack 接入 express生成 webpack 的 compiler
var webpack = require("webpack"), webpackDevConfig = require(path.resolve(config.root, "./fe/webpack.config.js")); var compiler = webpack(webpackDevConfig);
將 compiler 作為 express 的中間件
// attach to the compiler & the server app.use(webpackDevMiddleware(compiler, { // public path should be the same with webpack config publicPath: webpackDevConfig.output.publicPath, noInfo: false, stats: { colors: true } }));
其中 publicPath 指明了 assets 請求的根路徑,這里配置的是:http://localhost:8080/
2.2 hot reload 方案 2.2.1 js,css 修改自刷新js、css 的自刷新通過配置 webpack-hot-middleware 實現(fe 也需進行相應的配置)
// server const webpackHotMiddleware = require("webpack-hot-middleware"); app.use(webpackHotMiddleware(compiler)); // fe webpackPlugins.push( new webpack.HotModuleReplacementPlugin() );2.2.2 node 修改自刷新
node 文件修改通過配置 supervisor 服務實現自動刷新
安裝服務:
npm install supervisor -g
配置啟動參數:
// package.json "scripts": { "start": "cross-env NODE_ENV=dev supervisor -w server -e fe server/server.js" }
supervisor 監聽了 server 文件夾下所有的改動,改動后重啟 express服務。
想要實現瀏覽器自動刷新,需要在 layout 模板加入如下代碼:
{% if env == "dev" %} {% endif %}2.3 對 template 進行 render
當 webpack 作為 express 中間件時,生成的所有文件都存在內存中,當然也包括由 html-webpack-plugin 生成的模板文件。
然而 express 的 render 函數只能指定一個存在于文件系統中的模板, 即dev 環境下 render 模板前需要將其從內存中取得并存放到文件系統中。
module.exports = (res, template) => { if (config.env == "dev") { let filename = compiler.outputPath + template; // load template from compiler.outputFileSystem.readFile(filename, function(err, result) { let fileInfo = path.parse(path.join(config.templateRoot, filename)); mkdirp(fileInfo.dir, () => { fs.writeFileSync(path.join(config.templateRoot, filename), result); res.render(template); }); }); } else { res.render(template); } }
layout 模板的存儲需要一個中間件:
app.use((req, res, next) => { let layoutPath = path.join(config.templateRoot, config.layoutTemplate); let filename = compiler.outputPath + config.layoutTemplate; compiler.outputFileSystem.readFile(filename, function(err, result) { let fileInfoLayout = path.parse(layoutPath); mkdirp(fileInfoLayout.dir, () => { fs.writeFileSync(layoutPath, result); next(); }); }); });
其余的均為 express 基礎使用,參閱文檔即可
2.4 代理后端接口在dev環境時使用 http-proxy-middleware 對后端接口進行代理:
// set proxy app.use("/api", proxy({target: config.proxy, changeOrigin: true}));
所有 /api 的請求都會代理到 config.proxy 配置的 ip 端口。
在正式環境中直接配置 nginx 進行轉發
補充本文拋磚引玉簡單搭建了一個前后端分離框架,但還有很多不完善的地方。真實的線上應用還需要考慮 nodejs 運維成本,日志,監控等等。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86884.html
摘要:回到純靜態頁面開發階段,讓頁面不需要后端渲染也能跑起來。改造開始本文著重介紹如何將靜態頁面改造成后端渲染需要的模板。總結在后端渲染的項目里使用多頁應用架構是絕對可行的,可不要給老頑固們嚇唬得又回到傳統前端架構了。 本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。原文地址:https://segmentfault.com/a/119000000820338...
摘要:回到純靜態頁面開發階段,讓頁面不需要后端渲染也能跑起來。改造開始本文著重介紹如何將靜態頁面改造成后端渲染需要的模板。總結在后端渲染的項目里使用多頁應用架構是絕對可行的,可不要給老頑固們嚇唬得又回到傳統前端架構了。 本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。原文地址:https://segmentfault.com/a/119000000820338...
摘要:本文首發于的技術博客實用至上,非經作者同意,請勿轉載。原文地址如果您對本系列文章感興趣,歡迎關注訂閱這里前言書承上文多頁應用架構系列十如何打造一個自定義的。終于,發現了這一大殺器,打包時間過長的問題得到完美解決。 本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。原文地址:https://segmentfault.com/a/119000000710437...
摘要:淺談秒殺系統架構設計后端掘金秒殺是電子商務網站常見的一種營銷手段。這兩個項目白話網站架構演進后端掘金這是白話系列的文章。 淺談秒殺系統架構設計 - 后端 - 掘金秒殺是電子商務網站常見的一種營銷手段。 不要整個系統宕機。 即使系統故障,也不要將錯誤數據展示出來。 盡量保持公平公正。 實現效果 秒殺開始前,搶購按鈕為活動未開始。 秒殺開始時,搶購按鈕可以點擊下單。 秒殺結束后,按鈕按鈕變...
閱讀 2810·2021-10-26 09:48
閱讀 1671·2021-09-22 15:22
閱讀 4029·2021-09-22 15:05
閱讀 608·2021-09-06 15:02
閱讀 2607·2019-08-30 15:52
閱讀 2107·2019-08-29 18:38
閱讀 2755·2019-08-28 18:05
閱讀 2332·2019-08-26 13:55