vue-cli 的webpack模板项目结构分析

我们一般都是直接使用vue-cli生成我们的vue基础项目结构,今天我们就来分析一下其webpack配置信息。

目录

打包过程中主要分为 dev build 两个过程,因此webpack的配置中也区分了两种情况。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置。

─build 
│ ├─build.js 
│ ├─check-versions.js 
│ ├─logo.png 
│ ├─utils.js 
│ ├─vue-loader.conf.js 
│ ├─webpack.base.conf.js 
│ ├─webpack.dev.conf.js 
│ ├─webpack.prod.conf.js 
├─config 
│ ├─dev.env.js 
│ ├─index.js 
│ ├─prod.env.js 
├─… 
└─package.json
命令分析
// package.json
我们主要来看scripts和下面的依赖包
{
  "name": "vuetest",
  "version": "1.0.0",
  "description": "A Vue.js project",
  "author": "zjj19970517 <1392372716@qq.com>",
  "private": true,
  "scripts": {      
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", // 【1】 再开发环境下执行 build/webpack.dev.conf.js
    "start": "npm run dev",
    "unit": "jest --config test/unit/jest.conf.js --coverage",
    "e2e": "node test/e2e/runner.js",
    "test": "npm run unit && npm run e2e",
    "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
    "build": "node build/build.js"  // 【2】 生产环境下执行 build/build.js
  },
  "dependencies": {
    "vue": "^2.5.2",
    "vue-router": "^3.0.1"
  },
  "devDependencies": {
    "autoprefixer": "^7.1.2",
    "babel-core": "^6.22.1",
    "babel-eslint": "^8.2.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-jest": "^21.0.2",
    "babel-loader": "^7.1.1",
    "babel-plugin-dynamic-import-node": "^1.2.0",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-plugin-transform-vue-jsx": "^3.5.0",
    "babel-preset-env": "^1.3.2",
    "babel-preset-stage-2": "^6.22.0",
    "babel-register": "^6.22.0",
    "chalk": "^2.0.1",
    "chromedriver": "^2.27.2",
    "copy-webpack-plugin": "^4.0.1",
    "cross-spawn": "^5.0.1",
    "css-loader": "^0.28.0",
    "eslint": "^4.15.0",
    "eslint-config-standard": "^10.2.1",
    "eslint-friendly-formatter": "^3.0.0",
    "eslint-loader": "^1.7.1",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-node": "^5.2.0",
    "eslint-plugin-promise": "^3.4.0",
    "eslint-plugin-standard": "^3.0.1",
    "eslint-plugin-vue": "^4.0.0",
    "extract-text-webpack-plugin": "^3.0.0",
    "file-loader": "^1.1.4",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "html-webpack-plugin": "^2.30.1",
    "jest": "^22.0.4",
    "jest-serializer-vue": "^0.3.0",
    "nightwatch": "^0.9.12",
    "node-notifier": "^5.1.2",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "ora": "^1.2.0",
    "portfinder": "^1.0.13",
    "postcss-import": "^11.0.0",
    "postcss-loader": "^2.0.8",
    "postcss-url": "^7.2.1",
    "rimraf": "^2.6.0",
    "selenium-server": "^3.0.1",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "uglifyjs-webpack-plugin": "^1.1.1",
    "url-loader": "^0.5.8",
    "vue-jest": "^1.0.2",
    "vue-loader": "^13.3.0",
    "vue-style-loader": "^3.0.1",
    "vue-template-compiler": "^2.5.2",
    "webpack": "^3.6.0",
    "webpack-bundle-analyzer": "^2.9.0",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.0"
  },
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

测试的东西先不看,直接看”dev”和”build”。运行”npm run dev”的时候执行的是 build/webpack.dev.conf.js 文件,运行”npm run build”的时候执行的是 build/build.js 文件,我们可以从这两个文件开始进行代码阅读分析。

build文件夹分析

build/webpack.dev.config.js

'use strict'
// 1.引入相关插件和配置 
// 2.生成处理各种样式的规则 
// 3.配置开发环境,如热更新、监听端口号,是否自动打开浏览器等都在webpack中的devServer中配置完成 
// 4.寻找可利用的端口和添加显示程序编译运行时的错误信息。
const utils = require('./utils') // utils 工具函数
const webpack = require('webpack') 
const config = require('../config') // 引入配置,默认是index
const merge = require('webpack-merge') // 将基础配置和开发配置或者是生产配置合并在一起
const path = require('path') 
const baseWebpackConfig = require('./webpack.base.conf') // 引入基础配置
const CopyWebpackPlugin = require('copy-webpack-plugin') // 
const HtmlWebpackPlugin = require('html-webpack-plugin') // 自动打包生成index.html
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') // Friendly-errors-webpack-plugin可识别某些类型的webpack错误并清理,汇总和优先化它们以提供更好的开发者体验。
const portfinder = require('portfinder') // 查看闲置接口,默认是8000这个端口,解决端口占用冲突的问题

// 通过process这个全局变量,来获取全局数据
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)

