大数据最新前端面试题_fnjsoo(1),2024年最新轻松获得一线大厂面试offer

img
img
img

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

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

需要这份系统化资料的朋友,可以戳这里获取

原理

  1. Vue双向绑定原理是通过数据劫持结合发布订阅者模式来实现的
  2. 数据和视图同步,视图变化,数据也就变化;数据变化,视图也就变化
	const obj={}
    Object.defineProperty(obj,'hello',{
        get:function(){
            console.log("调用get方法")
        },
        set:function(newVal){
            console.log("调用set方法"+newVal)
        }
    })
    obj.hello; // 调用get方法
    obj.hello='hi'; // 调用set方法hi

详情


watch与computed,methods三者区别

作用机制上

  1. 侦听属性watch与计算属性computed都是以vue的依赖追踪机制(订阅者模式)为基础的,只有当一个函数变化时,才会调用相关的函数
  2. methods里面是用来定义函数的,需要手动调用才会执行

作用性质上

  1. methods中定义的是函数,你需要进行函数调用(func())
  2. computed是计算属性,他在使用了和data是对象中的数据类型是同一类
  3. watch类似于监听机制+事件机制
watch: {
   firstName: function (val) {  this.fullName = val + this.lastName }
}
/\*
\* 只有当firstName触发后才执行,而firstName对应的函数就相当于监听到事件发生后执行的方法
\*/

watch与computed异同

相同
  1. 他们都是在依赖数据发生改变的情况下,被依赖的数据根据预先定义好的函数,发生自动的变化
不同
  1. watch擅长处理
    1. 一个数据影响多个数据
    2. 当需要在数据变化时执行异步或开销较大的操作时
    3. watch可以监听新值与旧值
    4. watch属性还可以用来监听路由router的变化,只是这里的监听元素是固定的
  2. computed擅长处理(计算属性不能异步,异步函数return是没有任何意义的)
    1. 多个数据影响一个数据
    2. 当存在复杂逻辑、需要用到缓存时

methods

  1. methods与watch和computed是一种不同的概念,methods指的是方法也就是定义要用的函数,他不会主动触发需要在被调用时才会触发
  2. methods与computed看不出来差别,但要注意computed是存在缓存的

参考


Vue组件为啥需要name属性

  1. 当项目使用keep-alive缓存时,可用组件name属性进行缓存过滤
  2. 当组件有name时,就可以组件自己调用自己(递归调用)
  3. 使用vue-tool时,工具中组件名就是name属性决定的

Vue传值方式

props与$emit(在封装的组件中很常用)

  1. 在父组件中通过属性传值与子组件,子组件在props接收
  2. 子组件通过this.$emit(‘事件名’,值),父组件通过@事件名="事件处理函数"接受父组件传递的值

Vue.prototype.$bus

  1. 在mian.js中定义
  new Vue({
  	......
  	beforeCreate() {
  		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
  	}
  })

  1. 要传值的组件定义this. b u s . bus. bus.emit(‘事件名’,值)
  2. 接收值的组件定义this. b u s . bus. bus.on(‘事件名’,回调函数),也可以通过this. b u s . bus. bus.off(‘事件名’)解绑事件

VueX

  1. state 用来存储数据
  2. mutation 用来对state进行成员操作
  3. getters 加工state成员给外界
  4. actions 也是用来对state进行成员操作不过是异步操作而且需要提交给mutation,在action中不能直接更改
  5. modules 模块化状态管理

