webpack是前端资源模块化管理和打包工具。它将很多单独,松散的模块按照一定的规则和依赖打包成符合生产环境部署的资源。它可以进行代码转换,文件优化,代码分割,模块合并,自动刷新,自动发布等多种功能。
webpack中主要用到CommonJS和ES6Module。CommonJS定义的是模块的同步加载,它一般应用于服务器的开发,像node.js,而ES6Module,它是js官方标准模块定义的方式。webpack在代码解析的时候支持两种规范的解析。二者的导入和导出方式如下:
/* ES6Module
* 导出: export,export default(默认)
* 导入: import
*/
//文件名:a.js
//代码如下:
function sum(a, b) {
return a + b
}
//使用export导出时要记得使用{}
export { sum };
//使用export default导出时,后面不用再加上{}
export default sum
//文件名:b.js
//使用import导入时要记得变量名要写在{}中,要和导出一致,from后接的是相对路径
import { sum } from './a';
//可以在b.js中调用sum方法
console.log(sum(1, 2));
//导入,对应exprot default.import后面不用跟{},可以随意取变量名
import A from './a';
console.log(sum(3, 2));
需要注意的是:模块的导入要放在代码的最前面,而且浏览器不能直接识别,需要先进行编译才可以。
/*
* CommonJS模块化
* 导出:module.exports = {属性名:属性值}
* 导入:let/const exports导出的对象名 = require('文件名')
*/
//a.js
function sum(x,y){
return x+y
}
module.exports = {
sum:sum,//简写sum
}
//b.js
// 导入:
let s = require('./a');
console.log(s);
webpack中一共有以下几个概念:
入口(entry):它指示webpack应该使用哪个模块来作为构建其内部依赖图的开始。默认为:./src也可以通过配置entry中的属性来指定入口起点(一个或者多个)。。
出口(output):它告诉webpack在哪时输出所创建的bundles,以及如名命名。默认为./dist。它也是可以配置多输口的。
loader:模块转换,把模块原内容按照需求转换成新内容,可以加载非js的模块。本质上webpack loader将所有类型的文件转换为应用程序的依赖图可以引用的模块
插件(plugins):扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建的结果。它们都是可以进行配置的。想要使用一个插件,只需要用到require()它,然后把它添加到plugins数组中。多数插件是可以通过option来自定义选项的。
模式(mode):通过development或production之中的任何一个来设置mode参数用以启用相应模式下webpack内置的优化。
一、webpack的安装与基本使用
目前我们一般采用的都是webpack4.0以上的版本,它支持零配置的使用。
安装webpack,为了防止全局安装webpack导致的版本冲突,所以以本地安装为主。
步骤一:新建一个文件夹,文件夹不能有特殊的字符。如:中文,特殊符号且名字不能是webpack否则会报错
步骤二: 生成package.json文件。命令:npm init -y
命令:npm i(install) webpack webpack-cli --save -dev / npm i webpack webpack-cli -g
步骤三:零配置使用。新建一个src文件夹,默认会打包src(当前项目开发的源代码)目录中的文件(入口默认为index.js),打包完成后的目录是dist/main.js编译后的文件。
1、新建src目录且在src下分别创建a.js和index.js文件,代码如下:
/* src/a.js */
function sum(x,y){
return x+y;
}
export {sum};
/* src/index.js */
import { sum } from './a.js'
console.log(sum(1, 4));
2、进行打包处理,由于npm没有安装在全局不能使用命令,所以有两种方法。一种是使用:npx webpack。第二种是修改package.json中的script配置。就可以用npm run server 命令了。如下:
{
"scripts":{
"serve":"webpack"
}
}
3、我们可以看到项目的目录如下:
4、如果不能使用零配置,可以自己来进行配置,自定义基础配置。
4.1 新建一个webpack.config.js或者是webpackfile.js的文件。由于webpack分为开发环境和生产环境,所以我们可以创建三个不同的config.js进行不同的配置。
webpack.config.js中可以进行entry,output,mode等基础的配置。需要注意一点的是在output中编译后的文件名要加上hash值,用以保证每次生成的文件是不一样的,好清除强缓存。且输出的目录是一个绝对的路径,所以要用到node.js 中的path。
const path = require('path');
module.exports = {
//设置编译的模式:development production(默认的)
mode:'production',
//设置编译的入口文件(真实项目中一般开发的代码都要放在src下)
entry:'./src/main.js',
//设置编译的出口文件
output:{
filename:'bundle.[hash:10].main.js',
path:path.resolve(__dirname,'build')
}
}
4.2 可以还新建两个不同的文件webpack.config.dev.js 和webpack.config.pro.js用来区分不同的环境所需要的不同内容。在package.confg.js中写两个环境都需要的内容。在package.json中进行配置。
"scripts": {
"serve":"webpack --config webpack.config.dev.js",
"build":"webpack --config webpack.config.pro.js"
},
4.3 进行基本的自动化配置。
利用html-webpack-plugin。这样每一次代码更改,复新编译过后,就不需要手动的去更改html中导入的js信息。命令:npm i html-webpack-plugin --save-dev进行下载;
利用clean-webpack-plugin在每一次打包的时候都把之前打包的内容进行清空,即build下只保留最新的打包文件。命令:npm i clean-webpack-plugin --save-dev进行下载;
利用webpack-dev-server可以创建一个web服务来自动监听代码的改变,自动编译,自动打开浏览器渲染页面。命令:npm i webpack-dev-server --save-dev进行下载。
在配置html-webpack-plugin插件的时候需要注意的是路径的正确性,还有hash值是可以用来清除强缓存的,与output设置hash值是一样的,它也是可以进行配置。还可以把模板中的html代码进行压缩处理。
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [
//配置指定的html模板
new HtmlWebpackPlugin({
//路径
template: './public/index.html',
//文件名
filename: 'index.html',
//设置hash值
hash: true,
//把模板中的html代码进行压缩
minify: {
collapseWhitespace: true,
removeComments: true,
removeAttributeQuotes: true,
removeEmptyAttributes: true
}
}),
//每一次打包把之前的都清空
new CleanWebpackPlugin()
]
}
在配置dev-server时编译好的结果是放在计算机内存中并不会像之前那样编译出现在build下。需要注意的一点是要把webpack.json中script中的配置进行修改。
// webpack.config.js
module.exports = {
devServer:{
port:'8080',
compress:true,
contentBase:path.resolve(__dirname,"build"),
open:true,
hot:true
}
}
// package.json
"scripts": {
"serve": "webpack-dev-server --open Chrome",
"build": "webpack"
}
4.4 当我们有多入口,需要有多输口进行输出这样的需求时,这时,我们就要进行一系列的设置,配置多页面模板。
const path = require('path');
//配置多页面模板
const htmlPlugins = ['index','login'].map(item=>{
return new HtmlWebpackPlugin({
template: `./public/${item}.html`,
filename: `${item}.html`,
chunks:[item],//指定当前页面中的依赖项
hash: true,
minify: {
collapseWhitespace: true,
removeComments: true,
removeAttributeQuotes: true,
removeEmptyAttributes: true
}
});
});
module.exports = {
//多入口 => key:value
entry: {
index:'./src/main.js',
login:'./src/login.js'
},
output: {
//[name]多入口中配置的属性名 index/login
filename: '[name].[hash:10].min.js',
//输出的目录(需要的是绝对路径)
path: path.resolve(__dirname, 'build')
},
plugins: [
//配置指定的html页面模板
...htmlPlugins,
]
}
5. webpack中的loader加载器。它可以用来处理css,less,图片,字体等等资源。
5.1 处理css和less文件。使用之前必须先进行下载安装要用到style-loader、css-loader、less-style-loader,autoprefixer postcss-loader。命令:npm install css-loader style-loader less-loader autoprefixer postcss-loader --save -dev。
style-loader把处理好的css以内嵌的方式插入到页面中;css-loader用来处理import这类语法,postcss-loader处理兼容性问题它需要和autoprefixer一起使用,在配置时,它还需要新建一个postcss.config.js的文件来添加配置,当然还需要在package.json中进配置。
由于使用style-loader后css的引入是内嵌式的,这时就不是很方便,想要更好处理html中引入的css文件,还需要用到mini-css-extract-plugin来抽离css的内容。需要用到命令npm i mini-css-extract-plugin --save-dev进行下载和安装。
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
//webpack.config.js中
module.exports = {
plugins: [
//=>使用插件
new MiniCssExtractPlugin({
//=>设置编译后的文件名字
filename: 'main.[hash:10].css'
})
],
// 配置loader
module: {
rules: [
{
//基于正则来查看匹配哪些文件
test: /\.(css|less)$/i,
use: [
// "style-loader",
// 如果要把css分离出来style-loader了,而写下面这一个loader 它替代了style方式
MiniCssExtractPlugin,
"css-loader",
"postcss-loader",
"less-loader",
]
}
]
}
}
//package.json中浏览器的兼容配置
"browserslist": [
">0.1%",
"last 2 versiions"
]
//新生成的postcss.config.js中
module.exports = {
plugins: [
require('autoprefixer')
]
}
还可以使用optimize,uglifyjs或者是terser插件来优化压缩css和js文件,下面采用的是terser
命令:npm install optimize-css-assets-webpack-plugin terser-webpack-plugin --save -dev
配置:
const TerserWebpackPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
//配置webpack优化项
optimization:{
//设置压缩方式
minimizer:[
new OptimizeCssAssetsWebpackPlugin(),
new TerserWebpackPlugin()
]
}
}
5.2 处理图片,字体文件
在真实的项目中图片应用的位置在三处:css中的背景图,js中动态创建图片,html中直接写入图片
命令:npm install file-loader url-loader html-withimg-loader --save-dev
module.exports = {
module: {
//=>模块规则:使用加载器(默认从右向左执行)
rules: [{
test: /\.(png|jpe?g|gif)$/i,
use: [{
//=>把指定大小内的图片BASE64
//=>不在指定范围的采用file-loader进行处理
loader: 'url-loader',
options: {
limit: 200 * 1024,
outputPath:'/images',
//name:'[name].[ext]'
}
}],
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}, {
test:/\.(svg|eot|ttf|woff|woff2)$/i,
use:"file-loader"
}, {
test: /\.html$/,
use: ['html-withimg-loader']
}]
}
}
5.3 基于babel实现es6的转换和esList语法检测
安装包依赖
命令:$ npm install babel-loader @babel/core @babel/preset-env –save-dev
对于现在的开发来说,还有更高级的js语法如es7,8需要进行转换,所以还需要另外包的依赖和插件配置。一般要用到以下插件,先进行下载
命令:$ npm install @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-transform-runtime –save-dev
在真实的目中我们还会安装@babel/polyfill来进行兼容的处理。它和其它的webpack加载器插件不一样,其它是编译时进行处理,而polyfill是运行时处理。 在代码运行时,把一些es7等特殊语法进行兼容处理,它需要安装在生产环境下。也需要@babel/plugin-transform-runtime插件的支持。在真实的项目中我们还会安装一个ESLint 来进行词法解析。
命令:npm i @babel/runtime @babel/polyfill npm i eslint eslint-loader -save-dev
综上配置如下:
module.exports = {
module: {
rules: [{
test: /\.js$/,
use: [{
loader: 'babel-loader',
options: {
//=>转换的语法预设(ES6->ES5)
presets: [
"@babel/preset-env"
],
//=>基于插件处理ES6/ES7中CLASS的特殊语法
plugins: [
["@babel/plugin-proposal-decorators", {
"legacy": true
}],
["@babel/plugin-proposal-class-properties", {
"loose": true
}],
"@babel/plugin-transform-runtime"
]
}
}], //=>, "eslint-loader"
//=>设置编译时忽略的文件和指定编译目录
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}]
}
}