单文件组件

Vue单文件组件是指使用由Vue定义的.vue格式的文件。.vue文件模板如下:

 1<!-- Vue 只能包含以下三种标签 -->
 2
 3<!-- 组件的结构 -->
 4<template>
 5  <!-- ... -->
 6</template>
 7
 8<!-- 组件交互相关的代码(数据、方法等等) -->
 9<script>
10  export default {
11    // ...
12  };
13</script>
14
15<!-- 组件的样式 -->
16<style>
17  /* ... */
18</style>

其中:

  • <template>:用于定义组件的结构。相当于组件配置中的template配置项。

    和Html中的<template>一样,它不会影响页面最终的结构。

    由于<template>不会影响页面结构,所以在<template>中还需要定义一个元素作为整个组件的根节点。

  • <script>:定义组件的交互相关的代码。即定义创建组件的JS代码。

    <script>中可以使用Vue.extend()来创建组件。但是由于Vue会在需要的时候帮我们自动调用Vue.extend(),所以Vue.extend()可省略。一般情况下export default组件的配置对象即可

    如果要使用Vue.extend(),需要先引入Vue

  • <style>:和Html中的<style>一样,都是用于定义样式。

  • .vue注释:

    .vue中的文件有4个地方可以写注释:

    • <template>中:使用Html的注释<!-- 注释内容 -->

    • <script>中:使用JS的注释// 注释内容/* 注释内容 */

    • <style>中:使用CSS的注释/* 注释内容 */

    • 上述范围之外的其它地方:也就是在<template><script><style>之外的其它地方可以使用注释。这些注释是属于.vue文件自己的注释,一共有3种注释类型:

      1<!-- 注释内容 -->
      2// 注释内容
      3/* 注释内容 */
      

      也就是说,<template><script><style>之外的其它地方可以使用Html、JS和CSS的任何类型的注释。


Vue CLI

由于浏览器并不支持直接浏览.vue类型的文件,所以需要将.vue类型的文件编译成浏览器支持的文件类型。而Vue CLI(Vue Command Line Interface,Vue脚手架)可以帮助我们快速地编译.vue

安装 Vue CLI

Vue CLI需要使用npm安装,请确保你的电脑上已经安装了Node.js。

Vue CLI安装命令如下:

1npm install -g @vue/cli

安装完成后直接使用vue命令运行Vue CLI。查看Vue CLI版本以验证是否安装成功:

1vue -V

使用 Vue CLI

使用Vue CLI创建一个项目:

1vue create vue_demo

接着Vue CLI会让你选择一个默认的配置:

vue create

使用键盘的去移动选择,然后按下回车即可开始创建。

其中的babel是用来将ES6的代码转换为ES5。eslint是用来进行语法检查。

出现如下提示表示项目创建成功:

1🎉  Successfully created project vue_demo.
2👉  Get started with the following commands:
3
4 $ cd vue_demo
5 $ npm run serve

根据提示信息可以知道,进入vue_demo目录之后运行npm run serve可以启动这个项目:

1cd vue_demo
2npm run serve

启动成功后,项目服务的默认端口是8080,可以访问http://localhost:8080/以浏览Vue CLI为我们创建的HelloWorld这个项目示例。

项目结构

Vue CLI创建好的项目结构一般如下:

 1vue_demo
 2├── babel.config.js
 3├── dist
 4├── .git
 5├── .gitignore
 6├── jsconfig.json
 7├── package.json
 8├── package-lock.json
 9├── public
10│   ├── favicon.ico
11│   └── index.html
12├── README.md
13├── src
14│   ├── App.vue
15│   ├── assets
16│   │   └── logo.png
17│   ├── components
18│   │   ├── Hello.vue
19│   │   └── SiteUser.vue
20│   └── main.js
21└── vue.config.js

