单文件组件

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

<!-- Vue 只能包含以下三种标签 -->

<!-- 组件的结构 -->
<template>
  <!-- ... -->
</template>

<!-- 组件交互相关的代码(数据、方法等等) -->
<script>
  export default {
    // ...
  };
</script>

<!-- 组件的样式 -->
<style>
  /* ... */
</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种注释类型:

      <!-- 注释内容 -->
      // 注释内容
      /* 注释内容 */
      

      也就是说,<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安装命令如下:

npm install -g @vue/cli

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

vue -V

使用 Vue CLI

使用Vue CLI创建一个项目:

vue create vue_demo

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

vue create

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

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

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

🎉  Successfully created project vue_demo.
👉  Get started with the following commands:

 $ cd vue_demo
 $ npm run serve

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

cd vue_demo
npm run serve

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

项目结构

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

vue_demo
├── babel.config.js
├── dist
├── .git
├── .gitignore
├── jsconfig.json
├── package.json
├── package-lock.json
├── public
│   ├── favicon.ico
│   └── index.html
├── README.md
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   ├── Hello.vue
│   │   └── SiteUser.vue
│   └── main.js
└── vue.config.js

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

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

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

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

    其中需要注意一下的是:

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

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

    src的结构如下所示:

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

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

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

    <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

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

    import Vue from 'vue'       // 引入 Vue
    import App from './App.vue' // 引入 App 组件
    
    /* ... */
    
    new Vue({
      render: h => h(App),
    }).$mount('#app')
    

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

    import Vue from 'vue' // 引入 Vue
    // 其实这里也可以使用完整版的 Vue,但是没必要
    
    /* ... */
    
    new Vue({
      render(createElement) {
        console.log('render.');
        console.log(typeof createElement); // function
        return createElement('h2', 'Hello!')
      },
    }).$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配置,可运行:

vue inspect > output.js

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

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

{
  /* ... */
  entry: {
    app: [
      './src/main.js' // 项目入口文件
    ]
  }
}

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

module.exports = {
  /* 选项... */
}

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

const { defineConfig } = require('@vue/cli-service')

module.exports = defineConfig({
  /* 选项... */
})

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

module.exports = {
  /* ... */
  pages: {
    index: {
      // page 的入口
      entry: 'src/main.js',
      // 模板来源
      template: 'public/index.html',
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title: 'Index Page',
    }
  }
}

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

module.exports = {
  lintOnSave: false
}

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

注:

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

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

module.exports = {
  pages: {
    index: {
      /* 空的配置内容 */
    }
  }
}

那么在启动或者构建项目的时候就会发生错误。因为此时的配置中的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

    <template>
      <div class="my-site">
        <h2>网站名称:{{name}}</h2>
        <h2>网站地址:{{url}}</h2>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MySite',
      data() {
        return {
          name: 'Linner\'s Blog',
          url: 'blog.linner.asia',
        }
      },
    };
    </script>
    
    <style>
      .my-site {
        background-color: yellow;
      }
    </style>
    
  • SiteUser

    <template>
      <div>
        <h2>用户姓名:{{name}}</h2>
        <h2>用户年龄:{{age}}</h2>
      </div>
    </template>
    
    <script>
    export default {
      name: 'SiteUser',
      data() {
        return {
          name: '张三',
          age: 18,
        }
      },
    }
    </script>
    
  • App.vue

    <template>
      <div>
        <img src="./assets/logo.png" alt="logo">
        <MySite/>
        <hr>
        <SiteUser/>
      </div>
    </template>
    
    <script>
      // 引入组件
      import MySite from "./components/MySite.vue";
      import SiteUser from './components/SiteUser.vue';
    
      export default {
        name: 'app',
        components: {
          MySite,
          SiteUser,
        }
      }
    </script>
    

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


组件化编码流程

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