/Webpack-Vue-MultiplePage

在多页面项目下使用 Webpack + Vue

Primary LanguageJavaScript

在多页面项目下使用 Webpack + Vue

前言

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

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

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

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

项目目录结构

    ├─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 页面

<!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 入口文件

import Vue from 'vue'
import App from './app'

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

Webpack 配置文件

用法

先说下 demo 的运行命令

# 首先安装依赖
npm install

# 开发模式
npm run dev

# 打包
npm run build

配置

下面是 webpack 的配置文件 webpack.config.js,其中用注释指出了关键配置。

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 = {
    filename: prod? '../Application/Home/View/' + pathname + '.html' : pathname + '.html', // 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 文件

<!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。