vue-js-学习-1
上一级页面:vue-js-学习索引
本系列关注前端部分,根据学习路线图达到学习Vue.js的目的
developer路线图developer-roadmap/translations/chinese at master · kamranahmedse/developer-roadmap
前言
在开始Vue.js的学习之前,至少需要了解HTML+CSS+JS的基础知识。详见前端学习路线-目录
初学者学习前端知识我把它划分成三个阶段,我还给出了很好的学习方法,见编程初学者的三个学习阶段
我是后端程序员,在学习过程中,会用后端的一些思维来类比理解前端概念。
入门选择选项式Api
+单文件组件
本文选择选项式Api进行初始学习,因为我有面向对象语言的编程经历:
选项式 API 以“组件实例”的概念为中心 (即上述例子中的
this
),对于有面向对象语言背景的用户来说,这通常与基于类的心智模型更为一致。同时,它将响应性相关的细节抽象出来,并强制按照选项来组织代码,从而对初学者而言更为友好。
对于新手,或者没有使用过前端框架的经历,vue.js官方推荐通过选项式Api
+单文件组件
来入门。
![Pasted image 20230123214432.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230123214432.png)
如果你是一名来自 Vue 2 或其他框架的资深开发者,你可以调整一些设置来充分使用本教程。如果你是一名初学者,推荐使用默认设置进行学习。
互动教程
怎么看教程
跟着互动教程,第一遍对相关概念有个大致感觉,并且自己把题目做一遍。把题目作对即可
具体来说:
- 完成题目要求,可以看答案,但是看完后依然要手动完成这道题目。
- 停留在这道题目,仿写一个类似实现
- 如果题目提到了它有2种以上的实现方式,请把每种实现方式都操作一遍
- 做到后面的题目,遇到忘记的知识点,就回退到忘记的知识点上,并且重复完成这个忘记的知识点。
vue文件结构
下面介绍vue文件的结构:
以下面这个典型的vue文件为例,
script:写vue风格的JS
<script>
// 全局变量区
let id = 0;
export default {
// data()区类似于`类`的设计,你可以把它看做是在定义类里面的属性(Attribute)
// 调用时使用`this.属性(Attribute)名`来调用
// 注意里面的,和;
// 固定写法:
// data() {
// return {
// 注意这里的冒号后面要空一格,还有末尾有,
// 属性(Attribute)名: 属性(Attribute)值 ,
// };
// },
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` 选项声明一个响应式的属性,它的值由其他属性计算而来:
// 它本质上是一个属性(Attribute),与传统属性不同的是,它是响应式的,
// 即是会随触发器的改变而动态变化的.(能在改变时触发更新的状态被认为是响应式的)
// 例如用户点击了网页的按钮,这个属性值就随着他的点击事件变化。
// computed: {
// 响应式属性名() {
// 响应式属性值的变化方式
// },
// },
// 见下面这个示例,filteredTodos可以当作一个响应式变化的变量使用。
// 例如 v-if="filteredTodos" 这样的用法
// 或是 v-for="todo in filteredTodos" 这样的用法
computed: {
filteredTodos() {
if (this.hideCompleted) {
return this.todos.filter((t) => !t.done);
} else {
return this.todos;
}
},
},
// 使用 `methods` 选项声明函数,这里就是普通的函数,
// 或者你也可以把它叫做方法
// methods: {
// 方法名(){
// 方法体
// },
// }
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>
temple部分写变化的HTML
<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部分写css
<style>
.done {
text-decoration: line-through;
}
</style>
其他关键字
类似前面说的代码结构,vue还有一些关键字,类似于下面这样的
<script>
export default {
mounted() {
// 此时组件已经挂载。
},
watch: {
// 监控 count(newCount);这个方法的执行
count(newCount) {
// 没错,console.log() 是一个副作用
console.log(`new count is: ${newCount}`)
}
}
};
</script>
继续阅读文档--快速上手
参考快速上手,在你的机器上创建一个真实的 Vue 项目。
这一节内容比较简单,
- 注意vscode不要用相对路径,不要用目录链接(mklink),这些都会影响最后的vite编译。
深入指南--创建第一个应用
阅读深入指南——它更详细地探讨了我们目前学过的所有话题,并且还有许多其他更深入的内容。
下面我做一下自己的笔记,作为官方文档的辅助理解。
应用实例、程序入口
首先介绍程序入口
import { createApp } from 'vue'
const app = createApp({ /* 根组件选项 */ })
这两句话的意思是,这里是整个前端应用程序的入口,注册一个根组件就在这里注册。
根组件
在我们创建的项目里,有一个创建好的根组件src/App.vue
查看src/main.js
,代码大致如下:
import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
// 这个App作为createApp(App)的参数
import App from './App.vue'
// 先忽略这句
//import './assets/main.css'
const app = createApp(App)
注意到这句
// 从一个单文件组件中导入根组件
// 这个App作为createApp(App)的参数
import App from './App.vue'
解释一下,这里的import App XXX
中的App是什么意思:
App
或者./App.vue
被称为根组件,根组件可以作为createApp()
方法的参数,即
// createApp(根组件名)
createApp(App)
挂载应用
这里是说,我们注册了根组件,还需要做挂载操作。这很好理解,类比到linux中,你想要操作一个硬盘分区,首先要对它进行挂载
const app = createApp(App)
// 将app挂载到CSS中的#app标签
app.mount('#app')
// 上面两个语句的合并写法
createApp(App).mount('#app')
这里用标准一点的术语,就是,这个根组件将会被渲染在容器元素(#app
)里面。
这里的#app
是CSS的标签,使用的时候就是用css标签的用法,如下:
<div id="app"></div>
- 在项目里打开
./index.html
,你会发现脚手架项目里已经写好了这句
DOM 中的根组件模板
自己创建内联根组件
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.mount('#app')
这里是说,可以不使用import App from './App.vue'
,不使用默认提供的App
作为根组件。
作为替代,我们自己创建了一个内联的根组件,这个根组件的内容是
{
data() {
return {
count: 0
}
}
}
innerHTML
我们自己创建了一个内联的根组件,没有使用脚手架项目里面的App.vue。
我们自己创建的根组件,没有template。
当根组件没有设置
template
选项时,Vue 将自动使用容器的innerHTML
作为模板。
没有template,那么上面的语句写在哪里呢?Vue提示使用容器的 innerHTML
作为模板。
- 文档:element.innerHTML - Web API 接口参考 | MDN (mozilla.org)
innerHTML
这个概念我理解不到位,以后再补这块吧。
我这里写在./index.html
,
![Pasted image 20230126125143.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230126125143.png)
一开始文档里的按钮的功能我没有实现,我做了一个类似的
Hello vue
标签后来发现是因为我写了两个相同id的div标签,类似下面这样的操作,这样只有第一个div标签生效
<div id="app">{{ message }}</div> <div id="app"> <button @click="count++">{{ count }}</button> </div>
./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>
<!-- 引入依赖,因为在未采用构建流程的情况下使用 Vue -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
<button @click="count++">{{ count }}</button>
<p> {{ message }} </p>
</div>
<!-- 不要有两个相同id的div标签 -->
<!-- <div></div> -->
<script>
const { createApp } = Vue;
createApp({
data() {
return {
message: "Hello Vue!",
count: 0,
};
},
}).mount("#app");
</script>
<!-- 注释掉原来的两行 -->
<!-- <div id="app"></div> -->
<!-- <script type="module" src="/src/main.js"></script> -->
</body>
</html>
把内容加在./index.html
,然后刷新网页。即可看到效果
- vite帮助我们自动重启服务器,你无需关注服务器的问题,直接刷新网页即可
![Pasted image 20230126125206.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230126125206.png)
应用配置
确保在挂载应用实例之前完成所有应用配置!
解释一下这个注册组件,注册以后怎么用:
首先还原./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 { createApp } from "vue";
// 从一个单文件组件中导入根组件
// 这个App作为createApp(App)的参数
import App from "./App.vue";
import TodoDeleteButton from "./components/TodoDeleteButton.vue"
import "./assets/main.css";
const app = createApp(App);
app.config.errorHandler = (err) => {
/* 处理错误 */
console.log(err);
};
app.component('TodoDeleteButton', TodoDeleteButton)
// 确保在挂载应用实例之前完成所有应用配置!
app.mount("#app");
新建文件src/components/TodoDeleteButton.vue
,内容如下:
<script setup>
defineProps({
msg: {
type: String,
required: true
}
})
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
You’ve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
最后在App.vue
中添加代码,把原来的组件注释掉,这样方便看效果
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
</script>
<template>
<header>
<img
alt="Vue logo"
class="logo"
src="./assets/logo.svg"
width="125"
height="125"
/>
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<!-- <TheWelcome /> -->
<TodoDeleteButton msg="看这里!!!"/>
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
效果如下,红框是我自己截图的时候画上去的:
![Pasted image 20230126143244.png](https://webdav-1309345210.file.myqcloud.com/images/Pasted image 20230126143244.png)
多个应用实例
这个没啥好说的,挺好理解的。
大致说一下挂载后的用法:
假设app1挂载在#container-1'
const app1 = createApp({ /* ... */ }) app1.mount('#container-1')
则在HTML Body或者template中这样来使用它
<div id="container-1">
<!-- do something here -->
</div>
模板语法
实际示例
如果你是有经验的前端开发者,你可以查看一些更加实际的示例。
工具链
我这里使用官方推荐的VSCode,见工具链 | Vue.js (vuejs.org)