Webpack

webpack介绍

webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

webpack中文网站

使用webpack前需要先安装 node.js

webpack 模块

在模块化编程中,开发者将程序分解成离散功能块,并称之为模块

webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:

  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD definerequire 语句
  • css/sass/less 文件中的 @import 语句
  • 样式(url(...))或 HTML 文件(<img src=...>)中的图片链接(image url)

webpack 通过 loader 可以支持各种语言和预处理器编写模块。loader 描述了 webpack 如何处理 非 JavaScript(non-JavaScript) 模块,并且在 bundle 中引入这些 依赖。 webpack 社区已经为各种流行语言和语言处理器构建了 loader

核心概念

  • 入口(entry)

    • 入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
  • 输出(output)

    • output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist
  • loader

    • loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
  • 插件(plugins)

    • 插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

webpack4.0使用示例

功能

  1. 修改内容浏览器自动刷新
  2. es6转es5
  3. 支持 less,scss 扩展语言
  4. less,suss,css 抽取为独立文件
  5. js,css压缩
  6. 可生成source-map文件
  7. 目录拷贝
  8. 引入第三方库 jquery

CLI

  1. 启动:npm start
  2. 打包:npm run build

目录结构

  • img 存储项目要中使用的图片
  • src 工作目录
    • index
      • index.html
      • index.js
      • index.css
      • index.less
      • index.scss
  • package.json
  • webpack.common.js 主配置文件
  • webpack.dev.js 开发环境配置文件
  • webpack.prod.js 生产环境配置文件

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
"name": "mybook",
"version": "1.0.0",
"description": "",
"private": true,
"dependencies": {
"@babel/runtime": "^7.0.0-beta.51",
"jquery": "^3.3.1"
},
"devDependencies": {
"@babel/core": "^7.0.0-beta.51",
"@babel/plugin-transform-runtime": "^7.0.0-beta.51",
"@babel/preset-env": "^7.0.0-beta.51",
"babel-loader": "^8.0.0-beta.0",
"babel-preset-env": "^1.7.0",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.11",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"less": "^3.9.0",
"less-loader": "^5.0.0",
"node-sass": "^4.12.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^2.1.2",
"webpack": "^4.35.2",
"webpack-cli": "^3.0.6",
"webpack-dev-server": "^3.1.4",
"webpack-merge": "^4.1.3"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.prod.js",
"start": "webpack-dev-server --open --config webpack.dev.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}

webpack.common.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//webpack.common.js 通用配置
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //生成html
const ExtractTextPlugin = require("extract-text-webpack-plugin"); //将css作为单独文件
const CopyWebpackPlugin = require('copy-webpack-plugin'); //复制文件

module.exports = {
// 入口
entry: {
index: './src/index/index.js',
},
// 出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'src/[name].[chunkhash].js',
publicPath:'./'
},
// loader 配置
module: {
rules: [
{ // 配置 babel-loader
test: /\.js$/,
exclude: /(node_modules|bower_components)/, //此模块中的js不转码
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
cacheDirectory: true, //缓存转换结果以提高执行效率
plugins: ["@babel/plugin-transform-runtime"]
}
}
},
{ //配置 css-loader
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
minimize: true, //压缩css
}
}]
})
},
{ //配置 less-loader
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
sourceMap: true,
minimize: true, //压缩css
}
}, {
loader: "less-loader"
}],
})
},

{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader", options: {
sourceMap: true,
minimize: true, //压缩css
}
}, {
loader: "sass-loader", options: {
sourceMap: true
}
}]
})
},
]
},
// 插件配置
plugins: [
// 自动生成HTML文件,可以生成多个
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index/index.html', //指定html模板
chunks: ['index'], //指定要包含那些打包后的js
hash: true,
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
}),

// 将js引入的css分离出然后注入到html页面中
new ExtractTextPlugin("css/[name].[chunkhash].css"),

// 拷贝插件,可以设置多个参数
new CopyWebpackPlugin([
// 复制图片目录
{
from: './img',
to: 'img'
},
// 拷贝字体图片库
// {from: './iconfont',to: 'iconfont'}
]),

],

};

