# 第2章 Vue 环境快速搭建

本章节相关代码存放在Github (opens new window)中。

上一章我们介绍了 Vue 框架都解决了开发者的哪些痛点,而 Vue 的安装使用也是很容易上手的。一般来说,前端使用 Vue 框架有两种方式:
(1) 简单的页面,可以通过<script>引入 CDN 资源来使用。
(2) 从项目可维护和拓展性上看,搭建本地调试环境来构建 Vue 项目会更合适。

一般来说,我们可以考虑使用的场景来确定用哪种方式。如果只是简单的页面展示、需要快速看效果、测试某些代码片段等情况,例如直接在 codepen 这样的在线编程环境上测试某个功能,我们就可以直接引入 CDN 资源来加载 Vue。如果对于比较长期维护和管理的一个项目,或者说需要多人协作开发的一个较大型的项目来说,搭建一套开发环境来调试,从方便调试、方便接入 CI/CD、代码管理、代码规范检查和格式化、代码分包等角度来考虑,维护一套编译环境能减轻不少的技术债务。

# 2.1 直接引入 CDN

上一章中我们介绍了 Vue 框架通过模板引擎、虚拟 DOM、数据绑定和更新处理等功能,提供给开发者一种特殊的模板语法使用,但归根到底所有的这些功能都是通过 Javascript 来实现的,所以从某种程度来说,我们使用 Vue 框架,也就是引入一段 Vue 框架的 JS 代码来使用。

而在前端页面中引入一段 Javascript 代码片段,相信各位读者都很熟悉了。我们只需要通过<script>标签,把源文件的地址引入,浏览器在解析到<script>标签的时候,就会自动去请求获取到相关的资源、然后进行解析,所以我们可以直接使用 Vue 官方提供的一些 CDN 资源。直接引入 CDN 的方式来使用 Vue,适合跑 Demo 和测试某种特殊场景的情况使用。

# 2.1.1 html 编写

这是一个最简单的 demo,我们可以在浏览器中直接跑这样的代码:

<!-- 参考 code/2/1-import-cdn.html 文件 -->
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Vue直接引入Script资源</title>
    <!-- 引入 CDN 资源 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">{{ message }}</div>
    <script>
      // 初始化 Vue 实例
      new Vue({
        el: "#app",
        data() {
          return {
            message: "欢迎来到Vue的世界"
          };
        }
      });
    </script>
  </body>
</html>

打开浏览器,我们便能看到想要的效果:
image
图 2-1 直接引入 CDN 使用 Vue

点击此处查看页面效果 (opens new window) 点击此处查看源码 (opens new window)

浏览器在解析了 Vue 框架的代码并执行之后,我们通过new Vue()启动了一个 Vue 根实例,而<div id="app"></div>中使用双括号{{}}绑定的message变量也被替换成了对应的值。之前读完第一章的你们肯定已经能理解这中间究竟发生了什么,而 Vue 中也有很多相关的概念包括实例、组件、模板语法、生命周期等,会在后面几章详细地描述和介绍。

# 2.1.2 在线代码编写

前端有很多在线编写的平台,例如 codepen、jsfiddle 等,有了这些平台,我们再也不用在本地生成个临时的 HTML 文件来编写代码了。我们可以直接在这些平台上写测试代码,例如我们这里可以直接使用 Vue:
image
图 2-2 codepen 所见即所得编程

在线编写的好处是所见即所得,我们只需要引入需要的资源,然后编写好代码,下方就能直接看到代码的效果,还是很方便的。同时这些在线编程的网站也常常提供了代码片段保存和分享的功能,例如 Element UI 也将每一段的代码示例存到 codepen,开发者只需要打开对应的链接就可以看到效果,同时还可以很方便地基于现有的逻辑来快速调试,是特别好用的一个工具。

# 2.2 Vue CLI 脚手架

脚手架在前端中也是频繁会出现的一个词,尤其近几年前端领域经历了翻天覆地的变化,前端开发页面更经常会使用到脚手架。脚手架来自于现实生活中的房屋工程,为了保证各施工过程顺利进行而搭建的的工作平台。在前端领域中,脚手架的功能也是相似的,为了保证开发过程的顺利、提供便利设施而搭建的开发环境。有了脚手架,我们可以通过简单的命令就能快速生成 Demo 代码、构建本地测试环境、编译和打包代码、发布到现网等等功能。

