webpack

一。常见的构建工具对比

项目工具 优点 缺点 对比
npm script 1.内置,无须安装其他依赖 提供了pre和post两个钩子,但不能方便地管理多个任务之间但依赖 ---
grunt 灵活,1.它只负责执行我们定义的任务,2大量的可复用插件封装好了常见的构建任务。 集成度不高,要写很多配置后才可以用,无法开箱即用。 grunt 相当于是进化版的npm script,弥补它的不足。
gulp 基于流的自动化构建工具,管理和执行任务,还支持监听、读写文件。 集成度不高,要写很多配置后才可以用,无法开箱即用。 gulp 是 grunt的加强版,gulp增加了监听、读写文件,流式的处理功能。
Fis3 集成了各种web开发所需的构建功能,配置简单,开箱即用。 官方不维护,不支持最新node.js 它专注于web开发的完整解决方案,如果将grunt,gulp比作汽车的发动机,则可以将fis3比作一辆完整的汽车。
webpack 1,专注于处理模块化的项目,能做到开箱即用,一步到位;2.可通过plugin扩展,完整好用不失灵活。3.使用场景不局限于web开发;4.社区活跃;5.良好开发体验。 只能用于采用模块化开发的项目。 ------
Rollup 和webpack很类似但专注于es6但模块打包工具 -- --

DevServer

1.提供 HTTP 服务而不是使用本地文件预览;
2.监听文件的变化并自动刷新网页,做到实时预览;
3.支持 Source Map,以方便调试。

webpack-dev-server --hot --devtool source-map

配置 核心概念

  1. Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
  2. Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  3. Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
  4. Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
  5. Plugin:扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
  6. Output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。

配置 Loader

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

  1. 条件匹配:通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件。
  2. 应用规则:对选中后的文件通过 use 配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还可以分别给 Loader 传入参数。
  3. 重置顺序:一组 Loader 的执行顺序默认是从右到左执行,通过 enforce 选项可以让其中一个 Loader 的执行顺序放到最前或者最后。

webpack -- 优化webpack构建

1. 优化开发体验- 既提升开发效率

  • 优化构建速度
    1) 缩小文件搜索范围
    2) 使用 DllPlugin
    3) 使用 HappyPack
    4) 使用 ParallelUglifyPlugin
  • 优化使用体验,通过自动化手段完成一些重复的工作。
    1)使用自动刷新
    2)开启模块热替换

2. 优化输出质量

  • 减少用户能感知的加载时间,也就是首屏加载时间
  • 提升流畅度,提升代码性能
    优化输出质量本质是优化构建输出的要发布到线上的代码,分为以下几点:
  1. 减少用户能感知到的加载时间,也就是首屏加载时间。

    • 4-7 区分环境 :减少开发环境的代码,缩小体积。

    • 4-8 压缩代码: 也是缩小体积,以及安全。

    • 4-9 CDN 加速

    • 4-11 提取公共代码

      • 相同的资源被重复的加载,浪费用户的流量和服务器的成本;
      • 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
      • 虽然用户第一次打开网站的速度得不到优化,但之后访问其它页面的速度将大大提升。
        提取方法:Webpack 内置插件CommonsChunkPlugin
    • 4-12 按需加载: Webpack 内置了对 import(*) 语句的支持
      以 ./show.js 为入口新生成一个 Chunk;
      当代码执行到 import 所在语句时才会去加载由 Chunk 对应生成的文件。
      import 返回一个 Promise,当文件加载成功时可以在 Promise 的 then 方法中获取到 show.js 导出的内容。

  2. 提升流畅度,也就是提升代码性能。

3. 优化总结

优化开发体验- 既提升开发效率

1) 缩小文件搜索范围

1.1 开发体验 -- 缩小文件的搜索范围

  • 优化Loader配置
  • 优化resolve.modules配置
  • 优化resolve.mainFields配置
  • 优化resolve.alias配置
  • 优化resolve.extensions配置
  • 优化module.noParse配置

总体的优化的配置如下:

const path = require('path');