ref传值

  • 在父组件中在子组件上绑定ref,通过this.$refs.ref名字.子组件接受方法(数据)
 <Test ref="test"/>
 ...
 this.$refs.test.childMethod('val12343');

  • 子组件定义方法接受子组件接受方法(val){ // val就是传过来的值}
methods: {
    childMethod(val) {
      console.log(val); // val就是传过来的值 val12343
    },
  },

LoaclStorage(存储于本地)

  1. 先将需要的值存储于本地localStorage.setItem(“存储名”,存储值)
  2. 获取值通过localStorage.getItem(“存储名”)
  3. 不需要时也可以删除localStorage.removeItem(“存储名”)

Vue-router路由实现原理

Hash路由模式

  1. hash虽然标记在url中,但他不会发送网络请求,对服务端完全无用
  2. 通过window.location.hash实现的

History路由模式

  1. 通过window.history实现的
  2. 分别通过调用history.pushState()和history.replaceState()方法来实现push和replace方法
  3. pushState和replaceState方法修改浏览器历史记录栈后,虽然当前URL改变了,但浏览器不会立即发送请求该URL,这为更新视图但不重新发送请求提供了基础
  4. Hash与History两种模式的go、back和forward三个方法都是通过window.history.go()来实现的

Abstract模式

vue-router为非浏览器环境准备了一个abstract模式,其原理为用一个数组stack模拟出浏览器历史记录栈的功能


Vue-router传参

router-link传参

params
<router-link :to="{name:'home',params:{id:1}}" />
<router-link :to="{path:'home',params:{id:1}}" />
// 取参 this.$route.params.id 

query
<router-link :to="{name:'home',query:{id:1}}" /> 
<router-link :to="{path:'home',query:{id:1}}" /> 
// 取参 this.$route.query.id

this.$router.push()传参

params
this.$router.push({name:'home',params:{id:1,age:2}})

query
this.$router.push({path:'/home',query:{id:1,age:2}}) 
this.$router.push(`/home?id=1`);

参考


Vue-router有那些守卫

全局前置守卫beforeEach

  • 在router中进行配置
  • router.beforeEach((to,from,next)=>{})
    • to要到哪个路由去
    • from从哪个路由来
    • next下一步(无论失败与否都要调用,否则会阻止程序继续执行)

全局后置守卫 afterEach

  • router.afterEach((to,from)=>{})

组件内守卫(相当于给组件增加生命周期)

beforeRouteEnter 进入组件之前

beforeRouterEnter(to,from,next){}

beforeRouteUpdate 组件被复用时调用

beforeRouterUpdate(to,from,next){}

beforeRouteLeave 离开组件时调用

beforeRouteLeave(to,from,next){}


移动端防止事件穿透

事件穿透原理

  1. 假如页面上有两个元素A和B,B元素在A元素之上,B为遮罩层mask。我们在B元素上绑定 touchstart事件,作用是隐藏B元素;A元素上绑定click事件。我们发现,当我们点击B元素,B元素被隐藏了,随后,也触发了A元素的click事件
  2. 事件执行的顺序是 touchstart >touchmove>touched ==>click,click事件有300ms的延迟,当 touchstart事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转了

解决方案

禁用缩放

click事件的延迟300ms是为了移动端缩放

 <!-- 1.禁用缩放 user-scalable=no -->
 <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">

延迟上层元素消失

touchstart后延迟350ms后再隐藏,先把透明度设为0,解决视觉层面效果,在设置定时器延迟,让元素消失

$('.mask').on('touchstart',function() {
    console.log('mask-touchstart');
    $(this).css('opacity', 0); // 设置视觉层面消失
    setTimeout(function() {
        $('.mask').css('dispaly', 'none'); // DOM节点消失
    },350);
})

pointer-events,让被覆盖元素短时间内无法触发click
  • pointer-events两个属性
    • auto默认值,鼠标默认值不会穿透当前层
    • none使元素无法触发事件
$('.mask').on('touchstart',function() {
    console.log('mask-touchstart');
    $(this).css('display', 'none');
    $('.box').css('pointer-events', 'none');   // 让被覆盖元素无法响应click
    setTimeout(function() {
        $('.box').css('pointer-events', 'auto');   // 恢复被覆盖元素
    },300);
})

使用fastclick库

使用fastclick库后,所有点击事件都使用click,没有350ms延时,也没有样式穿透问题

<script type='application/javascript' src='/path/to/fastclick.js'></script>
...
if ('addEventListener' in document) {
	document.addEventListener('DOMContentLoaded', function() {
		FastClick.attach(document.body);
	}, false);
}

针对安卓的,android:clickable=“true”

在上层的布局中增加android:clickable="true"的属性,这样下层的事件就不会被触发了

详情


移动端适配

viewport适配

//获取meta节点
var metaNode = document.querySelector('meta[name=viewport]');
 
//定义设计稿宽度为375
var designWidth = 375;
 
//计算当前屏幕的宽度与设计稿比例
var scale = document.documentElement.clientWidth/designWidth;
 
//通过设置meta元素中content的initial-scale值达到移动端适配
meta.content="initial-scale="+scale+",minimum-scale="+scale+",maximum-scale="+scale+",user-scalable=no";

借助media实现rem适配

//对屏幕大小划分了html不同的font-size
@media screen and (min-width: 320px) {html{font-size:50px;}}
@media screen and (min-width: 360px) {html{font-size:56.25px;}}
@media screen and (min-width: 375px) {html{font-size:58.59375px;}}
@media screen and (min-width: 400px) {html{font-size:62.5px;}}
@media screen and (min-width: 414px) {html{font-size:64.6875px;}}
@media screen and (min-width: 440px) {html{font-size:68.75px;}}
@media screen and (min-width: 480px) {html{font-size:75px;}}
@media screen and (min-width: 520px) {html{font-size:81.25px;}}
@media screen and (min-width: 560px) {html{font-size:87.5px;}}
@media screen and (min-width: 600px) {html{font-size:93.75px;}}
@media screen and (min-width: 640px) {html{font-size:100px;}}
@media screen and (min-width: 680px) {html{font-size:106.25px;}}
@media screen and (min-width: 720px) {html{font-size:112.5px;}}
@media screen and (min-width: 760px) {html{font-size:118.75px;}}
@media screen and (min-width: 800px) {html{font-size:125px;}}
@media screen and (min-width: 960px) {html{font-size:150px;}}

JS配合修改配合rem适配

var designWidth = 375;  		// 设计稿宽度
var remPx = 100;               // 在屏幕宽度375px,的时候,设置根元素字体大小 100px
var scale = window.innerWidth / designWidth; //计算当前屏幕的宽度与设计稿比例
// 根据屏幕宽度 动态计算根元素的 字体大小
document.documentElement.style.fontSize = scale\*remPx + 'px';

JS动态修改配合CSS预处理器(less)

	// 计算屏幕宽度
var screen = document.documentElement.clientWidth;
	// 计算字体大小,取屏幕宽度的16分之一
var size = screen / 16;
	// 设置根元素字体大小
document.documentElement.style.fontSize = size + 'px';

js获取当前屏幕的宽度,将屏幕宽度的16分之一设置给html的font-size

// 此时设计稿的宽度为375,定义一个less变量等于16分之一的设计稿宽度
@rem: 375/16rem;

JS动态修改配合rem适配

	// 计算屏幕宽度
var screen = document.documentElement.clientWidth;
	// 设置根元素字体大小
document.documentElement.style.fontSize = screen + 'px';

详情

借助插件适配

  • 让不同设备的视口取得最佳CSS像素
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

  • 安装 postcss,postcss-pxtorem,postcss-loader,postcss-import,postcss-url一系列插件
npm install postcss@8.2.6 --save

npm install postcss-import@14.0.0 --save

npm install postcss-loader@5.0.0 --save

npm install postcss-pxtorem@5.1.1 --save

npm install postcss-url@10.1.1 --save

  • 在项目根目录下添加 .postcssrc.js 文件
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 32, //根目录的字体大小设为32px
      propList: ['\*'], //proplist:是那些属性需要转换成rem,全部
      minPixelValue: 2 //最小转换单位.2px
    }
  }
};

  • 在项目中index.html的头部添加下面这段js代码:
<script>(function () {
    function autoRootFontSize() {
      document.documentElement.style.fontSize = Math.min(screen.width, document.documentElement
        .getBoundingClientRect().width) / 470 \* 32 + 'px';
      // 取screen.width和document.documentElement.getBoundingClientRect().width的最小值;
      // 除以470,乘以32;意思就是相对于470大小的32px;
    }
    window.addEventListener('resize', autoRootFontSize);
    autoRootFontSize();
  })();</script>

注:设计师制作的效果图常为750px,为了方便直接读取UI标好的像素大小,根目录的字体大小就设置为32px;
详情


适配低版本浏览器

Babel

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API.(不转换新的API.的意思是:对极少部分浏览器无法支持的api,转换之后的也无法正常的使用,比如:import,export)

使用

单文件转换
  1. 全局安装babel的命令行工具
cnpm install babel-cli -g

  1. 执行命令,安装babel-preset-env到本地
cnpm install babel-preset-env --save-dev

  1. 在项目根目录下创建 .babelrc 文件,文件中的内容如下
{
 "presets": ["env"]
}

  1. 执行命令将es6转换为es5
文件到文件:babel js文件路径 -o 转译后生成的文件路径

在package.json文件的脚本中配置转义命令
  1. 安装babel-cli命令行工具
