±à¼ÍƼö: |
±¾ÎĽ«»áÈ«·½Î»µÄ½éÉÜVue3µÄй¦ÄÜ£¬ÐÂÓÅ»¯£¬ÐÂÌØÐÔ£¬ÒÔ¼°Éý¼¶Ö¸ÄÏ¡£
±¾ÎÄÀ´×ÔÓÚĽ¿Î,ÓÉ»ðÁú¹ûÈí¼þAnna±à¼ÍƼö¡£ |
|
Vue3»á´øÀ´Ð©Ê²Ã´£¿
¸ü¿ì
¸üС
¸üÒ×ÓÚά»¤
й¦ÄܺÍÌØÐÔ
¸ü¿ì
ÖØ¹¹ÁËVirtual DOM
Vue3ÖØÐ´ÁËÐéÄâDOMµÄʵÏÖ·½·¨£¬³õʼäÖȾ/¸üпÉÒÔÌáËÙ´ï100%¡£
¶ÔÓÚVue2.x°æ±¾µÄÐéÄâDOMÀ´Ëµ£¬Vue»á±éÀú<template>Ä£°åÖеÄËùÓÐÄÚÈÝ£¬²¢¸ù¾ÝÕâЩ±êÇ©Éú³É¶ÔÓ¦µÄÐéÄâDOM£¨ÐéÄâDOMÒ»°ãÖ¸²ÉÓÃkey/value¶ÔÏóÀ´±£´æ±êÇ©ÔªËØµÄÊôÐÔºÍÄÚÈÝ£©£¬µ±ÓÐÄÚÈݸıäʱ£¬±éÀúÐéÄâDOMÀ´diffÕÒµ½¶ÔÓ¦µÄ±êÇ©ÔªËØËù¶ÔÓ¦µÄDOM½Úµã£¬²¢¸Ä±äÆäÄÚÈÝ¡£ÀýÈçÏÂÃæÕâ¶ÎÄÚÈÝ£º
<template>
<div class="content"> <p>number1</p>
<p>number2</p> <p>number3</p>
<p>{{count}}</p> </div>
</template> |
µ±´¥·¢Ë«Ïò°ó¶¨Ê±£¬±éÀúËùÓеÄ<div>±êÇ©ºÍ<p>±êÇ©£¬ÕÒµ½{{count}}±äÁ¿¶ÔÓ¦µÄ<p>µÄDOM½Úµã£¬²¢¸Ä±äÆäÄÚÈÝ¡£Õâ¶ÔÓÚÄÇЩ´¿¾²Ì¬<p>µÄ½Úµã½øÐÐdiffÆäʵÊDZȽÏÀË·Ñ×ÊÔ´µÄ£¬µ±½ÚµãµÄÊýÁ¿ºÜÉÙʱ£¬±íÏÖ²¢²»Ã÷ÏÔ£¬µ«ÊÇÒ»µ©½ÚµãµÄÊýÁ¿¹ý´ó£¬ÔÚÐÔÄÜÉϾͻáÂýºÜ¶à¡£¶Ô´Ë£¬Vue3Ôٴλù´¡ÉϽøÐÐÁËÓÅ»¯Ö÷ÒªÓУº
±ê¼Ç¾²Ì¬ÄÚÈÝ£¬²¢Çø·Ö¶¯Ì¬ÄÚÈÝ¡£
¸üÐÂʱֻdiff¶¯Ì¬µÄ²¿·Ö¡£
Õë¶ÔÉÏÃæµÄ´úÂ룬Vue3ÖÐÊ×ÏÈ»áÇø·Ö³ö{{count}}Õⲿ·Ö¶¯Ì¬µÄ½Úµã£¬ÔÚ½øÐÐdiffʱ£¬Ö»Õë¶ÔÕâЩ½Úµã½øÐУ¬´Ó¶ø¼õÉÙ×ÊÔ´ÀË·Ñ£¬ÌáÉýÐÔÄÜ£¬¾ßÌåÕⲿ·ÖÂß¼¿ÉÒԲο¼Ô´Âë¡£
ʼþ»º´æ
ÎÒÃÇÖªµÀÔÚvue2ÖУ¬Õë¶Ô½Úµã°ó¶¨µÄʼþ£¬Ã¿´Î´¥·¢¶¼ÒªÖØÐÂÉú³ÉȫеÄfunctionÈ¥¸üС£ÔÚVue3ÖУ¬ÌṩÁËʼþ»º´æ¶ÔÏócacheHandlers£¬µ±cacheHandlers¿ªÆôµÄʱºò£¬±àÒë»á×Ô¶¯Éú³ÉÒ»¸öÄÚÁªº¯Êý£¬½«Æä±ä³ÉÒ»¸ö¾²Ì¬½Úµã£¬ÕâÑùµ±Ê¼þÔٴδ¥·¢Ê±£¬¾ÍÎÞÐèÖØÐ´´½¨º¯ÊýÖ±½Óµ÷Óûº´æµÄʼþ»Øµ÷·½·¨¼´¿É¡£
¿ªÆôcacheHandlers£º
import { createVNode
as _createVNode, toDisplayString as _toDisplayString,
openBlock as _openBlock, createBlock as _createBlock
} from "vue"
const _hoisted_1 = { id: "app" } export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div",
_hoisted_1, [
_createVNode("button", {
onClick: _cache[1] || (_cache[1] = $event =>
(_ctx.confirmHandler($event)))
}, "È·ÈÏ"),
_createVNode ("span", null, _toDisplayString (_ctx.vue3),
1 /* TEXT */)
]))
}
|
¹Ø±ÕcacheHandlers£º
import { createVNode
as _createVNode, toDisplayString as _toDisplayString,
openBlock as _openBlock, createBlock as _createBlock
} from "vue"
const _hoisted_1 = { id: "app" } export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div",
_hoisted_1, [
_createVNode("button", { onClick:
_ctx.confirmHandler }, "È·ÈÏ", 8 /*
PROPS */, ["onClick"]),
_createVNode("span", null, _toDisplayString(_ctx.vue3),
1 /* TEXT */)
]))
}
|
»ùÓÚProxyµÄÏìӦʽ¶ÔÏó
Proxy API¶ÔÓ¦µÄProxy¶ÔÏóÊÇES2015¾ÍÒÑÒýÈëµÄÒ»¸öÔÉú¶ÔÏó£¬ÓÃÓÚ¶¨Òå»ù±¾²Ù×÷µÄ×Ô¶¨ÒåÐÐΪ£¨ÈçÊôÐÔ²éÕÒ¡¢¸³Öµ¡¢Ã¶¾Ù¡¢º¯Êýµ÷Óõȣ©¡£
´Ó×ÖÃæÒâ˼À´Àí½â£¬Proxy¶ÔÏóÊÇÄ¿±ê¶ÔÏóµÄÒ»¸ö´úÀíÆ÷£¬ÈκζÔÄ¿±ê¶ÔÏóµÄ²Ù×÷£¨ÊµÀý»¯£¬Ìí¼Ó/ɾ³ý/ÐÞ¸ÄÊôÐԵȵȣ©£¬¶¼±ØÐëͨ¹ý¸Ã´úÀíÆ÷¡£Òò´ËÎÒÃÇ¿ÉÒÔ°ÑÀ´×ÔÍâ½çµÄËùÓвÙ×÷½øÐÐÀ¹½ØºÍ¹ýÂË»òÕßÐ޸ĵȲÙ×÷¡£ÀýÈçÏÂÃæµÄʾÀý£º
let foo = {
a:1,
b:2
}
let handler = {
set:(obj,key,value)=>{
console.log('set')
obj[key] = value
return true
}
}
let p = new Proxy(foo,handler) p.a = 3 // ´òÓ¡³öconsole.log('set')
|
ÔÚVue2.xÖУ¬Ê¹ÓÃObject.defineProperty()À´ÊµÏÖÏìӦʽ¶ÔÏ󣬶ÔÓÚһЩ¸´ÔӵĶÔÏ󣬻¹ÐèҪѻ·µÝ¹éµÄ¸øÃ¿¸öÊôÐÔÔö¼ÓÉÏgetter/setter¼àÌýÆ÷£¬ÕâʹµÃ×é¼þµÄ³õʼ»¯·Ç³£ºÄʱ£¬¶øVue3ÖУ¬composition-apiÌṩÁËÒ»ÖÖ´´½¨ÏìӦʽ¶ÔÏóµÄ·½·¨reactive£¬ÆäÄÚ²¿¾ÍÊÇÀûÓÃÁËProxy
APIÀ´ÊµÏֵģ¬ÕâÑù¾Í¿ÉÒÔ²»ÓÃÕë¶Ôÿ¸öÊôÐÔÀ´Ò»Ò»½øÐÐÌí¼Ó£¬¼õÉÙ¿ªÏúÌáÉýÐÔÄÜ¡£
¸üС
Tree shakingÖ§³Ö
Tree shakingÊÇÒ»¸öÊõÓͨ³£ÓÃÓÚÃèÊöÒÆ³ýJavaScriptÉÏÏÂÎÄÖеÄδÒýÓôúÂë(dead-code)£¬¾ÍÏñÒ»¿Ã´óÊ÷£¬½«ÄÇЩÎÞÓõÄÒ¶×Ó¶¼Ò¡µô¡£ËüÒÀÀµÓÚ
ES2015 Ä£¿éÓï·¨µÄ ¾²Ì¬½á¹¹ ÌØÐÔ£¬ÀýÈç import ºÍ export¡£Õâ¸öÊõÓïºÍ¸ÅÄîÔÚ´ò°ü¹¤¾ßrollupºÍwepackÖÐÆÕ¼°¿ªÀ´¡£
import {get}
from './api.js'
let doSome = ()=>{
get()
} doSome()
|
api.js´úÂ룺
let post = ()=>{
console.log('post')
}
export post
let get = ()=>{
console.log('get')
} export get |
export get
ÉÏÃæ´úÂëÖУ¬api.js´úÂëÖеÄpost·½·¨Ïà¹ØÄÚÈÝÊÇûÓб»ÒýÈëºÍʹÓõģ¬ÓÐÁËTree shakingÖ®ºó£¬Õⲿ·ÖÄÚÈÝÊDz»»á±»´ò°üµÄ£¬Õâ¾ÍÔÚÒ»¶¨³Ì¶ÈÉϼõÉÙÁË×ÊÔ´µÄ´óС¡£Ê¹ÓÃTree
shakingµÄÔÀíÊÇES6µÄÄ£¿é¾²Ì¬·ÖÎöÒýÈ룬Õâ¾Í¿ÉÒÔÔÚ±àÒëʱÕýÈ·Åжϵ½µ×¼ÓÔØÁËʲô´úÂ룬µ«ÊÇҪעÒâimport
ºÍ exportÊÇES6ÔÉúµÄ¶ø²»ÊÇͨ¹ýbabel»òÕßwebpackת»¯µÄ¡£
ÔÚVue3ÖУ¬¶Ô´úÂë½á¹¹½øÐÐÁËÓÅ»¯£¬ÈÃÆä¸ü¼Ó·ûºÏTree shakingµÄ½á¹¹£¬ÕâÑùʹÓÃÏà¹ØµÄapiʱ£¬²»»á°ÑËùÓеͼ´ò°ü½øÀ´£¬Ö»»á´ò°üÄãÓõ½µÄapi£¬ÀýÈ磺
<!-- vue
2.x -->
import Vue from 'vue' new Vue() Vue.nextTick(() => {}) const obj = Vue.observable({}) <!-- vue 3.x -->
import { nextTick, observable,createApp } from
'vue'
nextTick(() => {}) const obj = observable({}) createApp({})
|
ͬʱ£¬ÀýÈç<keep-alive>ºÍ<transition>,<teleport>µÈÄÚÖÃ×é¼þ£¬Èç¹ûûÓÐʹÓÃÒ²²»»á±»´ò°üµ½×ÊÔ´Àï¡£
¸üÒ×ÓÚά»¤
Vue3´ÓFlowÇ¨ÒÆµ½TypeScript
ÔÚVue3µÄÔ´Âë½á¹¹²ãÃæ£¬´ÓFlow¸Ä³ÉÁËTypeScriptÀ´±àд£¬Ò»°ãÀ´Ëµ¶ÔÓÚJavaScriptÔ´Âë¿ò¼ÜÀ´ËµÒýÈëÀàÐͼì²âÊǷdz£ÖØÒªµÄ£¬²»½ö¿ÉÒÔ¼õÉÙbugµÄ²úÉú£¬»¹¿ÉÒԹ淶һЩ½Ó¿ÚµÄ¶¨Ò壬Flowfacebook
³öÆ·£¬ÊÇÒ»¸ö¾²Ì¬ÀàÐͼì²âÆ÷£¬ÓÐÁËËü¾Í¿ÉÒÔÔÚJavaScriptÔËÐÐǰÕÒ³ö³£¼ûµÄ bug£¬°üÀ¨£º
×Ô¶¯ÀàÐÍת»»
null ÒýÓÃ
¿ÉÅ嵀 undefined is not a function
ÀýÈ磺
// @flow function foo(x: number): number {
return x + 10
} foo('hi') // ²ÎÊýxÐèҪΪnumberÀàÐÍ£¬·ñÔò¾Í»á±¨´í message: '[flow] string (This type is incompatible
with number See also: function call)'
|
ÕâÐ©ÌØÐÔºÍtypescript·Ç³£ÎǺϣ¬ËùÒÔÔÚVue3ÖÐÖ±½Ó²ÉÓÃÁËtypescriptÀ´½øÐÐÖØÐ´£¬´ÓÔ´Âë²ãÃæÀ´ÌáÉýÏîÄ¿µÄ¿Éά»¤ÐÔ¡£
´úÂëĿ¼½á¹¹×ñÑmonorepo
monorepoÊÇÒ»ÖÖ¹ÜÀí´úÂëµÄ·½Ê½£¬ËüµÄºËÐĹ۵ãÊÇËùÓеÄÏîÄ¿ÔÚÒ»¸ö´úÂë²Ö¿âÖУ¬µ«ÊÇ´úÂë·Ö¸îµ½Ò»¸ö¸öСµÄÄ£¿éÖУ¬¶ø²»ÊǶ¼·ÅÔÚsrcÕâ¸öĿ¼ÏÂÃæ¡£ÕâÑùµÄ·Ö¸î£¬Ã¿¸ö¿ª·¢Õߴ󲿷ÖʱֻÊǹ¤×÷ÔÚÉÙÊýµÄ¼¸¸öÎļþ¼ÐÒÔÄڵ쬲¢ÇÒÒ²Ö»»á±àÒë×Ô¼º¸ºÔðµÄÄ£¿é£¬¶øÇÒ²»»áµ¼ÖÂÒ»¸ö
IDE ´ò²»¿ªÌ«´óµÄÏîĿ֮ÀàµÄÊÂÇ飬ÕâÑùºÜ¶àÊÂÇé¾Í¼òµ¥Á˺ܶࡣÈçÏÂͼ£º

