vue3的基础使用

1、创建vue3的两种方式

第一种

##安装或者升级
npm install -g @vue/cli
##保证vue cli版本在4.5.0以上
vue --version或vue -V
##创建项目
vue create my-project

然后按照步骤:
1、Please pick a preset - 选择 Manually select features
2、Check the features needed for your project - 选择上 TypeScript ,特别注意点空格是选择,点回车是下一步
3、Choose a version of Vue.js that you want to start the project with - 选择 3.x (Preview)
4、Use class-style component syntax - 直接回车
5、Use Babel alongside TypeScript - 直接回车
6、Pick a linter / formatter config - 直接回车
7、Use history mode for router? - 直接回车
8、Pick a linter / formatter config - 直接回车
9、Pick additional lint features - 直接回车
10、Where do you prefer placing config for Babel, ESLint, etc.? - 直接回车
Save this as a preset for future projects? - 直接回车

第二种:使用vite创建

npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

如果新建的项目中组件使用有eslint报错。则点击vcode中上方的文件,选择首选项,选择设置,搜索eslint。找到Vetur,把右边的Validate vue-html in using eslint-plugin-vue勾选去掉。

2、与vue2的区别

1、vue2中的html模板中必须要有一对根标签,vue3组件的html模板中可以没有根标签。

3、Composition API(常用部分)

1、setup(函数)

  • 新的option所有的组合API函数都在此使用,只在初始化时执行一次
  • 函数如果返回对象,对象中的属性或方法,模板中可以直接使用
<template>
  <div>哈哈哈哈哈</div>
  <!-- 直接使用setup内返回对象的属性 -->
  <div>{{ number }}</div>

</template>
<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "App",
  // 函数如果返回对象,对象中的属性或方法,模板中可以直接使用
  setup() {
    console.log("welcome vue3");
    const number = 10;
    return {
      number,
    };
  },
});
</script>
<style>
</style>

2、ref

<template>
  <h2>setup和ref的基本使用</h2>
  <h3>{{ count }}</h3>
  <button @click="updateCount">更新数据</button>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  name: "App",
  //需求:页面打开后可以直接看到一个数据,点击按钮后,该数据可以发生变化
  //vue2的方式实现
  // data() {
  //   return {
  //     count: 0,
  //   };
  // },
  // methods: {
  //   updateCount() {
  //     this.count++;
  //   },
  // },

  //vue3的方式实现
  //setup是组合API的入口函数
  setup() {
    //变量
    // let count=0 //此时的数据并不是响应式的数据
    //ref是一个函数,作用:定义一个响应式的数据,返回的是一个Ref对象,
    //对象中有一个value属性,如果需要对数据进行操作,需要使用该Ref对象调用value属性的方式进行数据的操作。
    //html模板中不需要使用.value属性的写法
    
    const count = ref(0);
    console.log(count); //看到里面有一个value属性

    //方法
    function updateCount() {
      console.log("=======");
      //报错的原因:count是一个ref对象。对象不能++操作
      // count++;
      count.value++;
    }

    return {
      //属性
      count,
      //方法
      updateCount,
    };
  },
});
</script>
<style>
</style>

3、reactive

<template>
  <h2>reactive的使用</h2>
  <h3>名字:{{ user.name }}</h3>
  <h3>年龄:{{ user.age }}</h3>
  <h3>媳妇:{{ user.wife }}</h3>
  <h3>性别:{{ user.gender }}</h3>

  <hr />
  <button @click="updataUser">更新数据</button>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue";

export default defineComponent({
  name: "App",

  //需求:显示用户的相关信息,点击按钮,可以更新用户的相关信息数据

  //reactive
  // 作用: 定义多个数据的响应式
  // const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
  // 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
  // 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
  setup() {
    //把数据变成响应式数据
    //返回的是一个Proxy的代理对象,被代理者就是reactive中传入的对象
    // const user = reactive({
    //   name: "小明",
    //   age: 20,
    //   wife: {
    //     name: "小小明",
    //     age: 18,
    //     cars: ["奔驰", "宝马", "奥迪"],
    //   },
    // });
    //或者写成这样
    // const obj: any = { //为了在使用obj.gender="男"的时候不出现错误信息
    const obj = {
      name: "小明",
      age: 20,
      wife: {
        name: "小小明",
        age: 18,
        cars: ["奔驰", "宝马", "奥迪"],
      },
    };
    const user = reactive<any>(obj);

    console.log(user);

    //方法
    // function updataUser(){}
    const updataUser = () => {
      user.name = "小红";
      user.age = 12;
      user.wife.name = "小小红";
      user.wife.cars[0] = "玛莎拉蒂";

      //user--->代理对象,user--->目标对象
      //user对象或者obj对象添加一个新的属性,哪一种方式会影响界面的更新
      // obj.gender = "男"; //这种方式界面没有更新渲染
      // user.gender = "男"; //这种方式界面可以更新渲染,而且这种数据最终也添加到了obj对象
      //user对象或者obj对象中移除一个已经存在的属性,哪一种方式会影响界面的更新
      delete user.age;

      // 总结:
      // 如果操作代理对象,目标对象中的数据也会随之变化。
      // 同时如果想要在操作数据的时候,界面也要跟着重新更新渲染,那么也是操作代理对象(user)
    };

    return {
      user,
      updataUser,
    };
  },
});
</script>
<style>
</style>

