关于Android工程师转成vue的三两事儿(4)--webpack

 最近为了可以写一份值得参考的webpack文档,特地的去查了好多相应的书籍,博客。距离上次写的那篇文章好想也过去将近一周的时间了。我想是时候要准备下一篇文章了。否则就食言而肥了。  算了,技术类文章就直接从技术类开始提及吧。首先,学习webpack呢?是由于我在开发vue和react的时候遇到了这个工具,而后最近在看人家的招聘要求的时候,老是会带上这个。 javascript

腾讯
而后我就趁着下班时间研究了一下这个东西。仍是和以前的说法同样,若是有任何的疑惑,请在留言区留言,若是我能看见必定会及时的向您反馈。 新弄的github代码地址 ###一、新语言的诞生背景
新语言
 近些年来web应用的功能需求的完善和所设计到领域愈加宽广致使前端萌生了不少新的思想和框架。我简单的介绍一下:  首先行业的领导着提出 模块化的思想,他们认为将一个复杂的系统分为多个模块来开发会大大减小开发难度和提高开发效率。同时考虑到css只能用静态的语法描述元素的样式,没法像写javascript那样增长逻辑判断与共享变量。因而在这种大环境下,诞生了 es6typescriptscss等新的语言。可是考虑到例如es6没法在浏览器中直接运行,须要将es6转换成es5以后浏览器才能识别。因而新的构建工具就出现了。  构建工具的功能主要是:进行代码转换、文件压缩、代码分割、模块合并、自动刷新、代码检验、自动发布等功能。构建实际上是工程化、自动化思想在前端开发的体验,咱们要作的实际上是用代码去让前端项目自动化的执行这一系列化复杂的流程罢了。
gulp
 说到构建工具,我看百度上有不少。例如npm script、grunt、gulp、fis三、webpack等等。由于本文主要是讲webpack,若是有机会能接触到以上的几个工具,我会另外详细的描述。 ###二、 webpack的优点
webpack
 从我这些天不管是本身的实践仍是书上写的来讲,我认为webpack自己就是特别符合模块化开发的一种工具。在webpack里面一切文件都是模块,并经过loader转换文件,经过plugin注入钩子,最后输出由多模块组合成的文件。其优势主要是: 一、 专一于处理模块化开发的项目,能作到开箱即用,一步到位 二、 能经过plugin扩展 三、 应用各类领域 四、 社区庞大 五、 良好的开发体验 但谈及为何要选用webpack,我看书上主要有如下几个见解: 一、大多数团队在开发新项目的时候都会采用紧跟时代的技术,这些技术基本都会采用“模块化+新语言+新框架”,webpack能够为这些新项目提供一站式的解决方案 二、webpack有良好的生态链和维护团队,能提供必定的开发体验并保证质量 三、webpack被全世界大量的web开发者使用和验证,能找到各个层次面所需的教程和经验分享 (反正,综上所述 嗯 你再不学webpack就out了😂) ###三、 webpack的安装(须要node环境滴)

  • 初始化项目 css

    创建项目
    如上图所示,新建了一个项目。

    npm init
    复制代码
  • 全局安装html

    // 最新版本好像变成了webpack-cli注意一下
    npm install -g webpack
    复制代码
  • 项目内安装前端

    npm install webpack --save-dev
    复制代码

 在这里扯一句闲话,可能不少教程谈到安装webpack都会让你选择直接-g,可是我并不推荐你这么作,我总感受-g以后就成了全局变量,可是我并非每一个项目内都能用到这个所谓的webpack,将webpack的做用域设置为项目内,功能与全局没有差异。 ###四、webpack的基础使用 以下图所示,在上面新建的项目下面新建index.js.。而后敲一些简单的js代码: vue

index.js
而后打包该js
打包js

// 打包代码
webpack-cli index.js --output build/bundle.js --mode development
复制代码

打包成功以后就会发现目录下面多了一个build目录,里面有bundle.js文件,最后新建index,html,并引用该js,就会看到以下的效果html5

