转载

Webpack4 新特性 及 Vue-cli项目升级

webpack 于2018年2月25日正式发布 v4.0.0 版本,代号 legato ,这将会让 webpack 的配置更加简单,构建速度更快

Webpack4 中文文档

[TOC]

Nodejs版本

Node.js >= 8.9.4

当使用 webpack4 时,必须保证 Node.js 版本 >= 8.9.4 ,因为 webpack4 使用了大量的ES6语法,这些语法在 nodejs新版 v8 中得到了原生支持

安装

npm i webpack webpack-cli -D

webpack4 中 cli 工具分离成了 webpack 核心库 与 webpack-cli 命令行工具两个模块,需要使用 CLI ,必安装 webpack-cli 至项目中

零配置 0CJS

webpack4 设置了默认值,以便无配置启动项目

  • entry 默认值是 ./src/
  • output.path 默认值是 ./dist
  • mode 默认值是 production

模式

mode: development / production / none

开发模式 development

  • 浏览器调试工具
  • 注释、开发阶段的详细错误日志和提示
  • 快速和优化的增量构建机制
  • 开启 output.pathinfobundle 中显示模块信息
  • 开启 NamedModulesPlugin
  • 开启 NoEmitOnErrorsPlugin

生产模式 production

  • 启用所有优化代码的功能
  • 更小的bundle大小
  • 去除只在开发阶段运行的代码
  • 关闭内存缓存
  • Scope hoistingTree-shaking
  • 开启 NoEmitOnErrorsPlugin
  • 开启 ModuleConcatenationPlugin
  • 开启 optimization.minimize

none 会禁用所有的默认设置,可以使用 optimization.* 的方式去设定更详细的配置(搭建你的自定义模式)

插件优化

新增 optimization.splitChunksoptimization.runtimeChunk 来替代 CommonsChunkPlugin 插件,

新增 optimization.noEmitOnErrors 来替代 NoEmitOnErrorsPlugin 插件

新增 optimization.namedModules 来替代 NamedModulesPlugin 插件

内置 optimization.minimize 来压缩代码

loader

默认已支持加载 json 模块,不再需要 json-loader

允许通过ESM语法导入JSON,JSON模块中未使用的部分会被消除

详细升级日志请查看 webpack4.0 升级日志中文版

Webpack配置 基本结构

module.exports = {
  // 定义模块引用的绝对路径前缀
  context: path.resolve(__dirname, '../'),

  // 输入配置
  entry: {
    // 入口文件,如果是多页项目,可配置多个
    app: './src/main.js'
  },

  // 输出配置
  output:{
    // 输出目录
    path: path.resolve(__dirname, '../dist'),
    // 输出文件名 name 为 entry 的 key 值,也可以加上 hash 值, 如:[name].[hash:8].js
    filename: '[name].js',
    // 构建生成的 js 在html中引用时的路径
    publicPath: '/'
  },

  // 模块引用配置
  resolve: {
    // 定义模块查找的后缀,方便在代码引用时可省略后缀
    extensions: ['.js', '.vue', '.json'],
    // 定义引用路径别名 配置别名可以加快webpack查找模块的速度
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },

  // 模块加载配置
  module:{
    // 指定 不同的模块使用不同的加载器处理
    // 以 .css 结尾的文件,使用 css-loader 解析css模块,使用 style-loader 将生成的 css 内容以标签的形式添加到 HTML 文档中
    rules:[
      {
        // 文件匹配正则
        test://.css$/,
        // 加载器,从后向前倒序使用
        loader:['style-loader','css-loader']
      }
    ]
  },

  // 插件
  plugins:{
    // 使用 HtmlWebpackPlugin 将构建好的 js/css 嵌入到模板 index.html 中
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      title: '首页',
      hash: true,
    })
  },

  // Web服务器配置
  devServer:{
    contentBase:'../dist',
    host:'localhost',
    port:'8080',
  }
}

使用 HtmlWebpackPlugin 插件,需要npm安装相应模块

// 安装
npm install html-webpack-plugin -D

// 引用
const HtmlWebpackPlugin = require('html-webpack-plugin')

使用 devServer 同样需要安装相关模块

// 安装
npm install webpack-dev-server -D

// 在 package.json 中配置启动脚本
"script": {
  "dev": "webpack-dev-server --open --mode development"
}

// 启动 web服务,默认会查找目录下 webpack.config.js 读取其中 devServer 的配置以启动服务
npm run dev

常用 Loader 配置

babel-loader

将ES6的代码使用babel转码为浏览器兼容的ES5

