在本文中,我们将为您详细介绍webpack打包优化解决方案的相关知识,并且为您解答关于webpack打包优化的疑问,此外,我们还会提供一些关于00-认识webpack-为什么要模块化-webpack打
在本文中,我们将为您详细介绍webpack打包优化解决方案的相关知识,并且为您解答关于webpack 打包优化的疑问,此外,我们还会提供一些关于00-认识webpack - 为什么要模块化 -webpack打包过程、react webpack打包之后 图片裂了的原因和解决方法、vue webpack打包优化操作技巧、vue-cli3 webpack 打包优化的有用信息。
本文目录一览:- webpack打包优化解决方案(webpack 打包优化)
- 00-认识webpack - 为什么要模块化 -webpack打包过程
- react webpack打包之后 图片裂了的原因和解决方法
- vue webpack打包优化操作技巧
- vue-cli3 webpack 打包优化
webpack打包优化解决方案(webpack 打包优化)
单页应用首次进入项目会获取一部分数据,之后将JS包分片,走到那块再去加载那块的JS。
这样跨页面重复的JS,CSS不必再去获取,跨页面就不会出现进度条。这样减少了等待时间,提升了用户体验,省去了不必要的流量。
但是单页应用也有一个显著的问题:首次进入的时候,加载的资源太多,白屏时间太长。
这里介绍一些常用的webpack打包优化解决方案
- 使用插件查看项目所有包及体积大小
- webpack外部扩展
- DLL方式
一、查看项目打包
webpack有个插件,可以查看项目一共打了多少包,每个包的体积,每个包里面的包情况。
首先下载插件
npm intall webpack-bundle-analyzer --save-dev
同时在webpack.config.js配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; webpackConfig.plugins.push(new BundleAnalyzerPlugin());
在package.json中添加命令
“script”: { "analyz": "NODE_ENV=production npm_config_report=true npm run deploy:prod" }
我的webpack是1.X的 webpack 2.X的同学请看
然后命令行输入
npm run analyz
开始构建,根据项目大小不同,时间也不同。
过一会输出结果如下:
可以清晰的看到项目中一共有多少个包,包的体积是多少,里面加载了哪些东西,大小是多少。
这里演示的是一个干净的demo,打的包很少,体积也都很小,在真正项目中可能截图如下:
可以看到五颜六色的十分好看。
这里面 app.js 和 vendor.js是项目的主文件,其中包含了少部分业务代码和大部分公共依赖。
剩余的 number.hash.chunk.js是业务分片的代码,其中包含了大部分业务代码和少部分公共依赖。
到这里,我们就是用工具完成对项目打包的展示。
二、webpack外部扩展
列出了项目中较大的包,剩下的事情就是想办法如何减小这些包的体积(将一个大包拆成多个小包)。
项目中产生较大的包的原因可以从两个方面去考虑:
- 项目中引入的依赖包过于庞大;
- 业务代码集中在一块写,或者是业务代码写的比较繁琐;
对于这两个问题,我们可以从两个方面着手解决:
- 抽离项目中公共依赖的、不常变动的、体积较大的包;
- 将一个较大的业务代码文件,拆成多个较小的文件,异步加载(或者优化业务代码)。
这里面第二项涉及到改动业务代码,具体的情况就不同了,适合查看 如何优化JS代码。
我们来讨论第一种方法,在不改动业务代码的情况下,如何减小公共依赖。
要知道这些依赖是我们需要的,不可能排除不引入。
但是他们都是全局依赖的,万年不变的,可以使用浏览器自己的缓存来实现不重复加载。
具体做法就是:
将项目中需要的一些公共依赖包,并且不常变动的,单独取出,不再每次都打包编译(如React,Redux等)。
而是通过使用script标签形式cdn引入,这样在有缓存的情况下,这些资源均走缓存,不必加载。
具体做法:
总结需要抽离的公共依赖。
这些依赖需要满足一定的条件:
- 体积较大;
- 不常更新;
- 依赖较多;
- 是前置依赖;
常见的满足这类条件的包有:
- react
- react-dom
- redux
- react-redux
- moment
- jquery
另外一些包文件如 Antd库文件,整个包较大,但是每次用什么取什么的话,库文件也会按需加载,不必单独取出。
还有这类库文件不建议单独取出,因为里面可能会有bug,需要更新。
使用CDN引入资源
以我的demo为例:我需要抽离出的文件有 react,react-dom,react-router,redux,react-redux,history。
将这些文件放到cnd上,注意,这些文件要是压缩版本,并且是用ES5编写的,否则浏览器报错。
<head> <title>React Starter Kit</title> <Meta charset="utf-8"> <Meta name="viewport" content="width=device-width,initial-scale=1"> <!-- 体积较大的包 --> <script src="https://cdn.bootcss.com/react/15.0.0/react-with-addons.min.js"></script> <script src="https://cdn.bootcss.com/react/15.0.0/react-dom.min.js"></script> <script src="https://cdn.bootcss.com/react-router/3.0.0/ReactRouter.min.js"></script> <script src="https://cdn.bootcss.com/redux/3.6.0/redux.min.js"></script> <script src="https://cdn.bootcss.com/react-redux/5.0.1/react-redux.min.js"></script> <script src="https://cdn.bootcss.com/history/4.5.0/history.min.js"></script> </head>
配置webpack.conf.js
资源已经引入,接下来需要配置webpack,使其打包的时候不在将这些资源打包。
const webpackConfig = { name: 'client',target: 'web',devtool: config.compiler_devtool,resolve: { root: paths.client(),extensions: ['','.js','.jsx','.json'],},externals: { 'react': 'React','react-dom': 'ReactDOM','react-router': 'ReactRouter','redux': 'Redux','history': 'History' },module: {},}
这里externals告诉webpack那些资源从哪里寻找。
该对象的键表示 require 或者 import 时候的字符串
值表示的当前环境下的变量,比如引入React之后,React被作为全局对象,webpack就回去寻找React对象。
如果其中有一个找不到,打包就会失败。
配置vendor.js
接下来配置vendor,使vendor也不打包该些JS
compiler_vendors : [ // 'react',// 'react-redux',// 'react-router',// 'redux',],
接下来再次运行 npm run analyz
对比第一次的效果图,很明显app.js由原来的625kb减少到了78kb,
原来第二大的vendor.js现在已经很小了。
但是要注意的是,并不是包越小越好,越小的包反而越耗费链接。
应该让你的包里面的业务代码占大多数。
后来被告知,最大的包的体积压缩之后80k以内就可以。
三、DLL方式
dll 全称是:dynamic link library(动态链接库)
dll方式也就是通过配置,告诉webpack指定库在项目中的位置,从而直接引入,不将其打包在内。
上面介绍的方式是将包放到cdn上,build的时候不在引入对应的包;
dll方式就是指定包在项目中,build的时候不在打包对应的包,使用的时候引入。webpack
通过webpack.DllPlugin
与webpack.DllReferencePlugin
两个内嵌插件实现此功能。
新建webpack.dll.config.js
const webpack = require('webpack'); module.exports = { entry: { bundle: [ 'react','react-dom',//其他库 ],output: { path: './build',filename: '[name].js',library: '[name]_library' },plugins: [ new webpack.DllPlugin({ path: './build/bundle.manifest.json',name: '[name]_library',}) ] };
webpack.DllPlugin选项:
- path:manifest.json文件的输出路径,这个文件会用于后续的业务代码打包;
- name:dll暴露的对象名,要跟output.library保持一致;
- context:解析包路径的上下文,这个要跟接下来配置的 webpack.config.js 一致。
运行文件
运行:webpack --config webpack.dll.config.js
生成两个文件,一个是打包好的bundlejs,另外一个是bundle.mainifest.json,大致内容如下:
{ "name": "bundle_library","content": { "./node_modules/react/react.js": 1,"./node_modules/react/lib/React.js": 2,"./node_modules/process/browser.js": 3,"./node_modules/object-assign/index.js": 4,"./node_modules/react/lib/ReactChildren.js": 5,"./node_modules/react/lib/PooledClass.js": 6,"./node_modules/react/lib/reactProdInvariant.js": 7,//其他引用 }
配置webpack.config.js
const webpack = require('webpack'); var path = require('path'); module.exports = { entry: { main: './main.js',output: { path: path.join(__dirname,"build"),publicPath: './',filename: '[name].js' },module: { loaders:[ { test: /\.(png|jpg)$/,loader: 'url-loader?limit=8192'},{ test: /\.jsx?$/,loaders: ['babel-loader?presets[]=es2015&presets[]=react'],include: path.join(__dirname,'.') } ] },plugins: [ new webpack.DllReferencePlugin({ context: '.',manifest: require("./build/bundle.manifest.json"),}),] };
webpack.DllReferencePlugin的选项中:
- context:需要跟之前保持一致,这个用来指导webpack匹配manifest.json中库的路径;
- manifest:用来引入刚才输出的manifest.json文件。
参考链接:
- 层层优化webpack打包体积
- webpack打包bundle.js体积大小优化
- webpack 按需打包加载
- webpack dllPlugin 使用
完整的项目demo
00-认识webpack - 为什么要模块化 -webpack打包过程
webpack在不进行任何配置的情况下,他只认识js。
为什么要打包?
因为一个个小文件,我们合成一个,这样请求就只请求一次。
webpack除了打包之外,还具有翻译官的功能?
loader把浏览器看不懂的代码翻译成浏览器看的懂的代码。
Plugin?
对文件做点别的事情
不管是loader 还是 plugin 都是可插拔,意思就是你什么时候想用,你就装进来,不想用,删掉。所以说webpack不仅强大,而且灵活。
为什么要模块化?模块化的好处?
如上图,传统的一个页面去引入不同的js就是如上一个个去引入,但是这样会有命名冲突的问题,A.js 和B.js中可能有相同的变量命名。
解决方法:
在A.js中和B.js 中加上命名空间。
命名空间会造成一些问题:
如上图,把这个autoLogin命名空间的模块暴露出去了,其实暴露的只是想让login 给别人用,但是全部都给别人了,如果别人误操作去 autoLogin.username = '张三', 不小心把username给改掉了,那么可能你login方法里去做的登录名和密码就永远也不符合了,这不符合开闭原则。
所以,单纯使用命名空间只是为了起到被覆盖,冲突的作用,但是并不能保证随意的访问和篡改,那么如何做呢?
我们用函数作用域,函数的闭包机制,保护里面的变量不被污染。也叫作模块作用域:暴露该暴露的,隐藏该隐藏的。
如上图:这样利用闭包就保护了私有变量,又能只暴露我想暴露出去的这个方法。这样外面再去 :
改写一个比较标准的模块化写法:
上面的为什么要用一个对象包裹一下tell方法?不能直接赋值给susanModule吗?
如图以上对比:就是给susanModule 让其为一个对象,对象是叫做tell,tell是个与value同名的key,以后调用的时候就是 susanModule.tell()
综上:我们就可以得到模块化的优点:
1.作用域的封装 :模块内部的实现不会暴露在危险的全局作用域中,上面就是通过暴露tell接口,供外面使用。
2.重用性:如上面的,只需要执行susanModule.tell 就可以拿到这个方法
3.解除耦合 :如果把代码全部扔在一个文件中,出现BUG排查起来很难找,这样模块化后就容易找到定位到错误的模块。
webpack打包原理和过程:
1.把你写的模块(js文件)中的每个逻辑代码块当做webpack一个自执行函数的入参(入参是一个数组,你写的这些代码逻辑块都会装到这个数组中)
第一步:
第二步:把模块包装起来放入自执行函数中的数组(数组都是模块的入参)
第三步:实现模块加载的方法,并把它放到模块执行的环境中,确保模块间可以相互调用。
1.首选去检查,你现在塞给我的这个模块啊我是不是已经加载过了,我去已加载模块的对象中去查这个id,发现已经加载过了,就不进行二次加载了,就直接把加载过的这个结果给你返回过来。
2.如果这个模块没有加载过,那我把其存到那个已加载的对象中,存的时候添加一个moduleId属性,就是给其做个记录
3.接着我就把他的this指到module.exports的导出结果上去,并且去执行已经包装好的模块逻辑。这一步就是让模块逻辑进行执行
4.最后把模块的返回值给return 出去。
第四步:把执行入口文件的逻辑放在一个函数表达式中,并立即执行这个函数(只的就是下面的这个外层的自执行函数)
react webpack打包之后 图片裂了的原因和解决方法
如上图,图片裂了,而其他的图片好好的。
上图就是这个项目关于图片转码的配置,在看了配置之后,发现图片的大小是有限制的,也就是limit限制的大小是10kb?(额,这不重要)
而我这个图片250k太大了!(我觉得我就是个二百五)
但是讲道理,这个图片就算不转换成base64,也应该可以访问的,但实际上……
没错,图片的地址在打包之后因为我更改了原本的static的位置,所以访问不到于是发生了错误。
打包之前,我的static的位置没有变更,所以相对路径就可以直接寻到。
但打包之后,我把static与index.html文件分开了,所以导致访问失效。
既然找到原因,那么有两种方法:
- 不更改配置,把打包好的文件中的图片单独拎出来放到一个能找到的地方,
- 改配置,别的不变
1方法太邪道,于是我果断选择了2 。
其实很简单,重新编辑一下如图一的options下的publicPath,放到能找到的位置就可以了。
虽然这样一来,开发的时候大图片会裂开,但打包后的位置就不会出错了。
本着开发无所谓上线要看起来正常的项目标准,我无视了按下葫芦起的瓢。
以上。
vue webpack打包优化操作技巧
临近春节,公司很多同事都提前回家过年,剩余人员根据禅道去修改bug,当bug修正完毕以后,我们需要重新打包给运维,上测试服给测试同事提测,但是由于项目本体比较庞大,所以打包时间太过漫长(二十五分钟以上:sob:),所以有了打包优化的想法(其实想法早就有了,但是因为平时工作计划比较充实,所以一直没有去完成这个工作),这次正好有时间,所以去重新考虑了这个问题!
webpack是react项目标配的打包工具,和NPM搭配起来使用管理模块实在非常方便。
webapck 把所有的静态资源都看做是一个 module,通过 webpack,将这些 module 组成到一个 bundle 中去,从而实现在页面上引入一个 bundle.js,来实现所有静态资源的加载。
话不多说,直接开始正文吧
先给大家看一下项目的目录结构:
就是正常的项目结构,简单说一下吧:
- build文件夹包含的是一些打包配置的一下东西
- config文件夹是项目的基础配置
- dist是打包之后的文件
- node_modules是项目的依赖包
- src文件夹里面是项目的源码
- static文件夹里面放的是一些项目使用的静态资源
- index.html是项目的首页
- package.json文件是项目的配置json
- yarn.lock是使用yarn锁定项目用的依赖
优化思路
项目打包时间长,原因无外乎就是项目整体比较庞大、依赖复杂、组件之前拆分不够合理。
对于这三个问题呢,我们可以针对下面这几个方面去做一下处理:
- 对项目进行路由屏蔽,只打包自己需要打包的部分(我司就是好几个项目合并在了一起,至于原因则是 需求类似,所以放在一起比较省事 -_-||| 开发过程中是省了不少事,但是现在一样要还的!!!!)
- 依赖关系复杂,这里说的是项目中的依赖模块比较多,像我们现在这个项目,光算依赖包的话就有40+,另外一个重要原因就是组件之间存在相同引用的依赖。解决思路是把项目中重用的依赖抽离出来进行单独打包。
- 组件在写的过程中,需要考虑好这个组件的使用方向,以及实现功能,不能混为一谈。
实际操作
有了整体的思路,那么开搞就可以啦 去webpack文档去看了一下有一个DllPlugin,这个插件就是帮助我们解决问题的关键,下面是我webpack.dll.config的代码:
我们需要将项目中具有重用性的包抽离出来,放在vendor数组里面,然后在下面output里面定义一下打包输出的文件路径,然后在resolve里面配置解析参数,最后定义使用的DllPlugin插件,UglifyJsPlugin是压缩js的插件
DllPlugin里的path,会输出一个vendor-manifest.json
,这是用来做关联id的,打包的时候不会打包进去,所以不用放到static里 然后运行一下 webpack -p --progress --config build/webpack.dll.conf.js
成功以后,static下会有dll.vendor.js,根目录下会有vendor.manifest.json
各自打开看一下,就会看到依赖库的源码和匹配id
ok,到这里,抽离依赖库的事情就完成了,那么接下来问题就是怎么引用呢,怎么在dev和build跑呢?
这里补了一点dll和commonsChunk
概念上的区别,commonsChunk之所以慢和大,是因为每次run的时候,都会去做一次打包,而实际上我们不会一直去更新我们引用的依赖库,所以dll的做法就等于是,事先先打包好依赖库,然后只对每次都修改的js做打包。
继续上面的步骤,我们需要根据生成的json文件去修改webpack.base.config文件:
然后打开index.html,在底部加上 以上是小编为你收集整理的vue webpack打包优化操作技巧全部内容。 如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。
总结