使用 Vue 框架,脚手架一般会优先选择官方提供的 Vue CLI。如果是熟悉 Webpack 或者 Gulp 的小伙伴,当然也可以自行搭建脚手架。Vue CLI 其实也是基于 Webpack 封装的便捷脚手架,所以接下来我们会先介绍如何使用该脚手架,再对比 Webpcak 看看官方 CLI 都封装了哪些能力。

# 2.2.1 快速创建项目

通常来说,脚手架可以让你快速地生成示例代码、搭建本地环境,也可以更新依赖的版本等,避免了每个开发者自行调整开发环境、打包逻辑等配置。Vue cli 也提供了这样的能力:对 Babel、TypeScript、ESLint、PostCSS、PWA、单元测试和 End-to-end 测试提供开箱即用的支持。

# Vue CLI

Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。使用方式很简单:

// 安装脚手架
npm install -g @vue/cli
// 脚手架生成 vue 项目,同时会自动安装依赖
vue create vue-cli-demo

生成之后的代码目录是这样的:

图 2-3 Vue CLI 生成的项目 Demo

点击此处查看页面效果 (opens new window) 点击此处查看源码 (opens new window)

# 启动项目

一般来说,比较规范的项目都会有个 README.md 文件,我们可以通过该文件看到项目相关的一些内容,包括项目背景、项目启动和构建、相关负责人等说明。在这里,我们使用 Vue CLI 生成的项目 Demo 也有一个 README.md 文件。打开这个文件,我们能看到一些简单的说明:

# Project setup
yarn install

# Compiles and hot-reloads for development
yarn run serve

# Compiles and minifies for production
yarn run build

# Run your tests
yarn run test

# Lints and fixes files
yarn run lint

yarn 跟 npm 都是差不多的包管理器,区别在于 yarn 在安装时会速度更快(并行、离线等),以及版本统一管理的比较好。但如果你不是有特殊的喜好或者习惯,其实两个都可以用,例如这里的yarn run serve也可以用npm run serve来运行。

如果有些习惯不好的项目缺了 README,这种时候要怎么去启动一个项目呢?可以查看package.json文件:

{
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  }
}

一般来说,开发环境是devserve等,生产环境是buildscripts里是一些任务,运行命令npm run taskName就可以启动了。


图 2-4 Vue CLI 启动本地开发

