第一次写掘金,以前都是看别人的文章,此次主要是想记录下近期项目中遇到的问题,和本身的一些成长。php
先简述下最近项目的技术选型,公司在两个月前前端技术方面进行升级,以后的项目均要采用主流框架来开发。在作了三个 vue 的中型项目后,有一些坑和爬坑的过程,也是对这三个项目的进行一些系统的总结吧,如文章有哪些不足之处,评论区多多交流,互相学习。更但愿各位同窗看事后能有一些感触和收获。css
一. 项目概述:使用vue全家桶开发,但没有使用vue-cli,至于缘由,下面会详细解释。
二. 项目坑点:作了三个中型项目后,业务逻辑层并无踩什么坑,坑点主要在如下几个方面
复制代码
公司主项目仍是最传统的jQ + php的架构,坐标在帝都的互联网公司,技术上其实已经与大多互联网公司差了一大截。因此在进行新项目选型时,身为公司惟一一个前端,立即决定使用 vue 来作,真正作到先后端分离。但肯定了主框架,那到底使用vue-cli呢,仍是vue+webpack本身搭呢?(产品给的项目排期是5天研发并测试,5天后上线),还得考虑到工期问题。html
通过斟酌,仍是选择了本身搭建,缘由有如下几点:前端
- 本身定制的脚手架哪出了问题本身内心清楚,从而也能培养一些前端架构的能力
- 更熟练的掌握webpack配置的能力,以及webpack3.0升级为4.0踩坑的能力
- 若是后期项目转型为服务端渲染(SSR),能更快的更改配置,这个是 vue-cli不具有的能力
除了以上几点,我的以为,是否具备webpack配置的能力,是衡量一个前端的水平高低的标准,笔者面试不少前端,三年经验的前端能从0配置webpage的几乎不多,更不要说性能优化或是本身写loader和plugin了。vue
entry
,output
,loader
,plugins
,由于本文不是具体讲如何配置webpack,因此就不一一赘述了,网上的此类教程也是比比皆是。这里主要记录下我在配置过程当中踩过的坑和值得总结的地方。node
先简单聊聊webpack3.0版本和4.0版本的主要的区别吧jquery
1. webpack4必须安装webpack-cli
2. webpack4中设置加了一个mode配置,只有两个值development | production,对不一样的环境会提供不一样的一些默认配置
3. 拆分单独模块使用optimization.splitChunks替代了CommonsChunkPlugin
4. 提取单个css文件使用MiniCssExtractPlugin替代extract-text-webpack-plugin
5. 若是是开发vue项目,基础配置文件必须引入vue-loader/lib/plugin
6. 不在使用UglifyJsPlugin压缩js文件,而是经过optimization.minimize设置为true来压缩代码,mode为production时,其默认为ture
7. 引入了Tree Shaking,不打包无用代码
复制代码
以上就是4.0版本主要的升级,固然还有不少,在此附上官方change logwebpack
注意:
MiniCssExtractPlugin仅仅会把js中的css提取到单独的css文件中,但并不会对其进行压缩
压缩css须要单独使用optimize-css-assets-webpack-plugin,并在optimization选项中进行配置
复制代码
optimization: {
minimizer: [
<!--压缩css--> new OptimizeCSSAssetsPlugin({ assetNameRegExp: /\.css$/ }) ] } 复制代码
项目配置的大体思路就是根据不一样的环境配置不一样的config.js,经过webpack-merge合并基础配置,package.json中,根据不一样的命令进行相应操做便可。看图说话: git
"dev": "webpack-dev-server --config build/webpack.dev.config.js",
"build": "webpack --config build/webpack.prod.config.js",
"build:dll": "webpack -p --progress --config build/webpack.dll.config.js"
复制代码
1. Tree Shakinggithub
tree shaking 是一个术语一般用于打包时移出Javascript中为引用的代码,它依赖于ES6模块系统中的
import
和export
的静态结构特性。
开发时引入一个模块后,若是只使用其中一个功能,上线打包时只会把用到的功能打包进对应的bundle,其余没用的功能都不会打包进来,从而实现最基础的优化。
前提是使用了
import
和export
的语法进行导入导出。什么意思呢?意思就是 若是一个模块使用了ES6的导入导出,那在打包线上代码时,就会shaking掉无用代码;可是!注意咯,若是使用的是require这种commonJS规范的导入导出,那么它不会进行shaking。
为何require的模块不会被加入shaking行列呢?缘由在于require是动态导入,在webpack打包时,若是是动态导入的模块,它并不知道被导入的模块内是否有无用代码,因此不会被shaking掉。
2. scope hoisting做用于提高
scope hoisting 的做用是将模块之间的关系进行结果推测(预编译),可让webpack打包出来的代码文件更小,运行更快。
scope hoisting的实现原理其实很简单:分析出模块之间的依赖关系,尽量的把打散的模块合并到一个函数中去,但前提是不能形成代码冗余,所以,只有那些被引用了一次的模块才能被合并。
因为scope hoisting须要分析出模块之间的依赖关系,所以源码必须采用ES6模块话语句,这点和tree shaking同样。
3. 代码压缩
production模式下,默认会对js代码进行压缩和提取(SplitChunksPlugin)
1. 尽量的少用loader
webpack打包耗时的大部分时间是由于loader预编译这一阶段,因此尽量的少用loader,以下图
file-loader
,url-loader
,img-loader
. 可是url-loader
官方声明:url-loader works like file-loader,意思是url-loader
相似于file-loader
,那就不要再使用file-loader
来配置(除非图片过大,超出url-loader中配置的limit值)2. 动态导入(懒加载)
webpack默认是容许
import
语法动态导入的,可是须要babel
的插件支持,即 在.babelrc配置文件中添加@babel/plugin-syntax-dynamic-import
插件
动态导入的最大好处就是实现了懒加载,用到哪一个模块才会加载哪一个模块,能够提升SPA应用程序的首屏加载速度
3. noParse
在引入一些第三方模块时,例如jQuery,element-ui等,咱们知道其内部不会再依赖其余模块,由于咱们最终用到的只是一个单独的js文件,因此此时webpack若是再去解析jQuery内部的依赖关系,是很是耗时的。
能够在webpack配置文件中的module节点下加上noParse,并配置正则来告诉webpack,我不须要再去解析这些模块
module: {
noParse: /jquery|element-ui/
}
复制代码
坑点来啦:
element-ui
若是 按需引入,注意,这里不能使用noParse
来作element-ui
的优化,由于按需引入的element,内部会有一系列依赖关系,使用noParse,webpack并不会去解析其依赖
下面看看优化结果,打包速度提高了接近4倍
4. IgnorePlugin
项目中使用了element-ui和moment.js,这两个插件内部都有不少语言包,尤为是moment.js。而语言包打包时会比较占用空间,而咱们的项目只须要中文语言包,这时就应该忽略掉全部的语言包,改成按需引入,从而使得构建效率更高,打包生成的文件更小
下面看看打包耗时和dll包的大小,结果仍是不错的
5. 经过减小文件搜索范围来提升性能
什么叫减小文件搜索范围?这块须要先理解什么是webpack基本原理。webpack经过配置文件,将项目中引入的第三方和公共模块进行提取,每个提取出的模块都有一个id,在引用到该模块的主文件中,经过id进行映射查找。因此若是能提高文件查找速度,对webpack打包性能会有必定提高。
resolve.modules
经过配置,告诉webpack在解析模块时应该搜索哪些目录。import vue from 'vue'
import myCommonJs from '../src/myCommonJs'
复制代码
上面代码,分别引用了第三方库和公共js,默认的webpack配置,会在当前路径下向上递归的搜索文件进行打包引用。此时就可使用resolve.modules
来告诉webpack,我是要到node_modules目录下和src目录下去找这两个文件。
在配置loader时,经过配置include 和exclude更精确指定要处理的目录,这能够减小没必要要的遍历,从而减小性能损失。这个是更为简单的方式,这里就不进行赘述了
6. Happypack多进程进行打包构建
webpack打包构建默认是单进程进行打包,当一个babel-loader须要处理多个不一样类型的资源文件时,node的单进程读取文件特性就有耗性能了,这时候就须要用到 Happypack了。但多是项目规模和文件资源较为单一的缘由,笔者经过配置Happypack,实际打包速度并无获得提高,反而速度多了2-3s,以后0须要再仔细研究下。
7. 设置babel-loader 配置项cacheDirectory为true
设置后,给定目录将用于缓存加载器的结果。以后webpack构建将尝试从缓存中读取,以免在每次运行时运行可能昂贵的Babel从新编译过程
8. DllPlugin 和 DllReferencePlugin
DllPlugin为webpack提供的打包动态连接库,通常用来打包一些版本不须要常常更新的第三方库。单独提供一份打包动态连接库的配置文件(即上图中的webpack.dll.config.js),生成环境打包以前先打包动态连接库,打包后会生成vendor_dll.js和vendor-manifest.json,它包含从引入请求到模块ID的映射
DllReferencePlugin,该插件为生产环境配置文件(webpack.prod.config.js)中使用,其中的manifest选项,
require
DllPlugin打包生成的manifest.json文件,将依赖项名称映射到模块ID,而后使用内部__webpack_require__函数根据须要使用它们
主要思想在于,将一些不作修改的依赖文件提早打包,这样咱们在开发代码发布的时候,就不须要再对这部分代码进行打包,从而节省了打包时间
实际项目中配置此项,可提高6-8s的打包速度
坑点:
1. 生成的vendor_dll.js必须手动的引入index.html中,或者经过add-asset-html-webpack-plugin将vendor_dll.js动态的添加到html文件中
2. 再使用add-asset-html-webpack-plugin时动态引入动态连接库生成映射后的js时,由于打包生成环境时,使用了
speed-measure-webpack-plugin进行耗时分析,从而致使打包出错,报错以下。
猜想应该是两个插件不兼容吧,有大佬也遇到过此类问题和解决的方法,求分享
复制代码
9. 引用cdn连接引入第三方库
总结完毕,网上此类的总结有不少,但经过自身配置和经过webpack-bundle-analyzer和speed-measure-webpack-plugin来观测打包性能,并进行优化总结,又一次加深了对webpack配置及原理的理解.