打包后代码
index.html
效果
可是看到这个效果我以为并非很满意,由于我想要这句话成为一个红色的。此时就要用到css了。
image.png
随即调用
image.png
最后进行打包的时候发现会报以下的错误
image.png
根据其解释应该是少了一个loader致使打包失败,最后导入css-loader、style.css以后,而且按照官方的文档指示,成功加载出想要的页面
加载器
注意是加载loader优先级必定是从右向左
最终效果
但此时的问题又来了,我每次在进行操做的时候都须要去加载一个css,每次这样子的引用就会使得代码变得臃肿就彻底没又体现webpack的核心优点了。因而在查阅资料得知原来webpack和不少的框架同样都能进行只能配置,而后我就按照官网的指示一步步的进行配置
webpack.config.js配置
打包成功
修改文字颜色以后效果
这样作的好处也是体现到前面关于webpack的自动化配置、编译的体现。可是咱们发现每次编译以后,都须要去打包、刷新页面这样作实在是太浪费时间了,这里就要谈到webpack-dev-server。老规矩首先安装

cnpm install webpack-dev-server --save
复制代码

webpack-dev-server安装完成
而后在控制台里面输入下面代码,而后在网页中打开 http://localhost:8080/发现就能直接将代码运行在网页上面

// 两步
webpack-cli
webpack-dev-server
复制代码

编译代码
网页端显示
可是虽然是看到了本地编译的html被弄上了去,可是咱们发现每次都须要编译文件以后再进行webpack-dev-server插件弄到网上去,可是我每当js改变以后 并没可以自动编译,挺麻烦的。因而我便作了以下操做
修改index.html的js路径
输入webpack-dev-server --hot --inline
修改index.js的时候会发现命令行中有编译的字样
改变后的前端页面
可是此时的问题又来了,对于一个初学者,谁会记得这么长,这么麻烦的代码呢?在Android里面通常这种东西都会用到一个配置文件写好,而后每次都直接使用就完事儿了。说道配置文件,这里就想到了webpack.json。因而就在script标签下面添加以下代码,也能达到刚刚的效果
把刚刚那段话添加到script里面去
输入npm run test
前端改变后页面
一样咱们也能够把dev-server放置在webpack.config.js文件中,更多的配置项呢?烦请诸位看客直接去阅读官方文档
webpack.config.js
说到这里呢?咱们虽然讲js和css打包了,可是并无打包html文件。查阅一番知道webpack里面有一个插件是专门用来打包的:

cnpm install html-webpack-plugin --save
复制代码

而后加入配置项 java

webpack.config.js加入plugin
打包
打开服务
build下面就会自动生成index.html
生成自定的打包后文件
可是说到这么多依然没有提到咱们前面说的新语言的引用啊,前面在说优点的时候都说了webpack支持es6=》es5 啊,此时就不能不提到 babel了。如图安装babel插件,而后写一些es6的语法看看
babel引入
es6语法
导入babel的包
复制官网上面的东西
配置package.json文件
开启服务
网页效果
以上就是我不管是看其余书籍,仍是博客外加上阅读官网上得来的一些经验。虽然作的东西比较简单,可是这都无疑体现出了webpack的优良的品质。若是您对以上内容存在有任何的疑虑或者是有任何指教,欢迎提出 我会第一时间与您交流、讨论。 好了,webpack的入门相信你们看到这里都已经入门了,接下来我就结合一下具体的vue实际的例子来和你们进行讨论。 ###五、vue中的webpack 我在这里利用的就是我用vue-cli脚手架新生成的一个vue项目,就结合我这段时间的一些所见所闻来作一个简单的赘述:

  • index.js
'use strict' // 严格模式
// Template version: 1.2.7
// see http://vuejs-templates.github.io/webpack for documentation.
const config = require('./config') // 导入config文件
const path = require('path') //使用Node自带的文件路径插件

