从0到1搭建webpack2+vue2自定义模板详细教程

前言

webpack2和vue2已经不是新鲜东西了,满大街的文章在讲解webpack和vue,但是很多内容写的不是很详细,对于很多个性化配置还是需要自己过一遍文档。Vue官方提供了多个vue-templates,基于vue-cli用的最多,不过对于很多人来说,vue-cli 的配置还是过于复杂,对于我们了解细节实现不是很好,所以想自己从零开始搭建一个模板工程,也顺便重新认识一下webpack和vue工程化。

webpack 核心概念

Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。

从0到1搭建webpack2+vue2自定义模板详细教程_第1张图片

官方网站:https://webpack.js.org/

安装

在开始前,先要确认你已经安装Node.js的最新版本。使用 Node.js 最新的 LTS 版本,是理想的起步。使用旧版本,你可能遇到各种问题,因为它们可能缺少 webpack 功能或缺少相关 package 包。

本地局部安装:

# 安装 latest release
npm install --save-dev webpack
# 简写模式
npm install -D webpack
# 安装特定版本
npm install --save-dev webpack@ 

全局安装:

npm install -g webpack

注意:不推荐全局安装 webpack。这会锁定 webpack 到指定版本,并且在使用不同的 webpack 版本的项目中可能会导致构建失败。但是全局安装可以在命令行调用 webpack 命令。

【补充】npm install 安装模块参数说明:

-g, --global 全局安装(global)
-S, --save 安装包信息将加入到dependencies(生产阶段的依赖)
-D, --save-dev 安装包信息将加入到devDependencies(开发阶段的依赖),所以开发阶段一般使用它
-O, --save-optional 安装包信息将加入到optionalDependencies(可选阶段的依赖)
-E, --save-exact 精确安装指定模块版本

npm 相关的更多命令参考这篇文章:npm 常用命令详解

然后在根目录下创建一个 webpack.config.js 文件后,你可以通过配置定义webpack的相关操作。

入口(Entry)

入口起点告诉 webpack 从哪里开始,并遵循着依赖关系图表知道要打包什么。可以将您应用程序的入口起点认为是根上下文(contextual root)或 app 第一个启动文件。

单个入口(简写)语法:
用法:entry: string|Array

webpack.config.js:

module.exports = {
  entry: './src/main.js'
};

对象语法:
用法:entry: {[entryChunkName: string]: string|Array}

webpack.config.js:

module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  }
};

这里我们将vue作为库vendor打包,业务逻辑代码作为app打包,实现了多个入口,同时也可以将多个页面分开打包。

多页面应用程序通常使用对象语法构建。对象语法是“可扩展的 webpack 配置”,可重用并且可以与其他配置组合使用。这是一种流行的技术,用于将关注点(concern)从环境(environment)、构建目标(build target)、运行时(runtime)中分离。然后使用专门的工具(如webpack-merge)将它们合并。

注:vue-cli 生成的模板中build文件夹下有四个配置文件:

  • webpack.base.conf.js:基本配置
  • webpack.dev.conf.js:开发阶段配置
  • webpack.prod.conf.js:准生产阶段配置
  • webpack.test.conf.js:测试配置

后三个文件通过webpack-merge插件合并了基本配置,将不同环境下的配置拆分多个文件,这样更加方便管理。

出口(Output)

将所有的资源(assets)归拢在一起后,还需要告诉 webpack 在哪里打包应用程序。webpack 的 output 属性描述了如何处理归拢在一起的代码(bundled code)。output 选项控制 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。

在 webpack 中配置output 属性的最低要求是,将它的值设置为一个对象,包括以下两点:

  • output.filename:编译文件的文件名;
  • output.path对应一个绝对路径,此路径是你希望一次性打包的目录。

单个入口:

const path = require('path');
module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'build')  //__dirname + '/build'
  }
}

多个入口:
如果你的配置创建了多个 "chunk"(例如使用多个入口起点或使用类似CommonsChunkPlugin 的插件),你应该使用以下的替换方式来确保每个文件名都不重复。

  • [name] 被 chunk 的 name 替换。
  • [hash] 被 compilation 生命周期的 hash 替换。
  • [chunkhash] 被 chunk 的 hash 替换。
const path = require('path');
module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'build')
  }
}

// 写入到硬盘:./build/app.js, ./build/vendor.js

加载器(Loaders)

loader 用于对模块的源代码进行转换。loader 可以使你在 require() 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你在 JavaScript 中 require() CSS文件!

在你的应用程序中,有三种方式使用 loader:

  • 通过webpack.config.js配置
  • 使用 require 语句中显示使用
  • 通过 webpack CLI

这里我们主要说明一下使用webpack.config.js配置,使用loader需要在module的rules下配置相应的规则,以css-loader的webpack.config.js为例说明:

module.exports = { 
    module: { 
        rules: [
            {test: /\.css$/, use: 'css-loader'}
        ]
    }
};

这三种配置方式等效:

