文章内容输出来源:拉勾教育前端高薪训练营
cjs
global
browser
bundle
设计动机
Vue2的Options API
Vue3的Composition API
Composition API初体验
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="app">
x: {{ position.x }}
y: {{ position.y }}
div>
<script type="module">
import { createApp, reactive } from './node_modules/vue/dist/vue.esm-browser.js'
const app = createApp({
setup () {
// 第一个参数 props
// 第二个参数 context:attrs、emit、slots
const position = reactive({
x: 0,
y: 0,
})
return {
position,
}
},
mounted () {
this.position.x = 100
}
})
console.log(app)
app.mount('#app')
script>
body>
html>
setup函数及Composition中常用的API
setup是在组件初始化之前执行的,在beforeCreate和created之间,这两个生命周期内的代码可以直接放在setup中。生命周期包含
Options API | Hook inside inside setup |
---|---|
beforeCreate | Not needed |
created | Not needed |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
import { createApp, reactive, onMounted, onUnmounted } from './node_modules/vue/dist/vue.esm-browser.js'
const useMouseMove = () => {
// 第一个参数 props
// 第二个参数 context:attrs、emit、slots
const position = reactive({
x: 0,
y: 0,
})
const update = e => {
position.x = e.pageX
position.y = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return position
}
const app = createApp({
setup () {
const position = useMouseMove()
return {
position,
}
}
})
app.mount('#app')
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="app">
x: {{ x }}
y: {{ y }}
<button @click="handleAdd">addbutton>
<span>now count is {{ count }}span>
div>
<script type="module">
import { createApp, reactive, onMounted, onUnmounted, toRefs, ref } from './node_modules/vue/dist/vue.esm-browser.js'
const useMouseMove = () => {
// 第一个参数 props
// 第二个参数 context:attrs、emit、slots
const position = reactive({
x: 0,
y: 0,
})
const update = e => {
position.x = e.pageX
position.y = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return toRefs(position)
}
const useCount = () => {
const count = ref(0)
return {
count,
handleAdd: () => {
count.value++
},
}
}
const app = createApp({
setup () {
const { x, y } = useMouseMove()
return {
x,
y,
...useCount(),
}
}
})
app.mount('#app')
script>
body>
html>
computed(() => count.value + 1)
用法2:
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="app">
<button @click="push">按钮button>
未完成:{{ activeCount }}
div>
<script type="module">
import { createApp, reactive, computed } from './node_modules/vue/dist/vue.esm-browser.js'
const data = [
{ text: '看书', completed: false },
{ text: '敲代码', completed: false },
{ text: '约会', completed: true }
]
createApp({
setup () {
const todos = reactive(data)
const activeCount = computed(() => {
return todos.filter(item => !item.completed).length
})
return {
activeCount,
push: () => {
todos.push({
text: '开会',
completed: false
})
}
}
}
}).mount('#app')
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="app">
<p>
请问一个 yes/no 的问题:
<input v-model="question">
p>
<p>{{ answer }}p>
div>
<script type="module">
// https://www.yesno.wtf/api
import { createApp, ref, watch } from './node_modules/vue/dist/vue.esm-browser.js'
createApp({
setup () {
const question = ref('')
const answer = ref('')
watch(question, async (newValue, oldValue) => {
const response = await fetch('https://www.yesno.wtf/api')
const data = await response.json()
answer.value = data.answer
})
return {
question,
answer
}
}
}).mount('#app')
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div id="app">
<button @click="increase">increasebutton>
<button @click="stop">stopbutton>
<br>
{{ count }}
div>
<script type="module">
import { createApp, ref, watchEffect } from './node_modules/vue/dist/vue.esm-browser.js'
createApp({
setup () {
const count = ref(0)
const stop = watchEffect(() => {
console.log(count.value)
})
return {
count,
stop,
increase: () => {
count.value++
}
}
}
}).mount('#app')
script>
body>
html>
Vue.directive('editingFocus', {
bind(el, binding, vnode, prevVnode) {
binding.value && el.focus
},
inserted() {},
update() {}, // remove
componentUpdated() {},
unbind() {},
})
Vue3
app.directive('editingFocus', {
beforeMount(el, binding, vnode, prevVnode) {
binding.value && el.focus
},
mounted() {},
beforeUpdate() {}, // new
updated() {},
beforeUnmount() {}, // new
unmounted() {},
})
App.vue
todos
-
utils/useLocalStorage.js
function parse (str) {
let value
try {
value = JSON.parse(str)
} catch {
value = null
}
return value
}
function stringify (obj) {
let value
try {
value = JSON.stringify(obj)
} catch {
value = null
}
return value
}
export default function useLocalStorage () {
function setItem (key, value) {
value = stringify(value)
window.localStorage.setItem(key, value)
}
function getItem (key) {
let value = window.localStorage.getItem(key)
if (value) {
value = parse(value)
}
return value
}
return {
setItem,
getItem
}
}
响应式系统升级
编译优化
源码体积的优化
reactive
reactive vs ref
toRefs
把reactive返回的对象的每一个属性转换成类ref对象,以便对reactive返回的对象解构
依赖收集过程
模拟源码
const isObject = val => val !== null && typeof val === 'object'
const convert = target => isObject(target) ? reactive(target) : target
const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (target, key) => hasOwnProperty.call(target, key)
export function reactive (target) {
if (!isObject(target)) return target
const handler = {
get (target, key, receiver) {
// 收集依赖
track(target, key)
const result = Reflect.get(target, key, receiver)
return convert(result)
},
set (target, key, value, receiver) {
const oldValue = Reflect.get(target, key, receiver)
let result = true
if (oldValue !== value) {
result = Reflect.set(target, key, value, receiver)
// 触发更新
trigger(target, key)
}
return result
},
deleteProperty (target, key) {
const hadKey = hasOwn(target, key)
const result = Reflect.deleteProperty(target, key)
if (hadKey && result) {
// 触发更新
trigger(target, key)
}
return result
}
}
return new Proxy(target, handler)
}
let activeEffect = null
export function effect (callback) {
activeEffect = callback
callback() // 访问响应式对象属性,去收集依赖
activeEffect = null
}
let targetMap = new WeakMap()
export function track (target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}
export function trigger (target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => {
effect()
})
}
}
export function ref (raw) {
// 判断 raw 是否是ref 创建的对象,如果是的话直接返回
if (isObject(raw) && raw.__v_isRef) {
return
}
let value = convert(raw)
const r = {
__v_isRef: true,
get value () {
track(r, 'value')
return value
},
set value (newValue) {
if (newValue !== value) {
raw = newValue
value = convert(raw)
trigger(r, 'value')
}
}
}
return r
}
export function toRefs (proxy) {
const ret = proxy instanceof Array ? new Array(proxy.length) : {}
for (const key in proxy) {
ret[key] = toProxyRef(proxy, key)
}
return ret
}
function toProxyRef (proxy, key) {
const r = {
__v_isRef: true,
get value () {
return proxy[key]
},
set value (newValue) {
proxy[key] = newValue
}
}
return r
}
export function computed (getter) {
const result = ref()
effect(() => (result.value = getter()))
return result
}