vue3新特性

TypeScript

  1. 背景:
  • TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。
  • TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。
  • TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。
  • TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6+ 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
  1. 安装TypeScript:npm install -g typescript
    验证: tsc -V
    • script标签的更改:
    • 扩展名的更改: vue2中扩展名为 ‘js’ 的 全部改为 ’ts’
  2. 定义vue组件
    要让 TypeScript 正确推断 Vue 组件选项中的类型,需要使用 defineComponent 全局方法定义组件:
import { defineComponent } from 'vue'

const Component = defineComponent({
  // 已启用类型推断
  // 这里是script标签中除import引入组件外的所有代码
})

vue3的生命周期

  1. 使用vue3的生命周期:
    必须要先使用import引入,才能使用;

vue3中的父子组件传递

  1. vue3中的父子组件传递依然和vue2中的一样使用props和emit, 但是写法略有不同;
  2. emit(自定义事件)-子传父;
  3. props-父传子:
    父组件中:
<template>
  <div>
     <el-button @click="toTonfigure">配置</el-button>
     <efficacy-dialog
        v-if="dialogVisible"
        :dialogVisible="dialogVisible"
        @close="closeDialog"  />
        
  </div>
</template>

<script lang="ts">
import { defineComponent, getCurrentInstance, toRefs, reactive } from 'vue'
import efficacyDialog from './efficacyDialog.vue'

export default defineComponent({
    name: 'EfficacyEvaluation',
    components: {
      efficacyDialog
    },
    setup() {
        const { proxy } = getCurrentInstance() as any
        const state:any = reactive({
          dialogVisible: false
        })
        const states = toRefs(state);
        
        function toTonfigure() {
          proxy.dialogVisible = true
        }

        function closeDialog() {
          proxy.dialogVisible = false
        }
        

        return {
          ...states,
          toTonfigure,
          closeDialog
        }

    }

})


</script>

子组件中:

<template>
  <el-dialog
    title="疗效评估类型"
    :model-value="dialogVisible"
    :close-on-click-modal="false"
    width="50%"
    :before-close="handleClose">
    <el-form :model="ruleForm" :rules="rules" ref="ruleForm" >
      <el-form-item label="疗效评估类型" prop="region">
        <el-select v-model="ruleForm.region" placeholder="请选择">
          <el-option label="评估记录" value="record"></el-option>
          <el-option label="评估结果" value="result"></el-option>
          <el-option label="评估报告" value="report"></el-option>
        </el-select>
      </el-form-item>
    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script lang="ts">
import { defineComponent, getCurrentInstance, toRefs, reactive } from 'vue'

export default defineComponent({
    name: 'EfficacyDialog',
    props: {
      dialogVisible: {
        type: Boolean,
        default: false
      }
    },
    emits: ['close'],
    setup(props, { emit }) {
        const { proxy } = getCurrentInstance() as any
        const state:any = reactive({
          ruleForm: {
            region: ''
          },
          rules: {
            region: [
              { required: true, message: '请选择疗效评估类型', trigger: 'change' }
            ]
          }
        })
        const states = toRefs(state);
        
        function save() {
          
          handleClose()
        }

        function handleClose() {
          emit('close', false)
        }
        return {
          ...states,
          handleClose,
          save
        }
    }
})
</script>

this

  1. setup的执行时组件对象还没有创建,此时不能使用this来访问data/computed/methods/props;

  2. 可以通过 getCurrentInstance这个函数来返回当前组件的实例对象,也就是当前vue这个实例对象;

  3. 具体用法:
    import { getCurrentInstance } from ‘vue’;
    setup() {
    const { proxy } = getCurrentInstance() as any;
    // 变量名必须是 proxy, 且getCurrentInstance后的小括号不能丢掉;

    // 使用:
    const myChart = echarts.init(proxy.$refs.homeLaboratoryRef);
    // 这里的proxy就相当于vue2中共的this
    }

html

  1. vue2中如果需要使用行内数据,则要在标签中指明,如:
<template slot-scope="scope">
   ...    
</template>

vue3中需要指明行内数据,也需要在标签中指明,但写法不同,如:

<template #default="{ row,$index }">
  ...            
  // 这里的$index若不需要可以不写
</template>

