Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目(1)

//发送数据到后台

ly.http.post(“/search/page”,this.search).then(resp => {

console.log(resp);

}).catch(error =>{

})

}

},

3)刷新页面观察请求发送情况

请求路径

在这里插入图片描述

请求参数

在这里插入图片描述

发送请求成功

2、后台接收请求并返回数据

(1)编写对象用于接收页面发送的请求

在这里插入图片描述

在这里插入图片描述

package com.leyou.search.pojo;

public class SearchRequest {

private String key;//搜索字段

private Integer page;//当前页

private static final Integer DEFAULT_SIZE = 20;//每页个数

private static final Integer DEFAULT_PAGE = 1;//默认页

public String getKey() {

return key;

}

public void setKey(String key) {

this.key = key;

}

public Integer getPage() {

if(page == null){

return DEFAULT_PAGE;

}

//获取页码的时候进行校验,不能小于1

return Math.max(DEFAULT_PAGE,page);// Math.max返回DEFAULT_PAGE,page之间的最大值

}

public void setPage(Integer page) {

this.page = page;

}

public static Integer getDefaultSize() {

return DEFAULT_SIZE;

}

}

(2)SearchController

首先分析几个问题:

  • 请求方式:Post

  • 请求路径:/search/page,不过前面的/search应该是网关的映射路径,因此真实映射路径page,代表分页查询。

  • 请求参数:json格式,目前只有一个属性: key,搜索关键字,但是搜索结果页一定是带有分页查询的,所以将

来肯定会有page属性,因此我们可以用一个对象来接收请求的json数据:

  • 返回值为之前定义的PageResult

在这里插入图片描述

在这里插入图片描述

package com.leyou.search.web;

import com.leyou.common.vo.PageResult;

import com.leyou.search.pojo.Goods;

import com.leyou.search.pojo.SearchRequest;

import com.leyou.search.service.SearchService;

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

import org.springframework.http.ResponseEntity;

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

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

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

@RestController

public class SearchController {

@Autowired

private SearchService searchService;

/*

搜索功能

*/

@PostMapping(“page”)

public ResponseEntity<PageResult> search(@RequestBody SearchRequest request){

return ResponseEntity.ok(searchService.search(request));

}

}

(3)完善SearchService(设置将请求的字段进行)

在这里插入图片描述

在这里插入图片描述

public PageResult search(SearchRequest request) {

Integer page = request.getPage();

Integer size = request.getSize();

//创建查询构建器SpringDataElasticSearch - NativeSearchQueryBuilder过滤聚合高亮查询

NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

//0 结果过滤

queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{“id”,“subTitle”,“skus”},null));

//1 分页

queryBuilder.withPageable(PageRequest.of(page,size));

//2 过滤

queryBuilder.withQuery(QueryBuilders.matchQuery(“all”,request.getKey()));

//3 查询

Page result = repository.search(queryBuilder.build());

//4 解析结果

long total = result.getTotalElements();

int totalPage = result.getTotalPages();

List goodsList = result.getContent();

return new PageResult<>(total,totalPage,goodsList);

}

(4)从新启动运行测试

在这里插入图片描述

http://www.leyou.com/search.html?key=手机

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

(4)上述当中我们发现其他字段都为null(设置jackson如果是null就不返回数据)

在这里插入图片描述

jackson:

default-property-inclusion: non_null

重新运行并测试

在这里插入图片描述

刷新刚刚的网页

返回完美的数据

在这里插入图片描述

测试翻页

在这里插入图片描述

在这里插入图片描述

(5)商户数情况下发现永远查询不到第一页所有需要修改代码(SearchService)

在这里插入图片描述

Integer page = request.getPage() - 1;

重新运行并测试

在这里插入图片描述

在这里插入图片描述

二、功能实现(前台)搜索功能


1、将返回的数据渲染到页面上

(1)找到页面上的 <div class="goods-list"> 删除其他的li,设置v-for

