优化手段有哪些基础配置优化的配置顺序优化?
说到https和http的区别,可以说一下https服务器和客户端连接的差异,以及https特定的加密算法协商,甚至可能还要说到对称加密,非对称加密和证书等,篇幅很长,请看我之前单独写的一篇https详解,里面讲的非常详细。
请求优化
讲请求优化的之前先来总结下上面说到的js, css文件顺序优化,为了让渲染更快,我们需要把js放到尾部,css放到头部,然后还要注意在书写js的时候尽量减少重排,重绘。书写html,css的时候尽量简洁,不要冗余,目的是为了更快的构建DOM树和CSSOM树。好了下面我们在来说说请求优化,请求优化可以从请求数量和请求时间两方面入手
减少请求数量减少请求时间webpack优化
介绍了渲染优化,现在来看看webpack优化,自己平常写demo给团队做培训的时候都是自己手写webpack配置,虽然也就几十行,但每次都能让我巩固webpack的基本配置,下面直接说一下webpack优化手段有哪些
基础配置优化
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">resolve: {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> extensions: ['.ts', '.tsx', '.js']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
这个配置表示webpack会根据extensions去寻找文件后缀名,所以如果我们的项目主要用ts写的话,那我们就可以.tsx和.ts写前面,目的是为了让webpack能够快速解析
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">resolve: {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> alias: {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> Components: path.resolve(__dirname, './src/components')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
noParse表示不需要解析的文件,有的文件可能是来自第三方的文件,被 providePlugin引入作为windows上的变量来使用,这样的文件相对比较大,并且已经是被打包过的,所以把这种文件排除在外是很有必要的,配置如下
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">module: {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> noParse: [/proj4\.js/]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
某些loader会有这样一个属性,目的是指定loader作用的范围,exclude表示排除某些文件不需要babel-loader处理,loader的作用范围小了,打包速度自然就快了,用babel-loader举一个简单例子
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">{<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> test: /\.js$/,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> loader: "babel-loader",<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> exclude: path.resolve(__dirname, 'node_modules')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
这个配置是一个调试项,不同的配置展示效果不一样,打包大小和打包速度也不一样,比如开发环境下cheap-source-map肯定比source-map快,至于为什么,强烈推荐自己之前写的这一篇讲解devtool的文章:webpack devtools篇讲的非常详细。
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">{<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> devtool: 'cheap-source-map'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
.eslintignore
这个虽不是webpack配置但是对打包速度优化还是很有用的,在我的实践中eslint检查对打包的速度影响很大,但是很多情况我们不能没有这个eslint检查,eslint检查如果仅仅在vs里面开启的话,可能不怎么保险。
因为有可能你vs中的eslint插件突然关闭了或者某些原因vs不能检查了,只能靠webpack构建去帮你拦住错误代码的提交,即使这样还不能确保万无一失,因为你可能某一次提交代码很急没有启动服务,直接盲改提交上去了。这个时候只能通过最后一道屏障给你保护,就是在CI的时候。比如我们也会是在jenkins构建的时候帮你进行eslint检查css 加载层,三道屏障确保了我们最终出的镜像是不会有问题的。
所以eslint是很重要的,不能删掉,在不能删掉的情况下怎么让检查的时间更少了,我们就可以通过忽略文件,让不必要的文件禁止eslint,只对需要的文件eslint可以很大程度提高打包速度
loader,plugins优化
上述说了几个基础配置优化,应该还有其他的基础配置,今后遇到了再继续添加,现在在来讲讲利用某些loader,plugins来提高打包速度的例子
这个loader就是在第一次打包的时候会缓存打包的结果,在第二次打包的时候就会直接读取缓存的内容,从而提高打包效率。但是也需要合理利用,我们要记住一点你加的每一个loader,plugins都会带来额外的打包时间。这个额外时间比他带来的减少时间多,那么一味的增加这个loader就没意义,所以cache-loader最好用在耗时比较大的loader上,配置如下
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">{<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> rules: [<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> test: /\.vue$/,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> use: [<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 'cache-loader',<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 'vue-loader'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> ],<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> include: path.resolve(__dirname, './src')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> ]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
在上面的渲染优化中我们已经知道,文件越小渲染的速度是越快的。所以我们在配置webpack时候经常会用到压缩,但是压缩也是需要消耗时间的,所以我们我们经常会用到上面三个插件之一来开启并行压缩,减少压缩时间,我们用webpack4推荐使用的terse-webpack-plugin做例子来说明
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">optimization: {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> minimizer: [<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> new TerserPlugin({<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> parallel: true,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> cache: true<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> })<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> ],<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
这几个loader/plugin和上面一样也是开启并行的,只不过是开启并行构建。由于happypack的作者说自己的兴趣已经不再js上了,所以已经没有维护了,并推荐如果使用的是webpack4的话,就去使用thread-loader。基本配置如下
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> test: /\.js$/,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> use: [<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> loader: "thread-loader",<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> options: threadLoaderOptions<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> },<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> "babel-loader",<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> ],<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> exclude: /node_modules/,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
上面说的几个并行插件理论上是可以增加构建速度,网上很多文章都是这么说的,但是我在实际的过程中使用,发现有时候不仅没提升反而还降低了打包速度,网速查阅给的理由是可能你的电脑核数本来就低,或者当时你CPU运行已经很高了,再去开启多进程导致构建速度降低。
上面说的几个并行插件可能在某些情况下达不到你想要的效果css 加载层,然而在我们团队优化webpack性能经验来看,这次所说的两个插件是很明显并且每次都能提高打包速度的。原理就是先把第三方依赖先打包一次生成一个js文件,然后真正打包项目代码时候,会根据映射文件直接从打包出来的js文件获取所需要的对象,而不用再去打包第三方文件。只不过这种情况打包配置稍微麻烦点,需要写一个webpack.dll.js。大致如下
webpack.dll.js
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">const path = require('path');<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />const webpack = require('webpack');<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />module.exports = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> entry: {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> library: ["vue", "moment"]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> },<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> output: {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> filename: '[name].dll.js',<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> path: path.resolve(__dirname, 'json-dll'),<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> library: '[name]'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> },<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> plugins: [<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> new webpack.DllPlugin({<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> path: './json-dll/library.json',<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> name: '[name].json'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> })<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> ]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
webpack.dev.js
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;"> new AddAssetHtmlWebpack({<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> filepath: path.resolve(__dirname, './json-dll/library.dll.js')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> }),<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> new webpack.DllReferencePlugin({<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> manifest: require("./json-dll/library.json")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> })<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
其他优化配置
这些插件就简单的介绍下,在我的个人项目中已经使用过,自我感觉还可以,具体使用可以查阅npm或者github
这个插件可以用可视化帮我们分析打包体积,从而来采用合适的优化方式去改进我们的webpack配置
这个插件可以告诉我们打包时候每一个loader或者plugin花费了多少时间,从而对耗时比较长的plugin和loader做优化
这个插件可以帮我们优化打包日志,我们打包时候经常看到很长一个日志信息,有的时候是不需要的,也不会去看所以可以用这个插件来简化
这是最后一部分代码优化了,这里的代码性能优化我只说我在工作中感受到的,至于其他的比较小的优化点比如createDocumentFragment使用可以查查其他文章
能不操作dom不要操作dom,哪怕有时候需要改设计
很多情况下我们都能用css还原设计稿,但是有些时候单单从css没法还原,尤其组件还不是你写的时候,比如我们团队用的就是antd,有时候的产品设计单从css上没法实现,只能动用js,删除,增加节点在配合样式才能完成。
由于我们又是一个做大数据的公司,这个时候就会出现性能问题,最开始写代码时候,产品说什么就是什么,说什么我都会想办法搞出来,不管用什么方法。后来到客户现场大数据请况下,性能缺点立马暴露的出来。
所以代码优化的原则之一我认为是能不写的代码就不写,当然这是要从性能角度出发,通过性能分析给产品说出理由,并且最好还能提供更好的解决方案,这个才是我们需要考虑的。
如果用的是react 一定用写shouldComponentUpdate这个生命周期函数,不然打印的时候你会发现,你自己都迷糊为什么执行了这么多遍
将复杂的比对,变成简单比对
这句话是什么意思了?我们就拿shouldComponentUpdate举例子,用这个函数没问题,但是可以做的更好,我们在工作中经常这么写
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">shouldComponentUpdate(nextPrpops) {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> return JSON.stringify(nextPrpops.data) !== JSON.stringify(this.props.data)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
如果这是一个分页表格,data是每一页数据,数据改变了重新渲染,在小数据场景下这本身是没有问题。但是如果在大数据的场景下可能会有问题,可能有人有疑问,既然做了分页怎么还会有大数据了,因为我们的产品是做大数据分析日志的,一页十条日志,有的日志可能非常的长,也就是说就算是10条数据比对起来也是很耗时,所以当时想法能不能找到其他的替代变量来表示数据变了?比如下面这样
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">shouldComponentUpdate(nextPrpops) {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> return nextPrpops.data[0].id !== this.props.data[0].id<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
第一条的id不一样就表示数据变化了行不行,显然在某种情况下是存在的,也有人会说可能会出现id一样,那如果换成下面这种了?
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">shouldComponentUpdate(nextPrpops) {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> return nextPrpops.current !== this.props.current<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
将data的比对转换成了current的比对,因为页数变了,数据基本都是变了,对于我们自己日志的展示来说基本不存在两页数据是一模一样的,如果有那可能是后台问题。然后在好好思考这个问题,即使存在了两页数据一摸一样,顶多就是这个表格不重新渲染,但是两页数据一摸一样不重新渲染是不是也没有问题,因为数据是一样的。或者如果你还是不放心,那下面这种会不会好点
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">this.setState({<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> data,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> requestId: guid()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />})<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />shouldComponentUpdate(nextPrpops) {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> return nextPrpops.requestId !== this.props.requestId<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
给一个requestId跟宗data,后面就只比对requestId。上面的写法可能都有问题,但是主要是想说的是我们在写代码时候可以想想是不是可以"将复杂的比对,变成简单比对"
学习数据结构和算法,一定会在你的工作中派上用场
我们经常会听到学习数据结构和算法没有什么大的用处,因为工作基本用不上。这句话我之前觉得没错,现在看来错的很严重。我们所学的每一样技能,都会在将来的人生中派上用场。之前写完代码就丢了不去优化所以我觉得算法没意义,又难又容易忘记。但现在要求自己做完需求,开启mock,打开perfermance进行大数据量的测试,看着那些标红的火焰图和肉眼可见的卡顿,就明白了算法和数据结构的重要性,因为此时你只能从它身上获取优化,平时你很排斥它,到优化的时候你是那么想拥有它。我拿自己之前写的代码举例,由于公司代码是保密的我就把变量换一下,伪代码如下
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">data.filter(({id}) => {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> return selectedIds.includes(id);<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />})<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
就是这样几行代码,逻辑就是筛选出data里面已经被勾选的数据。基本上很多人都可能这么写,因为我看我们团队里面都是这么写的。产品当时已经限制data最多200数据,所以写完完全没压力,性能没影响。但是秉着对性能优化的原则(主要是被现场环境搞怕了~~~),我开启了mock服务,将数据调到了2万条再去测试,代码弊端就暴露出来了,界面进入卡顿,重新选择的时候也会卡顿。然后就开始了优化,当时具体的思路如下
按照现在的代码来看,这是一个两层循环的暴力搜索时间复杂度为O(n^2)。所以想着能不能降一下复杂度至少是O(nlogn),看了一下代码只能从selectedIds.includes(id)这句入手,于是想着可不可以用二分,但是立马被否定因为二分是需要有序的,我这数组都是字符串怎么二分。
安静了一下之后,回想起看过的算法课程和书籍以及做的算法题,改变暴力搜索的方法基本都是
1:上指针
2:数组升维
3:利用hash表
前两者被我否定了因为我觉得还没那么复杂,于是利用hash表思想解决这个问题,因为js里面有一个天然的hash表结构就是对象。我们知道hash表的查询是O(1)的,所以我将代码改写如下
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">const ids = {};<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />selectedIds.forEach(id => ids[id] = 1);<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />data.filter(({id}) => {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> return !!ids[id];<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />})<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
将从selectedIds查询变成从ids查询,这样时间复杂度就从O(n^2)变成了O(n)了,这段代码增加了
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;max-width: 100%;color: rgb(63, 63, 63);font-size: 16px;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;box-sizing: border-box !important;overflow-wrap: break-word !important;">const ids = {};<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />selectedIds.forEach(id => ids[id] = 1);<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
</pre>
其实增加了一个selectedIds遍历也是一个O(n)的复杂度,总来说复杂度是O(2n),但是从时间复杂度长期期望来看还是一个O(n)的时间复杂度,只不过额外增加了一个对象,所以这也是一个典型的空间换时间的例子,但是也不要担心,ids用完之后垃圾回收机制会把他回收的。
最后
其实这篇文章写出来还是对自己帮助很大,让自己系统的梳理了一下自己理解的前端优化,希望对你们也有帮助。
发表评论
热门文章
Spimes主题专为博客、自媒体、资讯类的网站设计....
一款个人简历主题,可以简单搭建一下,具体也比较简单....
仿制主题,Typecho博客主题,昼夜双版设计,可....
用于作品展示、资源下载,行业垂直性网站、个人博客,....
尘集杂货铺和官网1t5-cn
11月11日
[已回复]
希望主题和播放器能支持SQLite数据库,AI能多个讯飞星火