vue项目结构及启动文件加载过程分析

一、vue项目结构

1、导入项目

    准备好开发工具Visual Studio Code,导入生成的项目案例。我的Vue版本:

2、项目目录及文件说明

2.1、项目主体结构

目录/文件             说明
build                项目构建(webpack)相关代码
config               配置目录,包括端口号、环境变量等。我们初学可以使用默认的。
node_modules         npm 加载的项目依赖模块(根据package.json安装时候生成的的依赖安装包)

src                  这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:
   assets            脚手架自动会放入一个图片在里面作为初始页面的logo。平常我们使用的时候会在里面建立js,css,img,fonts等文件夹,作为静态资源调用    
   components        用来存放组件,合理地使用组件可以高效地实现复用等功能,从而更好地开发项目。
   router            路由相关的内容。该文件夹下有一个叫index.js文件,用于实现页面的路由跳转
   main.js           入口文件,主要作用是初始化vue实例并使用需要的插件,小型项目省略router时可放在该处
   App.vue           主组件,可通过使用<router-view/>开放入口让其他的页面组件得以显示。也可以直接将组件写这里,而不使用 components 目录。

static               静态资源目录,如图片、字体等。
    .gitkeep         git配置。
.babelrc             es6解析的一个配置
.editorconfig        编辑器的配置文件
.eslintignore        忽略eslint语法规范检查配置文件
.eslintrc.js         eslint(代码格式化检查工具)的配置文件,开启以后要严格遵照它规定的格式进行开发
.gitignore           忽略git提交的一个文件,配置之后提交时将不会加载忽略的文件
.postcssrc.js        文件是postcss-loader包的一个配置
index.html           首页入口文件,经过编译之后的代码将插入到这来。
package.lock.json    锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致
package.json         项目配置文件。需要哪些npm包来参与到项目中来,npm install命令根据这个配置文件增减来管理本地的安装包。
README.md            项目的说明文档,markdown 格式

2.2、build目录

目录/文件                   说明
build
    build.js               构建生产版本。package.json中的scripts的build就是node build/build.js,输入命令行npm run build对该文件进行编译生成生产环境的代码。
    check-versions.js      用于检测node和npm的版本,实现版本依赖
    utils.js               utils是工具的意思,是一个用来处理css的文件。
    vue-loader.conf.js     该文件的主要作用就是处理.vue文件,解析这个文件中的每个语言块(template、script、style),转换成js可用的js模块。
    webpack.base.conf.js   开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等
    webpack.dev.conf.js    开发环境配置文件
    webpack.prod.conf.js   生产环境配置文件

2.3、config目录

目录/文件                   说明
build                      config内的文件其实是服务于build的,大部分是定义一个变量export出去。
    dev.env.js             开发环境配置
    index.js               定义开发环境或生产环境中所需要的参数
    prod.env.js            生产环境配置

二、启动后文件加载过程

    当我们使用vue-cli脚手架完成一个项目的时候,下一步肯定会想要怎么把这个项目放到互联网上或者本地直接打开呢,我们在本地调试的时候只要命令行执行npm run dev就可以把这个项目跑起来,但是现在我们要把他放到服务器上的话用npm run build。

1、执行npm run dev

    在执行npm run dev的时候,会在当前目录中寻找 package.json(是npm的配置文件,包含项目的名称版本、项目依赖等相关信息,可通过webpack --config指令指定加载配置文件) 文件, 有点类似 Maven 的 pom.xml 文件,启动 npm run dev 命令后,会加载 build/webpack.dev.conf.js 配置并启动 webpack-dev-server (是一个小型的Node.js Express服务器,主要用于前端项目的本地开发和调试,内部使用webpack-dev-middleware来响应发送到服务器监听单口的HTTP请求)。

    【 "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"】 --- npm run dev执行的命令,热部署 可以在webpack-dev-server后面加--hot参数

    【"webpack-dev-server": "^2.9.1", //提供一个提供实时重载的开发服务器】--- 开发环境依赖包插件

