vue-router源码了解一下

vue-router源码了解一下

突然今天想了想,你自己天天用的 vue-router 到底是个啥?这个插件实现的方案是什么?

git日常操作

git日常操作

目前在我工作中用到的版本控制工具有 SVNGit,代码开发主要以 Git 为主,Git 的命令有很多不过只是常规操作也就那么 6 个,但是要熟练使用那就需要记住很多命令了

了解一些HTTP协议相关知识

了解一些HTTP协议相关知识

什么是 HTTP?

你能简单谈谈什么是 vue 的响应式更新?

你能简单谈谈什么是 vue 的响应式更新?

现在面试官随随便便都会问一句:请简单说一下 vue 的响应式原理是如何实现的?
答:使用 ES5 的Object.defineProperty!地球人都知道
再问:那你能简单的讲一下具体的实现流程吗?
答:。。。。。(好像知道但是好像又张嘴说不上来 😆 尴哩个尬)

微信小程序云开发初体验

微信小程序云开发初体验

云开发简单来说是配合微信小程序开发的一个弱后后端逻辑的一个东西,就是你不需要要再招一个 PHP,go,Java 这些程序员,前端就可以搞的,当然前端也需要一定的入门,需要了解非关系数据库中的集合,字段查询等等

前端大厂面试问答[2]

声明:关于以下面试题收集来源
壹题 答案综合了自己理解如有误区请帮忙指出,谢谢 😆

第 4 题:介绍下 Set、Map、WeakSet 和 WeakMap 的区别?

后面俩个我都没听过,没事问题不大,一起学习一下。

SetMap ,是ES6 提供的新数据结构

前端大厂面试问答[1]

声明:关于以下面试题收集来源 (壹题)https://github.com/Advanced-Frontend/Daily-Interview-Question/blob/master/datum/summary.md 答案综合了自己理解如有误区请帮忙指出,谢谢 😆

第 1 题:(滴滴、饿了么)写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

一文搞定前端 Jenkins 自动化部署

作者: 前端下午茶 公号 / SHERlocked93

这两天折腾了一下 Jenkins 持续集成,由于公司使用自己搭建的 svn 服务器来进行代码管理,因此这里 Jenkins 是针对 svn 服务器来进行的配置,Git 配置基本一致,后面也介绍了下针对 Github 管理的项目的 Jenkins 配置

之前项目每次修改之后都需要本地 npm run build一次手动发布到服务器上方便测试和产品查看,有了Jenkins持续集成之后只要 svn 或者 git 提交之后就会自动打包,很方便,此次记录以备后询。

声明:

  1. 后面的项目地址与打包地址都是使用 my-demo,自行修改;

  2. 另外还有路径等,根据自己情况自行修改;

1. 安装

1.1 安装 Nginx

可以直接去官网下直接下载,解压缩 start nginx就可以使了,常用命令:

  1. start nginx # 启动

  2. nginx -s reload # 修改配置后重新加载生效

  3. nginx -s reopen # 重新打开日志文件

  4. nginx -t # 配置文件检测是否正确

1.2 安装Jenkins

从官网下载文件安装之后,我这里安装到 C:Jenkins(Mac 不用在意),默认端口 8080,这时候浏览器访问 localhost:8080 就能访问 Jenkins 首页,这里注意如果不安装到 C 盘根目录有些插件安装会出错一文搞定前端 Jenkins 自动化部署

这里会让你去某个地方找一个初始密码文件打开并填到下面的密码框里,验证成功之后进入页面,选择 Installsuggested plugins 推介安装的插件

一文搞定前端 Jenkins 自动化部署

插件都安装完成之后进入用户登录界面,设定用户名、密码及邮箱。

然后提示 Jenkins is ready!→ Start using Jenkins ~

一文搞定前端 Jenkins 自动化部署

注意这里因为要使用node的命令来执行创建后操作,所以还需要安装插件:NodeJSPluginDeployto containerGithubPostbuild task

这里顺便记录一下启动和关闭Jenkins服务的命令行:

  1. net start jenkins // 启动Jenkins服务

  2. net stop jenkins // 停止Jenkins服务

2. 创建svn项目的Jenkins任务

2.1 新建

左边栏新建一个任务,输入一个任务名称,这里随便写一个

一文搞定前端 Jenkins 自动化部署

2.2 配置

General

这里才是重头戏,进入刚刚创建的任务的配置页面的 General

一文搞定前端 Jenkins 自动化部署

丢弃旧的构建就是检测到新的版本之后把旧版本的构建删除

源码管理

这里采用的是 svn 来管理代码,一文搞定前端 Jenkins 自动化部署

构建触发器

一文搞定前端 Jenkins 自动化部署

这里的 Poll SCM 表示去检测是否更新构建的频率, ***** 表示每分钟, H**** 表示每小时

