vue3与vue2比较

1.创建组件实例

在vue2中我们是通过new Vue() 的方法进行创建,而在vue3通过vue里面的一个全局方法createApp进行创建,可以通过链式调用

例子:

  const Counter = {
      data() {
        return {
          counter: 0
        }
      }
    }

    const app = Vue.createApp(Counter)
    app.mount('#root') 

该应用实例是用来在应用中注册“全局”组件的。

const app = Vue.createApp({})
app.component('SearchInput', SearchInputComponent)
app.directive('focus', FocusDirective)
app.use(LocalePlugin)

2.自定义指令

之前全局通过Vue.directive(),现在通过定义的全局变量,例如上面的app.directive()

例子:定义一个v-focus

 <input type="text" v-focus /> //用的时候,在刚打开页面就会获取焦点

app.directive('focus', {
      mounted(el) {
        el.focus() 
      }
    })

(2) 里面钩子函数的改变

在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使钩子的目标事件十分相似。我们现在把它们统一起来了:

- **created** - 新增!在元素的 attribute 或事件监听器被应用之前调用。

- bind → **beforeMount**

- inserted → **mounted**

- **beforeUpdate**:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。

- update → 移除!该钩子与 `updated` 有太多相似之处,因此它是多余的。请改用 `updated`。

- componentUpdated → **updated**

- **beforeUnmount**:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。

- unbind -> **unmounted**

例子:

 <div id="root">
    <h1>scroll down the page</h1>
    <input type="range" min="0" max="500" v-model="pinPadding">
    <p v-pin:[direction]="pinPadding">text</p>//:[direction]是building参数的arg
  </div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          pinPadding: 200,
          direction: 'right'
        }
      }
    })

    // app.directive('pin', {
    //   mounted(el, binding) {
    //     // console.log(binding)
    //     el.style.position = 'fixed'
    //     const s = binding.arg || 'top'
    //     el.style[s] = binding.value + 'px'
    //   },

    //   updated(el, binding) {
    //     const s = binding.arg || 'top'
    //     el.style[s] = binding.value + 'px'
    //   },
    // })

    app.directive('pin', (el, binding) => {
      el.style.position = 'fixed'
      const s = binding.arg || 'top'
      el.style[s] = binding.value + 'px'
    })

    app.mount('#root')
  </script>
</body>

3.自定义插件

首先在vue2通过install函数进行创建插件才能进行vue.use,在vue3中用app.use

例子;

i18n.js,在改文件夹下导出install方法

知识点: app.config.globalProperties.$translate是在vue实例下面创建一个$translate属性,在实例里直接通过this.$translate就能获取到该值

export default {
  install: (app, options) => {
    // 挂载一个全局属性
    app.config.globalProperties.$translate = key => {
      console.log(key.split('.'));
      return key.split('.').reduce((o, i) => {
        if (o) {
          console.log(o);//第一次是options对象,第二次是 greetings: {
        hello: '你好'
      }对象
          return o[i]
        }
      }, options)
    }
  }
}

在挂载的时候,用的时候,在刚打开页面就会打印“你好”

 <div id="app">
    <my-component></my-component>
  </div>
  <script type="module">
    import i18nPlugin from './i18n.js'
    const app = Vue.createApp({
      mounted() {
        console.log(this.$translate('greetings.hello'))
      }
    })

    // 全局定义的组件
    app.component('my-component', {
      template: `
        <h1>自定义全局组件</h1>
      `
    })

    // 使用插件
    const i18nString = {
      greetings: {
        hello: '你好'
      },

      oparator: {
        open: '打开',
        close: '关闭'
      }
    }

    app.use(i18nPlugin, i18nString)

    app.mount('#app')
  </script>

4.生命周期的改变

改变了卸载的生命周期,之前是destory,变成了unmount,还可以通过app.unmount()进行卸载组件,在vue2通过v-if=false

5.在根组件里面datab必须是一个函数,在vue2中是一个对象

防抖案例,是通过lodash里面的方法

首先需要引入lodash

然后

