webpack3终极配置及其优化(react)

本文主要研究webpack3版本的react项目配置。

webpack3终极配置及其优化(react)_第1张图片

    • webpack的原理
      • webpack几大概念
      • webpack执行流程
      • webpack如何解析文件与文件之间的联系?
      • 为什么要使用loader?
      • 为什么要使用plugin?
    • webpack的基础搭建
      • 基本配置
        • Scope Hoisting
        • webpack针对react-route的按需加载
      • 环境配置
      • loaders
        • scss loaders
        • postcss
        • js loaders
        • babel
        • 图片loaders
        • 精灵图
      • plugins
        • UglifyJsPlugin
        • webpack-parallel-uglify-plugin
        • CommonsChunkPlugin
      • 热替换与自动刷新
        • 插件HotModuleReplacementPlugin
        • 开启watch模式
        • webpack-dev-server
    • webpack的性能优化
      • happypack
      • 缩小范围
      • DllPlugin
      • etc
    • webpack相较于其他构建工具的优势与劣势

webpack的原理

在普通开发者看来,webpack就是一个黑盒。

webpack3终极配置及其优化(react)_第2张图片
由一个或多个Entry(输入)传入,最后输出一个或多个Output(输出)

webpack的内部具体是如何处理的呢?

webpack几大概念

  • Entry: 输入。webpack从此处开始执行构建。
  • Output:输出。webpack最终处理输出的文件。
  • Plugin: 插件。webpack处理时抛出事件,插件接受事件处理额外的逻辑。
  • Loader:模块转换器。内容转换。
  • Module:模块。一个文件就是一个模块。
  • Chunk:块。一个块有一个或多个模块组成。

webpack执行流程

  1. 首先webpack会把Entry(输入)文件当作一个Module(模块)
  2. (使用Loaders,稍后会讲)翻译Module文件,并识别出Module文件的所有依赖文件。
  3. 然后对其依赖的文件再进行步骤2。
  4. 所有Module识别完之后,根据每个模块和Entry的对应关系,组装成一个或多个Chunk,并输出到输出列表。
  5. 根据Output的相关配置,输出到文件里。
  6. 这个过程中,webpack会在操作过程中广播特定事件,PLugin(插件)会在自己感兴趣的事件后,执行特定逻辑。

webpack如何解析文件与文件之间的联系?

对于模块与模之间的联系,开发者通常会
- 使用script标签引入
- 通过commonjs(commonjs1,commonjs2)引入
- 通过AMD(require.js)引入
- 通过es6定义的模块引入方式引入

除了第一种方式,webpack都可以识别,作为文件依赖的标志,我们可以通过config配置,去支持或禁用哪一种引入方式。

为什么要使用loader?

假设我们写了这么一行代码:

import style from './index.css'

不好意思,webpack解析不了。

webpack3终极配置及其优化(react)_第3张图片

webpack原生不能解析css文件,目前webpack只能原生解析js文件,需要Loader转换并插入到js文件。还有图片文件等等都需要Loader支持。(具体配置见后文)

为什么要使用plugin?

Plugin可以大大扩展webpack的功能,哈哈。(具体配置见后文)

webpack的基础搭建

基本配置

只要有输入,输出,webpack就能跑起来了。

// commonJS语法
const path = require(path);
const config = {
    entry: 'app.js',
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, '')
    }

}

module.exports = config;

如果我们定义了多个入口文件,我们就会输出与入口文件数量对应的多个包。

Scope Hoisting

生成的output文件本身是一个自执行函数,在webpack3版本,对于导出的文件还会进行Scope Hoisting

打个比方。

// a.js
import b from 'b.js'
document.write("hello" + b);
// b.js

export default 'world';

假设我们的a.js是webpack的入口文件,a.js文件依赖b.js文件。webpack3打包之后会是这样:

// bundle.js

!function(){
....

}([function(e, t, r) {
    "use strict";
    r.r(t);
    document.write("hello world")
}])

b文件的作用域被提升了(webpack2就不会如此)。这会大大减小最后打包之后的体积。并且打包之后作用域变少,减小内存开销。

注: 只有es6代码可以被Scope Hoisting,而且,如果一个文件被多个文件依赖,就不会使用Scope Hoisting

所以我们如果对于webpack打包的速度,打包后的体积不满意,可以试着更新webpack的版本哦。

webpack针对react-route的按需加载

如果您的项目使用了react-route,webpack会对您的应用进行打包的时候,根据您的路由,打包成多个chunk输出。

比如: 我配置的路由有upload和plan两个路由。webpack会帮您打包成两个输出文件。实现按需加载。

环境配置

生产环境,发布环境代码要不同的嘛。
所以会做环境的区分。

// webpack.config.js
const envDevelopment = project.env === 'development'
const envProduction = project.env === 'production'
// package.json

"dev": "cross-env NODE_ENV=development webpack -p --config webpack.config.js",
"prod": "cross-env NODE_ENV=production webpack -p --config webpack.config.js",

loaders

scss loaders

module.exports = { module: {
rules: [

     test: /\.scss/ ,  // 使用正则表示,匹配文件名
     use: [’style-loader’,’css-loader’,’sass-loader’]
   ]
 }
}

对于use,从后向前解析。先使用sass-loader,再使用css-loader解析,最后使用style-loader解析。

