Web 前端技术的能力和应用领域不断增多,Web 前端开发工作的广度和深度也随之日益提升,这就要求 Web 前端工程师必须扩展自己的知识技能体系
不少前端开发者吐槽,这些技术更新太快了:刚刚学习了新语言、新框架、新组件库,没多久就变成了老技术,然后又要赶着去学习新技术;加上紧张的工作生活挤压自己的学习时间,一轮一轮下来,觉得自己没沉淀下来什么
如何在学习和实践前端技术中有所沉淀
开发的 Web 前端是一种图形用户界面,即 GUI
历史的车轮会一直向前,但技术的轮子会时不时往回滚。例如 移动互联网时代,在移动设备算力有限的条件下,iOS、Android 移动端的原生 App 要比 Web 应用更普及。又如 基于浏览器端 JS Web 应用为主流的今天,同构 JS 应用、SSR 服务器端渲染、SSG 静态网站生成又开始在行业中有了一席之地,如 Next.js、Nuxt.js
GUI 是 Graphical User Interface 的缩写,其中 Interface 可以翻译成“界面”,也可以翻译成“接口”。类比编程语言里接口的特性,使用者只关注接口,而无需关注接口对应的内部实现
前端界面也是这样,用户只关注与应用界面的交互,而不需要关注界面后面对应的程序是怎么实现的。前端开发者是这类“接口”的负责人,也是接口的实现者,用户是接口的使用者,用户不会提出接口该怎样设计(不过会抱怨“这界面怎么这么难用”),所以你同时也是接口设计的把关人
和编程接口有着一系列设计模式类似,GUI 作为接口也有它特有的设计准则。
作为前端工程师,你可能会有疑问,上面这些不都是 PM、设计师和交互设计师的工作吗?前端只要写代码就好了(最好我连切图都不需要做)。那有点冒犯地说,你可能被大公司惯坏了
无论国内国外,在具有活力的创业公司,你都能找到一些优秀的前端开发者,他们在前端代码之外,也负责交互设计,分担一部分 PM 工作,甚至还必须自己兼任设计师
当然,并不是每个人都会选择创业公司,也不否定大公司或其他非创业公司一样会有全能型的选手,只是需要知道在成为优秀的前端开发者的路上,程序代码以外的知识和技能必然会成为你的助力
当开发零售电商网站时,从商品促销页到详情页、到购物车、再到结算、下单最终支付成功,转化率是呈漏斗形下跌的,优化关键流程和关键交互可以有效提高各环节转化率。
再如,当开发面向欧洲客户的网站时,需要针对欧盟的 GDPR 开展合规工作,页面上使用 Cookie 时必须明确提示用户,Cookie 中保存了哪些数据,网站会怎样使用这些数据,以及数据会与哪些第三方分享等等。这些本身并不是前端程序代码,但却决定了前端软件产品的质量和效果
其实这与其他工程师并没有本质区别。一位优秀的后端工程师,除了编写后端代码,也需要深入了解业务需求,真正吃透业务才能开发出可伸缩、可扩展、可维护的后端服务。一位优秀的算法工程师,也不能只一味的去套用最先进的算法模型,而更需要分析业务,针对业务建模、设计适合的算法、实现并落地
JSP 应用的开发是这样演化的:最初是简单的 JSP 文件,里面混写了一段 Java 代码,通过 JDBC 连接数据库,SQL 查询到数据,然后直接在同一文件的 HTML 模板里混入 Java 变量展现出来。这样的 JSP 只要拷贝到 Tomcat Web Container 的 ROOT 目录中就可以工作了
<html>
<body>
<%! int getCount() { // JDBC ... int count = /* ... */; return count; } %>
<p>图书数量:<%= getCount() %></p>
</body>
</html>React 也可以把逻辑和视图写在一个文件里
// main.jsx
import React from "react"
import ReactDOM from "react-dom"
const App = () => {
const [count, setCount] = React.useState()
React.useEffect(() => {
;(async () => {
const res = await fetch("/book/count")
// ...
setCount(data)
})()
}, [])
return <p>图书数量:{count}</p>
}
ReactDOM.render(<App />, document.getElementById("app"))单从代码上看两者在 HTML 模板方面异曲同工
JSP 页面模板上有条件或者循环逻辑时,可以通过混写 Java 代码来实现。但考虑到代码的可读性和可维护性,JSP 引入了标签库,如下面的 JSTL
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <% // ... %>
<ul>
<c:forEach var="book" items="${books}">
<li>
书名:${book.title}
<c:if test="${book.isSoldOut}"> (已售空) </c:if>
</li>
</c:forEach>
</ul>Vue.js 框架模板包含 v-if 、 v-for 指令,可以在模板中实现条件和循环
<script>
// ...
</script>
<template>
<ul>
<li v-for="book in books">
书名:{{ book.title }}
<span v-if="book.isSoldOut"> (已售空) </span>
</li>
</ul>
</template>当业务变得复杂后,把控制逻辑和页面展现都写在同一个 JSP 文件中已经无法满足项目增长的需要。这时就引入了 MVC 架构,JSP 作为纯粹的视图,Servlet 作为控制器,加上 Java Bean 对象作为模型。这样就解耦了三种代码,提高了可维护性和可扩展性
// BookBean.java (Model)
public class BookBean {
// ...
}
// BookController.java (Controller)
public class ControllerServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String isbn = request.getParameter("isbn");
BookBean book = new BookBean();
// JDBC ...
request.setAttribute("book", book);
RequestDispatcher dispatcher = request.getRequestDispatcher("book.jsp");
dispatcher.forward(request, response);
}
}book.jsp (View)
<%@page import="BookBean"%>
<html>
<body>
<%! BookBean book = (BookBean) request.getAttribute("book"); %>
<p>书名:<%= book.getTitle() %></p>
</body>
</html>再来看三大 Web 前端框架的 Angular 是如何以 MVVM 架构拆分代码文件的。数据模型、HTML 视图与 JSP 在概念上是相似的,而 View-model 视图模型则取代了上面 JSP 中控制器的地位
// book.ts (Model)
export default class Book {
// ...
}
// book.component.ts (View-model)
import { Component, OnInit } from "@angular/core"
import Book from "./book"
@Component({
selector: "book-detail",
templateUrl: "./book.component.html",
styleUrls: ["./book.component.css"]
})
export class BookComponent implements OnInit {
title: string
ngOnInit() {
const book = new Book()
// ...
this.title = book.title
}
}
// book.component.html (View)
<p>书名:{{ title }}</p>当然,MVC 和 MVVM 架构本是平台无关的,Java Web 领域也有 MVVM 框架,JS 前端领域也有 MVC 框架。
当 JSP 项目包含 .java 源文件时,需要编译并与 JSP 文件一起打包成 .war 包,再部署到 Tomcat 里就可以提供服务了
虽然目标不同但 React、Vue.js、Angular 项目一般而言也需要先通过 Webpack、Vite 等工具构建,生成若干 bundle 后再部署到 CDN 上,即可投入使用
Java 的技术生态是极为丰富的,可以借助第三方开源库(或闭源库)实现很多功能。JSP 项目中也常会引入很多这样的依赖。在最早期,这些依赖是通过拷贝 .jar 包到项目中引入的。当依赖项增多,依赖关系变得复杂后,Java 引入了 Maven 工具。Maven 其中一项职能就是定义、管理依赖
<!-- pom.xml -->
<project>
<groupId>com.example.book</groupId>
<artifactId>bookProject</artifactId>
<version>${project1Version}</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
</project>在项目构建时 Maven 会从中心仓库里下载预编译好的 log4j 作为项目依赖。JS 项目的 package.json 与上面的 XML 有类似的作用:
{
"name": "book-project",
"version": "1.0.0",
"dependencies": {
"lodash": "^4.17.21"
}
}在安装依赖时 npm/pnpm 会从 npm 仓库下载预编译好的 lodash 供项目使用。
前端在这数十年中,沉淀下来的各种概念、原理、最佳实践,都会在新的前端技术中继续发扬光大。所以没有所谓“学了白学”,读书讲究“开卷有益”,学习前端技术的每个知识点、每次实践都帮你踏出坚实的一步