Vue 项目 build 流程解析(webpack工具解析)
Vue 项目 build 流程解析(webpack工具解析)
注:本篇文章解析框架为 vue2.0
本篇文章通过解析简单的项目打包步骤试着去了解我们的 Vue
项目是怎么打包的。
build.js 干了什么
首先我们贴上 build.js
代码,方便后续解读:
'use strict'
// 版本校验解析
require('./check-versions')()process.env.NODE_ENV = 'production'
// 引用解析
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')// 执行build解析
const spinner = ora('building for production...')
spinner.start()rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {if (err) throw errwebpack(webpackConfig, (err, stats) => {spinner.stop()if (err) throw errprocess.stdout.write(stats.toString({colors: true,modules: false,children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.chunks: false,chunkModules: false}) + '\n\n')if (stats.hasErrors()) {console.log(chalk.red(' Build failed with errors.\n'))process.exit(1)}console.log(chalk.cyan(' Build complete.\n'))console.log(chalk.yellow(' Tip: built files are meant to be served over an HTTP server.\n' +' Opening index.html over file:// won\'t work.\n'))})
})
从上述代码备注中,我们将代码分开三部解析,分别为:版本校验解析、引用解析、项目打包
版本校验
执行的第一项就是版本校验,我们打开代码看看校验里面做了什么事情
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
首先是引入工具及配置,作用分别为:
- chalk:能打印颜色信息的插件,主要用来打印一些重要信息
- semver:语义化版本工具,在本文件中主要做版本提出及当前版本是否符合版本规则校验,详细用途可参照语义化版本控制模块-Semver
- packageConfig:
package
配置内容,本文件中主要获取node
版本与npm
版本 - shelljs:
shelljs
是脚本语言解析器,可以调用其中的方法做到执行底层操作命令,比如shell.cd('lib');
进入lib
目录,本文件中主要校验npm
环境
接下来是一个工具方法:
// 执行命令并返回执行结果
function exec (cmd) {return require('child_process').execSync(cmd).toString().trim()
}
初始化获取 node
信息及 npm
信息
const versionRequirements = [{name: 'node',currentVersion: semver.clean(process.version), // 提取当前node版本versionRequirement: packageConfig.engines.node // 提取框架版本要求}
]// 检查是否有npm运行环境
if (shell.which('npm')) {versionRequirements.push({name: 'npm',currentVersion: exec('npm --version'), // 获取当前npm版本versionRequirement: packageConfig.engines.npm // 获取框架版本要求})
}
最后执行我们的校验方法:
module.exports = function () {// 填充报错内容的数组const warnings = []// 循环校验(其实只有npm与node)当前环境版本与要求版本是否匹配for (let i = 0; i < versionRequirements.length; i++) {const mod = versionRequirements[i]// 判断当前环境版本是否符合项目版本要求,如果不符合,我们将报错提示放入报错数组中if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {warnings.push(mod.name + ': ' +chalk.red(mod.currentVersion) + ' should be ' +chalk.green(mod.versionRequirement))}}// 判断当前报错数组是否有内容(是否有环境不符合项目要求),如果有,打印报错内容并退出本次进程if (warnings.length) {console.log('')console.log(chalk.yellow('To use this template, you must update following to modules:'))console.log()for (let i = 0; i < warnings.length; i++) {const warning = warnings[i]console.log(' ' + warning)}console.log()process.exit(1)}
}
总的来说,check-version.js
只做了一件事,就是校验 npm
与 node
环境是否符合项目标准。
当然,如果我们想加一些其他的校验也可以,比如我们在打包前要求 webpack
版本低于 7.19.5
,我们可以这样配置:
首先我们在 package.json
配置项目要求版本:
"engines": {"node": ">= 6.0.0","npm": ">= 3.0.0","webpack": "< 7.19.5"
},
然后,我们在 check-version.js
中添加校验 webpack
的配置项:
const versionRequirements = [{name: 'node',currentVersion: semver.clean(process.version),versionRequirement: packageConfig.engines.node},{name: 'webpack',currentVersion: exec('npm webpack -v'),versionRequirement: packageConfig.engines.webpack}
]
这时候我们执行打包命令: npm run build
可以看到报错如下:
因为此时我们 webpack
版本是 7.20.5
,而项目要求版本是小于 7.19.5
所以,编译失败。
引用解析
做完版本校验之后,我们开始执行打包逻辑,首先最重要的事就是改变当前的环境为 production
,这是为了确保有些生产环境不需要打包进去的工具或者逻辑被打包(比如上一篇讲的 mockjs
),其次便是一些工具与配置项引入:
// 将当前环境置为生产环境
process.env.NODE_ENV = 'production'const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')// 创建一个动态的打包进度文本打印
const spinner = ora('building for production...')
spinner.start()
这一块引入的工具及配置功能如下
- ora:这是一个优雅的终端旋转器,简单的来说就是可以在终端显示文本前加一个旋转的棍儿,用来标识当前正在执行状态,缓解使用者等待情绪
- rimraf:这个就是一个包装
rm -rf
命令的工具,工具使用方法为rimraf(path, callback)
- path:处理文件路径工具
- chalk:这个上面有讲,有颜色的打印插件
- webpack:打包工具,后面会讲到
- config:项目配置,里面包含打包路径、是否开启分析图等
- webpackConfig:
webpack
配置,其中包含webpack
打包入口、出口、插件等
项目打包
最后要执行的便是核心的一步:项目打包,打包工具为 webpack
,其配置及作用我将在后面说明,首先看一下打包代码:
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {if (err) throw errwebpack(webpackConfig, (err, stats) => {spinner.stop()if (err) throw errprocess.stdout.write(stats.toString({colors: true,modules: false,children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.chunks: false,chunkModules: false}) + '\n\n')if (stats.hasErrors()) {console.log(chalk.red(' Build failed with errors.\n'))process.exit(1)}console.log(chalk.cyan(' Build complete.\n'))console.log(chalk.yellow(' Tip: built files are meant to be served over an HTTP server.\n' +' Opening index.html over file:// won\'t work.\n'))})
})
打包分为三部:
- 打包目录下文件删除:使用
rm
方法执行删除命令,删除掉打包目录下面的文件夹 - 执行打包:
webpack
进行打包 - 打包结果展示:关闭
spinner
展示,打印提示及报错
webpack 配置了什么
首先要说到的是 webpack
是什么?
官网描述:webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
简单地说就是从一个或者多个入口开始,顺藤摸瓜的将所有引用集合然后压缩,在加载界面时能够进行模块式的加载一个 js
文件与其相关资源文件。在 webpack
中也可以配置不同的模块来适配多种语言,例如解析 TypeScript
、Sass
等,也可以对我们 js
代码进行兼容性处理。
webpack
核心配置如下:
- Entry:入口,打包入口文件,递归解析依赖的源头
- Module:模块 ,
webpack
模块配置,用于引入工具模块用作语言翻译 - resolve:模块规则,配置以什么规则寻找模块,配置路径映射等
- output:输出结果,在
Webpack
经过一系列处理并得出最终想要的代码后输出结果
在 vue
项目中,webpack
打包的配置分为两块: webpack.base.conf.js
及 webpack.prod.conf.js
,前者为公共配置,后者为打包的配置。
公共配置
路径配置
首先是三个打包路径相关配置:
context: path.resolve(__dirname, '../'),
entry: {app: './src/main.js'
},
output: {path: config.build.assetsRoot,filename: '[name].js',publicPath: process.env.NODE_ENV === 'production'? config.build.assetsPublicPath: config.dev.assetsPublicPath
},
- context:默认执行启动
webpack
时所在的当前工作目录,配置中所有路径以该路径为基础,当前以项目目录为基础 - entry:配置模块入口,当前项目入口为
src
下main.js
- output:该项配置打包后文件存放及读取位置
path
为打包位置,默认为static
目录filename
配置入口文件名,name
为entry
对象key
,例如上面生成app.js
, 如果有多个入口,也会生成多个文件publicPath
为静态资源的路径,默认为/
,可配置为其他外部地址
依赖解析
resolve
配置项
resolve: {extensions: ['.js', '.vue', '.json'],alias: {'vue$': 'vue/dist/vue.esm.js','@': resolve('src'),}
},
两个配置作用如下:
- extensions:当我们引入组件
demo.vue
时,我们可以import demo from demo
也可以import demo from demo.vue
那么系统怎么知道引入的是什么文件呢?那就依赖于这个配置项,在引入的时候,系统会先寻找demo.js
发现没有之后会寻找demo.vue
以此按顺序比对 - alias:该配置项为路径映射,在上述例子中配置项配置两个映射:
vue$
、@
,当我们要引入src/component/demo.vue
,则可写为@/component/demo.vue
文件解析
文件解析的目的有三个:
- 对于浏览器来说,是无法识别
.vue
、.less
这种文件的,所以我们在编译的时候需要工具将其转化为.js
文件 js
代码在不同浏览器上需要进行兼容性处理- 引入
Eslint
执行校验
配置模板分成三块:
- 匹配文件:通过
test
、include
、exclude
匹配需要解析的文件 - 解析规则:通过
use
配置解析模块数组,也可通过loader
配置一组解析模块,解析顺序从右往左按顺序处理 - 顺序调整:通过
enforce
配置,修改默认执行顺序,可以将一个loader
执行顺序放置在最前或者最后
module: {rules: [...(config.dev.useEslint ? [createLintingRule()] : []),{test: /\.vue$/,loader: 'vue-loader',options: vueLoaderConfig},{test: /\.js$/,loader: 'babel-loader',include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]}, // babel解析js文件,适配js代码兼容性{test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,loader: 'url-loader',options: {limit: 10000,name: utils.assetsPath('img/[name].[hash:7].[ext]')}}, // 将8KB以下的图片进行base64转换,减少界面加载图片时间{test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,loader: 'url-loader',options: {limit: 10000,name: utils.assetsPath('media/[name].[hash:7].[ext]')}},// 将8KB以下的媒体文件进行base64转换,减少界面加载视频时间{test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,loader: 'url-loader',options: {limit: 10000,name: utils.assetsPath('fonts/[name].[hash:7].[ext]')}}, // 将8KB以下的图标文件进行base64转换,减少界面加载图标文件时间{test: /\.less$/,loader: 'style-loader!css-loader!less-loader'} // 将less文件分别通过less-loader、css-loader、style-loader流水线对less文件进行解析]
},
首先第一项配置的是 ESlint
解析配置,由于 Eslint
配置并不是必须的,所以我们通过配置项判断是否解析,Eslint
配置如下:
const createLintingRule = () => ({// 匹配.js或者.vue文件test: /\.(js|vue)$/,// 解析模块为 eslint-loaderloader: 'eslint-loader',// 将解析顺序排至最前enforce: 'pre',// 解析范围为src文件夹下及test文件夹下include: [resolve('src'), resolve('test')],// 指定错误报告的格式规范options: {formatter: require('eslint-friendly-formatter'),emitWarning: !config.dev.showEslintErrorsInOverlay}
})
第二项为 vue
文件的解析,解析配置比较复杂,这次暂时不做详细说明
其他配置规则已在备注中说明
打包配置
打包配置内容合并了公共配置与打包需要的配置,打包配置主要为 Plugins
扩展功能,所以其他配置暂不做介绍
webpack.DefinePlugin
配置编译时的全局常量
new webpack.DefinePlugin({'process.env': env
}),
uglifyjs-webpack-plugin
配置 js
文件打包压缩,将 js
解析压缩成为浏览器可识别的小文件
new UglifyJsPlugin({// uglify配置项,配置是否压缩为falseuglifyOptions: {compress: {warnings: false}},// 是否将错误信息映射到模块(设置为true将会减慢编译速度)sourceMap: config.build.productionSourceMap,// 使用多进程提高构建速度parallel: true
}),
extract-text-webpack-plugin
在没有该项配置打包时,css
将会被打包到 js
文件中,这样我们更新 css
的时候,也会再编译 js
,反之也是相同,所以我们需要将编译后的 css
与 js
分离开来(感觉这个配置对于生产环境并没有优化点)
new ExtractTextPlugin({// 生产的css文件名filename: utils.assetsPath('css/[name].[contenthash].css'),// 支持提取异步引入的cssallChunks: true,
}),
optimize-css-assets-webpack-plugin
这个工具主要是做 css
压缩用的
new OptimizeCSSPlugin({cssProcessorOptions: config.build.productionSourceMap? { safe: true, map: { inline: false } }: { safe: true }
}),
html-webpack-plugin
这个工具主要是去创建一个入口的 html
文件,文件会引入 webpack
打包生成的 output
文件,如果有多个 output
也会引入多个文件,配置及作业如下所示
new HtmlWebpackPlugin({filename: config.build.index, // 文件路径及名称template: 'index.html', // 本地模板文件位置,这里引入项目内index.htmlinject: true, // 向template注入所有静态资源,项目中将打包后生成所有的js都引入进index.html// 传递 html-minifier 选项给 minify 输出minify: {removeComments: true, // 去除html注释collapseWhitespace: true, // 折叠文本节点中的空白,如果置为false,html文件将会缩进显示removeAttributeQuotes: true // 尽可能删除属性周围的引号// more options:// https://github.com/kangax/html-minifier#options-quick-reference},// necessary to consistently work with multiple chunks via CommonsChunkPluginchunksSortMode: 'dependency' // thunk插入到html的排列顺序,此处规定按照CommonsChunkPlugin规则排序
}),
webpack.HashedModuleIdsPlugin
这个工具是为了解决打包污染问题,当我们只修改部分文件时,vue
会将所有文件都重新编译一次,这样每次编译代码量会变大,浏览器需要重新获取所有的静态文件,而 webpack.HashedModuleIdsPlugin
以模块相对路径生成 hash
作为模块 id
,做到只更新变动的代码
new webpack.HashedModuleIdsPlugin()
webpack.optimize.ModuleConcatenationPlugin
这个工具能够预编译所有模块到一个闭包中,以提升代码在浏览器中执行速度
new webpack.optimize.ModuleConcatenationPlugin()
webpack.optimize.CommonsChunkPlugin
该模块主要用作公共模块的拆分独立,在进入系统的时候最开始加载一次,后续都从缓存中获取,减少大量的静态文件获取,提高加载界面速度,下面是项目内拆分规则:
// split vendor js into its own file
// 将node_modules内容拆分到vendor中
new webpack.optimize.CommonsChunkPlugin({name: 'vendor',minChunks (module) {// any required modules inside node_modules are extracted to vendorreturn (module.resource &&/\.js$/.test(module.resource) &&module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0)}
}),
// 该模块与上述HashedModuleIdsPlugin共同作用避免公共chunk改变
new webpack.optimize.CommonsChunkPlugin({name: 'manifest',minChunks: Infinity
}),
// 从代码中提取共享模块,并将其绑定在一个单独的模块中
new webpack.optimize.CommonsChunkPlugin({name: 'app',async: 'vendor-async',children: true,minChunks: 3
}),
copy-webpack-plugin
该工具执行打包流程的最后一步,将打包好的文件赋值到目标目录中:
new CopyWebpackPlugin([{from: path.resolve(__dirname, '../static'),to: config.build.assetsSubDirectory,ignore: ['.*']}
])
compression-webpack-plugin
该工具为可选工具,主要作用为准备资源的压缩版本以通过 Content-Encoding
为其提供服务,默认关闭,可在 config/index.js
中打开:
webpackConfig.plugins.push(new CompressionWebpackPlugin({asset: '[path].gz[query]',algorithm: 'gzip',test: new RegExp('\\.(' +config.build.productionGzipExtensions.join('|') +')$'),threshold: 10240,minRatio: 0.8})
)
注意,开启该功能需要下载组件,由于高版本不兼容,建议下载老版本组件:
npm install --save-dev compression-webpack-plugin@1.1.12
webpack-bundle-analyzer
该工具为可视化资源分析工具,能够比较详细的知道那一块代码内存占用较大,通常用于发布包大小优化:
if (config.build.bundleAnalyzerReport) {const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPluginwebpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
如果想要看到分析图,需要执行以下命令:
npm run build --report
执行后就可以看到分析图了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZQ25bGar-1639550253515)(./image/analyzer.png)]
有关 vue
项目的 build
分析基本完成,有不对的地方还请多多指正。
threshold: 10240,
minRatio: 0.8
})
)
注意,开启该功能需要下载组件,由于高版本不兼容,建议下载老版本组件:```shell
npm install --save-dev compression-webpack-plugin@1.1.12
webpack-bundle-analyzer
该工具为可视化资源分析工具,能够比较详细的知道那一块代码内存占用较大,通常用于发布包大小优化:
if (config.build.bundleAnalyzerReport) {const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPluginwebpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
如果想要看到分析图,需要执行以下命令:
npm run build --report
执行后就可以看到分析图了:
有关 vue
项目的 build
分析基本完成,有不对的地方还请多多指正。
Vue 项目 build 流程解析(webpack工具解析)相关推荐
- Vue 项目搭建流程和使用大全
Vue 项目搭建流程及项目中遇到的问题 项目搭建流程 1.使用vue cli 搭建项目框架 cnpm install -g vue-cli 安装vue cli 脚手架 vue init webpack ...
- Vue进阶(幺捌柒):vue项目build报错的解决办法(ERROR in static/js/***.js from UglifyJs)
文章目录 一.前言 二.问题分析 三.问题解决 四.拓展阅读 一.前言 Vue项目编译过程中,出现如下错误信息: ERROR in static/js/vendor.f1c68aa2d5e85847d ...
- 初始化一个vue项目的流程
为什么80%的码农都做不了架构师?>>> 一.安装 nodejs 建议安装 v 7.9.0 本人使用的此版本比较好 二.安装 安装vue.创建vue项目 # 全局安装 vue- ...
- Vue项目build后静态资源文件路径或新建文件夹图片路径找不到的问题
问题描述:使用vue-cli脚手架工具生成的vue项目,使用npm run build后生成的文件直接双击打开白屏一片. 解决方案: 第一步:修改build文件夹下utils.js,在以下位置加入 i ...
- vue项目调用通用组件_详细解析:uniapp项目|vue组件形式实现的科技感loading纯CSS动效...
前言 本人是一枚并不安分守己的后端程序猿,一直对前端开发"垂涎三尺",所以,一有机会就会"不务正业"一番.最近,发现了一个非常好的学习资料,于是乎,我的老毛病又 ...
- 离职后才搞懂vue项目开发流程中的疑惑点
在离职的最后一个月,帮两位同事申请加薪,确切的说,申请加薪是导火索,我被扣上了哄抬同事工资以提高自己工资的帽子,在推动前后端分离工作中处处碰壁,点燃了压抑许久的离职冲动,领导培养自己四五年,不让声张, ...
- 搭建SpringBoot+Vue 项目 完整流程
文章目录 一.创建后端 SpringBoot 项目 二.Vue 创建前端项目 三.SpringBoot 集成 MyBatis 四.前后端联调 一.创建后端 SpringBoot 项目 创建过程 略 创 ...
- 解决vue项目build的时候报错Warning: Accessing non-existent property ‘cat‘ of module exports inside circular de
* 正在执行任务: npm run build > selection-tool@1.0.0 build > node build/build.js - building for pro ...
- 【前端】Vue项目开发流程
项目初始化 安装Vue 脚手架 npm install -g @vue/cli 通过Vue脚手架创建项目 vue ui i.创建-选择路径; ii.输入项目名称-git初始化信息; iii.预设(手动 ...
最新文章
- python获取耗时的shell_python获取耗时的shell_关于Linux:在shell中获取程序执行时间...
- EDM营销中HTML邮件设计方法和技巧
- Ubuntu 11.04上搭建Android开发环境
- 深度剖析:CDN内容分发网络技术原理--转载
- 【职场建议】开发转算法,我们应该如何准备(过来人的肺腑之言)
- html表单ui图片,semantic-ui 表单(示例代码)
- EF Core 小坑:DbContextPool 会引起数据库连接池连接耗尽
- ZooKeeper未授权访问漏洞记录(影响范围:全版本,端口:2181)
- [Unity] ACT 战斗系统学习 4:重构前的第三人称控制器
- lssvm回归 matlab,lssvm回归预测的程序运行不了 求高手修改指点
- leecode 树是否是平衡树 java
- 转载 Log4j2在WEB项目中配置
- Dxg——Raspberry Pi Pico python 开发笔记整理分类合集【所有的相关记录,都整理在此】
- 算法与数据结构(基于C语言)中线性表的快速排序快速查找
- linux如何卸载金山安全终端,卸载和释放-文档中心-金山云
- Woff2字体404错误
- Maxwell参数化建模
- DuckDuckGo将与整合Apple Maps有更丰富的地图信息及隐私
- 分享10个最佳Linux VPS服务器托管
- 3个APP海外推广方式,不走寻常路