《深入浅出webpack》 读书笔记

第一章:入门

CommonJS 核心思想是通过 require 方法来 同步加载依赖 的其他模块,通过 module.exports 导出需要暴露的接口
// 导入
const moduleA = require ( './moduleA'); 
// 导出
module.exports = moduleA.someFunc;
AMDCommonJS 最大的不同在于,它采用了 异步的方式去加载依赖 的模块。

AMD 规范主要用于解决针对浏览器环境的模块化问题,最具代表性的实现是 requirejs.org

// 定义一个模块
define ('module', [ 'dep'] , function (dep) {
    return exports;
});

//导入和使用
require (['module'] ,function (module){

    
});
Webpack 有以 下几个核心概念 。
  • Entry: 入口,Webpack执行构建的第一步将从 Entry开始,可抽象成输入。

  • Module: 模块,在 Webpack里一切皆模块,一个模块对应一个文件。Webpack会从配置的 Entry 开始递归找出所有依赖的模块。

  • Chunk: 代码块,一个Chunk由多个模块组合而成,用于代码合并与分割。

  • Loader: 模块转换器,用于将模块的原内容按照需求转换成新内容。

  • Plugin: 扩展插件,在 Webpack构建流程中的特定时机注入扩展逻辑,来改变构 建结果或做我们想要的事情。

  • Output: 输出结果,在Webpack经过一系列处理并得出最终想要的代码后输出结果。

Webpack在启动后会从 Entry 里配置的 Module 开始,递归解析 Entry 依赖的所有 Module。每找到一个 Module ,就会根据配置的 Loader 去找出对应的转换规则,对 Module 进行转换后, 再解析出当前 Module 依赖的 Module。 这些模块会以 Entry 为单位进行分组, 一个 Entry 及其所有依赖的 Module 被分到一个组也就是一个 Chunk。最后, Webpack 会将所有 Chunk 转换成文件输出。在整个流程中, Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。

第二章:配置

context

  • Webpack 在寻找相对路径的文件时会以 context 为根目录, context 默认为执行启动 Webpack 时所在的当前工作目录。
  • 如果想改变 context 的默认配置,则可以在配置文件里设置它 :
module.exports = {
    context: path.resolve (__dirname ,'app')
}

Entry

配置模块的入口,必填。

