vue3 2023-05-10

1. 前端框架的作用-组件化开发

像python flask, golang gin一样,前端框架是规范了开发模式的js库,规范主要包括

  • 组件化
  • 建立数据和视图的关系

当使用前端框架开发,实际上是在写【组件】

前端开发核心理念:组件化开发。一个组件就是python/java中的class,golang中的struct

2. 写vue是在写什么

我们用vue框架写前端应用就是在写一个个的Vue组件

一个Vue组件就是一个.vue文件(SFC, Single-File Component, a.k.a. *.vue file),定义了一个视图(by template section),样式(by style section)以及这个视图对应的数据和业务逻辑(by script section)

  • 定义component(编写.vue文件) 就像定义一个 python class / golang type
  • 在一个 component A 里使用另一个 component B,等于 A 使用 B class/type 创建一个实例对象
  • 一个component重要的内部属性(不对外暴露)有:data method watch computed

使用Vue开发应用,先要定义好组件(一般来说,App.vue是整个组件树的root),然后调用Vue的createApp方法,传入root component,即可将所有组件渲染

2.1 start a Vite-powered Vue project in vue3

npm init vue@latest This command will install and execute create-vue, the official Vue project scaffolding tool. Follow the prompt and you will easily get a Vite-powered Vue project.
refer to https://vuejs.org/guide/quick-start.html#creating-a-vue-application

2.2 Vue project 的 3个核心文件

index.htmlmain.jsApp.vue是Vue.js应用程序的基本文件

index.html是Vue应用程序的入口页面。在这个页面中,Vue应用程序的JavaScript和CSS文件通常被加载和引用,同时该文件指定Vue根DOM元素,使用Vue控制的DOM将被插入到该元素内,例如<div id="app"></div>

main.js是Vue应用程序的主入口文件。在这个文件中,Vue实例被创建且挂载到指定的DOM元素上。在这个文件中通常还包括一些全局配置,如路由配置和Vuex Store配置。

App.vue是Vue应用程序中的主要组件,在这个文件中定义了Vue应用程序的视图层部分。它是一个组合了HTML、CSS和JavaScript的Vue单文件组件,用于构建页面和交互逻辑。

App.vue文件由三部分组成:<template>(模板)、<script>(脚本)和<style>(样式)。模板部分定义了组件的结构和渲染内容,脚本部分实现了组件的功能和交互逻辑,样式部分定义了组件的样式。它们组合起来形成了一个完整的Vue组件,可以重复使用和组合形成更为复杂的应用。

3. SFC in vue

3.1 definition of SFC

SFC(Single-File Component, a.k.a. *.vue file) is a special file format that allows us to encapsulate the 1. template, 2. logic, and 3. styling of a Vue component in a single file.

SFC是定义vue component的.vue文件,一个SFC定义一个vue component。一个组件的定义由模版、组件逻辑和组件样式3部分组成

Here's a basic example SFC, in Composition API style:

<!-- Each *.vue file can contain at most one <script> block (excluding <script setup>) -->
<script setup>
// ref 是 vue 提供的一种响应式引用类型,本质上和string/number一样,都是数据类型。【类似golang的指针】
// 如下创建了字符串'Hello World!'的响应式引用,并赋值给greeting
import { ref } from 'vue'
const greeting = ref('Hello World!')
</script>

<template>
  <p class="greeting_style">{{ greeting }}</p>
</template>

<style>
.greeting_style {
  color: red;
  font-weight: bold;
}
</style>

As we can see, Vue SFC is a natural extension of the classic trio of HTML, CSS and JavaScript.

3.2 three blocks in SFC

The <template>, <script>, and <style> blocks encapsulate and colocate the view, logic and styling of a component in the same file. The full syntax is defined in the SFC Syntax Specification.

  • script部分,定义的是控制视图的逻辑
  • template部分,定义的是无逻辑的视图本身(比如什么位置展示什么数据,负责界面和显示)
  • style部分,定义的是视图的样式

3.3 script setup