module.exports = {
  // 开发环境
  dev: {
    // Paths
    assetsSubDirectory: 'static', // 编译输出的二级目录
    assetsPublicPath: '/', // 编译发布上线路径的根目录,可配置为资源服务器域名或 CDN 域名
    proxyTable: {
    }, // 须要 proxyTable 代理的接口(可跨域),详情请看以前的文章

    // Various Dev Server settings
    host: '0.0.0.0', // host,若是设置成0.0.0.0能够经过统一局域网其余设备经过ip访问该网页
    port: 8080, // 网页默认端口号,若是端口被占用会自动分配一个随即未被占有的端口
    autoOpenBrowser: false, // 是否自动打开浏览器
    errorOverlay: true, //  在浏览器是否展现错误蒙层
    notifyOnErrors: true, // 是否展现错误的通知
    // 这个是webpack-dev-servr的watchOptions的一个选项,指定webpack检查文件的方式
    // 由于webpack使用文件系统去获取文件改变的通知。在有些状况下,这个可能不起做用。例如,当使用NFC的时候,
    // vagrant也会在这方面存在不少问题,在这些状况下,使用poll选项(以轮询的方式去检查文件是否改变)能够设定为true
    // 或者具体的数值,指定文件查询的具体周期。
    poll: false,

    // Use Eslint Loader?
    useEslint: true,//  eslint代码检查
    showEslintErrorsInOverlay: false,  // 若是设置为true,在浏览器中,eslint的错误和警告会以蒙层的方式展示。

    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'eval-source-map', // 调试工具
    cacheBusting: true,  // 指定是否经过在文件名称后面添加一个查询字符串来建立source map的缓存
    cssSourceMap: false,  // 是否开启 cssSourceMap
  },

  // 正式环境
  build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'), // 编译注入的 index.html 文件,必须是本地的绝对路径

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),  // 编译输出的静态资源根路径
    assetsSubDirectory: 'static', // 编译输出的二级目录
    assetsPublicPath: config.getOutPutPath(), // 编译发布上线路径的根目录,可配置为资源服务器域名或 CDN 域名

    // assetsPublicPath: './',

    /**
     * Source Maps
     */

    productionSourceMap: true,  //生成用于生产构建的源映射
    devtool: '#source-map', // 调试代码的模式,共有7种,这里是生成source-map文件

    productionGzip: false, // 是否开启 gzip
    productionGzipExtensions: ['js', 'css'], // 须要使用 gzip 压缩的文件扩展名

    // 一个实用工具,用于分析项目的依赖关系
    // 若是这个选项是true的话,那么则会在build后,会在浏览器中生成一份bundler报告
    bundleAnalyzerReport: process.env.npm_config_report
  }
}
复制代码
  • utils.js
const path = require('path') // 引入nodejs的path模块,用于操做路径
const config = require('../config') // 引入模板的配置文件,下面就须要去这个文件中看看有什么基本的配置
const ExtractTextPlugin = require('extract-text-webpack-plugin') // 提取特定文件的插件,好比把css文件提取到一个文件中去
const packageConfig = require('../package.json') // 加载package.json文件

// 生成编译输出的二级目录
exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory

  // path.posix是path模块跨平台的实现(不一样平台的路径表示是不同的)
  return path.posix.join(assetsSubDirectory, _path)
}

// 为不一样的css预处理器提供一个统一的生成方式,也就是统一处理各类css类型的打包问题。
// 这个是为在vue文件中的style中使用的css类型
exports.cssLoaders = function (options) {
  options = options || {}

  // 打包css模块
  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 编译postcss模块
  const postcssLoader = {
    // 使用postcss-loader来打包postcss模块
    loader: 'postcss-loader',
    // 配置source map
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 建立loader加载器字符串,结合extract text插件使用
  /**
   *
   * loader:loader的名称
   * loaderOptions:loader对应的options配置对象
   */
  function generateLoaders (loader, loaderOptions) {
    // 经过usePostCSS 来标明是否使用了postcss
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    // 若是指定了具体的loader的名称
    if (loader) {
      // 向loaders的数组中添加该loader对应的加载器
      // 一个很重要的地方就是,一个数组中的loader加载器,是从右向左执行的。
      loaders.push({
        // loader加载器的名称
        loader: loader + '-loader',
        // 对应的加载器的配置对象
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // 若是明确指定了须要提取静态文件,则使用
    // ExtractTextPlugin.extract({})来包裹咱们的各类css处理器。
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        // fallback这个选项咱们能够这样理解
        // webpack默认会按照loaders中的加载器从右向左调用编译各类css类型文件。若是一切顺利,在loaders中的
        // 各个加载器运行结束以后就会把css文件导入到规定的文件中去,若是不顺利,则继续使用vue-style-loader来处理
        // css文件
        fallback: 'vue-style-loader'
      })
    } else {
      // 若是没有提取行为,则最后再使用vue-style-loader处理css
      return ['vue-style-loader'].concat(loaders)
    }
  }

  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
  return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders('less'),
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
  }
}