类型列子含义
string'. / app/entry '入口模块的文件路径,可以是相对路径
array[' ./app/entryl’,'./app/entry2']入口模块的文件路径,可以是相对路径
object{a: '.app/entry-a', b: ['./app/entry-bl', './app/entry-b2']}配置多个入口,每个入口生成一个 Chunk
  • 如果 entry 是一个 stringarray,就只会生成一个 Chunk,这时 Chunk 的名 称是main

  • 如果 entry 是一个 object,就可能会出现多个 Chunk,这时 Chunk 的名称是 object 键值对中键的名称。

Output

filename
  • 输出文件的名称
变量名含义
idChunk 的唯一标识,从 0开始
nameChunk 的名称
hashChunk的唯一标识的 Hash
chunkhashChunk 内容的 Hash
  • hashchunkhash 的长度是可指定的,[ hash:8] 代表取 8Hash 值,默认是 20
hashchunkhashcontenthash区别
  • hash: 只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值

  • chunkhash:根据不同的入口文件进行依赖文件解析、构建对应的 chunk,生成对应的哈希值。

  • contenthash: 使用extra-text-webpack-plugin里的contenthash值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建

chunkFilename
  • 只用于指定在运行过程中生成的 Chunk 在输出时的文件名称。会在运行时生成 Chunk 的常见场景包括:使用 CommonChunkPlugin、使用 import ('path/to/module')动态加载等
path
  • 配置输出文件存放在本地的目录,必须是 string 类型的绝对路径
publicPath
  • 配置发布到线上资源的 URL 前缀,为 string 类型。默认值是空字符串 "",即使用相对路径。

Module

rules
  • 配置模块的读取和解析规则,通常用来配置 Loader。其类型是一个数组,数组里
    的每一项都描述了如何处理部分文件。配置一项 rules 时大致可通过以下方式来完成。

  • 条件匹配: 通过 testincludeexclude 三个配置项来选中 Loader 要应用规则的文件。

  • 应用规则: 对选中的文件通过 use 配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader ,同时可以分别向 Loader 传入参数。

  • 重置顺序: 一组 Loader 的执行顺序默认是从右到左执行的,通过 enforce 选项可以将其中 一个 Loader 的执行顺序放到最前(pre)或者最后(post) 。

noParse
  • noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析和处理,这 样做的好处是能提高构建性能。

Resolve

  • Resolve 配置 Webpack 如何寻找模块所对应的文件。
  • Webpack 内置 JavaScript 模块化语法解析功能,默认会采用模块化标准里约定的规 则去寻找,但也可以根据自己的需要修改默认的规则。
alias
  • 通过别名来将原导入路径映射成一个新的导入路径
// Webpack alias 配置
resolve:{
    alias : {
        components : './src/components/'
    }
}

当通过 import Button from 'components/button'导入时,实际上被 alias 等 价替换成了 import Button from './src/components/button '

mainFields
  • Webpack 会根据 mainFields 的配置去决定 优先采用哪份代码, mainFields 默认如下:
    mainFields : ['browser', 'main'],
    Webpack 会按照数组里的顺序在 package.json 文件里寻找,只会使用找到的第 1

  • 假如我们想优先采用 ES6 的那份代码,则可以这样配置: mainFields: ['jsnext:main','browser','main']

extensions
  • 在导入语句没带文件后缀时, Webpack 会自动带上后缀后去尝试访问文件是否存在。

  • resolve.extensions 用于配置在尝试过程 中用到的后缀列表,默认是:
    extensions: ['.js','.json']
    也就是说,当遇到 require ('./data ')这样的导入语句时, Webpack 会先寻找./data.js 文件,如果该文件不存在 , 就去寻找 ./data.json 文件,如果还 是找不到,就报错 。 假如我们想让 Webpack优先使用目录下的 Typescript文件,则可以这样配置:
    extensions:['.ts','.j5','.json']

modules
  • 配置 Webpack 去哪些目录下寻找第三方模块,默认是只会去 node_modules 目录下寻找。

  • 有时项目里会有一些模块会大量被其它模块依赖和导入,由于其它模块的位置分布不定,针对不同的文件都要去计算被导入模块文件的相对路径,这个路径有时候会很长,就像这样 import '../../../components/button' 这时你可以利用 modules 配置项优化,假如那些被大量导入的模块都在 ./src/components 目录下,把 modules 配置成 modules:['./src/components','node_modules'] 后,你可以简单通过 import 'button' 导入。

descriptionFiles
  • 配置描述第三方模块的文件名称,也就是 package.json 文件。默认如下:
    descriptionFiles: ['package.json']
enforceExtension
  • 如果配置为 true 所有导入语句都必须要带文件后缀,
  • 例如开启前 import './foo' 能正常工作,开启后就必须写成import './foo.js'
enforceModuleExtension
  • enforceModuleExtensionenforceExtension 作用类似,但 enforceModuleExtension 只对 node_modules 下的模块生效。 enforceModuleExtension 通常搭配 enforceExtension 使用,在 enforceExtension:true 时,因为安装的第三方模块中大多数导入语句没带文件后缀, 所以这时通过配置 enforceModuleExtension:false 来兼容第三方模块。

Plugin

  • 配置项接受一个数组,数组里面的每一项都是 plugin 的实例。
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  
  ...
  plugins: [
    ...
    new webpack.optimize.UglifyJsPlugin()
    ...
  ]
  ...
};

DevServer

  • 只有在通过DevServer去启动webpack时配置文件里devServer才会生效。
hot
  • devServer.hot 配置是否启用模块热替换功能。DevServer 默认是在发现源代码被更新后会通过自动刷新整个页面来做到实现预览,开启模块热替换功能后在不刷新整个页面的情况下通过用心模块替换老模块来实现实时预览。
