最近使用 webpack 打包工具,配合 nginx 做了个活动页。记录下本次项目中的问题,就当笔记用了。
一、webpack 配置
本次项目采用传统前后端分离模式,就是后端提供接口,前端写静态、查询接口就可以了,然后是基于微信公众号开发的 h5 活动页。话说在使用决定使用 webpack 之前,我对 webpack 的期望就只是能够完成前端代码的打包就可以,并且以前使用 webpack 也只是在 vue 全家桶中使用的,具体涉及到配置什么的完全没有关心过,因为是拿来即用不需要考虑特别多。然而啊,出来混迟早是要还的,用了还想不学会简直是想的美。
废话不多说,接下来直接介绍下我一步一步构建起来的配置。在此强调一点,任何不懂请详细看官方文档,请详细看官方文档!有些文档是英文,可以借助谷歌翻译、有道翻译等,这样能避免百度出来的各种不靠谱答案。亲测!!
这次我们直接使用 webpack 的最新版本 4.12.0 ,并搭配 webpack-cli 使用,版本为 3.0.6 。
1、项目初始化
新建一个文件夹 webpack 并在当前文件夹下初始化项目,打开终端,依次执行 npm init
、npm install --save-dev webpack-cli webpack
,以上依赖包安装完成后,增加项目目录如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23|-- webpack
|-- node_modules // 各种依赖包
|-- libs // 各个页面公用的插件
|-- utils // 各个页面通用的一些方法汇总
|-- assets // 各个页面通用资源
|-- css
|-- reset.css
|-- img
|-- xxx.png
|-- src // 页面资源
|-- index // 首页
|-- css
|-- index.less
|-- js
|-- index.js
|-- img
|-- xxx.png
|-- index.html
|-- award // 奖励
|-- login // 登录
|-- register // 注册
|-- package.json // 项目构建信息的文件
|-- README.md
2、增加 webpack 配置文件
在项目根目录增加文件 webpack.config.js
,基本配置如下:1
2
3
4
5
6
7
8
9const path = require('path');
module.exports = {
entry:'./src/index/js/index.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'dist')
}
}
以上,入口文件为 src/index/js/index.js ,最后会打包输出到根目录下 dist 文件目录下的 bundle.js 。此时在 package.json 文件中的 scripts 中添加一条指令 "build" : "webpack"
,保存后在终端运行指令 npm run build
,此时会在根目录生成 dist 目录并打包出文件 bundle.js 。 ok ,至此,我们已经能够完美实现 js 打包。
3、html 页面打包
在第二步的操作中,虽然我们完成了 js 源码的打包,但是打包到的目录中并没有 html 文件,也就是我们的视图层并没有完成一并打包。此处,我们需要安装 html-webpack-plugin ,在终端执行 npm install --save-dev html-webpack-plugin
,在 webpack-config.js 中引入此依赖,增加代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入依赖包
module.exports = {
entry:'./src/index/js/index.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'dist')
},
plugins:[ // 引入的依赖包在此进行配置
new HtmlWebpackPlugin({
filename:'index.html', // 输出的文件名称
template:'./src/index/index.html', // 打包的文件地址
minify:true, // 是否压缩,默认值为 false
chunks:[] // 引入其他插件,数组形式
})
]
}
删除 dist 目录,在终端执行 npm run build
则会在 dist 目录下生成 bundle.js 文件和 index.html ,并且 index.html 中默认引入 bundle.js 文件。
执行到这个步骤,我这边其实是报错了,但是一直无法找到其中的原因是因为什么,所以直接删除了 node_modules 目录然后重新安装了依赖,显然是解决了问题。
4、清理 dist 目录
每次文件打包都会打包到 dist 目录下,最好在每次打包前能够删除 dist 然后重新构建生成 dist 目录下资源。此时就使用到 clean-webpack-plugin 插件,首先在命令行输入:1
npm install --save-dev clean-webpack-plugin
然后在 webpack.config.js 中引入:1
2
3
4
5
6
7
8
9
10....
const CleanWebpackPlugin = require('clean-webpack-plugin');
....
plugins:[
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
...
})
]
....
在终端执行 npm run build 指令会先删除 dist 目录及其目录下资源,然后重新生成打包文件。
5、资源管理
常见的资源如 CSS 、 IMG 、Font 、数据等,这里所说的资源管理就是 webpack 通过各种 **-loader 将其所对应的资源打包入我们的 dist 目录中。
5.1 加载 CSS
为了从 Javascript 模块中 import 一个 CSS 文件,我们需要在 module 配置中安装并添加 style-loader
和 css-loader
。
在终端输入指令:1
npm install --save-dev style-loader css-loader
在 webpack.congfig.js 中修改配置如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14...
output:{},
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
]
}
]
}
...
这使你可以在依赖于此样式的文件中 import './style.css'
。现在,当该模块运行时,含有 CSS 字符串的 <style>
标签,将被插入到 html
文件的 <head>
中。
在终端输入 npm run build
后,打开生成的 dist 目录下的 index.html 文件。运行到这里发现代码出现了问题,静态页面和 js 是被打包并生成了,但是在浏览器中打开页面发现这二者并没有什么关联,也就是说 bundle.js 并没有被引入到 index.html 中。又查了下 html-wabpck-plugin
的配置说明,修改 webpack.config.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
33const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry:{
'index':'./src/index/js/index.js' // 此处做了修改,为引入多文件做准备
},
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
filename:'index.html',
template:'./src/index/index.html',
minify:true,
chunks:['index'] // 引入的 js
})
],
module:{
rules:[
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
}
在 ./src/index/js/index.js 中执行代码:1
2import './../css/index.css';
alert(1);
在 ./src/index/css/index.less 的代码如下:1
2
3
4
5body{
background: rgb(222,222,222);
font-size: 14px;
line-height: 1.5;
}
在终端执行 npm run build
后将 dist 目录下 index.html 在浏览器中打开就可以看到效果。
5.2 加载 img
首先添加依赖包 npm install --save-dev file-loader
,然后修改 webpack.config.js ,添加如下代码:1
2
3
4
5
6
7
8
9
10...
rules:[
...,
{
test:/\.(png|jpg|svg|gif)$/,
use:[
'file-loader'
]
}
]
此外,在 ./src/index/js/index.js 中增加如下代码:1
2
3
4
5
6import logo from './../img/logo.png'
let img = document.createElement('img');
img.src = logo;
document.getElementsByTagName('body')[0].appendChild(img);
在终端输入指令 npm run build
,可见图片资源已经被打包到 dist 目录中。
5.2 字体、数据等资源
因为这些资源在本项目中使用较少,在此就不进行过多的介绍了。详情请查案官方文档-资源管理;
6、增加各个页面内容
至此,只需要完成各个静态页面布局基本上能够完成整个项目,并修改 webpack.congfig.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
67const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry:{
'index':'./src/index/js/index.js',
'award':'./src/award/js/award.js',
'login':'./src/award/js/award.js'
},
output:{
filename:'[name].bundle.js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
filename:'index.html',
template:'./src/index/index.html',
minify:true,
chunks:['index']
}),
new HtmlWebpackPlugin({
filename:'award.html',
template:'./src/award/index.html',
minify:true,
chunks:['award']
}),
new HtmlWebpackPlugin({
filename:'login.html',
template:'./src/login/index.html',
minify:true,
chunks:['login']
})
],
module:{
rules:[
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test:/\.less$/,
use:[
{
loader:'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'less-loader'
}
]
},
{
test:/\.(png|svg|jpg|gif)$/,
use:[
'file-loader'
]
}
]
}
}
至此,项目基本搭建完成,接下来进行一步一步优化。
6.1 使用 source-map
为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。如果一个错误来自于 b.js
,source map 就会明确的告诉你。
具体配置请看代码:
webpack.config.js:1
2
3
4
5...
entry:{},
devtool:'inline-source-map',
plugins:[]
...
6.2 使用 webpack-dev-server
每次编译代码时,都需要在终端运行 npm run build
,这样就变得很麻烦。在 webpack 中提供了几种不同的选项,如:
- webpack’s watch mode
- webpack-dev-erver
- webpack-dev-middleware
此处只介绍下 webpack-dev-server ,关于其他两项,请查阅官方文档 。
首先安装 webpack-dev-server 依赖:1
npm install --save-dev webpack-dev-server
然后修改 webpak.config.js 配置文件:1
2
3
4
5
6
7
8
9
10...
entry:{...},
devtool:'...',
devServer:{
contentBase : './dist'
}
plugins:[
...
]
...
以上配置告知 webpack-dev-server
,在 localhost:8080
下建立服务,将 dist
目录下的文件,作为可访问文件。
接下来添加一个 script
脚本,可以直接运行开发服务器(dev-server):
package.json:1
2
3
4
5
6
7...
scripts:{
...
"start":"webpack-dev-server --open"
...
}
...
OK ,在终端输入 npm run start
,就会看到浏览器自动加载页面。如果现在修改和保存任意源文件,web 服务器就会自动重新加载编译后的代码。
6.3 页面内静态资源无法打包
在 index.html
中通过 img
引入图片资源,执行 npm run start
后发现图页面内图片资源无法打包输出。 官方文档推荐使用 html-loader,按照文档的操作以及相关资料的查询,我始终没能够成功完成页面内图片资源的打包,最后使用的是 html-withimg-loader
完成图片打包,在终端输入指令:1
npm install --save-dev html-withimg-loader
webpack.config.js :1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18...
entry:{},
plugin:[...],
modules:{
rules:[
...
{
test: /\.(html|htm|)$/,
use: [
'html-withimg-loader'
]
}
]
},
output:{
...
}
...
6.4 jquery 依赖安装配置
整个前端页面是依赖 jquery 完成开发的,所有需要引入 jquery 依赖。终端输入指令:1
npm install --save-dev jquery
webpack.config.js 增加如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13...
entry:{},
plugin:[
new webpack.ProvidePlugin({
$:"jquery",
jQuery:"jquery",
"window.jQuery":"jquery"
})
],
output:{
...
}
...
6.5 打包代码分类
目前所有资源包括 **.html 、 css 、 js 、 img 都在 dist 目录下,没有进行资源分类,所以接下来就是整合资源。
js
将 js 代码打包至 dist 目录下 js 文件加只需修改 webpack.config.js 的 output 即可:1
2
3
4
5
6...
output:{
filename:'js/[name].bundle.js', // 此处增加一层 js 目录
path:path.resolve(__dirname,'dist')
}
...
img
将图片打包至 images 目录下,需要修改下 file-loader 的使用规则:
webpack.config.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...
entry:{}
output:{},
plugins:[],
modules:{
rules:[
...
{
test:/\.(png|svg|jpg|gif)$/,
use:[
{
loader:'file-loader',
options:{
name: '[name][hash].[ext]',
outputPath: 'images/' // 此处修改输出路径
}
}
]
}
...
]
}
...
css
css 打包到相同目录就有点复杂了,需要使用 extract-text-webpack-plugin
插件,需要说明的是在 webpack 4.0 中,需要安装这个插件的 beta 版本。在终端输入指令:1
npm install --save-dev extract-text-webpack-plugin@next
webpack.config.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...
const ExtractTextPlugin = require('extract-text-webpack-plugin');
...
entry:{},
output:{},
plugins:[
...
new ExtractTextPlugin({
filename: 'css/[name].css' // 此处规定输出目录
})
...
],
modules:{
rules:[
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{
loader:"css-loader",
options:{
url: false,
minimize: true,
sourceMap: true
}
}
]
})
}
...
]
}
...
以上大概就能基本完成整个项目的打包。还有两点需要说明:
- 将生产和开发的配置文件拆开来使用,然后使用
webpack-merge
来实现代码合并 - 生产的代码需要删除
console.log
等控制台信息,可使用uglifyjs-webpack-plugin
来完成
以上两项的配置不再过多说明,详情请参考 webpack 官方文档或者我的github;
二、其他问题
1、ES6 语法不兼容
本次项目中使用了大量的 ES6 语法,实践证明在某些 IOS 机中对 ES6 语法并不支持( 根据手机型号、系统版本都有关系 ),暂时改用 ES5 语法进行开发。 webpack 应该有 loader 可以完成语法转换,后续再更新吧。
我又回来更新啦。这次更新一下 ES6 转 ES5 语法。
1) 依赖安装
最开始我是按照官网的步骤安装依赖并且修改配置的,然而并没有实现 ES6 转 ES5 。请看安装的依赖:
在终端输入以下指令完成 babel-loader
相关依赖的安装:1
npm install --save-dev babel-loader babel-core babel-preset-env
其中:
babel-loader
是 webpack 的 loader 的一种,作用同其他 loader 一样,实现对特定文件类型的处理。
loader
让webpack
能够去处理那些非JavaScript
文件(webpack
自身只理解JavaScript
)。loader
可以将所有类型的文件转换为webpack
能够处理的有效模块,然后你就可以利用webpack
的打包能力,对它们进行处理。
- 虽然
webpack
本身就能够处理.js
文件,但无法对ES2015+
的语法进行转换,babel-loader
的作用正是实现对使用了ES2015+
语法的.js
文件进行处理。 babel-core
的作用在于提供一系列api
。这便是说,当webpack
使用babel-loader
处理文件时,babel-loader
实际上调用了babel-core
的api
,因此也必须安装babel-core
babel-preset-env
的作用是告诉babel
使用哪种转码规则进行文件处理。事实上,babel
有几种规则都可以实现对ES6
语法的转码,如babel-preset-es2015
、babel-preset-latest
、babel-preset-env
,不过官方现已建议采用babel-preset-env
,本文也将采用babel-preset-env
2) babel 规则配置
在项目根目录下新建 .babelrc
文件,代码如下:1
{ "presets": [ "env" ] }
3) webpack.config.js 配置
修改 webpack.base.config.js
配置文件,此处只需要增加 modules -> rules
规则即可:1
2
3
4
5
6
7
8
9
10
11
12
13...
modules:{
rules:[
...
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
...
]
}
...
这就告诉 webpack
打包时,一旦匹配到 .js
文件就使用 babel-loader
进行处理,如前文所述, babel-loader
调用 babel-core
的 api 使用 bable-preset-env
的规则进行转码。这里并没有使用 entry 、 output 这样的参数,这是 webpack4.x
有默认的入口和出口,本项目无须改变,因此便不必进行设置。
完成以上配置,即可在终端输入指令 npm run build
,可以查看打包输出的代码已经将 let
转变成 var
。
2、判断是否在微信中打开
1 | var isInWeiXin = navigator.userAgent.toLowerCase().indexOf('micromessagenger') != -1 ? true : false; |
3、IOS 系统 300ms 延迟
本次活动页面同时部署到了 app 内,在使用过程中发现很多地方在点击后并不是立刻执行了绑定事件,主要是由于 IOS 内部存在 300ms 的延迟。有一部分历史原因,详情查看《ios的300ms点击延时问题》。
根据以上博客,我采用了 fastclick 来解决这个问题。在终端输入:1
npm install --save-dev fastclick
然后在各个页面的 js 文件头部引入此插件,如果使用了 jQuery 的话,在 $(function(){})
中执行:fastclick.attach()
./src/index/js/index.js
:1
2
3
4
5var fastClick = require('fastclick');
$(function(){
fastclick.attach(document.body);
.....
})
其他页面按照这个方法引入即可。
参考:《webpack4.x下babel的安装、配置及使用》
推荐:大公司里怎样开发和部署前端代码?
推荐:关于 webpack 的面试题有哪些?
代码地址: https://github.com/HappyJeannie/webpack-init
本文为个人原创,转载请注明出处!