https://cn.vuejs.org/tutorial
1 2 3 4 5 6 7 8 9 10 11 12 13 <script setup > import { reactive, ref } from 'vue' const counter = reactive ({ count : 0 })const message = ref ('Hello World!' )</script > // 在 template 中调用对象 <template > <h1 > {{ message }}</h1 > <p > Count is: {{ counter.count }}</p > </template >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script setup > import { ref } from 'vue' const titleClass = ref ('title' )</script > // 使用 v-bind 动态绑定一个对象为 attribute。 // 如 <div v-bind:id ="dynamicId" > </div > 把 div 的 id 属性动态绑定为 dynamicId <template > <h1 :class ="titleClass" > Make me red</h1 > </template > <style > .title { color : red; } </style >
这部分设涉及 CSS,todo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script setup > import { ref } from 'vue' const count = ref (0 )function increment ( ) { count.value ++ } </script > // 设置监听 DOM 事件 // v-on:click="handler" 或 @click="handler",handler 是 script 中定义的函数 // click 是最简单的点击的监听。可以用 click.xxxx.xxxx 指定更丰富的事件,以及 keyup 监听按键事件等 <template > <button @click ="increment" > Count is: {{ count }}</button > </template >
vue 提供了v-if
v-for
等逻辑类 attribute。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <script setup > import { ref } from 'vue' const awesome = ref (true )function toggle ( ) { awesome.value = !awesome.value } </script > <template > <button @click ="toggle" > Toggle</button > // 条件判断。 <h1 v-if ="awesome" > Vue is awesome!</h1 > <h1 v-else > Oh no 😢</h1 > </template >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 // todo list demo <script setup > import { ref } from 'vue' let id = 0 const newTodo = ref ('' )const todos = ref ([ { id : id++, text : 'Learn HTML' }, { id : id++, text : 'Learn JavaScript' }, { id : id++, text : 'Learn Vue' } ]) function addTodo ( ) { todos.value .push ( { id : id++, text : newTodo.value } ) newTodo.value = '' } function removeTodo (todo ) { todos.value = todos.value .filter (function (item ) { return item !== todo; }) } </script > <template > <form @submit.prevent ="addTodo" > <input v-model ="newTodo" required placeholder ="new todo" > <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 >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <script setup > import { ref, computed } from 'vue' let id = 0 const newTodo = ref ('' )const hideCompleted = ref (false )const todos = ref ([ { id : id++, text : 'Learn HTML' , done : true }, { id : id++, text : 'Learn JavaScript' , done : true }, { id : id++, text : 'Learn Vue' , done : false } ]) const filteredTodos = computed (() => { return hideCompleted.value ? todos.value .filter ((t ) => !t.done ) : todos.value }) function addTodo ( ) { todos.value .push ({ id : id++, text : newTodo.value , done : false }) newTodo.value = '' } function removeTodo (todo ) { todos.value = todos.value .filter ((t ) => t !== todo) } </script > <template > <form @submit.prevent ="addTodo" > <input v-model ="newTodo" required placeholder ="new todo" > <button > Add Todo</button > </form > <ul > <li v-for ="todo in filteredTodos" :key ="todo.id" > // v-model 语法,自动把 checkbox 的结果绑定给 todo.done <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 >
vue 提供的 computed
功能可以动态监测元素的更新。
这体现“声明式”和“命令式”的区别,声明式编程可以编写组件功能,而如何更新状态这类问题交给框架实现。想起了高程用 EasyX 写 GUI 的恐惧。
具体地,在“点击 hideCompleted”和“完成一个 todo”时,computed
都会更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <script setup > import { ref, onMounted } from 'vue' const pElementRef = ref (null )onMounted (() => { pElementRef.value .textContent = 'mounted!' }) </script > <template > <p ref ="pElementRef" > Hello</p > </template >
设计“声明周期”概念,todo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <script setup > import { ref, watch } from 'vue' const todoId = ref (1 )const todoData = ref (null )async function fetchData ( ) { todoData.value = null const res = await fetch ( `https://jsonplaceholder.typicode.com/todos/${todoId.value} ` ) todoData.value = await res.json () } fetchData ()watch (todoId, fetchData)</script > <template > <p > Todo id: {{ todoId }}</p > <button @click ="todoId++" :disabled ="!todoData" > Fetch next todo</button > <p v-if ="!todoData" > Loading...</p > <pre v-else > {{ todoData }}</pre > </template >
watch监视一个值(ref类),在变化时调用后面的函数。
当然,用之前的dom事件监视的方法也可以实现一样的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <script setup > import { ref, watch } from 'vue' const todoId = ref (0 )const todoData = ref (null )async function fetchNextData ( ) { todoId.value ++ todoData.value = null const res = await fetch ( `https://jsonplaceholder.typicode.com/todos/${todoId.value} ` ) todoData.value = await res.json () } fetchNextData ()</script > <template > <p > Todo id: {{ todoId }}</p > <button @click ="fetchNextData" :disabled ="!todoData" > Fetch next todo</button > <p v-if ="!todoData" > Loading...</p > <pre v-else > {{ todoData }}</pre > </template >
这段代码中出现的 async
await
等用于实现异步,即产生一个新的子线程,与其他进程同时运行。
由于 fetch 其他网页的信息有延迟,在这段时间内还有其他组件需要维护,所以需要使用异步。
1 2 3 4 5 6 7 8 <script setup > import ChildComp from './ChildComp.vue' </script > <template > <ChildComp /> </template >
import。我们c++23怎么你了?
1 2 3 4 5 6 <script setup > const props = defineProps ({ msg : String }) </script >
子组件中的 prop 类似于参数,通过defineProps
定义。defineProps
是宏函数,使用时无需导入。
父组件可以按如下方法向子组件传入 prop 的具体值:
1 <ChildComp :msg ="greeting" />
子组件不能改变 prop 的值。不过有 default 定义缺省值等操作,todo
除了接收 props,子组件还可以向父组件触发事件:
1 2 3 4 5 6 7 <script setup > const emit = defineEmits (['response' ])emit ('response' , 'hello from child' )</script >
这里 emit
的第一个参数是事件名称,而之后的参数会传给父组件。
在父组件中:
1 <ChildComp @response ="(msg) => childMsg = msg" />
@response
触发了事件,之后调用引号内的匿名函数。
这段逻辑有点复杂。
最后一个是插槽 slots,用于直接向子组件传递 html 代码(模板片段)
在子组件中:
1 2 3 4 <template > <slot > Fallback content</slot > </template >
而在父组件中,若像这样不传递任何内容,则会显示默认的 Fallback content
而把父组件改成这样:
1 2 3 4 5 <ChildComp > <h1 > Message: {{ msg }} </h1 > </ChildComp >
中间的 <h1>
也会一起传入,显示一个硕大的 Message。因此其能够传递 html 代码,而不仅是基于 js 的信息。
done!