本文主要介绍一种全新的前后端合作模式,在介绍这种模式之前咋们先来看看前后端合作模式的演变。
singsong:该模式主要对传统多页面应用构建的改进。关于 SPA(Single Page Application,单页应用程序)可以参考 SRR(Server-Side Render,服务端渲染)。
在那个前端角色比较弱化的年代,页面主要以静态页面为主。合作模式很简单粗暴。
前端开发完页面,输出给后端。后端拿到页面拼模板,然后再渲染输出。但随着前端业务复杂性逐渐地增加,这种模式合作起来就不是很愉快了。后端在拼模板时,不能确保输出的页面结构与前端保持一致。为了规避这个问题,聪明的开发人员就给前端同学建议:使用后端开发环境开发前端页面。
前端直接在本地部署后端开发环境,只负责 views 开发。再基于代码托管工具 git,实现前后端的合作。此时还是以后端为主导,维护性成本依然很高。
随着 node.js 的崛起,前端工程化也逐渐成为前端开发重要组成部分。各种构建工具(如 webpack、rollup、grunt、gulp 等),MVVM 框架(如 react、vue、angular 等),模块化系统(cjs、amd、umd、ES6-modules 等),css 预处理器(SASS、Less、Stylus、Postcss 等),模块管理工具(npm、Yarn、brower 等)犹如雨后春笋般不断涌现。SAP 也开始在前端领域流行来,前端能做的事情更多。如客户端渲染,静态分析、优化打包等。后端只需负责数据提供。此时,前后端已完全分离,各自负责各自的业务。
这种合作模式的核心:客户端渲染 + 接口。由于基于客户端渲染,对浏览器的 seo 不是很友好。虽然可以通过 SRR 来解决,但是 SRR 也存在局限性。关于 SRR 感兴趣的同学可自行查阅相关的资料。本文主要探讨传统多页面应用构建的优化。
基于前端工程化,要让传统多页面应用构建也支持前后端完全分离。还需要做一件事,前端脚手架需要与后端使用相同的模板渲染引擎。前端编写好后,直接输出模板给后端使用。为此自己也构建一个 fes 脚手架。
在使用 fes 构建了几个项目后,虽然在开发体验、开发效率上都得到了很大地提升。但与后端合作起来不是很轻松。因为在开发之前需要与后端约定好模板数据变量,一般会以文档形式进行说明。如果后端更新了数据,没同步更新文档,就会存在数据不一致的问题。对开发效率大打折扣,与传统的后端重新拼接模板相比优势不是很明显。秉着 geek 的精神,就想能不能 将模板数据以接口的形式提供给前端。这样上述问题不就迎刃而解了么。
于是就对 fes 进行改造。让其支持模板数据接口的配置,在渲染之前会根据配置拉起接口数据,再进行渲染输出。同时还提供了接口数据适配功能,能让客户端更好地控制数据结构。
mockConfig: {
// 访问路径作为key
'/index': {
// 提供渲染模板数据接口
api: 'https://postman-echo.com/get?page=index',
// 适配数据
format: data => data.args,
},
'/fes/info': {
api: 'https://postman-echo.com/get?page=info',
format: data => data.args,
},
}
另外,为了方便查看模板数据,开发模式下输入 mock 指令可获取当前的模板数据。
$ mock
Mock Data:
{
"/about": {
"page": "about",
"common": "commons",
"data": "singsong",
"name": "fes-about-page"
},
"/index": {
"page": "index",
"common": "commons",
"name": "fes-index-page",
"data": {
"name": "fes"
},
"article": {
"_value": {},
"_state": 1
}
},
"/post": {
"common": "commons"
}
}
如果需要查看对应页面数据,只需输入对应的路径即可。如 /index。
$ /index
Mock Data [/index]:
{
"page": "index",
"common": "commons",
"name": "fes-index-page",
"data": {
"name": "fes"
},
"article": {
"_value": {},
"_state": 1
}
}
除了 mock 指令,还提供了如下指令:
这种合作模式,需要后端多做一件事。额外提供一个包含模板数据的接口供前端使用(只存在开发环境下,在上线时需要关闭掉)。只需对该类接口定义特定的前缀,然后在模板渲染逻辑之前拦截请求响应数据。这样不仅能保持数据一致性,而且维护起来也方便。
这里以改进后 fes 进行演示。如下以 index 页面 为例进行讲解:
index 页面路由为:http://127.0.0.1:3001/index,结构如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>{{ title }}</title>
</head>
<body>
<h1>{{ title }}</h1>
</body>
</html>
模板数据接口为:http://127.0.0.1:3001/mockdata/index。其中/mockdata为前缀,接口返回的数据如下:
{
"rawdata": {
"title": "index page"
},
}
在 fes 中对模板数据接口进行如下配置
mockConfig: {
// 访问路径作为key
'/index': {
// 提供渲染模板数据接口
api: 'http://127.0.0.1:3001/mockdata/index',
}
}
这里如果只想要 rawdata 中的变量数据。可以通过format 进行适配。最后的配置信息如下:
mockConfig: {
// 访问路径作为key
'/index': {
// 提供渲染模板数据接口
api: 'http://127.0.0.1:3001/mockdata/index',
// 适配数据
format: data => data.rawdata,
}
}
路由处理器
//path: ./controllers/index.js
module.exports = async ctx => {
const {url} = ctx;
// 从 DB 获取数据
const getDataFromDB = () => {
// 做一些数据查询操作……
return {
rawdata: {
title: 'index page',
},
};
};
const data = getDataFromDB();
// 判断是否有前缀进行不同的响应
// 这里前缀已保存在 ctx.mockApiPrefix 中
if (url.indexOf(ctx.mockApiPrefix) === 0) {
ctx.body = data;
} else {
await ctx.render('index', data.rawdata);
}
};
定义路由
// path: ./pages/index.js
// 该接口与页面请求使用相同的路由处理,通过是否有`PREFIX`进行区别
module.exports = (PREFIX = '') => [
{
method: 'get',
path: `${PREFIX}/index`,
middleware: require('./controllers/index'),
},
];
注册路由
const Router = require('koa-router');
const router = new Router();
const pagesConfig = require('./pages/index');
const config = [];
// 定义前缀
const MOCK_API_PREFIX = '/mockdata';
// 注册页面路由
config.push(...pagesConfig());
// 注册模板数据接口
if (process.env.NODE === 'dev') {
config.push(...pagesConfig(MOCK_API_PREFIX));
}
const generateRoutes = (router, config) => {
config.forEach(({method, path, middleware}) => {
router[method](path, middleware);
});
};
module.exports = app => (ctx, next) => {
// 绑定前缀,方便后续逻辑使用
ctx.mockApiPrefix = MOCK_API_PREFIX;
generateRoutes(router, config);
app.use(router.routes()).use(router.allowedMethods());
return next();
};
本文主要与大家分享一种前后端合作新模式,也许当成一种新思路更恰当些。
来自:https://segmentfault.com/a/1190000019752589
单例模式是我们开发中一个非常典型的设计模式,js单例模式要保证全局只生成唯一实例,提供一个单一的访问入口,单例的对象不同于静态类,我们可以延迟单例对象的初始化,通常这种情况发生在我们需要等待加载创建单例的依赖。
工厂模式下的对象我们不能识别它的类型,由于typeof返回的都是object类型,不知道它是那个对象的实例。另外每次造人时都要创建一个独立的person的对象,会造成代码臃肿的情况。
建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象
主要涉及知识点: HTML与XHTML,HTML与XHTML的区别,DOCTYPE与DTD的概念,DTD的分类以及DOCTYPE的声明方式,标准模式(Standard Mode)和兼容模式(Quircks Mode),标准模式(Standard Mode)和兼容模式(Quircks Mode)的区别
JavaScript中常见的四种设计模式:工厂模式、单例模式、沙箱模式、发布者订阅模式
javascript 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句,策略模式提供了开放-封闭原则,使代码更容易理解和扩展, 策略模式中的代码可以复用。
javascript观察者模式又叫发布订阅模式,观察者模式的好处:js观察者模式支持简单的广播通信,自动通知所有已经订阅过的对象。存在一种动态关联,增加了灵活性。目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
熟悉 Vue 的都知道 方法methods、计算属性computed、观察者watcher 在 Vue 中有着非常重要的作用,有些时候我们实现一个功能的时候可以使用它们中任何一个都是可以的
我觉得聊一下我爱用的 JavaScript 设计模式应该很有意思。我是一步一步才定下来的,经过一段时间从各种来源吸收和适应直到达到一个能提供我所需的灵活性的模式。让我给你看看概览,然后再来看它是怎么形成的
在围绕设计模式的话题中,工厂这个词频繁出现,从 简单工厂 模式到 工厂方法 模式,再到 抽象工厂 模式。工厂名称含义是制造产品的工业场所,应用在面向对象中,顺理成章地成为了比较典型的创建型模式
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!