inline
  • DevServer 的实时预览功能依赖注入到页面里的代理客户端去接受来自DevServer的命令和负责刷新网页的工作。devServer.inline用于配置是否自动注入这个代理客户端到将运行在页面的Chunk里面,默认是会自动注入。DevServer会根据你是否开启inline来调整它的自动刷新策略:
  • 如果开启inlineDevServer会在构建完变化后的代码时通过代理客户端控制网页刷新。
  • 如果关闭inlineDevServer将无法直接控制要开发的网页。这时它会通过iframe的方式去运行要开发的网页,当构建完变化后的代码通过刷新iframe来实现实时预览。
historyApiFallback
  • devServer.historyApiFallback用于方便的开发使用HTML5 HistroyAPI的单页应用。
historyApiFallback: {
    //使用正则匹配命中路由
    rewrites: [
        {from: /^\/user/, to: '/user.html'},
        {from: /^\/game/, to: '/game.html'},
        {from: /./, to: '/index.html'}
    ]
}
contentBase
  • devServer.contentBase配置DevServer HTTP 服务器的文件根目录。默认情况下为当前执行目录,所以一般不必设置它,除非有额外的文件需要被DevServer服务。例如你想把项目根目录下的public目录设置成DevServer服务器的文件根目录:
devServer: {
    contentBase: path.join(__dirname, 'public')
}
  • DevServer服务器通过HTTP服务暴露出的文件分为两类:
  1. 暴露本地文件
  1. 暴露webpack构建出的结果,由于构建出的结果交给DevServer,所以你在使用DevServer时在本地找不到构建出的文件。

contentBase只能用来配置暴露本地文件的规则,你可以通过contentBase: false来关闭暴露本地文件。

headers
  • devServer.headers 配置项可以在 HTTP 响应中注入一些HTTP响应头,使用如下:
devServer: {
    headers: {
        'X-foo': 'bar'
    }
}
host
  • devServer.host 配置项用于配置DevServer服务器监听的地址。
port
  • devServer.port 配置项用于配置DevServer服务监听的端口,默认使用8080端口。
allowedHosts
  • devServer.allowedHosts配置一个白名单列表,只有HTTP请求的HOST在列表里才正常返回,使用如下:
allowedHosts: [
    //匹配单个域名
    'host.com',
    'sub.host.com',
    // host.com 和所有的子域名 *.host.com 都将匹配
    '.host.com'
]
disableHostCheck
  • devServer.disableHostCheck配置项用于配置是否关闭用于DNS重绑定的HTTP请求的host检查。DevServer 默认只接受来自本地的请求,关闭后可以接受来自任何 HOST 的请求。
https
  • DevServer 默认使用 HTTP 协议服务,它也能通过 HTTPS 协议服务。
devServer:{
  https: true
}
DevServer 会自动的为你生成一份 HTTPS 证书

devServer:{
  https: {
    key: fs.readFileSync('path/to/server.key'),
    cert: fs.readFileSync('path/to/server.crt'),
    ca: fs.readFileSync('path/to/ca.pem')
  }
}
clientLogLevel
  • devServer.clientLogLevel 配置在客户端的日志等级,可取如下之一的值 noneerrorwarninginfo。 默认为 info 级别,即输出所有类型的日志,设置成 none 可以不输出任何日志。
compress
  • devServer.compress 配置是否启用 gzip压缩。boolean 为类型,默认为 false
open
  • devServer.open 用于在DevServer 启动且第一次构建完时自动用你系统上默认的浏览器去打开要开发的网页。 同时还提供 devServer.openPage 配置项用于打开指定 URL 的网页。

其他配置项

Target
  • target配置项可以让webpack构建出针对不同运行环境的代码。target可以是以下之一:
  1. web: 针对浏览器(默认),所有代码都集中在一个文件里
  2. node:针对node,使用require语句加载chunk代码
  3. async-node: 针对node, 异步加载Chunk代码
  4. webworker:针对webworker
  5. electron-main:针对Electron主线程
  6. electron-renderer:针对electron渲染线程
