Web前端之路让前端飞程序员

webpack文档高级配置

2017-10-13  本文已影响546人  sunny519111

webpack文档高级配置

主要从下面几个方面讲解:

  1. webpack的生产配置环境
  2. webpack的优化机制
    • 模块分离
    • 按需加载
    • 第三方模块利用缓存
  3. webpack的部分插件

webpack的生产配置构建

在开发一个项目的时候,我们往往需要几个环境的切换,例如:开发(dev),生产(production), 测试(test),这里主要讲解开发和生产中的环境配置,webpack官方推荐我们将公共的基本配置放在一起,然后对开发和生产进行不同的配置文件,通过官方提供的插件webpack-merge进行配置的合并,然后通过webpack编译生产。

webpack.common.js // 通用文件

webpack.dev.js // 开发文件

webpack.prod.js // 生产文件

// 通用文件 包含生产和开发的一些基本的使用 webpack.common.js
module.exports = {
  entry: {
    app: './src/index.js',
    vendor: ['lodash']
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name][chunkhash:3].bundle.js',
    chunkFilename: '[name].bundle.js',
    // publicPath: '/'
  },
  plugins: [
    new htmlWebpackPlugin({
      title: 'prodution' 
    }),
    new cleanWebpackPlugin(['dist'])
  ]
}
// 开发文件  在通用文件下面进行拓展,合并并替换个性化的配置 webpack.dev.js
let merge = require('webpack-merge');
let commonConfig =  require('./webpack.common.js');

module.exports = merge(commonConfig, {
    // devtool: 'inline-source-map',
  devtool: 'source-map',
  devServer: {
    contentBase: './dist'
  },
})

// 生产文件   在通用文件下面进行拓展,合并并替换个性化的配置 webpack.prod.js
let merge = require('webpack-merge');
let webpack = require('webpack')
let commonConfig = require('./webpack.common.js');

module.exports = merge(commonConfig, {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      // 利用manifest来进行缓存
      // names: ['vendor', 'manifest'],
      name: 'vendor',
      minChunks: function(module, count) {
        console.log(module.resource)
        console.log(count)
      }
    }),
  ]
})

生产环境使用环境变量

当然这些只是配置文件,如果我们想在配置文件中使用环境变量process.env.NODE_ENV)进行不同情况下面的判定,我们可以通过npm script进行环境变量的配置。windows下要获取环境变量需要使用一个包插件cross-env

// package.json
{
   "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "cross-env NODE_ENV=production node_modules/.bin/webpack --config webpack.prod.js",
    "dev": "cross-env NODE_ENV=development webpack --config webpack.dev.js",
    "dev": "webpack-dev-server --open --config webpack.dev.js  NODE_ENV=production",  // 不能获取环境变量
    "server": "cross-env NODE_ENV=production node server.js"
  }, 
}
 
// 在配置文件中,我们可以通过process.env.NODE_ENV来获取环境变量 
// webpack.prod.js
if(process.env.node_ENV){
  console.log(111)
}
console.log(process.env.NODE_ENV)

一般情况下,我们已经区分了不同的环境下面的不同的webpack配置文件,所以很少通过这样配置环境变量,但是更多的是,我们在源文件中通过不同的环境参数进行不同的判定。但是当开启的是webpack-dev-server --open的时候,不能获取环境变量。

源文件中使用环境变量

webpack提供自带插件webpack.DefinePlugin()来给源文件来配置环境变量。

module.exports = {
  new webpack.DefinePlugin({
    'process.env' : {
      'NODE_ENV': JSON.stringify('development')
    }
  })
}

// src下的index.js
if (process.env.NODE_ENV !== 'production') {
  console.log('Looks like we are in development mode!');
}

这样我可以在源文件中根据不同的环境变量来编写不同的代码,也可以通过环境变量来提示用户信息。


webpack的优化机制

