webpack的理解、总结


核心概念

WebPack 是一个模块打包工具,可以使用WebPack管理模块,并分析模块间的依赖关系,最终编绎输出模块为HTML、JavaScript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。

  • 对于不同类型的资源,webpack有对应的模块加载器loader,比如说,
    CSS
    解析CSS的css-loader、style-loader,
    解析less的less-loader,sass的sass-loader,
    JS
    解析将 TypeScript 转换成 JavaScript的ts-loader,
    解析ES6为ES5的babel-loader,
    解析JavaScript 代码规范的eslint-loader
    Vue
    解析.vue文件的vue-loader、
    静态资源:音视频、文件、json
    解析常用图片以及音视频资源的url-loader、
    解析文件的file-loader,
    解析 JSON 文件的json-loader

webpack的基本功能(也就是各种loader的作用)

  1. 代码转换 :TypeScript 编译成 JavaScript、ES6转ES5、SCSS 编译成 CSS 等等(各种loader)

  2. 代码语法检测 :自动检测代码是否符合语法 (eslint-loader)

  3. 代码分割 :打包代码时,可以将代码切割成不同的chunk(块),实现按需加载,降低了初始化时间,提升了首屏渲染效率

  4. 监测代码更新 ,自动编译,刷新页面:监听本地源代码的变化,自动构建,刷新浏览器(自动刷新)

  5. 自动发布 :更新完代码后,自动构建出线上发布代码并传输给发布系统(没用过)。

  6. 文件压缩 :压缩 JavaScript、CSS、HTML 代码,缩小文件体积(比如说,打包后的js、css、html文件会去掉代码之间的空隔,紧凑显示)

  7. 模块合并 :由于模块化的开发,一个页面可能会由多个模块组成,所以编译时需要把各个模块合并成一个文件(模块化开发引出的功能)


webpack配置说明

Webpack运行在node.js环境下,它的配置文件webpack.config.js遵循CommonJS规范,最终export出一个json对象。
webpack.config.js基础配置说明:

entry,指定了模块的入口,它让源文件加入构建流程中被webpack控制。
output,配置输出文件的存放位置、文件名、文件基础路径publicPath。
module,配置各种类型文件的解析规则,比如说.vue文件、.js文件。
rosolve,配置alias(别名),或者定义寻找模块的规则。
plugins,配置扩展插件,扩展webpack的更多功能。
devServer,实现本地http服务等。
loader:模块加载器

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: ['babel-polyfill','./src/main.js']
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],  //  当找不到模块时,尝试从后进行寻找
    alias: {                                    //别名
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src')
    }
  },
  // 排除模块,下面的模块不会编译到 webpack 打包后的文件中
    externals: {
    "vue":  "Vue",
    "vuex": "Vuex",
    "vue-router": "VueRouter",
    "lodash": "_",
    "echarts": "echarts"
    },
  plugins: [
    // 全局模块对象
    new webpack.ProvidePlugin({
      "Vue": "vue",
      "Vuex": "vuex",
      "VueRouter": "vue-router",
      "_": "lodash",
      "echarts": "echarts"
    })
  ],
  module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.scss/,
        loader: ['style','css','scss']
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

webpack打包流程

入口(entry) 开始,递归转换入口文件所依赖的module

每找到一个module,就根据对应的loader去转换这个module

然后,再对当前module依赖的所有module进行转换 ,如果子module还有依赖的话,再转换,直至没有依赖

其次,以入口文件(entry)为单位进行分组 ,一个entry和其所有依赖的module被分到一个块(Chunk)。

最后,Webpack会把所有Chunk转换成文件输出 ,在整个流程中Webpack会在恰当的时机执行plugin里定义的扩展插件。


webpack的工作原理

简单的说就是分析代码,找到“require”、“exports”、“define”等关键词,并替换成对应模块的引用。

在一个配置文件中,指明对某些文件进行编译、压缩、组合等任务。把你的项目当成一个整体,通过一个给定的主文件 (index.js),webpack将从这个文件开始找到你的项目的所有的依赖文件,使用loaders处理他们,最后打包为一个浏览器可以识别的js文件。