3.3.1 how script setup work

The code inside <script setup> is compiled as the content of this component's setup() function.
<script setup> will execute every time an instance of the component is created, like __init__ in python

<script setup> 中的顶层的导入和变量声明可在同一组件的模板(template)中直接使用。可以理解为模板中的表达式和 <script setup> 中的代码处在同一个作用域中

在添加了setup的script标签中,我们不必声明和方法,这种写法会自动将所有顶级变量、函数,均会自动暴露给模板(template)使用

With <script setup>, we don't need named exports or default exports in SFCs anymore
we can simply define variables and use them in the template.
top-level bindings inside <script setup> are exposed directly to template

<script setup>
// variable
const msg = 'Hello!'

// functions
function log() {
  console.log(msg)
}

// imports
import { capitalize } from './helpers'
</script>

<template>
  <button @click="log">{{ msg }}</button>
  <div>{{ capitalize('hello') }}</div>
</template>

这里强调一句 “暴露给【当前template】,跟暴露给外部不是一回事”. 暴露给【当前template】的变量和函数类似当前组件的私有方法和私有属性,而暴露给外部的变量和函数类似当前组件的公开方法和公开属性

To explicitly expose properties in a <script setup> component, use the defineExpose compiler macro:

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

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>

When a parent gets an instance of this component via template refs, the retrieved instance will be of the shape { a: number, b: number } (refs are automatically unwrapped just like on normal instances).

3.3.2 what to defined in script setup

在setup部分内的函数可以有两种返回值:
若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
若返回一个渲染函数:则可以自定义渲染内容。(了解)

注意:setup本身不能是async函数,如果 setup 函数是一个异步函数,那么它将返回一个 Promise 对象,而不是一个普通对象。由于 Vue 的模板引擎不能处理 Promise 对象,所以在模板中就无法访问到返回的对象中的属性。

例如,以下代码将无法正常工作:

export default {
  setup: async () => {
    const data = await fetchData()
    return {
      data
    }
  }
}

在这个例子中,setup 函数返回的是一个 Promise 对象,而不是一个包含 data 属性的对象,所以在模板中无法访问到 data。

如果你需要在 setup 函数中使用异步操作,你可以使用 async/await 结合 ref 或 reactive 来实现:

import { ref } from 'vue'

export default {
  setup() {
    const data = ref(null)

    const fetchData = async () => {
      data.value = await fetchData()
    }

    fetchData()

    return {
      data
    }
  }
}

3.4 template

3.4.1 template是什么

所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码

3.4.2 template作用是什么

Templates should only be responsible for mapping the state to the UI.
说白了就是设计页面组件

3.4.3 template访问范围

模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 MathDate,没有显式包含在列表中的全局对象将不能在模板内表达式中访问,可以自行在 app.config.globalProperties 上显式地添加它们,供所有的 Vue 表达式使用

3.4.4 template中的指令(vue directive)

指令是由 Vue 提供的、带有 v- 前缀的特殊 attribute

指令的期望值均为一个 JavaScript 表达式!!!! (除了少数几个例外,即之后要讨论到的 v-forv-onv-slot)
一个指令的任务是在其表达式的值变化时响应式地更新 DOM

某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。例如用 v-bind 指令来响应式地更新一个 HTML attribute:

<a v-bind:href="url"> ... </a>

<!-- 简写 -->
<a :href="url"> ... </a>

这里 href 就是一个参数,它告诉 v-bind 指令将表达式 url 的值绑定到元素的 href attribute 上

指令的样例如下图所示


directive example

vue built-in directives:

在模版中

  • 文本绑定 使用{{ componentInstance.xxxattr }}. 如果这个componentInstance是ref类型,则当componentInstance的value发生变化时,页面内容也会响应式变化
  • 属性绑定 使用指令v-bind. 如果绑定的value是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除. v-bind 指令需要一个attribute“参数”,在指令名后通过一个冒号隔开做标识,指定绑定value到哪个attribute