// 使用这个函数,为那些独立的style文件建立加载器配置。
exports.styleLoaders = function (options) {
  // 保存加载器配置的变量
  const output = []
  // 获取全部css文件类型的loaders
  const loaders = exports.cssLoaders(options)

  for (const extension in loaders) {
    const loader = loaders[extension]
    // 生成对应的loader配置
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

exports.createNotifierCallback = () => {
  // node-notifier是一个跨平台的包,以相似浏览器的通知的形式展现信息。
  const notifier = require('node-notifier')

  return (severity, errors) => {
    // 只展现错误的信息
    if (severity !== 'error') return

    const error = errors[0]
    const filename = error.file && error.file.split('!').pop()

    // 须要展现的错误信息的内容
    notifier.notify({
      // 通知的标题
      title: packageConfig.name,
      // 通知的主体内容
      message: severity + ': ' + error.name,
      // 副标题
      subtitle: filename || '',
      // 通知展现的icon
      icon: path.join(__dirname, 'logo.png')
    })
  }
}
复制代码
  • vue.loader.config.js
const utils = require('./utils')
const config = require('../config')
// 设置是否是生产环境
const isProduction = process.env.NODE_ENV === 'production'
// 根据不一样的环境,引入不一样的source map配置文件
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap

module.exports = {
  // vue文件中的css loader配置
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    // 生产环境下就会把css文件抽取到一个独立的文件中
    extract: isProduction
  }),
  // css source map文件的配置
  cssSourceMap: sourceMapEnabled,
  // css source map文件缓存控制变量
  cacheBusting: config.dev.cacheBusting,
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}
复制代码
  • build/webpack.base.conf.js
const path = require('path') // 使用 NodeJS 自带的文件路径插件
const utils = require('./utils') //封装了一些方法的工具
const config = require('../config') //使用 config/index.js
const vueLoaderConfig = require('./vue-loader.conf') //使用vue-loader.conf

