本文内容强烈建议自己手动敲一遍,建立自己的基础模板,很重要。
不扯webpack是干嘛的了,不懂的小伙伴自行百度,也可看下面实践案例。本文内容主要转载于下文,是学习用的,直接上干货。
爱前端不爱恋爱:2020年了,再不会webpack敲得代码就不香了(近万字实战)zhuanlan.zhihu.com基础部分
1.1 新建一个项目
npm init
安装依赖
npm i -D webpack webpack-cli
- npm i -D 为npm install --save-dev的缩写
- npm i -S 为npm install --save的缩写
然后我们新建一个文件夹和文件src/main.js,测试一下
main.js 内容
console.log('测试webpack')
webpack 配置内容
"scripts": {
"build": "webpack src/main.js"
},
执行命令测试一下
npm run build
此时如果生成了一个dist文件夹, "scripts": {
"build": "webpack --config build/webpack.config.js"
},并且内部含有main.js说明已经打包成功了
1.2 自定义配置
上一个简单的例子只是webpack自己默认的配置,下面我们要实现更加丰富的自定义配置
新建一个build
文件夹,里面新建一个webpack.config.js
// webpack.config.js
const path = require('path');
module.exports = {
mode:'development', // 开发模式
entry: path.resolve(__dirname,'../src/main.js'), // 入口文件
output: {
filename: 'output.js', // 打包后的文件名称
path: path.resolve(__dirname,'../dist') // 打包后的目录
}
}
更改我们的打包命令
"scripts": {
"build": "webpack --config build/webpack.config.js"
}
执行 npm run build ,发现dist目录下又新生成了一个文件output.js, 这个js就是我们在浏览器下真正运行的文件。 ps: main.js是第一步生成的,不要误解。
1.3 配置html模板
我们要考虑一个问题,js文件打包好了,但是我们不可能每次在html
文件中手动引入打包好的js
(html模板建立在后面)
这里可能有的朋友会认为我们打包js文件名称不是一直是固定的嘛(output.js
)?这样每次就不用改动引入文件名称了呀?实际上我们日常开发中往往会这样配置:
module.exports = {
// 省略其他配置
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname,'../dist') // 打包后的目录
}
}
执行npm run build
会发现生成了一个main.9f1e44ba的文件,这个就是我们新的打包方式生成的js文件了。
为了缓存,你会发现打包好的js文件的名称每次都不一样。webpack打包出来的js文件我们需要引入到html中,但是每次我们都手动修改js文件名显得很麻烦,因此我们需要一个插件来帮我们完成这件事情
npm i -D html-webpack-plugin
好了,我们开始新建一个public目录(为了以后放一些其他的资源)和html文件
webpack配置如下
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode:'development', // 开发模式
entry: path.resolve(__dirname,'../src/main.js'), // 入口文件
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname,'../dist') // 打包后的目录
},
plugins:[
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html')
})
]
}
在dist文件夹下生成了一个html文件和一个js
可以发现打包生成的js文件已经被自动引入html文件中
补充1 多入口文件如何进行开发
生成多个
html-webpack-plugin
实例来解决这个问题,这里我不在演示。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode:'development', // 开发模式
entry: {
main:path.resolve(__dirname,'../src/main.js'),
header:path.resolve(__dirname,'../src/header.js')
},
output: {
filename: '[name].[hash:8].js', // 打包后的文件名称
path: path.resolve(__dirname,'../dist') // 打包后的目录
},
plugins:[
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html'),
filename:'index.html',
chunks:['main'] // 与入口文件对应的模块名
}),
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'../public/header.html'),
filename:'header.html',
chunks:['header'] // 与入口文件对应的模块名
}),
]
}
补充2 清理我们的dist目录
每次执行npm run build 会发现dist文件夹里会残留上次打包的文件,如下图所示,这里推荐一个plugin来帮我们在打包输出前清空文件夹
clean-webpack-plugin
npm i -D clean-webpack-plugin //安装插件
使用clean-webpack-plugin插件
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
// ...省略其他配置
plugins:[new CleanWebpackPlugin()]
}
这样每次打包我们的dist目录就很干净了,不会遗留之前打包的文件了
1.4 引入css
我们的入口文件是js,所以我们在入口js中引入我们的css文件
// index.css
body{
background: red;
}
// index.less
body{
color: yellow;
}
同时我们也需要一些loader来解析我们的css文件
npm i -D style-loader css-loader
如果我们使用less来构建样式,则需要多安装两个
npm i -D less less-loader
配置文件如下
// webpack.config.js
module.exports = {
// ...省略其他配置
module:{
rules:[
{
test:/.css$/,
use:['style-loader','css-loader'] // 从右向左解析原则
},
{
test:/.less$/,
use:['style-loader','css-loader','less-loader'] // 从右向左解析原则
}
]
}
}
浏览器打开index.html如下
我们的css已经生效了
1.4.1 为css添加浏览器前缀
npm i -D postcss-loader autoprefixer
配置如下
// webpack.config.js
module.exports = {
module:{
rules:[
test/.less$/,
use:['style-loader','css-loader','postcss-loader','less-loader'] // 从右向左解析原则
]
}
}
接下来,我们还需要引入autoprefixer
使其生效,这里有两种方式,这里我采用了第二种方式。
1 在项目根目录下创建一个postcss.config.js
文件,配置如下:
module.exports = {
plugins: [require('autoprefixer')] // 引用该插件即可了
}
2 直接在webpack.config.js
里配置
// webpack.config.js
module.exports = {
//...省略其他配置
module:{
rules:[{
test:/.less$/,
use:['style-loader','css-loader',{
loader:'postcss-loader',
options:{
plugins:[require('autoprefixer')]
}
},'less-loader'] // 从右向左解析原则
}]
}
}
1.4.2 拆分css
这时候我们发现css通过style标签的方式添加到了html文件中,但是如果样式文件很多,全部添加到html中,难免显得混乱。这时候我们想用把css拆分出来用外链的形式引入css文件怎么做呢?这时候我们就需要借助插件来帮助我们
npm i -D mini-css-extract-plugin
webpack 4.0以前,我们通过extract-text-webpack-plugin
插件,把css样式从js文件中提取到单独的css文件中。webpack4.0以后,官方推荐使用mini-css-extract-plugin
插件来打包css文件
配置文件如下:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
//...省略其他配置
module:{
rules:[
{
test:/.css$/,
use:['style-loader',MiniCssExtractPlugin.loader,'css-loader'] // 从右向左解析原则
},
{
test:/.less$/,
use:['style-loader',MiniCssExtractPlugin.loader,'css-loader',{
loader:'postcss-loader',
options:{
plugins:[require('autoprefixer')]
}
},'less-loader'] // 从右向左解析原则
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].[hash].css",
chunkFilename: "[id].css",
})
]
}
打包后目录如下所示
css文件如图
可见,我们的拆分css文件成功。
补充 拆分多个css
这里需要说的细一点,上面我们所用到的mini-css-extract-plugin
会将所有的css样式合并为一个css文件。如果你想拆分为一一对应的多个css文件,我们需要使用到extract-text-webpack-plugin
,而目前mini-css-extract-plugin
还不支持此功能。我们需要安装@next版本的extract-text-webpack-plugin
npm i -D extract-text-webpack-plugin@next
这里不再演示
// webpack.config.js
const path = require('path');
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
let indexLess = new ExtractTextWebpackPlugin('index.less');
let indexCss = new ExtractTextWebpackPlugin('index.css');
module.exports = {
module:{
rules:[
{
test:/.css$/,
use: indexCss.extract({
use: ['css-loader']
})
},
{
test:/.less$/,
use: indexLess.extract({
use: ['css-loader','less-loader']
})
}
]
},
plugins:[
indexLess,
indexCss
]
}
1.5 打包 图片、字体、媒体、等文件
file-loader
就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件url),并将文件移动到输出的目录中url-loader
一般与file-loader
搭配使用,功能与 file-loader 类似,如果文件小于限制的大小。则会返回 base64 编码,否则使用 file-loader 将文件移动到输出的目录中
npm i -D file-loader url-loader
配置如下
// webpack.config.js
module.exports = {
// 省略其它配置 ...
module: {
rules: [
// ...
{
test: /.(jpe?g|png|gif)$/i, //图片文件
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, //媒体文件
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /.(woff2?|eot|ttf|otf)(?.*)?$/i, // 字体
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
}
}
]
},
]
}
}
index.less文件修改
body{
color: yellow;
background: url("./images/webpack.jpeg")
}
当图片只有8kb时候,打包后Js如下
当图片超过10KB,40KB的时候
会是这样一个效果,在dist目录下生成了一个img文件。
1.6 用babel转义js文件
为了使我们的js代码兼容更多的环境我们需要安装依赖
npm i babel-loader @babel/preset-env @babel/core
- 注意
babel-loader
与babel-core
的版本对应关系
babel-loader
8.x 对应babel-core
7.xbabel-loader
7.x 对应babel-core
6.x
配置如下
// webpack.config.js
module.exports = {
// 省略其它配置 ...
module:{
rules:[
{
test:/.js$/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
}
},
exclude:/node_modules/
},
]
}
}
上面的babel-loader
只会将 ES6/7/8语法转换为ES5语法,但是对新api并不会转换 例如(promise、Generator、Set、Maps、Proxy等)
此时我们需要借助babel-polyfill来帮助我们转换(这个问题以前面试的时候遇到过,囧,当时没回答上来)
npm i @babel/polyfill
注意,这里我把main.js改为了index.js
// webpack.config.js
const path = require('path')
module.exports = {
entry: ["@babel/polyfill,path.resolve(__dirname,'../src/index.js')"], // 入口文件
}
好了,第一部分基础部分就到这里了,自己跟着敲打一遍还是有很多感悟和疑问的,实践完成之后我会总结一下。
第二部分有所不同,我想实现一个react的环境开发,而原文章中主要是vue的环境开发,我查阅一番资料,实现了在来补充。
下面是第二篇文章,搭建一个基础的react环境
林海:webpack实战第二篇(搭建react开发环境)zhuanlan.zhihu.com和第三篇文章
林海:webpack实战第三篇(优化webpack)zhuanlan.zhihu.com最后,原文连接在此,
https://segmentfault.com/a/1190000021395777segmentfault.com