Devtool
  • devtool配置webpack如何生成Source Map, 默认值是false,即不生成Source Map, 想为构建出的代码生成Source Map以方便调试,可以这样配置:
module.exports = {
    devtool: 'scource-map'
}
watchwatchOptions
  • webpack的监听模式,支持监听文件更新,在文件发生变化时重新编译。
  • 在使用webpack时监听模式默认是关闭的,想打开需要如下配置:
module.exports = {
    watch: true
}
  • 在使用DevServer时,监听模式默认是开启的。
  • 除此之外,webpack还提供了watchOptions配置项去更灵活的控制监听模式:
module.export = {
    //只有开启监听模式的时候,watchOptions才有意义
    watch: true,
    //监听模式运行时参数
    watchOptions: {
        //不监听的文件或文件夹,支持正则匹配
        //默认为空
        ignored: /node_modules/,
        //监听到变化后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
        //默认为300ms
        aggregateTimeout: 300,
        //判断文件是否发生变化是通过不停的询问系统指定文件有没有变化实现的
        //默认每秒问1000次
        poll: 1000
    }
}
Externals
  • Externals用来告诉webpack要构建的代码中使用了哪些不用被打包的模块,也就是说这些模板是外部环境提供的,webpack在打包的时候可以忽略它们。例如:<script src="path/to/jquery.js"></script>,引入jquery后,全局变量jquery就会被注入到网页的JavaScript运行环境。

  • 如果想在使用模块化的源代码里导入和使用jquery,可能需要这样:import $ from 'jquery';构建后会发现输出的Chunk中包含jquery库的内容,这导致了jquery库出现了两次,浪费了加载流量,我们做到的最好是不要在chunk里包含jquery

module.export = {
    externals: {
        //把导入语句里的jquery替换成运行环境里的jQuery
        jquery: 'jQuery'
    }
}
ResolveLoader
  • ResolveLoader告诉webpack如何去寻找loader。因为在使用loader时是通过其包名称去引用的,webpack需要根据配置的loader包名去找到loader的实际代码,以调用loader去处理源文件。
module.exports = {
  resolveLoader:{
    // 去哪个目录下寻找 Loader
    modules: ['node_modules'],
    // 入口文件的后缀
    extensions: ['.js', '.json'],
    // 指明入口文件位置的字段
    mainFields: ['loader', 'main']
  }
}
  • 该配置常用加载本地的loader

webpack的整体配置结构

const path = require('path')