function resolve (dir) {
  return path.join(__dirname, '..', dir)  // 拼接咱们的工做区路径为一个绝对路径
}
// eslint的规则
const createLintingRule = () => ({
  // 对.js和.vue结尾的文件进行eslint检查
  test: /\.(js|vue)$/,
  // 使用eslint-loader
  loader: 'eslint-loader',
  // enforce的值多是pre和post。其中pre有点和webpack@1中的preLoader配置含义类似。
  // post和v1中的postLoader配置含义类似。表示loader的调用时机
  // 这里表示在调用其余loader以前须要先调用这个规则进行代码风格的检查
  enforce: 'pre',
  // 须要进行eslint检查的文件的目录存在的地方
  include: [resolve('src'), resolve('test')],
  // eslint-loader配置过程当中须要指定的选项
  options: {
    // 文件风格的检查的格式化程序,这里使用的是第三方的eslint-friendly-formatter
    formatter: require('eslint-friendly-formatter'),
    // 是否须要eslint输出警告信息
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})
var webpack = require('webpack')
module.exports = {
  // webpack在寻找寻找相对路径的文件时候会以context做为根目录。
  // context默认为执行启动webpack时所在的当前工做目录
  context: path.resolve(__dirname, '../'),
   // entry表示入口,webpack构建的第一步从entry开始
   // 类型能够是string,object,array
  entry: {
    app: './src/main.js'
  },
  output: {
    // 输出文件存放的目录,必须是string类型的绝对目录
    path: config.build.assetsRoot,
    // 经过entry不一样生成不一样的文件名字,详情请看文章
    filename: '[name].js',
    // 发布到线上全部资源的url前缀,为string类型
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath,
    // 导出库的名称,为string类型,不填写的时候,默认输出格式是匿名的当即执行函数
    // library: "KlivitamLibrary"
    // 导出库的类型,为枚举类型,默认是var
    // 可选值: umd umd2 commonjs2,commonjs,amd,this,var,assign,window,global,jsonp
    // libraryTarget: "jsonp"
    // 是否包含游泳的文件信息到生成的代码里
    // pathinfo: true
    // 附加chunk的文件名称
    // chunkFilename: "[id].js"
    // chunkFilename: "[chunkhash].js"
    // jsonp异步加载资源时的回调函数名
    // jsonpFunction: "webpackJsonP"
    // 生成source map文件的名称
    // sourceMapFilename: "[file].map"
    //浏览器开发者工具里显示的远吗模块名称
    // devtoolModuleFilenameTemplate: "webpack:///[resource-path]"
    // 异步加载跨域的资源时使用的方式
    // crossOriginLoading: "use-credentials",
    // crossOriginLoading: "anonymous",
    // crossOriginLoading: false,
  },
  // 配置模块解析时候的一些选项
  resolve: {
    // 自动补全的扩展名,可以使用户在引入模块时不带扩展
    extensions: ['.js', '.vue', '.json'],
    // 默认路径代理,例如 import Vue from 'vue$',会自动到 'vue/dist/vue.esm.js'中寻找
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'components': resolve('src/components'),
      // 能够在引入文件的时候使用pages符号引入src/pages文件夹中的文件
      'pages': resolve('src/pages'),
      'http': resolve('src/http'),
      'public': resolve('src/public'),
      'jquery': 'jquery'
    }
  },
  // 下面是针对具体的模块进行的具体的配置
  // 下面的配置语法采用的是version >= @2的版本
  module: {
    noParse: [/videojs-contrib-hls/], // 不用解析和处理的模块
    // rules是一个数组,其中的每个元素都是一个对象,这个对象是针对具体类型的文件进行的配置。
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/, // 正则匹配loader名字
        loader: 'vue-loader', // loader名字
        // 针对此加载器的具体配置
        // 针对前面的分析,这个配置对象中包含了各类css类型文件的配置,css source map的配置 以及一些transform的配置
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        // js文件的处理主要使用的是babel-loader。在这里没有指定具体的编译规则,babel-loader会自动
        // 读取根目录下面的.babelrc中的babel配置用于编译js文件
        loader: 'babel-loader',
        // 指定须要进行编译的文件的路径
        // 这里表示只对src和test文件夹中的文件进行编译
        include: [resolve('src'), resolve('test'), resolve('config/myapi')] //规则所包含的文件夹
      },
      {
        // 对图片资源进行编译的配置
        // 指定文件的类型
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        // 使用url-loader进行文件资源的编译
        loader: 'url-loader',
        // url-loader的配置选项
        options: {
          // 文件的大小小于10000字节(10kb)的时候会返回一个dataUrl
          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]')
        }
      }
    ]
  },
  // 这些选项用于配置polyfill或mock某些node.js全局变量和模块。
  // 这可使最初为nodejs编写的代码能够在浏览器端运行
  node: {
    // 这个配置是一个对象,其中的每一个属性都是nodejs全局变量或模块的名称
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    // 设置成empty则表示提供一个空对象
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}
复制代码

+build/webpack.dev.conf.jsnode

'use strict'
// 首先引入的是一些工具方法,下面咱们就须要去util文件种看一下有哪些对应的工具方法
const utils = require('./utils')
// 引入webpack模块
const webpack = require('webpack')
// 引入配置文件
// 这个配置文件中包含了一些dev和production环境的基本配置
const config = require('../config')
// 引入webpack-merge模块。这个模块用于把多个webpack配置合并成一个配置,后面的配置会覆盖前面的配置。
const merge = require('webpack-merge')
// 引入webpack的基本设置,这个设置文件包含了开发环境和生产环境的一些公共配置
const baseWebpackConfig = require('./webpack.base.conf')
// 用于生成html文件的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 这个插件可以更好的在终端看到webpack运行时的错误和警告等信息。能够提高开发体验
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 查找一个未使用的端口
const portfinder = require('portfinder')

// 获取host环境变量,用于配置开发环境域名
const HOST = process.env.HOST
// 获取post环境变量,用于配置开发环境时候的端口号
const PORT = process.env.PORT && Number(process.env.PORT)

