vue开始

vue 开始

vue是前端开发的一个流行框架。

安装vue

# 用nvm管理node版本
nvm ls-remote
nvm list

//当前比较新的lts版本的node
nvm install v18.16.0
nvm use v18.16.0


cd xxx

npm init vue@latest

#参考 https://vuejs.org/guide/quick-start.html#creating-a-vue-application

cd vue-project
npm install

# 查看效果
npm run dev

# 开启http服务器
# npm run serve
# 或者
# npx serve

vue介绍

https://vuejs.org/guide/introduction.html

vue两种风格: Options API 和 Composition API.

vue2基本使用options风格,vue3两种都可以。

API Styles

Vue components can be authored in two different API styles: Options API and Composition API. Vue 组件可以用两种不同的 API 样式编写:Options API 和 Composition API。

Options API

With Options API, we define a component’s logic using an object of options such as data, methods, and mounted. Properties defined by options are exposed on this inside functions, which points to the component instance: 通过 Options API,我们使用 datamethodsmounted 等选项对象定义组件的逻辑。选项定义的属性暴露在 this 内部函数中,它指向组件实例:

vue

<script>
export default {
  // Properties returned from data() become reactive state
  // and will be exposed on `this`.
  data() {
    return {
      count: 0
    }
  },

  // Methods are functions that mutate state and trigger updates.
  // They can be bound as event listeners in templates.
  methods: {
    increment() {
      this.count++
    }
  },

  // Lifecycle hooks are called at different stages
  // of a component's lifecycle.
  // This function will be called when the component is mounted.
  mounted() {
    console.log(`The initial count is ${this.count}.`)
  }
}
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

Try it in the Playground 在操场上试试

Composition API

With Composition API, we define a component’s logic using imported API functions. In SFCs, Composition API is typically used with <script setup>. The setup attribute is a hint that makes Vue perform compile-time transforms that allow us to use Composition API with less boilerplate. For example, imports and top-level variables / functions declared in <script setup> are directly usable in the template. 通过 Composition API,我们使用导入的 API 函数定义组件的逻辑。在 SFC 中,组合 API 通常与 <script setup> 一起使用。 setup 属性是一个提示,它使 Vue 执行编译时转换,使我们能够使用更少样板的组合 API。例如, <script setup> 中声明的导入和顶级变量/函数可直接在模板中使用。

Here is the same component, with the exact same template, but using Composition API and <script setup> instead: 这是相同的组件,具有完全相同的模板,但使用 Composition API 和 <script setup> 代替:

vue

<script setup>
import { ref, onMounted } from 'vue'

// reactive state
const count = ref(0)

// functions that mutate state and trigger updates
function increment() {
  count.value++
}

// lifecycle hooks
onMounted(() => {
  console.log(`The initial count is ${count.value}.`)
})
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

Try it in the Playground 在操场上试试

Which to Choose? 选择哪个?

Both API styles are fully capable of covering common use cases. They are different interfaces powered by the exact same underlying system. In fact, the Options API is implemented on top of the Composition API! 两种 API 样式都完全能够涵盖常见用例。它们是由完全相同的底层系统提供支持的不同接口。事实上,Options API 是在 Composition API 之上实现的! The fundamental concepts and knowledge about Vue are shared across the two styles. 这两种风格共享有关 Vue 的基本概念和知识。

The Options API is centered around the concept of a “component instance” (this as seen in the example), which typically aligns better with a class-based mental model for users coming from OOP language backgrounds. It is also more beginner-friendly by abstracting away the reactivity details and enforcing code organization via option groups. Options API 以“组件实例”(如示例中所示的 this )的概念为中心,对于来自 OOP 语言背景的用户,它通常更符合基于类的心智模型。通过抽象掉反应性细节并通过选项组强制代码组织,它也对初学者更加友好。

The Composition API is centered around declaring reactive state variables directly in a function scope and composing state from multiple functions together to handle complexity. Composition API 的核心是直接在函数范围内声明反应状态变量,并将多个函数的状态组合在一起以处理复杂性。 It is more free-form and requires an understanding of how reactivity works in Vue to be used effectively. In return, its flexibility enables more powerful patterns for organizing and reusing logic. 它的形式更自由,需要了解 Vue 中反应性的工作原理才能有效使用。作为回报,它的灵活性支持更强大的模式来组织和重用逻辑。

You can learn more about the comparison between the two styles and the potential benefits of Composition API in the Composition API FAQ. 您可以在 Composition API FAQ 中了解有关两种样式之间的比较以及 Composition API 的潜在优势的更多信息。

