webpack4.0
2020-02-29 23:00:35 9 举报
AI智能生成
前端学习笔记
作者其他创作
大纲/内容
课程总结:<br>记住并理解核心概念<br>遇到问题查官方文档
1.课程介绍
面向对象编程,模块化
文件拆分成多个会造成http请求过多
2.初识webpack
webpack是什么?
模块打包工具<br>webpack is a module bundler
模块引入规范
ES6 module<br>
import<br>export default
CommonJS<br>
require<br>module.exports
AMD
CMD
可以打包多种类型的文件
js
css
图片
。。。
搭建webpack环境
基于nodejs开发的模块打包工具
安装最新的node LTS版本,打包速度快
常用命令
初始化项目
npm init
初始化项目,生成package.json文件
安装
全局安装
npm install webpack webpack-cli -g
不建议全局安装;建议局部安装,可以在不同项目内使用不同的版本
指定版本安装
列出版本
npm info webpack
npm install webpack@4.16.5 -D
@后可以指定版本号
-D
等价于 --save-dev
安装的包的名称及版本号就会存在package.json的devDependencies这个里面
-S
等价于 --save
--save会将包的名称及版本号放在dependencies里面
devDependencies和dependencies:<br>
devDependencies 指的是你本地开发环境下的依赖(比如webpack,babel以及一系列的loader等)<br>dependencies 指的是你生产环境下打包所需要的依赖(比如你项目中用到了lodash,那么你就应该配置到这个下边)
检查版本
webpack -v
webpack-cli -v
安装webpack-cli是为了使用webpack命令
打包
没有配置文件时的打包命令<br>npx webpack index.js
用webpack对index.js进行打包
在当前项目内运行webpack, npx可以再当前目录下找到node-modules里的webpack包
Linux命令
cd ../<br>mkdir webpack-demo 创建一个webpck-demo文件夹
webpack配置文件
配置文件是什么?
webpack.config.js,告诉webpack怎么去打包
简单配置
const path = require('path'); //node的核心包<br>module.exports = {<br> entry: './index.js',<br> output: {<br> path: path.resolve(__dirname, 'dist'),//__dirname指的是webpack.config.js所在的当前目录的路径<br> filename: 'my-first-webpack.bundle.js'<br> }<br>}
打包
三种打包方式
全局安装时<br>
webpack index.js
局部安装时
npx webpack index.js
npm scripts
配置package.json里的scripts,可以简化打包命令
npx 和npm :<br>
npx会在目录下的node_modules下面找webpack,没有就安装一个。<br>npm 则先去全局,然后再去当前目录下的node_modules找webpack,没有就不找了
打包输出内容
Hash
Assets 、 Size 、Chunks、 Chunk Names
mode
production 压缩
development 不压缩
注意
webpack做打包的时候并不知道该如何去打包,它需要配置文件来告诉它如何做打包,如果没有找到配置文件,默认会有自己的一套默认配置的
修改配置后要重启,否则它按原来的配置打包代码
3.webpack核心概念
Loader
loader是什么?
它是一个打包方案,webpack只知道如何打包js文件,不知道如何打包非.js文件,loader可以告诉webpack如何打包这些文件
常用loader
vue-loader
打包.vue文件
file-loader
module: {<br> rules: [{<br> test: /\.jpg$/,<br> use: {<br> loader: 'file-loader'<br> } <br> }]<br> },
把文件引入放到dist目录下,再把文件的地址返回给引入变量
打包图片、.txt等
打包图片时,单独生成一个图片文件
url-loader
与file-loader的区别
file-loader 单独生产一个图片文件<br>url-loader 把图片转换成一个base64的字符串,然后直接把这个base64的字符串放到bundle.js里,而不是单独生成一个图片文件
利弊<br>
好处是,可以少发一次http请求,适合图片比较小的情况
坏处是,如果图片过大,bundle.js会变得很大,请求时间过长,页面很久才能展示出来
改进
{<br> loader: 'file-loader',<br> options: {<br> limit:2048<br> }<br>}
图片大于2048字节(2KB)时使用file-loader<br>图片小于2048字节时,使用url-loader,把图片变成base64放到bundle.js里
使用loader打包静态资源
图片
placeholder占位符
{<br> loader: 'file-loader',<br> options: {<br> name: '[path][name]_[hash].[ext]'<br> }<br>}
[ext] 资源扩展名
[name] 资源的基本名称
[path] 资源相对于 context的路径
[hash] 内容的哈希值
在options里配置outputPath
打包后的图片放入outputPath指定的目录下
样式
module: {<br> rules: [<br> {<br> test: /\.css$/,<br> use: [ 'style-loader', 'css-loader','sass-loader','postcss-loader' ]<br> }<br> ]<br> }
style-css
在得到css生成的内容以后,把这段内容挂在到页面的<head>里
css-loader
分析几个css文件之间的关系,把几个css文件合并成一段css
css-loader扩展项
{<br> loader: 'css-loader',<br> options: {<br> importLoaders: 2 <br> modules:true<br> }<br> },
importLoaders: 2
用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader<br>对scss文件里@import的scss文件如何进行打包
modules:true
对css进行模块化打包,可以防止样式冲突
sass-loader
把.scss文件翻译成css
postcss-loader
自动添加厂商前缀
postcss.config.js
autoprefixer插件
执行顺序
从右到左
字体
iconfont
阿里矢量图标库
下载图标,把iconfont.css里的样式粘贴到本地样式文件里,修改字体src
file-loader
它告诉webpack去打包后缀为.eot .ttf .svg的字体
plugins
使打包更加便捷
plugin可以在webpack运行到某一个时刻时帮你做一些事情,比如html-webpack-plugin会在打包结束这一个时刻自动生成html文件
常用插件
html-webpack-plugin
plugins: [<br> new HtmlWebpackPlugin(), // Generates default index.html<br> ]
在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中
plugins: [<br> new HtmlWebpackPlugin({ // Also generate a test.html<br> filename: 'test.html',<br> template: 'src/assets/test.html'<br> })<br> ]
以template指定的文件为模板生成html文件
clean-webpack-plugin
plugins: [<br> new CleanWebpackPlugin(['dist']), <br>]
在打包之前,先删除dist目录
hot-module-replacement-plugin
热更新
Entry与Output
多个入口起点
{<br> entry: {<br> app: './src/app.js',<br> search: './src/search.js'<br> },<br> output: {<br> filename: '[name].js',<br> path: __dirname + '/dist'<br> }<br>}
filename是输出的js的文件名
[name]里的name就是entry里的key值
打包多个入口文件
静态资源放在CDN时
output: {<br> publicPath: 'http://cdn/...'<br>}
打包后的html引入的js文件中会加上 publicPath前缀
SouceMap
它是一个映射关系,它会建立打包后的文件和源文件之间的映射关系,告诉你源文件的什么位置出错
打包会变慢,因为它要构建映射关系
devtool配置选项
devtool: "source-map"
打包后生成 .js.map文件<br>该文件对源代码和打包后的代码进行映射<br>
devtool: "inline-source-map"
打包后不会生成.js.map<br>映射关系以base64的形式被引入打包后的js文件中的souceMappingURL中
devtool: "cheap-inline-source-map"
只提示哪一行出错不提示列,只针对业务代码不针对loader生成sourcemap,可以提升打包性能
devtool: "eval"
最快,但是业务复杂时提示不全面
devtool: "cheap-module-source-map"
module 针对loader生成sourcemap
production环境选用这个较好
devtool: "cheap-module-eval-source-map"
development环境选这个较好,性能快,信息全
WebpackDevServer
webpack --watch
可以监听变动并自动打包
它不能起服务器,每次打包后需要手动刷新浏览器
webpack-dev-server
功能
监听变动并自动打包
自动打开、刷新浏览器
模拟服务器特性
开启一个web服务器,localhost:8080,启动完服务器打包代码到dist目录下
以http的方式打开浏览器,可以发ajax请求<br>而以file
基础配置
devServer: {<br> contentBase: path.join(__dirname, 'public'),<br> open: true,<br> proxy: ,<br> port: ,<br> }
WebpackDevServer 服务器要启动在哪一个文件夹下
open: true, 启动时自动打开浏览器
proxy 接口代理
port 端口号
打包生成的文件在内存里,看不到main.js文件
node server.js
使用webpack-dev-middleware和express()<br>自己实现一个devServer<br>
express()创建一个http服务器
。。。比较复杂,不重要,参考https://webpack.js.org/guides/hot-module-replacement/
可以在命令行里使用webpack<br>也可以在node中使用webpack
Hot Module Replacement(HMR)
devServer: {<br> hot: true,<br> hotonly:true<br> }<br>并引入hot-module-replacement-plugin插件
module.hot.accept('file',()=>{ })
当file文件的内容改变时,执行后边的回调函数进行热更新
样式文件 vue react不需要手写这段HMR代码,是因为css-loader vue-loader内置了这个功能
Bable
把ES6代码转换成浏览器可以识别的ES5代码
安装
npm install babel-loader @babel/core --save-dev<br>npm install @babel/preset-env --save-dev<br>npm install @babel/polyfill --save<br>
@babel/core
是babel的核心库,<br>它能让babel去识别js代码里的内容,然后把js代码转成AST抽象语法树,然后再把抽象语法树编译转成新的语法
babel-loader
负责打通跟webpack的关联,并没有转换
@babel/preset-env
负责转换新的JavaScript句法(syntax)而不转换新的API
@babel/polyfill
弥补ES5缺失的变量或者函数,设置useBuiltIns为'usage'可以只打包已经用到的API语法,按需引入减少文件体积
配置
打包业务代码<br>
presets:[["@babel/preset-env",{<br> targets:{<br> chrome:'67'<br> },<br> useBuiltIns:"usage"<br> }]]
打包业务代码,只需要设置@babel/preset-env
在chrome大于67以上的版本进行语法转换,如果浏览器本身支持ES6语法,则不进行转换
@babel/pollyfill会污染全局环境
打包内库代码<br>
plugins:[["@babel/plugin-transform-runtime",{<br> "corejs":2,<br> "helpers":true,<br> "regenerator":true,<br> "useESModules":false<br> }]]<br>
生成一些第三方模块或者UI组件时,打包这些库时不希望污染全局变量,<br>@babel/plugin-transform-runtime会以闭包的方式注入对应的内容,当可以防止污染全局变量<br>
"corejs":2,
"corejs":要设置为2,当页面不存在map promise方法时,才会把代码打包进来
需要额外安装@babel/runtime-corejs2
babel配置也可以单配在.babelrc文件里,将.babelrc放到项目的根目录下
哪些文件转换?<br>哪些不转换?
include: [<br> path.resolve(__dirname, "app/src"),<br> path.resolve(__dirname, "app/test")<br> ],<br> exclude: /node_modules/
include 表示目录中的哪些 .js 文件需要进行转换<br>exclude babel-loader做语法解析时忽略node_modules这些第三方模块
4.webpack进阶
Tree Shaking
当引入一个模块时,不引入该模块所有的代码,只引入需要的代码,剔除掉无用代码
只支持ES Module,因为它的底层是静态引入<br>不支持CommonJS,因为它的底层是动态引入
配置
webpack.config.js
生产环境
mode: 'production',<br>
自动进行treeShaking<br>不需要写<br>optimization: {<br> usedExports: true,<br> }, <br>
开发环境
<br>mode: 'development',<br> optimization: {<br> usedExports: true,<br> }, <br>
usedExports: true<br>表示,哪些模块用export导出了就打包,如果模块没有export打包时就忽略掉
在development环境下打包时,即使用了treeShaking,也不会把代码直接从打包后的js文件里剔除掉,但是会提示exports used<br>因为开发环境需要做一些调试,如果直接剔除可能会导致soucemap对应的行数出错
package.json
"sideEffects": []
把不需要进行treeShaking的文件写到[],比如css文件['*.css']
"sideEffects":false
如果对所有文件都进行treeShaking,设为"sideEffects":false
development和production模式的区分打包
为什么区分环境打包?
不同环境的需求不同,比如测试需要devserver方便开发,生产环境需要压缩代码、对sourcemap进行精简。。。
如何区分?
在项目根目录下创建三个webpack配置文件<br>
webpack.prod.js 生产环境配<br>
webpack.dev.js 开发环境配置<br>
webpack.common.js 生产环境和开发环境相同的配置
package.json里配置:<br>scripts:{<br> dev:webpack-dev-server --config webpack.dev.js, <br> build:webpack --config webpack.prod.js<br>}<br>
在不同的环境下,根据对应的配置文件进行打包
npm install webpack-merge -D<br>使用merge()来将webpack.common.js和对应环境的配置合并起来
一般将配置文件放到build目录下,修改package.json里script的文件路径即可
webpack和code splitting
为什么要分割?
比如引入lodash,仅使用其中一两个方法,仍会把所有的lodash打包进来,造成打包文件过大,加载时间长
分割方法
手动代码分割
新建一个lodash.js<br>import _ from 'lodash'<br>window._ = _ ;
entry:{<br> lodash:'./src/lodash.js'<br>}
使用webpack配置进行代码分割
两种情形下的分割配置
同步引入模块代码
webpack.common.js<br>optimization:{<br> splitChunks:{<br> chunks: 'all'<br> }<br>}
异步引入模块代码<br>import('loadash').then。。。<br>
无需配置optimization<br>动态引入语法需安装 npm install babel-plugin-dynamic-import-webpack --save-dev<br>.bebelrc 添加配置:"plugins": ["dynamic-import-webpack"]
代码分割与webpack无关<br>
给打包的生成的文件命名
1,卸载 babel-plugin-dynamic-import-webpack (不支持)<br>2,动态加载组件的官方插件:npm install @babel/plugin-syntax-dynamic-import -D<br>3,魔术注释:import(/* webpackChunkName:"lodash" */'lodash') ,生成vendor~lodash.js<br>4,.babelrc 配置"plugins": ["@babel/plugin-syntax-dynamic-import"]<br> splitChunks:{<br> cacheGroups: { <br> vendors: false,<br> default: false<br> }<br>}<br>生成lodash.js,去掉了vendor~前缀
splitChunksPlugin参数详解
下边各参数的含义见视频讲解:https://coding.imooc.com/lesson/316.html#mid=22364
参数
splitChunks: {<br> chunks: "async",<br> minSize: 30000,<br> minChunks: 1,<br> maxAsyncRequests: 5,<br> maxInitialRequests: 3,<br> automaticNameDelimiter: '~',<br> name: true,<br> cacheGroups: {<br> vendors: {<br> test: /[\\/]node_modules[\\/]/,<br> priority: -10<br> },<br> default: {<br> minChunks: 2,<br> priority: -20,<br> reuseExistingChunk: true<br> }<br> }<br>}<br>
Lazy Loading
其实懒加载就是通过import来异步的加载一个模块。什么时候执行import语法时,它对应的模块才会被载入。<br>好处是借助Import这种语法,我们可以让页面加载的更快。
minChunks: 2<br>假设我们打包以后会有很多个文件,如果有两个以上文件里面需要引用lodash 那我们就需要对lodash进行代码分割。
打包分析 Preloding Prefeching
打包分析
生成打包过程的描述文件: <br>webpack --profile --json > stats.json
打包分析工具<br>bundle 分析<br>
https://github.com/webpack/analyse/
webpack-chart
webpack-bundle-analyzer
预加载
使用场景
异步加载交互代码时:例如当点击的时候再加载异步代码,虽然提高了页面初始化速度,但是对用用户点击的体验不好,速度太慢;<br>为了解决懒加载带来的问题:使用prefetch preload<br><br>使用预加载,比如打开一个网站,首页加载完,在首页加载完成后利用空闲时间,预先加载登陆模态框,等用户登陆时模态框可以很快的加载出来
使用方法
使用魔术注释<br>import(/* webpackPrefetch: true */ 'LoginModal');
区别
prefetch 会等主流程都加载完成,等待空闲再加载;(最优)<br>preload 和主线程一起加载<br>
优化
代码使用率
打开控制台 :ctrl+shift+p 输入coverage 查看js代码使用率
缓存能带来的性能提升是非常有限的,应该重点考虑提升代码的使用率,<br>有些交互之后才能用到的代码,可以放到单独的异步模块里 提高加载速度及页面利用率
CSS文件的代码分割
css代码分割插件<br>mini-css-extract-plugin
解决什么问题
css in js<br>默认会把css打包进js中<br>
作用
css代码分割,可以将css单独打包
缺点
暂时不支持HMR,因此只能在线上使用
安装使用
1、安装插件<br>
npm install --save-dev mini-css-extract-plugin
2、配置webpack.prod.js<br>
const MiniCssExtractPlugin = require('mini-css-extract-plugin');<br><br>module.exports = {<br> plugins: [new MiniCssExtractPlugin()],<br> module: {<br> rules: [<br> {<br> test: /\.css$/i,<br> use: [MiniCssExtractPlugin.loader, 'css-loader'],<br> },<br> ],<br> },<br>};
引入插件, require...
plugins中使用插件
new MiniCssExtractPlugin({...})
更改loader,开发环境和线上环境分开配置loader,配置loader为MiniCssExtractPlugin.loader
3、经过1和2,还不能把css拆分出来,原因在于<br>对css文件进行了tree shaking
webpack.prod.js
optimization:{<br> usedExports:true <br>}
package.json
sideEffects:[<br> '*.css'<br>]<br>
css文件不进行tree shaking
4、css压缩
对抽离出来的css文件进行代码的合并压缩<br>
optimize-css-webpack-plugin
扩展用法
Extracting CSS based on entry
根据入口分割css
将不同入口文件中加载的所有模块的css样式打包到一个文件里<br>比如有两个入口文件index.js index1.js,将index.js index1.js中加载的所有模块的样式分别进行打包
配置
配置 cacheGroups<br>optimization:{<br> splitChunks:{<br> cacheGroups:{ 。。。 }<br> }<br>}
Extracting all CSS in a single file
当有多个入口文件时,将所有入口文件里引入的css文件全部打包到一个css文件里
配置
optimization: {<br> splitChunks: {<br> cacheGroups: {<br> styles: {<br> name: 'styles',<br> test: /\.css$/,<br> chunks: 'all',<br> enforce: true,<br> },<br> },<br> },
将所有 .css文件打包到一个名为styles的文件里
output中的filename和chunkFileName的区别
入口文件的命名为filename<br>间接引入的模块会命名为chunkFilename
webpack与浏览器缓(caching)<br>
缓存
浏览器缓存<br>存在什么问题
代码打包,将dist目录下的文件上传到服务器<br>用户第一次打开页面,浏览器加载打包后的文件,<br>用户再次刷新,则浏览器从缓存中拿文件<br><br>修改源代码,重新打包,将dist目录下的文件上传到服务器<br>用户再次刷新,浏览器请求文件时发现本地有缓存(因为文件的名字没有改变),则从缓存中拿文件,就不会加载新上传的文件<br>
怎么解决
在每次修改源文件后,打包时使用contenthash改变文件的名字,可以解决上述问题
contenthash
作用
contenthash,是根据content产生的一个hash字符串,content不变hash字符串就不会变<br>如果源代码没有改变,那么打包生成文件的contenthash永远都不会变
配置
output: {<br> filename: '[name].[contenthash].js',<br> chunkFilename: '[name].[contenthash].js'<br>}
修改源文件,重新打包,将dist目录下的文件(main.js、vendor.js、index.html)放到服务器上去<br>当用户再次访问线上的页面时,流程如下:<br>打开index.html,浏览器加载index.html中<script>引入的js文件,<br>加载vendor.js时,发现vendors.***.js里的contenthash值没变,则使用本地缓存<br>加载main.js时,main.******.js里的contenthash值变了,则去服务器上加载最新的main.js文件<br>
旧版本webpack中存在的问题
manifest的作用:存储业务逻辑(main.js)和库(vendor.js)之间关联的文件<br>在旧版的webpack中有差异,因此在每次打包时,即使没改变源代码,contenthash都会有变化。
webpack.common.js文件<br>optimization: {<br> runtimeChunk: {<br> name: 'runtime'<br> }<br>}
shimming的作用
作用
解决webpack打包时存在的兼容性问题,修改webpack的默认行为,实现webpack自身无法实现的一些效果
使用场景
eg: 自动引入第三方库<br>如果使用了一些版本比较老的第三方模块,它里边用到了jquery或者lodash等第三方库,而这些模块并没有使用ES6 moudle的 import引入这些第三方库,<br>这时候打包会出错,使用providePlugin,可以在模块中自动引入依赖的第三方库
用途
自动引入第三方库
new webpack.providePlugin({<br> $: 'jquery'<br>})<br>
当发现一个模块中使用了$,就会在模块里自动引入jquery这个模块,然后把$作为模块的名字<br>即,在底层完成了 import $ from 'jquery'
改变this的指向
安装 imports-loader<br>修改配置 loader: 'imports-loader?this=>window'
this默认指向它所在的模块<br>改变this的指向,让它指向window
环境变量的使用方法
全局变量env
5.webpack实战配置案例
Library打包(库打包)
引子
当自己实现一个库时,打包后的文件给别人使用,用户可能会使用各种引入方式,比如 ES6 module 、CommonJS 、AMD、 <script>。。。<br>你要如何配置才能支持用户的各种引入方式?
配置
ouput:{<br> library:'root',<br> libraryTarget:'umd' <br>},<br>
<script>标签引入
library:'root',
生成一个全局变量root,用户可以在<srcipt src='root.js'></script>中引入
模块化引入
libraryTarget:' umd'
表示用户可以用任意的模块引入方式引入这个这个模块
libraryTarget:' this'
变量root会注入到this对象中
libraryTarget:' window'
变量root会注入到window对象中
防止重复引入
externals:['lodash']
externals的值为数组或对象
表示打包过程中要忽略打包的库,防止使用时用户重复引入库
扩展项
externals:{<br> lodash:{<br> commonjs:'lodash' <br> }<br>}
使用ComonJS引入时,必须起名为lodash<br>
发布npm包
npm adduser 用户 密码 邮箱 <br>npm publish 把项目发布到npm的仓库里<br>
PWA的打包配置<br>Progressive Web Application
作用
是一种缓存技术,如果一个页面被访问过,当服务器断开,也能通过缓存浏览之前访问过的页面
PWA技术的底层是service worker
安装使用
1.安装插件<br>
npm install workbox-webpack-plugin
2.配置<br>
plugins:[newWorkboxPlugin.GenerateSW({<br> clientClaim: true,<br> skipWaiting: true<br>})]<br>
3.应用
<br>
TypeScript的打包配置
typescript
它是javascript的一个超集
规范了js语法,提升代码的可维护性
安装配置
安装
npm install ts-loader typescript -D
创建tsconfig.js文件,并配置
配置webpack.config.js
在typescript中使用lodash
@types/lodash
@types/lodash是typescript lodash对应的类型文件
安装了@types/lodash,在typescript中使用lodash这个库时,会有语法提示<br>比如调用lodash的方法时,会提示参数类型个数等
使用
安装
npm install @types/lodash --save-dev
引入
import * as _ from 'lodash'
类型文件查找
在github的TypeSearch里可以搜索
WebpackDevServer
devServer.proxy<br>实现请求转发
作用
proxy方便在开发时进行接口转发
配置
devServer.proxy配置
target 请求代理到哪个网址
secure:false 支持对https的请求进行转发
pathRewrite 路径重写
changeOrigin 访问有origin限制的网站时,可以突破限制
header 更改请求头 ,cookie可以在请求转发时模拟登陆和鉴权
注意
proxy配置只能写在devServer中,只能在测试环境生效,因为生产环境没有webpackDevServer
解决单页面路由问题
解决什么问题
单页应用
提示list页面不存在
webpackDevServer会以为你访问了后端一个叫list的页面,<br>但是网页只有index.html,并没有list.html,所以会提示list页面不存在
怎么解决
配置<br>devServer.historyApiFallback<br>
devServer: {<br> historyApiFallback: true<br> }
访问任何路径,都转发到index.html
注意
只在测试环境有效
原理
设置historyApiFallback: true时,<br>当访问localhost:8080/list时,后端服务器找不到这个页面<br>它都会转为请求根路径localhost:8080/,都会引入index.html页面,index.html里加载了main.js(这里面打包了业务代码)
示例图,找不到该路径,转为请求根路径
Eslint在webpack中的配置
eslint是一个规范代码的工具
如何使用eslint
命令行
安装配置
1、在项目中安装eslint , npm install eslint --save-dev;<br>2、执行npx eslint --init<br>3、选择相应的eslint选项<br>4、配置初始化生成的.eslintrc.js文件(可以更改.eslinttrc.js改变默认的配置)
使用
npx eslint src
用eslint检测src这个目录这个目录下的代码
IDE插件
在IDE中安装eslint插件
协同开发如何保证代码规范
1.团队成员都在命令行中检测代码存在的问题 npx eslint src
太麻烦
2.使用eslint-loader
webpack性能优化
提高打包速度
1. 跟上技术的迭代(Node,Npm,Yarn)<br>
新版本的webpack node等性能更好,打包更快
2. 在尽可能少的模块上应用Loader
合理的使用include和exclude,减少loader的作用范围,减少一些无用的分析可以加快打包速度
3. Plugin尽可能精简并确保可靠
尽量少的使用插件,并确保每一个插件的可靠性和性能(尽量使用官方插件,性能有保证)<br>在开发环境下,不要使用代码压缩插件,节约代码压缩这部分的打包时间
4.resolve参数合理配置
mainFiles: ['index','child']
引入一个文件时,先找该目录下有没有index.js,再找有没有child.js
不要配太多
这个配置一般不用
extensions: ['.js','.jsx']
这个作用是引入文件的时候省略后缀名
要合理配置,如果配置过多,需要查找很多次有没有这些后缀的文件,会有性能损耗<br>因此,建议只把.js .jsx .vue 配置到extensions里,资源类的引入时直接带着后缀引入<br>
alias:{<br> child: path.resolve(__dirname,'../src/child')<br>}
指定一个路径的别名,当引入child时,其实是引入的'../src/child'
5.使用DllPlugin提高打包速度
问题
引入很多第三方库,每次打包时,都要从node-modules中取出这些库,然后把这些库打包到源代码中<br>每次打包时重复打包这些第三方库,导致打包时间很长
解决思路
而这些第三方库几乎不怎么变化,第一次打包时把这些第三方库打包成一个文件存起来,<br>之后再打包时,直接用第一次打包存起来的代码<br>
目标:
1.第三方模块打包一次
2.引入第三方模块的时候,使用.dll文件引入
实现过程
webpack.dll.js
webpack.dll.js的作用
把第三方库单独打包,生成 .dll.js这样一个打包结果
1.通过library把第三方库的打包结果通过全局变量暴露出去
2.暴露完成后,借助DllPulgin,对暴露的代码进行分析,生成manifest.json这样一个映射文件
经过1 2步,使用npm run build:dll,<br>可以生成第三方库打包后的所有内容,<br>包括一个源代码vendor.dll.js 、一个映射文件vendor.manifest.json<br>完成目标中的第一步
打包命令配置
webpack.common.js
3.在webpack.common.js中,使用DllReferencePlugin这个插件,引入映射文件<br>引入进来的好处:在源代码中引入第三方库时,它回去dll目录下去找,如果发现这个模块之前被打包过了,就直接使用这个打包文件,就不用重复进行打包的分析流程
4.把dll目录下的.dll.js文件挂在到index.html上
使用add-asset-html-webpack-plugin <br>
6.控制包文件大小
没用的包去除掉,或者不要引入
把大文件拆分成小文件<br>
7.thread-loader , parallel-webpack , happypack 多进程打包
webpack默认是通过nodejs来运行的,所以它是单进程的打包<br>可以借助node的多进程提升打包速度
thread-loader , happypack
多进程打包
parallel-webpack
多页应用打包时,通过parallel-webpack对多个页面一起进行打包
8.合理使用sourceMap
打包时生成sourcemap,内容越详细,打包越慢,根据情况合理使用
9.结合stats分析打包结果
10.开发环境内存编译
在开发环境使用devServer,它在做打包时不会生成dist目录,它会把编译生成的文件放到内存里,内存的读取比硬盘快
11.开发环境无用插件剔除
比如在开发环境,不需要进行代码压缩,所以不要使用压缩代码的插件
多页面打包配置
多页面应用的本质就是创建多个HtmlWebpackPlugin
增加入口
创建htmlWebpackPlugin
chunks的作用
让index.html中只引入index.js,list.html中只引入list.js<br>如果不配置chunks,.html中会引入所有的.js文件
6.webpack底层原理及脚手架分析
如何编写一个loader
loader本质
它是一个函数
写一个loader<br>并使用
使用自己写的loader
配置webpack
写一个loader
编写一个loader
不能写成箭头函数,要写成声明式函数,<br>因为这个函数里需要用this,webpack在调loader时,会把this做一下变更,变更之后才能使用this里的一些方法
source是引入文件的内容,即源代码,loader可以针对该源代码进行一些加工,返回你想要的内容
使用多个loader
只需要在module.rules.use中配置多个loader即可,顺序是先右后左,先下后上
使用resolveLoader简化use中loader的配置
简化use中loader的配置
当引入loader时,先去node_modules下查找,如果找不到,再去loaders目录下查找
loader API
this.callback
this.callback
当需要把转换后的代码或者sourcemap等一些额外信息传递出去时,使用this.callback
this.query
通过options传递参数
传递参数
使用参数
使用参数
使用this.query可以拿到options传递的参数
loader-utils
分析参数
可以帮助分析options的参数
this.async
异步返回loader的结果
常见使用场景
捕获业务代码异常
可以对业务代码进行异常捕获,当检测到function时,把它放入try{function(){}}catch(e){}中
实现整个网站的国际化
伪代码
在业务代码中,使用{{title}}占位符,loader会根据不同的语言环境,把网站打包成不同的语言版本
如何编写一个plugin
插件本质
它是一个类
编写&使用插件
定义一个插件
一个插件的基本格式
使用插件
在webpack.config.js中配置plugins
以new 一个类实例的形式来使用
插件的参数
传递参数
webpack配置文件
用name给插件传递参数
接受参数
在插件中,options接受参数
compiler
compiler是一个webpack的实例
compiler.hooks
在某一个时刻会自动执行的一个函数
有哪些时刻?
常用时刻
emit
异步,表示在生成dist目录前的那一时刻
done
表示打包完成,异步
compile
同步
示例
hooks使用示例
compiler和compilation的区别
compiler中存放了配置的和打包的所有内容
compilation
只存放当前这一次打包的所有内容
调试
写webpack插件时,可以通过node的调试工具
--inspect
开启node的调试工具
--inspect-brk
在调试时,给第一行代码打断点
Bundler源码编写
模块分析
示例
Bundler实现
1.分析入口文件
示例只分析了一个入口文件
2.读取文件内容
3.把文件代码转换成AST(抽象语法树)
借助@babel/parser
AST
4.找AST里的type为ImportDeclaration的对象<br>分析依赖里的内容,找到依赖的相对路径和绝对路径之间的对应关系
借助@babel/traverse找到入口声明
找到所有的入口声明
5.对模块的源代码进行编译,把ES6语法转换成浏览器可以执行的语法
6.返回文件名、依赖关系、编译后的代码
Dependencies Graph
分析所有模块的依赖关系,用依赖图谱存放所有模块的依赖信息
实现Dependencies Graph
使用队列的方式,递归分析依赖中的依赖
生成代码
Dependencies Graph
生成代码
7.Create-React-App 和 Vue-Cli 3.0脚手架工具配置分析
通过CreateReactApp深入学习webpack配置
create-react-app<br>
脚手架工具<br>可以初始化一个项目,包括项目的目录和打包的webpack配置
facebook.github.io.create-react-app
配置文件隐藏了,会根据默认的配置进行进行打包
弹出配置文件
eject
npm run eject<br>可以弹出配置文件
编译命令
子主题
start
测试环境
build
生产环境
Vue CLI 3的配置方法
vue-cli 3
它是Vue 的脚手架工具
官方文档
https://cli.vuejs.org/zh/
https://cli.vuejs.org/zh/config/#vue-config-js
好处
它不会暴露配置文件,用户不需要知道webpack怎么配置,<br>
它提供了一些更加简洁的API,对webpack的配置进行了封装<br>它的底层还是通过webpack来进行打包的<br>
修改默认配置
只需要用户创建一个vue.config.js<br>在这个文件里通过API来修改默认配置
对比
react
配置起来更灵活
vue
配置起来更简单,学习成本低
0 条评论
下一页