由于JS模块化的的兴起,前端代码从以前的一个大文件变成了一块块的代码,对于每一个模块的引用和使用就是一个问题了,总结由于模块化需要注意的问题。

  1. 对于重复引用的问题,一个模块重复引用,加大文件大小。
  2. 自己内部代码和第三方代码的分离问题
  3. 对于很大的第三方模块代码,一般都不会有很大的改变的情况下,我们能不能利用浏览器缓存技术?
  4. 我能不能按需加载,就是我什么页面需要什么模块就加载什么模块,减轻首页加载的负担?

带着上面的四个问题,我们来探究一下webpack的优化机制。

1. 解决模块重复引用的问题

对于多个入口chunk的重复应用一个模块的情况下。

现在我们有三个模块a.js, b.js index.js

//a.js
export default function() {
  console.log(1)
}

// b.js
import a from 'a.js'
a()

// index.js 
import a from 'a.js'
a()
console.log('This ia index.js')

对于模块a.js, 我们需要在其他的2个模块中使用,webpack打包的时候,就会重复打包a.js, 这样就加大了文件的大小和负担,有没有一种方法可以提前把a.js 存放起来,当我们在使用的时候,直接去调用就行了。webpack官方也考虑了这种情况,提供了一个自带的插件webpack.optimize.CommonsChunkPlugin来处理这种情况。注意的是,options.name不要和entry的name重合,不然会被认为第三方模块被分离。

const webpack = require('webpack');

module.exports = {
  entry: {
    app: './index.js',
    b: './b.js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common',
      minChunks: number|fun|Infinity,
    })
  ]
}
// name是重复引用的模块集合输出的文件名
// minChunks: 对于模块被引用的次数,对于小于次数的模块不会被打包进入,虽然还是会生成common.js文件,但是由于小于次数,不会被打包,所有还是像之前的那样重复的引用,加大大小。
// 当minChunks = Infinity的时候,会马上生成 公共chunk,但里面没有模块。利用缓存的时候可以用到

2. 解决内部模块和第三方模块的分离

由于模块化的好处,我们可以很轻松的使用第三方别人写好的模块,加速我们的代码编写,但是也不方便我们进行维护和进一步的拓展,所有有必要把第三方模块和自己的模块进行分离。CommonsChunkPlugin()提供了我们分离第三方模块的使用。通过entry里面指定对于的name,然后分离代码。

module.exports = {
  entry: {
    app: './index.js',
    vendor: ['lodash', 'axios']
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name][chunkhash:3].bundle.js',
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: Infinity
      // 传入 `Infinity` 会马上生成 公共chunk,但里面没有模块。
    })
  ]
}

3. 利用缓存技术处理第三方修改很少的模块

我们一般在写输出文件的时候,都会利用hash值,让客户端(浏览器)实时更新我们的修改代码,不会走缓存,但是当使用第三方修改很少的模块的时候,我们恰恰需要利用浏览器的缓存,来减少服务器请求,优化页面。webpack一般使用chunkhash来改变文件的hash

按照上面的写法,当我们npm run build的时候,会出现这样的结果,浏览器请求这些js文件的时候

缓存1.png

我们想的是,可以通过某个技术然后让我们在修改内部代码的时候,vendor[hash].build.js的值不改变,这样浏览器就可以利用缓存,不用重复请求了。

CommonsChunkPlugin 有一个较少有人知道的功能是,能够在每次修改后的构建结果中,将 webpack 的样板(boilerplate)和 manifest 提取出来。通过指定 entry 配置中未用到的名称,此插件会自动将我们需要的内容提取到单独的包中。然后就可以通过manifest内部机制,跳过vendor的hash值的改变。

module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      //利用manifest来进行缓存
      names: ['vendor', 'manifest']
    })
  ]
}
// 也可以分开写,但是manifest的必须写在vendor的后面
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      // name只是一个打包后的文件名,这里实际提取的manifest
      name: 'runtime'
    })
  ]
}

这样配置了后,我们再次修改后,第三方模块的文件名是不会更新的,这样我们可以只修改我们内部代码,然后重新打包后,可以不改变第三方模块的打包文件名,很好的利用浏览器的缓存。

4. 按需加载,减少首页的文件请求压力