通过ref获取元素

  1. 在vue2中我们可以通过在元素中定义 ref=“xxx”, 在js中通过this.$refs.xxx来获取该元素中的数据内容,但是在vue3中这么写是错误的;
  2. 在vue3中想要通过ref获取数据,首先需要在元素中定义ref=“xxx”, 然后再serup中: const xxx = ref(null), 在return中暴露xxx, 在setup的生命周期中打印xxx.value就可以看到该元素的数据;
<template>
   <div>
     <efficacyDialog
        v-if="dialogVisible"
        ref="addDialog"
        :dialog-visible="dialogVisible"/>
  </div>
</template>

<script lang="ts">
import { defineComponent, getCurrentInstance, toRefs, reactive, onMounted, ref } from 'vue'
import efficacyDialog from './efficacyDialog.vue'

export default defineComponent({
    name: 'EfficacyEvaluation',
    components: {
      efficacyDialog
    },
    setup() {
        const { proxy } = getCurrentInstance() as any
        const addDialog = ref(null)
        
        const state:any = reactive({
          dialogVisible: true
        })
        const states = toRefs(state);
        
        onMounted(() => {
          window.console.log(addDialog.value)
        })

        return {
          ...states,
          addDialog
        }
    }
})
</script>

reactive

  1. 定义数据: const obj = reactive({ count: 0 }) reactive定义响应式数据 ;
  2. 参数: reactive参数必须是对象(json/arr) ;
  3. 使用数据: 不能改变对象本身, 但可以改变内部count的值, 如: obj.count = 2
  4. reactive一般用来定义复杂的数据类型;
  5. 如果定义响应式的数组类型的数据,有两种方法,一是使用ref定义,二是使用reactive先定义一个对象,在对象中放数组;

ref

  1. 定义数据: const count = ref(0) ref定义响应式数据 ;
  2. 修改数据: 使用时必须加上 ’ .value ’ , 如: count.value = 2 ;
  3. 在template中使用ref数据不需要加.value;
  4. ref一般用来定义简单的数据类型,但ref也可以定义数组和对象;
  5. ref的本质是拷贝,改变响应式数据与原始数据无关,原始数据没有影响;

toRef

  1. 本质: toRef本质是引用,试图不更新,修改响应式数据,试图不更新,原始数据有影响;
  2. 修改数据: toRef (要修改的对象,‘对象中某个具体的数据属性名’), 但是需要注意,如果修改通过toRef创建的响应式数据,并不会触发UI界面的更新;
import {toRef} from 'vue';
export default {
  name:'App'
  setup(){
    let obj = {name : 'alice', age : 12};
    let newObj= toRef(obj, 'name');
    function change(){
      newObj.value = 'Tom';
      console.log(obj,newObj)
    }
    return {newObj,change}
  }
}
  1. toRef一次仅能设置一个数据,toRefs接收一个对象作为参数,它会遍历对象身上的所有属性,然后挨个调用toRef执行;
import {toRefs} from 'vue';
export default {
  name:'App'
  setup(){
    let obj = {name : 'alice', age : 12};
    let newObj= toRefs(obj);
    function change(){
      newObj.name.value = 'Tom';
      newObj.age.value = 18;
      console.log(obj,newObj)
    }
    return {newObj,change}
  }
}
ref与toRef的区别

(1). ref本质是拷贝,修改响应式数据不会影响原始数据;toRef的本质是引用关系,修改响应式数据会影响原始数据;
(2). ref数据发生改变,界面会自动更新;toRef当数据发生改变是,界面不会自动更新;
(3). toRef传参与ref不同;toRef接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性;

vue3中–setup组件选项

  1. setup 组件选项在创建组件之前执行,一旦 props 被解析,并充当合成 API 的入口点;
    setup 选项中没有 this。这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法
  2. setup 函数是一个新的组件选项。它作为在组件内部使用组合式 API 的入口点;
  3. 调用时间: 在创建组件实例时,在初始 prop 解析之后立即调用 setup。在生命周期方面,它是在 beforeCreate 钩子之前调用的;
export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String }
  },
  setup(props) {
    console.log(props) // { user: '' }

    return {} // 这里返回的任何内容都可以用于组件的其余部分
  }
  // 组件的“其余部分”
}

setup中的return
  1. 在setup组件选项中定义的变量和方法都要在最后的return中暴露出去;
import { fetchUserRepositories } from '@/api/repositories'