{
    test: //.js$/,
    loader: 'babel-loader',
    include: [resolve('src')]
}

babel 的配置在项目根目录下 .babelrc 文件中,如果没有则新建,根据不同的项目要求配置

npm i babel-core babel-loader babel-preset-env babel-preset-stage-2 --save-dev

/.babelrc
{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["istanbul"]
    }
  }
}

url-loader

将 图片 转成 data:base64 ,以减少页面中的图片请求

{
    test: //.(png|jpe?g|gif|svg)(/?.*)?$/,
    loader: 'url-loader',
    options: {
      limit: 10000, // 只转码 1M以下的图片
      name: 'img/[name].[hash:7].[ext]' // 发布到 dist/img 目录下,名称中添加 hash 值,避免缓存
    }
},

less-loader

将less文件编译为css

{
    test: //.less$/,
    use: ['style-loader', 'css-loader','less-loader']
}

这里先将 less-loader 转 css, 再经过 css-loader 将css模块化并解析其中的 @importurl() ,再通过 style-loader 将css嵌入html

常用插件

配置全局变量

webpack.DefinePlugin

用于定义在编译过程中使用的全局变量,常用来定义 process.env 用来区分开发环境和生产环境

const isProduction = process.env.NODE_ENV === 'production'

模块热替换

webpack.HotModuleReplacementPlugin 内置

模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面

  • 保留在完全重新加载页面时丢失的应用程序状态。
  • 只更新变更内容,以节省宝贵的开发时间。
  • 调整样式更加快速 - 几乎相当于在浏览器调试器中更改样式。

保证 chunkhash 的稳定

webpack.HashedModuleIdsPlugin 内置

使用 hash 做为模块ID, 避免缓存那些没有变化的模块内容,从而实现更优的缓存策略

用 webpack 实现持久化缓存

提取css为文件

extract-text-webpack-plugin NPM

将所有入口 chunk 中引用的 *.css ,提取合并为独立的css,在 index.html 中使用 link:src 来引用css文件,一般用于生产模式,提取公共的css

拷贝静态文件

copy-webpack-plugin NPM

应用:将模板 index.html 中引用的静态资源,在构建时复制到 dist 指定目录下

CSS优化

optimize-css-assets-webpack-plugin NPM

压缩css, 同时去除重复的样式,减少CSS打包后的体积

vue-cli 项目开发环境的 plugins 配置

plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),

    new webpack.HotModuleReplacementPlugin(),

    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      favicon: resolve('favicon.ico'),
      inject: true
    }),

    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
]

vue-cli 项目生产环境的 plugins 配置

plugins: [
  new webpack.DefinePlugin({
    'process.env': env
  }),
  new ExtractTextPlugin({
    filename: utils.assetsPath('css/[name].css'),
    allChunks: true,
  }),

  new OptimizeCSSPlugin({
    cssProcessorOptions: config.build.productionSourceMap
      ? { safe: true, map: { inline: false } }
      : { safe: true }
  }),

  // see https://github.com/ampedandwired/html-webpack-plugin
  new HtmlWebpackPlugin({
    filename: config.build.index,
    template: 'index.html',
    inject: true,
    favicon: resolve('favicon.ico'),
    minify: {
      removeComments: true,
      collapseWhitespace: true,
      removeAttributeQuotes: true
    },

    chunksSortMode: 'dependency'
  }),

  new webpack.HashedModuleIdsPlugin(),

  new CopyWebpackPlugin([
    {
      from: path.resolve(__dirname, '../static'),
      to: config.build.assetsSubDirectory,
      ignore: ['.*']
    }
  ])
]

配置服务代理

在开发过程中和后台连调时,一般需要解决跨域问题, webpack 提供了 proxy 配置用于,代理 api 请求,屏蔽浏览器跨域限制

proxy: {
    '/api': {
        target: 'http://192.168.1.12:5000',
        pathRewrite: {'^/api' : '/api'},
        changeOrigin: true
    }
}

当用户访问 /api/getUser 时,代理到 http://192.168.1.12:5000/api/getUser 去请求数据

Vue-cli 升级 Webpack4.x

升级模块

建议在 webpack 构建流程中使用到的 loadersplugins 都升级到最新版本

安装

如果 pakage.json 中有相应的模块配置,可删除之后重新安装

npm i webpack webpack-cli webpack-dev-server --save-dev

还有以下模块

"copy-webpack-plugin": "^4.0.1",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"html-webpack-plugin": "^3.1.0",
"optimize-css-assets-webpack-plugin": "^4.0.0",
"webpack-bundle-analyzer": "^2.11.1",
"webpack-dev-middleware": "^3.1.2",
"webpack-dev-server": "^3.1.3",
"webpack-merge": "^4.1.0"