图 2-4 中,我们可以看到任务已经启动了,访问输出的地址(这里是http://localhost:8080/或者http://10.40.120.53:8080/)就能看到我们的项目跑起来了。


图 2-5 Vue CLI 生成的 Demo 效果

# 2.2.2 CLI 与 Webpack 介绍

前面其实也有简单说到 Vue CLI 和 webpack 的关系,这里我们更准确地描述一下。Vue CLI 服务是构建于 webpack 和 webpack-dev-server 之上的,它包含了:

  • 加载其它 CLI 插件的核心服务
  • 一个针对绝大部分应用优化过的内部的 webpack 配置
  • 项目内部的 vue-cli-service 命令,提供 serve、build 和 inspect 命令

要理解 CLI 的一些配置,我们先要来理解一下 Webpcak 的一些概念。本质上,Webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。这里我们主要介绍搭建时涉及的一些配置。

四个核心概念:入口(entry)、输出(output)、loader、插件(plugins)。

# 入口(entry)

入口(entry)将您应用程序的入口起点认为是根上下文(contextual root)或 app 第一个启动文件。这个概念的理解可以举例来说明下,例如在 Vue 中是new Vue()位置所在的文件,在 Angular 中是启动.bootstrap()的文件,在 React 中则是ReactDOM.render()或者是React.render()的启动文件。

// 将entry指向启动文件即可
module.exports = {
  entry: "./path/to/my/entry/file.js"
};

// 我们来看看,Vue CLI里源码是怎样的:
webpackConfig
  .entry("app")
  .add("./src/main.js")
  .end();

显然,Vue CLI 的默认入口文件是./src/main.js。我们能看到 Vue CLI 内部的 webpack 配置是通过链式调用的,该能力通过 webpack-chain 库提供的。这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。

# 出口(output)

出口(output)属性描述了如何处理归拢在一起的代码(bundled code),在哪里打包应用程序。简单来说,就是最终打包好的代码放哪。一般需要以下两点配置:
(1) filename: 编译文件的文件名(main.js/bundle.js/index.js 等)。
(2) path:对应一个绝对路径,此路径是你希望一次性打包的目录。

// 这是一般的Webpack写法
module.exports = {
  output: {
    filename: "bundle.js",
    path: "/home/proj/public/assets"
  }
};

// 我们来看看,Vue CLI源码的实现:
webpackConfig.output
  .path(api.resolve(options.outputDir))
  .filename(isLegacyBundle ? "[name]-legacy.js" : "[name].js")
  .publicPath(options.publicPath);

# loader

Webpack 把每个文件(.css, .html, .scss, .jpg, etc.) 都作为模块处理,但 Webpack 只理解 JavaScript。如果你看过生成的 bundle.js 代码就会发现,Webpack 将所有的模块打包一起,每个模块添加标记 id,通过这样一个 id 去获取所需模块的代码。而我们的 loader 的作用,就是把不同的模块和文件转换为这样一个模块,打包进去。

loader 支持链式传递。能够对资源使用流水线(pipeline)。loader 链式地按照先后顺序进行编译,从后往前,最终需要返回 javascript。不同的应用场景需要不同的 loader,这里我简单介绍几个常用的(loader 使用前都需要安装,请自行查找依赖安装)。

babel-loader
babel-loader 将 ES6/ES7 语法编译生成 ES5,当然有些特性还是需要 babel-polyfill 支持的(Babel 默认只转换新的 JavaScript 句法,而不转换新的 API,如 Promise 等全局对象)。而对于 babel-loader 的配置,可以通过options进行,但一般更常使用.babelrc 文件进行(使用 Vue CLI 生成的项目目录中,可以使用 babel.config.js 文件来配置):

css 相关 loader

  • css-loader: 处理 css 文件中的 url()
  • style-loader: 将 css 插入到页面的 style 标签
  • less-loader: less 转换为 css
  • postcss-loader(autoprefixer-loader): 自动添加兼容前缀(-webkit--moz-等)

其他 loader

  • url-loader/file-loader: 修改文件名,放在输出目录下,并返其对应的 url
    • url-loader 在当文件大小小于限制值时,它可以返回一个 Data Url
  • html-loader/raw-loader: 把 Html 文件输出成字符串
    • html-loader 默认处理 html 中的<img src="image.png">require("./image.png"),需要在配置中指定 image 文件的加载器

说了这么多,我们来看看 Vue CLI 里自带了多少的 Loader:

// 最基本的,Vue文件解析
webpackConfig.module
  .rule("vue")
  // 命中后缀为.vue的文件
  .test(/\.vue$/)
  // 使用缓存cache-loader
  // 在一些性能开销较大的loader之前添加此loader,以将结果缓存到磁盘里
  .use("cache-loader")
  .loader("cache-loader")
  .options(vueLoaderCacheConfig)
  .end()
  // 使用vue-loader
  .use("vue-loader")
  .loader("vue-loader")
  .options(
    Object.assign(
      {
        compilerOptions: {
          preserveWhitespace: false
        }
      },
      vueLoaderCacheConfig
    )
  );

// 图片文件解析
webpackConfig.module
  .rule("images")
  // 命中后缀为.png/.jpeg/.jpg/.gif/.webp的文件
  .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
  // 使用url-loader
  // 修改文件名,放在输出目录 img 下,并返其对应的url
  // 默认情况下,生成文件的文件名,是文件内容的 MD5 哈希值
  .use("url-loader")
  .loader("url-loader")
  .options(genUrlLoaderOptions("img"));

// do not base64-inline SVGs.
// https://github.com/facebookincubator/create-react-app/pull/1180
// svg文件解析
webpackConfig.module
  .rule("svg")
  // 命中后缀为.svg的文件
  .test(/\.(svg)(\?.*)?$/)
  // 使用file-loader,与url-loader相似
  // 修改文件名,放在输出目录 img 下,并返其对应的url
  // 默认情况下,生成文件的文件名,是文件内容的 MD5 哈希值
  .use("file-loader")
  .loader("file-loader")
  .options({
    name: genAssetSubPath("img")
  });

// 媒体文件解析
webpackConfig.module
  .rule("media")
  // 命中后缀为以下的文件
  .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
  // 使用url-loader,此处输出目录为 media
  .use("url-loader")
  .loader("url-loader")
  .options(genUrlLoaderOptions("media"));

// 字体文件解析
webpackConfig.module
  .rule("fonts")
  .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
  // 使用url-loader,此处输出目录为 fonts
  .use("url-loader")
  .loader("url-loader")
  .options(genUrlLoaderOptions("fonts"));

除了上面这些之外,还有 CSS Loader 一大堆,包括 extract-css-loader、vue-style-loader、css-loader、cssnano、postcss-loader、sass-loader、less-loader 等,篇幅关系,这里就不再展示,我们也可以明白以下的 CLI 说明:
(1) 所有编译后的 CSS 都会通过 css-loader 来解析其中的url()引用,并将这些引用作为模块请求来处理。也就是说我们可以根据本地的文件结构用相对路径来引用静态资源。
(2) 内置的 webpack 被预配置了预处理器,支持 Sass/Less/Stylus 处理。
(3) Vue CLI 内部使用了 PostCSS,默认开启了 autoprefixer。

差点忘了,还有个特别重要的嘉宾没登场:babel-loader。babel 相关的被单独放在了 cli-plugin-babel 模块里了,我们来看看:

// babel-loader使用了插件的模式
// 插件地址可参考@vue/cli-plugin-babel
webpackConfig.module
  .rule('js')
    // 命中后缀为.js/.jsx/.mjs/.mjsx的文件
    .test(/\.m?jsx?$/)
    .exclude
      // 排除以下文件
      .add(filepath => {
        // blablabla
      })
      .end()
    // 使用缓存cache-loader
    // 在一些性能开销较大的loader之前添加此loader,以将结果缓存到磁盘里
    .use('cache-loader')
      .loader('cache-loader')
      // 这里还有一堆配置blablabla,感兴趣的可以自行去翻
      .end()
    // 使用babel-loader
    .use('babel-loader')
        .loader('babel-loader')

// 我们能看到,Vue CLI默认的babel配置如下:
{
  babel: {
    presets: ['@vue/app']
  },
  dependencies: {
    'core-js': '^2.6.5'
  }
}

这里使用的 babel 插件是@vue/babel-preset-app,它通过@babel/preset-env 和 browserslist 配置来决定项目需要的 polyfill。默认情况下,它会把useBuiltIns: 'usage'传递给@babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。

# 插件(plugins)

loader 仅在每个文件的基础上执行转换,插件目的在于解决 loader 无法实现的其他事。由于 plugin 可以携带参数/选项,需要在 wepback 配置中,向 plugins 属性传入new实例。这里也介绍几个常用的插件:

HtmlwebpackPlugin
功能有下:

  • 为 html 文件中引入的外部资源如 script、link 动态添加每次 compile 后的 hash,防止引用缓存的外部文件问题
  • 可以生成创建 html 入口文件,比如单页面可以生成一个 html 文件入口
  • 但其实最常使用的,无非是把 index.htnm 页面插入(因为入口文件为 js 文件)
// HtmlwebpackPlugin使用方式示例
new HtmlwebpackPlugin({
  template: path.resolve(__dirname, "src/index.html"),
  inject: "body"
});

CommonsChunkPlugin
提取代码中的公共模块,然后将公共模块打包到一个独立的文件中,以便在其他的入口和模块中使用。

webpack.ProvidePlugin
定义标识符,当遇到指定标识符的时候,自动加载模块。适合引入的全局变量,像我们常用的 jQuery:

// webpack.ProvidePlugin使用方式示例
new webpack.ProvidePlugin({
  jQuery: "jquery",
  $: "jquery"
});

ExtractTextPlugin
可以将样式从 js 中抽出,生成单独的.css 样式文件。即把所以的 css 打包合并:

// ExtractTextPlugin使用方式示例
new ExtractTextPlugin("style.css", {
  allChunks: true // 提取所有的chunk(默认只提取initial chunk,而上面CommonsChunkPlugin已经把部分抽离了)
});

我们看看 Vue CLI 中默认都使用了哪些插件:

// html-webpack-plugin插件
const HTMLPlugin = require("html-webpack-plugin");
// 这是html-webpack-plugin的扩展插件
const PreloadPlugin = require("@vue/preload-webpack-plugin");

webpackConfig.plugin("html").use(HTMLPlugin, [htmlOptions]);

// 用于自动连接异步(和其他类型)的JavaScript块<link rel='preload'>。这有助于延迟加载
webpackConfig.plugin("preload").use(PreloadPlugin, [
  {
    rel: "preload",
    include: "initial",
    fileBlacklist: [/\.map$/, /hot-update\.js$/]
  }
]);

// 来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。
webpackConfig.plugin("prefetch").use(PreloadPlugin, [
  {
    rel: "prefetch",
    include: "asyncChunks"
  }
]);

// 简单地复制public文件夹下的内容
const publicDir = api.resolve("public");
webpackConfig.plugin("copy").use(require("copy-webpack-plugin"), [
  [
    {
      from: publicDir,
      to: outputDir,
      toType: "dir",
      ignore: publicCopyIgnore
    }
  ]
]);

// 为什么不为html-webpack-plugin设置文件名?
// 1.它无法处理绝对路径
// 2.相对路径导致生成错误的SW清单(#2007)
webpackConfig
  .plugin("move-index")
  .use(require("../webpack/MovePlugin"), [
    path.resolve(outputDir, "index.html"),
    path.resolve(outputDir, options.indexPath)
  ]);

// DefinePlugin允许创建一个在编译时可以配置的全局常量
webpackConfig
  .plugin("define")
  .use(require("webpack/lib/DefinePlugin"), [resolveClientEnv(options)]);

// 友好的错误插件,在webpack时显示非常混乱的错误
// 无法解析加载器,因此我们提供自定义处理程序来改进它
const { transformer, formatter } = require("../util/resolveLoaderError");
webpackConfig
  .plugin("friendly-errors")
  .use(require("@soda/friendly-errors-webpack-plugin"), [
    {
      additionalTransformers: [transformer],
      additionalFormatters: [formatter]
    }
  ]);

// 热加载的插件
webpackConfig
  .plugin("hmr")
  .use(require("webpack/lib/HotModuleReplacementPlugin"));

看到这些插件,我们可以很快地理解一些 CLI 的基本情况:
(1) public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。
(2) <link rel="preload">用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload。默认情况下,一个 Vue CLI 应用会为所有初始化渲染需要的文件自动生成 preload 提示。
(3) <link rel="prefetch">用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。默认情况下,一个 Vue CLI 应用会为所有作为 async chunk 生成的 JavaScript 文件自动生成 prefetch 提示。
(4) 任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。

上面只是介绍了一部分,大家也可以自行查看 Vue CLI 的源码来看。

# 解析(resolve)

这些选项能设置模块如何被解析,因为这里会使用到所以也介绍一下用到的:

  • resolve.extensions
    • 自动解析确定的扩展。默认值为:[".js", ".json"]
  • resolve.modules
    模块将在 resolve.modules 中指定的所有目录内搜索。
  • resolve.alias
    • 创建importrequire的别名,来确保模块引入变得更简单。如果使用 typescript 的话,我们还需要配置 tsconfig.json

我们来看看 Vue CLI 提供的默认配置:

webpackConfig.resolve.extensions // 此处为支持解析的文件名后缀
  .merge([".mjs", ".js", ".jsx", ".vue", ".json", ".wasm"])
  .end()
  .modules // 这里所有的模块,我们都在 node_modules 目录下搜索
  .add("node_modules")
  .add(api.resolve("node_modules"))
  .add(resolveLocal("node_modules"))
  .end()
  .alias // 我们能看到,在Vue CLI生成的项目里,可以直接使用 @ 映射到 src 目录下
  .set("@", api.resolve("src"))
  .set(
    "vue$",
    options.runtimeCompiler
      ? "vue/dist/vue.esm.js"
      : "vue/dist/vue.runtime.esm.js"
  );

# 在 Vue CLI 里配置 Webpack

虽然 Vue CLI 里初始化了很多 Webpack 的配置,也内置了很多 Loader、Plugin,但如果我们需要自己进行一些 Webpack 配置的时候,CLI 也开放了接口给我们使用。调整 webpack 配置有几种种方式。

(1) 最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:

// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      // 没办法,我还是需要使用jQuery
      new webpack.ProvidePlugin({
        jQuery: "jquery",
        $: "jquery"
      })
    ]
  }
};