浏览器加载首页的时候,如果我们把js文件全部都已经打包好,并且全部放在首页加载的话,就会增大请求,从而首页加载的时间也会增加。可不可以我们点击某个按钮或者交互的时候,加载我们需要的部分,而不是在首页全部加载,这样就可以是实现按需加载,从而减少首页请求服务器和时间。

webpack提供2中方式实现按需加载

import()加载模块后返回一个promise,得到一个对象,其中default属性会是加载模块导出的内容。注释的webpackChunkName:name配合output.chunkFilename:[name].js会变成打包后的文件名。

// index.js
function get() {
  button.onclick = e => import(/* webpackChunkName: "print" */ './print').then((module) => {
     var print = module.default;
     print();
  });
}

// webpack config 
module.exports = {
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name][chunkhash:3].bundle.js',
    chunkFilename: '[name].bundle.js',
  },
}
交互之前.png

交互点击后:我们发现才加载已经打包好了的print.bundle.js

交互之后.png
//print.js
export default () => {
  console.log('This is a print')
}
// index.js
function get() {
  // print.js里面没有依赖,有依赖的话需要写在[]中。
  button.onclick = e => require.ensure([],() => {
    let print = require('print');
    print()
  },'print');
}
总结:

import方式和require.ensure决定了什么时候加载打包后的模块,webpackChunkName:namefilename配合webpack的输出配置output.chunkFilename控制文件打包后的文件名。


webpack的部分插件

上面的部分我们已经使用了很多的插件,这里总结一下一些常见的插件plugins

webpack通过options.plugins: []来配置相应的插件特性。

webpack自带插件

CommonsChunksPlugin插件

使用方法:

const webpack = require('webpack')
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      .....
    })
  ]
}

使用用途:

  1. 对于多入口chunk,打包重复被引用的模块。
  2. 分离第三方模块

DefinePlugin插件

在使用DefinePlugin之后,我们可以在源文件内部是用配置好的全局环境变量,为什么这样做呢?可以区分开发模式和发布模式。用过node的同学知道,process.env可以获取环境变量。这里沿用了node的。注意区分构建文件webpack.config.js和源文件index.js

使用方法:

const webpack = require('webpack')
module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env' : { 'NODE_ENV' : JSON.stringify('production') }
    })
  ]
}
// 字符串必须使用'"xx"'这样的形式, 一般都是用JSON.stringify()来转换

使用用途:

  1. 区分开发模式和发布模式
  2. 显示一些版本和一些实用技术

providePlugin插件

提供全局变量,我们不用在全局的去引用这个全局变量。webpack会自动记载模块。

使用方法:

const webpack = require('webpack')
module.exports = {
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      _map: ['lodash', 'map'],
      Vue: ['vue/dist/vue.esm.js', 'default']
    })
  ]
}

使用说明:

  1. 任何时候,使用$的时候,webpack都会自动加载模块jquery,并且$会被jquery的输出的内容所赋值。
  2. 对于es6的export default必须指定模块的 default 属性。例如: Vue
  3. 可以只赋值模块导出的一部分。 例如:lodash

其他插件

extractTextWebpackPlugin插件

用于把css内部样式提取成css单独文件。对于复杂的样式很有用。

使用方法:

npm install --save-dev extract-text-webpack-plugin

const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
  plugins: [
    new ExtractTextPlugin('filename')
  ]
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: "css-loader" 
        })
      }
    ]
  }
}

详细查看webpack插件

html-webpack-plugin插件

用于生成单独的html文件,并自动引入打包后的文件。如果有多个入口文件,也会生成多个index.html文件

使用方法:

npm install --save-dev html-webpack-plugin 

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: 'plugins'
    }) 
  ]
}
// 内部可以配置文件标题,文件名,文件模板等。。。。

详细查看webpack插件

UglifyjsWebpackPlugin

可以在webpack打包后,压缩文件,减少文件的大小。

使用方法:

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
  plugins: [
    new UglifyJSPlugin()
  ]
}

使用说明:

  1. 可以配置压缩的文件include exclude test
  2. source map 的配置,映射错误信息。
上一篇下一篇

猜你喜欢

热点阅读