Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)四(微服务搭建-通用工具类-通用异常处理-自定义异常处理)

在这里插入图片描述

package com.leyou.common.utils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.UnsupportedEncodingException;

import java.net.URLDecoder;

import java.net.URLEncoder;

/**

  • Cookie 工具类

*/

public final class CookieUtils {

protected static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);

/**

  • 得到Cookie的值, 不编码

  • @param request

  • @param cookieName

  • @return

*/

public static String getCookieValue(HttpServletRequest request, String cookieName) {

return getCookieValue(request, cookieName, false);

}

/**

  • 得到Cookie的值,

  • @param request

  • @param cookieName

  • @return

*/

public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {

Cookie[] cookieList = request.getCookies();

if (cookieList == null || cookieName == null){

return null;

}

String retValue = null;

try {

for (int i = 0; i < cookieList.length; i++) {

if (cookieList[i].getName().equals(cookieName)) {

if (isDecoder) {

retValue = URLDecoder.decode(cookieList[i].getValue(), “UTF-8”);

} else {

retValue = cookieList[i].getValue();

}

break;

}

}

} catch (UnsupportedEncodingException e) {

logger.error(“Cookie Decode Error.”, e);

}

return retValue;

}

/**

  • 得到Cookie的值,

  • @param request

  • @param cookieName

  • @return

*/

public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {

Cookie[] cookieList = request.getCookies();

if (cookieList == null || cookieName == null){

return null;

}

String retValue = null;

try {

for (int i = 0; i < cookieList.length; i++) {

if (cookieList[i].getName().equals(cookieName)) {

retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);

break;

}

}

} catch (UnsupportedEncodingException e) {

logger.error(“Cookie Decode Error.”, e);

}

return retValue;

}

/**

  • 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码

*/

public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue) {

setCookie(request, response, cookieName, cookieValue, -1);

}

/**

  • 设置Cookie的值 在指定时间内生效,但不编码

*/

public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage) {

setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);

}

/**

  • 设置Cookie的值 不设置生效时间,但编码

*/

public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, boolean isEncode) {

setCookie(request, response, cookieName, cookieValue, -1, isEncode);

}

/**

  • 设置Cookie的值 在指定时间内生效, 编码参数

*/

public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {

doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);

}

/**

  • 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)

*/

public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) {

doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);

}

/**

  • 删除Cookie带cookie域名

*/

public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {

doSetCookie(request, response, cookieName, “”, -1, false);

}

/**

  • 设置Cookie的值,并使其在指定时间内生效

  • @param cookieMaxage

  •        cookie生效的最大秒数
    

*/

private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {

try {

if (cookieValue == null) {

cookieValue = “”;

} else if (isEncode) {

cookieValue = URLEncoder.encode(cookieValue, “utf-8”);

}

Cookie cookie = new Cookie(cookieName, cookieValue);

if (cookieMaxage > 0)

cookie.setMaxAge(cookieMaxage);

if (null != request)// 设置域名的cookie

cookie.setDomain(getDomainName(request));

cookie.setPath(“/”);

response.addCookie(cookie);

} catch (Exception e) {

logger.error(“Cookie Encode Error.”, e);

}

}

/**

  • 设置Cookie的值,并使其在指定时间内生效

  • @param cookieMaxage

  •        cookie生效的最大秒数
    

*/

private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) {

try {

if (cookieValue == null) {

cookieValue = “”;

} else {

cookieValue = URLEncoder.encode(cookieValue, encodeString);

}

Cookie cookie = new Cookie(cookieName, cookieValue);

if (cookieMaxage > 0)

cookie.setMaxAge(cookieMaxage);

if (null != request)// 设置域名的cookie

cookie.setDomain(getDomainName(request));

cookie.setPath(“/”);

response.addCookie(cookie);

} catch (Exception e) {

logger.error(“Cookie Encode Error.”, e);

}

}

/**

  • 得到cookie的域名

*/

