kd-cloud-web / Blog

一群人, 关于前端, 做一些有趣的事儿
13 stars 1 forks source link

PeerShare-Webpack版本更替 #35

Open ohnowho opened 5 years ago

ohnowho commented 5 years ago

webpack各个版本比较

Gulp 本质是 task runner,Webpack 是 module bundler 我认为 Gulp 正如他的定义一样:基于流的自动化构建工具,定义每一个任务,然后自动将一个个任务执行。 而 webpack 是模块化地组织,模块化地依赖,然后模块化地打包。相对来上,场景局限在前端模块化打包上。

webpack-cli npm install webpack webpack-cli --save-dev webpack 4 把核心代码和客户端分离开了,现在需要安装一个 webpack-cli 才可以启动 webpack 命令。

webpack 1

webpack2

  1. 增加对 ES6 模块的原生支持
  2. 可以混用 ES2015 和 AMD 和 CommonJS
  3. 支持 tree-shaking(减少打包后的体积)
  4. 新增更多的 CLI 参数项 -p 指定当前的编译环境为生产环境,即设置 process.env.NODE_ENV 为 production

5. 配置选项语法有较大改动,且不向下兼容

  1. 配置项 - resolve(解析)

    取消了 extensions 空字符串(表示导入文件无后缀名)

    Webpack1

resolve: {
    extensions: ['', '.js', '.css'],
    modulesDirectories: ['node_modules', 'src']
}

    Webpack2

resolve: {
    extensions: ['.js', '.css'],
    modules: [
        path.resolve(__dirname, 'node_modules'),
        path.join(__dirname, './src')
    ]
}
  1. 配置项 - module(模块)

    外层 loaders 改为 rules 内层 loader 改为 use 所有插件必须加上 -loader,不再允许缩写 不再支持使用!连接插件,改为数组形式

    json-loader 模块移除,不再需要手动添加,webpack2 会自动处理

    Webpack1

module: {
    loaders: [{
        test: /\.(less|css)$/,
        loader: "style!css!less!postcss"
    }, {
        test: /\.json$/,
        loader: 'json'
    }]
}

    Webpack2

module: {
    rules: [{
        test: /\.(less|css)$/,
        use: [
            "style-loader", 
            "css-loader", 
            "less-loader", 
            "postcss-loader"
        ]
    }]
};
  1. 配置项 - plugins(插件)

    移除了 OccurenceOrderPlugin 模块(已内置)、NoErrorsPlugin 模块(已内置)

webpack 3

2 3 两个版本几乎完全兼容,新增部分新特性

  1. 加入 Scope Hoisting(作用域提升)

    之前版本将每个依赖都分别封装在一个闭包函数中来独立作用域。这些包装函数闭包函数降低了浏览器 JS 引擎解析速度 Webpack 团队参考 Closure Compiler 和 Rollup JS,将有联系的模块放到同一闭包函数中,从而减少闭包函数数量,使文件大小的少量精简,提高 JS 执行效率 在 Webpack3 配置中加入 ModuleConcatenationPlugin 插件来启用作用域提升

module.exports = { plugins: [ new webpack.optimize.ModuleConcatenationPlugin() ] };

  1. 加入 Magic Comments(魔法注解)

    在 Webpack2 中引入了 Code Splitting-Async 的新方法 import(),用于动态引入 ES Module,Webpack 将传入 import 方法的模块打包到一个单独的代码块(chunk),但是却不能像 require.ensure 一样,为生成的 chunk 指定 chunkName。因此在 Webpack3 中提出了 Magic Comment 用于解决该问题

import(/ webpackChunkName: "my-chunk-name" / 'module');

webpack4

webpac 4 是一个大版本,其中 plugin 机制都变化挺大的,几乎所有的 plugin 都需要升级来做兼容,这里简单记录一下遇到的一些问题。

零配置:Webpack 4 不需要配置文件,这是 Webpack 4 的一个全新的变更。

性能提升:Webpack 4 是迄今为止最快的 Webpack 版本。

合理的默认值:Webpack 4 的主要概念包括入口、输出、加载器和插件。

  1. webpack 4 把核心代码和客户端分离开了,现在需要安装一个 webpack-cli 才可以启动 webpack 命令。 npm i webpack-cli --save-dev

  2. html-webpack-plugin 要升级到 3.0.6 以上即可。

  3. module.loaders 替换为 modules.rules