module.exports = {
  // JS 执行入口文件
  entry: './src/main.js',
  output: {
    // 把所有依赖的模块合并输出到一个 bundle.js 文件
    filename: 'bundle.js',
    // 输出文件都放到 dist 目录下
    path: path.resolve(__dirname, './dist'),
  },
  resolve: {
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    modules: [path.resolve(__dirname, 'node_modules')],
    // 只采用 main 字段作为入口文件描述字段,以减少搜索步骤
    mainFields: ['main'],
    // 使用 alias 把导入 react 的语句换成直接使用单独完整的 react.min.js 文件,减少耗时的递归解析操作
    alias: {
      'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'),
      'react-dom': path.resolve(__dirname, './node_modules/react-dom/dist/react-dom.min.js'),
    },
    // 尽可能的减少后缀尝试的可能性
    extensions: ['js'],
  },
  module: {
    // 独完整的 `react.min.js` 文件就没有采用模块化,忽略对 `react.min.js` 文件的递归解析处理
    noParse: [/react\.min\.js$/],
    rules: [
      {
        // 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
        test: /\.js$/,
        // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
        use: ['babel-loader?cacheDirectory'],
        // 只对项目根目录下的 src 目录中的文件采用 babel-loader
        include: path.resolve(__dirname, 'src'),
      },
    ]
  },
};

1.2 使用 DllPlugin -- 动态链接库

为什么给 Web 项目构建接入动态链接库的思想后,会大大提升构建速度呢? 原因在于包含大量复用模块的动态链接库只需要编译一次,在之后的构建过程中被动态链接库包含的模块将不会在重新编译,而是直接使用动态链接库中的代码。 由于动态链接库中大多数包含的是常用的第三方模块,例如 react、react-dom,只要不升级这些模块的版本,动态链接库就不用重新编译。

webpack已经内置了对动态链接库的支持,需要通过2个插件完成

  • DllPlugin插件:用于打包出一个个单独的动态链接库文件。
  • DllReferencePlugin 插件: 用于在主要的配置文件中引入DllPlugin插件打包好的动态链接库文件。
构建出动态链接库文件

构建输出的以下这四个文件
├── polyfill.dll.js
├── polyfill.manifest.json
├── react.dll.js
└── react.manifest.json

和以下这一个文件:

├── main.js

以上JS是由两份不同的构建分别输出的。

一。动态链接库文件相关的文件需要由一份独立的构建输出,用于给主构建使用。新建一个 Webpack 配置文件 webpack_dll.config.js 专门用于构建它们,通过命令:webpack --config webpack_dll.config.js 构建,文件内容如下:

const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');

module.exports = {
  // JS 执行入口文件
  entry: {
    // 把 React 相关模块的放到一个单独的动态链接库
    react: ['react', 'react-dom'],
    // 把项目需要所有的 polyfill 放到一个单独的动态链接库
    polyfill: ['core-js/fn/object/assign', 'core-js/fn/promise', 'whatwg-fetch'],
  },
  output: {
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
    // 也就是 entry 中配置的 react 和 polyfill
    filename: '[name].dll.js',
    // 输出的文件都放到 dist 目录下
    path: path.resolve(__dirname, 'dist'),
    // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
    // 之所以在前面加上 _dll_ 是为了防止全局变量冲突
    library: '_dll_[name]',
  },
  plugins: [
    // 接入 DllPlugin
    new DllPlugin({
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      // 例如 react.manifest.json 中就有 "name": "_dll_react"
      name: '_dll_[name]',
      // 描述动态链接库的 manifest.json 文件输出时的文件名称
      path: path.join(__dirname, 'dist', '[name].manifest.json'),
    }),
  ],
};

用于输出 main.js 的主 Webpack 配置文件内容如下:

const path = require('path');
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');

module.exports = {
  entry: {
    // 定义入口 Chunk
    main: './main.js'
  },
  output: {
    // 输出文件的名称
    filename: '[name].js',
    // 输出文件都放到 dist 目录下
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        // 项目源码使用了 ES6 和 JSX 语法,需要使用 babel-loader 转换
        test: /\.js$/,
        use: ['babel-loader'],
        exclude: path.resolve(__dirname, 'node_modules'),
      },
    ]
  },
  plugins: [
    // 告诉 Webpack 使用了哪些动态链接库
    new DllReferencePlugin({
      // 描述 react 动态链接库的文件内容
      manifest: require('./dist/react.manifest.json'),
    }),
    new DllReferencePlugin({
      // 描述 polyfill 动态链接库的文件内容
      manifest: require('./dist/polyfill.manifest.json'),
    }),
  ],
  devtool: 'source-map'
};

注意:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 参数必须和 output.library 中保持一致。 原因在于 DllPlugin 中的 name 参数会影响输出的 manifest.json 文件中 name 字段的值, 而在 webpack.config.js 文件中 DllReferencePlugin 会去 manifest.json 文件读取 name 字段的值, 把值的内容作为在从全局变量中获取动态链接库中内容时的全局变量名。