// 在我们的组件内
setup (props) {
  let repositories = []
  const getUserRepositories = async () => {
    repositories = await fetchUserRepositories(props.user)
  }

  return {
    repositories,
    getUserRepositories // 返回的函数与方法的行为相同
  }
}
ref设置变量
  1. 在setup中设置变量,要设置响应式的,否则无法使用,响应式变量要通过ref来设置:
    1> 设置变量: import { ref } from ‘vue’
    const repositories = ref([ ])
    2> 使用变量: repositories.value = …
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'

// in our component
setup (props) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(props.user)
  }

  return {
    repositories,
    getUserRepositories
  }
}

这样设置每当我们调用 getUserRepositories 时,repositories 都将发生变化,视图也会随之更新

钩子函数
  1. 在setup组合式API中调用钩子函数:如:mounted,在setup中要写成onMounted;其次也要有: import { onMounted } from ‘vue’
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'

// in our component
setup (props) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(props.user)
  }

  onMounted(getUserRepositories) // on `mounted` call `getUserRepositories`

  return {
    repositories,
    getUserRepositories
  }
}
watch
  1. watch的响应式更改:
    1> import { watch } from ‘vue’
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'

// 在我们组件中
setup (props) {
  // 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
  const { user } = toRefs(props)

  const repositories = ref([])
  const getUserRepositories = async () => {
    // 更新 `prop.user` 到 `user.value` 访问引用值
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)

  // 在用户 prop 的响应式引用上设置一个侦听器
  watch(user, getUserRepositories)

  return {
    repositories,
    getUserRepositories
  }
}
computed
  1. computed属性
    1> import { computed } from ‘vue’
    2> const twiceTheCounter = computed(() => counter.value * 2)
    3> console.log(twiceTheCounter.value) // 2
    4> 使用变量: '变量.value ’
    5> 在setup中要return出去:
setup() {
  ...
  return {
    twiceTheCounter 
  }
}
toRefs
  1. 1> ;
    2> 使用步骤:
    import { toRefs } from ‘vue’
    setup() {
    const { user } = toRefs(props)
    // 这里的props可以替换成一个对象,把所有需要用到的数据写在对象中
    }
方法
    1. vue2中的方法在methods:{} 中包裹着,vue3中的方法没有外面的methods包裹,直接写方法即可(方法和name同级),且前要加上‘function’, 最后(和方法同级)必须要有一个return{}里面是所有的方法名,只有把方法名return出去才能使用这些方法;
    2. vue3中的函数如果需要传参,则要指明参数的类型,如:
      在这里插入图片描述
      对象类型的数据,统一为‘any’;
自定义事件 emit (vue3的子传父)
  1. 1> 使用步骤:
    • 首先在export default { } 中定义emits选项,里面以字符串形式定义原生事件(如: click事件);
    • 其次便可以在emit();中使用之前定义的原生事件了;
    • 注: emit(‘get’, state.fontIconPrefix); 中 ‘get’表示之前定义的原生事件,state.fontIconPrefix代表要传递的数据;