cnpm install babel-cli babel-preset-env --save-dev 

  1. 创建 .babelrc 文件,写入以下代码
{
 "presets": ["env"]
}

  1. 在你的 package.json 中添加一个 “scripts” 属性并将 babel 命令放在它的 build 属性中
"scripts": {
     "build": "babel src -d lib"
   },

  1. 终端运行命令开始转译
npm run build

详情


前端项目优化

普通项目优化

页面加载阶段
  1. dns预解析
  2. 使用cdn
  3. 静态资源压缩与合并
  4. 减少http请求
  5. 异步加载defer(页面解析完成后执行代码),async(当前JS解析完成后执行代码)
  6. 服务端渲染ssr
  7. 多使用内存和缓存
页面渲染阶段
  1. css放前面,js放后面
  2. 减少DOM查询,多次使用的保存为变量
  3. 减少DOM操作,统一通过DOM片段操作
  4. 事件函数的防抖和节流
  5. 图片懒加载

Vue项目优化

代码层面优化
  1. Object.freeze(data)(方法可以冻结对象的属性)对于一些查询类的页面,调取接口回来后的数据可 不进行数据劫持
  2. v-if和v-for不要在一起使用。v-if的条件通过函数来处理
  3. v-for中加上key,对于虚拟dom树查找提高性能
  4. computed和watch注意区分使用场景。前者是有缓存的。后者是监听到数据变化后的回调无缓存
  5. created中发起请求,mounted钩子中代表页面dom加载完成可以进行dom操作
  6. 长列表性能优化,只渲染可视区域的列表
  7. 长表格性能优化,通过canvers来绘制表格
  8. 合理使用$nextTick去操作dom,适用于更新了数据马上就要操作dom的场景
  9. 操作dom不要使用js原生的方式来操作。用vue提供的三种方式来操作 比如,ref、自定义指令el、事件中的话用e.target获取dom
  10. 尽量不要在前端进行大量的数据处理
  11. 合理使用keep-alive来缓存页面数据,跳过created,mounted钩子,他有自己特定的钩子activted等
  12. 路由懒加载通过import配合箭头函数,还有其他的方式require
  13. 组件懒加载,异步加载
  14. 尽量少用float,可以用flex布局
  15. 频繁切换的使用v-show,不频繁的使用v-if
  16. 不要在模板中写过多的样式
  17. 服务端渲染ssr,优化seo,与首屏白屏问题
  18. 通过addEventListenr添加的事件,需要自行销毁
  19. 把组件中的css提取成单独的文件
  20. 少使用闭包与递归,递归可做尾递归的优化
  21. 使用字体图标或者svg来代替传统的Png等格式的图片
  22. 在Js中避免“嵌套循环”和“死循环”
webpack优化
  1. 去除无用代码treeShaking
  2. babel编译es6到es5的时候,会有多余代码产生
  3. 减小app.js的体积,提取公共代码
  4. 减少vendor.js的体积,通过按需引入第三方库,或者有些资源可以通过script标签引入
  5. 代码切割,有一些组件没必要都打包到一起
  6. 使用chunck
  7. 使用SouceMap,来还原线上代码,更方便的去定位线上问题
  8. 构建结果,通过可视化插件,进行分析
  9. webpack对图片进行压缩等处理
  10. 图片可以使用webp,优雅降级处理
  11. 编译优化
  12. 模板预编译,使用vue-template-loader,把模板编译成渲染函数
  13. thread-loader多线程打包
