今天正式进入Vue3的学习。既然网上没有好的教程那就干脆看官网学习吧,虽然我真的很喜欢看教程,毕竟那是有老师上课的感觉。废话不多说开始学习。
Vue.js设计的初衷就是能被渐进式采用,可以通过四种方式写进项目:①在页面上以CDN方式导入②下载js文件③使用npm进行安装④使用官方CLI进行构建。
CDN不能应用于大型的生产环境,先跳过。
先暂停一下,我突然想到那个阿里巴巴编码规范了,先打开IDEA,setting的plugins下直接搜索alibaba就能找到java编码规范插件,下载就行,以后敲右键就可以进行代码规范查验了。安装后记得重启IDEA就行了。
好了回到正题,通过以下地址先拿一下Vue的源码:
https://unpkg.com/vue@next
然后全选复制,我新建了一个VueStudy的springboot项目,然后在static包下建js包,然后新建一个vue3.js文件,将代码粘贴进去,可见一共有15929行,如果以后有机会的话还是要看一看这个源码的。现在可以写一个简单的Vue应用了。
templates下新建index.html,引入js文件。写一个最简单的页面,别问为啥这么写,我也不知道,以后就会了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../static/js/vue3.js"></script>
</head>
<body>
<div id="counter">
<p>{{num}}</p>
<p>{{uname}}</p>
</div>
<script>
const Counter = { //data是配置对象的方法
data: function (){
return{
num: 0,
uname: "lxc"
}
}
}
Vue.createApp(Counter).mount('#counter') //创建对象,将配置对象传入,mount可以理解为挂载
</script>
</body>
</html>
运行起来查看一下效果,可见初次尝试成功。
这里我想看一下这个创建对象到底返回了个什么,所以用一个let对象接受一下然后在前端看一下吧。
let test = Vue.createApp(Counter).mount('#counter')
console.log(test)
可以看到还是返回了不少东西的,这里的函数理论上都可以使用才对。
这里现在IDEA下载插件吧,首先是在下图的地方激活ESLint。
接下来直接下载vue.js插件。这两个是Vue开发很有帮助的插件。
然后再回到主线上,别忘了重启IDEA。这边通过官方文件可以看到,可以通过如下命令创建Vue项目,我来试一下这个。
npm init vue@latest
这一路下来一顿yes就行了,可以看到新建了不少好东西。
每个Vue应用都是通过createApp函数创建一个新的应用实例,需要导入createApp方法:
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
传入createApp的对象是一个组件,每个应用都需要一个根组件和其他若干子组件。之前说过,应用实例必须调用mount()方法后才能渲染出来,该方法会接收一个容器参数,在整个应用配置和资源注册完成后被调用,会被渲染在容器元素里,但容器元素自己不会被视为应用的一部分。
在未采用构建流程的情况下时可以直接使用根组件模板:
<div id="app">
<button @click="count++">{{ count }}</button>
</div>
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.mount('#app')
这边我在打开自动生成的APP.vue时,发现第二行的vue-router爆红,这里根据IDEA的提示npm命令操作一下即可解决。Vue是一种声明式渲染,极大提高开发效率,即先声明好然后直接调用渲染。
我用着这东西这个别扭,,,新建一个空项目吧,取名叫VueTest,然后构建一个新的Vue项目vue-begin02。这次安装我发现了问题,原来是上次我没装到底,我说的怎么没有自动启动呢。这次显示是这样的:
访问一下试试。。。
这次感觉好多了哈哈哈。
咋说呢我属是可怜,找了一些教程根本看不明白都。。。我还是按照官网一步一步来吧,这里我也不想分天啥的了,这篇直到学完Vue3就一直写吧。。。
重来!新建一个空项目,命名为VueStudy01,在终端操作,这次我全选No。这里官网解释了,生成的项目中的示例组件是使用组合式API 和 <script setup>
编写的,并非选项式API。运行下面的命令将应用发布到生产环境。
npm run build
随着学习的深入,会将代码切割为单独的js文件,需要通过http协议为HTML提供服务,需要先安装node.js,然后在HTML所在的文件夹下运行npx serve。进入边动手边学的教程。
首先看到的是声明式渲染。通过扩展于标准 HTML 的模板语法,我们可以根据 JavaScript 的状态来描述 HTML 应该是什么样子的。当状态改变时,HTML 会自动更新。即响应式布局。data组件应该是一个返回对象的函数,data下的message属性可以通过双大括号的形式进行渲染,而且双大括号内的内容可以使用任何有效的js表达式。下面是这个题的答案:
<script>
export default {
// 组件选项
// 此处声明一些响应式状态
data(){
return {
message: 'Make me dynamic!'
}
}
}
</script>
<template>
<h1>{{message}}</h1>
</template>
下面是Attribute绑定。双大括号(即mustache语法)只能用于文本插值,绑定动态值就需要使用v-bind:指令。为了代码简洁,可以简写为:。本题答案如下:
<script>
export default {
data() {
return {
titleClass: 'title'
}
}
}
</script>
<template>
<h1 v-bind:class="titleClass">Make me red</h1> <!-- 此处添加一个动态 class 绑定 -->
</template>
<style>
.title {
color: red;
}
</style>
下面是事件监听。可以使用v-on:指令监听DOM事件,可以简写为@。答案如下:
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment(){
this.count++;
}
}
}
</script>
<template>
<!-- 使此按钮生效 -->
<button v-on:click="increment">count is: {{ count }}</button>
</template>
接下来是表单绑定。可以同时使用v-bind和v-on在表单的元素上进行双向绑定,为了简化这一过程,提供了一种v-model指令,会将绑定的值与input的值相绑定,这样就不用使用事件处理函数了,还可以用于各种不同类型的输入。
<script>
export default {
data() {
return {
text: '123'
}
}
}
</script>
<template>
<input v-model="text" placeholder="Type here">
<p>{{ text }}</p>
</template>
下面来条件渲染。听到条件两个字肯定if没跑了,果然。这里的指令是v-if,即只有在这里是真即Truthy时才会执行,否则过滤。那都有if了,v-else和v-else-if自然也少不了。
<script>
export default {
data() {
return {
awesome: true
}
},
methods: {
toggle() {
this.awesome = !this.awesome;
}
}
}
</script>
<template>
<button @click="toggle">toggle</button>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
</template>
列表渲染,使用v-for命令来渲染基于源数组的列表。这部分有点恶心了开始,首先要有一个局部变量这里命名为todo,他给出了一个例子先看一下:
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
这里todo对象的id就类似于数据库表的主键,是唯一标识的那种,并将它作为特殊的key绑定到每个<li>上。这里可以用this.todos.push(newTodo)调用变更方法,可以通过this.todos = this.todos.filter()使用新数组代替原数组。理论上v-for是可以与v-if一起使用的,但是不推荐,因为两者的优先级不明显。在同一个节点时if的条件更高,因此无法读取到for中的局部变量,为了解决上述问题可以在外面新建一层template即可。
为了让Vue更方便的跟踪每个节点,可以给一个唯一的标识符key。当使用template的v-for时,应将key放在template容器上,注意这里的key是通过v-bind绑定的。官方推荐在这里给每个都绑定一个key值。至于操作数组的几个函数都蛮简单的。这题的答案如下:
<script>
// 给每个 todo 对象一个唯一的 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>
下一个单元是计算属性,是跟上一单元连起来的,通过v-model将done属性绑定,添加了切换的功能,即:
<li v-for="todo in todos">
<input type="checkbox" v-model="todo.done">
...
</li>
在这之后就引入了这个全新的概念——计算属性,用computed选项进行声明,他的值由其他值计算而来。
<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>
下面,生命周期与模板引用。模板引用指向模板中的一个DOM元素的ref,在挂载后执行代码需要使用mounted选项。这里注意,虽然也是写在default里面,但执行时已经挂载结束,这被称为生命周期钩子。除了mounted,常见的还有updated和unmounted钩子。答案如下:
<script>
export default {
data() {
return {
message: "hello"
}
},
mounted() {
this.$refs.p.textContent = "nihao"
}
}
</script>
<template>
<p ref="p">{{message}}</p>
</template>
来继续,侦听器。可以实现比如,在某个数字改变时将其输出到控制台这样的功能。这东西一猜就知道是watch选项。有一说一这部分浪费我不少时间,确实看着代码有些吃力了。这道题的答案如下:
<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>
以上的全部内容都是单组件来搞的,但是很明显只有单组件是不可能完成复杂的应用的,是需要多个组件嵌套完成才对。果然,父组件可以在模板中渲染子组件,在使用子组件时候需要导入,例如下面:
import ChildComp from './ChildComp.vue'
export default {
components: {
ChildComp
}
}
在此之上还需要使用components注册组件,然后就可以直接用了<ChildComp />。本题的答案如下所示:
<script>
import ChildComp from './ChildComp.vue'
export default {
// register child component
components: {
ChildComp
}
}
</script>
<template>
<!-- render child component -->
<ChildComp />
</template>
子组件可以通过props从父组件接收动态数据,这里要声明接受的props,声明后就可以用this调用了,也可以使用v-bind语法等等。答案如下:
<script>
import ChildComp from './ChildComp.vue'
export default {
components: {
ChildComp
},
data() {
return {
greeting: 'Hello from parent'
}
}
}
</script>
<template>
<ChildComp v-bind:msg="greeting"/>
</template>
子组件还可以向父组件触发事件,这题答案:
<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>
除了props,还可以用插槽(slots)的形式将片段传给子组件,可使用<slots>元素作为插槽出口,渲染父组件的插槽内容。slot中的内容会作为默认内容,在无传递时候显示。答案如下:
<script>
import ChildComp from './ChildComp.vue'
export default {
components: {
ChildComp
},
data() {
return {
msg: 'from parent'
}
}
}
</script>
<template>
<ChildComp>Message: {{msg}}</ChildComp>
</template>
到目前为止,已经完成了Vue官网的互动教程,人家官方说了咱忽略了大量的细节,所以还得看他那个快速上手、深入指南还有实例,也确实,搞了这么半天也就能输出来一行文字,这照前端的页面那可差远了。
接下来将上手的内容是快速上手阶段。前面的都是分段式的教程,现在只是会用一些简单的组件之类的,现在要整体来看了。每个Vue应用都是通过createApp函数创建一个新的应用实例。这里注意,所有的函数都需要及进行一个导入:
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
传入createApp的是一个组件,每个应用都需要一个根组件,其他组件都是这个的子组件。
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'
const app = createApp(App)
打开之前创建的空Vue项目或者干脆新建一个也行,然后进行一下上述操作。这里官方教程写的太乱套了根本没说明白要在哪操作,我就尝试着在src文件夹下新建一个vue组件,起名为Test。讲道理这鬼东西根本不知道该往哪写啊。。。只是截取了部分代码谁知道往哪写啊也没说清楚,后面的内容我就自己去官网看了,就不上手写了。。。