zoukankan      html  css  js  c++  java
  • 【webpack】理解配置文件

    学习链接:

    http://blog.csdn.net/hongchh/article/details/55113751

    https://segmentfault.com/a/1190000009356434

    http://www.jianshu.com/p/42e11515c10f

    主要分析开发(dev)和构建(build)两个过程涉及到的文件,故下面文件结构仅列出相应的内容。

    ├─build
    │   ├─build.js
    │   ├─check-versions.js
    │   ├─dev-client.js
    │   ├─dev-server.js
    │   ├─utils.js
    │   ├─vue-loader.conf.js
    │   ├─webpack.base.conf.js
    │   ├─webpack.dev.conf.js
    │   ├─webpack.prod.conf.js
    │   └─webpack.test.conf.js
    ├─config
    │   ├─dev.env.js
    │   ├─index.js
    │   ├─prod.env.js
    │   └─test.env.js
    ├─...
    └─package.json

    package.json

    关注scripts字段的内容:

      "scripts": {
        "dev": "node build/dev-server.js",
        "build": "node build/build.js",
        "lint": "eslint --ext .js,.vue src"
      },

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

    build文件夹

    build/dev-server.js

    首先来看执行”npm run dev”时候最先执行的build/dev-server.js文件。该文件主要完成下面几件事情:

    1. 检查node和npm的版本
    2. 引入相关插件和配置
    3. 创建express服务器和webpack编译器
    4. 配置开发中间件(webpack-dev-middleware)和热重载中间件(webpack-hot-middleware)
    5. 挂载代理服务和中间件
    6. 配置静态资源
    7. 启动服务器监听特定端口(8080)
    8. 自动打开浏览器并打开特定网址(localhost:8080)

    说明: express服务器提供静态文件服务,不过它还使用了http-proxy-middleware,一个http请求代理的中间件。前端开发过程中需要使用到后台的API的话,可以通过配置proxyTable来将相应的后台请求代理到专用的API服务器。

    require('./check-versions')()   /*检查NodeJS和npm的版本*/
    var config = require('../config')  // 获取配置
    // 如果Node的环境变量中没有设置当前的环境(NODE_ENV),则使用config中的配置作为当前的环境
    if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
    // 一个可以调用默认软件打开网址、图片、文件等内容的插件
    // 这里用它来调用默认浏览器打开dev-server监听的端口,例如:localhost:8080
    var path = require('path')
    var express = require('express')
    var webpack = require('webpack')
    var opn = require('opn')
    // 一个express中间件,用于将http请求代理到其他服务器
    // 例:localhost:8080/api/xxx  -->  localhost:3000/api/xxx
    // 这里使用该插件可以将前端开发中涉及到的请求代理到API服务器上,方便与服务器对接
    var proxyMiddleware = require('http-proxy-middleware')
    // 引入相应的 webpack 配置
    var webpackConfig = require('./webpack.dev.conf')
    // dev-server 监听的端口,默认为config.dev.port设置的端口,即8080
    // default port where dev server listens for incoming traffic
    var port = process.env.PORT || config.dev.port
      // Define HTTP proxies to your custom API backend
      // https://github.com/chimurai/http-proxy-middleware
      // 定义 HTTP 代理表,代理到 API 服务器
    var proxyTable = config.dev.proxyTable
    // 创建1个 express 实例
    var app = express()
    
    // 数据mock
    var appData = require('../static/data.json')//在根目录下放置的一个data.json文件,用以模拟请求的数据
    var seller = appData.seller//获取data中的数据对象
    var goods = appData.goods
    var ratings = appData.ratings
    
    var apiRoutes = express.Router()//创建一个路由实例
    
    apiRoutes.get('/seller', function(req, res) {//获取数据
      res.json({
        errno: 0,
        data: seller
      })
    })
    
    apiRoutes.get('/goods', function(req, res) {
      res.json({
        errno: 0,
        data: goods
      })
    })
    
    apiRoutes.get('/ratings', function(req, res) {
      res.json({
        errno: 0,
        data: ratings
      })
    })
    
    app.use('/api', apiRoutes)// express使用api
    // 根据webpack配置文件创建Compiler对象
    var compiler = webpack(webpackConfig)
    // webpack-dev-middleware使用compiler对象来对相应的文件进行编译和绑定
    // 编译绑定后将得到的产物存放在内存中而没有写进磁盘
    // 将这个中间件交给express使用之后即可访问这些编译后的产品文件
    var devMiddleware = require('webpack-dev-middleware')(compiler, {
      publicPath: webpackConfig.output.publicPath,
      stats: {
        colors: true,
        chunks: false
      }
    })
    // webpack-hot-middleware,用于实现热重载功能的中间件
    var hotMiddleware = require('webpack-hot-middleware')(compiler)
      // force page reload when html-webpack-plugin template changes
      // 当html-webpack-plugin提交之后通过热重载中间件发布重载动作使得页面重载
    compiler.plugin('compilation', function(compilation) {
      compilation.plugin('html-webpack-plugin-after-emit', function(data, cb) {
        hotMiddleware.publish({
          action: 'reload'
        })
        cb()
      })
    })
    // 将 proxyTable 中的代理请求配置挂在到express服务器上
    // proxy api requests
    Object.keys(proxyTable).forEach(function(context) {
      var options = proxyTable[context]
      if (typeof options === 'string') {
        options = {
          target: options
        }
      }
      app.use(proxyMiddleware(context, options))
    })
    
    // handle fallback for HTML5 history API
    // 重定向不存在的URL,常用于SPA
    app.use(require('connect-history-api-fallback')())
    
    // serve webpack bundle output
    // 使用webpack开发中间件
    // 即将webpack编译后输出到内存中的文件资源挂到express服务器上
    app.use(devMiddleware)
    // 将热重载中间件挂在到express服务器上
    // enable hot-reload and state-preserving
    // compilation error display
    app.use(hotMiddleware)
    
    // serve pure static assets// 静态资源的路径// 将静态资源挂到express服务器上
    var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
    app.use(staticPath, express.static('./static'))
    // 启动express服务器并监听相应的端口(8080)
    module.exports = app.listen(port, function(err) {
      if (err) {
        console.log(err)
        return
      }
    // 应用的地址信息,例如:http://localhost:8080
    var uri = 'http://localhost:' + port console.log('Listening at ' + uri + ' ') // 如果符合自动打开浏览器的条件,则通过opn插件调用系统默认浏览器打开对应的地址uri // when env is testing, don't need open it if (process.env.NODE_ENV !== 'testing') { opn(uri) } })

     build/webpack.base.conf.js

    从代码中看到,dev-server使用的webpack配置来自build/webpack.dev.conf.js文件(测试环境下使用的是build/webpack.prod.conf.js,这里暂时不考虑测试环境)。而build/webpack.dev.conf.js中又引用了webpack.base.conf.js,所以先分析webpack.base.conf.js。

    webpack.base.conf.js主要完成了下面这些事情:

    1. 配置webpack编译入口
    2. 配置webpack输出路径和命名规则
    3. 配置模块resolve规则
    4. 配置不同类型模块的处理规则

    说明: 这个配置里面只配置了.js、.vue、图片、字体等几类文件的处理规则,如果需要处理其他文件可以在module.rules里面配置。

    var path = require('path')
    var config = require('../config')
    var utils = require('./utils')
    var projectRoot = path.resolve(__dirname, '../')// 给出正确的绝对路径
    
    var env = process.env.NODE_ENV
      // check env & config/index.js to decide weither to enable CSS Sourcemaps for the
      // various preprocessor loaders added to vue-loader at the end of this file
    var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
    var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
    var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
    
    module.exports = {
      entry: {// 配置webpack编译入口
        app: './src/main.js'
      },
      output: {// 配置webpack输出路径和命名规则
        path: config.build.assetsRoot,// webpack输出的目标文件夹路径(例如:/dist)
        publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,// webpack编译输出的发布路径
        filename: '[name].js'  // webpack输出bundle文件命名格式
      },
      resolve: {// 配置模块resolve的规则
        extensions: ['', '.js', '.vue'],// 自动resolve的扩展名
        fallback: [path.join(__dirname, '../node_modules')],
        alias: {// 创建路径别名,有了别名之后引用模块更方便,例如 // import Vue from 'vue/dist/vue.common.js'可以写成 import Vue from 'vue'
          'vue$': 'vue/dist/vue.common.js',
          'src': path.resolve(__dirname, '../src'),
          'assets': path.resolve(__dirname, '../src/assets'),
          'components': path.resolve(__dirname, '../src/components'),
          'common': path.resolve(__dirname, '../src/common'),
          'img': path.resolve(__dirname, '../resource/img')
        }
      },
      resolveLoader: {
        fallback: [path.join(__dirname, '../node_modules')]
      },
      module: {// 配置不同类型模块的处理规则
        preLoaders: [{
          test: /.vue$/,
          loader: 'eslint',
          include: projectRoot,
          exclude: /node_modules/
        }, {
          test: /.js$/,// 对所有.vue文件使用eslint
          loader: 'eslint',
          include: projectRoot,
          exclude: /node_modules/
        }],
        loaders: [{
          test: /.vue$/,
          loader: 'vue'
        }, {
          test: /.js$/,// 对src和test文件夹下的.js文件使用babel-loader
          loader: 'babel',
          include: projectRoot,
          exclude: /node_modules/
        }, {
          test: /.json$/,
          loader: 'json'
        }, {
          test: /.(png|jpe?g|gif|svg)(?.*)?$/,
          loader: 'url',
          query: {
            limit: 10000,
            name: utils.assetsPath('img/[name].[hash:7].[ext]')
          }
        }, {// 对字体资源文件使用url,query.name指明了输出的命名规则
          test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
          loader: 'url',
          query: {
            limit: 10000,
            name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
          }
        }]
      },
      eslint: {
        formatter: require('eslint-friendly-formatter')
      },
      vue: {
        loaders: utils.cssLoaders({
          sourceMap: useCssSourceMap
        }),
        postcss: [
          require('autoprefixer')({
            browsers: ['last 2 versions']
          })
        ]
      }
    }

    build/webpack.dev.conf.js

    接下来看webpack.dev.conf.js,这里面在webpack.base.conf的基础上增加完善了开发环境下面的配置,主要包括下面几件事情:

    1. 将hot-reload相关的代码添加到entry chunks
    2. 合并基础的webpack配置
    3. 使用styleLoaders
    4. 配置Source Maps
    5. 配置webpack插件
    var config = require('../config')
    var webpack = require('webpack')
    // 一个可以合并数组和对象的插件
    var merge = require('webpack-merge') var utils = require('./utils') var baseWebpackConfig = require('./webpack.base.conf')
    // 一个用于生成HTML文件并自动注入依赖文件(link/script)的webpack插件
    var HtmlWebpackPlugin = require('html-webpack-plugin') // add hot-reload related code to entry chunks Object.keys(baseWebpackConfig.entry).forEach(function (name) { baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) }) // 合并基础的webpack配置 module.exports = merge(baseWebpackConfig, {
    // 配置样式文件的处理规则,使用styleLoaders module: { loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) },
    // eval-source-map is faster for development// 配置Source Maps devtool: '#eval-source-map', plugins: [// 配置webpack插件 new webpack.DefinePlugin({ 'process.env': config.dev.env }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usage new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }) ] })

     build/webpack.prod.conf.js

    构建的时候用到的webpack配置来自webpack.prod.conf.js,该配置同样是在webpack.base.conf基础上的进一步完善。主要完成下面几件事情:

    1. 合并基础的webpack配置
    2. 使用styleLoaders
    3. 配置webpack的输出
    4. 配置webpack插件
    5. gzip模式下的webpack插件配置
    6. webpack-bundle分析

    说明: webpack插件里面多了丑化压缩代码以及抽离css文件等插件。

    var path = require('path')
    var config = require('../config')
    var utils = require('./utils')
    var webpack = require('webpack')
    var merge = require('webpack-merge')
    var baseWebpackConfig = require('./webpack.base.conf')
    var ExtractTextPlugin = require('extract-text-webpack-plugin')// 用于从webpack生成的bundle中提取文本到特定文件中的插件 // 可以抽取出css,js文件将其与webpack输出的bundle分离
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    var env = config.build.env
    
    var webpackConfig = merge(baseWebpackConfig, {// 合并基础的webpack配置
      module: {
        loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true })
      },
      devtool: config.build.productionSourceMap ? '#source-map' : false,
      output: {// 配置webpack的输出
        path: config.build.assetsRoot,
        filename: utils.assetsPath('js/[name].[chunkhash].js'),
        chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
      },
      vue: {
        loaders: utils.cssLoaders({
          sourceMap: config.build.productionSourceMap,
          extract: true
        })
      },
      plugins: [
        // http://vuejs.github.io/vue-loader/en/workflow/production.html
        new webpack.DefinePlugin({// 抽离css文件
          'process.env': env
        }),
        new webpack.optimize.UglifyJsPlugin({
          compress: {
            warnings: false
          }
        }),
        new webpack.optimize.OccurrenceOrderPlugin(),
        // extract css into its own file
        new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
        // generate dist index.html with correct asset hash for caching.
        // you can customize output by editing /index.html
        // see https://github.com/ampedandwired/html-webpack-plugin
        new HtmlWebpackPlugin({
          filename: config.build.index,
          template: 'index.html',
          inject: true,
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeAttributeQuotes: true
            // more options:
            // https://github.com/kangax/html-minifier#options-quick-reference
          },
          // necessary to consistently work with multiple chunks via CommonsChunkPlugin
          chunksSortMode: 'dependency'
        }),
        // split vendor js into its own file
        new webpack.optimize.CommonsChunkPlugin({
          name: 'vendor',
          minChunks: function (module, count) {
            // 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
            )
          }
        }),
        // 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',
          chunks: ['vendor']
        })
      ]
    })
    
    if (config.build.productionGzip) {
      var 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
        })
      )
    }
    
    module.exports = webpackConfig
  • 相关阅读:
    React.memo()使用教程
    React组件什么时候render
    redux-thunk使用教程
    Redux数据持久化
    Mac 中安装 nvm: 切换Node 不同版本
    上传文件类型选择
    Python获取软件安装列表
    Java读取text文件
    负载均衡
    远程调用
  • 原文地址:https://www.cnblogs.com/yujihang/p/6995098.html
Copyright © 2011-2022 走看看