webpack.dev.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
// devtool: 'inline-source-map',
devServer: {
stats: "errors-only", //信息输出,只有出错时输出
contentBase: './dist',
// compress: true, // 一切服务都启用gzip 压缩
port: 8090, //端口号
// host: "192.168.1.6", //设置IP地址
publicPath:'/'
},
});

webpack.prod.js

1
2
3
4
5
6
7
8
9
10
11
12
13
//webpack.prod.js
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin'); //清空目录
const common = require('./webpack.common.js');

module.exports = merge(common, {
devtool: 'source-map', //开启生成代码映射文件功能
plugins: [
new UglifyJSPlugin({sourceMap: true}),
new CleanWebpackPlugin(['dist']), //清理 dist 目录
]
});

使用

  1. 在项目根目录下创建文件 package.json ,webpack.common.js, webpack.dev.js ,webpack.prod.js并复制其内容。

  2. 项目根目录下新建:

    • src/index/index.html

    • src/index/index.css

    • src/index/index.less

    • src/index/index.scss

    • src/index/index.js

      1
      2
      3
      4
      5
      6
      7
      //引入依赖的样式模块
      import './index.css'
      import './index.less'
      import './index.scss'

      // 引入第三方库jquery
      import $ from 'jquery'

      样式文件作为 js 文件的依赖,需要在 index.js文件中引入。

  3. 进入DOS命令行,在项目根目录下执行 npm install ,安装当前项目所需要的包。

  4. npm start 启动项目。

webpack 配置文件优化

webpack.common.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//webpack.common.js 通用配置
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //生成html
const ExtractTextPlugin = require("extract-text-webpack-plugin"); //将css作为单独文件
const CopyWebpackPlugin = require('copy-webpack-plugin'); //复制文件



// 页面配置
let config = {
publicDir: ['image'], //公共目录 ,该目录中内容不需要编译, 会自动生成 并且添加到配置项
less: true, //是否支持 less
sass: false, //是否支持sass
// 该配置的目录及文件如果不存在会自动生成
page: [ //页面配置 ,所有内容在 src 目录下
{
dirName: 'index', //目录名称,
filsName: 'index', //要生成的文件名称,html, js,css
createLess: true, //生成less文件
createScss: false, //生成scss文件
},

]
}

let commonConfig = {
// 入口
entry: {
// index: './src/index/index.js',
},
// 出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'src/[name].[chunkhash].js',
publicPath: './'
},
// loader 配置
module: {
rules: [
{ // 配置 babel-loader
test: /\.js$/,
exclude: /(node_modules|bower_components)/, //此模块中的js不转码
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
cacheDirectory: true, //缓存转换结果以提高执行效率
plugins: ["@babel/plugin-transform-runtime"]
}
}
},
{ //配置 css-loader
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
minimize: true, //压缩css
sourceMap: true,
url: false,
}
}]
})
},
]
},
// 插件配置
plugins: [
// 将 css 抽取为独立文件
new ExtractTextPlugin("css/[name].[chunkhash].css"),
],

};

// 初始化配置
initConfig(config)
// 读取配置参数并设置
function initConfig(config) {
mkPageDir(config.page) //创建页面目录及文件
publicDirSet(config.publicDir) //生成公共目录并配置
//开启 less sass的支持
lessAndSassSet({
less: config.less,
sass: config.sass
}) //设置是否支持 less sass

pageSet(config.page)
}
// 配置 less sass
function lessAndSassSet(set) {
if (set.less) {
commonConfig.module.rules.push(
{ //配置 less-loader
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
sourceMap: true,
minimize: true, //压缩css
url: false,
}
}, {
loader: "less-loader"
}],
})
},
)
}
if (set.sass) {
commonConfig.module.rules.push(
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader", options: {
sourceMap: true,
minimize: true, //压缩css
url: false,
}
}, {
loader: "sass-loader", options: {
sourceMap: true
}
}]
})
},
)
}
}
// 页面配置
function pageSet(page) {
for (const val of page) {
commonConfig.entry[val.filsName] = `./src/${val.dirName}/${val.filsName}.js`
commonConfig.plugins.push(new HtmlWebpackPlugin({
filename: `${val.filsName}.html`,
template: `./src/${val.dirName}/${val.filsName}.html`,
chunks: [val.filsName],
hash: true,
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
}))

}
}