在创建好的vue_demo中,有以下这些注意点:

  • .gitignore:排除那些不需要被git所管理的文件或目录。

  • babel.config.jsbabel的配置文件。一般来说不需要修改该配置,如果需要修改,可以浏览Babel官网

  • package.json:当前(Node.js)项目的包管理配置文件。

    其中需要注意一下的是:

    1{
    2  /* ... */
    3  "scripts": {
    4    "serve": "vue-cli-service serve", // 预览项目
    5    "build": "vue-cli-service build", // 构建项目
    6    "lint": "vue-cli-service lint"    // 进行语法检查
    7  },
    8  /* ... */
    9}
    
  • READNE.md:程序说明文档。

  • src:项目源代码文件。在使用默认配置的情况下,名称不可随意更改。

    src的结构如下所示:

    • main.js:默认的项目入口文件,主要用来创建Vue实例。在使用默认配置的情况下,名称不可随意更改。
    • App.vue:项目中所有组件的根组件。
    • assets:存放项目的静态资源。
    • components:存放项目中(除了App.vue)组件的文件夹。
  • public:存放index.htmlfavicon.ico等文件。在使用默认配置的情况下,名称不可随意更改。

    public/index.html是当前项目默认打开的页面。在使用默认配置的情况下,名称不可随意更改。

    index.html中,有一个最重要的东西:

    1<div id="app"></div>
    
  • vue.config.js:Vue CLI的配置文件(可选),与package.json同级。

Vue CLI 入口文件

main.js中的import Vue from 'vue'render: h => h(App)语句需要格外注意:

main.js中,使用ES6引入的Vue(即import Vue from 'vue'),是vue.runtime.esm.js

vue.runtime.esm.js是一个精简版的Vue,其中去除了原本Vue中的模板解析器。所以在main.js的Vue实例中,使用template配置项会因为没有模板解析器而导致无法解析。

有两种方法可以解决该问题:

  • 引入完整版的vue.js

    1import Vue from 'vue/dist/vue'  // 引入 Vue
    2import App from './App.vue'     // 引入 App 组件
    3
    4/* ... */
    5
    6new Vue({
    7  components: {App},
    8  template: `<App>Hello!</App>`,
    9}).$mount('#app')
    
  • 使用render配置项:

    1import Vue from 'vue'       // 引入 Vue
    2import App from './App.vue' // 引入 App 组件
    3
    4/* ... */
    5
    6new Vue({
    7  render: h => h(App),
    8}).$mount('#app')
    

    render本质是Vue调用的一个函数,它需要接收一个参数createElement这个参数是function类型。createElement()可以帮助我们在页面中创建并渲染元素。例如:

     1import Vue from 'vue' // 引入 Vue
     2// 其实这里也可以使用完整版的 Vue,但是没必要
     3
     4/* ... */
     5
     6new Vue({
     7  render(createElement) {
     8    console.log('render.');
     9    console.log(typeof createElement); // function
    10    return createElement('h2', 'Hello!')
    11  },
    12}).$mount('#app')
    

    由于render中不需要使用this,所以可以使用Lambda表达式() => {}来定义。如上方的解决方案中h => h(App)就是render最简洁的定义方式。

    使用了render就无需使用components

注:在.vue中依旧是使用<template>components。也就是说,实例无需使用render

Vue CLI 配置文件

Vue CLI默认的入口文件就是main.js。并且Vue CLI隐藏了所有webpack相关的配置,若想查看具体的webpack配置,可运行:

1vue inspect > output.js

将Vue CLI的webpack相关配置输出到output.js这个文件中(仅用作输出浏览,无法在此更改配置)。

在Vue CLI的webpack相关配置中有这样的内容:

1{
2  /* ... */
3  entry: {
4    app: [
5      './src/main.js' // 项目入口文件
6    ]
7  }
8}

要修改这些配置内容,需要在vue.config.js中进行修改。它的基本形式如(使用的是common.js的模块化语法):

1module.exports = {
2  /* 选项... */
3}

或使用@vue/cli-service提供的defineConfig()帮手函数:

1const { defineConfig } = require('@vue/cli-service')
2
3module.exports = defineConfig({
4  /* 选项... */
5})

vue.config.js中有一个pages配置项(具体内容可在官方文档中复制):

 1module.exports = {
 2  /* ... */
 3  pages: {
 4    index: {
 5      // page 的入口
 6      entry: 'src/main.js',
 7      // 模板来源
 8      template: 'public/index.html',
 9      // 在 dist/index.html 的输出
10      filename: 'index.html',
11      // 当使用 title 选项时,
12      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
13      title: 'Index Page',
14    }
15  }
16}

除了pages配置选项,还有用于关闭语法检查的lintOnSave