private static final String getDomainName(HttpServletRequest request) {

String domainName = null;

String serverName = request.getRequestURL().toString();

if (serverName == null || serverName.equals(“”)) {

domainName = “”;

} else {

serverName = serverName.toLowerCase();

serverName = serverName.substring(7);

final int end = serverName.indexOf(“/”);

serverName = serverName.substring(0, end);

final String[] domains = serverName.split(“\.”);

int len = domains.length;

if (len > 3) {

// www.xxx.com.cn

domainName = domains[len - 3] + “.” + domains[len - 2] + “.” + domains[len - 1];

} else if (len <= 3 && len > 1) {

// xxx.com or xxx.cn

domainName = domains[len - 2] + “.” + domains[len - 1];

} else {

domainName = serverName;

}

}

if (domainName != null && domainName.indexOf(“:”) > 0) {

String[] ary = domainName.split(“\:”);

domainName = ary[0];

}

return domainName;

}

}

(2)IdWorker

在这里插入图片描述

package com.leyou.common.utils;

import java.lang.management.ManagementFactory;

import java.net.InetAddress;

import java.net.NetworkInterface;

/**

  • 名称:IdWorker.java

  • 描述:分布式自增长ID

  • Twitter的 Snowflake JAVA实现方案
    
  • 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:

  • 1||0—0000000000 0000000000 0000000000 0000000000 0 — 00000 —00000 —000000000000

  • 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,

  • 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),

  • 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。

  • 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),

  • 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。

  • 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))

  • @author Polim

*/

public class IdWorker {

// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)

private final static long twepoch = 1288834974657L;

// 机器标识位数

private final static long workerIdBits = 5L;

// 数据中心标识位数

private final static long datacenterIdBits = 5L;

// 机器ID最大值

private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);

// 数据中心ID最大值

private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

// 毫秒内自增位

private final static long sequenceBits = 12L;

// 机器ID偏左移12位

private final static long workerIdShift = sequenceBits;

// 数据中心ID左移17位

private final static long datacenterIdShift = sequenceBits + workerIdBits;

// 时间毫秒左移22位

private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

private final static long sequenceMask = -1L ^ (-1L << sequenceBits);

/* 上次生产id时间戳 */

private static long lastTimestamp = -1L;

// 0,并发控制

private long sequence = 0L;

private final long workerId;

// 数据标识id部分

private final long datacenterId;

public IdWorker(){

this.datacenterId = getDatacenterId(maxDatacenterId);

this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);

}

/**

  • @param workerId

  •        工作机器ID
    
  • @param datacenterId

  •        序列号
    

*/

public IdWorker(long workerId, long datacenterId) {

if (workerId > maxWorkerId || workerId < 0) {

throw new IllegalArgumentException(String.format(“worker Id can’t be greater than %d or less than 0”, maxWorkerId));

}

if (datacenterId > maxDatacenterId || datacenterId < 0) {

throw new IllegalArgumentException(String.format(“datacenter Id can’t be greater than %d or less than 0”, maxDatacenterId));

}

this.workerId = workerId;

this.datacenterId = datacenterId;

}

/**

  • 获取下一个ID

  • @return

*/

public synchronized long nextId() {

long timestamp = timeGen();

if (timestamp < lastTimestamp) {

throw new RuntimeException(String.format(“Clock moved backwards. Refusing to generate id for %d milliseconds”, lastTimestamp - timestamp));

}

if (lastTimestamp == timestamp) {

// 当前毫秒内,则+1

sequence = (sequence + 1) & sequenceMask;

if (sequence == 0) {

// 当前毫秒内计数满了,则等待下一秒

timestamp = tilNextMillis(lastTimestamp);

}

} else {

sequence = 0L;

}

lastTimestamp = timestamp;

// ID偏移组合生成最终的ID,并返回ID

long nextId = ((timestamp - twepoch) << timestampLeftShift)

| (datacenterId << datacenterIdShift)

| (workerId << workerIdShift) | sequence;

return nextId;

}

