Composition (组合) API
setup
新组件
其它API更新
文档: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 安装或者升级
npm install -g @vue/cli
## 保证 vue cli 版本在 4.5.0 以上
vue --version
## 创建项目
vue create my-project
然后的步骤
文档: https://v3.cn.vuejs.org/guide/installation.html
vite 是一个由原生 ESM 驱动的 Web 开发构建工具。在开发环境下基于浏览器原生 ES imports 开发,
它做到了本地快速开发启动, 在生产环境下基于 Rollup 打包。
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev
文档:
https://composition-api.vuejs.org/zh/api.html
{{count}}
name: {{state.name}}
age: {{state.age}}
wife: {{state.wife}}
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
proxy.name = 'tom'
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Proxy 与 Reflecttitle>
head>
<body>
<script>
const user = {
name: "John",
age: 12
};
/*
proxyUser是代理对象, user是被代理对象
后面所有的操作都是通过代理对象来操作被代理对象内部属性
*/
const proxyUser = new Proxy(user, {
get(target, prop) {
console.log('劫持get()', prop)
return Reflect.get(target, prop)
},
set(target, prop, val) {
console.log('劫持set()', prop, val)
return Reflect.set(target, prop, val); // (2)
},
deleteProperty (target, prop) {
console.log('劫持delete属性', prop)
return Reflect.deleteProperty(target, prop)
}
});
// 读取属性值
console.log(proxyUser===user)
console.log(proxyUser.name, proxyUser.age)
// 设置属性值
proxyUser.name = 'bob'
proxyUser.age = 13
console.log(user)
// 添加属性
proxyUser.sex = '男'
console.log(user)
// 删除属性
delete proxyUser.sex
console.log(user)
script>
body>
html>
setup执行的时机
setup的返回值
setup的参数
App
msg: {{msg}}
{{n}}
{{m}}
msg: {{msg}}
msg2: {{$attrs.msg2}}
App
m1: {{m1}}
m2: {{m2}}
m3: {{m3}}
computed函数:
watch函数
watchEffect函数
App
fistName:
lastName:
fullName1:
fullName2:
fullName3:
vue2.x的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLrqONVT-1652341048195)(https://vipkshttps3.wiz.cn/ks/note/view/49c30824-dcdf-4bd0-af2a-708f490b44a1/10311b3b-496c-41f1-8df3-c87572008080/index_files/1604629129730-y1h.png)]
vue3的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T5MIkBJr-1652341048196)(https://vipkshttps3.wiz.cn/ks/note/view/49c30824-dcdf-4bd0-af2a-708f490b44a1/10311b3b-496c-41f1-8df3-c87572008080/index_files/1604629129585-tqn.png)]
与 2.x 版本生命周期相对应的组合式 API
beforeCreate
-> 使用 setup()
created
-> 使用 setup()
beforeMount
-> onBeforeMount
mounted
-> onMounted
beforeUpdate
-> onBeforeUpdate
updated
-> onUpdated
beforeDestroy
-> onBeforeUnmount
destroyed
-> onUnmounted
errorCaptured
-> onErrorCaptured
新增的钩子函数
组合式 API 还提供了以下调试钩子函数:
msg: {{msg}}
App
使用Vue3的组合API封装的可复用的功能函数
自定义hook的作用类似于vue2中的mixin技术
自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂
需求1: 收集用户鼠标点击的页面坐标
hooks/useMousePosition.ts
import { ref, onMounted, onUnmounted } from 'vue'
/*
收集用户鼠标点击的页面坐标
*/
export default function useMousePosition () {
// 初始化坐标数据
const x = ref(-1)
const y = ref(-1)
// 用于收集点击事件坐标的函数
const updatePosition = (e: MouseEvent) => {
x.value = e.pageX
y.value = e.pageY
}
// 挂载后绑定点击监听
onMounted(() => {
document.addEventListener('click', updatePosition)
})
// 卸载前解绑点击监听
onUnmounted(() => {
document.removeEventListener('click', updatePosition)
})
return {x, y}
}
x: {{x}}, y: {{y}}
利用TS泛型强化类型检查
需求2: 封装发ajax请求的hook函数
hooks/useRequest.ts
import { ref } from 'vue'
import axios from 'axios'
/*
使用axios发送异步ajax请求
*/
export default function useUrlLoader<T>(url: string) {
const result = ref<T | null>(null)
const loading = ref(true)
const errorMsg = ref(null)
axios.get(url)
.then(response => {
loading.value = false
result.value = response.data
})
.catch(e => {
loading.value = false
errorMsg.value = e.message || '未知错误'
})
return {
loading,
result,
errorMsg,
}
}
LOADING...
{{errorMsg}}
- id: {{p.id}}
- title: {{p.title}}
- price: {{p.price}}
把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
应用: 当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
问题: reactive 对象取出的所有属性值都是非响应式的
解决: 利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
App
foo: {{foo}}
bar: {{bar}}
foo2: {{foo2}}
bar2: {{bar2}}
利用ref函数获取组件中的标签元素
功能需求: 让输入框自动获取焦点
App
---
shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
什么时候用浅响应式呢?
App
m1: {{m1}}
m2: {{m2}}
m3: {{m3}}
m4: {{m4}}
App
{{state}}
reactive
或 readonly
方法转换成响应式代理的普通对象。
{{state}}
App
{{state}}
{{foo}}
{{foo2}}
Child
{{foo}}
{{length}}
App
{{keyword}}
provide和
inject提供依赖注入,功能类似 2.x 的
provide/inject
实现跨层级组件(祖孙)间通信
父组件
当前颜色: {{color}}
子组件
孙子组件: {{color}}
reactive
创建的响应式代理readonly
创建的只读代理reactive
或者 readonly
方法创建的代理const reactiveHandler = {
get (target, key) {
if (key==='_is_reactive') return true
return Reflect.get(target, key)
},
set (target, key, value) {
const result = Reflect.set(target, key, value)
console.log('数据已更新, 去更新界面')
return result
},
deleteProperty (target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('数据已删除, 去更新界面')
return result
},
}
/*
自定义shallowReactive
*/
function shallowReactive(obj) {
return new Proxy(obj, reactiveHandler)
}
/*
自定义reactive
*/
function reactive (target) {
if (target && typeof target==='object') {
if (target instanceof Array) { // 数组
target.forEach((item, index) => {
target[index] = reactive(item)
})
} else { // 对象
Object.keys(target).forEach(key => {
target[key] = reactive(target[key])
})
}
const proxy = new Proxy(target, reactiveHandler)
return proxy
}
return target
}
/* 测试自定义shallowReactive */
const proxy = shallowReactive({
a: {
b: 3
}
})
proxy.a = {b: 4} // 劫持到了
proxy.a.b = 5 // 没有劫持到
/* 测试自定义reactive */
const obj = {
a: 'abc',
b: [{x: 1}],
c: {x: [11]},
}
const proxy = reactive(obj)
console.log(proxy)
proxy.b[0].x += 1
proxy.c.x[0] += 1
/*
自定义shallowRef
*/
function shallowRef(target) {
const result = {
_value: target, // 用来保存数据的内部属性
_is_ref: true, // 用来标识是ref对象
get value () {
return this._value
},
set value (val) {
this._value = val
console.log('set value 数据已更新, 去更新界面')
}
}
return result
}
/*
自定义ref
*/
function ref(target) {
if (target && typeof target==='object') {
target = reactive(target)
}
const result = {
_value: target, // 用来保存数据的内部属性
_is_ref: true, // 用来标识是ref对象
get value () {
return this._value
},
set value (val) {
this._value = val
console.log('set value 数据已更新, 去更新界面')
}
}
return result
}
/* 测试自定义shallowRef */
const ref3 = shallowRef({
a: 'abc',
})
ref3.value = 'xxx'
ref3.value.a = 'yyy'
/* 测试自定义ref */
const ref1 = ref(0)
const ref2 = ref({
a: 'abc',
b: [{x: 1}],
c: {x: [11]},
})
ref1.value++
ref2.value.b[0].x++
console.log(ref1, ref2)
const readonlyHandler = {
get (target, key) {
if (key==='_is_readonly') return true
return Reflect.get(target, key)
},
set () {
console.warn('只读的, 不能修改')
return true
},
deleteProperty () {
console.warn('只读的, 不能删除')
return true
},
}
/*
自定义shallowReadonly
*/
function shallowReadonly(obj) {
return new Proxy(obj, readonlyHandler)
}
/*
自定义readonly
*/
function readonly(target) {
if (target && typeof target==='object') {
if (target instanceof Array) { // 数组
target.forEach((item, index) => {
target[index] = readonly(item)
})
} else { // 对象
Object.keys(target).forEach(key => {
target[key] = readonly(target[key])
})
}
const proxy = new Proxy(target, readonlyHandler)
return proxy
}
return target
}
/* 测试自定义readonly */
/* 测试自定义shallowReadonly */
const objReadOnly = readonly({
a: {
b: 1
}
})
const objReadOnly2 = shallowReadonly({
a: {
b: 1
}
})
objReadOnly.a = 1
objReadOnly.a.b = 2
objReadOnly2.a = 1
objReadOnly2.a.b = 2
/*
判断是否是ref对象
*/
function isRef(obj) {
return obj && obj._is_ref
}
/*
判断是否是reactive对象
*/
function isReactive(obj) {
return obj && obj._is_reactive
}
/*
判断是否是readonly对象
*/
function isReadonly(obj) {
return obj && obj._is_readonly
}
/*
是否是reactive或readonly产生的代理对象
*/
function isProxy (obj) {
return isReactive(obj) || isReadonly(obj)
}
/* 测试判断函数 */
console.log(isReactive(reactive({})))
console.log(isRef(ref({})))
console.log(isReadonly(readonly({})))
console.log(isProxy(reactive({})))
console.log(isProxy(readonly({})))
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起
aaaa
aaaa
ModalButton.vue
I'm a teleported modal!
(My parent is "body")
App.vue
App
LOADING...
AsyncComp22
{{msg}}
{{data}}
VuePress官方在线文档
# 将 VuePress 作为一个本地依赖安装
npm install -D vuepress
# 新建一个 docs 文件夹
mkdir docs
# 新建一个文件: docs/README.md
echo '# Hello VuePress!' > docs/README.md
# 启动文档项目
npx vuepress dev docs
# 构建静态文件
npx vuepress build docs
|-- docs
|-- .vuepress
|-- config.js
|-- README.md
|-- dist
|-- dics
|-- .vuepress
|-- public
|-- ts-logo.png
|-- config.js
|-- chapter1
|-- 01_初识TS.md
|-- 02_安装TS.md
|-- 03_HelloWorld.md
|-- chapter2
|-- 1_type.md
|-- 2_interface.md
|-- 3_class.md
|-- 4_function.md
|-- 5_generic.md
|-- 6_other.md
|-- chapter3
|-- 01_react.md
|-- 02_vue.md
|-- chapter4
|-- README.md
|-- README.md
|-- package.json
// 注意: base的值为github仓库的名称
module.exports = {
base: '/ts-study/', /* 基础虚拟路径: */
dest: 'dist', /* 打包文件基础路径, 在命令所在目录下 */
title: 'TypeScript 入门', // 标题
description: '学习使用 TypeScript', // 标题下的描述
themeConfig: { // 主题配置
sidebar: [ // 左侧导航
{
title: '初识 TypeScript', // 标题
collapsable: false, // 下级列表不可折叠
children: [ // 下级列表
'chapter1/01_初识TS',
'chapter1/02_安装TS',
'chapter1/03_HelloWorld'
]
},
{
title: 'TypeScript 常用语法',
collapsable: false,
children: [
'chapter2/1_type',
'chapter2/2_interface',
'chapter2/3_class',
'chapter2/4_function',
'chapter2/5_generic',
]
},
]
}
}
---
#首页
home: true
# 图标
heroImage: /ts-logo.png
# 按钮文本
actionText: 开始学习 →
# 按钮点击跳转路径
actionLink: /chapter1/01_初识TS
---
"scripts": {
"doc:dev": "vuepress dev docs",
"doc:build": "vuepress build docs",
"doc:deploy": "gh-pages -d docs/dist"
}
使用git管理当前项目
将打包的项目推送到gitpage
# 下载工具包
yarn add -D gh-pages
# 执行打包命令
yarn doc:build
# 执行部署命令
yarn doc:deploy