构建

  1. cd cd C:Jenkinsworkspacemy-demo

  2. node -v

  3. npm -v

  4. cnpm i

  5. npm run build

构建后操作

安装插件 Postbuild task 后,可以在 增加构建后操作步骤中选择 Postbuild task 选项,增加构建后执行的script,具体也可以参考文章:jenkins部署maven项目构建后部署前执行shell脚本 - https://blog.csdn.net/minebk/article/details/73294785

我这里的 LogtextBuildcomplete

Script:

  1. rmdir /q/s C:nginx-1.14.0htmlmy-demo

  2. xcopy /y/e/i C:Jenkinsworkspacemy-demomy-demo C:nginx-1.14.0htmlmy-demo

复制生成好的文件到Nginx的目录下,路径自行修改

3. 创建Github项目的Jenkins任务

Jenkins 不仅可以持续集成 svn 项目,Git 项目也是可以的,这里以 Github 上的项目为例:

一文搞定前端 Jenkins 自动化部署

其他配置和上面一章一样

这样如果 github 有新的 push 请求,都会自动化部署到之前的服务器上,可以说很方便了。

试一试

配置好了我们试一试,在刚刚 github 项目中随便 commit 一版到 github :

一文搞定前端 Jenkins 自动化部署

稍等片刻去本地 Jenkins 地址 http://localhost:8080/job/vue-element-template/ 就能看到 Jenkins 已经在构建中了

一文搞定前端 Jenkins 自动化部署

50秒之后:

一文搞定前端 Jenkins 自动化部署

构建成功!构建用时 54 秒,现在访问本地服务器地址 http://localhost:8282/vue-element-template,已经能看到编译后的发布版本啦~

如果你希望发布的是测试版本,可以自行修改构建后操作的 script


网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

参考:

  1. 使用Jenkins自动编译部署web应用

  2. Jenkins+github 前端自动化部署

  3. 配置Jenkins邮件通知

  4. jenkins部署maven项目构建后部署前执行shell脚本

手摸手教你撸一个脚手架

作者:前端宇宙 公号 / 刘小夕 (本文来自作者投稿)

脚手架

vue-cli, create-react-appreact-native-cli 等都是非常优秀的脚手架,通过脚手架,我们可以快速初始化一个项目,无需自己从零开始一步步配置,有效提升开发体验。尽管这些脚手架非常优秀,但是未必是符合我们的实际应用的,我们可以定制一个属于自己的脚手架(或公司通用脚手架),来提升自己的开发效率。

脚手架的作用

  • 减少重复性的工作,不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。

  • 可以根据交互动态生成项目结构和配置文件。

  • 多人协作更为方便,不需要把文件传来传去。

实现的功能

在开始之前,我们需要明确自己的脚手架需要哪些功能。vue init template-name project-namecreate-react-app project-name。我们这次编写的脚手架(eos-cli)具备以下能力(脚手架的名字爱叫啥叫啥,我选用了Eos黎明女神):

  • eos init template-name project-name 根据远程模板,初始化一个项目(远程模板可配置)

  • eos config set 修改配置信息

  • eos config get [] 查看配置信息

  • eos --version 查看当前版本号

  • eos -h

大家可以自行扩展其它的 commander,本篇文章旨在教大家如何实现一个脚手架。

效果展示

初始化一个项目

手摸手教你撸一个脚手架

修改.eosrc文件,从 vuejs-template 下载模板

手摸手教你撸一个脚手架

需要使用的第三方库

  • babel-cli/babel-env: 语法转换

  • commander: 命令行工具

  • download-git-repo: 用来下载远程模板

  • ini: 格式转换

  • inquirer: 交互式命令行工具

  • ora: 显示loading动画

  • chalk: 修改控制台输出内容样式

  • log-symbols: 显示出 √ 或 × 等的图标

关于这些第三方库的说明,可以直接npm上查看相应的说明,此处不一一展开。

初始化项目

创建一个空项目(eos-cli),使用 npm init 进行初始化。

安装依赖

npm install babel-cli babel-env chalk commander download-git-repo ini inquirer log-symbols ora

目录结构

├── bin│   └── www             //可执行文件├── dist    ├── ...             //生成文件└── src    ├── config.js       //管理eos配置文件    ├── index.js        //主流程入口文件    ├── init.js         //init command    ├── main.js         //入口文件    └── utils        ├── constants.js //定义常量        ├── get.js       //获取模板        └── rc.js        //配置文件├── .babelrc             //babel配置文件├── package.json├── README.md

babel 配置

开发使用了ES6语法,使用 babel 进行转义,

.bablerc

{    "presets": [        [            "env",            {                "targets": {                    "node": "current"                }            }        ]    ]}

`eos` 命令

node.js 内置了对命令行操作的支持,package.json 中的 bin 字段可以定义命令名和关联的执行文件。在 package.json 中添加 bin 字段