If you are new to Vue, here’s our general recommendation: 如果您是 Vue 新手,这是我们的一般建议:

  • For learning purposes, go with the style that looks easier to understand to you. Again, most of the core concepts are shared between the two styles. You can always pick up the other style later. 出于学习目的,请选择您看起来更容易理解的样式。同样,大多数核心概念在两种风格之间是共享的。您以后可以随时选择其他样式。
  • For production use: 生产用途:
    • Go with Options API if you are not using build tools, or plan to use Vue primarily in low-complexity scenarios, e.g. progressive enhancement. 如果您不使用构建工具,或者计划主要在低复杂度场景中使用 Vue,请使用 Options API,例如渐进增强。
    • Go with Composition API + Single-File Components if you plan to build full applications with Vue. 如果您打算使用 Vue 构建完整的应用程序,请使用 Composition API + 单文件组件。

You don’t have to commit to only one style during the learning phase. The rest of the documentation will provide code samples in both styles where applicable, and you can toggle between them at any time using the API Preference switches at the top of the left sidebar. 在学习阶段,您不必只专注于一种风格。文档的其余部分将在适用的情况下提供两种样式的代码示例,您可以随时使用左侧边栏顶部的 API 首选项开关在它们之间切换。

Quick Start:vue基本使用(options API)

启动demo

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

src/main.js

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

在这里向dom挂载(绑定)vue组件

App.vue,可以自己写其他sfc (single file component, 在单个文件中聚合用到的html, js, css。一般是.vue后缀的文件)

<script>
export default {
  // Properties returned from data() become reactive state
  // and will be exposed on `this`.
  data() {
    return {
      count: 0
    }
  },

  // Methods are functions that mutate state and trigger updates.
  // They can be bound as event listeners in templates.
  methods: {
    increment() {
      this.count++
    }
  },

  // Lifecycle hooks are called at different stages
  // of a component's lifecycle.
  // This function will be called when the component is mounted.
  mounted() {
    console.log(`The initial count is ${this.count}.`)
  }
}
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

使用v-bind:attr="xxx"

vue组件中的data中变量xxx的更新,反映到dom元素的attr属性上。

简写: :attr="xxx"

使用v-on:event="xxx"

dom元素上event事件发生时,调用vue组件methods中的xxx方法。

简写: @event="xxx"

对form元素使用v-model="xxx"

在form的元素上vue和dom的双向绑定, 比如input,类似于v-bind和v-on的结合。

使用v-if="xxx"

条件渲染。

根据vue组件中data中xxx的值来显示或删除组件。

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else-if="justsoso">Just so so!</h1>
<h1 v-else>Oh no 😢</h1>

列表元素渲染

https://vuejs.org/tutorial/#step-7

<script>
// give each todo a unique id
let id = 0

export default {
  data() {
    return {
      newTodo: '',
      todos: [
        { id: id++, text: 'Learn HTML' },
        { id: id++, text: 'Learn JavaScript' },
        { id: id++, text: 'Learn Vue' }
      ]
    }
  },
  methods: {
    addTodo() {
      this.todos.push({ id: id++, text: this.newTodo })
      this.newTodo = ''
    },
    removeTodo(todo) {
      this.todos = this.todos.filter((t) => t !== todo)
    }
  }
}
</script>

<template>
  <form @submit.prevent="addTodo">
    <input v-model="newTodo">
    <button>Add Todo</button>    
  </form>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.text }}
      <button @click="removeTodo(todo)">X</button>
    </li>
  </ul>
</template>

使用Computed Property计算属性

https://vuejs.org/tutorial/#step-8

<script>
let id = 0

export default {
  data() {
    return {
      newTodo: '',
      hideCompleted: false,
      todos: [
        { id: id++, text: 'Learn HTML', done: true },
        { id: id++, text: 'Learn JavaScript', done: true },
        { id: id++, text: 'Learn Vue', done: false }
      ]
    }
  },
  computed: {
    filteredTodos() {
      return this.hideCompleted
        ? this.todos.filter((t) => !t.done)
        : this.todos
    }
  },
  methods: {
    addTodo() {
      this.todos.push({ id: id++, text: this.newTodo, done: false })
      this.newTodo = ''
    },
    removeTodo(todo) {
      this.todos = this.todos.filter((t) => t !== todo)
    }
  }
}
</script>

<template>
  <form @submit.prevent="addTodo">
    <input v-model="newTodo">
    <button>Add Todo</button>
  </form>
  <ul>
    <li v-for="todo in filteredTodos" :key="todo.id">
      <input type="checkbox" v-model="todo.done">
      <span :class="{ done: todo.done }">{{ todo.text }}</span>
      <button @click="removeTodo(todo)">X</button>
    </li>
  </ul>
  <button @click="hideCompleted = !hideCompleted">
    {{ hideCompleted ? 'Show all' : 'Hide completed' }}
  </button>
</template>

<style>
.done {
  text-decoration: line-through;
}
</style>

生命周期和模板引用 Lifecycle and Template Refs

https://vuejs.org/tutorial/#step-9

https://vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram

生命周期图

So far, Vue has been handling all the DOM updates for us, thanks to reactivity and declarative rendering. However, inevitably there will be cases where we need to manually work with the DOM. 到目前为止,由于反应性和声明式渲染,Vue 一直在为我们处理所有 DOM 更新。然而,不可避免地会出现我们需要手动操作 DOM 的情况。