1)使用自动刷新

自动刷新的原理

控制浏览器刷新有三种方法:

  1. 借助浏览器扩展去通过浏览器提供的接口刷新,WebStorm IDE 的 LiveEdit 功能就是这样实现的。
  2. 往要开发的网页中注入代理客户端代码,通过代理客户端去刷新整个页面。
  3. 把要开发的网页装进一个 iframe 中,通过刷新 iframe 去看到最新效果。
    DevServer 支持第2、3种方法,第2种是 DevServer 默认采用的刷新方法。

要让 Webpack 开启监听模式,有两种方式:

在配置文件 webpack.config.js 中设置 watch: true。
在执行启动 Webpack 命令时,带上 --watch 参数,完整命令是 webpack --watch。

  • watch: 监听文件变化。
  • inline: 通过什么方式去刷新页面,iframe或者客户端方式websocket。默认是websocket,
  • hot: 模块热替换,既修改了什么文件就替换什么文件。

总结:若想要开发构建速度提升,关闭粗暴的inline,开启文件监听,以及排除node_modoules监听,和监听时间。

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 模式

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

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

        watch: true, // 是否开始,  这个参数webpack4 写了后报错,应该是默认开启了。
        watchOptions: { // 监听模式选项
            // 不监听的文件或文件夹,支持正则匹配。默认为空
            ignored: /node_modules/,
            // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
            // 默认为300ms 
            // 值越大性能越好,因为这能降低重新构建的频率。
            aggregateTimeout: 300,
            // 判断文件是否发生变化是不停的去询问系统指定文件有没有变化,默认每隔1000毫秒询问一次
            // watchOptions.poll 值越大越好,因为这能降低检查的频率。
            poll: 1000
        }
    }

按需加载:

//第一步:内置的webpack功能:  import(/* webpackChunkName: "show" */ './show')
window.document.getElementById('btn').addEventListener('click', function () {
  // 当按钮被点击后才去加载 show.js 文件,文件加载成功后执行文件导出的函数
  import(/* webpackChunkName: "show" */ './show').then((show) => {
    show('Webpack');
  })
});

//二。搭配的配置文件chunkFilename
const path = require('path');

module.exports = {
  // JS 执行入口文件
  entry: {
    main: './main.js',
  },
  output: {
    // 为从 entry 中配置生成的 Chunk 配置输出文件的名称
    filename: '[name].js',
    // 为动态加载的 Chunk 配置输出文件的名称
    chunkFilename: '[name].js',
    path: path.resolve(__dirname, './dist'),
  }
};

webpack ---- 常用插件

  1. 适用用单页-多页面的插件 web-webpack-plugin.

一个很好的html-webpack-plugin替代品, 可以使 webpack 以 HTML 为入口和方便的管理多个页面。

  1. ExtractTextPlugin

插件的作用是提取出 JavaScript 代码里的 CSS 到一个单独的文件。 对此你可以通过插件的 filename 属性,告诉插件输出的 CSS 文件名称是通过 [name]_[contenthash:8].css 字符串模版生成的,里面的 [name] 代表文件名称, [contenthash:8] 代表根据文件内容算出的8位 hash 值

  1. CommonsChunkPlugin 内置插件

https://webpack.docschina.org/plugins/commons-chunk-plugin/

基础使用方式

const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');

new CommonsChunkPlugin({
  // 从哪些 Chunk 中提取
  chunks: ['a', 'b'],
  // 提取出的公共部分形成一个新的 Chunk,这个新 Chunk 的名称
  name: 'common'
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • 作者:小 boy (沪江前端开发工程师)本文原创,转载请注明作者及出处。原文地址:https://www.smas...
    iKcamp阅读 2,740评论 0 18
  • webpack使用学习 本分享学习借鉴webpack中文官网,官网链接(中文文档):https://www.web...
    腿毛怪丶叔叔阅读 865评论 0 5
  • webpack 是什么? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(mo...
    IT老马阅读 3,299评论 2 27
  • 一、概念 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bun...
    Timmy小石匠阅读 1,850评论 0 29
  • 写在开头 先说说为什么要写这篇文章, 最初的原因是组里的小朋友们看了webpack文档后, 表情都是这样的: (摘...
    Lefter阅读 5,269评论 4 31