webpack 基础与实践(项目优化)总结
栏目: JavaScript · 发布时间: 5年前
内容简介:前言:本文基于weboack4.x,主要涉及webpack4 基本概念、基本配置和实际项目打包优化。关于概念方面参考官网,常用配置来自于网络资源,在文末有相关参考链接,实践部分基于自己的项目进行优化配置。定义编译打包入口文件。类型:字符串(单入口)、对象(多入口)1、filename:
前言:本文基于weboack4.x,主要涉及webpack4 基本概念、基本配置和实际项目打包优化。关于概念方面参考官网,常用配置来自于网络资源,在文末有相关参考链接,实践部分基于自己的项目进行优化配置。
webpack 四大概念
entry
定义编译打包入口文件。类型:字符串(单入口)、对象(多入口)
entry: './src/index.js' // 等同于 entry: { main: './src/index.js' }
output
1、filename:
[name]
: 对应着entry中对象的key
[id]
: 内部的chumk id
[hash]
: 每次打包编译的唯一hash,改动会影响整个项目的打包,缓存失效
[chunkhash]
:对应着每个入口文件计算而来的hash,唯一,文件之间互不影响
[contenthash]
: contenthash = (moduleId + content) 生成的hash。同一文件中,修改某个module影响其他module。例如,js代码中引入css文件,修改js文件造成css文件的hash改变
2、path: 是配置输出文件存放在本地的目录,字符串类型,是绝对路径puclicPath: 对构建出的资源进行异步加载(图片,文件) 时候的路径前缀, 可以看作静态文件托管在cdn
3、chunkFilename: 决定了非入口(non-entry) chunk 文件的名称,如按需加载、异步加载
Module
配置Loader
rules 数组,包含多个处理文件的 loader 配置
- 条件匹配: 通过test、include、exclude三个配置来命中Loader要应用的规则文件。
- 应用规则: 对选中后的文件通过use配置项来应用loader,可以应用一个loader或者按照从后往前的顺序应用一组loader。同时还可以分别给loader传入参数。
- 重置顺序: 一组loader的执行顺序默认是从有道左执行,通过exforce选项可以让其中一个loader的执行顺序放到最前或者是最后。
loader配置属性
test:类型正则、字符串、数组。匹配文件
use:类型字符串、对象、数组【loader字符串或对象】。对选中的文件应用loader。
对象中包含:loader、options:loader的具体配置参数、enforce: 改变该loader的执行的顺序【pre/post】
include: 指定需要处理的文件。类型:一般为路径、可以是数组类型。
exclude:排除不需要处理的文件。类型:一般为路径、可以是数组类型。
noParse: 排除对没有采用模块化的文件解析和处理,类似:jQuery。 类型:RegExp, [RegExp], function其中一个。
parser:可以更细粒度的配置哪些模块语法【AMD、CommonJS、ES6等】是否需要解析
Plugin
Plugin 是用来扩展Webpack 功能的,通过在构建流程里注入钩子实现,它为Webpack 带
来了很大的灵活性。主要在打包的某个阶段执行该插件,对文件进行处理,类似于钩子函数
tree shaking
触发treeshking条件:
1.需要代码是es module规范的并且使用解构赋值的方式引入,
2.开启 optimization.usedExports:true
来标记使用和未使用的模块,
- 使用压缩的插件进行删除未使用代码。
webpack4的mode设置为production,默认开启optimization.usedExports和使用代码压缩
注:
1、tree shaking 不能作用于有副作用side-effect的代码
如果所有代码没有副作用,在package.json 中添加
sideEffects: false
如果存在副作用代码/模块,
sideEffects: [ ".src/some-side-effectful-file.js", "*.css" ]
"side effect(副作用)" 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。
2、不要意外地将 ES 模块编译成 CommonJS 模块。如果你使用 Babel 的时候,采用了 babel-preset-env 或者 babel-preset-es2015,请检查这些预置的设置。默认情况下,它们会将 ES 的导入和导出转换为 CommonJS 的 require 和 module.exports,可以通过设置.babelrc中 { modules: false } 选项来禁用它
参考: webpack tree shaking 的三个要点
HMR (Hot Module replacement)模块热替换
devServer: { proxy: { '/api': { target: 'http://localhost:3000', pathRewrite: {'^/api' : ''} }, context: ['/api', '/online'], //匹配多个路径,同时代理到同一个站点 target: 'http://localhost:3000' }, hot: true, hotOnly: true }
code spiliting
把代码分离到不同的 bundle 中,可以按需加载或并行加载这些文件
实现方式:
1、入口配置:entry 入口使用多个入口文件 =》 存在重复引用的模块
2、抽取公有代码:使用 SplitChunksPlugin 抽取公有代码,取代CommonsChunkplugin
webpack.config.js 配置: ``` optimizition: { splitChunks: { chunks: 'all' } } ```
3、动态加载 :动态加载一些代码 =》 ECMAScript 提案 的 import() 语法
方式1 + 方式2 需要配合使用,才能达到代码抽离的效果
prefetch && preload
prefetch(预取):将来某些导航下可能需要的资源
preload(预加载):当前导航下可能需要资源
import(/* webpackPrefetch: true */ 'LoginModel');
/ webpackPrefetch: true /:把主加载流程加载完毕,在空闲时在加载其他,等再点击其他时,只需要从缓存中读取即可,性能更好。推荐使用,提高代码利用率。把一些交互后才能用到的代码写到异步组件里,通过懒加载的形式,去把这块的代码逻辑加载进来,性能提升,页面访问速度更快。
/ webpackPreload: true /: 和主加载流程一起并行加载。
lazy loading
import() 异步加载加载的模块,开启代码分割后,会被单独打包在一个文件中
路由懒加载
const Login = () => import('./components/login')
模块异步加载
import(/* webpackChunkName: "vendor"*/ './page/vendor.js').then(({default: _}) => {// todo})
公共代码提取
mini-css-extract-plugin 用于提取公共css,取代 webpack 3 的 extract-text-webpack-plugin
注:一般适用于生产环境,在开发环境会导致HMR功能缺失;在开发环境,使用style-loader
使用方式:
const MiniCssExtractPlugin=require('mini-css-extract-plugin') const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 代码压缩 // modules 中 // css,scss,sass,less { test:/\.(sa|sc|c)ss$/, use: [ process.env.NODE_ENV === 'development' ? 'style-loader': MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ] } optimization: { minimizer: [new OptimizeCssAssetsPlugin({})] }, //plugins中 new MiniCssExtractPlugin({ filename: "[name].css" })
DllPlugin & DllReferencePlugin
防止第三方包多次编译打包。 第一次打包时,把第三方模块单独打包生成一个文件 vendors.dll.js,之后在打包时就可以直接从 vendors.dll.js 中引入之前打包好的第三方模块
实现过程:
1、编写一个用于生成动态链接库的配置文件
2、运行生成动态链接库和对应的.mainfest.json映射文件
3、在webpack.config.js中使用动态链接库。这样第二次编译打包会从json文件中找相应的模块
webpack.dll.config.js
module.exports = { entry: { react: ['react'] //react模块打包到一个动态连接库 }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].dll.js', //输出动态连接库的文件名称 library: '_dll_[name]' //全局变量名称 }, plugins: [ new webpack.DllPlugin({ name: '_dll_[name]', //和output.library中一致,值就是输出的manifest.json中的 name值 path: path.join(__dirname, 'dist', '[name].manifest.json') }) ] }
webpack.config.js
plugins: [ new webpack.DllReferencePlugin({ manifest: require(path.join(__dirname, 'dist', 'react.manifest.json')), }) ],
提高webpack的构建(打包/build)速度
- 多入口情况下,使用SplitChunk来提取公共代码
- 通过externals配置来提取常用库
- 利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
- 使用Happypack 实现多线程加速编译
- 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
- 使用Tree-shaking和Scope Hoisting来剔除多余代码
模式分离
webpack 作为模块化打包工具, 常用来对项目进行打包进行本地调试和发布到线上,所以无论是自己在项目配置使用webpack还是使用开发框架的脚手架进行开发,都需要区分开发和生产环境。在webpack4 的配置项中添加了 mode
属性,可以用来区分两者模式。
下面是开发和生产模式下一些默认配置和区别:
development 模式下,默认开启了NamedChunksPlugin 和NamedModulesPlugin方便调试,提供了更完整的错误信息,更快的重新编译的速度。
production 模式下,由于提供了splitChunks和minimize,代码就会自动分割、压缩、优化,同时 webpack 也会自动帮你 Scope hoisting 和 Tree-shaking
优化划分
体积优化
工具 - 打包文件可视化
使用 webpack-bundle-analyzer, 可直观的看出打包后每个模块所占比例和大小
使用方法:
-
安装
npm i -D webpack-bundle-analyzer
2.在 package.json -> script 中添加启动命令
"analyz": "cross-env NODE_ENV=prodution npm_config_report=true npm run build"
3.在 webpack.pro.conf.js -> plugin 添加以下代码,可以改变启动时的端口等配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
new BundleAnalyzerPlugin({analyzerPort: 8089})
关于使用方法详细可参考: webpack实践-webpack-bundle-analyzer使用
具体方案
路由懒加载
import xxxx from '@componets/xxx' => const xxx = () => require('@componets/xxx')
模块化按需加载
对于一些类似antd、element-ui、eCharts等库,可以按需引入,没有必要全局引入,具体方法见官方文档
对于loadash等API依赖工具,结合lodash-webpack-plugin和babel-plugin-lodash,实现按需引入,把需要的API一次性引入,并挂载在全局上
// 从lodash 中统一引入你需要的方法 import _ from 'lodash' export default { cloneDeep: _.cloneDeep, debounce: _.debounce, throttle: _.throttle, size: _.size, pick: _.pick, isEmpty: _.isEmpty } // 注入到全局 import _ from '@helper/lodash.js' Vue.prototype.$_ = _ // vue 组件内运用 this.$_.debounce()
外部模块使用CDN
对于类似Jquery等大而使用较少的库,可以在index.html使用cdn引入;如果顾虑到可能造成外部攻击等问题,可以下载成为本地资源,再引入
参考: webpack 打包优化之体积篇
速度优化
实现方法
缩短路径,减小文件搜索范围
1、缩小文件搜索范围或者指定特定的文件夹位置
2、排除不需要进行处理的文件
,,,,
使用并发压缩插件
不使用默认的 UglifyJs,使用并行压缩工具 webpack-parallel-uglify-plugin
具体参考: webpack 打包优化之速度篇
实践
vue 项目
我是在我之前开发一大型项目使用webpack进行项目优化
注:此项目使用的webpack版本为2.x,不是最新版本4.x,由于项目比较复杂,升级带来的潜在问题可能比较多,时间精力有限,暂时未升级
项目技术和库:vue全家桶 + vue-cli + webpack + jQuery + element-UI + eCharts ...
按照上文中的常用配置,就打包速度和体积进行了优化,从而导致页面加载速度得到一定提升
【手动捂脸】
最后
由于本文参考了许多相关文章,并加以自己的理解和实战,如有不妥之处,请多包涵并指出,谢谢
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Domain-Driven Design
Eric Evans / Addison-Wesley Professional / 2003-8-30 / USD 74.99
"Eric Evans has written a fantastic book on how you can make the design of your software match your mental model of the problem domain you are addressing. "His book is very compatible with XP. It is n......一起来看看 《Domain-Driven Design》 这本书的介绍吧!