webpack 4 新特性 & Vue-cli 升级

993 阅读7分钟

介绍webpack

webpack诞生之初主要想解决代码拆分问题,是现代JS应用程序的静态模块打包器。
4个核心概念:

  • 入口(entry)
    指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始
  • 输出(output)
    webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件
  • loader
    由于webpack只能处理javascript,所以我们需要对一些非js文件
    处理成webpack能够处理的模块,比如sass文件
  • 插件(plugins)
    Loaders将各类型的文件处理成webpack能够处理的模块,plugins
    有着很强的能力。插件的范围包括,从打包优化和压缩,一直到重新定
    义环境中的变量。

安装

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

零配置


webpack4 设置了默认值,以便无配置启动项目
  • entry 默认值是 ./src/
  • output.path 默认值是 ./dist
  • mode 默认值是 production

模式

mode: development / production / none

开发模式 development
  • 浏览器调试工具
  • 注释、开发阶段的详细错误日志和提示
  • 快速和优化的增量构建机制
  • 开启 output.pathinfo 在 bundle 中显示模块信息
  • 开启 NamedModulesPlugin
  • 开启 NoEmitOnErrorsPlugin
生产模式 production
  • 启用所有优化代码的功能
  • 更小的bundle大小
  • 去除只在开发阶段运行的代码
  • 关闭内存缓存
  • Scope hoisting 和 Tree-shaking
  • 开启 NoEmitOnErrorsPlugin
  • 开启 ModuleConcatenationPlugin
  • 开启 optimization.minimize
none 会禁用所有的默认设置,可以使用 optimization.* 的方式去设定更详细的配置(搭建你的自定义模式)

webpack 更新日志

配置更新:

* NoEmitOnErrorsPlugin
-> optimization.noEmitOnErrors(production 模式默认开启)
* ModuleConcatenationPlugin
-> optimization.concatenateModules(production 模式默认开启)
* NameModulesPlugin 
-> optimization.nameModules(development 模式默认开启)
* CommonsChunkPlugin已经被移除 
-> optimization.splitChunks,optimization.runtimeChunk
* ExtractTextWebpackPlugin调整
-> 建议选用新的CSS文件提取插件mini-css-extract-plugin

JSON:

* webpack现在能处理原生的json
    1.当你需要通过loader去把json转换成js的时候,你可能需要添加
    type:"javascript/auto"
    2.不使用loader也可以直接使用JSON
* 允许通过ESM语法导入JSON
    1.JSON模块的未使用的导出部分会被消除

其他相关细节-> webpack更新日志

基本配置 webpack.base.conf.js

1.主入口文件 entry:
entry: {
  //入口文件,如果是多页项目,可配置多个
  app: '.src/main.js'
}
2.输出文件 output:
output: {
  // 导出目录的绝对路径,通过HtmlWebpackPlugin插件生成的html文件
  存放在这个目录下面
  path: path.resolve(__dirname, '../dist/' + outputDir),
  // 生产模式或者开发模式下的html,js等文件内部引用的公共路径
  publicPath: '/', 
  //导出文件的文件名,编译生成的js文件存放到根目录下面的js目录下面,
  如果js目录不存在则自动创建
  filename: 'js/[name]' + chunkhash + '.js’, 
  /*
      * chunkFilename用来打包require.ensure方法中引入的模块,如果该
        方法中没有引入任何模块则不会生成任何chunk块文件
      * 比如在main.js文件中,require.ensure([],function(require)
        {alert(11);}),这样不会打包块文件
      * 只有这样才会打包生成块文件require.ensure([],function(require)
        {alert(11);require('./greeter')})
      * 或者这样require.ensure(['./greeter'],function(require)
        {alert(11);})
      * chunk的hash值只有在require.ensure中引入的模块发生变化,hash
        值才会改变
      * 注意:对于不是在ensure方法中引入的模块,此属性不会生效,只能用
        CommonsChunkPlugin插件来提取(webpack 4中用splitChunks&runtimeChunk)
      * 
   */
  chunkFilename: 'js/[name]' + chunkhash + '.js'
},
3.文件解析
resolve: {
 //自动解析确定的扩展名,使倒入模块时不带扩展名
 extensions: ['.js', '.vue', '.json'],
 // 定义引用路径别名 配置别名可以加快webpack查找模块的速度
 alias: {
   'vue$': 'vue/dist/vue.esm.js’, 
   '@': path.resolve(__dirname, '../src’),
   'src': path.resolve(__dirname, '../src')
  }
}
4.模块解析module
{
  test: /\.vue$/, //规则对vue后缀
  loader: 'vue-loader’, //使用vue-loader进行处理
    query:{  //query是对loader做的额外配置
    limit: 10000    
  }
  include: /src/, //必须包含src文件夹
 }
 
loader 的用法准则:
    1.单一职责,一个loader只做一件事情,这样设计的原因是因为,职责越单一,组合性就强,
    可配置性就好。
    2.从右到左,链式执行,上一个loader的处理结果传给下一个loader接着处理
    3.模块化,一个loader就是一个模块,单独存在不依赖其他的任何模块
    4.无状态,就类似于纯函数。
    5.loader有实用工具loader-utils解析loader参数的和schema-utils校验格式的
    6.loader的依赖

Vue-cli 升级 Q&A

1.webpack4推荐使用了最新版本的vue-loader("vue-loader": "^15.0.10"),
但是最新的vue-loader需要在webapck config文件中设置VueLoaderPlugin插件,
否则会报以下错误:
vue-loader was used without the corresponding plugin.
Make sure to include VueLoaderPlugin in your webpack config.
解决:
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
    ...
    plugins: [
        // 添加VueLoaderPlugin,以响应vue-loader
        new VueLoaderPlugin()
  ],
    ...
}