module: {
  loaders: [
    ...
  ]
}

替换为:

module: {
  rules: [
    ...
  ]
}
  1. 单独打包 CSS 的 extract-text-webpack-plugin

extract-text-webpack-plugin 暂不支持 webpack 4,我们可以使用另外一个插件 mini-css-extract-plugin 来替换之。

举个栗子(顺便支持一下 less):

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

...

  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: 'css/index.css' }),
  ],

...

  module: {
    rules: [
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
    ]
  }
  1. mode

webpack 4 增加了一个 mode 配置,值可选有两个 development 和 production,对不同的环境他会提供不同的一些默认配置,比如开发环境下默认开启 optimization.namedModules(原NamedModulesPlugin,现已弃用),而生产环境默认使用 `optimization.noEmitOnErrors(原NoEmitOnErrorsPlugin,现已弃用)。

不同模式下的默认配置:

生产环境默认开启了很多代码优化(minify,splite 等)
开发时开启注视和验证,并且自动加上了 eval devtool
生产环境不支持 watching,开发环境优化了重新打包的速度
生产环境开启模块串联(原 ModuleConcatenationPlugin),没用过不多说
自动设置 process.env.NODE_ENV 到不同环境,也就是不需要 DefinePlugin 来做这个了
如果你给 mode 设置为 none,所有默认配置都去掉了
  1. CommonsChunkPlugin 和 UglifyJsPlugin 已废弃CommonsChunkPlugin,已调整 UglifyJsPlugin 到optimization.minimizer 默认配置UglifyJsPlugin 除了mode: 'development' 的情况

CommonsChunkPlugin删除之后,改成使用optimization.splitChunks进行模块划分,详细文档看 这里。

官方的说法是默认设置已经对大部分用户来说非常棒了,但是需要注意一个问题,默认配置只会对异步请求的模块进行提取拆分,如果要对 entry 进行拆分,需要设置 optimization.splitChunks.chunks = 'all'。

对应之前我们拆分runtime的情况,现在也有一个配置optimization.runtimeChunk,设置为true就会自动拆分runtime文件。

真实例子 webpack.conf.js

'use strict';

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // Html文件处理

module.exports = {
  entry: {
    index: './src/index.js'
  },
  output: {
    path: path.resolve(__dirname, './build'), // This is where images AND js will go
    publicPath: '', // This is used to generate URLs to e.g. images
    filename: 'js/[name].js',
    chunkFilename: 'js/[id].chunk.js?[hash:8]'
  },
  mode: 'development', // 'production'
  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    // 单独打包CSS
    new MiniCssExtractPlugin({ filename: 'css/index.css' }),

    new CopyWebpackPlugin([
      { from: 'src/images/*', to: 'images/', flatten: true},
      { from: 'src/parallax/*', to: 'js/', flatten: true}
      ], {}),

    new HtmlWebpackPlugin({
      filename: 'index.html',
      chunks: ['index'],
      template: './src/index.html',
      hash: true
    })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader', // ES6
        exclude: /(node_modules|parallax|fullpage)/
      },
      // { test: /\.css$/, loader: 'style-loader!css-loader' },
      // { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.(png|jpg|gif)$/,
        loader: 'url-loader',
        query: {
          name: '[path][name].[ext]?[hash:8]',
          limit: 8192 // inline base64 URLs for <=8k images, direct URLs for the rest
        }
      }
    ]
  }
};

问题总结

extract-text-webpack-plugin


extract-text-webpack-plugin@2.1.2 requires a peer of webpack@^2.2.0 but none is installed. You must install peer dependencies yourself.
npm WARN webpack-dev-middleware@1.12.2 requires a peer of webpack@^1.0.0 || ^2.0.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN webpack-dev-server@2.11.2 requires a peer of webpack@^2.2.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN ajv-errors@1.0.1 requires a peer of ajv@>=5.0.0 but none is installed. You must install peer dependencies yourself.

webpack4,不再支持extract-text-webpack-plugin npm i mini-css-extract-plugin -D npm uninstall extract-text-webpack-plugin -D

npm i webpack-dev-server webpack-dev-middleware -D npm uninstall webpack-dev-server webpack-dev-middleware -D

ajv-errors npm uninstall ajv-errors -D npm i ajv-errors -D

npm install -g npm-install-peers

npm install -g npm npm i ajv

file-loader

 Module not found: Error: Can't resolve 'file' in 'E:\workspace\kae-doc\public'
BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.
                 You need to specify 'file-loader' instead of 'file',

npm i file-loader -D loader: 'file', 改为 loader: 'file-loader',

部分图片不可用

一部分图片可以用 一部分不可以 因为 url-loader 和file-loader处理不同

babel-cli

TypeError: Cannot read property 'bindings' of null

npm install --save-dev @babel/cli @babel/core @babel/preset-env npm uninstall -D babel-loader babel-core babel-preset-env

baggyui 和 chopperui同时在一个项目中引用

icon乱码
输出文件加上hash 用于区别

打包原理

- 模块:webpack中一切资源文件(JavaScript, 样式, 图片资源等)皆视为模块;
- 加载器:webpack通过加载器(JavaScript函数)将其他资源处理(构建)成JavaScript模块;
- 管理依赖:webpack在编译模块时,就能分析查找该模块内的依赖,可以很好的处理不同模块间的依赖;

webpack.config.js

文件输入输出


exports.entries = function () {
  var names = rf.readdirSync(PAGE_PATH + '/');
  var entryFiles = glob.sync(PAGE_PATH + '/*/index.js')
  var map = {}
  entryFiles.forEach((filePath, i) => {
    var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'));
    map[names[i]] = filePath;
  })

  return map
}

  context: path.resolve(__dirname, '../'),

  entry: utils.entries(),

  output: {
    path: path.join(__dirname, '../dist'),
    publicPath: '/',
    filename: '[name].js',
    chunkFilename: '[name].chunk.js'
  },

module

resolve

resolve.root与resolve.modules 配置根路径 resolve.root在webpack v1版本中使用,而在webpack v2 中,使用modules代替,建议使用v2版

    resolve: {
        root: [
            path.resolve('./src/'),
            'node_modules'
        ]
    }

resolve: {
    extensions: ['.js', '.css', '.scss', '.vue'],//解析模块时的查找文件的后缀顺序
    alias: {
      'vue$': 'vue/dist/vue.common.js',
      'utils': resolve('/src/utils'),
      'assist': resolve('/src/assist'),
    }
  },

当vue是最后一级路径时,webpack才会将该值解析成别名,进行别名与对应值替换,如vue/test.js中的vue是不会当作别名解析的

plugins

对你的项目webpack模块构建过程进行额外处理,如代码压缩,图片等资源提交压缩,构建异常捕获和提升,构建流程时间消耗比,等等

babel-polyfill

我们需要明白的一点是babel-preset-es2015能做的,只是转换ES6代码成ES5,使得浏览器可以解析执行,但是对于ES6新提出的API,如Promise,Generator等无法简单的转换成ES5代码,这时就需要babel-polyfill了,babel-polyfill是一个垫片,它可以模拟提供所有的ES6功能和特性,可以看作是提供了一个模拟的全局ES6环境

在webpack.config.js中配置打包入口文件时加入该垫片:

    entry: {
        app: ['babel-polyfill', './scripts/app.js']
    },

webpack-dev-server

webpack-dev-server是一个Node.js的express服务器,以webpack开发中间件的形式为webpack包提供服务,当监听到源码文件变更时,会自动重新打包,并且支持配置自动刷新浏览器,重新加载资源。

开启webpack-dev-server后,变更文件重新打包后,并不会实际输出到配置的output目录,而是在publicPath属性声明的相对路径所在的内存中读取

参考链接

打包原理

ohnowho commented 5 years ago

webpack各个版本比较

Gulp 本质是 task runner,Webpack 是 module bundler 我认为 Gulp 正如他的定义一样:基于流的自动化构建工具,定义每一个任务,然后自动将一个个任务执行。 而 webpack 是模块化地组织,模块化地依赖,然后模块化地打包。相对来上,场景局限在前端模块化打包上。

webpack-cli npm install webpack webpack-cli --save-dev webpack 4 把核心代码和客户端分离开了,现在需要安装一个 webpack-cli 才可以启动 webpack 命令。

webpack 1

webpack2

  1. 增加对 ES6 模块的原生支持
  2. 可以混用 ES2015 和 AMD 和 CommonJS
  3. 支持 tree-shaking(减少打包后的体积)
  4. 新增更多的 CLI 参数项 -p 指定当前的编译环境为生产环境,即设置 process.env.NODE_ENV 为 production

5. 配置选项语法有较大改动,且不向下兼容

  1. 配置项 - resolve(解析)

    取消了 extensions 空字符串(表示导入文件无后缀名)

    Webpack1

resolve: {
    extensions: ['', '.js', '.css'],
    modulesDirectories: ['node_modules', 'src']
}

    Webpack2

resolve: {
    extensions: ['.js', '.css'],
    modules: [
        path.resolve(__dirname, 'node_modules'),
        path.join(__dirname, './src')
    ]
}
  1. 配置项 - module(模块)

    外层 loaders 改为 rules 内层 loader 改为 use 所有插件必须加上 -loader,不再允许缩写 不再支持使用!连接插件,改为数组形式

    json-loader 模块移除,不再需要手动添加,webpack2 会自动处理

    Webpack1

module: {
    loaders: [{
        test: /\.(less|css)$/,
        loader: "style!css!less!postcss"
    }, {
        test: /\.json$/,
        loader: 'json'
    }]
}

    Webpack2

module: {
    rules: [{
        test: /\.(less|css)$/,
        use: [
            "style-loader", 
            "css-loader", 
            "less-loader", 
            "postcss-loader"
        ]
    }]
};
  1. 配置项 - plugins(插件)

    移除了 OccurenceOrderPlugin 模块(已内置)、NoErrorsPlugin 模块(已内置)

webpack 3

2 3 两个版本几乎完全兼容,新增部分新特性

  1. 加入 Scope Hoisting(作用域提升)

    之前版本将每个依赖都分别封装在一个闭包函数中来独立作用域。这些包装函数闭包函数降低了浏览器 JS 引擎解析速度 Webpack 团队参考 Closure Compiler 和 Rollup JS,将有联系的模块放到同一闭包函数中,从而减少闭包函数数量,使文件大小的少量精简,提高 JS 执行效率 在 Webpack3 配置中加入 ModuleConcatenationPlugin 插件来启用作用域提升

module.exports = { plugins: [ new webpack.optimize.ModuleConcatenationPlugin() ] };

  1. 加入 Magic Comments(魔法注解)

    在 Webpack2 中引入了 Code Splitting-Async 的新方法 import(),用于动态引入 ES Module,Webpack 将传入 import 方法的模块打包到一个单独的代码块(chunk),但是却不能像 require.ensure 一样,为生成的 chunk 指定 chunkName。因此在 Webpack3 中提出了 Magic Comment 用于解决该问题

import(/ webpackChunkName: "my-chunk-name" / 'module');

webpack4

webpac 4 是一个大版本,其中 plugin 机制都变化挺大的,几乎所有的 plugin 都需要升级来做兼容,这里简单记录一下遇到的一些问题。

零配置:Webpack 4 不需要配置文件,这是 Webpack 4 的一个全新的变更。

性能提升:Webpack 4 是迄今为止最快的 Webpack 版本。

合理的默认值:Webpack 4 的主要概念包括入口、输出、加载器和插件。

  1. webpack 4 把核心代码和客户端分离开了,现在需要安装一个 webpack-cli 才可以启动 webpack 命令。 npm i webpack-cli --save-dev

  2. html-webpack-plugin 要升级到 3.0.6 以上即可。

  3. module.loaders 替换为 modules.rules

module: {
  loaders: [
    ...
  ]
}

替换为:

module: {
  rules: [
    ...
  ]
}
  1. 单独打包 CSS 的 extract-text-webpack-plugin

extract-text-webpack-plugin 暂不支持 webpack 4,我们可以使用另外一个插件 mini-css-extract-plugin 来替换之。

举个栗子(顺便支持一下 less):

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

...

  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: 'css/index.css' }),
  ],

...

  module: {
    rules: [
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
    ]
  }
  1. mode

webpack 4 增加了一个 mode 配置,值可选有两个 development 和 production,对不同的环境他会提供不同的一些默认配置,比如开发环境下默认开启 optimization.namedModules(原NamedModulesPlugin,现已弃用),而生产环境默认使用 `optimization.noEmitOnErrors(原NoEmitOnErrorsPlugin,现已弃用)。

