前言

这篇是 webpack 1.x 的多页面配置,4.x 的版本在这里
但是多页面配置的思路是一样的,变的是配置,所以可以先看这篇配置,实际使用 4.x 版本的配置

webpack + vue 能很好的完成单页面应用的开发,官方也提供了很多例子和教程。但使用 webpack 能不能用到多页面项目中,同时又能使用 vue 进行模块组件化开发呢?

这里将结合具体的项目,说明一下我是如何配置的。我们希望能在项目里做到

  • 在每个业务模块下会有很多页面,每个页面都是一个文件夹,所需的资源文件也都放在这个文件夹下
  • 采用 vue + es6 的方式进行组件模块化开发
  • 生成自动引用 webpack 打包好的 js 文件到项目需要的目录
  • 具有良好的开发支持,拥有如 sourseMap,vue 组件的热替换

下面 DEMO 的代码地址: https://github.com/cnu4/Webpack-Vue-MultiplePage

下面是我们项目的目录结构

项目目录结构

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
├─Application (thinkphp 配置下的结构,可以结合自己项目做修改)
│ └─Home
│ └─View (线上用户访问的.html目录)
│ └─index (生成的一个模块)
│ ├─index.html (同一模块的模板放在一个模块目录下)
│ └─info.html
├─Public (线上资源文件目录)
│ ├─css
│ ├─imgs
│ ├─js
│ └─...
└─source (前端开发目录)
├─another (一个业务模块,每个业务下可能有多个页面)
│ └─index
│ ├─app.vue
│ ├─index.html
│ ├─index.js
│ └─static (资源文件)
├─components (vue组件目录)
│ ├─A
│ │ ├─A.vue
│ │
│ └─B
│ ├─B.vue

└─index (一个业务模块,每个业务下可能有多个页面)
├─index
│ ├─app.vue
│ ├─index.html
│ ├─index.js
│ └─static
└─info
└─info.html

页面文件

每个页面都是一个文件夹,所需的资源文件也都放在这个文件夹下,不需要这个页面时,也只需要删除这个文件夹。

下面是 index 模块下的 index 页面

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>index - Vue Webpack Example</title>
<!-- webpack 会将入口 JS 文件引入的 CSS 或者 vue 组件中的 css 生成 style 标签或者生成独立的 css 文件并使用 Link 标签加载它 -->
</head>
<body>
<app></app>
<!-- webpack 的 HtmlWebpackPlugin 插件会根据入口JS文件生成 script 标签并插入在这里或实现按需加载 -->
</body>
</html>

上面是 index 页面的 html 模板,我们无需引入任何 css 和 js ,webpack 会自动帮我打包引入。

其中的 app 标签是我们的 vue 组件,webpac k的加载器会帮我们处理 js 文件中引入的 vue 组件,这样就能正确处理这个标签。

下面 index 页面对应的 js 入口文件

1
2
3
4
5
6
7
import Vue from 'vue'
import App from './app'

new Vue({
el: 'body',
components: { App }
})

Webpack 配置文件

用法

先说下 demo 的运行命令

1
2
3
4
5
6
7
8
9
10
# 首先安装依赖
npm install

# 开发模式
# 注意非 Windows 环境在 package.json 将开发模式的命令改成:
# NODE_ENV=production webpack
npm run dev

# 打包
npm run build

配置

下面是 webpack 的配置文件 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
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
var path = require('path');
var webpack = require('webpack');
// 将样式提取到单独的 css 文件中,而不是打包到 js 文件或使用 style 标签插入在 head 标签中
var ExtractTextPlugin = require('extract-text-webpack-plugin');
// 生成自动引用 js 文件的 HTML
var HtmlWebpackPlugin = require('html-webpack-plugin');
var glob = require('glob');

var entries = getEntry('./source/**/*.js'); // 获得入口 js 文件
var chunks = Object.keys(entries);