4、setup参数及细节问题
setup细节问题
1、setup在生命周期beforeCreate执行之前执行,而且就执行一次
2、由此可以推断出:setup在执行的时候,当前的组件还没有创建出来。也意味着:组件实例对象this不能用
3、setup中的对象内部的属性和data函数中的return对象的属性都可以在html模板中使用
4、setup中的对象中的属性和data函数中的对象中的属性会合并为组件对象的属性
5、返回对象中的方法会与methods中的方法合并成为组件对象的方法

setup(props,context) {
    //setup有两个参数props,context
    //props参数,是一个对象,里面有父级组件向子级组件传递是数据,并且是在子级组件中使用props接受到的所有的属性
    //context参数,是一个对象,里面有attrs对象(获取当前组件标签上所有属性的对象,但是该属性在props中没有声明接收的所有的对象),emit方法(分发事件的),slots对象(插槽)
    console.log(props);//从props中获取自己传递的状态
    console.log(context);//可以从emit方法中获取子级传递的方法。
    return {
      //setup中一般都是返回一个对象,对象中的属性和方法都可以在html模板中直接使用
    };
  },

5、reactive细节问题

<template>
  <div>reactive和ref的细节问题</div>
  <h3>m1:{{ m1 }}</h3>
  <h3>m2:{{ m2 }}</h3>
  <h3>m3:{{ m3 }}</h3>
  <hr />

  <button @click="update">更新数据</button>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from "vue";
export default defineComponent({
  name: "App",

  //vue3的composition API中2个重要的响应式API(ref和reactive)
  //ref用来处理基本类型数据,reactive用来处理对象(递归深度响应式)
  //如果用ref对象/数组,内部会自动将对象/数组转换为reactive的代理对象
  //ref内部:通过value属性添加getter/setter来实现对数据的劫持
  //reactive内部:通过Proxy来实现对对象内部所有数据的劫持,并通过Reflect操作对象内部数据
  //ref的数据操作:在js中要.value,在模板中不需要(内部解析模板时会自动添加.value)
  
  setup() {
    //通过ref的方式设置的数据
    const m1 = ref("abc");
    const m2 = reactive({
      name: "小明",
      wife: {
        name: "小红",
      },
    });
    const m3 = ref({
      name: "小明",
      wife: {
        name: "小红",
      },
    });

    const update = () => {
      m1.value+="==="
      m2.wife.name+="==="
      // m3.value.name+="==="
      m3.value.wife.name+="==="
    };
    return {
      m1,
      m2,
      m3,
      update,
    };
  },
});
</script>
<style>
</style>

6、计算属性和监视

<template>
  <div>计算属性和监视</div>
  <fieldset>
    <legend>姓名操作</legend>
    姓氏:<input type="text" name="" id="" v-model="user.firstName" />
    名字:<input type="text" name="" id="" v-model="user.lastName" />
  </fieldset>
  <fieldset>
    <legend>计算属性和监视的演示</legend>
    姓名:<input type="text" name="" id="" v-model="fullName1" /> 姓名:<input
      type="text"
      name=""
      id=""
      v-model="fullName2"
    />
    姓名:<input type="text" name="" id="" v-model="fullName3" />
  </fieldset>