module.exports = {
    //entry表示入口, webpack执行构建的第一步将从entry开始,可抽象成输入。
    //类型可以是string | object | array
    entry: './app/entry', //只有1个入口,入口只有一个文件
    entry: ['./app/entry1', './app/entry2'], //只有一个入口,入口有两个文件
    entry: {    //有2个入口
        a: './app/entry-a',
        b: ['./app/entry-b1', './app/entry-b2']
    }//如何输出结果: 在webpack经过一系列处理后,如何输出最终想要的代码。
    output: {
        //输出文件存放的目录, 必须是string类型的绝对路径
        path: path.join(__dirname, 'dist')
        //输出文件的名称
        filename: 'bundle.js', //完整的名称
        filename: '[name].js', //当配置多个entry时,通过名称模板为不同的entry生成不同的文件名称
        filename: '[chunkhash].js', //根据文件内容hash值生成文件名称,用于浏览器长时间缓存
        
        //发布到线上的所有资源的URL前缀,string类型
        publicPath: '/assets/', //放到指定目录下
        publicPath: '', //放到根目录下
        publicPath: 'https://cdn.example.com/'放到cdn上去
        
        //导出库的名称, string类型
        //不填它时, 默认输出格式是匿名的立即执行函数
        library: 'MyLibrary',
        
        //导出库的类型,枚举类型,默认是var
        //可以是umd | umd2 | commonjs2 | commonjs | amd | this | var。。。
        libraryTarget: 'umd'
        
        //是否包含有用的文件路径信息到生成的代码里去,boolean类型
        pathinfo: true,
        
        //附加Chunk的文件名称
        chunkFilename: '[id].js',
        chunkFilename: '[chunkhash].js'
        
        //JSONP异步加载资源时的回调函数名称, 需要和服务端搭配使用
        jsonpFunction: 'myWebpackJsonp'
        
        //生成的Source Map文件名称
        sourceMapFilename: '[file].map',
        
        //浏览器开发者工具里显示的源码模块名称
        devtoolModuleFilenameTemplate: 'webpack:///[resource-path]',
        
        //异步加载跨域资源时使用的方式
        crossOriginLoading: 'use-credentials',
        crossOriginLoading: 'anonymous',
        crossOriginLoading: false,
    },
    
    //配置模块相关
    module: {
        //配置loader
        rules: [
            {
                test: /\.jsx?$/, //正则匹配命中要使用loader的文件
                include: [    //只会命中这里面的文件
                    path.resolve(__dirname, 'app')
                ],
                exclude: [ //忽略这里的文件
                    path.resolve(__dirname, 'app/demo-files')
                ],
                use: [    //使用哪些loader
                    'style-loader',    //直接使用loader名称
                    {
                        loader: 'css-loader',
                        options: { //loader传递参数
                            
                        }
                    }
                ]
            }
        ],
        noParese: [ //不用解析和处理的模块
            /special-library\.js$/  // 用正则匹配
        ]
    },
    
    //配置插件
    plugins: [],
    
    //配置寻找模块的规则
    resolve: {
        modules: [ //寻找模块的根目录,array类型,默认以node_modules为根目录
            'node_modules',
            path.resolve(__diranme, 'app')
        ],
        extensions: ['.js','.json','.jsx','.css'], //模块的后缀名
        alias: { //模块别名配置, 用于映射模块
            //把module映射new-module, 同样的'module/path/file'也会被映射到new-module/path/file
            'module': 'new-module',
            //使用结尾符号$后,把'only-module'映射成'new-module'
            //但是不想上面的,'module/path/file',不会被映射成new-module/path/file
            'only-module$': 'new-module'
        },
        alias: [ //alias还支持使用数组来详细配置
            {
                name: 'modules' //老的模块
                alias: 'new-module', //新的模块
                //是否是只映射模块,如果是true只有'module'会被映射,如果是 false 'module/inner/path' 也会被映射
                onlyModule: true
            }
        ]symlinks: true, //是否跟随文件软链接去搜寻模块的路径
        descriptionFile: ['package.json'], //模块的描述文件
        mainFields: ['main'], 模块的描述文件里的描述入口的文件的字段名称
        enforceExtension: false, //是否强制导入语句必须要写明文件后缀
    }//输出文件性能检查配置
    performance: {
        hints: warning, //有性能问题时输出警告
        hints: 'error', //有性能问题时输出错误
        hints: false, //关闭性能检查
        maxAssetSize: 200000, //最大文件大小,单位bytes
        maxEntrypointSize: 400000, //最大入口文件大小(单位bytes)
        assetFilter: function(assetFilename) { //过滤要检查的文件
            return assetFilename.endsWith('.css') || assetFilename.endsWith('.js')
        }
    },
    
    devtool: 'source-map', //配置source-map类型
    
    context: __dirname, //webpack使用的根目录,string类型必须是绝对路径
    
    //配置输出代码的运行环境
    target: 'web', // 浏览器,默认
    target: 'webworker', // WebWorker
    target: 'node', // Node.js,使用 `require` 语句加载 Chunk 代码
    target: 'async-node', // Node.js,异步加载 Chunk 代码
    target: 'node-webkit', // nw.js
    target: 'electron-main', // electron, 主线程
    target: 'electron-renderer', // electron, 渲染线程
    
    externals: {    //使用来自JavaScript运行环境提供的全部变量
        jquery: 'jQuery'
    },
    
    stats: { // 控制台输出日志控制
        assets: true,
        colors: true,
        errors: true,
        errorDetails: true,
        hash: true,
      },
      devServer: { // DevServer 相关的配置
    proxy: { // 代理到后端服务接口
      '/api': 'http://localhost:3000'
    },
    contentBase: path.join(__dirname, 'public'), // 配置 DevServer HTTP 服务器的文件根目录
    compress: true, // 是否开启 gzip 压缩
    historyApiFallback: true, // 是否开发 HTML5 History API 网页
    hot: true, // 是否开启模块热替换功能
    https: false, // 是否开启 HTTPS 模式
    },

    profile: true, // 是否捕捉 Webpack 构建的性能信息,用于分析什么原因导致构建性能不佳

    cache: false, // 是否启用缓存提升构建速度

    watch: true, // 是否开始
    watchOptions: { // 监听模式选项
    // 不监听的文件或文件夹,支持正则匹配。默认为空
    ignored: /node_modules/,
    // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
    // 默认为300ms 
    aggregateTimeout: 300,
    // 判断文件是否发生变化是不停的去询问系统指定文件有没有变化,默认每秒问 1000 次
    poll: 1000
  },
}

