博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多页架构的前后端分离方案(webpack+express)
阅读量:5734 次
发布时间:2019-06-18

本文共 7400 字,大约阅读时间需要 24 分钟。

SPA(单页架构)方案当下虽然很时髦,不过大多数的网站依旧选择多页或者单页+多页的混合架构。使用 express, webpack 本文低成本的实现了包含多页架构自动刷新前后端分离 等概念

先上项目

  1. git repo

  2. 开发

npm install npm install supervisor -g npm run start # 开发环境,配置 hot reload npm run prod # 生产环境 npm run build # 编译前端生产环境
  1. DEMO

    ezgif-2-dec6b379f7.gif

  2. FE目录:

    Paste_Image.png

  3. SERVER目录:

    Paste_Image.png

为了不浪费你的时间,在阅读以下内容时需要有:

  • 基础知识,以及对 node 简单了解

  • 中级了解,本文采用 webpack2 实现

1. FE 端配置

前端配置需要实现的功能点:

  • 多页架构自动生成 entry,并通过 html-webpack-plugin 生成每个页面的模板,且选择任意模板引擎需要实现 layout 模板功能(本文使用swig作为模板引擎)

  • 配置各种文件后缀的 loader

  • 使用 HotModuleReplacementPlugin 实现修改自刷新

1.1 自动分析entry

规定每个页面必须有一个同名的 js 文件作为此页面的 entry ,目录深度可变,如下图,分解为两个 entry:

Paste_Image.png

为实现自动化获取,使用了 获取所有 .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 分别注入到:

<!--webpack_style_placeholder-->
<!--webpack_script_placeholder-->
两个替换文案处,例如页面模板:

{% extends '../base/base.html' %}{% block title %}My Page{% endblock %}{% block style %}
{%endblock%}{% block head %} {% parent %}{% endblock %}{% block content %}

This is just an home page!!!

clouds
link page2{% endblock %}{% block script %}
{%endblock%}

编译后替换后为:

{% extends '../base/base.html' %}{% block title %}My Page{% endblock %}{% block style %}
{%endblock%}{% block head %} {% parent %}{% endblock %}{% block content %}

This is just an home page!!!

clouds
link page2{% endblock %}{% block script %}{%endblock%}

1.3 各种 loader 配置,提取页面 css

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 服务,实现的功能点如下:

  1. 使用 webpack-dev-middleware 进行 webpack 编译

  2. 使用 webpack-hot-middleware 实现 hot reload

  3. 使用 supervisor 服务监听 node 文件改动并自动重启

  4. render 模板时将内存中的文件写入硬盘,以进行渲染

2.1 webpack 接入 express

  • 生成 webpackcompiler

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 修改自刷新

jscss 的自刷新通过配置 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 生成的模板文件。

然而 expressrender 函数只能指定一个存在于文件系统中的模板, 即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();      });    });  });

其余的均为 基础使用,参阅文档即可

2.4 代理后端接口

dev环境时使用 http-proxy-middleware 对后端接口进行代理:

// set proxy  app.use('/api', proxy({target: config.proxy, changeOrigin: true}));

所有 /api 的请求都会代理到 config.proxy 配置的 ip 端口。

在正式环境中直接配置 nginx 进行转发

补充

本文抛砖引玉简单搭建了一个前后端分离框架,但还有很多不完善的地方。真实的线上应用还需要考虑 nodejs 运维成本,日志,监控等等。

转载地址:http://nwwzx.baihongyu.com/

你可能感兴趣的文章
iOS - Regex 正则表达式
查看>>
第 68 章 Logical Volume Manager (LVM)
查看>>
膝盖中了一箭之康复篇-第八个月暨2月份目标总结
查看>>
IPA提交APPStore问题记录(一)
查看>>
有利于seo优化的网站地图不能取巧
查看>>
快照产品体验优化
查看>>
ASCII
查看>>
ibatis SqlMap not found
查看>>
Android SD卡创建文件和文件夹失败
查看>>
Ubuntu 14.04 vsftp refusing to run with writable root inside chroot问题解决方法
查看>>
Intellij IDEA远程调试tomcat
查看>>
hadoop的学习论坛
查看>>
Struts2 学习小结
查看>>
烂泥:wordpress迁移到docker
查看>>
.扒渣机的性能及优势 
查看>>
Linux下磁盘保留空间的调整,解决df看到的空间和实际磁盘大小不一致的问题
查看>>
RSA 生成公钥、私钥对
查看>>
测试工具综合
查看>>
asp.net中调用COM组件发布IIS时常见错误 80070005解决方案
查看>>
分享一段ios数据库代码,包括对表的创建、升级、增删查改
查看>>