// webpack.base.js
 
{
        test: /\.js$/,
        use: [
          'thread-loader',
          'babel-loader'
        ],
      }

WEB层面
  1. 浏览器缓存的使用
  2. 开启gzip压缩
  3. CDN的使用,减少路由转发的次数,就近访问资源
  4. 使用chrome的性能分析工具,查找性能瓶颈
  5. dns预解析
  6. 静态资源的压缩与合并
  7. 减少https请求
  8. 异步加载defer,async
  9. 静态资源和服务不要放在同一台机器上。多个域名去并行加载解析

安全层面

XSS跨站请求攻击

XSS 前端替换关键字,建议后端也替换,如<>的替换,避免脚本的执行

XSRF跨站请求头伪造

XSRF增加验证流程,比如输入密码,指纹,短信验证码,人脸识别等

详情


模块化

概念

  1. 有组织的将一个大文件拆分成多个独立并相互依赖的小文件
  2. 模块内部有许多私有属性,只向外暴露一部分公开的接口

模块化方法

  1. AMD
  2. CMD
  3. CommonJS(Node JS中采用该规范)通过module.exports暴露模块,通过require引入模块
    1. 代码运行在模块作用域,不会污染全局
    2. 加载模块顺序是按照词法解析顺序来加载
    3. 加载模块是同步的
    4. 单例加载:模块会被缓存起来,再次运行直接加载,除非手动清除
    5. 加载模块得到的结果是深拷贝
  4. ES6模块export暴露模块,import … from … 引入模块
    1. 尽量静态化,编译时就确定模块的依赖关系
    2. 加载模块的值是引用,所以在全局只有一份
    3. 加载模块也是异步的

小程序生命周期

应用的生命周期,在app.js中进行调用

生命周期说明
onLanch小程序初始化完成时触发,全局只触发一次
onShow小程序启动,后者后台进入前台触发
onHide小程序从前台进入后台触发
onError小程序发生脚本错误或者API调用时报错时触发
onPageNotFound小程序打开时页面不存在时触发
onUnhandledRejection有未处理的promise时触发
onThemeChange系统切换主题时触发

页面生命周期,当你进入\切换到一个新页面时触发

生命周期说明作用
onLoad监听页面加载发送请求获取数据
onShow监听页面显示发送请求获取数据
onReady监听页面初次渲染完成获取页面元素
onHide监听页面隐藏终止任务,如定时器或播放音乐
onUnLoad页面卸载终止任务

组件生命周期

生命周期说明
created监听页面加载
attached监听页面显示
ready页面初次渲染完成
moved监听页面隐藏
datached监听页面卸载
error组件方法抛出错误时执行

参考


微信小程序如何分包

为何要分包

  1. 在第一本版本的小程序中,小程序代码不能超过1M,所以小程序可以秒开;而后面随着业务发展小程序分包大小不能超过8M,8M不能秒开,于是就分包加载
  2. 小程序所有分包不能超过8M,单个分包,主包不能超过2M
  3. 小程序在启动时,默认下载主包,并启动主包内页面;当用户进入分包页面时,客户端下载分包并展示

app.json中配置

{
  "pages":[
    "pages/index",
    "pages/logs"
  ],
  "subpackages": [
    {
      "root": "packageA", // 分包根目录
      "pages": [
        "pages/cat",
        "pages/dog"
      ]
    }, {
      "root": "packageB",
      "name": "pack2", // 分包别名,分包预下载时可以使用
      "pages": [ // 分包页面路径,相对于分包根目录
        "pages/apple",
        "pages/banana"
      ],
      independent:false // 是否独立分包
    }
  ]
}

分包注意事项

  1. 主包无法加载分包的资源
  2. 分包可以加载主包的资源
  3. 分包与分包之间无法相互加载资源

微信小程序获取用户信息

获取用户的头像,昵称

wx.getUserProfile