// 正式的dev配置,合并基础配置
const devWebpackConfig = merge(baseWebpackConfig, {
  // 下面的配置都是开发模式下要使用的
  // 
  module: {
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
    //自动生成关于css、less、postcss、等的规则
  },
  // 增加了调试功能
  devtool: config.dev.devtool,

  // 本地服务
  devServer: {
    clientLogLevel: 'warning', // 在开发工具(DevTools)的控制台将显示警告消息
    historyApiFallback: { // h5的history API处理,任意的 404 响应都可能需要被替代为 index.html。
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    hot: true, // 启动热更新模式
    contentBase: false, // since we use CopyWebpackPlugin.
    compress: true, // 一切服务都启动后,就会使用gzip压缩代码
    host: HOST || config.dev.host, // 默认是localhost
    port: PORT || config.dev.port, // 指定要监听的端口号
    open: config.dev.autoOpenBrowser, // 是否自动打开默认浏览器
    overlay: config.dev.errorOverlay //  当出现编译器错误或警告时,在浏览器中显示全屏叠加,覆盖到浏览器的项目页面的上方
      ? { warnings: false, errors: true }
      : false,
    publicPath: config.dev.assetsPublicPath,
    // 服务器假设运行在 http://localhost:8080 并且output.filename 被设置为bundle.js默认。
    // publicPath是"/",所以你的包(束)通过可以 http://localhost:8080/bundle.js 访问。
    // 如果config中的index.js dev对象的中的assertsPublicPath设置为"/asserts/"
    // 那么文件打开后将通过 http://localhost:8080/asserts/来进行访问
    proxy: config.dev.proxyTable,
    //  如果你有单独的后端开发服务器API,并且希望在同域名下发送API请求,
    //  那么代理某些URL将很有用.简称就是API代理,中间件  需引入 http-proxy-middleware
    quiet: true, 
    // 启用quiet后,除了初始启动信息之外的任何内容都不会被打印到控制台。
    // 这也意味着来自的WebPack的错误或警告在控制台不可见。
    watchOptions: {
      poll: config.dev.poll,
    }
    // webpack使用文件系统(file system)获取文件改动的通知
  },
  // 插件配置
  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    new webpack.HotModuleReplacementPlugin(),
    //  永远不能用在生产模式,模块热更新,修改文件的内容,允许在运行时更新各种模块,而无需进行完全刷新。
    new webpack.NamedModulesPlugin(), // // 当进行热更新时,相关文件名会被展示出来
    new webpack.NoEmitOnErrorsPlugin(),
    // 跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误。
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    //  该插件可自动生成一个 html5 文件或使用模板文件将编译好的代码注入进去
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
    // 拷贝文件到静态文件夹
  ]
})

// 对端口占用冲突的解决
module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port
      // add port to devServer config
      devWebpackConfig.devServer.port = port

      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      resolve(devWebpackConfig)
    }
  })
})

build/webpack.base.conf.js

// 1.配置webpack编译入口 
// 2.配置webpack输出路径和命名规则 
// 3.配置模块resolve规则 
// 4.配置不同类型模块的处理规则 
'use strict'
const path = require('path')
const utils = require('./utils')  // 工具函数
const config = require('../config') // 配置文件
const vueLoaderConfig = require('./vue-loader.conf')  // 工具函数集合
// vue-loader.conf配置文件是用来解决各种css文件的,定义了诸如css,less,sass之类的和样式有关的loader
/**
 * 获得绝对路径
 * @method resolve
 * @param  {String} dir 相对于本文件的路径
 * @return {String}     绝对路径
 */
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

