vue3知识总结

1,290 阅读2分钟

一,全局挂载实例到vue全局

1,使用 app.config.globalProperties

比如经常使用的axios

import { createApp } from'vue'

import axios from'axios'

//引入echarts
import * as echarts from 'echarts';

const app = createApp()
app.config.globalProperties.$echarts = echarts;//挂载echarts
app.config.globalProperties.$axios = axios
app.mount('#root')

在组件中使用

1. 通过 getCurrentInstance 的 proxy 使用,不过 proxy 的 ts 类性中还有一个 undefined,所以使用 ts 时,类型就要自己处理
2. 通过 getCurrentInstance 的 appContext 使用,appContext获取的即为main.js里创建的的 vue 对象.

import { defineComponent, getCurrentInstance } from 'vue'
export default defineComponent({
    name: 'Home',
    setup() {
      
        const { proxy } = getCurrentInstance();//获取公用方法proxy.$axios
	      
      	const chartDom = <HTMLElement>document.getElementById("main");
				const { $axios, $echarts } =proxy
        //或者使用echat
        const myChart = $echarts.init(chartDom);
  			console.log($axios)

        // appContext 使用
        const { $axios } = getCurrentInstance().appContext.config.globalProperties
      	console.log($axios)
    }
})

2,通过 依赖和注入( provide 和 inject) 来完成(推荐)

import { createApp } from'vue'
import axios from'axios'
const app = createApp()
//注入axios
app.provide('$axios', axios)

app.mount('#root')

在组件中使用

import { defineComponent, inject } from 'vue'

export default defineComponent({
    setup() {
        // js
        const $axios = inject('$axios')
       	console.log($axios)
    }
})

VUE3 不推荐 app.config.globalProperties方式在原型链上挂载一些东西这种方式,而是更建议使用 的 provide 和 inject 方式

二,路由总结

1,对比vue2 和如何使用

vue3 使用路由的方式跟vue2 其实差不多,区别在于vue3 不在需要通过vue.use()方式 把路由挂载到vue上,

api方面也有所调整,通过createRouter来创建路由,再在mian.js中use一下

其次,路由方式也有所调整,我们需要在vue-router中导入 路由方式 createWebHashHistory 和 createWebHistory 然后再进行使用。直接看代码,进行比较

  • vue2.xxx 使用路由
// router/index.js
import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

// 路由表
const routes = [
    {
      path: '/hot',
      name: 'Hot',
      component: () => import("@/views/hot"),
      meta: {
        title: '热门',
      }
    },
    {
      path: '/news',
      name: 'News',
      component: () => import("@/views/news"),
      meta: {
        title: '资讯',
      },
    },
]

const router = new VueRouter({
  mode: "history",
  routes,
});

export default router;

//main.js 

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

//全局的配置
//import './modules/config'

new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount("#app");
  • vue3.xx使用路由
// router/index.js
import { createRouter, createWebHistory } from "vue-router";

const routes = [
  { path: "/", redirect: "/watch" },
  { path: "/watch", name: "watch", component: () => import("../pages/watch") },
  {
    path: "/computer",
    name: "computer",
    component: () => import("../pages/computer")
  },
  {
    path: "/component",
    name: "component",
    component: () => import("../pages/component")
  },
  {
    path: "/setup",
    name: "setup",
    component: () => import("../pages/setup")
  }
];

const router = createRouter({
  history: createWebHistory(), //vue3 不在使用字符串方式 指定路由方式,需要通过引入vue-router中的方法去指定
  routes: routes
});

export default router;

// main.js 
import { createApp } from "vue";

import App from "./App.vue";
import router from "./router/index";
const app = createApp(App);
app.use(router);
app.mount("#app");

2,路由跳转方式及传参

vue2中我们知道,vue的路由跳转方式有通过router-link 直接跳转,或者通过编程式路由 进行跳转

vue3中其实差不多,只不过在使用编程式路由跳转的时候,我们不能再通过this.$router.xxx 的方式直接跳转,需要通过引入 useRouter 方式进行处理

我们先看一下 两种跳转的书写方式

<template>
  <button>
    <router-link
      :to="{ path: '/computer', query: { name: '啥也不是,计算属性' } }"
      >跳转计算属性</router-link
    >
    跳转计算属性页面
  </button>
  <button>
    <router-link to="/setup">setup语法糖demo</router-link>
  </button>
  <button @click="toWatch">跳转监听器页面</button>
  <button @click="toComponent">跳转组件传值</button>
  <router-view />
</template>