不同模式下的默认配置:

生产环境默认开启了很多代码优化(minify,splite 等)
开发时开启注视和验证,并且自动加上了 eval devtool
生产环境不支持 watching,开发环境优化了重新打包的速度
生产环境开启模块串联(原 ModuleConcatenationPlugin),没用过不多说
自动设置 process.env.NODE_ENV 到不同环境,也就是不需要 DefinePlugin 来做这个了
如果你给 mode 设置为 none,所有默认配置都去掉了
  1. CommonsChunkPlugin 和 UglifyJsPlugin 已废弃CommonsChunkPlugin,已调整 UglifyJsPlugin 到optimization.minimizer 默认配置UglifyJsPlugin 除了mode: 'development' 的情况

CommonsChunkPlugin删除之后,改成使用optimization.splitChunks进行模块划分,详细文档看 这里。

官方的说法是默认设置已经对大部分用户来说非常棒了,但是需要注意一个问题,默认配置只会对异步请求的模块进行提取拆分,如果要对 entry 进行拆分,需要设置 optimization.splitChunks.chunks = 'all'。

对应之前我们拆分runtime的情况,现在也有一个配置optimization.runtimeChunk,设置为true就会自动拆分runtime文件。

真实例子 webpack.conf.js

'use strict';

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // Html文件处理