ĿǰÖîÈç Babel, React, Angular, Ember, Meteor, Jest µÈµÈ¶¼²ÉÓÃÁË
Monorepo ÕâÖÖ·½Ê½À´½øÐÐÔ´ÂëµÄ¹ÜÀí¡£
Vue2.xµÄĿ¼½á¹¹£º

Vue3µÄĿ¼½á¹¹£º

й¦ÄܺÍÌØÐÔ
Composition API
ÔÚVue2.xÖУ¬×é¼þµÄÖ÷ÒªÂß¼ÊÇͨ¹ýһЩÅäÖÃÏîÀ´±àд£¬°üÀ¨Ò»Ð©ÄÚÖõÄÉúÃüÖÜÆÚ·½·¨»òÕß×é¼þ·½·¨£¬ÀýÈçÏÂÃæµÄ´úÂ룺
export default
{
name: 'test',
components: {},
props: {},
data () {
return {}
},
created(){},
mounted () {},
watch:{},
methods: {}
}
|
ÕâÖлùÓÚÅäÖõÄ×é¼þд·¨³ÉΪOptions API£¨ÅäÖÃʽAPI£©£¬Vue3µÄÒ»´óºËÐÄÌØÐÔÊÇÒýÈëÁËComposition
API£¨×éºÏʽAPI£©,ÕâʹµÃ×é¼þµÄ´ó²¿·ÖÄÚÈݶ¼¿ÉÒÔͨ¹ýsetup()·½·¨½øÐÐÅäÖã¬Í¬Ê±Composition
APIÔÚVue2.xÒ²¿ÉÒÔʹÓã¬ÐèҪͨ¹ý°²×°@vue/composition-apiÀ´Ê¹Ó㬴úÂëÈçÏ£º
npm install
@vue/composition-api
...
import VueCompositionApi from '@vue/composition-api'; Vue.use(VueCompositionApi);
|
ÏÂÃæ¾ÍÁоÙһЩʹÓÃComposition APIµÄÀý×Ó£º
ref»òÕßreactive´úÌædataÖеıäÁ¿:
ÔÚVue2.xÖÐͨ¹ý×é¼þdataµÄ·½·¨À´¶¨ÒåһЩµ±Ç°×é¼þµÄÊý¾Ý£º
...
data() {
return {
name: 'test',
list: [],
}
},
...
|
ÔÚVue3ÖÐͨ¹ýref»òÕßreactive´´½¨ÏìӦʽ¶ÔÏó£º
import {ref,reactive}
from 'vue'
...
setup(){
const name = ref('test')
const state = reactive({
list: []
})
return {
name,
state
}
}
...
|
ref½«¸ø¶¨µÄÖµ´´½¨Ò»¸öÏìӦʽµÄÊý¾Ý¶ÔÏ󲢸³Öµ³õʼֵ£¨int»òÕßstring£©£¬reactive¿ÉÒÔÖ±½Ó¶¨Ò帴ÔÓÏìӦʽ¶ÔÏó¡£
methodsÖж¨ÒåµÄ·½·¨Ò²¿ÉÒÔдÔÚsetup()ÖÐ:
ÔÚVue2.xÖÐmethodsÀ´¶¨ÒåһЩµ±Ç°×é¼þÄÚ²¿·½·¨£º
...
methods: {
fetchData() {
},
}
...
|
ÔÚVue3ÖÐÖ±½ÓÔÚsetup·½·¨Öж¨Òå²¢return£º
...
setup(){
const fetchData = ()=>{
console.log('fetchData')
} return {
fetchData
}
}
...
|
setup()ÖÐʹÓÃpropsºÍthis:
ÔÚVue2.xÖУ¬×é¼þµÄ·½·¨ÖпÉÒÔͨ¹ýthis»ñÈ¡µ½µ±Ç°×é¼þµÄʵÀý£¬²¢Ö´ÐÐdata±äÁ¿µÄÐ޸쬷½·¨µÄµ÷Óã¬×é¼þµÄͨÐŵȵȣ¬µ«ÊÇÔÚVue3ÖУ¬setup()ÔÚbeforeCreateºÍcreatedʱ»ú¾ÍÒѵ÷Óã¬ÎÞ·¨Ê¹ÓúÍVue2.xÒ»ÑùµÄthis£¬µ«ÊÇ¿ÉÒÔͨ¹ý½ÓÊÕsetup(props,ctx)µÄ·½·¨£¬»ñÈ¡µ½µ±Ç°×é¼þµÄʵÀýºÍprops£º
export default
{
props: {
name: String,
},
setup(props,ctx) {
console.log(props.name)
ctx.emit('event')
},
}
|
×¢ÒâctxºÍ2.xÖÐthis²¢²»ÍêȫһÑù£¬¶øÊÇÑ¡ÔñÐԵر©Â¶ÁËһЩproperty£¬Ö÷ÒªÓÐ[attrs,emit,slots]¡£
watchÀ´¼àÌý¶ÔÏó¸Ä±ä
Vue2.xÖУ¬¿ÉÒÔ²ÉÓÃwatchÀ´¼àÌýÒ»¸ö¶ÔÏóÊôÐÔÊÇ·ñÓи͝£º
...
data(){
return {
name: 'a'
}
},
watch: {
name(val) {
console.log(val)
}
}
...
|
Vue3ÖУ¬ÔÚsetup()ÖУ¬¿ÉÒÔʹÓÃwatchÀ´¼àÌý£º
...
import {watch} from 'vue'
setup(){
let state = reactive({
name: 'a'
})
watch(
() => state.name,
(val, oldVal) => {
console.log(val)
}
)
state.name = 'b'
return {
state
}
}
...
|
ÔÚVue3ÖУ¬Èç¹ûwatchµÄÊÇÒ»¸öÊý×éarray¶ÔÏó£¬ÄÇôÈç¹ûµ÷ÓÃarray.push()·½·¨Ìí¼ÓÒ»ÌõÊý¾Ý£¬²¢²»»á´¥·¢watch·½·¨£¬±ØÐëÖØÐ¸øarray¸³Öµ£º
let state
= reactive({
list: []
})
watch(
() => state.list,
(val, oldVal) => {
console.log(val)
}
)
state.list.push(1) // ²»»á´¥·¢watch
state.list = [1] // »á´¥·¢watch
|
´ËÎÊÌâ²»ÖªÊÇ·ñÊÇVue3.xÌØÒâ¼ÓÉϵģ¬ÓдýÕýʽ°æ³öÀ´ºóÔÚÑéÖ¤¡£
computed¼ÆËãÊôÐÔ£º
Vue2.xÖУº
...
computed: {
storeData () {
return this.$store.state.storeData
},
},
...
|
Vue3ÖУº
...
import {computed} from 'vue'
setup(){
const storeData = computed(() => store.state.storeData) return {
storeData
}
}
...
|
µ±È»£¬¶ÔÓÚÍêÕûµÄVue Composition API£¬¸÷λͬѧ¿ÉÒԲο¼Îĵµ
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
|
Fragments
ÔÚVue2.xÖУ¬<template>ÖеÄÄÚÈݱØÐëÓÉÒ»¸ö×îÍâ²ãµÄ¸¸ÔªËذü¹ü£¬´úÂëÈçÏ£º
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
|
½á¹û·¢ÏÖReactÉçÇøÒ²Óöµ½ÁËͬÑùµÄÎÊÌâ¡£ËûÃÇÏë³öµÄ½â¾ö·½°¸ÊÇÒ»¸öÃûΪFragmentµÄÐéÄâÔªËØ£¬Ê¹Ó÷½·¨ÈçÏ£º
class Hello
extends React.Component {
render() {
return (
<React.Fragment>
<header>...</header>
<main>...</main>
<footer>...</footer>
</React.Fragment>
);
}
}
|
¶øÔÚVue3ÖУ¬Ê¹Óøü¼Ó¼òµ¥£¬¿ÉÒÔÖ±½ÓÊ¡ÂÔ×îÍâ²ãµÄÔªËØ£¬Ð´·¨ÈçÏ£º
<template>
<header>...</header>
<main>...</main>
<footer>...</footer>
</template>
|
ÔÚVue3ÖУ¬<teleport>ÊÇÒ»¸öÄÚÖñêÇ©£¬Èç¹ûÄãÔø¾´´½¨¹ýģ̬¹¦ÄÜ£¬Äã»áÖªµÀËüͨ³£±»·ÅÖÃÔڹرյÄ</body>±êǩ֮ǰ£¬ÈçÏ£º
<body>
<div>
<!--main page content here-->
</div>
<!--modal here-->
</body>
|
Èç¹û°´ÕÕÒÔÍùµÄ˼·£¬ÐèÒª½«Ä£Ì¬µÄUI´úÂë·ÅÔڵײ¿£¬ÈçÏ£º
<body>
<div id="app">
<h3>Tooltips with Vue 3 Teleport</h3>
</div>
<div>
<modal-button></modal-button>
</div>
</body>
|
ÕâÑù×öÊÇÒòΪģʽͨ³£¾ßÓи²¸ÇÒ³ÃæµÄ±³¾°£¬ÒªÊ¹ÓÃCSSÀ´ÊµÏÖ£¬Äú²»ÐèÒª´¦Àí¸¸ÔªËØ¶¨Î»ºÍz-index¶ÑÕ»ÉÏÏÂÎÄ£¬Òò´Ë×î¼òµ¥µÄ½â¾ö·½°¸Êǽ«Ä£Ê½·ÅÔÚÒ³ÃæµÄ×îµ×²¿¡£ÕâÑùµÄ»°Õⲿ·ÖÂß¼¾ÍÍÑÀëÁËÕû¸öÏîÄ¿µÄ¸ú×é¼þAppµÄ¹ÜÀí£¬¾ÍÔì³ÉÖ±½Ó²ÉÓÃJavaScriptºÍCSSÀ´ÐÞ¸ÄUI£¬²¢²»ÊǺܹ淶¡£ÎªÁËÔÊÐí½«Ò»Ð©UIƬ¶Î¶ÎÒÆ¶¯µ½Ò³ÃæÖÐµÄÆäËûλÖã¬ÔÚVue3ÖÐÌí¼ÓÁËÒ»¸öеÄ<teleport>×é¼þ¡£
ҪʹÓÃ<teleport>£¬Ê×ÏÈÒªÔÚÒ³ÃæÉÏÌí¼ÓÒ»¸öÔªËØ£¬ÎÒÃÇÒª½«Ä£Ì¬ÄÚÈÝÒÆ¶¯µ½¸ÃÒ³Ãæ¡£´úÂëÈçÏ£º
app.component('modal-button',
{
template: `
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button> <teleport to="#endofbody">
<div v-if="modalOpen" class="modal">
...
</div>
</teleport>
`,
data() {
return {
modalOpen: false
}
}
})
|
<body>
<div id="app">
<h3>Tooltips with Vue 3 Teleport</h3>
</div>
<div id="endofbody"></div>
</body>
|
Æä#endofbody±íʾ´Ë²¿·ÖÄÚÈݽ«»áÌí¼Óµ½Ò³Ãæµ×²¿¡£
<Suspense>
<Suspense>ÊÇÒ»¸öÌØÊâµÄ×é¼þ£¬Ëü½«³ÊÏÖ»ØÍËÄÚÈÝ£¬¶ø²»ÊǶÔÓÚµÄ×é¼þ£¬Ö±µ½Âú×ãÌõ¼þΪֹ£¬ÕâÖÖÇé¿öͨ³£ÊÇ×é¼þsetup¹¦ÄÜÖз¢ÉúµÄÒì²½²Ù×÷»òÕßÊÇÒì²½×é¼þÖÐʹÓá£ÀýÈçÕâÀïÓÐÒ»¸ö³¡¾°£¬¸¸×é¼þչʾµÄÄÚÈݰüº¬Òì²½µÄ×Ó×é¼þ£¬Òì²½µÄ×Ó×é¼þÐèÒªÒ»¶¨µÄʱ¼ä²Å¿ÉÒÔ¼ÓÔØ²¢Õ¹Ê¾£¬Õâʱ¾ÍÐèÒªÒ»¸ö×é¼þ´¦ÀíһЩռλÂß¼»òÕß¼ÓÔØÒì³£Âß¼£¬ÒªÓõ½<Suspense>£¬ÀýÈ磺
<Suspense>
<template >
<Suspended-component />
</template>
<template #fallback>
Loading...
</template>
</Suspense>
|
ÉÏÃæ´úÂëÖУ¬¼ÙÉè<Suspended-component>ÊÇÒ»¸öÒì²½×é¼þ£¬Ö±µ½ËüÍêÈ«¼ÓÔØ²¢äÖȾǰ¶¼»áÏÔʾռλÄÚÈÝ£ºLoading£¬Õâ¾ÍÊÇ<Suspense>µÄ¼òµ¥Ó÷¨£¬¸ÃÌØÐÔºÍFragmentÒÔ¼°<teleport>Ò»Ñù£¬Áé¸ÐÀ´×ÔReact¡£
Vite
°éËæ×ÅVue3£¬VueÍŶÓÒ²ÍÆ³öÁË×Ô¼ºµÄ¿ª·¢¹¹½¨¹¤¾ßVite£¬¿ÉÒÔÔÚÒ»¶¨³Ì¶ÈÉÏÈ¡´úvue-cliºÍwebpack-dev-serverµÄ¹¦ÄÜ£¬»ùÓÚ´ËViteÖ÷ÒªÓÐÒÔÏÂÌØÐÔ£º
¿ìËÙµÄÀäÆô¶¯
¼´Ê±µÄÄ£¿éÈȸüÐÂ
ÕæÕýµÄ°´Ðè±àÒë
ViteÔÚ¿ª·¢»·¾³Ï»ùÓÚä¯ÀÀÆ÷ÔÉú ES Modules ¿ª·¢£¬ÔÚÉú²ú»·¾³Ï»ùÓÚ Rollup ´ò°ü£¬ÎÒÃÇÏÈÀ´Á˽âÒ»ÏÂES
Modules¡£
ES Modules
ES Modules¼ò³ÆESM£¬ÊÇ JavaScript ¹Ù·½µÄ±ê×¼»¯Ä£¿éϵͳ¡£Á˽â¹ýseaJSºÍrequireJS£¨ÒÔ¼°ºóÆÚµÄwebpackºÍbabel£©µÄ¶¼Ó¦¸ÃÖªµÀʲôÊÇ¡°Ä£¿é»¯¡±£¬ÔÚʹÓÃÕâÁ½¸ö¿âÊÇ£¬»áÓõ½require()·½·¨È¥¼ÓÔØÄ£¿é£¬Ê¹ÓÃdefine()·½·¨È¥¶¨ÒåÄ£¿é£¬ÕâЩ·½·¨¶¼ÊÇ¿âÌṩ¸øÎÒÃǵ쬶øä¯ÀÀÆ÷ÔÉúÊÇÎÞ·¨Ê¶±ðµÄ¡£¶øESMÊÇES6¸øÎÒÃÇÌṩµÄ±ê×¼API£¬ÔÚä¯ÀÀÆ÷Ö§³ÖµÄÇé¿öÏ£¬ÎÒÃÇ¿ÉÒÔʹÓÃimport()ÒÔ¼°export()À´ÊµÏÖ¡°Ä£¿é»¯¡±£¬Ê¹Ó÷½·¨ÈçÏ£º
µ¼³öÄ£¿é(a.js)£º
export const
name = 'tenny'; export function get(num) { return {
num: num+10
};
}
|
µ¼ÈëÄ£¿é(b.js)£º
import {
name, get } from '/a.js'; import * as aModule from '/a.js';
|
ÔÚHTMLÖÐÒýÈëʹÓÃÁËESMµÄjsÎļþÐèÒªÔÚ<script>±êÇ©ÉÏÌí¼Ótype="module"£¬ÀýÈ磺
<script
type="module" src="b.js"></script>
|
ÒÔÉϾÍÊǼòµ¥µÄES ModulesÓ÷¨£¬Vite»ùÓÚÕâÖÖд·¨¸ü·ûºÏTree ShakingµÄ½á¹¹¹æ·¶£¬Í¬Ê±Ìí¼ÓÁË×Ô¼ºµÄdev-serverºÍHMR(ÈȸüÐÂ)»úÖÆ£¬ÕâÑù¾Í¿ÉÒÔÂú×㿪·¢½×¶ÎµÄ¹¦ÄÜÌṩ¡£
Rollup
Rollup ÊÇÒ»¸ö JavaScript Ä£¿é´ò°üÆ÷£¬Ò²ÊÇ×îÏÈÌá³öTree Shaking¸ÅÄºÍWebpack»òÕßBrowserifyÓÐ×ÅͬÑùµÄÄ£¿é´ò°ü¹¦ÄÜ£¬ËüµÄ×î´óÌØµãÊÇ»ùÓÚES
Modules½øÐдò°ü£¬²»ÐèҪͨ¹ýÀàËÆBabelת»¯µÄ·½°¸½«importת»¯³ÉCommonjsµÄrequire·½Ê½£¬¼«´óµØÀûÓÃä¯ÀÀÆ÷µÄÔÉúÌØÐÔ¡£
»ùÓÚTree ShakingÌØÐÔ£¬Rollup¿ÉÒÔ×î´ó»¯Ëü±£³Ö´ò°üºóµÄÎļþÌå»ý¸üС£¬ÕâÒ²ÊÇViteÔÚÉú²ú»·¾³Ï²ÉÓÃRollupµÄÖ÷ÒªÔÒò¡£
´´½¨Ò»¸öVite app
$ npm init
vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
|
²»Ö»ÏÞÓÚVue£¬ViteͬʱÌṩÁË´´½¨React appµÄÄÜÁ¦£¬¿ÉÒÔͨ¹ýÅäÖÃÄ£°åÀ´Éú³É£º
npm init
vite-app --template react or --template preact
|
µ±È»£¬ViteÔÚʵ¼ÊÔËÓÃÖпÉÄܲ»µ¥µ¥ÊǼòµ¥´´½¨£¬ÆÚ¼ä»¹ÓÐһЩÅäÖÃÏÀýÈçVite+typescript£¬ÕâÀïÎÒÌṩ¼¸¸ö´´½¨ºÃµÄÄ£°åÏîÄ¿À´¹©²Î¿¼£º
vue3+typescript+vue -class-component+vite
vue3+typescript+vue-class-component+vuecli
Vue2.xÉý¼¶Ö¸ÄÏ
¶ÔÓÚÏÖÓеÄVue2.xÏîÄ¿£¬Èç¹ûÏëҪƽ»¬µÄÉý¼¶µ½Vue3£¬ÔÚ²»Ê¹ÓÃһЩÐÂÌØÐÔµÄÇé¿öÏ£¬¸Ä¶¯»¹²»ËãºÜ´ó£¬ÐèÒª¹Ø×¢ÒÔÏÂÎÊÌâ¡£
Vue3ÖÐÒÆ³ýµÄһЩAPIºÍ·½·¨
È¡ÏûKeyboardEvent.keyCode
ÔÚVue2.xÖУ¬°ó¶¨¼üÅÌʼþ»áÓõ½ÈçÏ´úÂ룺
<!-- keyCode
version -->
<input v-on:keyup.13="submit" /> <!-- alias version -->
<input v-on:keyup.enter="submit"
/>
|
»òÕßÊÇ£º
Vue.config.keyCodes
= {
f1: 112
}
<!-- keyCode version -->
<input v-on:keyup.112="showHelpText"
/> <!-- custom alias version -->
<input v-on:keyup.f1="showHelpText"
/>
|
ÔÚʼþÖУ¬¸økeyupÅäÖÃÒ»¸öÖ¸¶¨°´Å¥µÄkeyCode(Êý×Ö)ÔÚVue3Öн«²»»áÉúЧ£¬µ«ÊÇÒÀÈ»¿ÉÒÔʹÓñðÃû£¬ÀýÈ磺
<input
v-on:keyup.delete="confirmDelete"
/>
|
ÒÆ³ý$on, $off ºÍ $once·½·¨
ÔÚVue2.xÖпÉÒÔͨ¹ýEventBusµÄ·½·¨À´ÊµÏÖ×é¼þͨÐÅ£º
var EventBus
= new Vue()
Vue.prototype.$EventBus = EventBus
...
this.$EventBus.$on() this.$EventBus.$emit()
|
ÕâÖÖÓ÷¨ÔÚVue3ÖоͲ»ÐÐÁË£¬ÔÚVue3ÖÐÒÆ³ýÁË$on, $offµÈ·½·¨£¨²Î¿¼rfc£©£¬¶øÊÇÍÆ¼öʹÓÃmitt·½°¸À´´úÌæ£º
import mitt
from 'mitt'
const emitter = mitt()
// listen to an event
emitter.on('foo', e => console.log('foo',
e) )
// fire an event
emitter.emit('foo', { a: 'b' })
|
ÒÆ³ýfilters
ÔÚVue3ÖУ¬ÒƳýÁË×é¼þµÄfiltersÏ¿ÉÒÔʹÓÃmethodsµÄ»òÕßcomputedÀ´½øÐÐÌæ´ú£º
<template>
<p>{{ accountBalance | currencyUSD }}</p>
</template>
<script>
export default { filters: {
currencyUSD(value) {
return '$' + value
}
}
}
</script>
|
Ìæ»»Îª£º
<template>
<p>{{ accountInUSD }}</p>
</template> <script>
export default {
props: {
accountBalance: {
type: Number,
required: true
}
},
computed: {
accountInUSD() {
return '$' + this.accountBalance
}
}
}
</script>
|
ÒÆ³ýinline-template
ÔÚVue2.xÖУ¬ÔÚ¸¸×é¼þÒýÈë×Ó×é¼þʱ£¬»áÓõ½inline-templateÀ´Ê¹×Ó×é¼þµÄÄÚÈÝÒ²µÃµ½Õ¹Ê¾£¬²Î¿¼ÕâÀÀýÈ磺
<my-component
inline-template>
<div>
<p>These are compiled as the component's
own template.</p>
<p>Not parent's transclusion content.</p>
</div>
</my-component>
|
ÔÚVue3ÖУ¬Õâ¸ö¹¦Äܽ«±»ÒƳý£¬Ä¿Ç°inline-templateʹÓõIJ¢²»¶à£¬ÕâÀï¾Í²»ÔÙ¹ý¶à½²½â¡£
Vue3ÖиıäµÄAPIºÍд·¨
¸ùʵÀý³õʼ»¯:
ÔÚ2.xÖÐͨ¹ýnew Vue()µÄ·½·¨À´³õʼ»¯£º
import
App from './App.vue'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
|
ÔÚ3.xÖÐVue²»ÔÙÊÇÒ»¸ö¹¹Ô캯Êý£¬Í¨¹ýcreateApp·½·¨³õʼ»¯£º
import App from './App.vue'
createApp(App).use(store).mount('#app')
|
È«¾ÖAPIµ÷Ó÷½Ê½¸Ä±ä
ÔÚVue2.xÖУ¬´ó²¿·ÖÈ«¾ÖAPI¶¼ÊÇͨ¹ýVue.xxx»òÕßVue.abc()·½Ê½µ÷Óã¬ÀýÈ磺
import Vue from 'vue' Vue.mixin() Vue.use()
...
|
¶øÔÚVue3ÖУ¬ÕâЩ·½Ê½½«»á¸Ä±ä£¬È¡¶ø´úÖ®µÄÊÇÈçÏ£º
import { createApp
} from 'vue' const app = createApp({}) app.mixin() app.use() ...
|
ͬʱ£¬¿ÉÒÔÖ»ÒýÈëһЩÐèÒªµÄAPI£¬²»ÐèÒªµÄ²»ÓÃÒýÈ룬ÕâÑùÒ²·ûºÏThree
ShakingµÄÒªÇó£¬ÀýÈ磺
import { nextTick,reactive,onMounted
} from 'vue' nextTick(() => {
})
onMounted(() => {
})
|
ÓÉÓÚVue3ÖÐÈ«¾ÖAPI¶¼»áͨ¹ýapp.xxxµÄ·½·¨µ÷Óã¬ËùÒÔ֮ǰͨ¹ýVue.prototype.xxx°ó¶¨µÄÈ«¾Ö·½·¨ºÍ±äÁ¿½«ÎÞ·¨Ê¹Ó㬿ÉÒÔ²ÉÓÃÈçÏ·½Ê½À´´úÌæ£º
//ÔÚmain.jsÖУº
app.config.globalProperties.http = function(){} //ÔÚvue×é¼þÖУº
this.http()
|
render·½·¨ÐÞ¸Ä
ÔÚVue2.xÖУ¬ÓÐʱ»á×Ô¶¨Òårender·½·¨À´·µ»ØÄ£°åÄÚÈÝ£¬ÈçÏ£º
export
default {
render(h) {
return h('div')
}
}
|
ÔÚVue3ÖУ¬hͨ¹ývueÀ´ÒýÈ룬ÈçÏ£º
import
{ h } from 'vue' export default {
render() {
return h('div')
}
}
|
еÄÒì²½×é¼þ´´½¨·½Ê½
ÔÚVue2.xÖУ¬ÓÈÆäÊÇÔÚVue RouterÖУ¬»á¾³£Ê¹Óõ½Òì²½×é¼þ£¬½èÖúwebpackµÄ´ò°ü·½Ê½£¬¿ÉÒÔ½«Ò»¸ö×é¼þµÄ´úÂë½øÐÐÒì²½»ñÈ¡£¬ÀýÈ磺
const
asyncPage = () => import('./NextPage.vue') const asyncPage = {
component: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent
}
|
ÔÚVue3ÖУ¬ÌṩÁËdefineAsyncComponent()·½·¨´´½¨Òì²½×é¼þ£¬Í¬Ê±¿ÉÒÔ·µ»ØÒ»¸öPromise¶ÔÏóÀ´×Ô¼º¿ØÖƼÓÔØÍê³Éʱ»ú£¬ÈçÏ£º
import
{ defineAsyncComponent } from 'vue' const asyncPageWithOptions = defineAsyncComponent({
loader: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent
}) const asyncComponent = defineAsyncComponent(
() =>
new Promise((resolve, reject) => {
/* ... */
})
)
|
dataÊôÐÔÖ»Ö§³Öfunction
ÔÚVue2.xÖУ¬¸ù×é¼þµÄdata¿ÉÒÔÖ±½ÓÅäÖÃÒ»¸ö¶ÔÏó£¬×Ó×é¼þµÄdata¿ÉÒÔÅäÖÃfunction£¬È»ºó·µ»ØÒ»¸ö¶ÔÏó£¬ÈçÏ£º
<!--
Object Declaration -->
<script>
const app = new Vue({
data: {
apiKey: 'a1b2c3'
}
})
</script> <!-- Function Declaration -->
<script>
const child = new Vue({
data() {
return {
apiKey: 'a1b2c3'
}
}
})
</script>
|
ÔÚVue3ÖУ¬ËùÓÐ×é¼þ¶¼Ö»Ö§³ÖÅäÖÃfunction·µ»ØÒ»¸ö¶ÔÏóµÄ·½Ê½£¬ÈçÏ£º
<script>
import { createApp } from 'vue' createApp({
data() {
return {
apiKey: 'a1b2c3'
}
}
}).mount('#app')
</script>
|
ʹÓÃ×Ô¶¨Òå±êÇ©ºÍisÊôÐԸ͝
ÔÚVue2.xÖУ¬¿ÉÒÔʹÓÃһЩ·ÇHTML±ê×¼µÄ±êÇ©£¨ÀýÈçʹÓÃWeb
Components£©£¬ÐèÒªÔÚVueÈ«¾ÖÅäÖÃÖÐÉùÃ÷ºöÂԸñêÇ©£¬ÀýÈ磺
<plastic-button></plastic-button> Vue.config.ignoredElements = ['plastic-button']
|
ÔÚVue3ÖУ¬Í¬ÑùÖ§³Ö×Ô¶¨Òå±êÇ©£¬ÉùÃ÷ʱ¿ÉÒÔʹÓÃÈçÏ£º
const
app = Vue.createApp({})
app.config.isCustomElement = tag => tag ===
'plastic-button'
|
³ýÁË×Ô¶¨Òå±êÇ©µÄÐ޸ģ¬ÔÚVue2.xÖУ¬»áÓõ½¶¯Ì¬×é¼þis£¬ÀýÈ磺
<button is="plastic-button">
Click Me!</button>
|
ÓÉÓÚisµÄÌØÐÔ£¬ÕâÖÖд·¨ÔÚVue2.x×îÖջᱻäÖȾ³É<plastic-button>×é¼þ£¬µ«ÊÇÔÚVue3ÖУ¬Ö»»á°Ñisµ±×÷Ò»¸öÆÕͨµÄpropsÊôÐÔ£¬Èç¹ûÏëʵÏÖVue2.xÒ»ÑùµÄЧ¹û£¬¿ÉÒÔʹÓÃv-is£¬ÀýÈ磺
<button v-is="plastic-button">Click
Me!</button> //result£º <plastic-button></plastic-button>
|
ÐèҪעÒâµÄÊÇÔÚÌØÊâÔªËØ<component>ÉÏÅäÖõÄisÊôÐÔÓ÷¨ÒÀÈ»ÉúЧ¡£
this.$scopedSlotsÌæ´úΪthis.$slots
ÔÚVue2.xµÄijЩ³¡¾°£¬ÌرðÊÇÓõ½×Ô¶¨Òårender·½·¨ºÍ²å²Ûʱ£¬»áÓõ½this.$scopedSlots»ñÈ¡Êý¾Ý£¬ÀýÈ磺
h(LayoutComponent,
[
h('div', { slot: 'header' }, this.header),
h('div', { slot: 'content' }, this.content)
])
this.$scopedSlots.header
|
ÔÚVue3ÖУ¬this.$scopedSlots½«»á±»ÒƳý£¬Í³Ò»Ìæ´ú³Éthis.$slots£¬ÀýÈ磺
h(LayoutComponent,
{}, {
header: () => h('div', this.header),
content: () => h('div', this.content)
})
this.$slots.header
|
×Ô¶¨ÒåÖ¸ÁîÉúÃüÖÜÆÚ·½·¨ÐÞ¸Ä
ÔÚVue2.xÖУ¬ÓÐʱ»á×Ô¼º´´½¨×Ô¶¨ÒåÖ¸ÁÀýÈ磺
<p v-highlight="yellow">
Highlight this text bright yellow</p> |
ÔÚ×Ô¶¨ÒåÖ¸ÁîÖУ¬¿ÉÒÔʹÓÃVueÌṩµÄһЩÉùÃ÷ÖÜÆÚ·½·¨£¬Íê³É¾ßÌåµÄÂß¼£¬ÀýÈ磺
Vue.directive('highlight',
{
bind(el, binding, vnode) {
el.style.background = binding.value
},
inserted(),
beforeUpdate(),
update(),
componentUpdated(),
beforeUnmount(),
unbind()
})
|
ÔÚVue3ÖУ¬Ö÷Òª¶ÔÕâЩÉúÃüÖÜÆÚ·½·¨½øÐÐÁ˵÷Õû£¬ÈçÏ£º
Vue.directive('highlight',
{
beforeMount(el, binding, vnode) { // ¶ÔÓ¦bind
el.style.background = binding.value
},
mounted() {}, // ¶ÔÓ¦inserted
beforeUpdate() {}, // ÐÂÔö
updated() {}, // ¶ÔÓ¦update
beforeUnmount() {}, // ÐÂÔö
unmounted() {} // ¶ÔÓ¦unbind
})
|
watch·½·¨²»ÔÙÖ§³Ö"µã·Ö¸ô"д·¨
ÔÚVue2.xÖУ¬Ê¹ÓÃwatch·½·¨¼àÌý¶ÔÏó¸Ä±äʱ£¬Èç¹û¶ÔÏó²ã¼¶½ÏÉ¿ÉÒÔ²ÉÓá°µã·Ö¸î¡±µÄд·¨£¬ÀýÈ磺
var vm = new Vue({
data: {
e: {
f: {
g: 5
}
}
},
watch: {
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
})
|
ÕâÖÖд·¨ÔÚVue3Öн«²»ÔÚÖ§³Ö£¬°üÀ¨Ê¹ÓÃʵÀý·½·¨this.$watch¡£
IE¼æÈÝ
»ùÓÚÉÏÎÄÖÐÌáµ½µÄES6µÄProxyÌØÐÔ£¬¶ÔÓÚIE11¼æÈÝÐÔ²¢²»ÓѺ㬺ÃÔÚVueÍŶӻáÌṩ¼æÈÝIE11µÄ°æ±¾£¬µ«ÊÇijЩеÄÌØÐÔ¿ÉÄܾÍÎÞ·¨Ê¹ÓÃÁË¡£
×ܽá
Vue2»¹»áÌṩһ¸öÎȶ¨µÄ2.7°æ±¾£¬²¢½«»á³¤ÆÚά»¤£¬ËùÒÔ»¹Êǽ¨Ò飬Èç¹ûÄãµÄÏîÄ¿ºÜÎȶ¨£¬ÇÒ¶Ôй¦ÄÜÎÞ¹ý¶àµÄÒªÇó»òÕßÇ¨ÒÆ³É±¾¹ý¸ß£¬Ôò²»½¨ÒéÉý¼¶Vue3¡£
¸½ÉÏ»ùÓÚVue3¿ª·¢µÄToDoListÏîÄ¿´«ËÍÃÅ
|