<div id="root">
    <!-- {{title}} -->
    <save-button></save-button>
  </div>
  <script>
    const app = Vue.createApp({
    
    })

    app.component('save-button', {
      created() {
        this.deboucedClick = _.debounce(this.click, 500) //这里是_.debounce是lodash里面的防抖发方法,第一个参数是返回出来的一个函数,也就是在methods里面定义的click,第二个是这个函数需要执行的时间
      },

      template: `
        <button @click="deboucedClick">
          Save  
        </button>
      `,

      methods: {
       
        click() {
          console.log('clicked.')
        }
      }
unmounted() {
    // 移除组件时,取消定时器
    this.debouncedClick.cancel()
  },
    })

    app.mount('#root')

6.v-if与v-for 

(1)在vue3 当 `v-if` 与 `v-for` 一起使用时,`v-if` 具有比 `v-for` 更高的优先级。

在vue2中v-for优先级高,当你循环遍历[2,3,4,5,6]的时候你v-if=item>2,在vue2中只有2b不渲染,而在vue3中所以的都不渲染,相当于数组中every只要一个是false就返回false,因为v-if优先级高,所以都不会渲染

(2)

在 Vue 2 中,在 v-for 中使用的 `ref` attribute 会用 ref 数组填充相应的 `$refs` property。当存在嵌套的 `v-for` 时,这种行为会变得不明确且效率低下。

例如:

<div id="root">
    <ul>
      <li v-for="li in lists" :ref="li">{{ li }}</li> //通过这种方式绑定ref,会自动给你创建一个$refs,的数据挂载到当前实例下面
    </ul>
  </div>
  <script>
    const vm = new Vue({
      el: '#root',
      data: {
        lists: ['🍒', '🍊', '🍇', '🥕'],
        refs: 'lists'
      },
      mounted() {
         console.log(this.$refs); //在这里可以打印出来,但是在vue3中就不会给你自动创建$refs的数组了
      },
      methods: {
        setItemRef(el) {
          // if (el) {
          //   this.itemRefs.push(el)
          // }
          console.log(0)
        }
      },
    })
  </script>

在 Vue 3 中,此类用法将不再自动创建 `$ref` 数组。要从单个绑定获取多个 ref,请将 `ref` 绑定到一个更灵活的函数上 (这是一个新特性)

思路:在要v-for的节点上,用:ref绑定到自己实例里面methods里面的一个方法setItemRef,在data里面创建一个空数组,用来存ref,而setItemRef方法进行把每次的ref进行push到data定义的数组里面

例子:

<div id="root">
    <ul >
      <!-- <li v-for="li in lists" v-if="li!=='🥕'">{{ li }}</li> -->
      <li v-for="li in lists" :ref="setItemRef">{{ li }}</li>//这里把:ref绑定到自己实例里面methods里面的一个方法,
    </ul>
    <!-- <my-component v-for="i in 3">{{i}}</my-component> -->
  </div>
  
  <script>
    const app = Vue.createApp({
      data() {
        return {
          lists: ['🍒', '🍊', '🍇', '🥕'],
          refs: 'lists',
          itemRefs: [] //在data里面创建一个数组用来存ref
        }
      },

    
      methods: {
        setItemRef(el) {
          if (el) {
            this.itemRefs.push(el)//这里el是绑定这个方法的dom元素
          }
        }
      },

      mounted() {
        console.log(this.itemRefs)
      },
    })
    app.mount('#root')
  </script>
</body>

 7.多事件处理器

事件处理程序中可以有多个方法,这些方法由逗号运算符分隔:

```html
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<button @click="one($event), two($event)">
  Submit
</button>

```js
// ...
methods: {
  one(event) {
    // 第一个事件处理器逻辑...
  },
  two(event) {
   // 第二个事件处理器逻辑...
  }
}
```

8.按键修饰符keyup

在vue2中

```html
<!-- 键码版本 -->
<input v-on:keyup.13="submit" />

<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />

此外,也可以通过全局的 `config.keyCodes` 选项定义自己的别名。

```js
Vue.config.keyCodes = {
  f1: 112
}
```

