美文网首页
vuejs 极简入门

vuejs 极简入门

作者: SlowGO | 来源:发表于2020-04-05 19:32 被阅读0次

hello world

<!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" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <title>Vue App</title>
</head>

<body>
<div id="app">{{message}}</div>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            message: "hello world"
        }
    });
</script>
</body>
</html>

dev tool

Chrome vue devtool

安装完插件后,如果页面中使用了 vue,插件图标就会变亮,打开开发工具即可看到 vue 的标签。

image

vue CLI

安装

# install with npm
npm i -g @vue/cli @vue/cli-service-global

# install with yarn
yarn global add @vue/cli @vue/cli-service-global

这样是全局安装 vue,在任何目录下都可以使用 vue 命令。

创建 vue 项目:

vue create vue-app

执行完成后,会有提示:

image

按提示执行这2个命令:

cd vue-app
npm run serve

执行完成后:

image image

生成的项目文件目录结构:

image

重要文件:

  • public/index.html - 主页面
  • src/main.js - 主入口
  • src/App.vue - 最基础的 vue 组件
  • src/components/HelloWorld.vue - 业务组件

main.js 加载 App.vueApp.vue 导入 HelloWorld.vuemain.jsApp 组件内容渲染到 index.html 中的 <div id="app"></div>

Vue 组件文件的结构

vue 组件文件使用 .vue 后缀,内容总会包含3部分:

  • <template>

  • <script>

  • <style>

形式如下:

<template></template>

<script>
  export default {
    name: 'component-name',
  }
</script>

<style scoped></style>

和我们以前开发的思路不同,以前我们会把 HTML、JS、CSS 的内容都分开放入不同的文件,而 vue 组件是把3者放在一起,这样是为了便于维护。

继续学习 vue 之前,先引入一个 CSS 样式库,在 index.html 中添加:

<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css" />

创建一个 vue 组件

src/components/EmployeeTable.vue

<template>
  <div id="employee-table">
    <table>
      <thead>
        <tr>
          <th>Employee name</th>
          <th>Employee email</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Richard Hendricks</td>
          <td>richard@piedpiper.com</td>
        </tr>
        <tr>
          <td>Bertram Gilfoyle</td>
          <td>gilfoyle@piedpiper.com</td>
        </tr>
        <tr>
          <td>Dinesh Chugtai</td>
          <td>dinesh@piedpiper.com</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
  export default {
    name: 'employee-table',
  }
</script>

<style scoped></style>

Vue 中的惯例是文件名和导入将使用PascalCase,例如EmployeeTable,但是在模板中,将转换为 <employee-table>

App.vue 中引入 EmployeeTable

<template>
  <div id="app" class="small-container">
    <h1>Employees</h1>

    <employee-table />
  </div>
</template>

<script>
  import EmployeeTable from '@/components/EmployeeTable/EmployeeTable.vue'

  export default {
    name: 'app',
    components: {
      EmployeeTable,
    },
  }
</script>

<style>
  button {
    background: #009435;
    border: 1px solid #009435;
  }

  .small-container {
    max-width: 680px;
  }
</style>

页面效果:

image

Vue 组件动态数据

组件改造

EmployeeTable.vue 中暴露一个属性:

export default {
  name: 'employee-table',
  props: {
    employees: Array,
  },
}

EmployeeTable.vue 模板中循环显示数据:

<template>
  <div id="employee-table">
    <table>
      <!-- ...thead... -->
      <tbody>
        <tr v-for="employee in employees" :key="employee.id">
          <td>{{ employee.name }}</td>
          <td>{{ employee.email }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

App 中引用组件

设置数据:

  export default {
    name: 'app',
    components: {
      EmployeeTable,
    },
    data() {
      return {
        employees: [
          {
            id: 1,
            name: 'Richard Hendricks',
            email: 'richard@piedpiper.com',
          },
          {
            id: 2,
            name: 'Bertram Gilfoyle',
            email: 'gilfoyle@piedpiper.com',
          },
          {
            id: 3,
            name: 'Dinesh Chugtai',
            email: 'dinesh@piedpiper.com',
          },
        ],
      }
    },
  }

引用组件时设置属性:

<employee-table :employees="employees" />

页面效果

image

表单

创建组件 src/components/EmployeeForm.vue,使用表单添加 employee 数据

<template>
  <div id="employee-form">
    <form>
      <label>Employee name</label>
      <input type="text" />
      <label>Employee Email</label>
      <input type="text" />
      <button>Add Employee</button>
    </form>
  </div>
</template>

<script>
  export default {
    name: 'employee-form',
    data() {
      return {
        employee: {
          name: '',
          email: '',
        },
      }
    },
  }
</script>

<style scoped>
  form {
    margin-bottom: 2rem;
  }
</style>

App 中引入此组件

<template>
  <div id="app" class="small-container">
    <h1>Employees</h1>

    <employee-form />
    <employee-table :employees="employees"/>
  </div>
</template>

<script>
  import EmployeeTable from '@/components/EmployeeTable.vue'
  import EmployeeForm from '@/components/EmployeeForm.vue'

  export default {
    name: 'app',
    components: {
      EmployeeTable,
      EmployeeForm,
    },
    data: {
      // ...
    }
  }
</script>

页面效果:

image

表单中填入的数据如何拿到呢?

需要使用v-model绑定表单输入框与组件中定义的变量:

<template>
    <div id="employee-form">
        <form>
            <label>Employee name</label>
            <input v-model="employee.name" type="text" />
            <label>Employee Email</label>
            <input v-model="employee.email" type="text" />
            <button>Add Employee</button>
        </form>
    </div>
</template>

使用 vue devtool 就可以看到效果,输入框中输入内容后,可以看到组件数据同步变化:

image

接下来需要提交表单,把输入的内容放入列表中。

事件监听

EmployeeForm.vue 中为 form 绑定提交处理方法:

<form @submit.prevent="handleSubmit"></form>

添加处理提交的方法:

export default {
  name: 'employee-form',
  data() {
    return {
      employee: {
        name: '',
        email: '',
      },
    }
  },
  methods: {
    handleSubmit() {
      console.log('testing handleSubmit')
    },
  },
}

handleSubmit 方法中打印了一条测试信息,可以在开发工具中实验。

EmployeeForm 组件已经可以拿到提交的数据了,但如何把数据给 EmployeeTable 组件呢?

向父组件传递事件

EmployeeForm 把自己的数据传递给父组件 App

handleSubmit() {
  this.$emit('add:employee', this.employee)
}

第一个参数是事件的名称,第二个参数是要传递的数据。

页面中提交一下表单,devtool 中会看到事件的信息:

image

从子组件接收事件

EmployeeForm 已经向 App 发送了事件,那么 App 需要接收到这个事件。

首先,App 中要在 employee-form 标签中声明事件的名称以及处理方法:

<employee-form @add:employee="addEmployee" />

add:employee 是 EmployeeForm 中事件的名称,addEmployee 是处理事件的方法名。

App.vue 中添加一个方法:

methods: {
  addEmployee(employee) {
      const lastId = this.employees.length > 0
          ? this.employees[this.employees.length - 1].id : 0;
      const id = lastId + 1;
      const newEmployee = {...employee, id};
  
      this.employees = [...this.employees, newEmployee];
  }
}

效果:

image

现在基础的功能已经实现了,但还需要一些辅助的功能,例如:

  • 显示成功信息
  • 显示错误提示
  • 数据不符合规则时,input 高亮
  • 表单提交后,input 清空
  • 成功提交后,第一个 input 自动聚焦

计算的属性 Computed properties

Computed properties 是在某些变动后自动计算的函数。

这种方式可以避免我们写复杂的逻辑。

下面在 EmployeeForm.vue 中做一个简单的检查:输入框不为空。

computed: {
  invalidName() {
    return this.employee.name === ''
  },

  invalidEmail() {
    return this.employee.email === ''
  },
},

需要添加几个状态变量:

  • submitting - 表单是否正在提交的状态
  • error - 是否发生错误
  • success - 是否成功
data() {
  return {
    submitting: false,
    error: false,
    success: false,
    employee: {
      name: '',
      email: '',
    }
  }
}

提交方法中添加状态的验证逻辑:

methods: {
  handleSubmit() {
    this.submitting = true
    this.clearStatus()

    if (this.invalidName || this.invalidEmail) {
      this.error = true
      return
    }

    this.$emit('add:employee', this.employee)
    this.employee = {
      name: '',
      email: '',
    }
    this.error = false
    this.success = true
    this.submitting = false
  },

  clearStatus() {
    this.success = false
    this.error = false
  }
}

为提示信息添加样式:

<style scoped>
  form {
    margin-bottom: 2rem;
  }

  [class*='-message'] {
    font-weight: 500;
  }

  .error-message {
    color: #d33c40;
  }

  .success-message {
    color: #32a95d;
  }
</style>

修改表格:

<form @submit.prevent="handleSubmit">
  <label>Employee name</label>
  <input
    type="text"
    :class="{ 'has-error': submitting && invalidName }"
    v-model="employee.name"
    @focus="clearStatus"
    @keypress="clearStatus"
  />
  <label>Employee Email</label>
  <input
    type="text"
    :class="{ 'has-error': submitting && invalidEmail }"
    v-model="employee.email"
    @focus="clearStatus"
  />
  <p v-if="error && submitting" class="error-message">
    ❗Please fill out all required fields
  </p>
  <p v-if="success" class="success-message">
    ✅ Employee successfully added
  </p>
  <button>Add Employee</button>
</form>

效果:

image

[图片上传失败...(image-18c7f7-1586086511847)]

添加 reference

在提交表单之后,最好将焦点放回第一个条目上,可以通过 refs 实现。

在第一个 input 上添加:

<input ref="first" ... />

handleSubmit 方法中添加 focus

this.$emit('add:employee', this.employee)
this.$refs.first.focus()

效果:

image

删除列表中记录

EmployeeTable.vue 在每条记录后添加“编辑”、“删除”的按钮:

<template>
  <div id="employee-table">
    <table>
      <thead>
        <tr>
          <th>Employee name</th>
          <th>Employee email</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="employee in employees" :key="employee.id">
          <td>{{ employee.name }}</td>
          <td>{{ employee.email }}</td>
          <td>
            <button>Edit</button>
            <button>Delete</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<style scoped>
  button {
    margin: 0 0.5rem 0 0;
  }
</style>

删除按钮上添加点击事件:

<button @click="$emit('delete:employee', employee.id)">Delete</button>

点击后会发出一个事件 delete:employee,并传递数据 employee.id

App.vue 中处理此事件,首先在 employee-table 标签中绑定事件处理方法:

<employee-table :employees="employees" @delete:employee="deleteEmployee" />

然后添加处理方法:

methods: {
  ...
  deleteEmployee(id) {
    this.employees = this.employees.filter(
      employee => employee.id !== id
    )
  }
}

EmployeeTable.vue 中判断表格是否为空:

<div id="employee-table">
  <p v-if="employees.length < 1" class="empty-table">
    No employees
  </p>
  <table v-else>
    ...
  </table>
</div>

效果:

image

编辑列表中的记录

EmployeeTable.vue 中为 "编辑" 按钮添加点击事件:

<button @click="$emit('edit:employee', employee.id)">Edit</button>

App.vue 中处理事件。

绑定处理方法:

<employee-table
  :employees="employees"
  @delete:employee="deleteEmployee"
  @edit:employee="editEmployee"
/>

添加编辑方法:

editEmployee(id, updatedEmployee) {
  this.employees = this.employees.map(employee =>
    employee.id === id ? updatedEmployee : employee
  )
}

效果:

image

添加 Cancel 的处理:

editMode(employee) {
  this.cachedEmployee = Object.assign({}, employee)
  this.editing = employee.id
},
cancelEdit(employee) {
  Object.assign(employee, this.cachedEmployee)
  this.editing = null;
}

修改 Cancle 按钮:

<button class="muted-button" @click="cancelEdit(employee)">Cancel</button>

修改 Edit 按钮,去掉参数的 .id

<button @click="editMode(employee)">Edit</button>

调用 REST API

生命周期方法

把默认数据移除,使用 GET 从接口获取数据,什么时候调用 GET 呢?应该在 mounted 这个生命周期点中。

mounted告诉组件可以执行一些动作了,因为组件已经插入到 DOM 中了。

App.vue 的 mounted 中获取数据:

export default {
  name: 'app',
  components: {
    EmployeeTable,
    EmployeeForm,
  },
  data() {
    return {
      employees: [],
    }
  },

  mounted() {
    this.getEmployees()
  },
}

GET

async getEmployees() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users')
    const data = await response.json()
    this.employees = data
  } catch (error) {
    console.error(error)
  }
}

