vue3核心笔记
本文最后更新于 2024年12月31日 晚上
前言
最近想系统性学习一下vue,在B站看到了技术蛋老师
的教学视频https://github.com/eggtoopain/vue-router-4-tutorial
,受益匪浅,蛋老师提供了各个章节源码,但是没有归纳笔记,因此本人按照视频目录粗略归纳了一下内容,以便快速回顾。
创建vue应用和插值表达式
- 以script方式引入vue.js
1
<script src="https://unpkg.com/vue@3"></script>
- 创建Vue应用
1
2
3<script>
Vue.createApp({data(){}, methods:{}, template:''}).mount('#app')
</script> - 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue3核心笔记</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app">
<h1>{{ title }}</h1>
</div>
<script>
Vue.createApp({
data() {
return {
title: '零食清单'
}
}
}).mount('#app');
</script>
</body>
</html>
v-for,v-bind,v-model用法
- v-for:循环元素
- v-bind:单向绑定属性data->dom,“v-bind:”可以简写为“:”
- v-model:双向绑定data<->dom
- 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<ul>
<li v-for="food in foods">
<img v-bind:src="food.image">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased">
<span>{{ food.purchased }}</span>
</li>
</ul>
<script>
Vue.createApp({
data() {
return {
title: '零食清单',
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
]
}
}
}).mount('#app');
</script>
key,v-show,computed用法
- key:给每一个元素添加唯一标识,避免vue对元素进行重新渲染
- v-show:根据条件显示元素,原理是根据CSS的display属性控制元素的显示与隐藏
- computed:计算属性,依赖于data中的数据,可以缓存计算结果,提高渲染效率,适用于高频计算的属性
- 代码
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<div id="app">
<section v-show="beforeBuy.length"><!-- beforeBuy.length为true时显示,长度为0时隐藏 -->
<h2>未购零食</h2>
<ul>
<li v-for="food in beforeBuy" :key="food.id">
<img :src="food.image">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased">
</li>
</ul>
</section>
<section v-show="afterBuy.length">
<h2>已购零食</h2>
<ul>
<li v-for="food in afterBuy" :key="food.id">
<img :src="food.image">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased">
</li>
</ul>
</section>
</div>
<script>
Vue.createApp({
data() {
return {
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
]
}
},
computed: {
beforeBuy() {//筛选purchased为false的元素
return this.foods.filter(item => !item.purchased);
},
afterBuy() {//筛选purchased为true的元素
return this.foods.filter(item => item.purchased);
}
}
}).mount('#app');
</script>
拆组件和引用组件
- 拆组件:将一个大的组件拆分成多个小组件,提高代码的可维护性
- 引用组件:在其他组件中引用其他组件,提高代码的复用性
- 步骤
- 创建组件:在单独的文件中定义组件,使用template标签定义组件的结构,如新建components/AppSection.js文件
1
2
3
4
5export default {
template: /*html*/ ``,//在反引号前面加上/*html*/,表示该字符串为html代码,vscode中使用es6-string-html插件来高亮关键字
data() {},
computed: {}
} - 引用组件:在其他组件中引用组件,使用template标签引用组件,由于html不区分大小写,对于驼峰式命名的组件,需要使用kebab-case命名法,如AppSections.vue文件,在html引用时写成AppSection.js
1
2
3
4
5
6
7
8import AppSections from "./AppSections.js"
export default {
components: { AppSections },
template: /*html*/ `
<app-sections></app-sections>
`
}App.js1
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
42export default {
template: /*html*/ `
<section v-show="beforeBuy.length">
<h2>未购零食</h2>
<ul>
<li v-for="food in beforeBuy" :key="food.id">
<img :src="food.image">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased">
</li>
</ul>
</section>
<section v-show="afterBuy.length">
<h2>已购零食</h2>
<ul>
<li v-for="food in afterBuy" :key="food.id">
<img :src="food.image">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased">
</li>
</ul>
</section>
`,
data() {
return {
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
]
}
},
computed: {
beforeBuy() {
return this.foods.filter(item => !item.purchased);
},
afterBuy() {
return this.foods.filter(item => item.purchased);
}
}
}index.html1
2
3
4
5
6
7
8import AppSections from "./AppSections.js"
export default {
components: { AppSections },
template: /*html*/ `
<app-sections></app-sections>
`
}1
2
3
4
5
6
7<div id="app"></div>
<script type="module">
import App from './components/App.js';
Vue.createApp(App).mount('#app');
</script>
- 创建组件:在单独的文件中定义组件,使用template标签定义组件的结构,如新建components/AppSection.js文件
props和组件复用
- 继续拆组件,把已购零食和未购零食拆分成两个组件,两个组件不同的是标题和数组,可以使用props来传递这两个数据
- 用props传参的方式来写子组件,子组件中规定参数类型
子组件:AppSectionsList.js
1 |
|
- 父组件首先import子组件,然后在template中引用子组件,并传递props
- 父组件的computed中定义filters对象,filters对象中包含两个数组,分别为未购零食和已购零食的数组
父组件:AppSections.js爷爷组件:App.js1
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
32import AppSectionsList from "./AppSectionsList.js";
export default {
components: { AppSectionsList },
template: /*html*/ `
<app-sections-list
headline="未购零食"
:buyChild="filters.beforeBuy">
</app-sections-list>
<app-sections-list
headline="已购零食"
:buyChild="filters.afterBuy">
</app-sections-list>
`,
data() {
return {
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
]
}
},
computed: {
filters() {
return {
beforeBuy: this.foods.filter(item => !item.purchased),
afterBuy: this.foods.filter(item => item.purchased)
}
}
}
}1
2
3
4
5
6
7
8import AppSections from "./AppSections.js"
export default {
components: { AppSections },
template: /*html*/ `
<app-sections></app-sections>
`
}
v-on,methods,v-if,style和class绑定事件
- v-on:绑定事件,可以简写为@
@submit.prevent可以阻止表单的默认事件,可以在事件处理函数中执行自定义逻辑 - v-if:真正的条件渲染,根据条件决定是否渲染元素,可以有v-else-if、v-else
- v-show的原理是根据CSS的display属性控制元素的显示与隐藏,不会移除dom元素
- :class={className: true}AppSections.js
1
2
3
4
5
6<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood" />
<button type="submit" v-if="foods.length <=3">添加</button>
<button type="submit" :class="{buttonColor: true}" v-else-if="foods.length > 3 && foods.length < 5">再加</button>
<button type="submit" :class="{buttonColor: true}" v-else>继续加</button>
</form>index.html1
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
45import AppSectionsList from "./AppSectionsList.js";
export default {
components: { AppSectionsList },
template: /*html*/ `
<app-sections-list headline="未购零食" :buyChild="filters.beforeBuy"></app-sections-list>
<app-sections-list headline="已购零食" :buyChild="filters.afterBuy"></app-sections-list>
<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood" />
<button type="submit" v-if="foods.length <=3">添加</button>
<button type="submit" :class="{buttonColor: true}" v-else-if="foods.length > 3 && foods.length < 5">再加</button>
<button type="submit" :class="{buttonColor: true}" v-else>继续加</button>
</form>
`,
data() {
return {
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
],
newFood: '',
}
},
methods: {
add() {
this.foods.push({
id: this.foods.length + 1,
name: this.newFood,
image: '../images/鱿鱼丝.png',
purchased: false
});
this.newFood = '';
}
},
computed: {
filters() {
return {
beforeBuy: this.foods.filter(item => !item.purchased),
afterBuy: this.foods.filter(item => item.purchased)
}
}
}
}style.css1
<link rel="stylesheet" href="./style.css">
1
2
3.buttonColor {
background-color: aquamarine;
}
$emit
- $emit 是一个实例方法,用于在子组件中触发事件,使得父组件可以监听并响应这些事件。$emit 方法的第一个参数是事件名称,随后的参数是传递给事件处理函数的数据。
- 子组件:
- AppSectionsFrom.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19export default {
template: /*html*/ `
<form @submit.prevent="add">
<input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood" />
<button type="submit">添加</button>
</form>
`,
data() {
return {
newFood: ''
}
},
methods: {
add() {
this.$emit('addFather', this.newFood);
this.newFood = '';
}
}
}
- AppSectionsFrom.js
- 父组件
- AppSections.js
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
41import AppSectionsList from "./AppSectionsList.js";
import AppSectionsForm from "./AppSectionsForm.js";
export default {
components: { AppSectionsList, AppSectionsForm },
template: /*html*/ `
<app-sections-list headline="未购零食" :buyChild="filters.beforeBuy"></app-sections-list>
<app-sections-list headline="已购零食" :buyChild="filters.afterBuy"></app-sections-list>
<app-sections-form
@addFather="fatherAdd"
></app-sections-form>
`,
data() {
return {
foods: [
{ id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
{ id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
{ id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
]
}
},
methods: {
fatherAdd(youyusi) {//youyusi参数对应子组件的newFood
this.foods.push({
id: this.foods.length + 1,
name: youyusi,
image: '../images/鱿鱼丝.png',
purchased: false
});
}
},
computed: {
filters() {
return {
beforeBuy: this.foods.filter(item => !item.purchased),
afterBuy: this.foods.filter(item => item.purchased)
}
}
}
}
- AppSections.js
vue项目组成逻辑、文件关系和作用
vue把页面理解为一个组件树,每个组件对应一个.vue文件,组件树的根节点是App.vue文件,App.vue文件中包含了整个页面的结构和逻辑。
SFC
Vue 单文件组件(Single File Component,简称 SFC)是 Vue.js 的一个核心特性,它允许开发者将组件的模板、逻辑和样式封装在一个单独的 .vue 文件中。这种文件结构使得组件的组织和复用变得更加简单和直观。
1 |
|
使用 SFC 的好处:
1.组件化:每个 SFC 都是一个独立的组件,可以包含自己的模板、逻辑和样式。
2.模块化:SFC 使得组件可以被轻松地导入和导出,便于模块化开发。
3.维护性:将相关的代码组织在一起,便于维护和理解。
4.开发效率:可以在一个文件中快速地编辑模板、逻辑和样式,提高开发效率。
npm
npm(node package manager)是一个开源的包管理工具,它可以帮助我们管理和发布代码。
生成 package.json 文件:
1 |
|
安装vue依赖包,会生成package-lock.json文件,该文件记录了当前项目依赖的具体版本号,以便于后续项目依赖的复现,依赖全都安装在node_modules目录下。
- package.json文件:简易包管理文件
- package-lock.json文件:详细包管理文件
1
npm i vue
webpack
webpack是一个模块打包工具,它可以将多个模块按照一定规则转换成浏览器可以识别的静态资源。
1 |
|
创建webpack.config.js文件,配置webpack的入口和出口文件,以及loader:
1 |
|
创建.babelrc文件,配置babel的转译规则:
1 |
|
webpack命令简化,回到package.json文件中,添加scripts命令:
1 |
|
可以使用npm run dev命令启动webpack开发环境,使用npm run build命令打包生产环境代码。
git
1 |
|
新建.gitignore文件,配置不需要提交的文件:
1 |
|
链接到远程仓库
1 |
|
提交代码
1 |
|
其他
1 |
|
jsconfig.json
jsconfig.json文件是VS Code的配置文件,它可以帮助我们在VS Code中更好地开发JavaScript项目。
1 |
|
vue-cli
vue-cli是一个脚手架工具,它可以帮助我们快速搭建基于Vue的项目结构。
创建一个新项目:
1 |
|
1 |
|
目录结构:
1 |
|
vue-router客户端路由的核心基础知识
这里使用vite创建项目,使用vue-cli也差不多:
1 |
|
cd进入项目目录安装依赖:
1 |
|
新建router文件夹,创建index.js
1 |
|
新建views文件夹,创建对应组件
1 |
|
1 |
|
App.vue
1 |
|
main.js
1 |
|
进阶案例
路由由数据动态决定,不写死,比如:
准备一个data.json文件,内容如下:
要把每一条数据单独做一条路由
1 |
|
详细的逻辑请看注释****详细的逻辑请看注释
App.vue中使用v-for添加路由
1 |
|
router/index.js中配置路由
1 |
|
Egg.vue
1 |
|
其他:views/NotFound.vue
1 |
|