(2) 如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象:

// vue.config.js
module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === "production") {
      // 为生产环境修改配置...
    } else {
      // 为开发环境修改配置...
    }
  }
};

(3) 在 vue.config.js 中的 chainWebpack 修改,允许我们更细粒度的控制其内部配置。例如:

// vue.config.js
module.exports = {
  filenameHashing: false,
  chainWebpack: config => {
    // 我不想要预加载的preload和prefetch
    // delete删除HTML相关的preload和prefetch webpack插件
    config.plugins.delete("preload");
    config.plugins.delete("prefetch");

    // 我想要使用typescript
    // 加个loader
    config
      .rule("ts")
      .test(/\.ts$/)
      .use("ts-loader");
  }
};

# 2.2.3 更多配置和使用方法

这里介绍一些 Vue CLI 的其他使用方法,以及一些常用的配置方式。

# 快速原型开发

前面提到,如果只是写一段简单的代码来跑 Demo,还得在本地生成个 HTML 文件然后在浏览器打开。当然,使用在线平台来写代码会更方便。那如果是 Vue 的代码呢?我们不光要生成个 Vue 文件,同时还得搭配一整套环境来测试,这个时候,我们就可以使用 Vue CLI 的快速原型开发了。例如还是这个代码:

<!-- App.vue -->
<template>
  <div>{{ message }}</div>