{test: /\.css$/, use: 'css-loader'}
{test: /\.css$/, loader: 'css-loader',options: { modules: true }}
{test: /\.css$/, use: {
    loader: 'css-loader',
    options: {
      modules: true
    }
}}

注:loader/query可以和options可以在同一级使用,但是不要使用use和options在同一级使用。

CSS样式分离

为了用 webpack 对 CSS 文件进行打包,你可以像其它模块一样将 CSS 引入到你的 JavaScript 代码中,同时用css-loader(像 JS 模块一样输出 CSS),也可以选择使用ExtractTextWebpackPlugin(将打好包的 CSS 提出出来并输出成 CSS 文件)。

引入 CSS:

import 'bootstrap/dist/css/bootstrap.css';

安装css-loader和style-loader:

npm install --save-dev css-loader style-loader

在 webpack.config.js 中配置如下:

module.exports = {
    module: {
        rules: [{
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
        }]
    }
}

资源路径处理

因为.png等图片文件不是一个 JavaScript 文件,你需要配置 Webpack 使用file-loader或者url-loader去处理它们。使用它们的好处:

  • file-loader 可以指定要复制和放置资源文件的位置,以及如何使用版本哈希命名以获得更好的缓存。此外,这意味着 你可以就近管理你的图片文件,可以使用相对路径而不用担心布署时URL问题。使用正确的配置,Webpack 将会在打包输出中自动重写文件路径为正确的URL。
  • url-loader 允许你有条件将文件转换为内联的 base-64 URL(当文件小于给定的阈值),这会减少小文件的 HTTP 请求。如果文件大于该阈值,会自动的交给 file-loader 处理。

安装 file-loader 和 url-loader:

npm install --save-dev file-loader url-loader

配置说明:

{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'img/[name]_[hash:7].[ext]'
    }
},
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'fonts/[name].[hash:7].[ext]'
    }
}

插件(Plugins)

由于 loader 仅在每个文件的基础上执行转换,而插件(plugins)最常用于(但不限于)在打包模块的“compilation”和“chunk”生命周期执行操作和自定义功能(查看更多)。webpack 的插件系统极其强大和可定制化。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,你需要使用 new 创建实例来调用它。

生产环境构建

对于Vue生产环境构建过程中压缩应用代码和使用Vue.js 指南 - 删除警告去除 Vue.js 中的警告,这里我们参考vue-loader文档中的配置说明:

if (process.env.NODE_ENV === 'production') {
    // http://vue-loader.vuejs.org/zh-cn/workflow/production.html
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: false,
            compress: {
                warnings: false
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true
        })
    ])
}

显然我们不想在开发过程中使用这些配置,所以这里我们需要使用环境变量动态构建,我们也可以使用两个分开的 Webpack 配置文件,一个用于开发环境,一个用于生产环境,类似于vue-cli中使用 webpack-merge 合并配置的方式。

可以使用 Node.js 模块的标准方式:在运行 webpack 时设置环境变量,并且使用 Node.js 的process.env
来引用变量。NODE_ENV变量通常被视为事实标准(查看这里)。使用cross-env
包来跨平台设置(cross-platform-set)环境变量。

安装cross-env:

npm install --save-dev cross-env

设置package.json中的scripts字段:

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
}

这里我们使用了cross-env插件,cross-env使得你可以使用单个命令,而无需担心为平台正确设置或使用环境变量。

模块热替换

模块热替换功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载页面。这使得你可以在独立模块变更后,无需刷新整个页面,就可以更新这些模块,极大地加速了开发时间。

这里我们使用webpack-dev-server插件,webpack-dev-server 为你提供了一个服务器和实时重载(live reloading)功能。webpack-dev-server是一个小型的node.js Express服务器,它使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供Web服务。它还有一个通过Socket.IO连接着webpack-dev-server服务器的小型运行时程序。webpack-dev-server发送关于编译状态的消息到客户端,客户端根据消息作出响应。

安装 webpack-dev-server:

npm install --save-dev webpack-dev-server

安装完成之后,你应该可以使用 webpack-dev-server 了,方式如下:

webpack-dev-server --open

上述命令应该自动在浏览器中打开 http://localhost:8080。

webpack.config.js配置:

module.exports = {
    ...
    devServer: {
        historyApiFallback: true, // 任意的 404 响应都替代为 index.html
        hot: true, // 启用 webpack 的模块热替换特性
        inline: true // 启用内联模式
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
    ...
}

更多的配置说明可以看文档:DevServer

动态生成 html 文件

该插件将为你生成一个HTML5文件,其中包括使用script标签的body中的所有webpack包,也就是我们不需要手动通过script去引入打包生成的js,特别是如果我们生成的文件名是动态变化的,使用这个插件就可以轻松的解决,只需添加插件到您的webpack配置如下:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    ...
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html',
            inject: true
        })
    ]
    ...
}

提取 CSS 文件

extract-text-webpack-plugin是一个 可以将*.vue 文件内的