</template>
<script lang="ts">
import { computed, defineComponent, reactive, watch, ref, watchEffect } from "vue";
export default defineComponent({
  name: "App",

  setup() {
    //定义一个响应式对象
    const user = reactive({
      //姓氏
      firstName: "东方",
      //名字
      lastName: "不败",
    });

    //通过计算属性的方式,实现第一个姓名的显示
    //vue3中的计算属性
    //计算属性的函数中如果只传入一个回调函数,表示的是get

    // 第一个姓名:
    // 返回的是一个Ref类型的对象
    const fullName1 = computed(() => {
      return user.firstName + "_" + user.lastName;
    });

    // 第二个姓名
    const fullName2 = computed({
      get() {
        return user.firstName + "_" + user.lastName;
      },
      set(val: string) {
        console.log(val);
        const names = val.split("_");
        user.firstName = names[0];
        user.lastName = names[1];
      },
    });

    // 第三个姓名
    const fullName3 = ref("");
    //监视----监视指定的数据
    // watch(
    //   user,
    //   ({ firstName, lastName }) => {
    //     fullName3.value = firstName + "_" + lastName;
    //   },
    //   { immediate: true, deep: true }
    //   // immediate 一开始就执行一次
    //   // deep 深度监视
    // );

    //监视,跟上面监视一样。但不需要配置immediate,本身默认就会进行监视(默认执行一次)
    watchEffect(()=>{
      fullName3.value = user.firstName + "_" + user.lastName;
    })

    return {
      user,
      fullName1,
      fullName2,
      fullName3,
    };
  },
});
</script>
<style>
</style>

7、vue3生命周期

<template>
  <div>子级组件</div>
  <div>{{ msg }}</div>
  <button @click="update">更新数据</button>
</template>
<script lang="ts">
import {
  defineComponent,
  ref,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
} from "vue";

export default defineComponent({
  name: "Child",

  //vue2.x中的生命周期钩子
  beforeCreate() {
    console.log("2.x中的beforeCreate");
  },
  created() {
    console.log("2.x中的created");
  },
  beforeMount() {
    console.log("2.x中的beforeMount");
  },
  mounted() {
    console.log("2.x中的mounted");
  },
  beforeUpdate() {
    console.log("2.x中的beforeUpdate");
  },
  updated() {
    console.log("2.x中的updated");
  },
  //vue2.x中单beforeDestroy、destroyed这两个已经在vue3.x中改名了。所以不能再使用
  beforeUnmount() {
    console.log("2.x中的beforeUnmount");
  },
  unmounted() {
    console.log("2.x中的unmounted");
  },
  setup() {
    console.log("vue3.x中是setup");

    onBeforeMount(() => {
      console.log("3.x中的onBeforeMount");
    });
    onMounted(() => {
      console.log("3.x中的onMounted");
    });
    onBeforeUpdate(() => {
      console.log("3.x中的onBeforeUpdate");
    });
    onUpdated(() => {
      console.log("3.x中的onUpdated");
    });
    onBeforeUnmount(() => {
      console.log("3.x中的onBeforeUnmount");
    });
    onUnmounted(() => {
      console.log("3.x中的onUnmounted");
    });

    const msg = ref("abc");

    const update = () => {
      msg.value += "==";
    };
    return {
      msg,
      update,
    };
  },
});
</script>
<style>
</style>


4、vue3的响应式原理

<script>
  const user = {
    name: "小明",
    age: 20,
    wife: {
      name: "小英",
      age: 19
    }
  }

  //把目标对象编程代理对象。实现响应式
  //参数1:user--->target目标对象
  //参数2:handler--->处理器对象,用来监视数据,及数据的操作
  const proxyUser = new Proxy(user, {
    //获取目标对象的某个属性值
    get(target, prop) {
      console.log("get方法调用了");
      return Reflect.get(target, prop)
    },
    //修改目标对象的属性值,为目标对象添加新的属性
    set(target, prop, val) {
      console.log("set方法调用了");
      return Reflect.set(target, prop, val)
    },
    //删除目标对象上的某个属性
    deleteProperty(target, prop) {
      console.log("delete方法调用了");
      return Reflect.deleteProperty(target, prop)
    }

  })

  //通过代理对象获取目标对象中的某个属性值
  console.log(proxyUser.name);
  //通过代理对象更新目标对象上的某个属性值
  proxyUser.name = '小新'
  console.log(user);
  //通过代理对象向目标对象中添加一个新的属性
  proxyUser.gender = "男"
  console.log(user);
  //通过代理对象向目标对象中删除某个属性
  delete proxyUser.name
  console.log(user);
  //更新目标对象中的某个属性对象中的属性值
  proxyUser.wife.name = "小安"
  console.log(user);
</script>

5、vue3总结

1、ref的使用(创建基础类型)

声明:	let name = ref('张三')
template标签中使用<div>name</div>
方法中取值:name.value