</template>

<script>
  export default {
    data() {
      return {
        message: "欢迎来到Vue的世界"
      };
    }
  };
</script>

使用快速原型开发,需要先额外安装一个全局的扩展(额外的意思是,请记得也全局安装脚手架npm install -g @vue/cli噢):

npm install -g @vue/cli-service-global

然后在目录下可以使用vue servevue build命令对单个*.vue 文件进行快速原型开发。这里我们在 App.vue 目录文件下运行vue serve App.vue:

image
图 2-6 Vue CLI 启动快速原型开发

然后我们打开http://localhost:8080/或者http://10.40.52.42:8080/就能看到运行效果了:

image 图 2-7 Vue CLI 启动快速原型开发效果

如果要直接部署使用,可以执行vue serve App.vue将目标文件构建成一个生产环境的包,我们最终能得到这样的代码:

image
图 2-8 Vue CLI 快速原型开发代码构建

vue servevue build只适用于快速原型开发,如果需要,你还可以提供一个 index.html、package.json、安装并使用本地依赖、甚至通过相应的配置文件配置 Babel、PostCSS 和 ESLint。更多的使用大家可以去官网查看,这里就不多说了。

# 其他配置

除了前面讲到的一些 Webpack 配置,Loader、Plugins 相关的,我们还有一些可能会用到的其他配置。其实更多的可能是比较基础的配置:

module.exports = {
  publicPath: "/", // 部署应用包时的基本 URL
  outputDir: "dist", // 当运行 vue-cli-service build 时生成的生产环境构建文件的目录
  assetsDir: "", // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录
  indexPath: "index.html", // 指定生成的 index.html 的输出路径 (相对于 outputDir)
  filenameHashing: true, // 默认情况下,生成的静态资源在它们的文件名中包含了 hash 以便更好的控制缓存
  pages: undefined, // 在 multi-page 模式下构建应用
  lintOnSave: true, // 是否在开发环境下通过 eslint-loader 在每次保存时 lint 代码

  // 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中
  // 如果这个值是一个函数,则会接收被解析的配置作为参数。该函数及可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本
  configureWebpack: undefined,

  // 是一个函数
  // 会接收一个基于 webpack-chain 的 ChainableConfig 实例。允许对内部的 webpack 配置进行更细粒度的修改
  chainWebpack: undefined,

  devServer: undefined // 所有 webpack-dev-server 的选项都支持
};

目前比较常用的大概就是上面这些了,关于多页应用的会在《第14章 实战:使用 Webpack 或 Vue CLI 搭建多页应用》介绍,更多的大家可以自行探索使用方式。

在很久以前,Vue CLI 还会把内置的 webpack 配置暴露在开发者的项目中,现在已经通过内置的方式来提供很多的通用和基础能力,开发者可以直接使用而无需进行很多的配置。与此同时,Vue CLI 还提供了很多的配置给到开发者进行灵活配置,真的特别棒。