// rules 中的 eslint 规则
const createLintingRule = () => ({
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  enforce: 'pre',
  include: [resolve('src'), resolve('test')],
  options: {
    formatter: require('eslint-friendly-formatter'),
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})

module.exports = {
  context: path.resolve(__dirname, '../'),
  //基础目录(绝对路径),用于从配置中解析入口点和加载程序
  entry: {
    app: './src/main.js'      // 入口文件
  },
  output: {                   // 输出
    path: config.build.assetsRoot, // 输出的路径如:./dist
    filename: '[name].js',         // webpack输出bundle文件命名格式
    publicPath: process.env.NODE_ENV === 'production'  // 正式发布环境下编译输出的上线路径的根路径
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  // 模块resolve的规则
  resolve: {
    // 自动补全的扩展名
    extensions: ['.js', '.vue', '.json'],
    // 别名,方便引用模块,例如有了别名之后,
    // 省略扩展名,比方说import index from '../js/index'会默认去找index文件,然后找index.js,index.vue,index.json文件
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
    // 使用别名  使用上面的resolve函数,意思就是用@代替src的绝对路径
  },
  module: {
    rules: [
      // 根据配置,决定是否创建eslint
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/, // vue
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        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: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}
config文件夹

config文件夹下最主要的是index.js 保存着开发环境和生产环境岁需要的一些信息。

// index.js
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
  // 开发模式下的配置
  dev: {
    // Paths
    assetsSubDirectory: 'static',
    // 二级目录,存放静态资源文件,在dist下
    assetsPublicPath: '/',
    // 发布路径,如果构建后的产品文件有用于CDN或者放到其他域名服务器,可以在这里设置,当然本地打包,本地浏览一般都将这里设置为"./"
    // 设置之后的构建的产品在注入到index.html中就会带上这里的发布路径
    proxyTable: {},
    // 代理示例: proxy: [{context: ["/auth", "/api"],target: "http://localhost:3000",}]
    // Various Dev Server settings
    host: 'localhost', // 这个可以被process.env.HOST重写
    port: 8080, // process.env.PORT重写  在端口空闲的情况下
    autoOpenBrowser: false, // 是否自动打开浏览器
    errorOverlay: true, // 是否出现编译器错误或者警告,在浏览器显示全屏叠加,覆盖到浏览器页面上面
    notifyOnErrors: true, // 是否允许窗口弹出错误信息
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

    // Use Eslint Loader?
    // If true, your code will be linted during bundling and
    // linting errors and warnings will be shown in the console.
    useEslint: true, // 是否使用eslint
    // If true, eslint errors and warnings will also be shown in the error overlay
    // in the browser.
    showEslintErrorsInOverlay: false, // 是否让eslint报错显示在浏览器的透明黑色层上面

    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'cheap-module-eval-source-map', // 调试的类型

    // If you have problems debugging vue-files in devtools,
    // set this to false - it *may* help
    // https://vue-loader.vuejs.org/en/options.html#cachebusting
    cacheBusting: true, //是否通过将哈希查询附加到文件名来生成具有缓存清除的源映射[疑问,求解]

    cssSourceMap: true // 开发环境下,显示 cssSourceMap
  },
  // 生产环境
  build: {
    // 获得绝对路径,inde.html的模板文件
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'), // 获得dist文件夹的绝对路径
    assetsSubDirectory: 'static', // 二级目录
    assetsPublicPath: '/',

    /**
     * Source Maps
     */

    productionSourceMap: true, // production环境下生成sourceMap文件
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false, // 是否压缩
    productionGzipExtensions: ['js', 'css'], //  gzip模式下需要压缩的文件的扩展名,设置js、css之后就只会对js和css文件进行压

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
    // 是否展示webpack构建打包之后的分析报告
  }
}
// prod.env.js
'use strict'
// 生产环境的环境变量的配置
module.exports = {
  NODE_ENV: '"production"'
}
// dev.env.js
'use strict'
// 该文件主要来设置开发环境变量。 
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
// 引入生产环境配置

// 合并两个环境变量的配置,然后暴露出去
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"' // 配置NODE_ENV来决定开发环境
})

// 这个就是用来上线的时候用到,来决定是开发环境还是生产环境,来进行相关的配置解决方案
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值