export default {
	name: 'iconSelector',
	emits: ['update:modelValue', 'get', 'clear'],
	props: {
		// 输入框占位文本
		placeholder: {
			type: String,
			default: () => '请输入内容搜索图标或者选择图标',
		},
		// 输入框占位文本
		size: {
			type: String,
			default: () => 'small',
		},
		// 双向绑定值,字段名为固定,改了之后将不生效
		// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
		modelValue: String,
	},
	setup(props, { emit }) {
		const inputWidthRef = ref();
		const state: any = reactive({
			fontIconPrefix: '',
			fontIconVisible: false,
			fontIconTabsIndex: 0,
			fontIconPlaceholder: '',
		});
		// 获取当前点击的 icon 图标
		const onColClick = (v: any) => {
			state.fontIconPlaceholder = v;
			state.fontIconVisible = false;
			if (state.fontIconTabsIndex === 0) state.fontIconPrefix = `${v}`;
			else if (state.fontIconTabsIndex === 1) state.fontIconPrefix = `${v}`;
			else if (state.fontIconTabsIndex === 2) state.fontIconPrefix = `${v}`;
			emit('get', state.fontIconPrefix);
			emit('update:modelValue', state.fontIconPrefix);
		};
		// 清空当前点击的 icon 图标
		const onClearFontIcon = () => {
			state.fontIconPrefix = '';
			emit('clear', state.fontIconPrefix);
			emit('update:modelValue', state.fontIconPrefix);
		};
		// 页面加载时
		onMounted(() => {
			initFontIconData();
			initResize();
			getInputWidth();
		});
		// 监听双向绑定 modelValue 的变化
		watch(
			() => props.modelValue,
			() => {
				initModeValueEcho();
			}
		);
		return {
			inputWidthRef,
			onColClick,
			onClearFontIcon,
			onIconFocus,
			...toRefs(state),
		};
	},
};
2> 验证自定义事件
- 验证自定义事件条件: 如果使用对象语法而不是数组语法定义发出的事件,则可以验证它。
- 要添加验证,将为事件分配一个函数,该函数接收传递给 $emit 调用的参数,并返回一个布尔值以指示事件是否有效
emits: {
    // 没有验证
    click: null,

    // 验证submit 事件
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
v-model参数
  1. 默认情况下,组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件。我们可以通过向 v-model 传递参数来修改这些名称;

// 子组件中: 
// 子组件中一定有两步: 1. 在props中接收某个数据(modelValue);
//                    2. 使用$emit方法把数据传给父组件, 事件的写法是: update: modelValue
app.component('my-component', {
  props: {
    foo: String
  },
  template: `
    <input 
      type="text"
      :value="foo"
      @input="$emit('update:foo', $event.target.value)">
  `
})

// 父组件中: 
// 父组件中接收子组件传递过来的数据并且可以更改名称,
// 写法: v-model:modelValue="更改后的名称"(这里的名称也可以不进行更改)
<my-component v-model:foo="bar"></my-component>

  1. 多个v-model绑定:
    1> 多个v-model绑定的方式和单个绑定的方式一致,互不影响;每个 v-model 将同步到不同的 prop,而不需要在组件中添加额外的选项;
// 子组件中:
app.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  template: `
    <input 
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)">

    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)">
  `
})

// 父组件中:
<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>

  1. v-model修饰符:
    1> 内置修饰符:
    v-model 的内置修饰符有——.trim、.number 和 .lazy
    2> 自定义修饰符:
    自定义修饰符分为两种: 带参数和不带参数
  • 不带参数的自定义修饰符在子组件中将通过prop传递给组件,之后在父组件中使用;
  • 带参数的自定义修饰符生成的 prop 名称将为 arg + “Modifiers”,如:fooModifiers;
  • 案例: 实现效果----将 v-model 绑定提供的字符串的第一个字母大写;
  • 不带参数案例:
    1> 自定义修饰符要首先在子组件中的props中定义并传递给组件,如这里的modelModifiers;
    2> 在父组件中的使用: v-model.自定义修饰符名称=" "
// 不带参数的自定义修饰符:
// 子组件中:
// 1. 自定义修饰符要首先在子组件中的props中定义并传递给组件,如这里的modelModifiers;
// 2. 注意: 不带参数的自定义修饰符在props中统一名称都是modelModifiers,在父组件中使用时写法都为:v-model.自定义修饰符=""

app.component('my-component', {
  props: {
    modelValue: String,
    // 自定义修饰符,不带参数(modelModifiers)
    modelModifiers: {
      default: () => ({})
    }
  },
  template: `
    <input type="text" 
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)">
  `,
  created() {
    console.log(this.modelModifiers) // { capitalize: true } 这里的capitalize就是在父组件中使用的自定义修饰符名称,可以自己定义
  }
})

// 父组件中:
<div id="app">
  <my-component v-model.capitalize="myText"></my-component>
  {{ myText }}
</div>
  • 带参数的案例:
    1> 带参数的自定义修饰符要首先在子组件中的props中定义并传递给组件,名称是:‘arg + Modifiers’,如这里的fooModifiers, 参数就是前面的 arg;
    2> 在父组件中的使用: v-model:arg.自定义修饰符名称=“arg”
// 子组件中:
app.component('my-component', {
  props: ['foo', 'fooModifiers'],
  template: `
    <input type="text" 
      :value="foo"
      @input="$emit('update:foo', $event.target.value)">
  `,
  created() {
    console.log(this.fooModifiers) // { capitalize: true }
  }
})