获取用户手机号

  1. 小程序端调用 wx.login() 方法,获取 code 后,将 code 通过后台 api 接口传递到后台
  2. 后台 api 接口收到 code 后,调用微信接口 jscode2session , 换取 openid 、 session_key 、 unionid
    1. openid同一用户不同应用下不相同
    2. unionid同一用户不同应用下相同
    3. session_key为应用安全,session_key应该存储在服务端
  3. 小程序端提交 code 调用后台 api 接口后 , 获取 api 接口返回的 userToken ,通过页面 button 点击调用后台接口 , 传递参数 userToken 、 iv 、 encryptedData 到后台进行数据解密,得到 phoneNumber
<button
	class="btn" 
	open-type="getPhoneNumber"
	bindgetphonenumber="getPhoneNumber"
>   
   {{phoneNumber==null?'获取手机号':phoneNumber}}
</button>
...
getPhoneNumber: function (e) {
    var that = this;
    if (e.detail.errMsg == "getPhoneNumber:ok") {
      wx.request({
        url: app.serverUrl+'/api/login/decodePhone',
        data: {
          encryptedData: e.detail.encryptedData,
          iv: e.detail.iv,
          userToken: that.data.userToken,
        },
        header: {
          'content-type': 'application/json' 
        },
        success: function (res) {
          that.setData({
            phoneNumber:res.data.phoneNumber
          })
        }
      })
    }
  },

后端给的二进制流如何渲染到页面

// 调用方法准换为图片路径
window.URL.createObjectURL(res)


后端提供10W条数据,如何渲染到页面

setTimeout进行分页渲染

<template>
 <div class='testDate'>
    <div v-for="item in newList" :key="item.id">
        <span>{{item.text}}</span>
    </div>
 </div>
</template>

<script>
export default {
  name: 'testDate',
  components: {},
  props: {},
  data() {
    return {
      list: [], // 用来模拟后端给的10W条数据
      newList: [], // 用于渲染的数据
      total: 100000, // 一共多少条数据
      page: 0, // 第几页,默认0为第一页
      limit: 200, // 一页200条数据
    };
  },
  computed: {},
  watch: {},
  created() {
    for (let i = 0; i < 100000; i += 1) {
      this.list.push({
        id: i,
        text: `测试数据${i}`,
      });
    }
  },
  mounted() {
    this.render();
  },
  methods: {
    render() {
      if (this.page >= Math.ceil(this.total / this.limit)) return; // 如果第几页的页数已经大于总共的页数,说明数据已经完了,直接return返回
      setTimeout(() => {
        for (let i = this.page \* this.limit; i < this.page \* this.limit + this.limit; i += 1) {
          this.newList.push(this.list[i]); // 添加到newList用于渲染的数据列表中
        }
        this.page += 1;
        this.render(); // 递归调用
      }, 0);
    },
  },
};
</script>

requestAnimationFrame,这样可以减少reflow次数(浏览器重新计算集合属性),提高性能

<template>
 <div class='testDate'>
    <div v-for="item in newList" :key="item.id">
        <span>{{item.text}}</span>
    </div>
 </div>
</template>

<script>
export default {
  name: 'testDate',
  components: {},
  props: {},
  data() {
    return {
      list: [], // 用来模拟后端给的10W条数据
      newList: [], // 用于渲染的数据
      total: 100000, // 一共多少条数据
      page: 0, // 第几页,默认0为第一页
      limit: 200, // 一页200条数据
    };
  },
  computed: {},
  watch: {},
  created() {
    for (let i = 0; i < 100000; i += 1) {
      this.list.push({
        id: i,
        text: `测试数据${i}`,
      });
    }
  },
  mounted() {
    this.render();
  },
  methods: {
    render() {
      if (this.page >= Math.ceil(this.total / this.limit)) return;
      requestAnimationFrame(() => {


![img](https://img-blog.csdnimg.cn/img_convert/1c1f97c32af7e01df031dbee93061b0c.png)
![img](https://img-blog.csdnimg.cn/img_convert/376162d6c285af403c2d2e1bab02e1c9.png)
![img](https://img-blog.csdnimg.cn/img_convert/551bc1ab0674b75ad5a011c33f7e8295.png)

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

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

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

性),提高性能



{{item.text}}
  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值