安装webpack
首先安装webpack、webpack-cli、webpack-dev-server。javascript
项目初始化
这里使用vue项目作演示,大家能够用别的,不影响webpack的构建。创建webpack.config.js文件css
引入一系列的依赖和loader
vue-loader和vue-template-compiler是vue必须的,官方地址:https://vue-loader.vuejs.org/zh/。html
node-sass、less、css-loader、vue-style-loader、less-loader和sass-loader让咱们项目支持css、less和sass。vue
babel-loader、@babel/core、@babel/preset-env让咱们项目支持ES6。java
还有图片文件和字体文件的加载器,url-loader依赖于file-loader因此都要安装 。node
关于devServer
如其名称,devServer是用于启动开发环境的服务的,配置项能够上官网看,比较好理解。这里就提两个重要的配置项,一是hot为true时,能够启动HMR。二是proxy,在先后端分离的项目,可使用这个配置来代理后台的服务地址。webpack
//webpack.config.js const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports ={ entry:'./src/main.js', // 项目的入口文件 output:{ filename: 'bundle.js', // 打包后的文件名 path: path.join(__dirname, 'dist'), // 项目的打包文件路径 publicPath: 'dist/' // 输出解析文件的目录,指定资源文件引用的目录 }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: file => ( /node_modules/.test(file) && !/\.vue\.js/.test(file) ) }, { test: /\.vue$/, loader: 'vue-loader' // vue官网介绍,使用这个loader的同时,还须要VueLoaderPlugin }, { test: /\.css$/, //匹配后缀名为css的文件,而后分别用css-loader,vue-style-loader去解析 use: [ //解析器的执行顺序是从下往上(先css-loader再vue-style-loader) 'vue-style-loader', 'css-loader' ], }, { test: /\.scss$/, use: [ 'vue-style-loader', 'css-loader', 'sass-loader' ], }, { test: /\.less$/, use: [ 'vue-style-loader', 'css-loader', 'less-loader' ] }, { test:/\.(png|jpe?j|gif|svg)(\?.*)?$/, loader:'url-loader', options:{ limit: 10 * 1024, // 10 KB //图片文件大小小于limit的数值,就会被改写成base64直接填入url里面 } }, ] }, plugins: [ new VueLoaderPlugin() // 官方使用vue-loader时,必要的plugin ], devServer: { // 开发服务器工具 contentBase: './public', //基于的根目录 open: true, // 自动打开浏览器 overlay: true // 将错误显示在html之上 } }
为了之后方便启动,把config文件配置好后,再把package.json的scripts也配置一下web
"scripts": { "serve": "webpack-dev-server", //webpack5.0和webpack-cli4.0不支持webpack-dev-server,使用webpack serve "build": "webpack" }
这时启动项目,就能够看到编译后的项目了。json
引入经常使用的插件
clean-webpack-plugin能够在每次打包前把上一次打包的文件删除再进行打包。后端
html-webpack-plugin能够将html文件打包后自动引入打包后的js文件,能够经过设置相关属性来指定html,若是想要生成多个html,能够屡次new这个插件。引入这个插件后,就不须要设置output.publicPath和devServer.contentBase属性了。
copy-webpack-plugin将须要拷贝的文件放入打包后的文件,通常不在开发阶段使用。
HotModuleReplacementPlugin热更新插件(也称HMR),这个插件是webpack自己提供的,不须要再单独去下载依赖。在devServer中配置{hot:true}时启动,css自动生效,而js等须要手动添加(使用module.hot.accept(dep,callback))
//webpack.config.js const webpack = require('webpack') const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports ={ entry:'./src/main.js', // 项目的入口文件 output:{...}, module: {...}, plugins: [ new VueLoaderPlugin(), // 官方使用vue-loader时,必要的plugin new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Webpack Vue Sample', // 设置title meta: { // 设置meta viewport: 'width=device-width' }, template: './public/index.html' // 指定模板 }), new webpack.HotModuleReplacementPlugin(), // // 开发阶段最好不要使用这个插件 // new CopyWebpackPlugin(['public']) ], devServer: { // 开发服务器工具 hot: true, // 启动HMR ,若是要手动添加一些文件的HMR,能够用hotOnly来调试 open: true, // 自动打开浏览器 overlay: true // 将错误显示在html之上 } }
不一样工做环境的配置
建立不一样环境配置的方式主要有两种方法,一是在配置文件中添加相应的条件,根据不一样的条件来导出相应的配置;二是根据不一样的环境添加对应的配置文件。在不一样的环境能够有不一样配置项,像mode、devtool等,还有上面用到的clean-webpack-plugin、copy-webpack-plugin等插件均可以区分到所对应的环境中。
根据不一样的条件来导出相应的配置
这个方法主要经过cli配置的环境名参数,而后再module.exports里接收这个参数来进行判断,启动production的方法能够是在命令行输入‘webpack --env production’,也能够配置在scripts中。
module.exports = (env, argv) => { const config = { mode: 'development', entry: './src/main.js', output: {...}, devtool: 'cheap-eval-module-source-map', devServer: {...}, module: {...}, plugins: [...] }; if (env === 'production') { config.mode = 'production'; config.devtool = false; config.plugins = [ ...config.plugins, new CleanWebpackPlugin(), new CopyWebpackPlugin(['public']) ]; } return config; }
根据不一样的环境添加对应的配置文件
这个方法如其名,是要创建不一样环境的配置文件来进行匹配。通常须要创建一个webpack.common.js一个公共配置,而后再按开发、生产的等环境在创建对应环境特有的配置文件,最后在启动项目时经过'webpack --config webpack.prod.js'来启动不一样的项目便可。
// webpack.common.js const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports ={ entry:'./src/main.js', // 项目的入口文件 output:{...}, module: {...}, plugins: [ new VueLoaderPlugin(), // 官方使用vue-loader时,必要的plugin new HtmlWebpackPlugin({ title: 'Webpack Vue Sample', // 设置title meta: { // 设置meta viewport: 'width=device-width' }, template: './public/index.html' // 指定模板 }), ], }
//webpack.dev.js const webpack = require('webpack'); const {merge} = require('webpack-merge'); const common = require('./webpack.common'); module.exports = merge(common,{ mode: 'development', devtool: 'cheap-eval-module-source-map', plugins: [ new webpack.HotModuleReplacementPlugin(), ], devServer: { // 开发服务器工具 hot: true, // 启动HMR open: true, // 自动打开浏览器 overlay: true // 将错误显示在html之上 } });
// webpack.prod.js const webpack = require('webpack') const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin') const {merge} = require('webpack-merge'); const common = require('./webpack.common'); module.exports = merge(common,{ mode:'production', plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin( ["public"]) ], });
配置全局常量
经过对开发模式和生产模式配置不一样的常量,从而能够在构建时容许不一样的行为。 可使用webpack自带的DefinePlugin来进行配置。
new webpack.DefinePlugin({ BASE_URL:JSON.stringify('./') }),
打包过程的优化
Tree Shaking
Tree Shaking在production模式下会自动开启,咱们想要在其余模式下也使用的话,须要本身来配置。
optimization: { usedExports: true, // 模块只导出被使用的成员 concatenateModules: true, // 尽量合并每个模块到一个函数中 minimize: true, // 压缩输出结果 },
sideEffects
sideEffects在production模式下也会自动开启,它主要的做用是对所标识的没有反作用文件进行识别,将没有用的到的代码不进行打包。想在除production模式下也启用这个功能,能够对webpack配置文件和package.json文件两个文件进行配置。
// webpack配置文件 optimization: { sideEffects: true, // 开启sideEffects 功能 },
// package.json "sideEffects": [ "*.css" //标识有反作用的文件 ]
Code Splitting
Code Splitting主要做用就是将打包后的代码进行分包,具体实现有两种方法:1.多入口打包;2.动态导入;
多入口打包
多入口打包的功能主要经过webpack的配置来实现
// 多入口打包 module.exports = { entry: { // 入口改成多文件导入 index: './src/index.js', app: './src/app.js' }, output: { filename: '[name].bundle.js' // 导出的文件名动态生成 }, module: {...}, optimization: { splitChunks: { chunks: 'all' // 防止重复引用,自动提取全部公共模块到单独 bundle } }, plugins: [ new HtmlWebpackPlugin({ title: 'index Page', template: './src/index.html', filename: 'index.html', chunks: ['index'] //设置html须要导入的js文件 }), new HtmlWebpackPlugin({ title: 'app Page', template: './src/app.html', filename: 'app.html', chunks: ['app'] }) ] }
动态导入
动态导入指的是在应用运行到须要某个模块的时候,再来导入这个模块。webpack自动支持这种方式而且会单独进行分包,其主要应用场景是路由切换时。打包后的文件还能够经过魔法注释(/* webpackChunkName: 'name' */)来定义文件的名称
const render = () => { const hash = window.location.hash || '#home' const mainElement = document.querySelector('.main') mainElement.innerHTML = '' if (hash === '#home') { import(/* webpackChunkName: 'homeCom' */'./home/home').then(({ default: home}) => { mainElement.appendChild(home()) }) } else if (hash === '#index') { import(/* webpackChunkName: 'indexCom' */'./index/index').then(({ default: index}) => { mainElement.appendChild(index()) }) } } render() window.addEventListener('hashchange', render)
css文件提取到单个文件
当css文件比较大想要单独提取出来时,可使用‘mini-css-extract-plugin’插件,在使用这个插件时,还要同时使用‘optimize-css-assets-webpack-plugin’、'terser-webpack-plugin'这两个插件,‘optimize-css-assets-webpack-plugin’是用来压缩css文件的,'terser-webpack-plugin'是用来压缩js文件的。由于在配置minimizer后,webpack会取消默认的js压缩plugin,多以须要本身再把js的plugin给添加进来。
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') const TerserWebpackPlugin = require('terser-webpack-plugin') module.exports = { entry: './src/index.js', output: {...}, optimization: { minimizer: [ // webpack官方建议把压缩性质的文件用minimize来统一管理 new TerserWebpackPlugin(), // js文件压缩 new OptimizeCssAssetsWebpackPlugin() // css文件压缩 ] }, module: { rules: [ ..., { test: /\.css$/, use: [ // 'style-loader', // 将样式经过 style 标签注入 MiniCssExtractPlugin.loader, // 替换掉'style-loader' 'css-loader' ] } ] }, plugins: [ ..., new MiniCssExtractPlugin() // 提取css文件到单个文件,若是单个css文件体积不大,不必单独提取到一个文件 ] }
substitution
webpack提供了一个substitution(可替换的模版字符串)的方式,这个方式将根据资源内容建立出惟一 hash 。主要有三个模板:1.'hash',2.'chunckhash',3.'contenthash',具体何时用,就不作多介绍了。
new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash:4].css', // hash默认20位长度,能够用':'接数字,指定长度 chunkFilename: 'static/css/[name].[contenthash:4].css', }),
ESlint
使用eslint-loader做为pre-loader运用,能够在开发过程当中每次保存的时候就会自动进行代码校验。
module.exports = { module: { rules: [ { enforce: 'pre', test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /node_modules/ } ] } }