2.Error: No PostCSS Config found in… 
解决:在项目根目录新建postcss.config.js文件,并对postcss进行配置:
module.exports = {  
  plugins: [
    require('autoprefixer')()
    ...
  ]  
}
3.关于CSS Modules相关
现在 Vue Loader v15 使用了一个不一样的策略来推导语言块使用的 loader。
拿`< style lang="less" >` 举例:在 v14 或更低版本中,它会尝试使用 
less-loader 加载这个块,并在其后面隐式地链上 css-loader 
和 vue-style-loader,这一切都使用内联的 loader 字符串。
在 v15 中,`< style lang="less" >` 会完成把它当作一个真实的 *.less 文件
来加载。因此,为了这样处理它,你需要在你的主 webpack 配置中显式地提供一条规则:
{
  module: {
    rules: [
      // ... 其它规则
      {
        test: /\.less$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'less-loader'
        ]
      }
    ]
  }
}
在 v15 中你再也不用在 Vue Loader 自己的 loaders 选项中将它重复一遍~
* 关于 CSS modules: https://juejin.cn/post/6844903624456273927
4.提取 js 中的 css 部分到单独的文件
* 新的替代插件 mini-css-extract-plugin
loader 部分:
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
rules: [
  {
    test: /\.css$/,
    use: [
      MiniCssExtractPlugin.loader,
      "css-loader"
    ]
  }
]
plugins 部分(区分环境决定hash值):
plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name]' + hash + '.css',
      chunkFilename: 'css/[name]' + hash + '.css'
    })
],
5.影响也最大的就是webpack4使用optimization.splitChunks替代了
CommonsChunkPlugin.以前的CommonsChunkPlugin主要用来抽取代码中的共用部分,
webpack runtime之类的代码,结合chunkhash,实现最好的缓存策略。
而optimization.splitChunks则实现了相同的功能,并且配置更加灵活,具体解释如下:
optimization: {
  //! 替代 CommonsChunkPlugin 提取公共代码
  splitChunks: {
    // chunks: "async",
    // minSize: 30000, //!块生成最小大小。
    // minChunks: 1, //!最小数量的块分裂之前,必须共享一个模块。
    // maxAsyncRequests: 5, //!按需加载时并行请求的最大数量。
    // maxInitialRequests: 3, //!最大数量的并行请求一个入口点。
    // name: true, //!分割块的名称。提供true将根据块和缓存组密钥自动生成名称
    //!缓存组会从splitChunks.*中继承相应的选项值,
    test、priority和reuseExistingChunk则只能在缓存组层次上配置。
    cacheGroups: {
        // default: {
        //   minChunks: 2,
        //   priority: -20,
        //   reuseExistingChunk: true
        // },
        //!"initial"、"async"和"all"。分别用于选择初始块、按需加载的块和所有块。
        vendors: {
            test: /[\\/]node_modules[\\/]/, //! 不继承
            priority: -10, //!不继承,该配置项是设置处理的优先级,数值越大越优先处理
            chunks: 'initial',
            reuseExistingChunk: true, //!不继承 ,没有默认值,选项用于配置在模块完全
            匹配时重用已有的块,而不是创建新块
            name: 'vendors',
        },
        'async-vendors': {
            test: /[\\/]node_modules[\\/]/,
            minChunks: 2,
            chunks: 'async',
            reuseExistingChunk: true,
            name: 'async-vendors'
        },
        //!多个css chunk合并成一个css文件
        styles: {
            name: 'styles',
            test: /\.(scss|sass|less|css)$/,
            chunks: 'all',
            minChunks: 1,
            reuseExistingChunk: true
        }
      }  
    },
   // !分离出webpack编译运行时的代码
   runtimeChunk: { name: 'manifest' }
},

* webpack.optimize.UglifyJsPlugin现在也不需要了,只需要使用
  optimization.minimize为true就行,
  production mode下面自动为true,当然如果想使用第三方的压缩插件也可以
  在optimization.minimizer的数组列表中进行配置
/** like this code **/
optimization:{
  minimizer: [
    new UglifyJsPlugin({
        exclude: /\.min\.js$/,
        cache: false, //! 文件缓存 
        parallel: true, //! 利用多进程并行运行提高构建速度
        sourceMap: false,
        extractComments: false, //! 移除注释
        //! uglifyES
        uglifyOptions: {
          compress: {
            warnings: false, //! 在UglifyJs删除没有用到的代码时不输出警告
            drop_console: true, //! 删除所有的 `console` 语句,可以兼容ie浏览器
            collapse_vars: true, //! 内嵌定义了但是只用到一次的变量
            reduce_vars: true //! 提取出出现多次但是没有定义成变量去引用的静态值
          },
          output: {
            beautify: false, //!不需要格式化
            comments: false //!不保留注释
          }
        }
    })
  ]
}

性能优化

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

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

Tips

1.建议在 webpack 构建流程中使用到的 loaders 及 plugins 都升级到最新版本,
如果 pakage.json 中有相应的模块配置,可删除之后重新安装
Webpack5展望:
* ESM 模块导出支持
* 持久缓存
* WebAssembly 支持从 experimental 升级为 stable 稳定版。并增加 * tree-shaking 和未使用代码去除!
* Presets —— 基于零配置设计,任何东西都能支持零配置
* CSS 模块类型——支持 CSS 作为入口文件(再见吧 ExtractTextWebpackPlugin)
* HTML 模块类型——支持 HTML 作为入口文件
* URL/文件 模块类型
* 自定义模块类型
* 多线程
* 重新定义我们的组织章程和计划任务
* Google Summer of Code (之后单独写问说明!!!)


参考阅读:
webpack 更新日志-English
webpack4.0 升级日志-中文版
webpack-module官网
webpack 4 发布English
webpack 4 发布