private long tilNextMillis(final long lastTimestamp) {

long timestamp = this.timeGen();

while (timestamp <= lastTimestamp) {

timestamp = this.timeGen();

}

return timestamp;

}

private long timeGen() {

return System.currentTimeMillis();

}

/**

  • 获取 maxWorkerId

*/

protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {

StringBuffer mpid = new StringBuffer();

mpid.append(datacenterId);

String name = ManagementFactory.getRuntimeMXBean().getName();

if (!name.isEmpty()) {

/*

  • GET jvmPid

*/

mpid.append(name.split(“@”)[0]);

}

/*

  • MAC + PID 的 hashcode 获取16个低位

*/

return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);

}

/**

  • 数据标识id部分

*/

protected static long getDatacenterId(long maxDatacenterId) {

long id = 0L;

try {

InetAddress ip = InetAddress.getLocalHost();

NetworkInterface network = NetworkInterface.getByInetAddress(ip);

if (network == null) {

id = 1L;

} else {

byte[] mac = network.getHardwareAddress();

id = ((0x000000FF & (long) mac[mac.length - 1])

| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;

id = id % (maxDatacenterId + 1);

}

} catch (Exception e) {

System.out.println(" getDatacenterId: " + e.getMessage());

}

return id;

}

}

(3)JsonUtils

在这里插入图片描述

package com.leyou.common.utils;

import com.fasterxml.jackson.core.JsonProcessingException;

import com.fasterxml.jackson.core.type.TypeReference;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.sun.istack.internal.Nullable;

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

import lombok.ToString;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.io.IOException;

import java.util.List;

import java.util.Map;

public class JsonUtils {

public static final ObjectMapper mapper = new ObjectMapper();

private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);

@Nullable

public static String serialize(Object obj) {

if (obj == null) {

return null;

}

if (obj.getClass() == String.class) {

return (String) obj;

}

try {

return mapper.writeValueAsString(obj);

} catch (JsonProcessingException e) {

logger.error(“json序列化出错:” + obj, e);

return null;

}

}

@Nullable

public static String toString(Object obj) {

if (obj == null) {

return null;

}

if (obj.getClass() == String.class) {

return (String) obj;

}

try {

return mapper.writeValueAsString(obj);

} catch (JsonProcessingException e) {

logger.error(“json序列化出错:” + obj, e);

return null;

}

}

@Nullable

public static T parse(String json, Class tClass) {

try {

return mapper.readValue(json, tClass);

} catch (IOException e) {

logger.error(“json解析出错:” + json, e);

return null;

}

}

@Nullable

public static T toBean(String json, Class tClass) {

try {

return mapper.readValue(json, tClass);

} catch (IOException e) {

logger.error(“json解析出错:” + json, e);

return null;

}

}

@Nullable

public static List parseList(String json, Class eClass) {

try {

return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));

} catch (IOException e) {

logger.error(“json解析出错:” + json, e);

return null;

}

}

@Nullable

public static List toList(String json, Class eClass) {

try {

return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));

} catch (IOException e) {

logger.error(“json解析出错:” + json, e);

return null;

}

}

@Nullable

public static <K, V> Map<K, V> parseMap(String json, Class kClass, Class vClass) {

try {

return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));

} catch (IOException e) {

logger.error(“json解析出错:” + json, e);

return null;

}

}

@Nullable

public static <K, V> Map<K, V> toMap(String json, Class kClass, Class vClass) {

try {

return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));

} catch (IOException e) {

logger.error(“json解析出错:” + json, e);

return null;

}

}

@Nullable

public static T nativeRead(String json, TypeReference type) {

try {

return mapper.readValue(json, type);

} catch (IOException e) {

logger.error(“json解析出错:” + json, e);

return null;

}

}

}

(4)NumberUtils

在这里插入图片描述

package com.leyou.common.utils;