module.exports = {
  entry: {
    index: './src/index.js'
  },
  output: {
    path: path.resolve(__dirname, './build'), // This is where images AND js will go
    publicPath: '', // This is used to generate URLs to e.g. images
    filename: 'js/[name].js',
    chunkFilename: 'js/[id].chunk.js?[hash:8]'
  },
  mode: 'development', // 'production'
  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  plugins: [
    // 单独打包CSS
    new MiniCssExtractPlugin({ filename: 'css/index.css' }),

    new CopyWebpackPlugin([
      { from: 'src/images/*', to: 'images/', flatten: true},
      { from: 'src/parallax/*', to: 'js/', flatten: true}
      ], {}),

    new HtmlWebpackPlugin({
      filename: 'index.html',
      chunks: ['index'],
      template: './src/index.html',
      hash: true
    })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader', // ES6
        exclude: /(node_modules|parallax|fullpage)/
      },
      // { test: /\.css$/, loader: 'style-loader!css-loader' },
      // { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.(png|jpg|gif)$/,
        loader: 'url-loader',
        query: {
          name: '[path][name].[ext]?[hash:8]',
          limit: 8192 // inline base64 URLs for <=8k images, direct URLs for the rest
        }
      }
    ]
  }
};

问题总结

extract-text-webpack-plugin