第三章实战

加载图片 file-loader

...
rules:[
    ...
    {
        test: /\.png$/,
        use: ["file-loader"]
    },
    ...
]
...

图片转base64 url-loader

...
rules:[
    ...
    {
        test: /\.png$/,
        use: [{
            loader: "file-loader",
            options:{
                limit: 1024 * 30,
                failback: "file-loader"
            }
        }]
    },
    ...
]
...

图片压缩 imagemin-webpack-plugin

const ImageminPlugin = require('imagemin-webpack-plugin').default
module.exports = {
  plugins: [

    new ImageminPlugin({
      disable: process.env.NODE_ENV !== 'production', 
      pngquant: {
        quality: '95-100'
      }
    })
  ]
}

生成雪碧图 webpack-spritesmith


const path = require('path');
const SpritesmithPlugin = require('webpack-spritesmith');
 
module.exports = {
    // ...
    module: {
        rules: [
            {test: /\.png$/, use: [
                'file-loader?name=i/[hash].[ext]'
            ]}
        ]
    },
    resolve: {
        modules: ["node_modules", "spritesmith-generated"]
    },
    plugins: [
        new SpritesmithPlugin({
            src: {
                cwd: path.resolve(__dirname, 'src/ico'),
                glob: '*.png'
            },
            target: {
                image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
                css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl')
            },
            apiOptions: {
                cssImageRef: "~sprite.png"
            }
        })
    ]
    // ...
};
 

SVG 压缩 svg-inline-loader

...
rules:[
    ...
    {
        test: /\.svg/,
        use: ["svg-inline-loader"]
    },
    ...
]
...

第四章优化

优化项

  • 使用 Loader 时,可以通过 testincludeexclude 三个配置项来命中 Loader 要应用规则的文件

  • resolve.modules 用于配置 Webpack 去哪些目录下寻找第三方模块。默认值是 ["node modules "]。使用绝对路径指明第三方模块存放的位置,以减少搜索步骤。

  • resolve.mainFields 用于配置第三方模块使用哪 个入口文件(在其package. json中)。

resolve.mainFields 的默认值和当前的target 配置有关系,对应的关系如 下。

  • targetweb 或者 webworker 时,值是 ['browser','module','main']
  • target 为其他情况时,值是[ 'module','main']
  • resolve.alias 配置项通过别名来将原导入路径映射成一个新的导入路径。

  • resolve.extensions 用于配置在尝试过程中用到的后缀列表,默认是:
    extensions : [ '.js' , '.json']

  • module.noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能

  • 动态链接库 DllPlugin 插件

  • 将任务分解给多个子进程去并发执行,子进程处理完后再将结果发送给主进程。HappyPack插件

  • ParallelUglifyPlugin插件 会开启多个子进程,将对多个文件的压缩工作分配给多个 子进程去完成.

  • UglifyJsPlugin 压缩 JS

  • uglifyjs-webpack-plugin 压缩 ES6

  • 开启 css-loaderminimize 选项压缩 CSS

  • 借助 publicpath 开启 CDN

  • Tree Shaking 去除死代码

  • CommonsChunkPlugin 提取公共代码

  • 按需加载 import (***).then()

  • Prepack 优化代码运行效率

  • Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行更快