有哪些常见的Plugin(插件)

  1. UglifyJsPlugin :压缩、混淆代码
  2. CommonsChunkPlugin:代码分割
  3. ProvidePlugin:自动加载模块
  4. html-webpack-plugin:加载html,并且引入css/js文件
  5. extract-text-webpack-plugin; 抽离样式,生成css文件
  6. optimize-css-assets-webpack-plugin:css去重
  7. webpack-bundle-analyzer:代码分析
  8. compression-webpack-plugin:使用gzip压缩js和css
  9. happypack:使用多线程,加快代码构建
  10. EnvironmentPlugin:定义局部的变量
  11. mini-css-extract-plugin : 分离样式文件,CSS 提取为独立文件,支持按需加载;
  12. hard-source-webpack-plugin : 利用缓存,提高打包与启动项目的速度;
  13. uglifyjs-webpack-plugin:压缩代码,同时可以去掉代码中的debugger、console.log
  14. clean-webpack-plugin: 目录清理
  15. copy-webpack-plugin: 将单个文件或整个目录复制到打包后的目录。

说一说Loader和Plugin的区别

  1. 功能不同
    Loader 本质就是一个函数,对接收到的文件进行转换,比如将ts转换成js,将scss转换成css等。因为 webpack 只能理解 JavaScript 和 JSON 文件,对于其他资源例如 css,图片,或者其他的语法集是没有办法加载的。 这就需要对应的loader将资源转化,所以说loader主要就是用来转化文件的。

    Plugin 是webpack的插件,目的在于解决loader无法实现的其他事,它直接作用于 webpack,扩展了它的功能。loader只是专注于转换文件这一个领域,而plugin的功能更加的丰富,而不仅局限于资源的加载。

  2. 运行时机不同
    loader运行在打包文件之前,对文件进行预处理;

    plugins 运行在loader结束后,webpack打包的整个过程中,它是基于事件机制,监听webpack打包过程中的某些节点,从而执行相应任务,进而改变输出。


如何优化 Webpack 的构建速度

  1. 使用高版本的 Webpack 和 Node.js

  2. 压缩代码
    1). 通过 uglifyjs-webpack-plugin, 压缩JS代码
    2). 通过 mini-css-extract-plugin ,分离 CSS 代码到单独文件,
    3). 通过 css-loader 的 minimize 选项开启 cssnano 压缩 CSS

  3. 压缩图片
    通过image-webpack-loader

  4. 多线程/多进程构建
    happyPack或thread-loader

  5. 预编译资源模块:DLLPlugin

  6. 充分利用缓存提升二次构建速度
    1). babel-loader 开启缓存
    2). terser-webpack-plugin 开启缓存
    3). 使用 hard-source-webpack-plugin

  7. 缩小打包作用域
    1). exclude/include (确定 loader 规则范围)
    2). resolve.modules 指明第三方模块的绝对路径 (减少不必要的查找)
    3). resolve.mainFields 只采用 main 字段作为入口文件描述字段 (减少搜索步骤,需要考虑到所有运行时依赖的第三方模块的入口文件描述字段)
    4). resolve.extensions 尽可能减少后缀尝试的可能性
    5). noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)
    6). ignorePlugin (完全排除模块)
    7). 合理使用alias

  8. 公共资源提取
    CommonsChunkPlugin
    html-webpack-externals-plugin,将基础包通过 CDN 引入,不打入 bundle 中。

总结

(1)打包速度优化

1、使用高版本的webpack和Node.js

2、多进程打包:happyPack或thread-loader

3、多进程并行压缩:parallel-uglify-plugin、uglifyjs-webpack-plugin 开启 parallel 参数、terser-webpack-plugin 开启 >parallel 参数

4、预编译资源模块:DLLPlugin

5、缓存(babel-loader、terser-webpack-plugin、cache-loader)

6、exclude、include缩小构建目标,noParse忽略、IgnorePlugin

7、resolve配置减少文件搜索范围(alias、modules、extensions、mainFields)

(2) 打包体积优化

1、Tree-shaking擦除无用代码

2、Scope Hoisting优化代码

3、图片压缩(image-webpack-loader)

4、公共资源提取(CommonsChunkPlugin)

5、动态Polyfill

6、分包设置Externals,使用 html-webpack-externals- plugin,将 react、react-dom 基础包通过 cdn 引入,不打入 bundle 中

7、删除无用CSS代码(purgecss-webpack-plugin)

8、JS、CSS压缩(UglifyJsPlugin(3)、terser-webpack-plugin(4)、optimize-css-assets-webpack-plugin)


文章作者: me
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 me !