extract-text-webpack-plugin@2.1.2 requires a peer of webpack@^2.2.0 but none is installed. You must install peer dependencies yourself.
npm WARN webpack-dev-middleware@1.12.2 requires a peer of webpack@^1.0.0 || ^2.0.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN webpack-dev-server@2.11.2 requires a peer of webpack@^2.2.0 || ^3.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN ajv-errors@1.0.1 requires a peer of ajv@>=5.0.0 but none is installed. You must install peer dependencies yourself.

webpack4,不再支持extract-text-webpack-plugin npm i mini-css-extract-plugin -D npm uninstall extract-text-webpack-plugin -D

npm i webpack-dev-server webpack-dev-middleware -D npm uninstall webpack-dev-server webpack-dev-middleware -D

ajv-errors npm uninstall ajv-errors -D npm i ajv-errors -D

npm install -g npm-install-peers

npm install -g npm npm i ajv

file-loader

 Module not found: Error: Can't resolve 'file' in 'E:\workspace\kae-doc\public'
BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.
                 You need to specify 'file-loader' instead of 'file',

npm i file-loader -D loader: 'file', 改为 loader: 'file-loader',

部分图片不可用

一部分图片可以用 一部分不可以 因为 url-loader 和file-loader处理不同

babel-cli

TypeError: Cannot read property 'bindings' of null

npm install --save-dev @babel/cli @babel/core @babel/preset-env npm uninstall -D babel-loader babel-core babel-preset-env

baggyui 和 chopperui同时在一个项目中引用

icon乱码
输出文件加上hash 用于区别

参考链接

打包原理