侧重优化开发体验

// 侧重优化开发体验
const path = require('path');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const {AutoWebPlugin} = require('web-webpack-plugin');
const HappyPack = require('happypack');

// 自动寻找 pages 目录下的所有目录,把每一个目录看成一个单页应用
const autoWebPlugin = new AutoWebPlugin('./src/pages', {
  // HTML 模版文件所在的文件路径
  template: './template.html',
  // 提取出所有页面公共的代码
  commonsChunk: {
    // 提取出公共代码 Chunk 的名称
    name: 'common',
  },
});

module.exports = {
  // AutoWebPlugin 会找为寻找到的所有单页应用,生成对应的入口配置,
  // autoWebPlugin.entry 方法可以获取到生成入口配置
  entry: autoWebPlugin.entry({
    // 这里可以加入你额外需要的 Chunk 入口
    base: './src/base.js',
  }),
  output: {
    filename: '[name].js',
  },
  resolve: {
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    // 其中 __dirname 表示当前工作目录,也就是项目根目录
    modules: [path.resolve(__dirname, 'node_modules')],
    // 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件,使用 Tree Shaking 优化
    // 只采用 main 字段作为入口文件描述字段,以减少搜索步骤
    mainFields: ['jsnext:main', 'main'],
  },
  module: {
    rules: [
      {
        // 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
        test: /\.js$/,
        // 使用 HappyPack 加速构建
        use: ['happypack/loader?id=babel'],
        // 只对项目根目录下的 src 目录中的文件采用 babel-loader
        include: path.resolve(__dirname, 'src'),
      },
      {
        test: /\.js$/,
        use: ['happypack/loader?id=ui-component'],
        include: path.resolve(__dirname, 'src'),
      },
      {
        // 增加对 CSS 文件的支持
        test: /\.css$/,
        use: ['happypack/loader?id=css'],
      },
    ]
  },
  plugins: [
    autoWebPlugin,
    // 使用 HappyPack 加速构建
    new HappyPack({
      id: 'babel',
      // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
      loaders: ['babel-loader?cacheDirectory'],
    }),
    new HappyPack({
      // UI 组件加载拆分
      id: 'ui-component',
      loaders: [{
        loader: 'ui-component-loader',
        options: {
          lib: 'antd',
          style: 'style/index.css',
          camel2: '-'
        }
      }],
    }),
    new HappyPack({
      id: 'css',
      // 如何处理 .css 文件,用法和 Loader 配置中一样
      loaders: ['style-loader', 'css-loader'],
    }),
    // 4-11提取公共代码
    new CommonsChunkPlugin({
      // 从 common 和 base 两个现成的 Chunk 中提取公共的部分
      chunks: ['common', 'base'],
      // 把公共的部分放到 base 中
      name: 'base'
    }),
  ],
  watchOptions: {
    // 4-5使用自动刷新:不监听的 node_modules 目录下的文件
    ignored: /node_modules/,
  }
};

侧重优化输出质量

// 侧重优化输出质量
const path = require('path');
const DefinePlugin = require('webpack/lib/DefinePlugin');
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const {AutoWebPlugin} = require('web-webpack-plugin');
const HappyPack = require('happypack');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');

// 自动寻找 pages 目录下的所有目录,把每一个目录看成一个单页应用
const autoWebPlugin = new AutoWebPlugin('./src/pages', {
  // HTML 模版文件所在的文件路径
  template: './template.html',
  // 提取出所有页面公共的代码
  commonsChunk: {
    // 提取出公共代码 Chunk 的名称
    name: 'common',
  },
  // 指定存放 CSS 文件的 CDN 目录 URL
  stylePublicPath: '//css.cdn.com/id/',
});