import java.math.BigDecimal;

import java.math.RoundingMode;

import java.util.ArrayList;

import java.util.List;

import java.util.Random;

import java.util.regex.MatchResult;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

/**

  • @author: HuYi.Zhang

  • @create: 2018-04-25 09:13

**/

public class NumberUtils {

public static boolean isInt(Double num) {

return num.intValue() == num;

}

/**

  • 判断字符串是否是数值格式

  • @param str

  • @return

*/

public static boolean isDigit(String str){

if(str == null || str.trim().equals(“”)){

return false;

}

return str.matches(“^\d+$”);

}

/**

  • 将一个小数精确到指定位数

  • @param num

  • @param scale

  • @return

*/

public static double scale(double num, int scale) {

BigDecimal bd = new BigDecimal(num);

return bd.setScale(scale, RoundingMode.HALF_UP).doubleValue();

}

// 从字符串中根据正则表达式寻找,返回找到的数字数组

public static Double[] searchNumber(String value, String regex){

List doubles = new ArrayList<>();

Pattern pattern = Pattern.compile(regex);

Matcher matcher = pattern.matcher(value);

if(matcher.find()) {

MatchResult result = matcher.toMatchResult();

for (int i = 1; i <= result.groupCount(); i++) {

doubles.add(Double.valueOf(result.group(i)));

}

}

return doubles.toArray(new Double[doubles.size()]);

}

/**

  • 生成指定位数的随机数字

  • @param len

  • @return

*/

public static String generateCode(int len){

len = Math.min(len, 8);

int min = Double.valueOf(Math.pow(10, len - 1)).intValue();

int num = new Random().nextInt(Double.valueOf(Math.pow(10, len + 1)).intValue() - 1) + min;

return String.valueOf(num).substring(0,len);

}

}

(5)引入上述所有需要的依赖

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns=“http://maven.apache.org/POM/4.0.0”

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>

leyou

com.leyou.parent

1.0.0-SNAPSHOT

4.0.0

com.leyou.common

ly-common

org.apache.tomcat.embed

tomcat-embed-core

org.springframework.boot

spring-boot-starter-logging

com.fasterxml.jackson.core

jackson-databind

2.9.6

(6)JSON方法的测试

在这里插入图片描述

public static void main(String[] args) {

User user = new User(“Jack”,21);

//序列化

String struser = toString(user);

System.out.println(struser);

//反序列化

User usertobean = toBean(struser,User.class);

System.out.println(usertobean);

//toList

String json = “[20 , -10 , 5 , 15]”;

List list = toList(json,Integer.class);

System.out.println(“list:”+list);

//toMap

String json2 = “{“name”: “Jack”,“age”: “21”}”;

Map<String,String> map = toMap(json2,String.class,String.class);

System.out.println(“Map:”+map);

System.out.println(“===============================”);

String json3 = “[{“name”: “Jack”,“age”: “21”},{“name”: “zhangsan”,“age”: “18”}]”;

List<Map<String,String>> maps = nativeRead(json3, new TypeReference<List<Map<String,String>>>() {});

for (Map<String, String> stringStringMap : maps) {

System.out.println(“stringStringMap:”+stringStringMap);

}

}

运行结果

在这里插入图片描述

三、 通用异常处理


在项日中出现异常是在所难免的,但是出现异常后怎么处理,这就很有学问了。

1、场景预设

(1)场景

我们预设这样一个场景,假如我们做新增商品,需要接收下面的参数;

price:价格

nane:名称

然后对数据做简单校验;

价格不能为空

新增时,白动形成lD,后随商品对象一起返回

(2)创建实体类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.pojo;

import lombok.Data;

@Data

public class Item {

private Integer id;

private String name;

private Long price;

}

(3)创建实体类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.service;

import com.leyou.item.pojo.Item;

import org.springframework.stereotype.Service;

import java.util.Random;

@Service