// 开发环境的完整的配置文件
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    // 为那些独立的css类型文件添加loader配置(没有写在vue文件的style标签中的样式)
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // 开发环境使用'eval-source-map'模式的source map
  // 由于速度快
  devtool: config.dev.devtool,

  // 下面是对webpack-dev-server选项的基本配置,这些配置信息,咱们能够在/config/index.js
  // 文件中进行自定义配置。
  devServer: {
    // 用于配置在开发工具的控制台中显示的日志级别
    // 注意这个不是对bundle的错误和警告的配置,而是对它生成以前的消息的配置
    clientLogLevel: 'warning',
    // 表示当使用html5的history api的时候,任意的404响应都须要被替代为index.html
    historyApiFallback: true,
    // 启用webpack的热替换特性
    hot: true,
    // 一切服务都须要使用gzip压缩
    // 能够在js,css等文件的response header中发现有Content-Encoding:gzip响应头
    compress: true,
    // 指定使用一个 host。默认是 localhost
    // 若是但愿服务器外部能够访问(经过咱们电脑的ip地址和端口号访问咱们的应用)
    // 能够指定0.0.0.0,使用这个可使得同一局域网内全部设备都能访问你的本地网页
    host: HOST || config.dev.host,
    // 指定要监听请求的端口号
    port: PORT || config.dev.port,
    // 是否自动打开浏览器
    open: config.dev.autoOpenBrowser,
    // 当编译出现错误的时候,是否但愿在浏览器中展现一个全屏的蒙层来展现错误信息
    overlay: config.dev.errorOverlay
      // 表示只显示错误信息而不显示警告信息
      // 若是二者都但愿显示,则把这两项都设置为true
      ? { warnings: false, errors: true }
      // 设置为false则表示啥都不显示
      : false,
    // 指定webpack-dev-server的根目录,这个目录下的全部的文件都是能直接经过浏览器访问的
    // 推荐和output.publicPath设置为一致
    publicPath: config.dev.assetsPublicPath,
    // 配置代理,这样咱们就能够跨域访问某些接口
    // 咱们访问的接口,若是符合这个选项的配置,就会经过代理服务器转发咱们的请求
    proxy: config.dev.proxyTable,
    // 启用 quiet 后,除了初始启动信息以外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
    quiet: true, // necessary for FriendlyErrorsPlugin
    // 与监视文件相关的控制选项
    watchOptions: {
      // 若是这个选项为true,会以轮询的方式检查咱们的文件的变更,效率很差
      poll: config.dev.poll,
    }
  },
  plugins: [
    // 建立一个在编译时能够配置的全局变量
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    // 启用热替换模块
    // 记住,咱们永远不要再生产环境中使用hmr
    new webpack.HotModuleReplacementPlugin(),
    // 这个插件的主要做用就是在热加载的时候直接返回更新文件的名称,而不是文件的id
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    // 使用这个插件能够在编译出错的时候来跳过输出阶段,这样能够确保输出资源不会包含错误。
    new webpack.NoEmitOnErrorsPlugin(),

    // 这个插件主要是生成一个html文件
    new HtmlWebpackPlugin({
      // 生成的html文件的名称
      filename: 'index.html',
      // 使用的模板的名称
      template: 'index.html',
      // 将全部的静态文件都插入到body文件的末尾
      inject: true
    }),
  ]
})

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  // 这种获取port的方式会返回一个promise
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // 把获取到的端口号设置为环境变量PORT的值
      process.env.PORT = port
      // 从新设置webpack-dev-server的端口的值
      devWebpackConfig.devServer.port = port

      // 将FriendlyErrorsPlugin添加到webpack的配置文件中
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        // 编译成功时候的输出信息
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        // 当编译出错的时候,根据config.dev.notifyOnErrors来肯定是否须要在桌面右上角显示错误通知框
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      // resolve咱们的配置文件
      resolve(devWebpackConfig)
    }
  })
})
复制代码
  • webpack.prod.conf.js
// 引入path模块
const path = require('path')
// 引入工具方法
const utils = require('./utils')
// 引入webpack模块
const webpack = require('webpack')
// 引入基本的配置
const config = require('../config')
// 引入webpack-merge模块
const merge = require('webpack-merge')
// 引入开发环境和生产环境公共的配置
const baseWebpackConfig = require('./webpack.base.conf')
// 这个模块主要用于在webpack中拷贝文件和文件夹
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 这个插件主要是用于基于模版生成html文件的
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 这个插件主要是用于将入口中全部的chunk,移到独立的分离的css文件中
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 这个插件主要是用于压缩css模块的
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 这个插件主要是用于压缩js文件的
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