// 父组件中:
<my-component v-model:foo.capitalize="bar"></my-component>
props数据
  1. 从父组件中接收的数据都在props的包裹下,但vue2和vue3中访问props中是数据方法是不同的;props: { value: { type: String} }
  2. vue2中访问props中的数据: js中访问----this.value, html中访问—value;
  3. vue3中访问props中的数据: js中访问—props.value, html中访问—value;
v-is指令
  1. v-is绑定的值是一个js的字符串文本;
<!-- 不正确,不会渲染任何内容 -->
<tr v-is="blog-post-row"></tr>

<!-- 正确 -->
<tr v-is="'blog-post-row'"></tr>
  1. v-is指令可以解析当前标签为组件:
teleport标签
  1. 背景: 当开发一些大型的vue项目时,可能会有很多重复使用的代码,但使用某些类型的组件和工具时,html逻辑可能不希望和我们渲染的元素处于同一个组件中,这时就会使用到teleport标签;
  2. 作用:
  3. 使用:
    < teleport to=“body”> </ teleport>
    属性: to: 表示teleport标签的内容要放在什么位置, to的后面可以是元素id,元素的类名,数据选择器,响应查询字符串;
    disabled: 表示teleport标签是否禁用;
多根节点组件
  1. 在vue2中一个组件内只能有一个根节点,所有的元素都被包裹在一个div中,但在vue3中一个组件内可以有多个根节点;

vue2中:

<template>
  <div>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </div>
</template>

vue3中:

<template>
  <header>...</header>
  <main v-bind="$attrs">...</main>
  <footer>...</footer>
</template>
setup(组合式API)
  • 本质: setup 选项是一个接收 props 和 context 的函数;
  • 执行时机: 在组件创建之前执行,一旦props被解析,就将作为组合式API的入口;
  • 调用时机: setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取;
  • 返回值: 将 setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板
  • 注意点: 1. 在setup中应避免使用‘this’, 因为它找不到组件实例;
    1. setup中的props是响应式的,但不能使用ES6解构,否则会消除props的响应;如果需要解构props可以使用toRefs函数来实现:
// MyBook.vue

import { toRefs } from 'vue'

setup(props) {
  const { title } = toRefs(props)

  console.log(title.value)
}
  • 3 .context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构
// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup(props) {
    console.log(props) // { user: '' }

    return {} // 这里返回的任何内容都可以用于组件的其余部分
  }
  // 组件的“其余部分”
}
// MyBook.vue

export default {
  setup(props, context) {
    // Attribute (非响应式对象)
    console.log(context.attrs)

    // 插槽 (非响应式对象)
    console.log(context.slots)

    // 触发事件 (方法)
    console.log(context.emit)
  }
}
访问组件的 property
  1. 因为setup执行时,组件实例还未被创建,因此,只能访问以下property:
    props, attrs, slots, emit;
    不能访问以下组件选项:
    data, computed, methods
class类的使用
  1. class类中所用到的所有变量(当前文件中的数据),必须提前在class类中定义好(定义类型),且要以分号结尾,否则会报错;
// 定义类(可写在单独的文件中)
class Topu {
	// Topu类中涉及到的变量,都要提前在这个部分定义好
	width: Number;		// 结尾必须顿号,不然报错
	height: Number;
	data: any;
	edges: any;	
	svg: any;
	...  
	// 有多少就定义多少,这是vue3+ts的写法,如果是vue2的话,不用事先声明,直接在constructor里面this.变量名,Topu类上就会出现该变量
	
	constructor(option) {		// 形参,实参会在实例化这个类的时候传进来
		console.log(this)		// this指向的是Topu这个类
		this.data = option		
	}
	
	// 写函数,挂载在Topu类上的函数,调用时就用this.函数名()即可
	函数名() {
		...
	}
	
	// 如果当前这个类中的方法比较多,还需同时调用,可以再写个方法,将其他方法都写在里面,最后只调用这一个就好
	render() {
		this.函数1()
		this.函数2()
		this.函数3()
		...
	}	
}

//  使用类

<script>
// 引入类
import Topu from '@/utils/Topu.ts'

// 使用类
let T = new Topu(optionData)
T.render()		// 调用Topu类中的方法
</script>

  1. class类的CSDN地址:https://blog.csdn.net/JackieDYH/article/details/115492392
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值