module.exports = {
  // AutoWebPlugin 会找为寻找到的所有单页应用,生成对应的入口配置,
  // autoWebPlugin.entry 方法可以获取到生成入口配置
  entry: autoWebPlugin.entry({
    // 这里可以加入你额外需要的 Chunk 入口
    base: './src/base.js',
  }),
  output: {
    // 给输出的文件名称加上 Hash 值
    filename: '[name]_[chunkhash:8].js',
    path: path.resolve(__dirname, './dist'),
    // 指定存放 JavaScript 文件的 CDN 目录 URL
    publicPath: '//js.cdn.com/id/',
  },
  resolve: {
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    // 其中 __dirname 表示当前工作目录,也就是项目根目录
    modules: [path.resolve(__dirname, 'node_modules')],
    // 只采用 main 字段作为入口文件描述字段,以减少搜索步骤
    mainFields: ['jsnext:main', 'main'],
  },
  module: {
    rules: [
      {
        // 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
        test: /\.js$/,
        // 使用 HappyPack 加速构建
        use: ['happypack/loader?id=babel'],
        // 只对项目根目录下的 src 目录中的文件采用 babel-loader
        include: path.resolve(__dirname, 'src'),
      },
      {
        test: /\.js$/,
        use: ['happypack/loader?id=ui-component'],
        include: path.resolve(__dirname, 'src'),
      },
      {
        // 增加对 CSS 文件的支持
        test: /\.css$/,
        // 提取出 Chunk 中的 CSS 代码到单独的文件中
        use: ExtractTextPlugin.extract({
          use: ['happypack/loader?id=css'],
          // 指定存放 CSS 中导入的资源(例如图片)的 CDN 目录 URL
          publicPath: '//img.cdn.com/id/'
        }),
      },
    ]
  },
  plugins: [
    autoWebPlugin,
    // 4-14开启ScopeHoisting
    new ModuleConcatenationPlugin(),
    // 4-3使用HappyPack
    new HappyPack({
      // 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
      id: 'babel',
      // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
      loaders: ['babel-loader?cacheDirectory'],
    }),
    new HappyPack({
      // UI 组件加载拆分
      id: 'ui-component',
      loaders: [{
        loader: 'ui-component-loader',
        options: {
          lib: 'antd',
          style: 'style/index.css',
          camel2: '-'
        }
      }],
    }),
    new HappyPack({
      id: 'css',
      // 如何处理 .css 文件,用法和 Loader 配置中一样
      // 通过 minimize 选项压缩 CSS 代码
      loaders: ['css-loader?minimize'],
    }),
    new ExtractTextPlugin({
      // 给输出的 CSS 文件名称加上 Hash 值
      filename: `[name]_[contenthash:8].css`,
    }),
    // 4-11提取公共代码
    new CommonsChunkPlugin({
      // 从 common 和 base 两个现成的 Chunk 中提取公共的部分
      chunks: ['common', 'base'],
      // 把公共的部分放到 base 中
      name: 'base'
    }),
    new DefinePlugin({
      // 定义 NODE_ENV 环境变量为 production 去除 react 代码中的开发时才需要的部分
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    // 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
    new ParallelUglifyPlugin({
      // 传递给 UglifyJS 的参数
      uglifyJS: {
        output: {
          // 最紧凑的输出
          beautify: false,
          // 删除所有的注释
          comments: false,
        },
        compress: {
          // 在UglifyJs删除没有用到的代码时不输出警告
          warnings: false,
          // 删除所有的 `console` 语句,可以兼容ie浏览器
          drop_console: true,
          // 内嵌定义了但是只用到一次的变量
          collapse_vars: true,
          // 提取出出现多次但是没有定义成变量去引用的静态值
          reduce_vars: true,
        }
      },
    }),
  ]
};

第五章 Webpack 运行流程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程 。

  • 初始化参数 : 从配置文件和 Shell语句中读取与合并参数,得出最终的参数 。

  • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,通过执行对象的 run 方法开始执行编译 。

  • 确定入口 : 根据配置中的 entry 找出所有入口文件

  • 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理 。

  • 完成模块编译 : 在经过第 4 步使用 Loader 翻译完所有模块后, 得到了每个模块被翻译后的最终内容及它们之间的依赖关系。

  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk, 再将每个 Chunk 转换成一个单独的文件加入输出列表中,这是可以修改输出内容 的最后机会 。

  • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,将文件的内容写入文件系统中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值