700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > vue-cli@2.9.3 搭建的webpack项目工程

vue-cli@2.9.3 搭建的webpack项目工程

时间:2023-08-19 22:42:22

相关推荐

vue-cli@2.9.3 搭建的webpack项目工程

前言

已经有很多分析Vue-cli搭建工程的文章,为什么自己还要写一遍呢。学习就好比是座大山,人们沿着不同的路登山,分享着自己看到的风景。你不一定能看到别人看到的风景,体会到别人的心情。只有自己去登山,才能看到不一样的风景,体会才更加深刻。

正文从这里开始~

使用vue-cli初始化webpack工程

// # 安装npm install -g vue-cli// 安装完后vue命令就可以使用了。实际上是全局注册了vue、vue-init、vue-list几个命令// # ubuntu 系统下// [vue-cli@2.9.3] link /usr/local/bin/vue@ -> /usr/local/lib/node_modules/vue-cli/bin/vue// [vue-cli@2.9.3] link /usr/local/bin/vue-init@ -> /usr/local/lib/node_modules/vue-cli/bin/vue-init// [vue-cli@2.9.3] link /usr/local/bin/vue-list@ -> /usr/local/lib/node_modules/vue-cli/bin/vue-listvue list// 可以发现有browserify、browserify-simple、pwa、simple、webpack、webpack-simple几种模板可选,这里选用webpack。// # 使用 vue initvue init <template-name> <project-name>// # 例子vue init webpack analyse-vue-cli

更多vue-cli如何工作的可以查看这篇文章vue-cli是如何工作的,或者分析Vue-cli源码查看这篇走进Vue-cli源码,自己动手搭建前端脚手架工具,再或者直接查看vue-cli github仓库源码

如果对webpack还不是很了解,可以查看webpack官方文档中的概念,虽然是最新版本的,但概念都是差不多的。

package.json

分析一个项目,一般从package.json的命令入口scripts开始。

