Setup 语法糖
flag
和text
都能在模版上渲染出来- 执行
changeText
方法,text
的值会更新,但不渲染 - 执行
change
方法,flag
的值会更新并渲染 - 先执行
changeText
方法,再执行change
方法,flag
和text
的值都会更新并渲染 - 执行
batchChange
方法,flag
和text
的值都会更新并渲染
flag
和 text
都能在模版上渲染出来changeText
方法,text
的值会更新,但不渲染change
方法,flag
的值会更新并渲染changeText
方法,再执行 change
方法,flag
和 text
的值都会更新并渲染batchChange
方法,flag
和 text
的值都会更新并渲染在 Vue 中,我们经常会用到 $refs 去取组件的实例引用,同时去调用实例上的一些方法与访问一些属性。在 Vue2 中,默认所有的方法与属性都是可以直接被访问到的;而在 Vue3 中,多了一个 expose 的概念。
在使用选项式 API 或者没有使用 setup 语法的情况下,被引用的组件实例与该组件的 this 完全一致,可以访问到所有的方法和属性。但在使用了 <script setup>
语法的情况下,父组件无法访问到子组件内部的任何东西,除非通过 expose 显式暴露。
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
a,
b,
})
</script>
export enum NodeTypes {
// 0 根节点 最顶层那个节点
ROOT,
// 1 元素节点 <Comp></Comp>
ELEMENT,
// 2 普通文本节点
TEXT,
// 3 注释节点
COMMENT,
// 4 表达式节点 :total="pageInfo.total" 中 pageInfo.total 就是表达式
SIMPLE_EXPRESSION,
// 5 插值表达式节点 {{ something }} 这种就是插值表达式
INTERPOLATION,
// 6 普通属性节点 style="xx" 这种不是v-bind的就会被当成普通属性节点处理
ATTRIBUTE,
// 7 指令节点 v-on :total .prop @on-change #a v-slot v-pre 还有自定义指令等
// 可以看测试用例去找到指定的case vuejs/core/packages/compiler-core/__tests__/parse.spec.ts v-if v-show v-for v-bind 这种都是
DIRECTIVE,
// containers
// 8 复合表达式 看测试用例的结果丢到 ast explorer 中无法解析成这个类型 看起来是 ast explorer 中版本太旧了
COMPOUND_EXPRESSION,
// 9 v-if 表达式
IF,
// 10 v-if 的节点下面会有 branches 属性
IF_BRANCH,
// 11 v-for 表达式 举个例子:<div v-for="item of []"></div>的父子关系是:root -> v-for -> div
FOR,
// 12 <div/>{{ foo }} bar {{ baz }}<div/> 中间的 bar 即是 TEXT_CALL 有点不明所以
TEXT_CALL,
// codegen
// codegen 即根据template块生成代码的节点类型
// 13
VNODE_CALL,
// 14
JS_CALL_EXPRESSION,
// 15
JS_OBJECT_EXPRESSION,
// 16
JS_PROPERTY,
// 17
JS_ARRAY_EXPRESSION,
// 18
JS_FUNCTION_EXPRESSION,
// 19
JS_CONDITIONAL_EXPRESSION,
// 20
JS_CACHE_EXPRESSION,
// ssr codegen
JS_BLOCK_STATEMENT,
JS_TEMPLATE_LITERAL,
JS_IF_STATEMENT,
JS_ASSIGNMENT_EXPRESSION,
JS_SEQUENCE_EXPRESSION,
JS_RETURN_STATEMENT,
}
Vue2 中,nextTick 的源码在 /src/core/util/next-tick.ts
下面:
// Here we have async deferring wrappers using microtasks.
// In 2.5 we used (macro) tasks (in combination with microtasks).
// However, it has subtle problems when state is changed right before repaint
// (e.g. #6813, out-in transitions).
// Also, using (macro) tasks in event handler would cause some weird behaviors
// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).
// So we now use microtasks everywhere, again.
// A major drawback of this tradeoff is that there are some scenarios
// where microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690, which have workarounds)
// or even between bubbling of the same event (#6566).
let timerFunc
// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
// In problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (
!isIE &&
typeof MutationObserver !== 'undefined' &&
(isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
// Use MutationObserver where native Promise is not available,
// e.g. PhantomJS, iOS7, Android 4.4
// (#6466 MutationObserver is unreliable in IE11)
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true,
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// Fallback to setImmediate.
// Technically it leverages the (macro) task queue,
// but it is still a better choice than setTimeout.
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// Fallback to setTimeout.
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
项目结构:Vue2 与 Vue3 共存的组件库,采用 pnpm workspace Monorepo 方式组织仓库。
由于项目中有 Vue2 与 Vue3 的组件,因此需要搭两套文档,Vitepress 与 Vuepress2+只支持 Vue3 的宿主环境,Vue2 的组件库文档只能使用 Vuepress1.x 搭建。
依赖这块,package.json 中以 vue3 为主,vue2 通过别名的方式安装与引用。
//package.json
{
"devDependencies": {
"vue": "^3.3.4",
"vue2": "npm:vue@2.7.15"
}
}
在我们写 Vue 组件时,经常会遇到自定义 v-model 或 prop.sync 的需求。一般来说,我们有两种方法实现这种需求:
watch
分别监听 props 和 内部变量的变更,前者初始化内部变量,后者在内部变量变更时 emit
一个 input
/ update:prop
事件使外部变量同步变更。computed
变量,定义它的 get
方法为获取 props 值,set 方法为 emit
事件。组合式 API (Composition API) 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件。
在刚接触 Vue3 的时候,由于了解到 Vue3 最大的变化就是提供了全新的 composition api,因此在初上手的时候,所有组件清一色用的都是 <script setup>
+ coposition api 编写,想着多用新特性总没错。在经历了一段时间的使用后,才逐渐有了一些自己的理解。
在我看来,组合式 API 不是 Vue3 的必须品,它有些时候更像一种编码思维,在该用到的地方使用会给你提升十分多的效率,但如果是为了用而用,在大多数简单的场景下,可能选项式 API 会显得更简单无脑一些,因为它给你列好了框架,手把手教你该把代码放在哪里。官方的原话是:
举个例子,我们在 .container
类下面有一个 iView
的 poptip
组件,按往常我们的写法,都套一层 :deep
比较稳妥。
但这里由于 .ivu-poptip
和 .ivu-poptip-rel
是同一个组件,组件内层并没有使用 scoped
作用域的 css,因此我们只需要加一层 :deep
到组件的第一级,后续的子类选择器就会选择到对应父类下的子类。
直接查看 vite
官方文档,通过官方提供的创建项目命令创建一个空项目:
yarn create vite my-vue-app --template vue