1module.exports = {
2  lintOnSave: false
3}

vue.config.js的配置选项可参考 Vue CLI 配置参考

注:

vue.config.js被修改之后,需要重新运行npm run serve

如果在vue.config.js中类似这样配置:

1module.exports = {
2  pages: {
3    index: {
4      /* 空的配置内容 */
5    }
6  }
7}

那么在启动或者构建项目的时候就会发生错误。因为此时的配置中的pages.index传递的是一个没有任何属性的对象,而Vue CLI并不会去解析它并为他配上默认的内容。

所以在使用vue.config.js的时候,要么干脆不添加配置选项,使用默认的内容;要么就要按照配置选项的内容进行正确的配置。

Vue 包

Vue的依赖包在node_modules/vue/dist中。其中有这么些版本:

  • .esm:包含.esm的文件代表使用了ES6的模块化语法。使用ES6的模块化语法的版本,浏览器无法直接使用。

  • .min:包含.min就是指压缩版本。

  • 完整版:

    • vue.js:浏览器可以直接使用的完整版本。
    • vue.esm.js:使用了ES6的模块化语法的完整版。
  • runtime:运行时版本,是Vue的精简版本,去除了模板解析器。

    • vue.runtime.js
    • vue.runtime.min.js
    • vue.runtime.esm.js

    runtime版本不能使用template配置项。需要使用render函数接收到的createElement函数去指定具体内容。

  • common:使用common.js时使用的版本。

    • vue.common.js
    • vue.common.dev.js
    • vue.common.prod.js
    • vue.runtime.common.dev.js
    • vue.runtime.common.js
    • vue.runtime.common.prod.js

Vue CLI中<template>标签配置的模板,是由vue-template-compiler(Vue 模板解析器)来解析。这个vue-template-compiler只能解析.vue中的<template>模板,而不能解析template配置项。

示例

根据以上步骤和信息,将非单文件组件中的siteuser定义为单文件组件的形式:

PS:由于使用的Vue CLI版本为5,默认开启语法检查。根据ESLint的官方代码规范,除了App的其它组件只能以multi-word(多单词)的形式命名。所以下方组件的命名遵循ESLint规范。

multi-word形式选择kebab-caseCameCase都可以。

  • MySite

     1<template>
     2  <div class="my-site">
     3    <h2>网站名称:{{name}}</h2>
     4    <h2>网站地址:{{url}}</h2>
     5  </div>
     6</template>
     7
     8<script>
     9export default {
    10  name: 'MySite',
    11  data() {
    12    return {
    13      name: 'Linner\'s Blog',
    14      url: 'blog.linner.asia',
    15    }
    16  },
    17};
    18</script>
    19
    20<style>
    21  .my-site {
    22    background-color: yellow;
    23  }
    24</style>
    
  • SiteUser

     1<template>
     2  <div>
     3    <h2>用户姓名:{{name}}</h2>
     4    <h2>用户年龄:{{age}}</h2>
     5  </div>
     6</template>
     7
     8<script>
     9export default {
    10  name: 'SiteUser',
    11  data() {
    12    return {
    13      name: '张三',
    14      age: 18,
    15    }
    16  },
    17}
    18</script>
    
  • App.vue

     1<template>
     2  <div>
     3    <img src="./assets/logo.png" alt="logo">
     4    <MySite/>
     5    <hr>
     6    <SiteUser/>
     7  </div>
     8</template>
     9
    10<script>
    11  // 引入组件
    12  import MySite from "./components/MySite.vue";
    13  import SiteUser from './components/SiteUser.vue';
    14
    15  export default {
    16    name: 'app',
    17    components: {
    18      MySite,
    19      SiteUser,
    20    }
    21  }
    22</script>
    

其它文件暂时不用改动。然后在项目目录下运行npm run serve即可预览。


组件化编码流程

  1. 拆分静态组件:组件按功能点拆分,命名不要与Html元素冲突。
  2. 实现动态组件:根据数据被使用的范围,考虑好数据的存放位置。
  • 数据只有一个组件在使用:放在组件中即可。
  • 数据被一部分组件共同使用:放在这些组件的父组件上(这种方法被称为状态提升,Vue中的数据也可被称为状态)。
  1. 实现交互:从绑定事件开始。