"scripts": {// dev webpack-dev-server --inline 模式 --progress 显示进度 --config 指定配置文件(默认是webpack.config.js)"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js","start": "npm run dev",// jest测试"unit": "jest --config test/unit/jest.conf.js --coverage",// e2e测试"e2e": "node test/e2e/runner.js",// 运行jest测试和e2e测试"test": "npm run unit && npm run e2e",// eslint --ext 指定扩展名和相应的文件"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",// node 执行build/build.js文件"build": "node build/build.js"},

Npm Script底层实现原理是通过调用Shell去运行脚本命令。npm run start等同于运行npm run dev

Npm Script还有一个重要的功能是能运行安装到项目目录里的node_modules里的可执行模块。

例如在通过命令npm i -D webpack-dev-serverwebpack-dev-server安装到项目后,是无法直接在项目根目录下通过命令webpack-dev-server去执行webpack-dev-server构建的,而是要通过命令./node_modules/.bin/webpack-dev-server去执行。

Npm Script能方便的解决这个问题,只需要在scripts字段里定义一个任务,例如:

"dev":"webpack-dev-server--inline--progress--configbuild/webpack.dev.conf.js"

Npm Script会先去项目目录下的node_modules中寻找有没有可执行的webpack-dev-server文件,如果有就使用本地的,如果没有就使用全局的。 所以现在执行webpack-dev-server启动服务时只需要通过执行npm run dev去实现。

再来看下 npm run dev

webpack-dev-server其实是一个node.js的应用程序,它是通过JavaScript开发的。在命令行执行npm run dev命令等同于执行node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --progress --config build/webpack.dev.conf.js。你可以试试。

更多package.json的配置项,可以查看阮一峰老师的文章 package.json文件

npm run dev指定了build/webpack.dev.conf.js配置去启动服务,那么我们来看下这个文件做了什么。

build/webpack.dev.conf.jswebpack开发环境配置

这个文件主要做了以下几件事情:

1、引入各种依赖,同时也引入了config文件夹下的变量和配置,和一个工具函数build/utils.js

2、合并build/webpack.base.conf.js配置文件,

3、配置开发环境一些devServerplugin等配置,

4、最后导出了一个Promise,根据配置的端口,寻找可用的端口来启动服务。

具体可以看build/webpack.dev.conf.js这个文件注释。

'use strict'// 引入工具函数const utils = require('./utils')// 引入webpackconst webpack = require('webpack')// 引入config/index.js配置const config = require('../config')// 合并webpack配置const merge = require('webpack-merge')const path = require('path')// 基本配置const baseWebpackConfig = require('./webpack.base.conf')// 拷贝插件const CopyWebpackPlugin = require('copy-webpack-plugin')// 生成html的插件const HtmlWebpackPlugin = require('html-webpack-plugin')// 友好提示的插件 /geowarin/friendly-errors-webpack-pluginconst FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')// 查找可用端口 // github仓库 /indexzero/node-portfinderconst portfinder = require('portfinder')// process模块用来与当前进程互动,可以通过全局变量process访问,不必使用require命令加载。它是一个EventEmitter对象的实例。// 后面有些process模块用到的,所以这里统一列举下。// 更多查看这篇阮一峰的这篇文章 /nodejs/process.html// process对象提供一系列属性,用于返回系统信息。// process.pid:当前进程的进程号。// process.version:Node的版本,比如v0.10.18。// process.platform:当前系统平台,比如Linux。// process.title:默认值为“node”,可以自定义该值。// process.argv:当前进程的命令行参数数组。// process.env:指向当前shell的环境变量,比如process.env.HOME。// process.execPath:运行当前进程的可执行文件的绝对路径。// process.stdout:指向标准输出。// process.stdin:指向标准输入。// process.stderr:指向标准错误。// process对象提供以下方法:// process.exit():退出当前进程。// process.cwd():返回运行当前脚本的工作目录的路径。_// process.chdir():改变工作目录。// process.nextTick():将一个回调函数放在下次事件循环的顶部。// hostconst HOST = process.env.HOST// 端口const PORT = process.env.PORT && Number(process.env.PORT)// 合并基本的webpack配置const devWebpackConfig = merge(baseWebpackConfig, {module: {// cssSourceMap这里配置的是truerules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })},// cheap-module-eval-source-map is faster for development// 在开发环境是cheap-module-eval-source-map选项更快// 这里配置的是cheap-module-eval-source-map// 更多可以查看中文文档:/configuration/devtool/#devtool// 英文 /configuration/devtool/#developmentdevtool: config.dev.devtool,// these devServer options should be customized in /config/index.jsdevServer: {// 配置在客户端的日志等级,这会影响到你在浏览器开发者工具控制台里看到的日志内容。// clientLogLevel 是枚举类型,可取如下之一的值 none | error | warning | info。// 默认为 info 级别,即输出所有类型的日志,设置成 none 可以不输出任何日志。clientLogLevel: 'warning',// historyApiFallback boolean object 用于方便的开发使用了 HTML5 History API 的单页应用。// 可以简单true 或者 任意的 404 响应可以提供为 index.html 页面。historyApiFallback: {rewrites: [// config.dev.assetsPublicPath 这里是 /{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },],},// 开启热更新hot: true,// contentBase 配置 DevServer HTTP 服务器的文件根目录。// 默认情况下为当前执行目录,通常是项目根目录,所有一般情况下你不必设置它,除非你有额外的文件需要被 DevServer 服务。contentBase: false, // since we use CopyWebpackPlugin.// compress 配置是否启用 gzip 压缩。boolean 为类型,默认为 false。compress: true,// host// 例如你想要局域网中的其它设备访问你本地的服务,可以在启动 DevServer 时带上 --host 0.0.0.0// 或者直接设置为 0.0.0.0// 这里配置的是localhosthost: HOST || config.dev.host,// 端口号 这里配置的是8080port: PORT || config.dev.port,// 打开浏览器,这里配置是不打开falseopen: config.dev.autoOpenBrowser,// 是否在浏览器以遮罩形式显示报错信息 这里配置的是trueoverlay: config.dev.errorOverlay? { warnings: false, errors: true }: false,// 这里配置的是 /publicPath: config.dev.assetsPublicPath,// 代理 这里配置的是空{},有需要可以自行配置proxy: config.dev.proxyTable,// 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。// 开启后一般非常干净只有类似的提示 Your application is running here: http://localhost:8080quiet: true, // necessary for FriendlyErrorsPlugin// webpack-dev-middleware// watch: false,// 启用 Watch 模式。这意味着在初始构建之后,webpack 将继续监听任何已解析文件的更改。Watch 模式默认关闭。// webpack-dev-server 和 webpack-dev-middleware 里 Watch 模式默认开启。// Watch 模式的选项watchOptions: {// 或者指定毫秒为单位进行轮询。// 这里配置为falsepoll: config.dev.poll,}// 更多查看中文文档:/configuration/watch/#src/components/Sidebar/Sidebar.jsx},plugins: [// 定义为开发环境new webpack.DefinePlugin({// 这里是 { NODE_ENV: '"development"' }'process.env': require('../config/dev.env')}),// 热更新插件new webpack.HotModuleReplacementPlugin(),// 热更新时显示具体的模块路径new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.// 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。new webpack.NoEmitOnErrorsPlugin(),// github仓库 /ampedandwired/html-webpack-pluginnew HtmlWebpackPlugin({filename: 'index.html',template: 'index.html',// inject 默认值 true,script标签位于html文件的 body 底部// body 通true, header, script 标签位于 head 标签内// false 不插入生成的 js 文件,只是单纯的生成一个 html 文件inject: true}),// copy custom static assets// 把static资源复制到相应目录。new CopyWebpackPlugin([{// 这里是 staticfrom: path.resolve(__dirname, '../static'),// 这里是 staticto: config.dev.assetsSubDirectory,// 忽略.开头的文件。比如这里的.gitkeep,这个文件是指空文件夹也提交到gitignore: ['.*']}])]})// 导出一个promisemodule.exports = new Promise((resolve, reject) => {// process.env.PORT 可以在命令行指定端口号,比如PORT=2000 npm run dev,那访问就是http://localhost:2000// config.dev.port 这里配置是 8080portfinder.basePort = process.env.PORT || config.dev.port// 以配置的端口为基准,寻找可用的端口,比如:如果8080占用,那就8081,以此类推// github仓库 /indexzero/node-portfinderportfinder.getPort((err, port) => {if (err) {reject(err)} else {// publish the new Port, necessary for e2e testsprocess.env.PORT = port// add port to devServer configdevWebpackConfig.devServer.port = port// Add FriendlyErrorsPlugindevWebpackConfig.plugins.push(new FriendlyErrorsPlugin({compilationSuccessInfo: {messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],},// notifyOnErrors 这里配置是 true// onErrors 是一个函数,出错输出错误信息,系统原生的通知onErrors: config.dev.notifyOnErrors? utils.createNotifierCallback(): undefined}))resolve(devWebpackConfig)}})})

build/utils.js工具函数

上文build/webpack.dev.conf.js提到引入了build/utils.js工具函数。

该文件主要写了以下几个工具函数:

1、assetsPath返回输出路径,

2、cssLoaders返回相应的css-loader配置,

3、styleLoaders返回相应的处理样式的配置,

4、createNotifierCallback创建启动服务时出错时提示信息回调。

具体配置可以看该文件注释:

'use strict'const path = require('path')// 引入配置文件config/index.jsconst config = require('../config')// 提取css的插件const ExtractTextPlugin = require('extract-text-webpack-plugin')// 引入package.json配置const packageConfig = require('../package.json')// 返回路径exports.assetsPath = function (_path) {const assetsSubDirectory = process.env.NODE_ENV === 'production'// 二级目录 这里是 static? config.build.assetsSubDirectory// 二级目录 这里是 static: config.dev.assetsSubDirectory// 生成跨平台兼容的路径// 更多查看Node API链接:/api/path.html#path_path_posixreturn path.posix.join(assetsSubDirectory, _path)}exports.cssLoaders = function (options) {// 作为参数传递进来的options对象// {// // sourceMap这里是true// sourceMap: true,// // 是否提取css到单独的css文件// extract: true,// // 是否使用postcss// usePostCSS: true// }options = options || {}const cssLoader = {loader: 'css-loader',options: {sourceMap: options.sourceMap}}const postcssLoader = {loader: 'postcss-loader',options: {sourceMap: options.sourceMap}}// generate loader string to be used with extract text plugin// 创建对应的loader配置function generateLoaders (loader, loaderOptions) {// 是否使用usePostCSS,来决定是否采用postcssLoaderconst loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]if (loader) {loaders.push({loader: loader + '-loader',// 合并 loaderOptions 生成optionsoptions: Object.assign({}, loaderOptions, {sourceMap: options.sourceMap})})}// Extract CSS when that option is specified// (which is the case during production build)if (options.extract) {// 如果提取使用ExtractTextPlugin插件提取// 更多配置 看插件中文文档:/plugins/extract-text-webpack-plugin/return ExtractTextPlugin.extract({// 指需要什么样的loader去编译文件// loader 被用于将资源转换成一个 CSS 导出模块 (必填)use: loaders,// loader(例如 'style-loader')应用于当 CSS 没有被提取(也就是一个额外的 chunk,当 allChunks: false)fallback: 'vue-style-loader'})} else {return ['vue-style-loader'].concat(loaders)}}// https://vue-/en/configurations/extract-css.htmlreturn {css: generateLoaders(),postcss: generateLoaders(),less: generateLoaders('less'),// sass indentedSyntax 语法缩进,类似下方格式// #main// color: blue// font-size: 0.3emsass: generateLoaders('sass', { indentedSyntax: true }),scss: generateLoaders('sass'),stylus: generateLoaders('stylus'),styl: generateLoaders('stylus')}}// Generate loaders for standalone style files (outside of .vue)// 最终会返回webpack css相关的配置exports.styleLoaders = function (options) {// {// // sourceMap这里是true// sourceMap: true,// // 是否提取css到单独的css文件// extract: true,// // 是否使用postcss// usePostCSS: true// }const output = []const loaders = exports.cssLoaders(options)for (const extension in loaders) {const loader = loaders[extension]output.push({test: new RegExp('\\.' + extension + '$'),use: loader})}return output}// npm run dev 出错时, FriendlyErrorsPlugin插件 配置 onErrors输出错误信息exports.createNotifierCallback = () => {// 'node-notifier'是一个跨平台系统通知的页面,当遇到错误时,它能用系统原生的推送方式给你推送信息const notifier = require('node-notifier')return (severity, errors) => {if (severity !== 'error') returnconst error = errors[0]const filename = error.file && error.file.split('!').pop()notifier.notify({title: packageConfig.name,message: severity + ': ' + error.name,subtitle: filename || '',icon: path.join(__dirname, 'logo.png')})}}

build/webpack.base.conf.jswebpack基本配置文件

上文build/webpack.dev.conf.js提到引入了build/webpack.base.conf.js这个webpack基本配置文件。

这个文件主要做了以下几件事情:

1、引入各种插件、配置等,其中引入了build/vue-loader.conf.js相关配置,

2、创建eslint规则配置,默认启用,

3、导出webpack配置对象,其中包含context,入口entry,输出outputresolvemodule下的rules(处理对应文件的规则),和node相关的配置等。

具体可以看这个文件注释:

// 使用严格模式,更多严格模式可以查看// [阮一峰老师的es标准入门](/?search=%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F&x=0&y=0#docs/function#%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F)'use strict'const path = require('path')// 引入工具函数const utils = require('./utils')// 引入配置文件,也就是config/index.js文件const config = require('../config')// 引入vue-loader的配置文件const vueLoaderConfig = require('./vue-loader.conf')// 定义获取绝对路径函数function resolve (dir) {return path.join(__dirname, '..', dir)}// 创建eslint配置const createLintingRule = () => ({test: /\.(js|vue)$/,loader: 'eslint-loader',// 执行顺序,前置,还有一个选项是post是后置// 把 eslint-loader 的执行顺序放到最前面,防止其它 Loader 把处理后的代码交给 eslint-loader 去检查enforce: 'pre',// 包含文件夹include: [resolve('src'), resolve('test')],options: {// 使用友好的eslint提示插件formatter: require('eslint-friendly-formatter'),// eslint报错提示是否显示以遮罩形式显示在浏览器中// 这里showEslintErrorsInOverlay配置是falseemitWarning: !config.dev.showEslintErrorsInOverlay}})module.exports = {// 运行环境的上下文,就是实际的目录,也就是项目根目录context: path.resolve(__dirname, '../'),// 入口entry: {app: './src/main.js'},// 输出output: {// 路径 这里是根目录下的distpath: config.build.assetsRoot,// 文件名filename: '[name].js',publicPath: process.env.NODE_ENV === 'production'// 这里是 /,但要上传到github pages等会路径不对,需要修改为./? config.build.assetsPublicPath// 这里配置是 /: config.dev.assetsPublicPath},// Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块,Resolve 配置 Webpack 如何寻找模块所对应的文件。resolve: {// 配置了这个,对应的扩展名可以省略extensions: ['.js', '.vue', '.json'],alias: {// 给定对象的键后的末尾添加 $,以表示精准匹配 node_modules/vue/dist/vue.esm.js// 引用 import Vue from 'vue'就是引入的这个文件最后export default Vue 导出的Vue;// 所以这句可以以任意大写字母命名 比如:import V from 'vue''vue$': 'vue/dist/vue.esm.js',// src别名 比如 :引入import HelloWorld from '@/components/HelloWorld''@': resolve('src'),}},// 定义一些文件的转换规则module: {rules: [// 是否使用eslint 这里配置是true...(config.dev.useEslint ? [createLintingRule()] : []),{test: /\.vue$/,// vue-loader中文文档:https://vue-loader-/zh-cn/loader: 'vue-loader',options: vueLoaderConfig},{// js文件使用babel-loader转换test: /\.js$/,loader: 'babel-loader',include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]},{// 图片文件使用url-loader转换test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,loader: 'url-loader',options: {// 限制大小10000B(bytes)以内,转成base64编码的dataURL字符串limit: 10000,// 输出路径 img/名称.7位hash.扩展名name: utils.assetsPath('img/[name].[hash:7].[ext]')}},{// 视频文件使用url-loader转换test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,loader: 'url-loader',options: {limit: 10000,name: utils.assetsPath('media/[name].[hash:7].[ext]')}},{test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,loader: 'url-loader',options: {limit: 10000,name: utils.assetsPath('fonts/[name].[hash:7].[ext]')}}]},// 这里的node是一个对象,其中每个属性都是 Node.js 全局变量或模块的名称,每个 value 是以下其中之一// empty 提供空对象。// false 什么都不提供。// 更多查看 中文文档:/configuration/node/node: {// prevent webpack from injecting useless setImmediate polyfill because Vue// source contains it (although only uses it if it's native).// 防止webpack注入一些polyfill 因为Vue已经包含了这些。setImmediate: false,// prevent webpack from injecting mocks to Node native modules// that does not make sense for the clientdgram: 'empty',fs: 'empty',net: 'empty',tls: 'empty',child_process: 'empty'}}

build/vue-loader.conf.jsvue-loader配置文件

上文build/webpack.dev.conf.js提到引入了build/vue-loader.conf.js

这个文件主要导出了一份Vue-loader的配置,

主要有:loaderscssSourceMapcacheBustingtransformToRequire

具体看该文件注释:

'use strict'const utils = require('./utils')const config = require('../config')const isProduction = process.env.NODE_ENV === 'production'const sourceMapEnabled = isProduction// 这里是true? config.build.productionSourceMap// 这里是true: config.dev.cssSourceMap// 更多配置 可以查看vue-loader中文文档:https://vue-loader-/zh-cn/module.exports = {// cssLoaders 生成相应loader配置,具体看utils文件中的cssLoaderloaders: utils.cssLoaders({// 是否开启sourceMap,便于调试sourceMap: sourceMapEnabled,// 是否提取vue单文件的cssextract: isProduction}),// 是否开启cssSourceMap,便于调试cssSourceMap: sourceMapEnabled,// 这里是true// 缓存破坏,进行sourceMap debug时,设置成false很有帮助。cacheBusting: config.dev.cacheBusting,// vue单文件中,在模板中的图片等资源引用转成require的形式。以便目标资源可以由 webpack 处理。transformToRequire: {video: ['src', 'poster'],source: 'src',img: 'src',// 默认配置会转换 <img> 标签上的 src 属性和 SVG 的 <image> 标签上的 xlink:href 属性。image: 'xlink:href'}}

看完了这些文件相应配置,开发环境的相关配置就串起来了。

其中config/文件夹下的配置,笔者都已经注释在build/文件夹下的对应的文件中,所以就不单独说明了。

那回过头来看,package.jsonscripts中的npm run build配置,node build/build.js,其实就是用node去执行build/build.js文件。

build/build.jsnpm run build指定执行的文件

这个文件主要做了以下几件事情:

1、引入build/check-versions文件,检查nodenpm的版本,

2、引入相关插件和配置,其中引入了webpack生产环境的配置build/webpack.prod.conf.js

3、先控制台输出loading,删除dist目录下的文件,开始构建,构建失败和构建成功都给出相应的提示信息。

具体可以查看相应的注释:

'use strict'// 检查node npm的版本require('./check-versions')()process.env.NODE_ENV = 'production'// 命令行中的loadingconst ora = require('ora')// 删除文件或文件夹const rm = require('rimraf')// 路径相关const path = require('path')// 控制台输入样式 chalk 更多查看:/chalk/chalkconst chalk = require('chalk')// 引入webpackconst webpack = require('webpack')// 引入config/index.jsconst config = require('../config')// 引入 生产环境webpack配置const webpackConfig = require('./webpack.prod.conf')// 控制台输入开始构建loadingconst spinner = ora('building for production...')spinner.start()// 删除原有构建输出的目录文件 这里是dist 和 staticrm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {// 如果出错,抛出错误if (err) throw errwebpack(webpackConfig, (err, stats) => {// 关闭 控制台输入开始构建loadingspinner.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'))})})

build/check-versions检查nodenpm版本

上文提到build/check-versions检查nodenpm版本,

这个文件主要引入了一些插件和配置,最后导出一个函数,版本不符合预期就输出警告。

具体查看这个配置文件注释:

'use strict'// 控制台输入样式 chalk 更多查看:/chalk/chalkconst chalk = require('chalk')// 语义化控制版本的插件 更多查看:/npm/node-semverconst semver = require('semver')// package.json配置const packageConfig = require('../package.json')// shell 脚本 Unix shell commands for Node.js 更多查看:/shelljs/shelljsconst shell = require('shelljs')function exec (cmd) {return require('child_process').execSync(cmd).toString().trim()}const versionRequirements = [{name: 'node',currentVersion: semver.clean(process.version),// 这里配置是"node": ">= 6.0.0",versionRequirement: packageConfig.engines.node}]// 需要使用npmif (shell.which('npm')) {versionRequirements.push({name: 'npm',currentVersion: exec('npm --version'),// 这里配置是"npm": ">= 3.0.0"versionRequirement: packageConfig.engines.npm})}// 导出一个检查版本的函数module.exports = function () {const warnings = []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)}}

build/webpack.prod.conf.jswebpack生产环境配置

上文build/build.js提到,引入了这个配置文件。

这个文件主要做了以下几件事情:

1、引入一些插件和配置,其中引入了build/webpack.base.conf.jswebpack基本配置文件,

2、用DefinePlugin定义环境,

3、合并基本配置,定义自己的配置webpackConfig,配置了一些modules下的rulesdevtools配置,output输出配置,一些处理js、提取css、压缩css、输出html插件、提取公共代码等的

plugins

4、如果启用gzip,再使用相应的插件处理,

5、如果启用了分析打包后的插件,则用webpack-bundle-analyzer

6、最后导出这份配置。

具体可以查看这个文件配置注释:

'use strict'// 引入node路径相关const path = require('path')// 引入utils工具函数const utils = require('./utils')// 引入webpackconst webpack = require('webpack')// 引入config/index.js配置文件const config = require('../config')// 合并webpack配置的插件const merge = require('webpack-merge')// 基本的webpack配置const baseWebpackConfig = require('./webpack.base.conf')// 拷贝文件和文件夹的插件const CopyWebpackPlugin = require('copy-webpack-plugin')// 压缩处理HTML的插件const HtmlWebpackPlugin = require('html-webpack-plugin')const ExtractTextPlugin = require('extract-text-webpack-plugin')// 压缩处理css的插件const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')// 压缩处理js的插件const UglifyJsPlugin = require('uglifyjs-webpack-plugin')// 用DefinePlugin定义环境const env = process.env.NODE_ENV === 'testing'// 这里是 { NODE_ENV: '"testing"' }? require('../config/test.env')// 这里是 { NODE_ENV: '"production"' }: require('../config/prod.env')// 合并基本webpack配置const webpackConfig = merge(baseWebpackConfig, {module: {// 通过styleLoaders函数生成样式的一些规则rules: utils.styleLoaders({// sourceMap这里是truesourceMap: config.build.productionSourceMap,// 是否提取css到单独的css文件extract: true,// 是否使用postcssusePostCSS: true})},// 配置使用sourceMap true 这里是 #source-mapdevtool: config.build.productionSourceMap ? config.build.devtool : false,output: {// 这里是根目录下的distpath: config.build.assetsRoot,// 文件名称 chunkhashfilename: utils.assetsPath('js/[name].[chunkhash].js'),// chunks名称 chunkhashchunkFilename: utils.assetsPath('js/[id].[chunkhash].js')},plugins: [// http://vuejs.github.io/vue-loader/en/workflow/production.html// 定义具体是什么环境new webpack.DefinePlugin({'process.env': env}),// 压缩js插件new UglifyJsPlugin({uglifyOptions: {compress: {// 警告warnings: false// 构建后的文件 常用的配置还有这些// 去除console.log 默认为false。 传入true会丢弃对console函数的调用。// drop_console: true,// 去除debugger// drop_debugger: true,// 默认为null. 你可以传入一个名称的数组,而UglifyJs将会假定那些函数不会产生副作用。// pure_funcs: [ 'console.log', 'console.log.apply' ],}},// 是否开启sourceMap 这里是truesourceMap: config.build.productionSourceMap,// 平行处理(同时处理)加快速度parallel: true}),// extract css into its own file// 提取css到单独的css文件new ExtractTextPlugin({// 提取到相应的文件名 使用内容hash contenthashfilename: utils.assetsPath('css/[name].[contenthash].css'),// Setting the following option to `false` will not extract CSS from codesplit chunks.// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,// increasing file size: /vuejs-templates/webpack/issues/1110// allChunks 默认是false,true指提取所有chunks包括动态引入的组件。allChunks: true,}),// Compress extracted CSS. We are using this plugin so that possible// duplicated CSS from different components can be deduped.new OptimizeCSSPlugin({// 这里配置是truecssProcessorOptions: config.build.productionSourceMap? { safe: true, map: { inline: false } }: { safe: true }}),// generate dist index.html with correct asset hash for caching.// you can customize output by editing /index.html// see /ampedandwired/html-webpack-pluginnew HtmlWebpackPlugin({// 输出html名称filename: process.env.NODE_ENV === 'testing'? 'index.html'// 这里是 根目录下的dist/index.html: config.build.index,// 使用哪个模板template: 'index.html',// inject 默认值 true,script标签位于html文件的 body 底部// body 通true, header, script 标签位于 head 标签内// false 不插入生成的 js 文件,只是单纯的生成一个 html 文件inject: true,// 压缩minify: {// 删除注释removeComments: true,// 删除空格和换行collapseWhitespace: true,// 删除html标签中属性的双引号removeAttributeQuotes: true// 更多配置查看html-minifier插件// more options:// /kangax/html-minifier#options-quick-reference},// necessary to consistently work with multiple chunks via CommonsChunkPlugin// 在chunk被插入到html之前,你可以控制它们的排序。允许的值 ‘none’ | ‘auto’ | ‘dependency’ | {function} 默认为‘auto’.// dependency 依赖(从属)chunksSortMode: 'dependency'}),// keep module.id stable when vendor modules does not change// 根据代码内容生成普通模块的id,确保源码不变,moduleID不变。new webpack.HashedModuleIdsPlugin(),// enable scope hoisting// 开启作用域提升 webpack3新的特性,作用是让代码文件更小、运行的更快new webpack.optimize.ModuleConcatenationPlugin(),// split vendor js into its own file// 提取公共代码new monsChunkPlugin({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)}}),// extract webpack runtime and module manifest to its own file in order to// prevent vendor hash from being updated whenever app bundle is updated// 提取公共代码new monsChunkPlugin({// 把公共的部分放到 manifest 中name: 'manifest',// 传入 `Infinity` 会马上生成 公共chunk,但里面没有模块。minChunks: Infinity}),// This instance extracts shared chunks from code splitted chunks and bundles them// in a separate chunk, similar to the vendor chunk// see: /plugins/commons-chunk-plugin/#extra-async-commons-chunk// 提取动态组件new monsChunkPlugin({name: 'app',// 如果设置为 `true`,一个异步的 公共chunk 会作为 `options.name` 的子模块,和 `options.chunks` 的兄弟模块被创建。// 它会与 `options.chunks` 并行被加载。可以通过提供想要的字符串,而不是 `true` 来对输出的文件进行更换名称。async: 'vendor-async',// 如果设置为 `true`,所有 公共chunk 的子模块都会被选择children: true,// 最小3个,包含3,chunk的时候提取minChunks: 3}),// copy custom static assets// 把static资源复制到相应目录。new CopyWebpackPlugin([{from: path.resolve(__dirname, '../static'),// 这里配置是staticto: config.build.assetsSubDirectory,// 忽略.开头的文件。比如这里的.gitkeep,这个文件是指空文件夹也提交到gitignore: ['.*']}])]})// 如果开始gzip压缩,使用compression-webpack-plugin插件处理。这里配置是false// 需要使用是需要安装 npm i compression-webpack-plugin -Dif (config.build.productionGzip) {const CompressionWebpackPlugin = require('compression-webpack-plugin')webpackConfig.plugins.push(new CompressionWebpackPlugin({// asset: 目标资源名称。 [file] 会被替换成原始资源。// [path] 会被替换成原始资源的路径, [query] 会被替换成查询字符串。默认值是 "[path].gz[query]"。asset: '[path].gz[query]',// algorithm: 可以是 function(buf, callback) 或者字符串。对于字符串来说依照 zlib 的算法(或者 zopfli 的算法)。默认值是 "gzip"。algorithm: 'gzip',// test: 所有匹配该正则的资源都会被处理。默认值是全部资源。// config.build.productionGzipExtensions 这里是['js', 'css']test: new RegExp('\\.(' +config.build.productionGzipExtensions.join('|') +')$'),// threshold: 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。threshold: 10240,// minRatio: 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。minRatio: 0.8}))}// 输出分析的插件 运行npm run build --report// config.build.bundleAnalyzerReport这里是 process.env.npm_config_report// build结束后会自定打开 http://127.0.0.1:8888 链接if (config.build.bundleAnalyzerReport) {// 更多查看链接地址:/package/webpack-bundle-analyzerconst BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPluginwebpackConfig.plugins.push(new BundleAnalyzerPlugin())}// 当然也可以用官方提供的网站 http://webpack.github.io/analyse/#home// 运行类似 webpack --profile --json > stats.json 命令// 把生成的构建信息stats.json上传即可// 最终导出 webpackConfigmodule.exports = webpackConfig

至此,我们就分析完了package.json中的npm run devnpm run build两个命令。测试相关的类似就略过吧。

npm run lint.eslintrc.js中的配置不多,更多可以查看eslint英文文档或eslint中文官网,所以也略过吧。不过提一下,把eslint整合到git工作流。可以安装huskynpm i husky -S。安装后,配置package.jsonscripts中,配置precommit,具体如下:

"scripts": {"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs","precommit": "npm run lint",},

配置好后,每次git commit -m提交会检查代码是否通过eslint校验,如果没有校验通过则提交失败。还可以配置prepushhusky不断在更新,现在可能与原先的配置不太相同了,具体查看husky github仓库。原理就是git-hooks,pre-commit的钩子。对shell脚本熟悉的同学也可以自己写一份pre-commit。复制到项目的.git/hooks/pre-commit中。不需要依赖husky包。我司就是用的shell脚本。

最后提一下.babelrc文件中的配置。

.babelrcbabel相关配置

配置了一些转码规则。这里附上两个链接:babel英文官网和babel的中文官网。

具体看文件中的配置注释:

{// presets指明转码的规则"presets": [// env项是借助插件babel-preset-env,下面这个配置说的是babel对es6,es7,es8进行转码,并且设置amd,commonjs这样的模块化文件,不进行转码["env", {"modules": false,"targets": {"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]}}],"stage-2"],// plugins 属性告诉 Babel 要使用哪些插件,插件可以控制如何转换代码。// transform-vue-jsx 表明可以在项目中使用jsx语法,会使用这个插件转换"plugins": ["transform-vue-jsx", "transform-runtime"],// 在特定的环境中所执行的转码规则,当环境变量是下面的test就会覆盖上面的设置"env": {// test 是提前设置的环境变量,如果没有设置BABEL_ENV则使用NODE_ENV,如果都没有设置默认就是development"test": {"presets": ["env", "stage-2"],"plugins": ["transform-vue-jsx", "transform-es-modules-commonjs", "dynamic-import-node"]}}}

文件中presets中有配置envstage-2,可能不知道是什么。这里引用深入浅出webpack书中,第三章,3-1使用ES6语言 小节的一段,解释一下。

presets属性告诉Babel要转换的源码使用了哪些新的语法特性,一个 Presets 对一组新语法特性提供支持,多个Presets可以叠加。Presets其实是一组Plugins的集合,每一个Plugin完成一个新语法的转换工作。Presets是按照ECMAScript草案来组织的,通常可以分为以下三大类(书中就是说三大类,我发现就两点~~~):

1、已经被写入 ECMAScript 标准里的特性,由于之前每年都有新特性被加入到标准里,所以又可细分为:

es 包含在里加入的新特性;

es 包含在里加入的新特性;

es 包含在里加入的新特性;

es 包含在里加入的新特性;

env 包含当前所有 ECMAScript 标准里的最新特性。

2、被社区提出来的但还未被写入ECMAScript标准里特性,这其中又分为以下四种:

stage0只是一个美好激进的想法,有Babel插件实现了对这些特性的支持,但是不确定是否会被定为标准;

stage1值得被纳入标准的特性;

stage2该特性规范已经被起草,将会被纳入标准里;

stage3该特性规范已经定稿,各大浏览器厂商和 `` 社区开始着手实现;

stage4在接下来的一年将会加入到标准里去。

文章出处:/a/1190000015252698

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。