以前一直用vue+webpack在写前端项目,最近换了个环境后,项目都是用react来开发的,因此也想捣鼓下框架啥的东西,最近也看了下webpack4,感受这玩意儿愈来愈集成化了,有些插件已经被丢弃了或者集成了,不过在实际项目开发中,也存在一些依赖版本方面的问题,本文将带你走进webpack的世界,一块儿看看那些坑吧。css
本人写的实例,已上传至GitHub,点击项目地址能够查看详情,欢迎star哦。html
webpack做为时下前端最流行的自动化构建工具,其版本更新也是一路备受关注,目前大多数新项目都会采用webpack4.0+去构建,接下来在了解如何用webpack从零开始搭建本身的项目以前,咱们先仍是熟悉下webpack的基本概念:前端
webpack: 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序须要的每一个模块,而后将全部这些模块打包成一个或多个 bundle文件被浏览器识别使用。vue
gulp和grunt是基于流的一种管理工具,经过创建一个个task任务,配置执行用户须要的功能,如格式检验,代码压缩等,值得一提的是,通过这二者处理的代码只是局部变量名被替换简化,总体并无发生改变,仍是你的代码。node
webpack是基于入口文件,识别模块依赖关系,分析代码,转换代码,编译代码,输出代码,通过打包后的代码基本已是认不出来了,有点像压缩的jq那样的感受,webpack它自己也是一个node的模块,webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)。react
熟悉了webpck的一些基本概念后,接下来,咱们从零开始真正的配置下webpack.config.js文件。webpack
mkdir react-webpack4
cd react-webpack4
mkdir src
mkdir dist
npm init -y
// 安装webpack和相关模块
yarn add webpack webpack-cli webpack-dev-server -D //webpack4.X 把webpack拆分了
touch webpack.config.js // mac上建立文件
echo test>webpack.config.js // win上建立文件
module.exports = {
entry: ["./src/index.js"], // 项目文件入口
output: {
// 输出目录
path: path.resolve(__dirname, "../dist")
},
module: {},
plugins:[],
devServer: {}
}
复制代码
webpack.config.js的文件结构就是如上,可是在项目开发时, 每每开发环境和生产环境在配置上会有些不一样,因此为了区分开来,咱们在项目根目录下建立一个build的文件夹,在该文件夹下面建立如下三个文件:git
webpack.base.config.js // 基本配置
webpack.dev.config.js // 开发环境
webpack.prod.config.js // 生产环境
复制代码
接下来先看下webpack.base.config.js基本配置:es6
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
entry: ["./src/index.js"],
output: {
// 输出目录
path: path.resolve(__dirname, "../dist")
},
resolve: {
extensions: [".js", ".jsx"], // 扩展
alias: {
'@': path.resolve(__dirname, 'src'),
'@pages': path.resolve(__dirname, 'src/pages'),
'@router': path.resolve(__dirname, 'src/router'),
'@assets': path.resolve(__dirname, 'src/assets')
}
},
module: {
rules: [
{
test: /\.(js|jsx?)$/,
exclude: /node_modules/,
use: [
{
loader: "happypack/loader?id=happyBabel"
}
]
},
{
test: /\.(le|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader", // 编译css
"postcss-loader", // 使用 postcss 为 css 加上浏览器前缀
"less-loader", // 编译less
]
},
{
test: /\.(png|jpg|jpeg|gif|svg)/,
use: {
loader: "url-loader",
options: {
outputPath: "images/", // 图片输出的路径
limit: 10 * 1024
}
}
},
{
test: /\.(eot|woff2?|ttf|svg)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name]-[hash:5].min.[ext]',
limit: 5000, // 使用base64进行转换, 大小限制小于5KB, 不然使用svg输出
publicPath: 'fonts/',
outputPath: 'fonts/'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title:'webpack配置', // 项目标题
filename: "index.html", // 最终建立的文件名
template: path.resolve(__dirname, '..', "src/index.html"), // 指定模板路径
minify: {
collapseWhitespace: true // 去除空白
}
}),
// 单独生成css文件和js文件分离开来 加快页面渲染
new MiniCssExtractPlugin({
filename: "[name]-[hash:5].css",
chunkFilename: "[id]-[hash:5].css"
}),
// happypack
new HappyPack({
//用id来标识 happypack处理那里类文件
id: 'happyBabel',
//如何处理 用法和loader 的配置同样
loaders: [{
loader: 'babel-loader?cacheDirectory=true',
}],
//共享进程池threadPool: HappyThreadPool 表明共享进程池,即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多。
threadPool: happyThreadPool,
//容许 HappyPack 输出日志
verbose: true,
}),
],
performance: false // 关闭性能提示
};
复制代码
以上配置,可能你见过,可是不必定知道有些东西用来干吗的,作个解释:github
yarn add html-webpack-plugin
复制代码
new HtmlWebpackPlugin({
title:'webpack配置', // 网站标题
filename: "index.html", // 最终建立的文件名
template: path.resolve(__dirname, '..', "src/index.html"), // 指定模板路径
minify: {
collapseWhitespace: true // 去除空白
}
}),
复制代码
使用 HtmlWebpackPlugin插件,来生成 html, 并将每次打包的js自动插入到你的 index.html 里面去,并且它还能够基于你的某个 html 模板来建立最终的 index.html,也就是说能够指定模板
yarn add mini-css-extract-plugin
复制代码
若是不作该配置,咱们的css是直接打包进js里面的,咱们但愿能单独生成css文件。 由于单独生成css,css能够和js并行下载,提升页面加载效率。 特别说明:目前 extract-text-webpack-plugin 最新版本不支持 Webpack 4.3.0 版本. Webpack 4.2.0 一下可用。 目前从 extract-text-webpack-plugin issues 了解, 将来 extract-text-webpack-plugin 将废弃,做者建议使用 mini-css-extract-plugin
{
test: /\.(le|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader", // 编译css
"postcss-loader", // 使用 postcss 为 css 加上浏览器前缀
"less-loader", // 编译less
]
},
复制代码
new MiniCssExtractPlugin({
filename: "[name]-[contenthash:5].css",
chunkFilename: "[id]-[contenthash:5].css"
}),
复制代码
hash: 若是都使用hash的话,由于这是工程级别的,即每次修改任何一个文件,全部文件名的hash至都将改变。因此一旦修改了任何一个文件,整个项目的文件缓存都将失效。
chunkhash: 根据不一样的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着咱们采用chunkhash的方式生成哈希值,那么只要咱们不改动公共库的代码,就能够保证其哈希值不会受影响,可是同一个模块,就算将js和css分离,其哈希值也是相同的,修改一处,js和css哈希值都会变,同hash,没有作到缓存意义。
contenthash:表示由文件内容产生的hash值,内容不一样产生的contenthash值也不同。在项目中,一般作法是把项目中css都抽离出对应的css文件来加以引用。(只要文件内容不同,产生的哈希值就不同)
因此css文件最好使用contenthash。
yarn add happypack
复制代码
运行在 Node.之上的Webpack是单线程模型的,也就是说Webpack须要一个一个地处理任务,不能同时处理多个任务。 Happy Pack 就能让Webpack作到这一点,它将任务分解给多个子进程去并发执行,子进程处理完后再将结果发送给主进程。
plugins:[
new HappyPack({
//用id来标识 happypack处理那里类文件
id: 'happyBabel',
//如何处理 用法和loader 的配置同样
loaders: [{
loader: 'babel-loader?cacheDirectory=true',
}],
//共享进程池threadPool: HappyThreadPool 表明共享进程池,即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多。
threadPool: happyThreadPool,
//容许 HappyPack 输出日志
verbose: true,
})
]
复制代码
以上是对webpack.base.config.js基础配置的一个解释说明,接下来我们再看看再开发环境:webpack.dev.config.js
const path = require("path");
const merge = require('webpack-merge')
const commonConfig = require('./webpack.base.config.js')
const webpack = require("webpack");
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
module.exports = merge(commonConfig, {
mode: "development", //webpack 4.0 要申明这个
devtool: 'cheap-module-eval-soure-map',
output: {
// 输出目录
path: path.resolve(__dirname, "../dist"),
// 文件名称
filename: "bundle.js",
chunkFilename: '[name].js'
},
plugins: [
//开启HMR(热替换功能,替换更新部分,不重载页面!) 至关于在命令行加 --hot
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'ENV': JSON.stringify("development"),
'process.env': {
VUEP_BASE_URL: '/'
}
}),
//识别某些类别的webpack错误
new FriendlyErrorsPlugin({
// 运行成功
compilationSuccessInfo: {
message: ['你的应用程序在这里运行http://localhost:8001'],
notes: ['有些附加说明要在成功编辑时显示']
},
// 运行错误
onErrors: function(severity, errors){
//您能够收听插件转换和优先级的错误,严重性能够是'错误'或'警告'
},
//是否每次编译之间清除控制台,默认为true
clearConsole: true,
//添加格式化程序和变换器(见下文)
additionalFormatters: [],
additionalTransformers: []
})
],
devServer: {
contentBase: path.resolve(__dirname, "../dist"), // 指定访问资源目录
historyApiFallback: true, // 该选项的做用全部的404都链接到index.html
disableHostCheck: true, // 绕过主机检查
inline: true, // 改动后是否自动刷新
host: 'localhost', // 访问地址
port: 8001, // 访问端口
overlay: true, // 出现编译器错误或警告时在浏览器中显示全屏覆盖
stats: "errors-only", // 显示捆绑软件中的错误
compress: true, // 对全部服务启用gzip压缩
open: true, // 自动打开浏览器
progress: true, // 显示编译进度
proxy: {
// 代理到后端的服务地址
"/api": "http://localhost:8000"
}
}
});
复制代码
devServer: {
contentBase: path.resolve(__dirname, "../dist"), // 指定访问资源目录
historyApiFallback: true, // 该选项的做用全部的404都链接到index.html
disableHostCheck: true, // 绕过主机检查
inline: true, // 改动后是否自动刷新
host: 'localhost', // 访问地址
port: 8001, // 访问端口
overlay: true, // 出现编译器错误或警告时在浏览器中显示全屏覆盖
stats: "errors-only", // 显示捆绑软件中的错误
compress: true, // 对全部服务启用gzip压缩
open: true, // 自动打开浏览器
progress: true, // 显示编译进度
proxy: {
// 代理到后端的服务地址
"/api": "http://localhost:8000"
}
}
复制代码
webpack4已经集成了open-browser-webpack-plugin 和 progress-bar-webpack-plugin这两个插件,因此不须要再单独引入,直接将 devServer.open 和devServer.progress设置为true便可。
mode: "development/production", webpack 4.0+ 要申明这个
output: {
// 输出目录
path: path.resolve(__dirname, "../dist"),
// 文件名称
filename: "bundle.js",
chunkFilename: '[name].js'
},
复制代码
yarn add friendly-errors-webpack-plugin
复制代码
Friendly-errors-webpack-plugin识别某些类别的webpack错误,并清理,聚合和优先级,以提供更好的开发人员体验。
//识别某些类别的webpack错误
new FriendlyErrorsPlugin({
// 运行成功
compilationSuccessInfo: {
message: ['你的应用程序在这里运行http://localhost:8001'],
notes: ['有些附加说明要在成功编辑时显示']
},
// 运行错误
onErrors: function(severity, errors){
//您能够收听插件转换和优先级的错误, 严重性能够是'错误'或'警告'
},
//是否每次编译之间清除控制台,默认为true
clearConsole: true,
//添加格式化程序和变换器(见下文)
additionalFormatters: [],
additionalTransformers: []
})
复制代码
最后,我们看看再实际生产环境中,webpack.dev.config.js的配置:
const path = require("path");
const webpack = require("webpack");
const merge = require('webpack-merge')
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const commonConfig = require('./webpack.base.config.js')
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
const WorkboxPlugin = require('workbox-webpack-plugin') // 引入 PWA 插件
module.exports = merge(commonConfig, {
mode: "production", //webpack 4.0 要申明这个
output: {
// 输出目录
path: path.resolve(__dirname, "../dist"),
// 文件名称
filename: '[name].[contenthash:5].js',
chunkFilename: '[name].[contenthash:5].js'
},
devtool: 'cheap-module-source-map',
optimization: {
usedExports: true, //js Tree Shaking 清除到代码中无用的js代码,只支持import方式引入,不支持commonjs的方式引入
splitChunks: {
chunks: "all", // 全部的 chunks 代码公共的部分分离出来成为一个单独的文件
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors'
}
}
},
},
plugins: [
new CleanWebpackPlugin(),// 打包时删除以前的文件
// 清除无用 css---生产环境---csstree-shaking
new PurifyCSS({
paths: glob.sync([
// 清除无用 css---生产环境-- 对于 css的tree shaking 使用 purifycss-webpack 配合 glob-all来实现 。
path.resolve(__dirname, '..', 'src/*.html'),
path.resolve(__dirname, '..', 'src/*.js'),
path.resolve(__dirname, '..', 'src/**/*.jsx'),
])
}),
// PWA配置,生产环境才须要,PWA优化策略,在你第一次访问一个网站的时候,若是成功,作一个缓存,当服务器挂了以后,你依然可以访问这个网页 ,这就是PWA。那相信你也已经知道了,这个只须要在生产环境,才须要作PWA的处理,以防不测。
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
}),
]
});
复制代码
你执行yarn run build的时候,每次都会在dist目录下边生成一堆文件,可是上一次的打包的文件还在,这个咱们须要每次打包时清除 dist 目录下旧版本文件
yarn add clean-webpack-plugin
复制代码
注意: 这个引入的坑,最新版的须要这样引入,而不是直接
// 报错:const CleanWebpackPlugin = require('clean-webpack-plugin')
复制代码
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin()
]
复制代码
webpack中devtool选项用来控制是否生成,以及如何生成 source map。简言之,source map就是帮助咱们定位到错误信息位置的文件。正确的配置source map,可以提升开发效率,更快的定位到错误位置。
devtool:"cheap-module-eval-source-map",// 开发环境配置
devtool:"cheap-module-source-map", // 线上生成配置
复制代码
yarn add glob-all purify-css purifycss-webpack
复制代码
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
plugins:[
// 清除无用 css
new PurifyCSS({
paths: glob.sync([
// 要作 CSS Tree Shaking 的路径文件
path.resolve(__dirname, './src/*.html'), // 请注意,咱们一样须要对 html 文件进行 tree shaking
path.resolve(__dirname, './src/*.js')
])
})
]
复制代码
在webpack4中已经移除了UglifyJsPlugin,只须要配置mode为"production",便可显式激活 UglifyjsWebpackPlugin 插件。
清除到代码中无用的js代码,只支持import方式引入,不支持commonjs的方式引入
只要mode是production就会生效,develpoment的tree shaking是不生效的,由于webpack为了方便你的调试
optimization: {
usedExports:true,
}
复制代码
yarn add workbox-webpack-plugin
复制代码
PWA的做用就是当你第一次成功访问网站是,作一个缓存,那么在服务器挂了的状况下,你依然能够访问这个网页,这个只须要在生产环境下处理,以防不测。
const WorkboxPlugin = require('workbox-webpack-plugin')
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
}),
复制代码
以上是关于本人在webpack在开发环境和生产环境的一些配置和优化,欢迎你们继续补充和指正,你们对webpack有兴趣的也能够先去官网了解下webpack的一些知识点。
因为篇幅太长,本文分为两部分来写,这一篇主要讲解下webpack方面的配置。
下一篇 将写一些关于基于react-router4.0实现路由的按需加载。