vue3
安装创建项目
安装/升级脚手架
未安装过vue-cli npm install -g @vue/cli
或 yarn global add @vue/cli
已安装直接升级 npm update -g @vue/cli
或 yarn global upgrade --latest @vue/cli
创建项目
使用官方的vue-cli脚手架升级安装
vue create vue3-test
创建名为vue3-test的项目 cd vue3-test
切入目录运行 yarn serve
或 npm run serve
忘记选择3.0可以yarn add vue-next
自动升级到vue3的版本 设置别名 tsconfig.json
{
"compilerOptions" : {
"paths" : {
"@/*" : [ "src/*" ]
}
}
}
使用vite安装 (Vite当前仅适用于Vue3.x!!! )
git clone https://github.com/vitejs/vite.git
拉取代码到本地 cd vite
进入目录 yarn
安装依赖 yarn build
打包 yarn link
创建项目 yarn create vite-app vue3-test
进入目录 cd project-name
安装依赖 yarn
或 npm i
运行 yarn dev
或npm run dev
设置别名,新建vite.config.js
文件 module. exports= {
alias: {
'/@/' : path. resolve ( __dirname, './src' ) ,
'/@view/' : path. resolve ( __dirname, './src/views' ) ,
'/@components/' : path. resolve ( __dirname, './src/components' ) ,
'/@utils/' : path. resolve ( __dirname, './src/utils' ) ,
} ,
}
与vue2的区别
全部采用函数式写法替换原来类的写法,源码用ts编写中实现了对ts更好的支持 移除了原有的生命周期函数和data(vue3中去掉了data,使用setup的返回值来给模板绑定value,return的对象如果是常量不会变成响应式数据)、computed、watch、method等vue2中的对象,去掉了this(this在setup中不可用。由于setup()在解析 2.x 选项前被调用,setup() 中的 this 将与 2.x 选项中的 this 完全不同。同时在 setup() 和 2.x 选项中使用 this 时将造成混乱。),并且去除了过滤器api -> filter 完全兼容vue2,两种写法可同时存在 组件中同时存在两种写法时,当setup返回值中定义的方法和methods中的方法同名时,会抛出错误.定义的数据和data定义的数据字段相同时,会被data定义的字段覆盖 vue3采用proxy的方式实现数据代理,只会代理第一层数据,在 vue2 中响应式数据需要通过 data() 初始化预先定义好,但是在 vue3 之后,data 中的数据不再进行响应式追踪,而是将它转化为proxy 代理再进行追踪更新,避免了vue2中对data的深层递归,提升了组件渲染性能在vue3中,它将任何在data()函数中定义的数据,会使用getter或setter遍历所有的property,将其转化成proxy代理。
Vue3流程图
app 挂载流程
调用 createApp(App) 生成应用实例,然后调用mount方法 调用 mount 的阶段,首先根据入口组件 App 创建VNode,接着触发 render(reactive+effect) 函数,整个render的阶段,就是 Patch 所有 Vnode 的阶段,当中分为初始化挂载与更新组件 mount 对于内部 patch 的阶段,会对不同节点类型有不同的处理,主要是组件与element,组件就继续执行创建、挂载等等 data/props 挂到 ctx 上
createApp创建应用(较之前new Vue的方式在使用上基本无差别)
import { createApp } from 'vue'
import App from './App.vue' ;
import './index.css' ;
createApp ( App) . mount ( '#app' )
setup函数:在组件内使用 Composition API 的入口点
创建组件实例-初始化props-调用setup(setup只执行一次,位于组件生命周期beforeCreate钩子之前,所以拿不到当前实例this,不能用this来调用vue2写法中定义的方法) 在vue2中,data数据需要在使用的时候调用this访问,现在vue3中使用reactive,ref来设置响应式数据。 使用:
模板中:setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文中() 渲染函数 / JSX 中:可返回一个函数,函数中也能使用当前 setup 函数作用域中的响应式数据 < template>
< div> {{ count }} {{ object.foo }}</ div>
</ template>
< script>
import { ref, reactive } from 'vue'
export default {
setup ( ) {
const count = ref ( 0 )
const object = reactive ( { foo: 'bar' } )
return { count, object }
} ,
}
</ script>
setup ( ) {
function Func ( ) {
console. log ( 1111 ) ;
}
return {
Func,
} ;
}
setup的第一个参数,就是props,setup的第二个参数,提供了上下文对象,并且从2.x中选择性暴露了一些属性。例如:需要用到this.$emit用context.emit方法来替代 import { SetupContext } from 'vue' ;
emits: [ 'eventName' ] ,
setup ( props, ctx: SetupContext) {
ctx. emit ( 'event-name' , value) ;
}
生命周期函数
生命周期都变成了回调的形式,只在setup函数中使用有效,可以一次写多个相同的生命周期函数,按照注册顺序执行,生命周期钩子注册函数只能在 setup() 期间同步使用,组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除。 import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted , onErrorCaptured, onRenderTriggered } from 'vue' ;
setup ( ) {
onBeforeMount ( ( ) => {
} ) ;
onMounted ( ( ) => {
} ) ;
onBeforeUpdate ( ( ) => {
} ) ;
onUpdated ( ( ) => {
} ) ;
onBeforeUnmount ( ( ) => {
} ) ;
onUnmounted ( ( ) => {
} ) ;
onErrorCaptured ( ( e) => {
} ) ;
onRenderTriggered ( ( e) => {
} ) ;
} ,
getCurrentInstance 支持在setup函数中访问自己的实例 import { getCurrentInstance } from 'vue' ;
setup ( ) {
const internalInstance = getCurrentInstance ( ) ;
}
ref:简单的响应式数据
接受一个参数值并返回一个响应式且可改变的ref 对象,ref 对象拥有一个指向内部值的单一属性.value,如果传入 ref 的是一个对象,将调用 reactive 方法进行深层响应转换 setup 返回的 ref 在模板中会自动解开,可直接渲染在页面上,不需要.value 当 ref 作为 reactive 对象的 property 被访问或修改时,也将自动解套 value 值 当嵌套在 reactive Object 中时,ref 才会解套。从 Array 或者 Map 等原生集合类中访问 ref 时,不会自动解套 < template>
< div> {{ count }}</ div>
</ template>
< script>
export default {
setup ( ) {
const count = ref ( 0 ) ;
const state = reactive ( { count} ) ;
console. log ( count. value) ;
count. value++ ;
console. log ( count. value) ;
state. count = 2 ;
console. log ( count. value) ;
const otherCount = ref ( 3 ) ;
state. count = otherCount;
console. log ( state. count) ;
const arr = reactive ( [ ref ( 0 ) ] ) ;
console. log ( arr[ 0 ] . value) ;
const map = reactive ( new Map ( [ [ 'foo' , ref ( 0 ) ] ] ) ) ;
console. log ( map. get ( 'foo' ) . value) ;
return { count }
}
}
</ script>
reactive:接收一个普通**对象*然后返回该对象的响应式副本,等同2.x的Vue.observable()
响应式转换是深层的,会影响对象内部所有嵌套的属性。 computed:计算属性
传入一个 getter 函数,返回一个默认不可手动修改 的 ref 对象 const count = ref ( 1 ) ;
const plusOne = computed ( ( ) => count. value + 1 ) ;
console. log ( plusOne. value) ;
plusOne. value++ ;
传入一个拥有 get 和 set 函数的对象,创建一个可手动修改 的计算状态 const count = ref ( 1 ) ;
const plusOne = computed ( {
get : ( ) => count. value + 1 ,
set : ( val) => { count. value = val - 1 } ,
} ) ;
plusOne. value = 1 ;
console. log ( count. value) ;
readonly:获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。只读代理是深层的:访问的任何嵌套 property 也是只读的. const original = reactive ( { count: 0 } )
const copy = readonly ( original)
watchEffect ( ( ) => {
console. log ( copy. count)
} )
original. count++
copy. count++
watchEffect:立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
侦听数据变化: const count = ref ( 0 )
watchEffect ( ( ) => console. log ( count. value) )
setTimeout ( ( ) => { count. value++ ; } , 100 )
停止侦听:
清除副作用:侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参, 用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:
副作用即将重新执行时 侦听器被停止 (如果在 setup() 或 生命周期钩子函数中使用了 watchEffect, 则在卸载组件时) watchEffect ( ( onInvalidate) => {
const token = performAsyncOperation ( id. value)
onInvalidate ( ( ) => {
token. cancel ( )
} )
} )
watch:等同vue2.x中watch
,watch 需要侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调。 对比watchEffect
不同:
watch允许懒执行副作用; watch更明确哪些状态的改变会触发侦听器重新运行副作用; watch访问侦听状态变化前后的值。 相同:
在停止侦听, 清除副作用 (相应地 onInvalidate 会作为回调的第三个参数传入),副作用刷新时机 和 侦听器调试 等方面行为一致. 侦听使用
单个数据源 const state = reactive ( { count: 0 } ) ;
watch ( ( ) => state. count, ( count, prevCount) => { } ) ;
const count = ref ( 0 ) ;
watch ( count, ( count, prevCount) => { } ) ;
多个数据源 watch ( [ fooRef, barRef] , ( [ foo, bar] , [ prevFoo, prevBar] ) => { } ) ;
模板 Refs
当使用组合式 API 时,reactive refs 和 template refs 的概念已经是统一的。 < template>
< div ref = " root" > </ div>
</ template>
< script>
import { ref, onMounted } from 'vue'
export default {
setup ( ) {
const root = ref ( null )
onMounted ( ( ) => {
console. log ( root. value)
} )
return { root }
} ,
}
</ script>
配合 render 函数 / JSX 的用法 export default {
setup ( ) {
const root = ref ( null )
return ( ) => h ( 'div' , { ref: root} ) ;
return ( ) => < div ref= { root} / >
} ,
}
在 v-for 中使用
v-for中需要使用函数型的ref来自定义处理方式: < template>
< div v-for = " (item, i) in list" :ref = " el => { divs[i] = el }" >
{{ item }}
</ div>
</ template>
< script>
import { ref, reactive, onBeforeUpdate } from 'vue'
export default {
setup ( ) {
const list = reactive ( [ 1 , 2 , 3 ] )
const divs = ref ( [ ] )
onBeforeUpdate ( ( ) => {
divs. value = [ ]
} )
return { list, divs }
} ,
}
</ script>
响应式工具集
unref:如果参数是一个 ref 则返回它的 value,否则返回参数本身。它是 val = isRef(val) ? val.value : val 的语法糖。 function useFoo ( x: number | Ref< number> ) {
const unwrapped = unref ( x)
}
toRef: 可以用来为一个 reactive 对象的属性创建一个 ref,这个 ref 可以被传递并且能够保持响应性。 const state = reactive ( {
foo: 1 ,
bar: 2 ,
} )
const fooRef = toRef ( state, 'foo' )
fooRef. value++
console. log ( state. foo)
state. foo++
console. log ( fooRef. value)
将一个 prop 中的属性作为 ref 传给组合逻辑函数时 export default {
setup ( props) {
useSomeFeature ( toRef ( props, 'foo' ) )
} ,
}
toRefs: 把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref,和响应式对象 property 一一对应。
当想要从一个组合逻辑函数中返回响应式对象时,toRefs可以解构 / 扩展(使用 … 操作符)返回的对象,并不会丢失响应性: function useFeatureX ( ) {
const state = reactive ( {
foo: 1 ,
bar: 2 ,
} )
return toRefs ( state)
}
export default {
setup ( ) {
const { foo, bar } = useFeatureX ( )
return { foo, bar }
} ,
}
isRef: 检查一个值是否为一个 ref 对象。 isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。- isReactive: 检查一个对象是否是由 reactive 创建的响应式代理。如果这个代理是由 readonly 创建的,但是又被 reactive 创建的另一个代理包裹了一层,那么同样也会返回 true。 isReadonly: 检查一个对象是否是由 readonly 创建的只读代理。 高级响应式系统 API
customRef:用于自定义一个 ref,可以显式地控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回一个带有 get 和 set 属性的对象。
使用自定义 ref 实现带防抖功能的 v-model function useDebouncedRef ( value, delay = 200 ) {
let timeout
return customRef ( ( track, trigger) => {
return {
get ( ) {
track ( )
return value
} ,
set ( newValue) {
clearTimeout ( timeout)
timeout = setTimeout ( ( ) => {
value = newValue
trigger ( )
} , delay)
} ,
}
} )
}
export default {
setup ( ) {
return { text: useDebouncedRef ( 'hello' ) }
}
}
markRaw:显式标记一个对象为“永远不会转为响应式代理”,函数返回这个对象本身。
如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的 const foo = markRaw ( { } )
console. log ( isReactive ( reactive ( foo) ) )
const bar = reactive ( { foo } )
console. log ( isReactive ( bar. foo) )
shallowReactive:只为某个对象的私有(第一层)属性创建浅层的响应式代理,不做深层次、递归地响应式代理,而只是保留原样。 const state = shallowReactive ( {
foo: 1 ,
nested: { bar: 2 } ,
} )
state. foo++
isReactive ( state. nested)
state. nested. bar++
shallowReadonly:只为某个对象的自有(第一层)属性创建浅层的只读 响应式代理,同样也不会做深层次、递归地代理,深层次的属性并不是只读的。 const state = shallowReadonly ( {
foo: 1 ,
nested: { bar: 2 } ,
} )
state. foo++
isReadonly ( state. nested)
state. nested. bar++
shallowRef:创建一个 ref,将会追踪它的 .value 更改操作,但是并不会对变更后的.value 做响应式代理转换(即变更不会调用 reactive) const foo = shallowRef ( { } )
foo. value = { }
isReactive ( foo. value)
toRaw:返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发更改。不建议一直持有原始对象的引用。谨慎使用 const foo = { }
const reactiveFoo = reactive ( foo)
console. log ( toRaw ( reactiveFoo) === foo)
nextTick 写法略有不同,原理相同import { nextTick } from 'vue' ;
export default defineComponent ( {
setup ( ) {
nextTick ( ( ) => {
} )
}
} )
vue-router 组件中使用路由时用useRoute和useRouter
```js
import {useRoute, useRouter} from 'vue-router'
const route = useRoute(); // 相当于 vue2 中的this.$route,获取当前路由
const router = useRouter(); // 相当于 vue2 中的this.$router,用于路由跳转
```
vuex import { useStore} from 'vuex'
setup ( ) {
const store = useStore ( ) ;
store. dispatch ( ) ;
store. commit ( ) ;
let category = computed ( ( ) => store. state. home. currentCagegory)
return { category }
}
jsx定义组件 export const AppMenus = defineComponent ( {
setup ( ) {
return ( ) => {
return (
< div class = "app-component" >
< h1> 组件< / h1>
< / div>
) ;
} ;
} ,
} ) ;
插槽(一个不带 name 的 出口会带有隐含的名字name=“default”) < div class = " container" >
< header>
< slot name = " header" > </ slot>
</ header>
< main>
< slot> </ slot>
</ main>
< footer>
< slot name = " footer" > </ slot>
</ footer>
</ div>