```html
<!-- 键码版本 -->
<input v-on:keyup.112="showHelpText" />

<!-- 自定义别名版本 -->
<input v-on:keyup.f1="showHelpText" />

在vue3中全局的 `config.keyCodes` 选项定义自己的别名已经废弃,直接.enter就行

```html
<!-- Vue 3 在 v-on 上使用按键修饰符 -->
<input @keyup.page-down="nextPage">

<!-- 同时匹配 q 和 Q -->
<input @keypress.q="quit">

9.$attrs

在vue2中,要是在组件上面写上属性<date-picker data-status="activated" class="a"></date-picker> 不管是自定义属性还是,class,style,都会传递到组件实例最外层div上面,但是要是加上

inheritAttrs: false,这个属性,只有class与style传递到组件实例,自定义属性不会传递

而在vue3中书写inheritAttrs: false,class与style也不会传递到组件实例,所有属性都不会传递

,虽然不会传递但是都存在this.$attrs下面,如果想让某个节点要有某个属性可以这样写

<body>
  <div id="root">
    <date-picker data-status="activated" class="a"></date-picker>
  </div>
  <script>
    const app = Vue.createApp({

    })

    app.component('date-picker', {
      inheritAttrs: false,
      template: `
        <div v-bind:class="$attrs.class"> //直接拿$attrs下面存的值
          <input type="datetime-local" v-bind="$attrs"/>//这样写会拿到$attrs所有的属性
        </div>
      `,

      created() {
        // console.log(this.$attrs.class)
      }
    })

    app.mount('#root')

 10.自定义事件

首先在vue2中,要是字组件想要使用父组件的方法的时候需要@click.native=" ",而在vue3中已经不用.native,直接写时间就行,但是注意它只适合浏览器自定的事件,自定义事件不可以

例如:

 <div id="app">
    <my-component @click.native="handleClick"></my-component> //点击就会触发父组件的方法
  </div>

  <script>
    Vue.component('my-component', {
      template: `
        <button>按钮</button>
      `
    })

    const vm = new Vue({
      el: '#app',
      methods: {
        handleClick() {
          console.log('click.')
        }
      },
    })
  </script>

自定义事件的改变,在vue2中自定义事件,通过子组件的实例mounted里面自定义事件,在组件上这个事件触发父元素的方法,而在vue3中并不需去操作父元素,前提是父元素必须有一个值来接收,然后只需要在子元素进行操作就行,

知识点:新增了emits,update:

例子:

<div id="root">
    {{counted}}
    <counter v-model:total="counted"></counter> //首先通过v-model:一个自定义属性绑定的是要传到父元素里面的值
  </div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          counted: 0,
        }
      }
    })

    app.component('counter', {
      props: ['total'], //子组件通过props接收属性
      emits: ['update:total'], //这里update是必须写的后面跟props接收的total属性

      data() {
        return {
          count: 0,
        }
      },

      methods: {
        handleClick() {
          this.count++
          this.$emit('update:total', this.count) //这里同样要抛出这个update:total,第二个参数是传给父元素的值,前提是父元素必须有一个值来接收,
        }
      },

      template: `
        <button @click="handleClick">click</button>
      `
    })
    
    app.mount('#root')
  </script>

emits:里面的事件必须是自定义事件,不可以是浏览器自带的事件名

还可以写一个对象的形式。里面是一个回调函数,返回true才会触发,返回false会出现一个警告

emits: {
        // click: null,
        myEvent: (num) => {
          // 返回值是个true/false
          // true 可以触发事件
          // false 不触发事件
          if (num > 10) {
            return true
          } else {
            return false
          }
        }
      },

methods: {
        handleClick() {
          this.$emit('myEvent', 9)//这里传的参数是9,会出现一个警告
        }
      }

11.插槽

一个不带 `name` 的 `<slot>` 出口会带有隐含的名字“default”。

#list是插槽相当于v-slot:list

不带name的插槽相当于v-slot:defult

作用域插槽

有时让插槽内容能够访问到组件的数据是很有用的。当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式

例子:

实现:这里的插槽就会访问到组件中的数据,并进行使用,之前写个span标签里面的内容是自己写死的,并不能访问到组件data里面的数据

<body>
  <div id="root">
    <todo-list #list="{ index, item }"> //这里#list是插槽相当于v-slot:list
      <span>{{index}}</span> - 
      <span>{{item}}</span> //这里的插槽就会访问到组件中的数据,并进行使用,之前写个span标签里面的内容是自己写死的,并不能访问到组件data里面的数据
    </todo-list>
  </div>
  <script>
    const app = Vue.createApp({

    })
    app.component('todo-list', {
      data() {
        return {
          items: ['feed a cat', 'Buy milk']
        }
      },

      template: `
        <ul>
          <li v-for="(item, index) in items">
            <slot name="list" :item="item" :index="index"></slot>
          </li>
        </ul>
      `
    })
    app.mount('#root')
  </script>

12.provider 与inject

相当于react里面context上下文,在上层组件使用provider,在各个子组件使用inject接收父组件传递过来的值,如果上层组件中传递过来的state的值改变了,那么子组件通过inject接收的值是不变的,要想变的话要从Vue下面去一个方法computed,这个计算属性与options中API的computed不一样

<script>
    const { createApp, computed } = Vue
    const app = createApp({
      
    })
    // app.config.unwrapInjectedRef = true

    app.component('TodoList', {
      template: `
        <div>{{str}}</div>
        <slot></slot>
      `,

      data() {
        return {
          str: 'hello'
        }
      },

      // provide: {
      //   msg: 'hello'
      // }

      provide() {
        return {
          msg: Vue.computed(() => this.str) //通过computed进行传递,当str通过下面的方法改变之后,在通过inject的数据也会改变
        }
      },

      mounted() {
        setTimeout(() => {
          this.str = 'world'
        }, 2000)
      },
    })
    app.component('TodoItem', {
      template: `
        <div>TodoItem</div>
        <slot></slot>
      `
    })
    app.component('TodoListFooter', {
      template: `
        <div>TodoListFooter</div>
        <slot></slot>
      `
    })
    app.component('ClearTodosButton', {
      template: `
        <div>ClearTodosButton</div>
      `
    })
    app.component('TodoListStatistics', {
      inject: ['msg'], //在这里进行接收
      template: `
        <div>{{msg}} TodoListStatistics</div> //渲染到页面
      `,
      mounted(){
      console.log(this.msg._value);
      this.msg._value=2222
      console.log(this.msg._value);

}
    },
   
    )

    app.mount('#root')

13.异步加载组件

defineAsyncComponent 需要用到vue里面的这个方法,返回的是一个promise对象

模拟一个懒加载,先加载loading......两秒之后在加载异步组件

知识点: <suspense>组件,懒加载的组件

 <div id="root">
    <suspense>
      <template #default>  //这个是默认加载
        <async-example></async-example>
      </template>
      <template #fallback> //这里是首先加载
        <div>
          loading...
        </div>
      </template>
    </suspense>
  </div>
  <script>
    const { createApp, defineAsyncComponent } = Vue
    const app = createApp({})

    const AsyncComp = defineAsyncComponent(
      () => 
        new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve({
              template: '<div>I am async!</div>'
            })
          }, 3000)
        })
    )

    app.component('async-example', AsyncComp)
    app.mount('#root')
  </script>

14.$children在vue3中已经废弃

15.is属性的改变

在vue2中的动态组建中:is后面是跟的该渲染哪个组件,但是有个问题就是,如果你传的不是组件是一个自定义属性呢,这样的话就会出现冲突,所以在vue3中通过      <tr is="vue:mytr"></tr>组件前面加一个vue:来进行写动态组件,is也不需要写冒号

例子

<body>
  <div id="root">
    <div :is="myDiv"></div>
    <table>
      <tr is="vue:mytr"></tr>
    </table>
  </div>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          myDiv: 'abc'
        }
      }
    })

    app.component('mytr', {
      template: `
        <tr><td>abc</td></tr>
      `
    })
    app.mount('#root')
  </script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值