{ # 版本信息
  "name": "vue-test-big", //项目名称
  "version": "1.0.0",     //版本
  "description": "A Vue.js project", //项目描述
  "author": "mjx", //作者
  "private": true,  //是否私人项目 
  "scripts": { //scripts中的子项即是我们在控制台运行的脚本的缩写
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 
    //npm run dev 的 dev 直接运行开发环境 
    //webpack-dev-server:启动了http服务器 
    //inline模式会在webpack.config.js入口配置中新增webpack-dev-server/client?http://localhost:8080/的入口,使得我们访问路径为localhost:8080/index.html
    //progress 显示打包的进度
    "start": "npm run dev",  // npm run start 跑的是同样的命令
    "lint": "eslint --ext .js,.vue src",  //前端编码规范、代码质量管理工具eslint-->根目录多了一个 .eslintrc.js 文件,这是 eslint 的配置文件,可以配置自定义规则(rules)等
    "build": "node build/build.js"  //使用node运行build/build.js文件
  },
  "dependencies": {  // dependencies 设定的是项目里使用的依赖
    "vue": "^2.5.2", //vue.js
    "vue-router": "^3.0.1" //vue的路由插件
  },
  "devDependencies": {// devDependencies(开发依赖库)设定的是开发使用的依赖 
    "autoprefixer": "^7.1.2", // 是用于给css3属性自动加属性前缀的
    "babel-core": "^6.22.1", // babel相关的都是用于处理es6语法的,把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理
    "babel-eslint": "^8.2.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-loader": "^7.1.1",
    "babel-plugin-syntax-jsx": "^6.18.0", //支持jsx
    "babel-plugin-transform-runtime": "^6.22.0", //避免编译输出中的重复,直接编译到build环境中
    "babel-plugin-transform-vue-jsx": "^3.5.0", //babel转译过程中使用到的插件,避免重复
    "babel-preset-env": "^1.3.2", //转为es5,transform阶段使用到的插件之一
    "babel-preset-stage-2": "^6.22.0", //ECMAScript第二阶段的规范
    "chalk": "^2.0.1", //用来在命令行输出不同颜色文字
    "copy-webpack-plugin": "^4.0.1", //拷贝资源和文件
    "css-loader": "^0.28.0", // 所有的*-loader都是 webpack的扩展,webpack是把各种资源理解为一个模块,css-loader就是读取css模块的加载器
    "eslint": "^4.15.0", // eslint 相关是代码格式化检查工具,开启以后要严格遵照它规定的格式进行开发
    "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", //打包压缩文件,与url-loader用法类似
    "friendly-errors-webpack-plugin": "^1.6.1", 识别某些类别的WebPACK错误和清理,聚合和优先排序,以提供更好的开发经验
    "html-webpack-plugin": "^2.30.1", // webpack 里载入和处理html的插件     
    "node-notifier": "^5.1.2", //支持使用node发送跨平台的本地通知
    "optimize-css-assets-webpack-plugin": "^3.2.0", //压缩提取出的css
    "ora": "^1.2.0", //加载(loading)的插件
    "portfinder": "^1.0.13", //查看进程端口
    "postcss-import": "^11.0.0", //可以消耗本地文件、节点模块或web_modules
    "postcss-loader": "^2.0.8", //用来兼容css的插件
    "postcss-url": "^7.2.1", //URL上重新定位、内联或复制
    "rimraf": "^2.6.0", //节点的UNIX命令RM—RF,强制删除文件或者目录的命令
    "semver": "^5.3.0", //用来对特定的版本号做判断的
    "shelljs": "^0.7.6", //使用它来消除shell脚本在UNIX上的依赖性,同时仍然保留其熟悉和强大的命令,即可执行Unix系统命令
    "uglifyjs-webpack-plugin": "^1.1.1", //压缩js文件
    "url-loader": "^0.5.8", // 压缩文件,可将图片转化为base64
    "vue-loader": "^13.3.0", //VUE单文件组件的WebPACK加载器
    "vue-style-loader": "^3.0.1", //类似于样式加载程序,您可以在CSS加载器之后将其链接
    "vue-template-compiler": "^2.5.2",//这个包可以用来预编译VUE模板到渲染函数,以避免运行时编译开销和CSP限制
    "webpack": "^3.6.0", //打包工具
    "webpack-bundle-analyzer": "^2.9.0", //可视化webpack输出文件的大小
    "webpack-dev-server": "^2.9.1", //提供一个提供实时重载的开发服务器
    "webpack-merge": "^4.1.0" //它将数组和合并对象创建一个新对象。如果遇到函数,它将执行它们,通过算法运行结果,然后再次将返回的值封装在函数中
  },
  "engines": { // 项目依赖的引擎版本node、npm版本要求
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [ //限制了浏览器或者客户端需要什么版本才可运行
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

1.1、加载配置文件webpack.dev.conf.js

    引入了很多模块的内容,其中就包括 config 目录下服务器环境的配置文件./config/index.js(有一些生产环境和开发环境的配置)和webpack.base.conf.js(webpack.base.conf.js主要是:合并基础的webpack配置、使用styleLoaders、配置Source Maps、配置webpack插件等)

    【const config = require('../config') // 有一些生产环境和开发环境的配置,IP、端口...】

    【const baseWebpackConfig = require('./webpack.base.conf') //是开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等】

    【new HtmlWebpackPlugin({ filename: 'index.html',template: 'index.html',inject: true}) //生成创建html入口文件,vue.app要注入的模板】

    【  open: config.dev.autoOpenBrowser, //调试时是否自动打开浏览器

         messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`] 控制台打印访问链接

     】

'use strict'
const utils = require('./utils') //引入当前目录中的utils工具配置文件
const webpack = require('webpack') // 引入webpack来使用webpack内置插件
const config = require('../config') // 引入config目录中的index.js配置文件
const merge = require('webpack-merge') //
// 引入webpack-merge插件用来合并webpack配置对象,也就是说可以把webpack配置文件拆分成几个小的模块,然后合并
const path = require('path')  //处理关于文件路径的问题
const baseWebpackConfig = require('./webpack.base.conf') //是开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等
const CopyWebpackPlugin = require('copy-webpack-plugin') //拷贝文件到打包后的路径中
const HtmlWebpackPlugin = require('html-webpack-plugin') //自动生成html的插件,能够把资源自动加载到html文件中
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')//美化webpack的错误信息和日志的插件
const portfinder = require('portfinder') // 查看空闲端口位置,默认情况下搜索8000这个端口

const HOST = process.env.HOST  //processs为node的一个全局对象获取当前程序的环境变量,即host-ip
const PORT = process.env.PORT && Number(process.env.PORT) //processs为node的一个全局对象获取当前程序的环境变量,即port-端口

//下面是合并配置对象,将这个配置文件特有的配置添加替换到base配置文件中
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    //把utils配置中的处理css类似文件的处理方法拿过来,并且不生成cssMap文件
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // cheap-module-eval-source-map is faster for development
  //debtool是开发工具选项,用来指定如何生成sourcemap文件,cheap-module-eval-source-map此款soucemap文件性价比最高
  devtool: config.dev.devtool,

  // these devServer options should be customized in /config/index.js
  //这些devServer选项都是在config/index.js中设定好的
  devServer: {
    clientLogLevel: 'warning', //控制台显示的选项有none, error, warning 或者 info
    historyApiFallback: {
      rewrites: [//当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    hot: true, //热加载
    contentBase: false, // since we use CopyWebpackPlugin.
    compress: true, //压缩
    host: HOST || config.dev.host,
    port: PORT || config.dev.port,
    open: config.dev.autoOpenBrowser, //调试时是否自动打开浏览器
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true } // warning 和 error 都要显示
      : false,
    publicPath: config.dev.assetsPublicPath,
    proxy: config.dev.proxyTable, //接口代理
    quiet: true, // necessary for FriendlyErrorsPlugin //控制台是否禁止打印警告和错误
    watchOptions: {
      poll: config.dev.poll, // 文件系统检测改动
    }
  },
  plugins: [
    //用来定义全局变量
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    new webpack.HotModuleReplacementPlugin(), //模块热替换插件,修改模块时不需要刷新页面
    new webpack.NamedModulesPlugin(), // 显示文件的正确名字HMR shows correct file names in console on update.
    new webpack.NoEmitOnErrorsPlugin(), //当webpack编译错误的时候,中断打包进程
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({ //生成创建html入口文件
      filename: 'index.html',
      template: 'index.html',  //指定注入的模板
      inject: true
    }),
    // copy custom static assets
    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 {//发布新的端口,对于e2e测试
      // 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)
    }
  })
})

扩展内容--require(X):

    上面的代码中有很多const config = require('X')。当 Node 遇到 require(X) 时,按下面的顺序处理:如果 X 以 "./" 或者 "/" 或者 "../" 开头,根据 X 所在的父模块,确定 X 的绝对路径。

    将 X 当成文件,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。

  • X
  • X.js
  • X.json
  • X.node

    将 X 当成目录,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。

  • X/package.json(main字段)
  • X/index.js
  • X/index.json
  • X/index.node

1.2、引入config目录中的index.js配置文件

    【const config = require('../config') // 引入config目录中的index.js配置文件】

     index.js导出了一些配置属性。包含服务器 host 和 port 以及入口文件的相关配置,默认启动端口是8080,这里可以进行修改。

'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', //子目录,一般存放css,js,image等文件
    assetsPublicPath: '/', //根目录
    proxyTable: {}, //可利用该属性解决跨域的问题

    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST // 地址
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined //端口号设置,端口号占用出现问题可在此处修改
    autoOpenBrowser: false, //是否在编译(输入命令行npm run dev)后打开http://localhost:8080/
    errorOverlay: true, //浏览器错误提示
    notifyOnErrors: true, //跨平台错误提示
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- //使用文件系统(file system)获取文件改动的通知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,
    // If true, eslint errors and warnings will also be shown in the error overlay
    // in the browser.
    showEslintErrorsInOverlay: false,
    /**
     * 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 //代码压缩后进行调bug定位将非常困难,于是引入sourcemap记录压缩前后的位置信息记录,当产生错误时直接定位到未压缩前的位置,将大大的方便我们调试
  },

  build: {// 生产环境下面的配置
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'), //index编译后生成的位置和名字,根据需要改变后缀,比如index.php

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'), //编译后存放生成环境代码的位置
    assetsSubDirectory: 'static', //js,css,images存放文件夹名
    assetsPublicPath: '/', //发布的根目录,通常本地打包dist后打开文件会报错,此处修改为./。

    /**
     * Source Maps
     */
    productionSourceMap: true,
    // 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'], //unit的gzip命令用来压缩文件,gzip模式下需要压缩的文件的扩展名有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
  }
}

1.3、引入webpack.base.conf.js配置文件

    webpack.base.conf.js是开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等   

  【const baseWebpackConfig = require('./webpack.base.conf') //开发和生产共同使用提出来的基础配置文件,主要实现配制入口,配置输出环境,配置模块resolve和插件等

  【entry: {//配置入口,默认为单页面所以只有app一个入口app: './src/main.js'}】

'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir) //拼接出绝对路径
}

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, '../'), //path.join将路径片段进行拼接,而path.resolve将以/开始的路径片段作为根目录,在此之前的路径将会被丢弃
  entry: {//配置入口,默认为单页面所以只有app一个入口
    app: './src/main.js'
  },
  output: {//配置出口,默认是/dist作为目标文件夹的路径
    path: config.build.assetsRoot, //路径
    filename: '[name].js', //文件名
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath //公共存放路径
  },
  resolve: {//自动的扩展后缀,比如一个js文件,则引用时书写可不要写.js
    extensions: ['.js', '.vue', '.json'],
    alias: { //创建路径的别名
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  module: {//使用插件配置相应文件的处理方法
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      { //使用vue-loader将vue文件转化成js的模块
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {//js文件需要通过babel-loader进行编译成es5文件以及压缩等操作
        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: {//图片、音像、字体都使用url-loader进行处理,超过10000会编译成base64
          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: {//以下选项是Node.js全局变量或模块,这里主要是防止webpack注入一些Node.js的东西到vue中
    // 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'
  }
}

1.4、主入口文件main.js

    main.js是我们的入口文件,主要作用是初始化vue实例,并引入所需要的插件,会用App.vue替换index.html中的id='app'的div

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

1.5、App.vue

    App.vue是我们的主组件,所有页面都是在App.vue下进行切换的。其实你也可以理解为所有的路由也是App.vue的子组件。所以我将router标示为App.vue的子组件;App.vue中添加<router-view/>,用来承载路由组件。

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view />
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

1.6、router/index.js

    vue路由会去监听url变化,通过路由配合找到相应组件,加载到<router-view/>上,实现页面切换。

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    }
  ]
})

2、执行npm run build

    开发模式:nm  run  dev 不会打包的,只会把项目放到服务器里。而执行npm run build执行的是build/build.js。

2.1、build/build.js

    构建环境下的配置,执行”npm run build”的时候首先执行的是build/build.js文件,build.js主要完成下面几件事:进行node和npm的版本检查、打包时产生loading动画、删除目标文件夹、输出打包信息。

'use strict'
require('./check-versions')() // npm和node版本检查

process.env.NODE_ENV = 'production' // 设置环境变量为production

const ora = require('ora') // ora是一个命令行转圈圈动画插件
const rm = require('rimraf') // rimraf插件是用来执行UNIX命令rm和-rf的用来删除文件夹和文件,清空旧的文件
const path = require('path')// node.js路径模块
const chalk = require('chalk') // chalk插件,用来在命令行中输入不同颜色的文字
const webpack = require('webpack') // 引入webpack模块使用内置插件和webpack方法
const config = require('../config') // 引入config下的index.js配置文件
const webpackConfig = require('./webpack.prod.conf') // 下面是生产模式的webpack配置文件
// 开启转圈圈动画
const spinner = ora('building for production...')
spinner.start()
// 调用rm方法,第一个参数的结果就是 dist/static,表示删除这个路径下面的所有文件
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, (err, stats) => {
    spinner.stop()
    if (err) throw err // 如果删除的过程中出现错误,就抛出这个错误,同时程序终止
    process.stdout.write(stats.toString({// stats对象中保存着编译过程中的各种消息
      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'
    ))
  })
})

2.2、引入config目录中的index.js配置文件

    生产环境的一些配置信息

 build: {
    index: path.resolve(__dirname, '../dist/index.html'),
    //  获得绝对路径,inde.html的模板文件
    // 传送门:http://javascript.ruanyifeng.com/nodejs/path.html#toc1
    assetsRoot: path.resolve(__dirname, '../dist'),
    // 获得dist文件夹的绝对路径
    // 传送门:http://javascript.ruanyifeng.com/nodejs/path.html#toc1
    assetsSubDirectory: 'static',
    // 二级目录
    assetsPublicPath: '/',
    // 发布路径,如果构建后的产品文件有用于CDN或者放到其他域名服务器,可以在这里设置,当然本地打包,本地浏览一般都将这里设置为"./"
    // 设置之后的构建的产品在注入到index.html中就会带上这里的发布路径

    productionSourceMap: true,
    // production环境下生成sourceMap文件
    // 传送门:https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',
    // 开启调试的类型
    //传送门: https://webpack.js.org/configuration/devtool/#development

    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],
    // gzip模式下需要压缩的文件的扩展名,设置js、css之后就只会对js和css文件进行压

    bundleAnalyzerReport: process.env.npm_config_report
    // 是否展示webpack构建打包之后的分析报告
  }

2.3、引入webpack.prod.conf.js配置文件

    构建的时候用到的webpack配置来自webpack.prod.conf.js,该配置同样是在webpack.base.conf基础上的进一步完善。主要完成下面几件事情:合并基础的webpack配置、配置样式文件的处理规则,styleLoaders、配置webpack的输出、配置webpack插件、gzip模式下的webpack插件配置、webpack-bundle分析

'use strict'
const path = require('path')
// node.js的文件路径,用来处理文件当中的路径问题
// 传送门:http://www.jianshu.com/p/fe41ee02efc8
const utils = require('./utils')
//引入utils.js模块
const webpack = require('webpack')
// 引入webpack模块
const config = require('../config')
// 默认是index文件,引入index.js模块
const merge = require('webpack-merge')
// 将基础配置和开发环境配置或者生产环境配置合并在一起的包管理
const baseWebpackConfig = require('./webpack.base.conf')
// 引入基本webpack基本配置
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 在webpack中拷贝文件和文件夹
// 传送门:https://doc.webpack-china.org/plugins/copy-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 文件名即使更改,自动打包并且生成响应的文件在index.html里面
// 传送门:https://webpack.js.org/plugins/html-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件
// https://doc.webpack-china.org/plugins/extract-text-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
//一个用来压缩优化CSS大小的东西
// 传送门:http://npm.taobao.org/package/optimize-css-assets-webpack-plugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// 一个用来压缩优化JS大小的东西
// 传送门:https://webpack.js.org/plugins/uglifyjs-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
const env = require('../config/prod.env')
// 引入生产环境
const webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    })
  },
  // 将webpack基本配置和生产环境配置合并在一起,生成css,postcss,less等规则,并进行模块转换,转换成webpack可识别的文件,进行解析
  // 将CSS提取到单独文件中去
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  // 是否使用sourcemap
  output: {
    path: config.build.assetsRoot,
    // 文件打包的输出路径
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // 主文件入口文件名字
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
    // 非主文件入口文件名,可以存放cdn的地址
    // 传送门:http://react-china.org/t/webpack-output-filename-output-chunkfilename/2256
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // DefinePlugin 允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。
    // https://doc.webpack-china.org/plugins/define-plugin/#src/components/Sidebar/Sidebar.jsx
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
    // 一个用来压缩优化JS大小的东西
    // 传送门:https://webpack.js.org/plugins/uglifyjs-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      allChunks: false,
    }),
    // 它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件
    // https://doc.webpack-china.org/plugins/extract-text-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap ? {
        safe: true,
        map: {
          inline: false
        }
      } : {
        safe: true
      }
    }),
    //一个用来压缩优化CSS大小的东西
    // 传送门:http://npm.taobao.org/package/optimize-css-assets-webpack-plugin
    new HtmlWebpackPlugin({
      filename: config.build.index, //打包生成的文件名
      template: 'index.html', //使用的模板名
      inject: true, //脚本插入的位置
      minify: {
        removeComments: true,
        // 删除index.html中的注释
        collapseWhitespace: true,
        // 删除index.html中的空格
        removeAttributeQuotes: true
        // 删除各种html标签属性值的双引号
      },
      chunksSortMode: 'dependency'
      // 注入依赖的时候按照依赖先后顺序进行注入,比如,需要先注入vendor.js,再注入app.js
    }),
    new webpack.HashedModuleIdsPlugin(),
    // 该插件会根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境。
    // 传送门:https://doc.webpack-china.org/plugins/hashed-module-ids-plugin/#src/components/Sidebar/Sidebar.jsx
    new webpack.optimize.ModuleConcatenationPlugin(),
    // 预编译所有模块到一个闭包中,提升你的代码在浏览器中的执行速度。
    // 传送门:https://doc.webpack-china.org/plugins/module-concatenation-plugin/#src/components/Sidebar/Sidebar.jsx
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks(module) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // 将所有从node_modules中引入的js提取到vendor.js,即抽取库文件
    // 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 webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // 把webpack的runtime和manifest这些webpack管理所有模块交互的代码打包到[name].js文件中,防止build之后vendor的hash值被更新[疑问]
    // 传送门:https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),
    // 传送门:https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new CopyWebpackPlugin([{
      from: path.resolve(__dirname, '../static'),
      to: config.build.assetsSubDirectory,
      ignore: ['.*']
    }])
    // 在webpack中拷贝文件和文件夹
    // 传送门:https://doc.webpack-china.org/plugins/copy-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
  ]
})

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}
// 提供带 Content-Encoding 编码的压缩版的资源
// 传送门:https://doc.webpack-china.org/plugins/compression-webpack-plugin/#src/components/Sidebar/Sidebar.jsx
if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
// 分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。
module.exports = webpackConfig

2.4、引入webpack.base.conf.js配置文件

...
module.exports = {
  // 基础上下文
  context: path.resolve(__dirname, '../'),
  // webpack的入口文件
  entry: {
    app: './src/main.js'
  },
  // webpack的输出文件
  output: {
    path: config.build.assetsRoot, //打包输出路径assetsRoot: path.resolve(__dirname, '../dist')
    filename: '[name].js',  //输出文件名
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath 
      : config.dev.assetsPublicPath  
  }
...

打包后生成情况:

扩展:webpack及其四大核心概念简介

    webpack可以看作是模块打包机:它做的是,分析你项目结构,找到JavaScript模块、其他的一些浏览器不能直接运行的拓展语言(Scss , less等)以及新语法(像ES6等)

,并将其转换和打包为合适的格式供浏览器使用。其四大核心概念:

    1)入口(entry):要打包哪个文件

    2)出口(output):要打包到哪里

    3)加载器(loader):加载除了js文件其他文件的功能

    4)插件(plugins):处理加载器完成不了的功能,使用插件

Logo

前往低代码交流专区

更多推荐