POST

async addEmployee(employee) {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      body: JSON.stringify(employee),
      headers: { 'Content-type': 'application/json; charset=UTF-8' },
    })
    const data = await response.json()
    this.employees = [...this.employees, data]
  } catch (error) {
    console.error(error)
  }
}

PUT

async editEmployee(id, updatedEmployee) {
  try {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
      method: 'PUT',
      body: JSON.stringify(updatedEmployee),
      headers: { 'Content-type': 'application/json; charset=UTF-8' },
    })
    const data = await response.json()
    this.employees = this.employees.map(employee => (employee.id === id ? data : employee))
  } catch (error) {
    console.error(error)
  }
}

DELETE

async deleteEmployee(id) {
  try {
    await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
      method: "DELETE"
    });
    this.employees = this.employees.filter(employee => employee.id !== id);
  } catch (error) {
    console.error(error);
  }
}

参考资料:

https://www.taniarascia.com/getting-started-with-vue/

相关文章

  • vuejs 极简入门

    hello world dev tool Chrome vue devtool 安装完插件后,如果页面中使用了 v...

  • 机器学习&深度学习知识体系——写过的博文(博客目录索引)

    机器学习&深度学习入门 机器学习简介 深度学习简介 深度学习入门极简教程(一) 深度学习入门极简教程(二) 深度学...

  • 我只是一只黑天鹅而已

    我有一本理财界小白的股票入门圣经——《极简股票课》。 是的,我称它为股票入门的圣经,因为对于不聪明的我来说,《极简...

  • VueJs入门

    标签(空格分隔): JavaScript框架 兼容性说明:Vue.js 不支持 IE8 及其以下版本,因为 Vue...

  • Nginx 极简教程

    Nginx 极简教程 本项目是一个 Nginx 极简教程,目的在于帮助新手快速入门 Nginx。examples ...

  • docker-springboot配置及部署

    Mac下 Docker部署SpringBoot应用 Docker与Dockerfile极简入门文档 Spring ...

  • 极简Python入门

    极简Python入门 安装 https://www.python.org/downloads/[https://w...

  • kafka极简入门(四)--常用配置

    回顾:kafka极简入门(三)--创建topic 前言 kafka针对broker, topic, produce...

  • CMake 极简入门

    本文根据《cmake实践.pdf》写的并简化一些内容 项目地址 p1 最简单的hello world 文件结构(其...

  • Docker极简入门

    一、Docker概述 Docker通过一个包括应用程序运行时所需的一切的可执行镜像启动容器,包括配置有代码、运行时...

网友评论

      本文标题:vuejs 极简入门

      本文链接:https://www.haomeiwen.com/subject/acdpphtx.html