To make it easier to learn, let call the component <child>, the component's prop foo and the property we are binding bar.

With quotes:

<child v-bind:foo="bar"></child>

binds the value of the bar property in the current scope to the child's foo prop

anything within the quotes will be evaluated as a javascript expression. So v-bind:foo="bar + 1" (given bar equals 1) would bind the value 2 to the child's foo prop.

  • v-model实现数据的双向绑定

    先看数据双向绑定的手工实现

    1. v-bind 绑定ref数据,让ref数据的变化可以反应到页面上。ref data -> template
    2. v-on 添加事件监听器. template -> ref data

    v-model这一个指令把上面两件事都干了,它会根据所使用的element自动使用对应的 DOM 属性和事件组合
    以下的两个input框是一模一样的

    <script setup>
    import { ref } from 'vue'
    
    const message = ref('')
    </script>
    
    <template>
    <p>Message is: {{ message }}</p>
        <input v-model="message" placeholder="v-model mode input" />
        <br>
        <br>
        <input :value="message" @input="(event) => {message = event.target.value}" placeholder="manual bind mode input" />
    </template>
    

vue框架中如何实现父子组件交互

  • 编写带有propertyemit的子组件MyChildComponent.vue
<!-- MyChildComponent.vue -->
<template>
  <div>
    <h2>子组件</h2>
    <p>{{ message }}</p>
  </div>
</template>

<script>
import { watch } from 'vue'

const props = defineProps({
  message: {
    type: String,
    default: 'Hello from child component!',
  },
});

// 定义子组件可以emit的事件. messageChange, fakeEmit都是emit的事件名称
const emit = defineEmits(['messageChange', 'fakeEmit']);

// 侦听一个关于props.message的 getter 函数
watch(
    () => props.message,
    (new_msg) => {
        console.log(`new_msg is: ${new_msg}`);
        // 产生messageChange事件,并将new_msg作为事件值
        emit('messageChange', new_msg);
    }
)

// 定义该组件的公开属性和公开方法
const public_variable = ref(1);
const public_func = () => {
    console.log('this is public_func');
}
defineExpose({
  public_variable,
  public_func
})
</script>

props是实例化子组件时的入参,类似python __init__的参数;emit是子组件可以产生的事件,一定程度上类似returned value,可以在实例化子组件时使用@注册事件的handler

上面代码中,这个子组件接受一个名为 message 的 prop,并在模板中显示它;并且它可以产生messageChangefakeEmit事件

  • 在父组件中,导入这个子组件。在这个例子中,我们创建一个名为 MyParentComponent.vue 的父组件:
<!-- MyParentComponent.vue -->
<template>
  <div>
    <h1>父组件</h1>
    <MyChildComponent ref="childComponentRef" :message="parentMessage" @message-change="handleMessageChange" />
  </div>
</template>

<script>
// import 子组件。假设子组织在同一目录下
import MyChildComponent from './MyChildComponent.vue'

// childComponentRef将作为template中MyChildComponent的`ref` attribute的值
const childComponentRef = ref(null);
const parentMessage = ref('Hello from parent component!');

// 子组件messageChange事件的handler
const handleMessageChange = () => {
  console.log("child's saying that msg changed");
};

// 直接操作DOM element,需使用Template Refs. https://vuejs.org/guide/essentials/template-refs
// callChildPubFunc直接调用子组件公开方法
const callChildPubFunc = () => {
    // childComponentRef.value表示子组件实例本身
    childComponentRef.value.public_func();
};
</script>

在父组件中,我们首先使用 import 语句导入子组件,就可以在父组件的模板中使用子组件标签(在这里是 <MyChildComponent />)。

然后,通过在子组件标签上使用 v-bind(简写为 :)将父组件中的数据传递给子组件的 prop。在这个例子中,我们将 parentMessage 数据传递给子组件的 message prop。现在,当你在项目中使用 MyParentComponent 时,它将包含并显示 MyChildComponent 子组件,子组件将显示从父组件传递的消息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容