在这里插入图片描述

在这里插入图片描述

  • (2)处理返回的数据

    在这里插入图片描述

    el: “#searchApp”,

    data: {

    search:{},

    goodsList:[],

    total: 0,

    totalPage: 0,

    selectedSku:{}

    },

    created(){//这是个构造方法

    //获取请求参数

    const search = ly.parse( location.search.substring(1));

    this.search = search;

    //发送后台数据

    this.loadData();

    },

    methods:{

    loadData(){

    //发送数据到后台

    ly.http.post(“/search/page”,this.search).then(resp => {

    //保存分页结果

    this.total = resp.data.total;

    this.totalPage = resp.data.totalPage;

    //保存当前页商品数据

    resp.data.items.forEach(goods =>{

    // 把JSON处理为JS对象

    goods.skus = JSON.parse(goods.skus);//JSON.parse将JSON字符串转换为JSON对象

    //初始化被选中的sku

    goods.selectedSku = goods.skus[0];

    })

    this.goodsList = resp.data.items;

    }).catch(error =>{

    })

    }

    },

    (3)完善HTML代码

    在这里插入图片描述

    • ¥

      促{{ goods.subTitle }}

      已有2000人评价

      加入购物车

      对比

      关注

      刷新页面

      显示成功

      在这里插入图片描述

      (4)处理上述的代码
      1)设置价格

      定义ly变量

      在这里插入图片描述

      设置价格格式

      在这里插入图片描述

      刷新页面

      在这里插入图片描述

      2)处理字太多下不下

      在这里插入图片描述

      促{{ goods.subTitle.substring(0,15) }}…

      刷新页面

      在这里插入图片描述

      3)设置点击小图片切换大图片

      在这里插入图片描述

      在这里插入图片描述

      • 刷新页面

        在这里插入图片描述

        设置鼠标动到图片上切换选择并显示对应的图片

        在这里插入图片描述

        鼠标悬浮直接切换当前的sku,之后图片价格和描述都会改变

        @mouseenter=“goods.selectedSku=sku”

        切换成功

        在这里插入图片描述

        2、设置分页条

        在这里插入图片描述

        在这里插入图片描述

      • 设置初始页

        在这里插入图片描述

        search.page = parseInt(search.page) || 1;

        刷新页面。我们可以看到设置了page的初始值为1

        在这里插入图片描述

        设置是否选中当前页

        在这里插入图片描述

      • 刷新页面

        在这里插入图片描述

        3、设置分页的当前页永远在中间

        当当页大于3的时候整体页数+1

        在这里插入图片描述

        v-for=“i in Math.min(5,totalPage)” :key=“i”>

        实现index函数

        在这里插入图片描述

        index(i){

        if(this.search.page <= 3 || this.totalPage < 5){

        return i;

        }else if(this.search.page >= this.totalPage - 2){

        return this.totalPage - 5 + i;

        }else {

        return i + this.search.page - 3;

        }

        }

        在这里插入图片描述

        在这里插入图片描述

        在这里插入图片描述

        在这里插入图片描述

        4、实现点击分页条实现页面的切换

        (1)实现上一页和下一页的切换
        1)添加点击事件

        在这里插入图片描述

        • <a href=“#” @click.prevent=“prePage” >«上一页

        • ...
        • <a href=“#” @click.prevent=“nextPage”>下一页»

          2)实现对应函数

          在这里插入图片描述

          prePage(){

          if(this.search.page > 1){

          this.search.page–;

          }

          },

          nextPage(){

          if(this.search.page < this.totalPage){

          this.search.page++;

          }

          }

          (2)设置总页数,和总数量
          1)总页数

          在这里插入图片描述

          共{{totalPage}}页 

          在这里插入图片描述

          2)总数量

          在这里插入图片描述

          {{total}} 商品

          {{ search.page }}/{{ totalPage }}

          <a class=“btn-arrow” href=“#” @click.prevent=“prePage” style=“display: inline-block”><

          <a class=“btn-arrow” href=“#” @click.prevent=“nextPage” style=“display: inline-block”>>

          在这里插入图片描述

          (3)(实现点击发送请求刷新数据)监控page的状态如果发生变化就发送请求

          在这里插入图片描述

          watch:{

          search:{//page在search当中

          deep:true,

          handler(){

          if(!oldVal || !oldVal.key){

          return;

          }

          //把请求参数写到URL当中,防止页面刷新的时候page丢失

          location.search = “?” + ly.stringify(this.search);

          }

          }

          },

          在这里插入图片描述

          数据变换成功

          在这里插入图片描述

          三、展示过滤条件


          1、对分类和品牌(进行聚合)

          (1)拓展PageResult,创建SearchResult

          在这里插入图片描述

          在这里插入图片描述

          package com.leyou.search.pojo;

          import com.leyou.common.vo.PageResult;

          import com.leyou.item.pojo.Brand;

          import com.leyou.item.pojo.Category;

          import lombok.AllArgsConstructor;

          import lombok.Data;

          import lombok.NoArgsConstructor;

          import java.util.List;

          @Data

          public class SearchResult extends PageResult {

          private List categories;

          private List brands;

          public SearchResult(){

          }

          public SearchResult(Long total, Integer totalPage, List items, List categories, List brands) {

          super(total, totalPage, items);

          this.categories = categories;

          this.brands = brands;

          }

          }

          (2)修改SearchService的search方法(聚合分类和品牌)

          在这里插入图片描述

          @Autowired

          private ElasticsearchTemplate template;

          在这里插入图片描述

          public PageResult search(SearchRequest request) {

          Integer page = request.getPage() - 1;

          Integer size = request.getSize();

          //创建查询构建器SpringDataElasticSearch - NativeSearchQueryBuilder过滤聚合高亮查询

          NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

          //0 结果过滤

          queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{“id”,“subTitle”,“skus”},null));

          //1 分页

          queryBuilder.withPageable(PageRequest.of(page,size));

          //2 过滤

          queryBuilder.withQuery(QueryBuilders.matchQuery(“all”,request.getKey()));

          //3 聚合分类和品牌

          //3.1集合分类

          String categoryAggName = “category_agg”;

          queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field(“cid3”));

          //3.2对品牌进行聚合

          String brandAggName = “brand_agg”;

          queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field(“brandId”));

          //4 查询

          AggregatedPage result = template.queryForPage(queryBuilder.build(), Goods.class);

          //5 解析结果

          //5.1解析分页结果

          long total = result.getTotalElements();

          int totalPage = result.getTotalPages();

          List goodsList = result.getContent();

          //5.2解析聚合结果

          Aggregations aggs = result.getAggregations();

          List categories = parseCategoryAgg(aggs.get(categoryAggName));

          List brands = parseBrandAgg(aggs.get(brandAggName));

          return new PageResult<>(total,totalPage,goodsList);

          }

          在这里插入图片描述

          (3)完善上述两个方法
          1)添加一个新的API接口对应的方法

          在这里插入图片描述

          @GetMapping(“brand/brands”)

          List queryBrandByIds(@RequestParam(“ids”) List ids);

          2)实现对应的控制层

          在这里插入图片描述

          3)实现brandService

          在这里插入图片描述

          完善前先让BrandMapper继承BaseMapper

          在这里插入图片描述

          完善BrandService当中的queryByIds方法

          在这里插入图片描述

          public List queryByIds(List ids) {

          List brands = brandMapper.selectByIdList(ids);

          if(CollectionUtils.isEmpty(brands)){

          //查询失败抛出自定义的通用异常

          throw new LyException(ExceptionEnum.BRAND_NOT_FOUND);

          }

          return brands;

          }

          4)继续完善ly-search当中的SearchService当中的两个方法

          在这里插入图片描述

          private List parseBrandAgg(LongTerms terms) {

          try {

          List ids = terms.getBuckets()

          .stream().map(bucket -> bucket.getKeyAsNumber().longValue())

          .collect(Collectors.toList());

          List list = brandClient.queryBrandByIds(ids);

          return list;

          }catch (Exception e){

          return null;

          }

          }

          private List parseCategoryAgg(LongTerms terms) {

          try {

          List ids = terms.getBuckets()

          .stream().map(bucket -> bucket.getKeyAsNumber().longValue())

          .collect(Collectors.toList());

          List categories = categoryClient.queryCategoryByIds(ids);

          return categories;

          }catch (Exception e){

          return null;

          }

          }

          5)修改search方法的返回值类型

          在这里插入图片描述

          return new SearchResult(total,totalPage,goodsList,categories,brands);

          6)为了防止手残添加全都的代码

          在这里插入图片描述

          package com.leyou.search.service;

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

          import com.leyou.common.enums.ExceptionEnum;

          import com.leyou.common.exception.LyException;

          import com.leyou.common.utils.JsonUtils;

          import com.leyou.common.vo.PageResult;

          import com.leyou.item.pojo.*;

          import com.leyou.search.client.BrandClient;

          import com.leyou.search.client.CategoryClient;

          import com.leyou.search.client.GoodsClient;

          import com.leyou.search.client.SpecificationClient;

          import com.leyou.search.pojo.Goods;

          import com.leyou.search.pojo.SearchRequest;

          import com.leyou.search.pojo.SearchResult;

          import com.leyou.search.repository.GoodsRepository;

          import org.apache.commons.lang.StringUtils;

          import org.apache.commons.lang.math.NumberUtils;

          import org.apache.lucene.util.CollectionUtil;

          import org.elasticsearch.index.query.QueryBuilder;

          import org.elasticsearch.index.query.QueryBuilders;

          import org.elasticsearch.search.aggregations.Aggregation;

          import org.elasticsearch.search.aggregations.AggregationBuilders;

          import org.elasticsearch.search.aggregations.Aggregations;

          import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;

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

          import org.springframework.data.domain.Page;

          import org.springframework.data.domain.PageRequest;

          import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;

          import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;

          import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;

          import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

          import org.springframework.stereotype.Service;

          import org.springframework.util.CollectionUtils;

          import java.util.*;

          import java.util.stream.Collectors;

          @Service

          public class SearchService {

          @Autowired

          private CategoryClient categoryClient;

          @Autowired

          private BrandClient brandClient;

          @Autowired

          private GoodsClient goodsClient;

          @Autowired

          private SpecificationClient specClient;

          @Autowired

          private GoodsRepository repository;

          @Autowired

          private ElasticsearchTemplate template;

          public Goods buildGoods(Spu spu){

          //查询分类

          List categories = categoryClient.queryCategoryByIds(

          Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));

          if(CollectionUtils.isEmpty(categories)){

          throw new LyException(ExceptionEnum.CATEGORY_NOT_FOND);

          }

          //将categories集合当中所有的name取出来封装为一个字符串集合

          List names = categories.stream().map(Category::getName).collect(Collectors.toList());

          //查询品牌

          Brand brand = brandClient.queryBrandById(spu.getBrandId());

          if(brand == null){

          throw new LyException(ExceptionEnum.BRAND_NOT_FOUND);

          }

          //搜索字段 将字符串集合变成一个字符串以空格为分隔拼接到后面

          String all = spu.getTitle() + StringUtils.join(names," ") + brand.getName();

          //查询sku

          List skuList = goodsClient.querySkuBySpuId(spu.getId());

          if(CollectionUtils.isEmpty(skuList)){

          throw new LyException(ExceptionEnum.GOODS_SKU_NOT_FOND);

          }

          //对Sku进行处理

          List<Map<String,Object>> skus = new ArrayList<>();

          //价格集合

          ArrayList priceList = new ArrayList();

          for (Sku sku : skuList) {

          Map<String,Object> map = new HashMap<>();

          map.put(“id”,sku.getId());

          map.put(“title”,sku.getTitle());

          map.put(“price”,sku.getPrice());

          //截取sku当中图片逗号之前的第一个

          map.put(“images”,StringUtils.substringBefore(sku.getImages(),“,”));

          skus.add(map);

          //处理价格

          priceList.add(sku.getPrice());

          }

          //查询规格参数

          List params = specClient.queryParamList(null, spu.getCid3(), true);

          if(CollectionUtils.isEmpty(params)){

          throw new LyException(ExceptionEnum.SPEC_GROUP_NOT_FOND);

          }

          //查询商品详情

          SpuDetail spuDetail = goodsClient.queryDetailById(spu.getId());

          //获取通用规格参数,获取到通用规格参数的JSON字符串,将其转换为Map集合

          Map<Long, String> genericSpec = JsonUtils.toMap(spuDetail.getGenericSpec(), Long.class, String.class);

          //获取特有规格参数,获取到特有规格参数的JSON字符串,将其转换为Map集合,而Map集合当中的值是String,键为List集合

          Map<Long, List> specailSpec =

          JsonUtils.nativeRead(spuDetail.getSpecialSpec(),

          new TypeReference<Map<Long, List>>(){});

          //处理规格参数,key是规格参数的名称,值是规格参数的值

          Map<String,Object> specs = new HashMap<>();

          for (SpecParam param : params) {

          //规格名称

          String key = param.getName();

          Object value = “”;

          //判断是否是通过规格参数

          if(param.getGeneric()){

          value = genericSpec.get(param.getId());

          //判断是否是数值类型

          if(param.getNumeric()){

          //处理成段

          value = chooseSegment(value.toString(),param);

          }

          }else {

          value = specailSpec.get(param.getId());

          }

          //存入map

          specs.put(key,value);

          }

          //构建good对象

          Goods goods = new Goods();

          goods.setBrandId(spu.getBrandId());

          goods.setCid1(spu.getCid1());

          goods.setCid2(spu.getCid2());

          goods.setCid3(spu.getCid3());

          goods.setCreateTime(spu.getCreateTime());

          goods.setId(spu.getId());

          goods.setAll(all);//搜索字段,包含标题,分类,品牌,规格等信息

          goods.setPrice(priceList);// 所有sku价格的集合

          goods.setSkus(JsonUtils.toString(skus));// 所有sku的集合的JSON格式

          goods.setSpecs(specs);// 所有可以搜索的规格参数

          goods.setSubTitle(spu.getSubTitle());

          return goods;

          }

          private String chooseSegment(String value, SpecParam p) {

          double val = NumberUtils.toDouble(value);

          String result = “其它”;

          // 保存数值段

          for (String segment : p.getSegments().split(“,”)) {

          String[] segs = segment.split(“-”);

          // 获取数值范围

          double begin = NumberUtils.toDouble(segs[0]);

          double end = Double.MAX_VALUE;

          if(segs.length == 2){

          end = NumberUtils.toDouble(segs[1]);

          }

          // 判断是否在范围内

          if(val >= begin && val < end){

          if(segs.length == 1){

          result = segs[0] + p.getUnit() + “以上”;

          }else if(begin == 0){

          result = segs[1] + p.getUnit() + “以下”;

          }else{

          result = segment + p.getUnit();

          }

          break;

          }

          }

          return result;

          }

          public PageResult search(SearchRequest request) {

          Integer page = request.getPage() - 1;

          Integer size = request.getSize();

          //创建查询构建器SpringDataElasticSearch - NativeSearchQueryBuilder过滤聚合高亮查询

          NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

          //0 结果过滤

          queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{“id”,“subTitle”,“skus”},null));

          //1 分页

          queryBuilder.withPageable(PageRequest.of(page,size));

          //2 过滤

          queryBuilder.withQuery(QueryBuilders.matchQuery(“all”,request.getKey()));

          //3 聚合分类和品牌

          //3.1集合分类

          String categoryAggName = “category_agg”;

          queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field(“cid3”));

          //3.2对品牌进行聚合

          String brandAggName = “brand_agg”;

          queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field(“brandId”));

          //4 查询

          AggregatedPage result = template.queryForPage(queryBuilder.build(), Goods.class);

          //5 解析结果

          //5.1解析分页结果

          long total = result.getTotalElements();

          int totalPage = result.getTotalPages();

          List goodsList = result.getContent();

          //5.2解析聚合结果

          Aggregations aggs = result.getAggregations();

          List categories = parseCategoryAgg(aggs.get(categoryAggName));

          List brands = parseBrandAgg(aggs.get(brandAggName));

          return new SearchResult(total,totalPage,goodsList,categories,brands);

          }

          private List parseBrandAgg(LongTerms terms) {

          try {

          List ids = terms.getBuckets()

          .stream().map(bucket -> bucket.getKeyAsNumber().longValue())

          .collect(Collectors.toList());

          List list = brandClient.queryBrandByIds(ids);

          return list;

          }catch (Exception e){

          return null;

          }

          }

          private List parseCategoryAgg(LongTerms terms) {

          try {

          List ids = terms.getBuckets()

          .stream().map(bucket -> bucket.getKeyAsNumber().longValue())

          .collect(Collectors.toList());

          List categories = categoryClient.queryCategoryByIds(ids);

          return categories;

          }catch (Exception e){

          return null;

          }

          }

          }

          7)运行测试

          在这里插入图片描述

          在这里插入图片描述

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

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

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

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

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

          如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
          img

          最后

          一个好的心态和一个坚持的心很重要,很多冲着高薪的人想学习前端,但是能学到最后的没有几个,遇到困难就放弃了,这种人到处都是,就是因为有的东西难,所以他的回报才很大,我们评判一个前端开发者是什么水平,就是他解决问题的能力有多强。

          CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

          分享一些前端面试题以及学习路线给大家

          urn null;

          }

          }

          private List parseCategoryAgg(LongTerms terms) {

          try {

          List ids = terms.getBuckets()

          .stream().map(bucket -> bucket.getKeyAsNumber().longValue())

          .collect(Collectors.toList());

          List categories = categoryClient.queryCategoryByIds(ids);

          return categories;

          }catch (Exception e){

          return null;

          }

          }

          }

          7)运行测试

          在这里插入图片描述

          在这里插入图片描述

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

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

          因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
          [外链图片转存中…(img-Smibm3tD-1712090924672)]
          [外链图片转存中…(img-lVqm54jw-1712090924672)]
          [外链图片转存中…(img-8LY0vpBG-1712090924673)]
          [外链图片转存中…(img-BkDUfB5A-1712090924673)]
          [外链图片转存中…(img-YrMwcUrZ-1712090924673)]
          [外链图片转存中…(img-79kDZlvM-1712090924674)]

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

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

          如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
          [外链图片转存中…(img-7YnCIZay-1712090924674)]

          最后

          一个好的心态和一个坚持的心很重要,很多冲着高薪的人想学习前端,但是能学到最后的没有几个,遇到困难就放弃了,这种人到处都是,就是因为有的东西难,所以他的回报才很大,我们评判一个前端开发者是什么水平,就是他解决问题的能力有多强。

          CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

          分享一些前端面试题以及学习路线给大家

          [外链图片转存中…(img-egac50BD-1712090924674)]

        • 15
          点赞
        • 23
          收藏
          觉得还不错? 一键收藏
        • 0
          评论
        评论
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

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

        抵扣说明:

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

        余额充值