We can request a template ref – i.e. a reference to an element in the template – using the special ref attribute: 我们可以使用特殊的 ref 属性来请求模板引用——即对模板中元素的引用:

<p ref="p">hello</p>

演示在mount期间修改dom元素(模板元素)

<script>
export default {
  mounted() {
    this.$refs.p.textContent = 'mounted!'
  }
}
</script>
<script>
export default {
  data() {
    return {
      todoId: 1,
      todoData: null
    }
  },
  methods: {
    async fetchData() {
      this.todoData = null
      const res = await fetch(
        `https://jsonplaceholder.typicode.com/todos/${this.todoId}`
      )
      this.todoData = await res.json()
    }
  },
  mounted() {
    this.fetchData()
  },
  watch: {
    todoId() {
      this.fetchData()
    }
  }
}
</script>

<template>
  <p>Todo id: {{ todoId }}</p>
  <button @click="todoId++">Fetch next todo</button>
  <p v-if="!todoData">Loading...</p>
  <pre v-else>{{ todoData }}</pre>
</template>
<template>
  <p ref="p">hello</p>
</template>

使用watch监听变量变更

https://vuejs.org/tutorial/#step-10

<script>
export default {
  data() {
    return {
      todoId: 1,
      todoData: null
    }
  },
  methods: {
    async fetchData() {
      this.todoData = null
      const res = await fetch(
        `https://jsonplaceholder.typicode.com/todos/${this.todoId}`
      )
      this.todoData = await res.json()
    }
  },
  mounted() {
    this.fetchData()
  },
  watch: {
    todoId() {
      this.fetchData()
    }
  }
}
</script>

<template>
  <p>Todo id: {{ todoId }}</p>
  <button @click="todoId++">Fetch next todo</button>
  <p v-if="!todoData">Loading...</p>
  <pre v-else>{{ todoData }}</pre>
</template>

组件嵌套

https://vuejs.org/tutorial/#step-11

ChildComp.vue

<template>
  <h2>A Child Component!</h2>
</template>

App.vue

<script>
import ChildComp from './ChildComp.vue'

export default {
  components: {
    ChildComp
  }
}
</script>

<template>
  <ChildComp />
</template>

父组件向子组件传递输入:子组件使用Props接收,父组件使用v-bind传递

https://vuejs.org/tutorial/#step-12

ChildComp.vue

<script>
export default {
  props: {
    msg: String
  }
}
</script>

<template>
  <h2>{{ msg || 'No props passed yet' }}</h2>
</template>

App.vue

<script>
import ChildComp from './ChildComp.vue'

export default {
  components: {
    ChildComp
  },
  data() {
    return {
      greeting: 'Hello from parent'
    }
  }
}
</script>

<template>
  <ChildComp :msg="greeting" />
</template>

子组件向父组件发送事件:子组件用emits声明,用this.$emit*()发送,父组件用v-on接收

https://vuejs.org/tutorial/#step-13

In addition to receiving props, a child component can also emit events to the parent: 除了接收 props 之外,子组件还可以向父组件发送事件:

js

export default {
  // declare emitted events
  emits: ['response'],
  created() {
    // emit with argument
    this.$emit('response', 'hello from child')
  }
}

The first argument to this.$emit() is the event name. Any additional arguments are passed on to the event listener. this.$emit() 的第一个参数是事件名称。任何附加参数都会传递给事件侦听器。

The parent can listen to child-emitted events using v-on – here the handler receives the extra argument from the child emit call and assigns it to local state: 父级可以使用 v-on 监听子级发出的事件——这里的处理程序从子级发出调用接收额外的参数并将其分配给本地状态:

template

<ChildComp @response="(msg) => childMsg = msg" />

Now try it yourself in the editor. 现在在编辑器中自己尝试。

ChildComp.vue

<script>
export default {
  emits: ['response'],
  created() {
    this.$emit('response', 'hello from child')
  }
}
</script>

<template>
  <h2>Child component</h2>
</template>

App.vue

<script>
import ChildComp from './ChildComp.vue'

export default {
  components: {
    ChildComp
  },
  data() {
    return {
      childMsg: 'No child msg yet'
    }
  }
}
</script>

<template>
  <ChildComp @response="(msg) => childMsg = msg" />
  <p>{{ childMsg }}</p>
</template>

父组件向子组件传递输入:子组件使用<slot></slot>,父组件直接在子组件标签对内写值

https://vuejs.org/tutorial/#step-14

ChildComp.vue

<template>
  <slot>Fallback content</slot>
</template>

App.vue

<script>
import ChildComp from './ChildComp.vue'

export default {
  components: {
    ChildComp
  },
  data() {
    return {
      msg: 'from parent'
    }
  }
}
</script>

<template>
  <ChildComp>Message: {{ msg }}</ChildComp>
</template>

进一步学习

https://vuejs.org/tutorial/#step-15


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注