webpack文档高级配置

作者: sunny519111 | 来源:发表于2017-10-13 16:56 被阅读546次

    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中方式实现按需加载

    • ES6 的import加载 (官网推荐使用这种方式加载)

    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
    • webpack自带的require.ensure(dependencies, callback,filename)
    //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 的配置,映射错误信息。

    相关文章

      网友评论

        本文标题:webpack文档高级配置

        本文链接:https://www.haomeiwen.com/subject/whozyxtx.html