Vue Loader 篇(上):基于 Vue CLI 初始化原型项目


Vue Loader 概述

上篇教程中,我们提到,如果所有的组件代码都在单个 HTML 文档或者 JavaScript 文件中编写,会导致代码的可读性和可维护性很差,为此,Vue 框架引入了 Vue Loader,我们可以通过它实现单文件 Vue 组件。

Vue Loader 是一个 Webpack loader,因此,使用它之前,需要安装相关的依赖包并手动配置 Webpack 支持 Vue Loader,以便编译打包 Vue 组件文件到相应的 JavaScript 文件(相关安装配置细节请参考 Vue Loader 官方文档)。

如果你对 Webpack 不太了解,或者不想要手动去安装配置,可以基于 Vue.js 框架提供的 Vue CLI 直接创建一个 Vue 原型项目。

通过 Vue CLI 创建的原型项目会针对大多数常见的开发需求进行预先 Webpack 配置,这对于我们学习或者快速入门单文件 Vue 组件开发完全够用了(如果使用 Laravel 框架进行全栈开发的话,Laravel Mix 也是一个封装了大多数 Webpack 常用配置的依赖包,所以在 Laravel 项目中单文件 Vue 组件也是开箱可用的),下面我们就基于 Vue CLI 来创建一个原型项目,并演示单文件 Vue 组件的编写。

通过 Vue CLI 初始化原型项目

使用 Vue CLI 之前,先要通过 NPM 或者 Yarn 全局安装它:

npm install -g @vue/cli

然后,在任意目录下通过如下命令初始化一个 Vue 原型项目(这里,我们选择在 vue_learning 目录下执行):

vue create demo-project

此时会出现一个 Preset 选项(包含预定义选项和插件),我们直接回车选择默认的 Vue 2:

-w791

安装完成后,通过 cd demo-project 进入演示项目,然后运行 npm run serve 启动这个项目:

-w838

这样,我们就可以在浏览器中通过 http://localhost:8080 访问这个 Vue 项目了:

-w811

Vue 原型项目目录结构

我们来大致看下这个演示项目的目录结构:

-w537

该项目的 HTML 入口文件是 public/index.html(关于该文档中 <%=%> 语法的说明参考 Vue CLI 官方文档,这里不详细展开了):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

对应的 JavaScript 入口文件是 src/main.js

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

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

可以看到,Vue 全局对象实例通过 $mount 函数挂载到了 HTML 视图的 div#app 元素上,其效果和之前通过 el:'#app' 属性设置实现挂载完全一致。这种挂载方式的好处是可以先定义 Vue 对象实例,再挂载到某个 HTML 元素上。

注:HTML 文档中并没有显示引入任何 JavaScript 文件,这一切都是 Vue CLI 底层自动完成的,index.html 会被当作模板进行处理,相应的 CSS、图标、JavaScript 文件资源也会自动注入进去。

至于为什么使用这种方式挂载,是因为 Vue 对象实例中使用了 render 函数的缘故,该函数是之前通过 template 属性定义 Vue 组件字符串模板的替代方案,通过该函数可以直接基于 JavaScript 构建虚拟 DOM,而不再需要将字符串模板进行转化,无论从功能还是性能角度来说都更加强大,这里的 hcreateElement 函数的简写(关于 render 函数的使用我们后面还会单独介绍)。

如果在 Vue 对象实例中定义了 render 函数,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出 HTML 模板编译渲染函数,所以这里需要通过 $mount 函数去手动挂载。

这里我们传入了一个 App.vue 组件到 h 函数作为整个 Vue 应用组件树的根组件,然后把其他功能组件注册到 App.vue 中,并以此来完成整个 Vue 组件树的渲染:

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

比如用于显示演示项目首页的 HelloWorld 组件,这里也可以看到,注册组件是通过 components 属性,之前我们使用的是 Vue.component 函数,二者的区别是前者是局部注册(注册到指定父组件),后者是全局注册(整个 Vue 全局对象实例作用域内有效)。

这里的 App.vue 或者 HelloWorld.vue 都是单文件 Vue 组件,单文件 Vue 组件以 .vue 后缀结尾,可以通过普通组件注册的方式注册到指定父级作用域,并且包含以下结构:

  • <template>...</template>:用于定义渲染组件的 HTML 字符串模板,和之前定义的 Vue 组件模板一样,只能包含一个根 HTML 元素(可以对应到之前定义组件对象的 template 属性);
  • <script>...</script>:用于定义 Vue 组件的 JavaScript 代码,通过 export default 返回该 Vue 组件的对象实例,其中可以包含前面通过 Vue.component 定义 Vue 组件对象定义的除 template 属性外的所有其他属性和函数,还有一点不同的是这里的 Vue 组件名通过 name 属性显示设置;
  • <style>...</style>:用于定义当前 Vue 组件模板元素的 CSS 样式(如果 style 标签设置了 scoped 属性,则意味着 CSS 样式只能在当前组件被渲染的时候生效)。

文字看起来可能有点懵,接下来我们通过单文件 Vue 组件来重构上篇教程编写的模态框组件进行演示。


点赞 取消点赞 收藏 取消收藏

<< 上一篇: Vue 组件插槽:父子组件间的内容分发和插槽作用域

>> 下一篇: Vue Loader 篇(下):编写一个单文件 Vue 组件