修改开发环境

build/webpack.dev.conf.js 中添加 mode 配置

注释掉 webpack.NamedModulesPluginwebpack.NoEmitOnErrorsPlugin 插件,因为 webpack4 开发模式已经内置

module.exports = {
    // ...
    mode: 'development',
    // ...
    plugins: {
        // new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
        // new webpack.NoEmitOnErrorsPlugin(),
    }
}

修改生产环境

build/webpack.production.conf.js 中添加 modeoptimization 配置

同时注释掉 webpack.optimize.CommonsChunkPluginuglifyjs-webpack-pluginwebpack.optimize.ModuleConcatenationPlugin 相关配置及引用

const webpackConfig = merge(baseWebpackConfig, {
    // ...
    mode: 'production',
    // webpack4 内置了 optimization.splitChunks、optimization.runtimeChunk 用来抽取共公代码,优化了缓存策略
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          test: /[///]node_modules[///]/,
          chunks: 'initial',
          name: 'vendors',
        },
        'async-vendors': {
          test: /[///]node_modules[///]/,
          minChunks: 2,
          chunks: 'async',
          name: 'async-vendors'
        }
      }
    },
    runtimeChunk: { name: 'runtime' }
  },
  // ...
}

经过上面三步, vue-cli 项目升级 webpack4.x 就完成了

需要注意的是当前项目一定要是较新的 webpack 模板生成的项目,是不是新模板,可以查看 package.jsonscripts.dev 是否是使用 webpack-dev-server 启动的,如果是,则为新的模板

性能优化

使用happypack

HappyPack就能让Webpack把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程

npm i happypack@5.0.0-beta.3 --save-dev

const HappyPack = require('happypack')
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })

  {
    test: //.js$/,
    // loader: 'babel-loader',
    loader: 'happypack/loader?id=happy-babel-js', // 增加新的HappyPack构建loader
    include: [resolve('src')],
    exclude: /node_modules/,
  }

  plugins: [
    new HappyPack({
      id: 'happy-babel-js',
      loaders: ['babel-loader?cacheDirectory=true'],
      threadPool: happyThreadPool
    })
]

配置noparse

忽略对已知文件的解析: 一个模块中没有其它新的依赖 就可以配置这项,webpack 将不再扫描这个文件中的依赖

resolve: {
    alias: {
        moment: "moment/min/moment-with-locales.min.js"
    }
},
module: {
    noParse: [/moment-with-locales/]
}
  • webpack 检查到 entry.js 文件对 moment 的请求
  • 请求被 alias 重定向,转而请求 moment/min/moment-with-locales.min.js
  • noParse 规则中的 /moment-with-locales/ 一条生效,所以 webpack 就直接把依赖打包进了 bundle.js

生产环境不产生source-map

eval: 生成代码 每个模块都被eval执行,并且存在@sourceURL

cheap-eval-source-map: 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl

cheap-module-eval-source-map: 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能

eval-source-map: 原始代码 同样道理,但是最高的质量和最低的性能

cheap-source-map: 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用

cheap-module-source-map: 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射

source-map: 原始代码 最好的sourcemap质量有完整的结果,但是会很慢

当我们不需要调试时,可以关掉 sourcemap 或降低 sourcemap 的级别来加快打包的速度

使用CDN资源或静态资源

使用CDN

webpack.config.js 中配置模块变量为外部依赖

externals: {
    moment: true
}

index.html 中添加资源引用

<script src="//apps.bdimg.com/libs/moment/2.8.3/moment-with-locales.min.js"></script>

使用静态资源

有时候,由于网络限制,不允许使用CDN资源,又不想经过Webpack打包,则可以将资源直接引入 index.html ,在 webpack build 配合 copy-webpack-plugin 插件,将资源复制到 dist 目录下,例 vue-cli 生成的项目根目录下的 static 目录就是用来放这类静态资源的

为babel-loader设置缓存

cacheDirectory : 指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程

{
    test: //.js$/,
    loader: 'babel-loader?cacheDirectory',
    exclude: /node_modules/,
    include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},

官方文档中表示设置 cacheDirectory 可将 babel-loader 提速至少两倍

参考阅读

深入浅出Webpack

vue cli 平稳升级webapck4

Webpack4 那点儿东西

30分钟快速了解webpack
原文  http://coderlt.coding.me/2018/04/12/webpack4/
正文到此结束
Loading...