2、reactive的使用(创建对象、数组等等类型)

声明:	let person = reactive({
				name:'张三',
				age:18,
				hobby:['抽烟','喝酒','烫头']
			})
template标签中使用<div>person.name</div>
方法中取值:person.name

6、vue3语法糖写法

子组件

<!--
 * @Descripttion: 
 * @Date: 2021-12-14 10:56:40
-->
<template>
  <div>
    <div>num:{{num}}</div>
    <div>num2:{{num2}}</div>
    <div>msg:{{msg}}</div>
    <hr />
    <button @click="ziupdata">子传父</button>
    <button @click="updata">修改num/num2</button>
    <hr />
    <div>{{doubleCount}}</div>
  </div>
</template>

<script setup>
//setup语法糖,不需要return出去
import {
  ref,
  reactive,
  toRefs,
  toRef,
  computed,
  watch,
  watchEffect,
  nextTick,
  // useRoute,
  // useRouter,
} from "vue";

const num = ref(2);
const num2 = reactive({ a: 1, b: 2 });

const updata = () => {
  myTitle.value = 10;
  num.value = 1111;
  num2.b = 3333333;
};

// ------------------------------------------------------父传子defineProps
//用来接受props的Api
const props = defineProps({
  msg: {
    type: String,
    default: "我是默认值",
  },
});
// console.log(props);
// ------------------------------------------------------子传父defineEmits
const ziupdata = () => {
  em("updata", "我是子组件的值");
};
// 子组件向父组件传值defineEmits
const em = defineEmits(["updata"]);
// ------------------------------------------------------暴露出自己的属性,在父组件中可以拿到defineExpose
defineExpose({
  num,
  num2,
});
// ------------------------------------------------------toRefs,常用于es6的解构赋值操作,因为在对一个响应式对象直接解构时解构后的数据将不再有响应式,而使用toRefs可以方便解决这一问题。
const { a, b } = toRefs(num2);
// console.log(a.value);

// ------------------------------------------------------toRef,引用num2中的属性a,会影响到num2中原始数据a的改变
const myTitle = toRef(num2, "a");
// ------------------------------------------------------计算属性computed
const doubleCount = computed(() => {
  return num.value * 2;
});
// ------------------------------------------------------监听watch
// 监听使用ref定义的变量的时候时候,第一个参数直接使用
watch(num, (newValue, old) => {
  // console.log("num", newValue, old);
});
// 使用reactive定义的变量需要没在监听的时候需要使用函数返回值的形式才能监听到
watch(
  () => num2,
  (newValue, old) => {
    // console.log(1111, newValue, old);
  },
  {
    deep: true,
    immediate: true,
  }
);
// ------------------------------------------------------监听watchEffect,立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
watchEffect(() => {
  console.log(num.value, "改变");
});

// ------------------------------------------------------v-modal类似sync
//子组件
// const emit = defineEmits(["update:num"]);
// const changeInfo = () => {
//   // 触发父组件值更新
//   emit("update:num", 100);
// };
// 父组件使用v-modal接收

// ------------------------------------------------------nextTick
nextTick(() => {
  // ...
});

// ------------------------------------------------------useRoute, useRouter
// 必须先声明调用
// const route = useRoute();
// const router = useRouter();

// // 路由信息
// console.log(route.query);

// // 路由跳转
// router.push("/newPage");

// ------------------------------------------------------onBeforeRouteLeave, onBeforeRouteUpdate
// 添加一个导航守卫,在当前组件将要离开时触发。
// onBeforeRouteLeave((to, from, next) => {
//   next();
// });

// // 添加一个导航守卫,在当前组件更新时触发。
// // 在当前路由改变,但是该组件被复用时调用。
// onBeforeRouteUpdate((to, from, next) => {
//   next();
// });
</script>

<style>
</style>

父组件


<template>
  <button @click="isclick">父组件button</button>
  <HelloWorld msg="Hello Vue 3 + Vite" @updata="updata" ref="zihello" v-model="num3" />

</template>

<script setup>
import { ref } from "vue";
import HelloWorld from "./components/HelloWorld.vue";

const num3=ref()

const zihello = ref();

const isclick = () => {
  console.log(zihello);
  console.log("接收ref暴漏出来的值", zihello.value.num);
  console.log("接收reactive暴漏出来的值", zihello.value.num2);
};

const updata = (data) => {
  console.log(data); //我是子组件的值
};
</script>

<style>
#app {
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

你可能感兴趣的:(vue,vue)