public class ItemService {

public Item saveItem(Item item){

int id = new Random().nextInt();

item.setId(id);

return item;

}

}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.leyou.item.web;

import com.leyou.item.pojo.Item;

import com.leyou.item.service.ItemService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

@RestController //声明当前类是控制层,服务请求相当于Servlet

@RequestMapping(“item”) //设置其对应的访问的映射名称,也就是当前Servlet的别名

public class itemController {

//注入Service

@Autowired

private ItemService itemService;

@PostMapping //拦截post请求,可以设置其对应的映射方法的路径

public ResponseEntity saveItem(Item item){//ResponseEntity设置返回状态码类型

//业务逻辑做校验

//校验价格

if(item.getPrice() == null){

//请求失败返回状态码400,设置其内容是空

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);

}

item = itemService.saveItem(item);

//设置返回的是状态码类型的是201以及其类型是item类型

return ResponseEntity.status(HttpStatus.CREATED).body(item);

}

}

a、启动测试项目

在这里插入图片描述

b、使用Insomnia用于测试发送网络请求

链接:https://pan.baidu.com/s/1_aqtJRIAGmPF83CUh4CWQA

提取码:kr9h

下载并安装

在这里插入图片描述

起个名字

在这里插入图片描述

选择请求访问和访问路径,设置提交方式

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

返回正确结果

在这里插入图片描述

删除价格再次发送请求

在这里插入图片描述

在这里插入图片描述

请求失败并返回状态码

在这里插入图片描述

c、上述出现异常没有友好提示设置对应其提示

在这里插入图片描述

if(item.getPrice() == null){

//请求失败返回状态码400,设置其内容是空

throw new RuntimeException(“价格不能为空”);

}

重新启动测试并运行

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上述返回方式不正确

2、统一异常处理(统一拦截Controller)

(1)创建CommonExceptionHandler

在这里插入图片描述

在这里插入图片描述

修改一下包路径

在这里插入图片描述

在这里插入图片描述

(2)完善CommonExceptionHandler类前,先在这里引入SpringMVC的依赖

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns=“http://maven.apache.org/POM/4.0.0”

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>

leyou

com.leyou.parent

1.0.0-SNAPSHOT

4.0.0

com.leyou.common

ly-common

org.apache.tomcat.embed

tomcat-embed-core

org.springframework.boot

spring-boot-starter-logging

com.fasterxml.jackson.core

jackson-databind

org.springframework

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

结尾

正式学习前端大概 3 年多了,很早就想整理这个书单了,因为常常会有朋友问,前端该如何学习,学习前端该看哪些书,我就讲讲我学习的道路中看的一些书,虽然整理的书不多,但是每一本都是那种看一本就秒不绝口的感觉。

以下大部分是我看过的,或者说身边的人推荐的书籍,每一本我都有些相关的推荐语,如果你有看到更好的书欢迎推荐呀。

戳这里获取前端学习资料

?>

<project xmlns=“http://maven.apache.org/POM/4.0.0”

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>

leyou

com.leyou.parent

1.0.0-SNAPSHOT

4.0.0

com.leyou.common

ly-common

org.apache.tomcat.embed

tomcat-embed-core

org.springframework.boot

spring-boot-starter-logging

com.fasterxml.jackson.core

jackson-databind

org.springframework

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-h3ud4EbB-1712938493061)]

[外链图片转存中…(img-gc5x5GCv-1712938493062)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-IVqJ1fsd-1712938493062)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

结尾

正式学习前端大概 3 年多了,很早就想整理这个书单了,因为常常会有朋友问,前端该如何学习,学习前端该看哪些书,我就讲讲我学习的道路中看的一些书,虽然整理的书不多,但是每一本都是那种看一本就秒不绝口的感觉。

以下大部分是我看过的,或者说身边的人推荐的书籍,每一本我都有些相关的推荐语,如果你有看到更好的书欢迎推荐呀。

戳这里获取前端学习资料

前端学习书籍导图-1

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值