Vue.js 3.0
版在2020年9月18日发布Vue3
支持vue2
的大多数特性Vue3
在2022年2月7日成为新的默认版本Proxy
代替Object.defineProperty
实现数据响应式DOM
的实现和Tree-Shaking
Typescript
打开vue3官网->起步->安装->CDN->单击CDN的src->右键另存为(将vue.global.js
存到自己创建的文件夹)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="./vue.global.js">script>
head>
<body>
<div id="app">
<h1>{{msg}}h1>
div>
body>
html>
<script>
/*
vue2写法:
new Vue({
// 挂载应用实例
el:"#app",
// data可以写成一个对象
data:{
},
methods: {
},})
*/
// 1.创建vue实例对象
const app = Vue.createApp({
// data属性必须采用函数的写法
data() {
return {
msg: 'hello vue3'
}
},
})
console.log(app);
// 2.挂载应用实例
app.mount('#app');
script>
安装最新版本的vue cli
脚手架
npm i @vue/cli -g
创建项目
vue create 项目名称
选择vue3
版本的默认选项
启动项目
npm run serve
vite
是vue
官方发布的一款构建工具
vite
底层不是基于webpack
vite
底层是基于浏览器对ES模块语法的支持来进行项目构建, 所有构建速度很快
创建项目
npm init vite 项目名称
安装项目依赖
项目根目录运行初始化命令
npm install
// 或者
npm i
启动项目
项目根目录运行启动命令
npm run dev
默认监听端口号3000
http://localhost:3000
(模块化理解):
tools.js
export default{
add(x,y){
return x+y;
},
reduce(x,y){
return x-y;
}
}
index.html
方法一:
方法二:
vue2
中组件的模板必须有一个唯一的根标签
vue3
中组件模板可以有多个根标签
Home.vue
{{msg}}
{{test}}
App.vue
页面结果:
用法: 在组件对象中添加setup
方法
该方法在组件创建完成之前自动调用, 所用不能在其内部使用this
访问组件对象
该方法中返回的数据会自动和组件对象中的data
合并
该方法中返回的方法会自动和组件对象中的methods
合并
export default {
setup(){
}
}
eg:
{{name}}
{{age}}
作用: 帮助我们创建响应式数据对象
语法
import {reactive} from 'vue';
export default {
setup(){
// 创建响应式数据对象
const obj=reactive({
});
return obj;
}
}
eg:
{{name}}
{{age}}
作用: 基于基本数据类型(字符串, 布尔, 数值) 创建一个响应式的数据对象
语法
import {ref} from 'vue';
export default {
setup(){
// 使用ref基于基本数据类型创建响应式数据对象
const name=ref('张三');
const age=ref(20);
console.log(name);
console.log(age);
return {
name,age
}
}
}
视图中引用直接通过数据名称引用即可
{{name}}
eg:
{{name}}
{{age}}
reactvie
方法创建的响应式数据对象, 支持ES6
的解构赋值操作( reactive
创建的响应式数据对象默认不支持ES6
解构赋值, 因为会使其失去响应式 )reactive
创建的响应式数据对象import {torRefs} from 'vue';
export default {
setup(){
// 创建响应式数据对象
const {name,age}=toRefs(reactive({
name:'张三',
age:20
}));
return {
name,age
};
}
}
eg:
{{name}}
{{age}}
vue2
export default {
computed:{
// 计算属性方法
}
}
vue3
使用方式1
export default {
computed:{
// 计算属性方法
}
}
使用方式2
import {computed} from 'vue';
const computedData=computed(()=>{})
eg:
{{msg}}
{{reverseMsg}}
vue2
export default {
// 侦听器
watch:{
}
}
vue3
使用方式1
export default {
// 侦听器
watch:{
}
}
使用方式2
watch
方法创建的普通侦听器无法侦听到数组元素的变化, 必须使用深度侦听器setup
中监听对象的某个属性,监听不到setup
中监听对象,监听不到某个属性的新旧值变化watch
中监听对象的某个属性,可以监听新旧值变化toRefs
解构reactive
对象可实现监听某属性新旧值ref
,不用reactive
import {watch} from 'vue';
export default {
setup(){
// 普通侦听器
watch(被侦听的数据,function(value,oldValue){
});
// 深度侦听器
watch(被侦听的数据,function(value,oldValue){
},{deep:true});
}
}
eg:
普通/深度监听写法
监听对象的某个属性
(setup中监听对象的某个属性,监听不到)
(setup中监听对象,监听不到某个属性的新旧值变化)
(watch中监听对象的某个属性,可以监听新旧值变化)
监听数组添加属性
(能监听,但是监听不到变化)
- {{ item }}
toRefs解构reactive对象可实现监听新旧值
如果要在setup
方法中调用组件生命周期钩子函数: 在原来的生命周期钩子函数名称前加on
关键, 并且需要保持小驼峰的命名方式
不能在setup方法中调用beforeCreate
,created
者2个生命周期钩子函数 ( 因为beforeCreate
,created
这两个生命周期钩子函数的执行时间和setup
方法接近 )
注意:
beforeDestory
->beforeUnmount
destoryed
->unmounted
this.$destory()
->app.unmount()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R6X6Qq2p-1659530104528)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a16c02f295714778abcbd52057a3eeee~tplv-k3u1fbpfcp-watermark.image?)]
语法
// 导入
import {onMounted,onUpdated,onUnmounted} from 'vue';
export default {
setup(){
onMounted(function(){
console.log('组件视图挂载完成');
});
onUpdated(function(){
console.log('组件更新完成');
});
onUnmounted(function(){
console.log('组件卸载完成');
});
}
}
vue2
系统会自动在render中注入渲染函数
import App from './App.vue';
new Vue({
// h是系统自动注入的渲染函数
render(h){
return h(App);
}
});
vue3
vue3中提供了一个h渲染函数, 我们需要手动导入才能使用, 系统不会自动将其注入到render中
import { createApp,h} from 'vue'
import App from './App.vue'
// createApp(App).mount('#app')
createApp({
render(){
// 调用渲染函数h进行组件渲染
// return h(App);
// h创建元素对象: h('元素名称',{/*属性集合*/},['子节点'])
return h('h1',{style:{color:'red'}},'hello vue3');
}
}).mount('#app')
teleport是一个vue3中提供的系统组件
作用: 可以让开发者动态指定组件内的某个视图结构的挂载位置
语法
通过函数方式定义的组件
特点: 函数组件默认没有状态数据和生命周期
语法
定义
import { h } from 'vue';
export default function FunctionComponent(props){
// console.log(props);
return h('h1',{style:{color:'red'}},props.msg);
}
调用
作用: 给Vue注册全局功能( 给每一个vue组件提供全局的属性或者方法 )
vue2中创建vue应用
const app=new Vue({
});
vue3中创建应用对象
const app=Vue.createApp({
});
app.config.globalProperties
需求: 希望在每一个vue组件中都可以通过组件对象访问axios
下载安装axios
npm i axios
必须使用app.config.globalProperties全局注册axios
app.config.globalPropperties.$axios=axios;
vue2中插件注册
Vue.use();
vue3中插件注册
// app: 是通过Vue.createApp()方法创建的应用对象
app.use();
功能: 将axios自动注册成全局对象( 在每一个组件中都可以通过this来访问axios )
开发vue插件
函数方式
// app: 系统自动注入, 应用对象
// options: 调用者通过app.use()方法手动传入的配置对象(可选)
function MyAxios(app,options){
}
对象方式
const MyAxios={
// app: 系统自动注入的应用对象
// options: 调用者通过app.use()方法手动传递的配置对象(可选)
install(app,options){
}
}
// 导入axios
import axios from 'axios';
export default {
// app: 系统自动注入的应用对象
install(app){
// 通过app.config.globalProperties全局注册axios
app.config.globalProperties.$http=axios;
}
}
销毁阶段生命周期钩子函数名称发生了变换
vue2中
export default {
// 销毁前
beforeDestroy(){
},
// 销毁后
destroyed(){
}
}
vue3中
$destroy
export default {
// 销毁前
beforeUnmount(){
},
// 销毁后
unmounted(){
}
}
vue2中注册全局组件
Vue.component('组件名称',{
/* 组件对象 */
});
vue3中注册全局组件
import {createApp} from 'Vue';
// 创建实例对象
const app=createApp();
// 注册全局组件
app.component('组件名称',{
// 组件对象
})
const Home=()=>import('./pages/Home.vue');
const asyncModal = {
// vue2中通过component属性节点指定目标组件
component: () => import('./Modal.vue'),
// 延时时长
delay: 200,
// 超时时间
timeout: 3000,
// 错误处理组件
error: ErrorComponent,
// 加载中的组件
loading: LoadingComponent
}
import {defineAsyncComponent} from 'vue';
const Home=defineAsyncComponent(()=>import('./pages/Home.vue'));
const asyncModalWithOptions = defineAsyncComponent({
// vue3中通过loader属性节点指定目标组件
loader: () => import('./Modal.vue'),
// 延时时长
delay: 200,
// 超时时间
timeout: 3000,
// 错误处理组件
error: ErrorComponent,
// 加载中的组件
loading: LoadingComponent
})
vue2中
this.$refs.list[0];
this.$refs.list[1];
vue3中: 需要手动维护一个数组用来保存每一个子元素对象
export default {
data(){
return {
// 保存列表元素的元素对象
refList:[]
}
},
methods:{
// el: 系统自动注入的参数, 子元素的元素对象
addRef(el){
this.refList.push(el);
}
},
// 在组件更新之前, 清空数组中的元素, 否则该数组中的元素会越来越多
beforeUpdate(){
this.refList=[];
}
}
vue2中
v-if和v-for指令同时应用于同一个元素之上, v-for指令优先级会更高, v-if会作用域v-for循环遍历产生的每一个子元素之上
vue3中
v-if指令的优先级高于v-for指令, v-if指令执行, 然后执行v-for指令
vue2中
Vue.directive('指令名称',function(el, binding, vnode){
});
vue3中
然而,在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使钩子的目标事件十分相似。我们现在把它们统一起来了:
updated
有太多相似之处,因此它是多余的。请改用 updated
。const app=createApp();
app.directive('指令名称',function(el, binding, vnode){
});
$attrs
包含class&style
vue2中
this.$attrs
包含调用组件的时候通过自定义属性所传递的数据, 但是不包含class属性和style属性vue3中
this.$attrs
: 调用组件的时候通过自定义属性所传递的所有数据, 包含class属性和style属性
vue3中针对组件对象新增的属性节点
作用:
EmitsTest子组件
过渡动画选择器名称的变化
transition
组件属性的变化
transition-group组件默认不再自动渲染一个span标签, 但是依然可以通过tag属性指定transition-group渲染的元素名称
vue2中使用v-model实现组件双向数据绑定
在组件的调用标签上添加了一个自定义属性value, 通过value属性向组件内部传递数据
在组件的调用标签上通过v-on监听了input事件
希望对v-model指令背后使用的自定义属性名称和自定义事件名称进行修改
export default{
model:{
prop:'', // 修改自定义属性名称
event:'' // 修改自定义事件名称
}
}
vue3中使用v-model指令实现组件双向数据绑定
在组件的调用标签上添加了一个自定义属性modelValue
, 通过modelValue
向组件内部传递数据
在组件的调用标签上通过v-on
监听了update:modelValue
希望对v-model指令背后的自定义属性名称和自定义事件名称进行修改
isShow
来接收数据$emit()
触发事件update:isShow
vue3中允许在同一个组件的调用标签上多次使用v-model指令
isShow
, msg
, 同时监听了两个自定义事件update:isShow
和update:msg
$children
获取子组件的组件对象ref
属性来操作子组件vue2中可以通过native事件修饰符修饰某个事件, 被修饰的事件将被作为原生事件处理, 可以在子组件的内部使用原生方式对其进行触发
vue3中移除了native事件修饰符
vue3中如果希望某个事件可以在子组件的内部被当做原生事件来处理?
在vue3中组件调用标签上注册的事件, 默认都会当做原生的事件进行触发
在vue3中如果我们希望组件调用标签上所注册的事件是一个自定义事件, 可以在组件内部通过属性节点emits
来进行声明, 在emits
属性节点中生命过的事件就不会当做系统事件来处理
export default {
// 此处声明过的事件, 会认为是自定义事件, 必须通过自定义事件的方式进行触发, 即使事件明层和系统事件名称冲突, 也不会通过系统事件的触发方式触发该事件
emits:['click']
}
vue2中过滤器的使用
作用: 对视图中即将输出的数据进行预处理
定义
// data: 系统自动注入, 待处理的数据
Vue.filter('过滤器名称',function(data){
// 必须返回处理结果
});
export default {
filters:{
// data: 系统自动注入, 待处理的数据
过滤器名称:function(data){
// 必须返回处理结果
}
}
}
使用: 只能在插值表达式和v-bind指令中使用
{{待处理数据 | 过滤器名称}}
vue3中建议使用自定义方法或者computed计算属性来代替过滤器
vue2中可以使用vm.$set
方法解决通过数组下标方式向数组中添加元素, 视图不更新的问题
vm.$set(目标数组,数组下标,数组元素);
vm.$set(目标对象,属性名,属性值);
vue3中移除实例方法vm.$set
和静态方法Vue.set
$destroy
销毁vue组件$destroy
,下载安装
npm i vue-router
导入路由模块
import Router from 'vue-router';
注册插件
Vue.use(Router);
创建路由实例对象
const router=new Router({
routes:[], // 引用路由规则数组
mode:'hash' // 指定路由模式, 可选值hash(默认路由模式),history
});
挂载路由实例对象
new Vue({
el:'#app',
router // 挂载路由实例对象
});
在根组件中添加路由占位符
下载安装
npm i vur-router@next
导入路由模块
import {createRouter,createWebHashHistory,createWebHistory} from 'vue-router';
创建路由实例
const router=createRouter({
routes:[], // 路由规则
//必须手动指定路由模式, createWebHashHistory: 提供的是访问地址带#的路由模式
// createWebHistory: 提供的是访问地址不带#的路由模式
history:createWebHashHistory()
});
注册路由实例
//app: 通过Vue.createApp()创建的应用实例
app.use(router);
在根组件中添加路由占位符
定义动态路由规则
const routes=[
{
path:'/goods/:id',
component:Goods
}
]
进行路由传参
获取动态路由参数
this.$route.params.id
在页面访问地址中拼接查询字符串
新闻详情
在目标页面中获取查询字符串参数
this.$route.query.id
this.$route.query.title
配置嵌套路由规则
const routes=[
{
path:'/ucenter',
component:Ucenter,
// 嵌套路由规则
children:[
{
name:'order',
path:'/order',
component:Order
},
{
name:'address',
path:'/Address',
component:Address
}
]
}
]
父级路由对应的视图中添加二级路由占位符
npm i element-plus
将element-plus组件库中提供的所有组件全部导入我们自己的vue3的项目中
缺点: 导致项目最终构建出来的文件体积过大, 性能不好
main.js
// 导入element-plus核心文件
import Element from 'element-plus';
// 导入element-plus的样式文件
import 'element-plus/dist/index.css';
// 全局注册组件库
const app=Vue.createApp(App);
app.use(Element);
安装一个开发依赖
npm i unplugin-vue-components
在项目配置文件中添加配置项
vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
+import Components from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
+ Components({
+ resolvers: [ElementPlusResolver()],
+ }),
]
})
重启开发服务器
npm run dev
安装插件
npm install unplugin-vue-components
修改项目配置文件(webpack
进行配置)
vue.config.js
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports={
// webpack的配置节点
configureWebpack:{
plugins: [
Components({
resolvers: [ElementPlusResolver()],
}),
]
}
}
重启开发服务器
npm run serve
下载安装
npm i vuex
导入
import Vuex from 'vuex'
注册
Vue.use(Vuex)
创建数据仓储对象
const store=new Vuex.Store({
state:{
// 状态数据
},
mutaionts:{
// 操作state状态数据的同步方法
},
actions:{
// 发送数据请求的异步方法
},
getters:{
// 计算属性方法
},
modules:{
// 子模块
}
})
挂载数据仓储对象
/mian.js
new Vue({
// 注册数据仓储对象
store
});
在组件中使用数据仓储对象
this.$store
下载安装
npm i vuex@next
导出
import {createStore} from 'vuex';
创建数据仓储对象
const store=createStore({
state:{
// 状态数据
},
mutations:{
// 操作state状态数据的同步方法
},
actions:{
// 发送数据请求的异步方法
},
getters:{
// 计算属性方法
},
modules:{
// 子模块
}
});
注册仓储对象
// app:Vue.createApp()创建的应用对象
app.use(store);
留言板
提交留言
x
昵称: {{item.nickname}}
留言内容: {{item.content}}