package.json

{    "name": "eos-cli",    "version": "1.0.0",    "description": "脚手架",    "main": "index.js",    "bin": {        "eos": "./bin/www"    },    "scripts": {        "compile": "babel src -d dist",        "watch": "npm run compile -- --watch"    }}

www 文件

行首加入一行 #!/usr/bin/env node 指定当前脚本由node.js进行解析

#! /usr/bin/env noderequire('../dist/main.js');

链接到全局环境

开发过程中为了方便调试,在当前的 eos-cli 目录下执行 npm link,将 eos 命令链接到全局环境。

启动项目

npm run watch

处理命令行

利用 commander 来处理命令行。

main

import program from 'commander';import { VERSION } from './utils/constants';import apply from './index';import chalk from 'chalk';/** * eos commands *    - config *    - init  */let actionMap = {    init: {        description: 'generate a new project from a template',        usages: [            'eos init templateName projectName'        ]    },    config: {        alias: 'cfg',        description: 'config .eosrc',        usages: [            'eos config set <k> <v>',            'eos config get <k>',            'eos config remove <k>'        ]    },    //other commands}// 添加 init / config 命令Object.keys(actionMap).forEach((action) => {    program.command(action)    .description(actionMap[action].description)    .alias(actionMap[action].alias) //别名    .action(() => {        switch (action) {            case 'config':                 //配置                apply(action, ...process.argv.slice(3));                break;            case 'init':                apply(action, ...process.argv.slice(3));                break;            default:                break;        }    });});function help() {    console.log('rnUsage:');    Object.keys(actionMap).forEach((action) => {        actionMap[action].usages.forEach(usage => {            console.log('  - ' + usage);        });    });    console.log('r');}program.usage('<command> [options]');// eos -h program.on('-h', help);program.on('--help', help);// eos -V   VERSION 为 package.json 中的版本号program.version(VERSION, '-V --version').parse(process.argv);// eos 不带参数时if (!process.argv.slice(2).length) {    program.outputHelp(make_green);}function make_green(txt) {    return chalk.green(txt); }

下载模板

download-git-repo 支持从 Github、Gitlab 下载远程仓库到本地。

get.js

import { getAll } from './rc';import downloadGit from 'download-git-repo';export const downloadLocal = async (templateName, projectName) => {    let config = await getAll();    let api = `${config.registry}/${templateName}`;    return new Promise((resolve, reject) => {        //projectName 为下载到的本地目录        downloadGit(api, projectName, (err) => {            if (err) {                reject(err);            }            resolve();        });    });}

`init` 命令

命令行交互

在用户执行 init 命令后,向用户提出问题,接收用户的输入并作出相应的处理。命令行交互利用 inquirer 来实现:

inquirer.prompt([    {        name: 'description',        message: 'Please enter the project description: '    },    {        name: 'author',        message: 'Please enter the author name: '    }]).then((answer) => {    //...});

手摸手教你撸一个脚手架

视觉美化

在用户输入之后,开始下载模板,这时候使用 ora 来提示用户正在下载模板,下载结束之后,也给出提示。

import ora from 'ora';let loading = ora('downloading template ...');loading.start();//downloadloading.succeed(); //或 loading.fail();

手摸手教你撸一个脚手架

index.js

import { downloadLocal } from './utils/get';import ora from 'ora';import inquirer from 'inquirer';import fs from 'fs';import chalk from 'chalk';import symbol from 'log-symbols';let init = async (templateName, projectName) => {    //项目不存在    if (!fs.existsSync(projectName)) {        //命令行交互        inquirer.prompt([            {                name: 'description',                message: 'Please enter the project description: '            },            {                name: 'author',                message: 'Please enter the author name: '            }        ]).then(async (answer) => {            //下载模板 选择模板            //通过配置文件,获取模板信息            let loading = ora('downloading template ...');            loading.start();            downloadLocal(templateName, projectName).then(() => {                loading.succeed();                const fileName = `${projectName}/package.json`;                if(fs.existsSync(fileName)){                    const data = fs.readFileSync(fileName).toString();                    let json = JSON.parse(data);                    json.name = projectName;                    json.author = answer.author;                    json.description = answer.description;                    //修改项目文件夹中 package.json 文件                    fs.writeFileSync(fileName, JSON.stringify(json, null, 't'), 'utf-8');                    console.log(symbol.success, chalk.green('Project initialization finished!'));                }            }, () => {                loading.fail();            });        });    }else {        //项目已经存在        console.log(symbol.error, chalk.red('The project already exists'));    }}module.exports = init;

`config` 配置

eos config set registry vuejs-templates

config 配置,支持我们使用其它仓库的模板,例如,我们可以使用 vuejs-templates 中的仓库作为模板。这样有一个好处:更新模板无需重新发布脚手架,使用者无需重新安装,并且可以自由选择下载目标。

config.js

// 管理 .eosrc 文件 (当前用户目录下)import { get, set, getAll, remove } from './utils/rc';let config = async (action, key, value) => {    switch (action) {        case 'get':            if (key) {                let result = await get(key);                console.log(result);            } else {                let obj = await getAll();                Object.keys(obj).forEach(key => {                    console.log(`${key}=${obj[key]}`);                })            }            break;        case 'set':            set(key, value);            break;        case 'remove':            remove(key);            break;        default:            break;    }}module.exports = config;

rc.js

.eosrc 文件的增删改查

import { RC, DEFAULTS } from './constants';import { decode, encode } from 'ini';import { promisify } from 'util';import chalk from 'chalk';import fs from 'fs';const exits = promisify(fs.exists);const readFile = promisify(fs.readFile);const writeFile = promisify(fs.writeFile);//RC 是配置文件//DEFAULTS 是默认的配置export const get = async (key) => {    const exit = await exits(RC);    let opts;    if (exit) {        opts = await readFile(RC, 'utf8');        opts = decode(opts);        return opts[key];    }    return '';}export const getAll = async () => {    const exit = await exits(RC);    let opts;    if (exit) {        opts = await readFile(RC, 'utf8');        opts = decode(opts);        return opts;    }    return {};}export const set = async (key, value) => {    const exit = await exits(RC);    let opts;    if (exit) {        opts = await readFile(RC, 'utf8');        opts = decode(opts);        if(!key) {            console.log(chalk.red(chalk.bold('Error:')), chalk.red('key is required'));            return;        }        if(!value) {            console.log(chalk.red(chalk.bold('Error:')), chalk.red('value is required'));            return;        }        Object.assign(opts, { [key]: value });    } else {        opts = Object.assign(DEFAULTS, { [key]: value });    }    await writeFile(RC, encode(opts), 'utf8');}export const remove = async (key) => {    const exit = await exits(RC);    let opts;    if (exit) {        opts = await readFile(RC, 'utf8');        opts = decode(opts);        delete opts[key];        await writeFile(RC, encode(opts), 'utf8');    }}

发布

npm publish 将本脚手架发布至npm上。其它用户可以通过 npm install eos-cli -g 全局安装。
即可使用 eos 命令。

项目地址

本项目完整代码请戳: https://github.com/YvetteLau/Blog/tree/master/eos-cli

Web 实现前后端分离,前后端解耦

(给前端大全加星标,提升前端技能)

作者:山河远阔

blog.csdn.net/weixin_37539378/article/details/79956760

一、前言

”前后端分离“已经成为互联网项目开发的业界标杆,通过Tomcat+Ngnix(也可以中间有个Node.js),有效地进行解耦。并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚实的基础。

前后端分离(解耦)的核心思想是:前端Html页面通过Ajax调用后端的RestFul API并使用Json数据进行交互。

注:【在互联网架构中,web服务器:一般指像nginx,apache这类的服务器,他们一般只能解析静态资源。应用服务器:一般指像tomcat,jetty,resin这类的服务器可以解析动态资源也可以解析静态资源,但解析静态资源的能力没有web服务器好。】

一般只有Web服务器才能被外网访问,应用服务器只能内网访问。

二、为什么前后端分离

一般公司后端开发人员直接兼顾前端的工作,一边实现API接口,一边开发页面,两者互相切换着做,而且根据不同的url动态拼接页面,这也导致后台的开发压力大大增加。前后端工作分配不均。不仅仅开发效率慢,而且代码难以维护。

而前后端分离的话,则可以很好的解决前后端分工不均的问题,将更多的交互逻辑分配给前端来处理,而后端则可以专注于其本职工作,比如提供API接口,进行权限控制以及进行运算工作。而前端开发人员则可以利用nodejs来搭建自己的本地服务器,直接在本地开发,然后通过一些插件来将api请求转发到后台,这样就可以完全模拟线上的场景,并且与后台解耦。前端可以独立完成与用户交互的整一个过程,两者都可以同时开工,不互相依赖,开发效率更快,而且分工比较均衡。

三、从MVC到前后端分离

MVC 是一种经典的设计模式,全名为 Model-View-Controller,即 模型-视图-控制器。

其中,模型 是用于封装数据的载体,例如,在 Java 中一般通过一个简单的 POJO(Plain Ordinary Java Object)来表示,其本质是一个普通的 Java Bean,包含一系列的成员变量及其 getter/setter 方法。对于 视图 而言,它更加偏重于展现,也就是说,视图决定了界面到底长什么样子,在 Java 中可通过 JSP 来充当视图,或者通过纯 HTML 的方式进行展现,而后者才是目前的主流。模型和视图需要通过 控制器 来进行粘合,例如,用户发送一个 HTTP 请求,此时该请求首先会进入控制器,然后控制器去获取数据并将其封装为模型,最后将模型传递到视图中进行展现。

综上所述,MVC 的交互过程如下图所示:

Web 实现前后端分离,前后端解耦

也就是说,我们输入的是 AJAX 请求,输出的是 JSON 数据,市面上有这样的技术来实现这个功能吗?答案是 REST。

REST 全称是 Representational State Transfer(表述性状态转移),它是 Roy Fielding 博士在 2000 年写的一篇关于软件架构风格的论文,此文一出,威震四方!国内外许多知名互联网公司纷纷开始采用这种轻量级的 Web 服务,大家习惯将其称为 RESTful Web Services,或简称 REST 服务。

如果将浏览器这一端视为前端,而服务器那一端视为后端的话,可以将以上改进后的 MVC 模式简化为以下前后端分离模式:

Web 实现前后端分离,前后端解耦

可见,有了 REST 服务,前端关注界面展现,后端关注业务逻辑,分工明确,职责清晰。

四、认识Rest架构

REST 本质上是使用 URL 来访问资源种方式。众所周知,URL 就是我们平常使用的请求地址了,其中包括两部分:请求方式 与 请求路径,比较常见的请求方式是 GET 与 POST,但在 REST 中又提出了几种其它类型的请求方式,汇总起来有六种:GET、POST、PUT、DELETE、HEAD、OPTIONS。

尤其是前四种,正好与CRUD(Create-Retrieve-Update-Delete,增删改查)四种操作相对应,例如,GET(查)、POST(增)、PUT(改)、DELETE(删),这正是 REST 与 CRUD 的异曲同工之妙!需要强调的是,REST 是“面向资源”的,这里提到的资源,实际上就是我们常说的领域对象,在系统设计过程中,我们经常通过领域对象来进行数据建模。

REST 是一个“无状态”的架构模式,因为在任何时候都可以由客户端发出请求到服务端,最终返回自己想要的数据,当前请求不会受到上次请求的影响。也就是说,服务端将内部资源发布 REST 服务,客户端通过 URL 来访问这些资源,这不就是 SOA 所提倡的“面向服务”的思想吗?所以,REST 也被人们看做是一种“轻量级”的 SOA 实现技术,因此在企业级应用与互联网应用中都得到了广泛应用。

下面我们举几个例子对 REST 请求进行简单描述:

Web 实现前后端分离,前后端解耦

可见,请求路径相同,但请求方式不同,所代表的业务操作也不同,例如,/advertiser/1 这个请求,带有 GET、PUT、DELETE 三种不同的请求方式,对应三种不同的业务操作。

虽然 REST 看起来还是很简单的,实际上我们往往需要提供一个 REST 框架,让其实现前后端分离架构,让开发人员将精力集中在业务上,而并非那些具体的技术细节。

五、前后端分离意义大吗?

1、该网站前端变化远比后端变化频繁,则意义大。

2、该网站尚处于原始开发模式,数据逻辑与表现逻辑混杂不清,则意义大。

3、该网站前端团队和后端团队分属两个领导班子,技能点差异很大,则意义大。

4、该网站前端效果绚丽/跨设备兼容要求高,则意义大。

六、术业有专攻(开发人员分离)

以前的JavaWeb项目大多数都是java程序员又当爹又当妈,又搞前端(ajax/jquery/js/html/css等等),又搞后端(java/mysql/oracle等等)。

随着时代的发展,渐渐的许多大中小公司开始把前后端的界限分的越来越明确,前端工程师只管前端的事情,后端工程师只管后端的事情。

正所谓术业有专攻,一个人如果什么都会,那么他毕竟什么都不精。

大中型公司需要专业人才,小公司需要全才,但是对于个人职业发展来说,我建议是分开。

对于后端java工程师:

把精力放在java基础,设计模式,jvm原理,spring+springmvc原理及源码,linux,mysql事务隔离与锁机制,mongodb,http/tcp,多线程,分布式架构(dubbo,dubbox,spring cloud),弹性计算架构,微服务架构(springboot+zookeeper+docker+jenkins),java性能优化,以及相关的项目管理等等。

后端追求的是:三高(高并发,高可用,高性能),安全,存储,业务等等。

对于前端工程师:

把精力放在html5,css3,jquery,angularjs,bootstrap,reactjs,vuejs,webpack,less/sass,gulp,nodejs,Google V8引擎,javascript多线程,模块化,面向切面编程,设计模式,浏览器兼容性,性能优化等等。

前端追求的是:页面表现,速度流畅,兼容性,用户体验等等。

七、耦合时代

几曾何时,我们的JavaWeb项目都是使用了若干后台框架,springmvc/struts + spring + spring jdbc/hibernate/mybatis 等等。

大多数项目在java后端都是分了三层,控制层(controller/action),业务层(service/manage),持久层(dao)。

控制层负责接收参数,调用相关业务层,封装数据,以及路由&渲染到jsp页面。

然后jsp页面上使用各种标签(jstl/el/struts标签等)或者手写java表达式(<%=%>)将后台的数据展现出来,玩的是MVC那套思路。

我们先看这种情况:需求定完了,代码写完了,测试测完了,然后呢?要发布了吧?

你需要用maven或者eclipse等工具把你的代码打成一个war包,然后把这个war包发布到你的生产环境下的web容器(tomcat/jboss/weblogic/websphere/jetty/resin)里,对吧?

发布完了之后,你要启动你的web容器,开始提供服务,这时候你通过配置域名,dns等等相关,你的网站就可以访问了(假设你是个网站)。

那我们来看,你的前后端代码是不是全都在那个war包里?包括你的js,css,图片,各种第三方的库,对吧?

好,下面在浏览器中输入你的网站域名(www.xxx.com),之后发生了什么?(这个问题也是很多公司的面试题)

我捡干的说了啊,基础不好的童鞋请自己去搜。

浏览器在通过域名通过dns服务器找到你的服务器外网ip,将http请求发送到你的服务器,在tcp3次握手之后(http下面是tcp/ip),通过tcp协议开始传输数据,你的服务器得到请求后,开始提供服务,接收参数,之后返回你的应答给浏览器,浏览器再通过content-type来解析你返回的内容,呈现给用户。

那么我们来看,我们先假设你的首页中有100张图片,此时,用户的看似一次http请求,其实并不是一次,用户在第一次访问的时候,浏览器中不会有缓存,你的100张图片,浏览器要连着请求100次http请求(有人会跟我说http长连短连的问题,不在这里讨论),你的服务器接收这些请求,都需要耗费内存去创建socket来玩tcp传输(消耗你服务器上的计算资源)。

重点来了,这样的话,你的服务器的压力会非常大,因为页面中的所有请求都是只请求到你这台服务器上,如果1个人还好,如果10000个人并发访问呢(先不聊服务器集群,这里就说是单实例服务器),那你的服务器能扛住多少个tcp连接?你的带宽有多大?你的服务器的内存有多大?你的硬盘是高性能的吗?你能抗住多少IO?你给web服务器分的内存有多大?会不会宕机?

这就是为什么,越是大中型的web应用,他们越是要解耦。

理论上你可以把你的数据库+应用服务+消息队列+缓存+用户上传的文件+日志+等等都扔在一台服务器上,你也不用玩什么服务治理,也不用做什么性能监控,什么报警机制等等,就乱成一锅粥好了。

但是这样就好像是你把鸡蛋都放在一个篮子里,隐患非常大。如果因为一个子应用的内存不稳定导致整个服务器内存溢出而hung住,那你的整个网站就挂掉了。

如果出意外挂掉,而恰好这时你们的业务又处于井喷式发展高峰期,那么恭喜你,业务成功被技术卡住,很可能会流失大量用户,后果不堪设想。
注意:技术一定是要走在业务前面的,否则你将错过最佳的发展期。

此外,你的应用全部都耦合在一起,相当于一个巨石,当服务端负载能力不足时,一般会使用负载均衡的方式,将服务器做成集群,这样其实你是在水平扩展一块块巨石,性能加速度会越来越低,要知道,本身负载就低的功能or模块是没有必要水平扩展的,在本文中的例子就是你的性能瓶颈不在前端,那干嘛要水平扩展前端呢???
还有发版部署上线的时候,我明明只改了后端的代码,为什么要前端也跟着发布呢???(引用:《架构探险-轻量级微服务架构》,黄勇)

正常的互联网架构,是都要拆开的,你的web服务器集群,你的应用服务器集群+文件服务器集群+数据库服务器集群+消息队列集群+缓存集群等等。

JSP的痛点

以前的javaWeb项目大多数使用jsp作为页面层展示数据给用户,因为流量不高,因此也没有那么苛刻的性能要求,但现在是大数据时代,对于互联网项目的性能要求是越来越高,因此原始的前后端耦合在一起的架构模式已经逐渐不能满足我们,因此我们需要需找一种解耦的方式,来大幅度提升我们的负载能力。

1、动态资源和静态资源全部耦合在一起,服务器压力大,因为服务器会收到各种http请求,例如css的http请求,js的,图片的等等。

一旦服务器出现状况,前后台一起玩完,用户体验极差。

2、UI出好设计图后,前端工程师只负责将设计图切成html,需要由java工程师来将html套成jsp页面,出错率较高(因为页面中经常会出现大量的js代码),
修改问题时需要双方协同开发,效率低下。

3、jsp必须要在支持java的web服务器里运行(例如tomcat,jetty,resin等),无法使用nginx等(nginx据说单实例http并发高达5w,这个优势要用上),
性能提不上来。

4、第一次请求jsp,必须要在web服务器中编译成servlet,第一次运行会较慢。

5.每次请求jsp都是访问servlet再用输出流输出的html页面,效率没有直接使用html高(是每次哟,亲~)。

6、jsp内有较多标签和表达式,前端工程师在修改页面时会捉襟见肘,遇到很多痛点。

7、如果jsp中的内容很多,页面响应会很慢,因为是同步加载。

8、需要前端工程师使用java的ide(例如eclipse),以及需要配置各种后端的开发环境,你们有考虑过前端工程师的感受吗。

基于上述的一些痛点,我们应该把整个项目的开发权重往前移,实现前后端真正的解耦!

开发模式

以前老的方式是:

  • 产品经历/领导/客户提出需求

  • UI做出设计图

  • 前端工程师做html页面

  • 后端工程师将html页面套成jsp页面(前后端强依赖,后端必须要等前端的html做好才能套jsp。如果html发生变更,就更痛了,开发效率低)

  • 集成出现问题

  • 前端返工

  • 后端返工

二次集成

集成成功

交付

新的方式是:

  • 产品经历/领导/客户提出需求

  • UI做出设计图

  • 前后端约定接口&数据&参数

  • 前后端并行开发(无强依赖,可前后端并行开发,如果需求变更,只要接口&参数不变,就不用两边都修改代码,开发效率高)

  • 前后端集成

  • 前端页面调整

  • 集成成功

  • 交付

请求方式

以前老的方式是:

  • 客户端请求

  • 服务端的servlet或controller接收请求(后端控制路由与渲染页面,整个项目开发的权重大部分在后端)

  • 调用service,dao代码完成业务逻辑

  • 返回jsp

  • jsp展现一些动态的代码

新的方式是:

  • 浏览器发送请求

  • 直接到达html页面(前端控制路由与渲染页面,整个项目开发的权重前移)

  • html页面负责调用服务端接口产生数据(通过ajax等等,后台返回json格式数据,json数据格式因为简洁高效而取代xml)

  • 填充html,展现动态效果,在页面上进行解析并操作DOM。

(有兴趣的童鞋可以访问一下阿里巴巴等大型网站,然后按一下F12,监控一下你刷新一次页面,他的http是怎么玩的,大多数都是单独请求后台数据,使用json传输数据,而不是一个大而全的http请求把整个页面包括动+静全部返回过来)

总结一下新的方式的请求步骤:

大量并发浏览器请求—>web服务器集群(nginx)—>应用服务器集群(tomcat)—>文件/数据库/缓存/消息队列服务器集群

同时又可以玩分模块,还可以按业务拆成一个个的小集群,为后面的架构升级做准备。

前后分离的优势

1、可以实现真正的前后端解耦,前端服务器使用nginx。

前端/WEB服务器放的是css,js,图片等等一系列静态资源(甚至你还可以css,js,图片等资源放到特定的文件服务器,例如阿里云的oss,并使用cdn加速),前端服务器负责控制页面引用&跳转&路由,前端页面异步调用后端的接口,后端/应用服务器使用tomcat(把tomcat想象成一个数据提供者),加快整体响应速度。

这里需要使用一些前端工程化的框架比如nodejs,react,router,react,redux,webpack

2、发现bug,可以快速定位是谁的问题,不会出现互相踢皮球的现象。

页面逻辑,跳转错误,浏览器兼容性问题,脚本错误,页面样式等问题,全部由前端工程师来负责。

接口数据出错,数据没有提交成功,应答超时等问题,全部由后端工程师来解决。

双方互不干扰,前端与后端是相亲相爱的一家人。

3、在大并发情况下,我可以同时水平扩展前后端服务器,比如淘宝的一个首页就需要2000+台前端服务器做集群来抗住日均多少亿+的日均pv。

去参加阿里的技术峰会,听他们说他们的web容器都是自己写的,就算他单实例抗10万http并发,2000台是2亿http并发,并且他们还可以根据预知洪峰来无限拓展,很恐怖,就一个首页。。。

4、减少后端服务器的并发/负载压力

除了接口以外的其他所有http请求全部转移到前端nginx上,接口的请求调用tomcat,参考nginx反向代理tomcat。

且除了第一次页面请求外,浏览器会大量调用本地缓存。

5、即使后端服务暂时超时或者宕机了,前端页面也会正常访问,只不过数据刷不出来而已。

6、也许你也需要有微信相关的轻应用,那样你的接口完全可以共用,如果也有app相关的服务,那么只要通过一些代码重构,也可以大量复用接口,提升效率。(多端应用)

7、页面显示的东西再多也不怕,因为是异步加载。

8、nginx支持页面热部署,不用重启服务器,前端升级更无缝。

9、增加代码的维护性&易读性(前后端耦在一起的代码读起来相当费劲)。

10、提升开发效率,因为可以前后端并行开发,而不是像以前的强依赖。

11、在nginx中部署证书,外网使用https访问,并且只开放443和80端口,其他端口一律关闭(防止黑客端口扫描),
内网使用http,性能和安全都有保障。

12、前端大量的组件代码得以复用,组件化,提升开发效率,抽出来!

注意事项

1、在开需求会议的时候,前后端工程师必须全部参加,并且需要制定好接口文档,后端工程师要写好测试用例(2个维度),不要让前端工程师充当你的专职测试,
推荐使用chrome的插件postman或soapui或jmeter,service层的测试用例拿junit写。ps:前端也可以玩单元测试吗?

2、上述的接口并不是java里的interface,说白了调用接口就是调用你controler里的方法。

3、加重了前端团队的工作量,减轻了后端团队的工作量,提高了性能和可扩展性。

4、我们需要一些前端的框架来解决类似于页面嵌套,分页,页面跳转控制等功能。(上面提到的那些前端框架)。

5、如果你的项目很小,或者是一个单纯的内网项目,那你大可放心,不用任何架构而言,但是如果你的项目是外网项目,呵呵哒。

6、以前还有人在使用类似于velocity/freemarker等模板框架来生成静态页面,仁者见仁智者见智。

7、这篇文章主要的目的是说jsp在大型外网java web项目中被淘汰掉,可没说jsp可以完全不学,对于一些学生朋友来说,jsp/servlet等相关的java web基础还是要掌握牢的,不然你以为springmvc这种框架是基于什么来写的?

8、如果页面上有一些权限等等相关的校验,那么这些相关的数据也可以通过ajax从接口里拿。

9、对于既可以前端做也可以后端做的逻辑,我建议是放到前端,为什么?

因为你的逻辑需要计算资源进行计算,如果放到后端去run逻辑,则会消耗带宽&内存&cpu等等计算资源,你要记住一点就是:服务端的计算资源是有限的,而如果放到前端,使用的是客户端的计算资源,这样你的服务端负载就会下降(高并发场景)。

类似于数据校验这种,前后端都需要做!

10、前端需要有机制应对后端请求超时以及后端服务宕机的情况,友好的展示给用户。

扩展阅读

1、其实对于js,css,图片这类的静态资源可以考虑放到类似于阿里云的oss这类文件服务器上(如果是普通的服务器&操作系统,存储在到达pb级的文件后,或者单个文件夹内的文件数量达到3-5万,io会有很严重的性能问题),再在oss上配cdn(全国子节点加速),这样你页面打开的速度像飞一样, 无论你在全国的哪个地方,并且你的nginx的负载会进一步降低。

2、如果你要玩轻量级微服务架构,要使用nodejs做网关,用nodejs的好处还有利于seo优化,因为nginx只是向浏览器返回页面静态资源,而国内的搜索引擎爬虫只会抓取静态数据,不会解析页面中的js,这使得应用得不到良好的搜索引擎支持。同时因为nginx不会进行页面的组装渲染,需要把静态页面返回到浏览器,然后完成渲染工作,这加重了浏览器的渲染负担。

浏览器发起的请求经过nginx进行分发,URL请求统一分发到nodejs,在nodejs中进行页面组装渲染;API请求则直接发送到后端服务器,完成响应。

3、如果遇到跨域问题,spring4的CORS可以完美解决,但一般使用nginx反向代理都不会有跨域问题,除非你把前端服务和后端服务分成两个域名。
JSONP的方式也被淘汰掉了。

4、如果想玩多端应用,注意要去掉tomcat原生的session机制,要使用token机制,使用缓存(因为是分布式系统),做单点,对于token机制的安全性问题,可以搜一下jwt。

5、前端项目中可以加入mock测试(构造虚拟测试对象来模拟后端,可以独立开发和测试),后端需要有详细的测试用例,保证服务的可用性与稳定性。

总结

前后端分离并非仅仅只是一种开发模式,而是一种架构模式(前后端分离架构)。

千万不要以为只有在撸代码的时候把前端和后端分开就是前后端分离了。需要区分前后端项目。前端项目与后端项目是两个项目,放在两个不同的服务器,需要独立部署,两个不同的工程,两个不同的代码库,不同的开发人员。

前后端工程师需要约定交互接口,实现并行开发,开发结束后需要进行独立部署,前端通过ajax来调用http请求调用后端的restful api。

前端只需要关注页面的样式与动态数据的解析&渲染,而后端专注于具体业务逻辑。

推荐阅读

(点击标题可跳转阅读)

通过阅读源码,提高你的 JS 水平

深入理解 ESLint

详解 ES 2018 新特性

觉得本文对你有帮助?请分享给更多人

关注「前端大全」加星标,提升前端技能

Web 实现前后端分离,前后端解耦

好文章,我在看❤️

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×