module.exports = {
entry: entries,
output: {
path: path.resolve(__dirname, 'Public'), // html,css,js,图片等资源文件的输出路径,将所有资源文件放在 Public 目录
publicPath: '/Public/', // html,css,js,图片等资源文件的 server 上的路径
filename: 'js/[name].[hash].js', // 每个入口 js 文件的生成配置
chunkFilename: 'js/[id].[hash].js'
},
resolve: {
extensions: ['', '.js', '.vue']
},
module: {
loaders: [
{
test: /\.css$/,
// 使用提取 css 文件的插件,能帮我们提取 webpack 中引用的和 vue 组件中使用的样式
loader: ExtractTextPlugin.extract('style', 'css')
},
{
// vue-loader,加载 vue 组件
test: /\.vue$/,
loader: 'vue'
},
{
test: /\.js$/,
// 使用 es6 开发,这个加载器帮我们处理
loader: 'babel',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
// 图片加载器,较小的图片转成 base64
loader: 'url',
query: {
limit: 10000,
name: './imgs/[name].[ext]?[hash:7]'
}
}
]
},
babel: {
presets: ['es2015'],
plugins: ['transform-runtime']
},
vue: { // vue 的配置
loaders: {
js: 'babel',
css: ExtractTextPlugin.extract('vue-style-loader', 'css-loader')
}
},
plugins: [
// 提取公共模块
new webpack.optimize.CommonsChunkPlugin({
name: 'vendors', // 公共模块的名称
chunks: chunks, // chunks是需要提取的模块
minChunks: chunks.length
}),
// 配置提取出的样式文件
new ExtractTextPlugin('css/[name].css')
]
};

var prod = process.env.NODE_ENV === 'production';
module.exports.plugins = (module.exports.plugins || []);
if (prod) {
module.exports.devtool = 'source-map';
module.exports.plugins = module.exports.plugins.concat([
// 借鉴 vue 官方的生成环境配置
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new webpack.optimize.OccurenceOrderPlugin()
]);
} else {
module.exports.devtool = 'eval-source-map';
module.exports.output.publicPath = '/View/';
}

var pages = getEntry('./source/**/*.html');
for (var pathname in pages) {
// 配置生成的 html 文件,定义路径等
var conf = {
// html 文件输出路径
filename: prod? '../Application/Home/View/' + pathname + '.html' : pathname + '.html',
template: pages[pathname], // 模板路径
inject: true, // js 插入位置
minify: {
removeComments: true,
collapseWhitespace: false
}
};
if (pathname in module.exports.entry) {
conf.chunks = ['vendors', pathname];
conf.hash = false;
}
// 需要生成几个 html 文件,就配置几个 HtmlWebpackPlugin 对象
module.exports.plugins.push(new HtmlWebpackPlugin(conf));
}

// 根据项目具体需求,具体可以看上面的项目目录,输出正确的 js 和 html 路径
// 针对不同的需求可以做修改
function getEntry(globPath) {
var entries = {},
basename, tmp, pathname;

glob.sync(globPath).forEach(function (entry) {
basename = path.basename(entry, path.extname(entry));
tmp = entry.split('/').splice(-3);
pathname = tmp.splice(0, 1) + '/' + basename; // 正确输出 js 和 html 的路径
entries[pathname] = entry;
});
console.log(entries);
return entries;

开发模式

运行 npm run dev 开发模式运行 demo

根据 webpack 配置文件中 output 的 publicPath 配置项和 HtmlWebpackPlugin 插件的 filename 配置项

demo 中 dev 环境下中分别是 /View 和pathname + ‘.html’

所以 demo 中通过 http://localhost:8080/View/another/index.html 可以访问到 another 模块下的 index 页面

打包

运行 npm run build 打包,可以看到 Application/Home/View 目录下成功生成了按模块分组的 html 文件,这正是项目需要的。

如 Application/Home/View/index 下的 index.html 文件

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>index - Vue Webpack Example</title>

<link href="/Public/css/vendors.css" rel="stylesheet"><link href="/Public/css/index/index.css" rel="stylesheet"></head>
<body>
<app></app>

<script src="/Public/js/vendors.91e0fac1fd8493060c99.js"></script><script src="/Public/js/index/index.91e0fac1fd8493060c99.js"></script></body>
</html>

venders.css 和 venders.js 文件是 webpack 插件帮我们自动生成的公共样式模块和公共 js 模块。打开页面,还能看到其他资源文件也都被正确处理了。

总结

总结一下 webpack 帮我们做了下面几件事

  • 使用 vue-loader 使我们能进行组件化开发。
  • 根据项目需求自动生成按模块分组的 html 文件。
  • 自动提取样式文件,并和打包后的 js 文件加入到自动生成的 html 文件。
  • 将 js 打包为不同的入口文件,并使用插件抽取公用模块。
  • 为开发调试提供需要的环境,包括热替换,sourceMap。

代码地址: https://github.com/cnu4/Webpack-Vue-MultiplePage