<script>
import { useRouter } from "vue-router";
export default {
  name: "App",
  setup() {
    const router = useRouter();
    let toWatch = () => {
      router.push({ path: "/watch", params: { name: "我来啦,监听器" } });
    };
    let toComponent = () => {
      router.push({ path: "/component" });
    };
    return {
      toWatch,
      toComponent,
    };
  },
</script>

//组件中接收参数
  
<script>
import { useRoute } from "vue-router";
export default {
  name: "",
  components: {},
  setup() {
    const route = useRoute();
    //获取params参数
    const name = route.params.name;
    //获取query参数
    //let name = route.query.name;
    return{
    	name
    }
  }

三,计算属性和监听器

1,计算属性

vue3中的计算属性 我们也需要通过引入的方式使用,与vue2不同的是,vue3的计算属性是通过定义变量后使用computed函数的方式进行获取值,如下代码:

<template>
  <div>
    {{ number }}
    {{ newNumber }}
    <button @click="changeQuery">点一下试试?</button>
  </div>
</template>

<script>
import { ref, onMounted, toRefs, computed } from "vue";
export default {
  name: "computer",
  components: {
  },
  setup() {
    onMounted(() => {
    });
    const number = ref(100);
    let newNumber = computed(() => {
      return number.value * 2;
    });
    const changeQuery = () => {
      number.value += 100;
    };
    return {
      number,
      newNumber,
      changeQuery,
    };
  },
  methods: {},
};
</script>

2,监听器

我们都知道,vue的监听器是监听某个值的变化而执行的。vue3中使用watch的方式跟计算属性差不多。也是通过引入的方式去调用,看一下代码

<template>
  <div>
    <p>举个栗子</p>
    <p>我是{{ character }}</p>
    <p v-if="characters.length !== 0">
      监听到被打,现在的情况是{{ characters }}
    </p>
    <button @click="hitClick">打他</button>
  </div>
</template>

<script>
import { ref, onMounted, watch } from "vue";
export default {
  name: "App",
  components: {},

  setup() {
    const character = ref("帅哥");
    const characters = ref("");
    watch(character, (val) => {
      characters.value = "好惨啊,变成了" + val;
    });
    const hitClick = () => {
      character.value = "鼻青脸肿的人";
    };
    return {
      characters,
      character,
      hitClick,
    };
  },
};
</script>
//watch 设置深度监听 
//    {
//      immediate: true, // 立即执行
//      deep: true // 深度监听
//    }

如果使用reactive定义的变量,我们可以

import { watch, reactive,toRefs } from 'vue'
export default {
name: "App",
components: {},
setup() {
  const state = reactive({
      count: 1
  })
  // 监听count
  watch(
    () => state.count,
    (newVal, oldVal) => {
      console.log(state.count)
      console.log(`watch监听变化前的数据:${oldVal}`)
      console.log(`watch监听变化后的数据:${newVal}`)
    },
    {
      immediate: true, // 立即执行
      deep: true // 深度监听
    }
  )
  return{
  ...toRefs(state)
  }
 }
}

四,组件传值

1,常用方式

vue遵循的是单向数据流模式,意思就是 数据从父组件传递到子组件,子组件只能使用而不能更改,如果需要更改,则需要通过订阅-发布的方式 告诉父组件,让父组件自行去修改值。

父子组件中传值,可以通过props的方式进行传递,子组件接收props需要在setup函数中接收props,如果需要使用emit方式通知父组件,需要接收多一个context上下文

如:

//父组件代码
<template>
  <div>
    <p ref="errorBox" />
    <p>欢迎来到‘组件传值!’,来体验一下吧!</p>
    <Children :money="money" @ask-money="gotMoneyChildren"></Children>
  </div>
</template>

<script>
import Children from "../components/Children.vue";
import { ref, onMounted, reactive, toRefs } from "vue";
export default {
  name: "App",
  components: { Children },
  setup() {
    const errorBox = ref(null);
    const data = reactive({
      money: "",
    });
    onMounted(() => {
      errorBox.value.innerHTML = "这里是组件传值";
    });
    const gotMoneyChildren = (val) => {
      if (val.total_money) {
        let total_money = Number(data.money);
        let ask_money = Number(val.total_money);
        data.money = total_money + ask_money;
      } else {
        errorBox.value.style = "color:red";
        errorBox.value.innerHTML = "你到是说你要多少钱啊";
      }
    };
    return {
      gotMoneyChildren,
      ...toRefs(data),
      errorBox,
    };
  },
};
</script>

//子组件代码

<template>
  <div class="children-class">
    <h3>
      我是儿子:
      <p v-if="money == 0 || money == ''">
        俺爹没有给零花钱我,所以我身上有:0块钱
      </p>
      <p v-else>俺爹给零花钱我了,所以我身上有{{ money }}块钱</p>
      <p>
        爹,给俺<input
          type="text"
          v-model="total_money"
          placeholder="可怜可怜孩子吧!爸"
        />块钱
      </p>
      <button @click="askFatherGotmoney">点击要钱</button>
    </h3>
  </div>
</template>

<script>
import { ref, onMounted } from "vue";
export default {
  name: "Children",
  props: {
    money: {
      type: Number,
      default: 0,
    },
  },
  setup(props, context) {
    console.log(props.money,'父组件传递的money')
    const total_money = ref("");
    onMounted(() => {});
    const askFatherGotmoney = () => {
      context.emit("ask-money", { total_money: total_money.value });
    };
    return {
      total_money,
      askFatherGotmoney,
    };
  },
};
</script>
<style scoped>
.children-class {
  color: #42b983;
}
</style>

2,setup 语法糖方式

setup语法糖是通过在script标签添加 setup属性 ,组件只需引入不用注册,属性和方法也不用返回,也不用写setup函数,也不用写export default ,甚至是自定义指令也可以在我们的template中自动获得。

ps:vue3版本需要在 "^3.2.6" 之上

这种写法会自动将所有顶级变量、函数,均会自动暴露给模板(template)使用**
**但是这里强调一句 “暴露给模板,跟暴露给外部不是一回事” 意思是 外部使用不了当前模板的变量,函数等等

下面介绍一下,再setup语法糖中 如何进行组件传值

  • 简单传值和接收
//父组件 传值子组件
<template>
  <child name='我是父组件传递过来的值'/>  
</template>
 
<script setup>
  // 引入子组件(组件自动注册)
  import child from './child.vue'
</script>

由于没有setup函数,我们需要通过引入

defineProps 用来接收父组件传来的 props ; defineEmits 用来声明触发的事件。

defineEmitsdefineProps

<template>
  <span>{{props.name}}</span>
  <span>{{name}}</span>
  <button @click='changeName'>更名</button>
</template>
 
<script setup>
  import { defineEmits, defineProps } from 'vue'
  // 声明props
  const props = defineProps({
    name: {
      type: String,
      default: ''
    }
  }) 
  // 声明事件
  const emit = defineEmits(['updateName'])
  const changeName = () => {
    // 执行
    emit('updateName', 'Tom')
  }
</script>
  • 子组件通知父组件(修改值)

上面代码中,子组件引入了defineEmits 并定义了updateName为通知父组件 让其调用的方法,我们修改一下父组件,并监听一下子组件传递的方法

<template>
  <child :name='state.name' @updateName='updateName'/>  
</template>
 
<script setup>
  import { reactive } from 'vue'
  import child from './child.vue'
  const state = reactive({
    name: 'Jerry'
  })
  // 接收子组件触发的方法
  const updateName = (name) => {
    state.name = name
  }
</script>

五,vue3中使用ref操作dom

1,常用方式

我们都知道,vue2中可以通过在节点中绑定ref属性,在代码中使用 this.$refs.xxx 方式操作dom,vue3中如何使用ref操作dom呢? 我们看一下代码

<template>
  <p ref="errorBox" />
</template>

<script>
import { ref,nMounted } from "vue";
export default {
  name: "",
  components: {},
  setup() {
    const errorBox = ref(null);
    onMounted(() => {
      errorBox.value.innerHTML = "这里是监听器";
    });
    return {
      errorBox
    };
  },
};
</script>

vue3中通过绑定节点属性ref='xxx' 的方式 在代码中,定义与绑定属性'xxx' 相同的名称的ref(ref可以为空,也可以为null) 然后通过return方式 返回出去。

2,setup语法糖方式

setup语法糖中没有setup函数,无法retuen返回出去,我们该怎么操作?

可以通过引入vue中的getCurrentInstance方式 进行使用,如:

<template>
  <div>
    <p>这里是setup语法糖,详情请看代码</p>
  </div>
  <p ref="errorBox" />
</template>

<script setup>
import { ref, onMounted, getCurrentInstance } from "vue";
let currentInstance = null;
onMounted(() => {
  currentInstance = getCurrentInstance();
  console.log(currentInstance, "xx");
  currentInstance.refs.errorBox.innerHTML = "这里是setup语法糖";
});
</script>

注意一下:如果父组件没使用语法糖,而子组件使用了setup语法糖,我们知道,使用了语法糖的是不会对外暴露的

这时候,我们需要在子组件中使用 defineExpose 对外暴露属性,如

//父组件

<template>
  <Childre ref="childre" />
</template>

<script lang="ts" setup>
import { ref } from "vue";
import Childre from "./Childre.vue";

const childre = ref('childre')
console.log('---childre---',childre)
</script>


//子组件

<template>
  <div>你好,这里是{{ msg }}</div>
</template>

<script lang="ts" setup>
import { ref ,defineExpose} from "vue";
const msg = ref('子组件')
defineExpose({
    msg
})
</script>

六,最后

使用setup语法糖有许多的便利,后续有完整的开发体验,在进行更新交流,另外

附上本文实例演示地址 跳转链接 setup语法糖不知道怎么回事,在codesandbox上无法正常显示,需要源代码的 可以添加我的微信:'ZT_9998051022'