乐趣区

关于vue3:vue30与vue20的不同点总结

这里总结一些 3.x 的变更, 不便查阅

1. 全局 Api 相干

(1) 全局 api 改为应用应用程序实例

import {createApp} from 'vue'

const app = createApp({})

Vue.config  ->  app.config
Vue.use()   ->  app.use     // 注册插件

一些更改点:

  • Vue.config.productionTip 移除
  • Vue.extend 移除
// 结构器相干
//  vue2.x
const Profile = Vue.extend({   // 生成组件结构器
  template: 'xxx',
  data() {return {}
  }
})
new Profile().$mount(dom)

//  vue3.x, 移除组件结构器,改用 createApp 挂载
const Profile = {
  template: 'xxxxx',
  data() {return {}
  }
}
Vue.createApp(Profile).mount(dom)
---------------------------------
//  继承相干
3.x 应用 组合式 API 来代替继承与 mixin, 或应用 extends 选项代替 Vue.extend

(2) 全局 Api tree-shaking(webpack 打包时去除无用代码,优化打包体积)

全局 API(不全局扭转行为)以及一些外部 api 应用 具名导出, 有利于反对 tree-shaking 的打包工具打包时去除未应用的全局 api, 晋升性能

例如:
Vue.nextTick() -> import { nextTick} from ‘vue’
Vue.set()

2. 模板指令

(1) v-model
3.x

  • v-model 参数指定
  • v-model 替换.sync
  • 绑定多个 v -model
  • 自定义 v -model 修饰符
// 2.x
// v-model 定制 prop,event

// ChildComponent.vue
export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    title: {type: String,}
  }
}
<ChildComponent v-model="pageTitle" /> 等价于
<ChildComponent :title="pageTitle" @change="pageTitle = $event" />

// .sync 实现双向绑定

// ChildComponent.vue
this.$emit('update:title', newValue)
<ChildComponent :title.sync="pageTitle" /> 等价于
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

-------------------

// 3.x
<ChildComponent v-model="pageTitle" />
等价于:<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle =$event"/>

// 指定 v -model 参数
<ChildComponent v-model:title="pageTitle" />  等价于
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event"/>

(2) v-for, key
3.x

  • <template v-for> 的 key 应该设置在 <template> 标签上
  • 条件分支不须要应用 key
  • v-if 优先级高于 v -for

(3).native 修饰符
3.x 移除.native 修饰符

// 2.x
<my-component
  v-on:close="handleComponentEvent"
  v-on:click.native="handleNativeClickEvent"   // 监听原生事件
/>

// 3.x, 未被定义为子组件触发的事件,将被作为子组件的根元素的原生事件监听
<my-component
  v-on:close="handleComponentEvent"
  v-on:click="handleNativeClickEvent"
/>
// MyComponent
<script setup>
   const emit = defineEmits(['close'])
</script>

3. 组件

(1) 函数式组件
3.x

  • 只能由一般函数创立
  • 移除 functional 选项
// 2.x
export default {
  functional: true,
  props: ['level'],
  render(h, { props, data, children}) {return h(`h${props.level}`, data, children)
  }
}

// 3.x
import {h} from 'vue'
const DynamicHeading = (props, context) => {return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading

(2) 异步组件
应用 defineAsyncComponent 定义

// 2.x
const asyncModal = {component: () => import('./Modal.vue'),
  delay: 200,
  timeout: 3000,
  error: ErrorComponent,
  loading: LoadingComponent
}

// 3.x
const asyncModalWithOptions = defineAsyncComponent({loader: () => import('./Modal.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})

(3) 在 emits 中申明组件须要触发的事件
因为 3.x 移除.native 修饰符, 未被定义为子组件触发的事件,将被作为原生事件进行监听

(4) 高阶组件相干的 api
3.x

  • $attrs 包含 style、class
  • $listeners 移除,被整合到 $attrs 中(事件监听器被当作以 on 结尾的属性解决)

(5) 渲染函数
3.x

  • render 函数不承受参数,原有的 h 参数现改为全局引入
  • 应用 resolveComponent 取代字符串查找已注册的组件
// 2.x
export default {render(h) {return h('div')
  }
}

//3.x
import {h} from 'vue'
export default {render() {return h('div')
  }
}

// 3.x
import {h, resolveComponent} from 'vue'

export default {setup() {const ButtonCounter = resolveComponent('button-counter')
    return () => h(ButtonCounter)
  }
}

4. 其余

(1) is attribute

限度在只能用于 component 标签中

// 2.x
// 应用 is 绕开 html 解析限度,被解析为 <blog-post-row> 组件
<table>
  <tr is="blog-post-row"></tr>
</table>

// 3.x
<table>
  <tr is="vue:blog-post-row"></tr>
</table>

(2) 自定义指令的钩子函数与组件得申明周期统一

const MyDirective = {created(el, binding, vnode, prevVnode) {}, // 新增
  beforeMount() {},   // 替换 bind
  mounted() {},  // 替换 inserted
  beforeUpdate() {}, // 新增
  updated() {},   // 替换 componentUpdated
  beforeUnmount() {}, // 新增
  unmounted() {}
}

(3) 对于 data 选项进行合并时(来自 mixin/extend)应用浅合并

const Mixin = {data() {
    return {
      user: {
        name: 'Jack',
        id: 1
      }
    }
  }
}

const CompA = {mixins: [Mixin],
  data() {
    return {// 2.x:  { user: { name: 'jack', id: 2} }
      // 3.x:  {user: { id: 2} }
      user: {id: 2}
    }
  }
}

(4) watch 监听数组

3.x 监听数组变更必须应用 deep

data() {arr: [1, 2, 3, 4],
}
// 2.x
watch: {
    arr: {handler() {xxx},
        // deep: true,  // 监听对象
    },
},

arr = [1,2,3]   // 数组替换, 触发
this.$set(arr, 0, 4)   // 数组变更,触发,等价于 arr.splice(0, 1, 4)

// 3.x
created() {this.$watch('arr', () => {xxx}, {deep: true});
}
arr[1] = 4   // 数组变更

// 3.x setup 语法糖
const arr = ref([])
watch(() => arr.value, () => {xxx}, {deep: true})
// 等价于
watch(arr, () => {xxx}, {deep: true})

5. 移除的 api

(1)过滤器

  • 部分过滤器应用办法或者计算属性代替
  • 全局过滤器应用全局属性代替
const app = createApp(App)

app.config.globalProperties.$filters = {currencyUSD: (value) => '$' + value,
}

<template>
  <p>{{$filters.currencyUSD(xxxx) }}</p>
</template>

(2) Vue.set/$set, Vue.delete/$delete
(3) 移除 $on,$off,$once 事件接口,移除 $children(应用 $refs)
(4) 移除 propsData

// 2.x
const Comp = Vue.extend({props: ['username'],
  template: '<div>{{username}}</div>'
})

new Comp({
  propsData: {username: 'Evan'}
})

// 3.x
const app = createApp(
  {props: ['username'],
    template: '<div>{{username}}</div>'
  },
  {username: 'Evan'}
)

参考链接:
webpack 之 tree-shaking
vue 属性透传

退出移动版