前端项目启动慢?项目打包体积大?跟着陈教练一起,项目的肥油咔咔掉。
项目经过几年的迭代,项目的启动速度随着项目的迭代,肉眼可见的变慢,打包的产物也是一天比一天大。启动项目的时间、打包的时间是越来越久了。如同一个胖子,速度跟不上,体重蹭蹭涨。
接下来就跟着陈教练来两套健身操,减减脂,去去油。
第一套健身操 —— 提速操
所有的优化,都是需要针对问题进行优化,少数的优化是通用优化。
首先要知道,项目启动的慢,是因为什么慢。
开始前,先看一波目前我手上这个巨大项目的启动时间:
将近190s,3分10秒+,陈教练已经跟着刘畊宏教练跳了一首《本草纲目》了!
直接上工具 speed-measure-webpack-plugin, 跟着配置完后,运行项目既可看到如下的分析报告:

npm install --save-dev cache-loader
在 webpack 配置文件内对需要缓存的 loader 进行缓存
const cacheLoader = { // 新增代码
loader: 'cache-loader' // 新增代码
} // 新增代码
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
use: [
cacheLoader, // 新增代码
{
loader: 'vue-loader',
options: vueLoaderConfig
}
]
},
{
test: /\.js$/,
use: [
cacheLoader, // 新增代码
{
loader: 'babel-loader',
}
],
include: [
resolve('src'),
]
},
... ...
... ...
]
}
}
或者也可以像下面,我写的这样,在配置文件内劫持 webpack 的配置,调用 dynamicWebpackConfig 方法,批量缓存
/**
*
* @param {*} rules
* @returns rules
* @description 按需缓存需要缓存的loader
*/
function rules (rules) {
const shouldCache = ['vue-loader', 'eslint-loader', 'babel-loader']
const cacheLoader = {
loader: 'cache-loader'
}
rules = rules.map(rule => {
if (
rule.use &&
rule.use[0] &&
shouldCache.includes(rule.use[0].loader)
) {
rule.use.unshift(
cacheLoader
)
}
return rule
})
return rules
}
/**
*
* @param {*} devWebpackConfig
* @returns devWebpackConfig
* @description 完整的webpack配置
*/
function dynamicWebpackConfig (devWebpackConfig) {
devWebpackConfig.module.rules = rules(devWebpackConfig.module.rules)
return devWebpackConfig
}
module.exports = dynamicWebpackConfig
做完这一步,需要正常启动一次项目,此时会建立 loader 的缓存。缓存内容在可以在 node_modules/.cache 下看到。再次启动项目,看看启动时间:
提速了近80秒!此时不知道你是否还记得,在上面我说的这样一句话:
“所有的优化,都是需要针对问题进行优化,少数的优化是通用优化。”
针对问题的优化,已经举了一个例子说明了,那么有没有通用优化的?
通用优化,他来了
hard-source-webpack-plugin 中间缓存,不管三七二一,只要 webpack 配置不变,通通缓存,这种这种提速方法过于简单暴力,有其他个性化的需求可以看文档进行配置
hard-source-webpack-plugin,
要注意: ip变化,端口变化也是 webpack 配置的变化
使用方法也很简单:
const hardSourceWebpackPlugin = require('hard-source-webpack-plugin')
... ...
... ...
plugins: [
new hardSourceWebpackPlugin({
cachePrune: {
maxAge: 7 * 24 * 60 * 60 * 1000, // 默认2天,现改7天删除
sizeThreshold: 500 * 1024 * 1024 // 默认50,现修改为500,目前项目启动后占用400MB的空间
}
}), //
... ...
]
同样,这些额外的配置,也可以跟我上面劫持修改 webpack 配置一样,写在一起,方便维护:
/**
*
* @param {*} plugins
* @returns plugins
* @description 按需push插件
*/
function plugins (plugins) {
const hardSourceWebpackPlugin = require('hard-source-webpack-plugin')
plugins.push(
new hardSourceWebpackPlugin({
cachePrune: {
maxAge: 7 * 24 * 60 * 60 * 1000, // 默认2天,现改7天删除
sizeThreshold: 500 * 1024 * 1024 // 默认50,现修改为500,目前项目启动后占用400MB的空间
}
}), //
)
return plugins
}
/**
*
* @param {*} rules
* @returns rules
* @description 按需缓存需要缓存的loader
*/
function rules (rules) {
const shouldCache = ['vue-loader', 'eslint-loader', 'babel-loader']
const cacheLoader = {
loader: 'cache-loader'
}
rules = rules.map(rule => {
if (
rule.use &&
rule.use[0] &&
shouldCache.includes(rule.use[0].loader)
) {
rule.use.unshift(
cacheLoader
)
}
return rule
})
return rules
}
/**
*
* @param {*} devWebpackConfig
* @returns devWebpackConfig
* @description 完整的webpack配置
*/
function dynamicWebpackConfig (devWebpackConfig) {
devWebpackConfig.plugins = plugins(devWebpackConfig.plugins)
devWebpackConfig.module.rules = rules(devWebpackConfig.module.rules)
return devWebpackConfig
}
module.exports = dynamicWebpackConfig
由于该优化方案还是缓存,所以还是需要正常启动一次项目后,建立缓存。缓存内容在可以在 node_modules/.cache 下看到。再次启动,查看启动时间:
第一套健身操昨完,项目的启动速度已经从 180s 提速至 30s。
累了的同学可以先休息一会儿
休息结束,跟上第二套健身操 ——— 打包体积优化操
经过第一套的健身操之后,打包速度上,也会沾了缓存的光,打包速度也会有提升。但是,我们打包的体积还是那么大,怎么办?
开始前,还是需要看一看,我们打完包后的产物体积:
还是那句话,“所有的优化,都是需要针对问题进行优化,少数的优化是通用优化。”
那么,如何找到问题呢?
这时候就需要用到一个很常见的工具,webpack 打包分析插件 webpack-bundle-analyzer,简单配置完后,重新运行打包命令,可以在本地的8888端口看到如下效果:

每个颜色的快,就是打包产物内的代码块,图中占面积越大的文件,其文件的大小越大,部署到服务器之后,在浏览器中加载的时间越长。我们可以根据这个图,去优化
通过这个分析图,我们可以知道:
- 了解 bundle 包中的真正内容
- 找出哪些模块尺寸最大
- 查找误引入的模块
- 优化项目
具体优化就看各位同学的眼力和优化方向了。这边我举一个例子:
譬如,我发现在 app、outer-form 两个个模块内,有一块公共的代码,分别打入这两个个模块

此时可以在 webpack 配置优化打包如下:
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: ['app', 'outer-form'] // 抽取commons chunk
}),
打包后体积略有减小,因为这个地方属于略有优化。

动动眼睛动动手,其实还可以发现更多的优化点,再比如:

OK,点到为止!
希望大家身上的肥油也咔咔掉
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
我正在尝试创建一个带有项目符号字符的Ruby1.9.3字符串。str="•"+"helloworld"但是,当我输入它时,我收到有关非ASCII字符的语法错误。我该怎么做? 最佳答案 你可以把Unicode字符放在那里。str="\u2022"+"helloworld" 关于ruby-如何在Ruby字符串中插入项目符号字符?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1195
我的Rails站点使用了一个确实不是很好的gem。每次我需要做一些新的事情时,我最终不得不花费与向实际Rails项目添加代码一样多的时间来为gem添加功能。但我不介意,我将我的Gemfile设置为指向我的gem的GitHub分支(我尝试提交PR,但维护者似乎已经下台)。问题是我真的没有找到一种合理的方法来测试我添加到gem的新东西。在railsc中测试它会特别好,但我能想到的唯一方法是a)更改~/.rvm/gems/.../foo。rb,这看起来不对或者b)升级版本,推送到Github,然后运行bundleup,这除了耗时之外显然是一场灾难,因为我不确定我所做的promise是否正
我想用Capistrano启动sidekiq。下面是代码namespace:sidekiqdotask:startdorun"cd#{current_path}&&bundleexecsidekiq-c10-eproduction-Llog/sidekiq.log&"pcapture("psaux|grepsidekiq|awk'{print$2}'|sed-n1p").strip!endend它执行成功但sidekiq仍然没有在服务器上启动。输出:$capsidekiq:starttriggeringloadcallbacks*2014-06-0315:03:01executing`
我一直在尝试使用nanoc用于生成静态网站。我需要组织一个复杂的排列页面,我想让我的内容保持干燥。包含或合并的概念在nanoc系统中如何运作?我已阅读文档,但似乎找不到我想要的内容。例如:我如何获取两个部分内容项并将它们合并到一个新的内容项中。在staticmatic您可以在您的页面中执行以下操作。=partial('partials/shared/navigation')类似的约定在nanoc中如何运作? 最佳答案 这里是nanoc的作者。在nanoc中,部分是布局。因此,您可以拥有layouts/partials/shared/