// 配置多份环境
let env
if(process.argv[2] === 'test'){
  env=require('../config/dev.env')
}else{
  env=require('../config/prod.env')
}

// 合并公共配置和生产环境独有的配置并返回一个用于生产环境的webpack配置文件
const webpackConfig = merge(baseWebpackConfig, {
  // 用于生产环境的一些loader配置
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      // 在生产环境中使用extract选项,这样就会把thunk中的css代码抽离到一份独立的css文件中去
      extract: true,
      usePostCSS: true
    })
  },
  // 配置生产环境中使用的source map的形式。在这里,生产环境使用的是#source map的形式
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    // build所产生的文件的存放的文件夹地址
    path: config.build.assetsRoot,
    // build以后的文件的名称
    // 这里[name]和[chunkhash]都是占位符
    // 其中[name]指的就是模块的名称
    // [chunkhash]chunk内容的hash字符串,长度为20
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // [id]也是一个占位符,表示的是模块标识符(module identifier)
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // 压缩javascript的插件
    new webpack.optimize.UglifyJsPlugin({
      // 压缩js的时候的一些基本配置
      uglifyOptions: {
        // 配置压缩的行为
        compress: {
          // 在删除未使用的变量等时,显示警告信息,默认就是false
          warnings: false
        }
      },
      // 使用 source map 将错误信息的位置映射到模块(这会减慢编译的速度)
      // 并且这里不能使用cheap-source-map
      sourceMap: config.build.productionSourceMap,
      // 使用多进程并行运行和文件缓存来提升构建速度
      parallel: true
  }),

    // 提取css文件到一个独立的文件中去
    new ExtractTextPlugin({
      // 提取以后css文件存放的地方
      // 其中[name]和[contenthash]都是占位符
      // [name]就是指模块的名称
      // [contenthash]根据提取文件的内容生成的 hash
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // 从全部额外的 chunk(additional chunk) 提取css内容
      // (默认状况下,它仅从初始chunk(initial chunk) 中提取)
      // 当使用 CommonsChunkPlugin 而且在公共 chunk 中有提取的 chunk(来自ExtractTextPlugin.extract)时
      // 这个选项须要设置为true
      allChunks: true,
    }),
    // duplicated CSS from different components can be deduped.
    // 使用这个插件压缩css,主要是由于,对于不一样组件中相同的css能够剔除一部分
    new OptimizeCSSPlugin({
      // 这个选项的全部配置都会传递给cssProcessor
      // cssProcessor使用这些选项决定压缩的行为
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // 建立一个html文件
    new HtmlWebpackPlugin({
      // 建立一个html文件
      filename: config.build.index,
      // 使用的模板的名称
      template: 'index.html',
      // 把script和link标签放在body底部
      inject: true,
      // 配置html的压缩行为
      minify: {
        // 移除注释
        removeComments: true,
        // 去除空格和换行
        collapseWhitespace: true,
        // 尽量移除属性中的引号和空属性
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // chunks 目录
      chunks: ['manifest', 'vendor', 'app'],
      // 控制chunks的顺序,这里表示按照依赖关系进行排序
      // 也能够是一个函数,本身定义排序规则
      chunksSortMode: 'dependency'
    }),
    // 根据模块的相对路径生成一个四位数的hash做为模块id
    new webpack.HashedModuleIdsPlugin(),
    // webpack2处理过的每个模块都会使用一个函数进行包裹
    // 这样会带来一个问题:下降浏览器中JS执行效率,这主要是闭包函数下降了JS引擎解析速度。
    // webpack3中,经过下面这个插件就可以将一些有联系的模块,
    // 放到一个闭包函数里面去,经过减小闭包函数数量从而加快JS的执行速度。
    new webpack.optimize.ModuleConcatenationPlugin(),
    // 这个插件用于提取多入口chunk的公共模块
    // 经过将公共模块提取出来以后,最终合成的文件可以在最开始的时候加载一次
    // 而后缓存起来供后续使用,这会带来速度上的提高。
    new webpack.optimize.CommonsChunkPlugin({
      // 这是 common chunk 的名称
      name: 'vendor',
      // 把全部从mnode_modules中引入的文件提取到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
        )
      }
    }),
    // 为了将项目中的第三方依赖代码抽离出来,官方文档上推荐使用这个插件,当咱们在项目里实际使用以后,
    // 发现一旦更改了 app.js 内的代码,vendor.js 的 hash 也会改变,那么下次上线时,
    // 用户仍然须要从新下载 vendor.js 与 app.js——这样就失去了缓存的意义了。因此第二次new就是解决这个问题的
    // 参考:https://github.com/DDFE/DDFE-blog/issues/10
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: 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
    }),

    // copy custom static assets
    // 拷贝静态资源到build文件夹中
    new CopyWebpackPlugin([
      {
        // 定义要拷贝的资源的源目录
        from: path.resolve(__dirname, '../static'),
        // 定义要拷贝的资源的目标目录
        to: config.build.assetsSubDirectory,
        // 忽略拷贝指定的文件,可使用模糊匹配
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  // 若是开启了生产环境的gzip
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      // 目标资源的名称
      // [path]会被替换成原资源路径
      // [query]会被替换成原查询字符串
      asset: '[path].gz[query]',
      // gzip算法
      // 这个选项能够配置成zlib模块中的各个算法
      // 也能够是(buffer, cb) => cb(buffer)
      algorithm: 'gzip',
      // 处理全部匹配此正则表达式的资源
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      // 只处理比这个值大的资源
      threshold: 10240,
      // 只有压缩率比这个值小的资源才会被处理
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  // 若是须要生成一分bundle报告,则须要使用下面的这个插件
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
复制代码
  • build/check-versions.js
// 在终端为不一样字体显示不一样的风格
const chalk = require('chalk')
// 解析npm包的version
const semver = require('semver')
// 引入package.json文件
const packageConfig = require('../package.json')
// node版本的uninx shell命令
const shell = require('shelljs')
// 执行命令的函数
function exec (cmd) {
 return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
 {
   name: 'node',
   // node的版本
   // process.version就是node的版本
   // semver.clean('v8.8.0') => 8.8.0
   currentVersion: semver.clean(process.version),
   // package.json中定义的node版本的范围
   versionRequirement: packageConfig.engines.node
 }
]

// 至关于 which npm
if (shell.which('npm')) {
 // 若是npm命令存在的话
 versionRequirements.push({
   name: 'npm',
   // 检查npm的版本
   currentVersion: exec('npm --version'),
   // package.json中定义的npm版本
   versionRequirement: packageConfig.engines.npm
 })
}

module.exports = function () {
 const warnings = []

 for (let i = 0; i < versionRequirements.length; i++) {
   const mod = versionRequirements[i]

   // semver.satisfies()进行版本之间的比较
   if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
     // 若是现有的npm或者node的版本比定义的版本低,则生成一段警告
     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/build.js
'use strict'
require('./check-versions')() // 检查npm和node的版本

process.env.NODE_ENV = 'production' // 设置环境变量NODE_ENV的值是production


const ora = require('ora') // 终端的spinner
const rm = require('rimraf') // node.js版本的rm -rf
const path = require('path') // 使用Node自带的文件路径工具
const chalk = require('chalk') // 引入显示终端颜色模块
// const opn = require('opn')   // 一个能够强制打开浏览器并跳转到指定url的插件
const webpack = require('webpack') // 使用webpack
const config = require('../config') // 加载config的配置
const webpackConfig = require('./webpack.prod.conf') // 引入webpack在production环境下的配置文件

const spinner = ora('building for production...')
spinner.start()

// 删除打包目标目录下的文件
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({
      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' )) }) }) 复制代码

###六、最后说两句react

原本想一想还有不少东西要写的,预备是从最基础的方面提及,可是一想到可能会有不少东西要写。外加上我就我本身而言,我都没法去看那么长的博客,况且和我同样浮躁的程序员呢?而且,我在这个上面已经花了将近二周的时间了。本身其实也差很少知道webpack再重要也不过是js的一个附着品,本身就算学的再精湛,也只不过是景上添花,可是处于js快速增加期的我 不该该把大把的时间花在这个上面。总之综合考虑,我决定将webpack分为两个章节:基础篇和深刻篇。
好了,基础篇到这里就结束了,今天从下班开始一直整理到9点多。直接在项目文件上面作的标注,不知道老大看到了会不会打我。无论了 万一是躲不过就git reset😂😂😂
相关文章
相关标签/搜索