sass-loader会将sass文件转换成css文件。
css-loader会解析css文件的依赖关系(@import),构建关于css的依赖。
style-loader会将css注入js,动态向DOM注入样式。

可以使用ExtractTextPlugin插件将css单独打包而不是注入js文件中。

postcss

postcss是一个css处理平台。它是一个css编译器。平台有一系列css处理插件,比如autoprefixer插件可以自动为css添加前缀-webkit- , --mos-

关于postcss的配置,可以直接在webpack的配置里写,也可以单独一个postcss.config.js文件写。

// postcss.config.js
module.exports = {
    options: {
        plugins: [
            require('autoprefixer')({
                browsers: [
                    'ie >= 9',
                    'Edge >= 12'
                ]
            })
        ]
    }
};

js loaders

vue有专门的vue-loader
react,es6代码可以使用babel转换。
typescript代码使用awesome-typescript-loader

babel

类似postcss,babel是一个js编译器。它可以帮我们将代码转译成我们需要的js代码。

我们可以在.babelrc里配置。

{
    "presets": [
        "es2015",
        "react",
        "stage-2"
    ],
    "plugins": [
        "transform-runtime",
        "add-module-exports",
        "transform-class-properties",
        [
            "import",
            {
              "libraryName": "antd",
              "style": true
            }
        ]
    ]
}

plugins指定我们需要哪些插件。其中transform-runtime是必须要装的,为了保证babel正常运行。
presets告诉babel待转换的js代码都使用了哪些特性。
stage-2是还未纳入ECMAScript标准的草案。

需要注意的是,我们在webpack的配置时

module.exports = { 
    module: {
        rules: [{
            test: /\.js$/,
            use: ['babel-loader'],
        }]
    }
    //输出 source-map 以方便直接调试 ES6 源码 
    devtool: 'source-map'
}

图片loaders

{
                test: /\.(png|jpe?g|gif|svg|cur)?$/,
                exclude: /node_modules/,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit : 2560,
                        name: '[path][name].[ext]?v=[hash]'
                    }
                }]
            },

url-loader可以将小于limit大小的图片转换成base64,嵌入到html文件中,减少网络请求。

精灵图

还记得我们在介绍sass loader的时候,解析到css-loader之前,可以加入sprite-loader。它可以将图片组成雪碧图,减少网络请求。

module.exports = { module: {
rules: [

     test: /\.scss/ ,  // 使用正则表示,匹配文件名
     use: [’style-loader’,’css-loader’,’sass-loader’]
   ]
 }
}

plugins

UglifyJsPlugin

压缩代码插件,在prod打包模式下,需要将代码压缩。
webpack有一个秒杀其他打包工具的特性: tree-shaking(虽然它也是借鉴来的)。tree-shaking是webpack自带的功能,它可以检查无用代码,需要配合UglifyJsPlugin在代码压缩的时候,剔除无用代码。

所以在dev模式,我的项目3mb,但是在prod模式,我的代码在压缩之后,只有200kb。(看来我引入了大的包,而且利用率不高呀)

webpack3终极配置及其优化(react)_第4张图片

webpack-parallel-uglify-plugin

随着项目越来越庞大,如果觉得UglifyJsPlugin比较慢 ,可以使用webpack-parallel-uglify-plugin,它可以使用多进程,并行压缩代码,速度显著提高。

CommonsChunkPlugin

如果有很多个文件都引入了一个文件,我们可以使用CommonsChunkPlugin将重复的文件打包到一起,防止重复打包。

还有很多插件,在webpack官网上面可以找到。

热替换与自动刷新

我们希望我们的构建工具可以实时监听代码的变化,便于调试。

这里有webpack体系下的几种办法:

插件HotModuleReplacementPlugin

这家伙只能监听变化,不能刷新浏览器。

开启watch模式

// webpack.config.js
export default = {
    watch: true,
    module.export = { 
        watchOptions : {
            //不监听的 node modules 目录下的文件 
            ignored : /node_modules/,
        }
    }
}

不监听的 node modules 目录下的文件 ,会大大优化webpack速度。

// 或 package.json
'dev': "webpack --watch"

这家伙只能监听变化,不能刷新浏览器。

webpack-dev-server

这个插件会自动开启webpack的watch模式,而且会自动刷新浏览器。

webpack的性能优化

happypack

多进程并行执行Loader

缩小范围

Loaders 和Plugins可以配置includesexcludes,可以缩小执行范围。
比如babel转换。

  module: {
        rules: [
            {
                test: /\.(js|jsx)?$/,
                exclude: /node_modules/,  // 去除node modules文件夹的文件
                use: 'happypack/loader?id=jsx'  // 这里使用了happypack
            }]
 }

DllPlugin

动态链接库。
提前打包一些文件,在真实打包的时候,把他们链接进来,不需打包,直接引入。

etc

其他优化方式前面提过

  1. tree-shaking和压缩代码,使代码体积变小
  2. CommonsChunkPlugin单独打包重复使用的文件
  3. 按需加载

webpack相较于其他构建工具的优势与劣势

优势:

  1. TreeShaking(lodash,moment库blabla)
  2. 热替换功能,以提高开发效率;
  3. 支持SourceMap,以方便调试;
  4. 区分打包环境

劣势: 有点慢 这里写图片描述

你可能感兴趣的:(webpack)