// 生成页面所需要文件
function mkPageDir(pageDir) {
// 写入到html文件的模板内容
let htmlCode = `<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>

</body>
</html>`

for (let i = 0; i < pageDir.length; i++) {
let fName = [
`src/${pageDir[i].dirName}/${pageDir[i].filsName}.html`,
`src/${pageDir[i].dirName}/${pageDir[i].filsName}.css`,
`src/${pageDir[i].dirName}/${pageDir[i].filsName}.js`,
]
try {
//检查目录是否存在
let stats = fs.statSync(`src/${pageDir[i].dirName}`)
if (!stats.isDirectory()) {
console.error(`src/${pageDir[i].dirName} 不是目录,请手动删除后再试`)
return
}
for (let j = 0; j < fName.length; j++) {
// 目录存在但文件缺少时,创建出缺少的文件
try {
fs.accessSync(fName[j], fs.constants.R_OK | fs.constants.W_OK)
} catch (error) {
fs.writeFileSync(fName[j], '')
// 在html 中写入模板
if(/\.html/g.test(fName[j])){
fs.appendFile(fName[j], htmlCode, (err) => {
if (err) throw err;
});
// 将css 文件 引入到 js文件中
}else if(/\.js/g.test(fName[j])){
let cssFile = `import './${pageDir[i].filsName}.css'\r\n`
fs.appendFile(fName[j], cssFile, (err) => {
if (err) throw err;
});
}
}
}
console.log('=====================')
} catch (error) { //目录 不存在
// 创建目录及文件
fs.mkdirSync(`src/${pageDir[i].dirName}`, { recursive: true })
console.info('创建目录:',pageDir[i].dirName )
// 创建文件:html css js
for (let j = 0; j < fName.length; j++) {
fs.writeFileSync(fName[j], '')
console.info('创建文件:', fName[j])
}
console.log('+++++++++++++++++++++')
// 写入html文件中写入模板
let htmlFile = `src/${pageDir[i].dirName}/${pageDir[i].filsName}.html`
fs.appendFile(htmlFile, htmlCode, (err) => {
if (err) throw err;
});
// 将css 文件 引入到 js文件中
let jsFile = `src/${pageDir[i].dirName}/${pageDir[i].filsName}.js`
let cssFile = `import './${pageDir[i].filsName}.css'\r\n`
fs.appendFile(jsFile, cssFile, (err) => {
if (err) throw err;
});
}
// 创建less scss文件
if (pageDir[i].createLess) {
let lessName = `src/${pageDir[i].dirName}/${pageDir[i].filsName}.less`
try {
fs.accessSync(lessName, fs.constants.R_OK | fs.constants.W_OK)
} catch (error) {
fs.writeFileSync(lessName, '')
// 将less 文件 引入到 js文件中
let jsFile = `src/${pageDir[i].dirName}/${pageDir[i].filsName}.js`
let lessFile = `import './${pageDir[i].filsName}.less'\r\n`
fs.appendFile(jsFile, lessFile, (err) => {
if (err) throw err;
});
}
}
if (pageDir[i].createScss) {
let scssName = `src/${pageDir[i].dirName}/${pageDir[i].filsName}.scss`
try {
fs.accessSync(scssName, fs.constants.R_OK | fs.constants.W_OK)
} catch (error) {
fs.writeFileSync(scssName, '')
// 将scss 文件 引入到 js文件中
let jsFile = `src/${pageDir[i].dirName}/${pageDir[i].filsName}.js`
let scssFile = `import './${pageDir[i].filsName}.scss'\r\n`
fs.appendFile(jsFile, scssFile, (err) => {
if (err) throw err;
});
}
}

}
}
//生成公共目录并配置
function publicDirSet(dirs) {
let dNames = []
for (const dName of dirs) {
fs.mkdirSync(dName, { recursive: true })
dNames.push({ from: `./${dName}`, to: `${dName}` })
}
commonConfig.plugins.push(new CopyWebpackPlugin(dNames))
}


module.exports = commonConfig;