webpack是实现工程化,模块化,自动化的一个静态编译工具。当前项目开发过程当中脚手架也是非常方便,比如vue的脚手架,react的脚手架。只要命令行自动帮我们搭建项目环境。实则这些脚手架的基础就是webpack,所以了解和学会运用webpack搭建项目环境是前端开发工程师非常必要的一环。
运用webpack4搭建项目环境,我搭建过2次。这两次不同的地方在于,项目的目录结构是不一样的,那么配置过程中路径写法也是不一样的。实际在我搭建的过程中,遇到的坑是真的多,不过坑踩多了,路就更顺咯。
先来了解一下什么是魔术变量—__dirname。这个概念非常重要,因为在配置过程中路径会让你搞得特别晕,我所理解的是指当前文件所在位置。即用于指向当前执行脚本所在的目录路径。
直接附上代码和目录结构吧~~
1、目录结构一
config文件夹是存放webpack配置的目录,public是静态html,static是一些静态的东西会原封不动的打包到dist目录。
package.json的代码
{
"name": "web",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "cross-env NODE_ENV=production webpack --config config/webpack.pro.js",
"dev": "webpack-dev-server --config config/webpack.dev.js "
},
"author": "",
"license": "ISC",
"dependencies": {
"babel-core": "^6.26.3",
"cross-env": "^6.0.3",
"file-loader": "^4.2.0",
"html-webpack-plugin": "^3.2.0",
"path": "^0.12.7",
"resolve-url-loader": "^3.1.0",
"vue": "^2.6.10",
"vue-loader": "^15.7.1",
"vue-template-compiler": "^2.6.10",
"webpack-cli": "^3.3.9"
},
"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/preset-env": "^7.6.3",
"babel-loader": "^8.0.4",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.0.4",
"css-loader": "^3.2.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"less-loader": "^5.0.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"url-loader": "^2.2.0",
"webpack": "^4.41.2",
"webpack-dev-server": "^3.8.2",
"webpack-merge": "^4.2.2"
}
}
基本配置
const path=require('path');
const pathRoot=path.resolve('./'); //根目录
var ExtractTextPlugin = require("extract-text-webpack-plugin"); //抽离公共的css代码
var HtmlWebpackPlugin=require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const webpack = require('webpack');
const env = process.env.NODE_ENV
module.exports={
entry:{
// index:path.resolve(pathRoot,'src/main.js'),
index:path.resolve(__dirname,'../src/main.js'),
venders:['vue']
},
output:{
filename:'js/[name]-[hash].js', //配置出口文件在哪里放置和文件命名
path:path.resolve(pathRoot,'dist'), //出口目录
},
module:{
rules:[
{
test:/\.less$/,
use:[
{
//对象写法
loader:'style-loader',
},
{
loader:'css-loader'
},
{
loader:'less-loader'
}
]
},
{
test:/\.scss$/,
use:['style-loader','css-loader','resolve-url-loader','sass-loader'] //数组写法
},
{
test:/\.css$/,
use: ExtractTextPlugin.extract({
// fallback:'style-loader', //额外的css要链接到html中
use:[ 'css-loader' ]
})
},
{
test:/\.(png|jpg|gif)$/,
// loader:'url-loader?limit=10&name=img/[hash:8].[name].[ext]'
use:[
{
loader:'url-loader', //将图片转成base64
options:{
limit:10,
fallback:'file-loader', //当文件或图片超过8192,用file-loader进行压缩
name:"imgs/[name].[ext]", //超过10字节将在dist目录生成img图片被file-loader压缩
// name: '[name].[hash:8].[ext]',
// publicPath: path.resolve(pathRoot,'assets/img'),
// outputPath:path.resolve(__dirname,'../imgs'),
}
}
]
},
{
test:/\.vue$/,
use:{
loader:'vue-loader'
}
},
{
test:/\.js/,
use:[{
loader:'babel-loader',
options:{
"presets": [
[
"@babel/preset-env",
]
]
}
}],
exclude: /(node_modules)/
}
]
},
resolve:{
//配置模块如何解析
extensions:['.vue','.js','.json','.css','.less','.scss'], //可省略扩展名
alias:{
//src下的文件夹可用@省略符号
'@':path.resolve(pathRoot,'src'),
}
},
// resolveLoader: { //解析webpack的loader包
// moduleExtensions: ["-loader"]
// },
plugins: [
new ExtractTextPlugin({
filename:'style/[name].css' //css抽离出来的文件名
}),
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/page.html'), //要打包的html模板
filename:'index.html', //打包之后生成的目录和文件名称
inject: true, //是否引用打包好的js
minify:{
//
crashWhitespace :true,//去除所有的空格
removeComments:true,//去除所有的注释
removeRedundantAttributes:false, //去除多余的属性
removeScriptTypeAttributes:false, //去除js标签
// removeStyleLinkTypeAttributes:true, //去除样式标签
// useShortDoctype: true //使用简短的文档
}
}),
new VueLoaderPlugin(),
// 按需加载,webpack4被弃用代码分离主要目的是防止代码重复,减少代码体积,达到加载速度快减少 服务器 压力和带宽等目的。
// new webpack.optimize.CommonsChunkPlugin({
// name: "venders",
// minChunks: function(module) {
// if (module.resource && (/^.*\.(css|scss)$/).test(module.resource)) {
// return false;
// }
// return module.context && module.context.includes("node_modules");
// }
// }),
// new webpack.optimize.CommonsChunkPlugin({
// name: "manifest" //But since there are no more common modules between them we end up with just the runtime code included in the manifest file
// }),
],
// watch:true //监听模式,当为true,打包成功还是还会更新文件
}
dev配置
const merge = require('webpack-merge');
const common=require('./webpack.config');
const path=require('path');
const pathRoot=path.resolve('./');
const webpack = require('webpack');
module.exports=merge(common,{
mode:'development',
plugins:[
new webpack.HotModuleReplacementPlugin(), //热模块更新
],
devServer:{
open:true,
port:3080,
hot:true,
inline:true,
// contentBase:path.join(__dirname, "../dist/"),
publicPath:'/'
},
devtool: 'inline-source-map',
})
pro配置
const merge = require('webpack-merge');
const common=require('./webpack.config');
const path=require('path');
const pathRoot=path.resolve('./');
const CopyPlugin = require('copy-webpack-plugin');
const {
CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports=merge(common,{
mode:'production',
devtool: 'source-map',
plugins:[
new CopyPlugin([
{
from:path.resolve(pathRoot,'static'), to: path.resolve(pathRoot,'dist/static') },
]),
new CleanWebpackPlugin({
verbose:true, //是否启用控制台输出信息
dry: false, //设置为false,启用删除文件
}), //清除dist,默认删除dist
],
// 推荐此方式抽离,实现按需加载
optimization: {
splitChunks: {
automaticNameDelimiter: '-',
cacheGroups: {
commons: {
name: "commons",
chunks: "initial",
minChunks: 2
},
venders:{
name:'venders',
test:/vue/,
chunks:'all'
}
}
},
minimize:false, //压缩代码,webpack自动压缩代码
},
})
{
"name": "npmcs_web",
"version": "1.0.0",
"description": "项目管理数据分析平台",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --port 8888 --open --config webpack_dev",
"build": "webpack --config webpack_pro.js"
},
"author": "hhl",
"license": "ISC",
"dependencies": {
"@babel/polyfill": "^7.7.0",
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
"path": "^0.12.7"
},
"devDependencies": {
"@babel/core": "^7.7.7",
"@babel/preset-env": "^7.7.7",
"babel-loader": "^8.0.4",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^5.0.2",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"style-loader": "^1.0.2",
"transfer-webpack-plugin": "^0.1.4",
"url-loader": "^3.0.0",
"vue": "^2.6.11",
"vue-loader": "^15.8.3",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.41.4",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1",
"webpack-merge": "^4.2.2"
}
}
基本配置
const path = require('path');
const pathBase = path.resolve('./'); //当前根目录
const VueLoaderPlugin=require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry:['@babel/polyfill',path.resolve(__dirname, './src/main.js')], //@babel/polyfill实现预编译
// entry:path.join(pathBase,'index.html'), //程序主入口,单页面字符串。多页面,可对象{key:value}
output: {
//浏览器出口,单文件可字符命名或占位符,多文件可使用占位符。
filename: 'js/[name].[hash].bundle.js',
path: path.resolve(__dirname, './dist')
},
module: {
rules: [
//{
//test: /\.less$/,
//use: [
// {
//loader: "style-loader" //将生成的