Vue3

一 ref属性

被用来给元素或子组件注册引用信息(id的替代者)
应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
使用方式:
打标识:

.....


获取:this.$refs.xxx

 

<template>
  <div>
    <h1 v-text="msg" ref="title"></h1>
    <button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
    <HelloWorld ref="sch" />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  name: "App",
  components: { HelloWorld },
  data() {
    return {
      msg: "lqz",
    };
  },
  methods: {
    showDOM() {
      console.log(this.$refs.title); //真实DOM元素
      console.log(this.$refs.btn); //真实DOM元素
      console.log(this.$refs.sch); //School组件的实例对象(vc)
    },
  },
};
</script>

二 props配置项

功能:让组件接收外部传过来的数据

传递数据:

接收数据:

第一种方式(只接收):props:['name']

第二种方式(限制类型):props:{name:String}

第三种方式(限制类型、限制必要性、指定默认值):

props:{
	name:{
	type:String, //类型
	required:true, //必要性
	default:'老王' //默认值
	}
}

三 mixin(混入)

mixin(混入)
功能:可以把多个组件共用的配置提取成一个混入对象

使用方式:

第一步定义混入:

export const hunhe = {
  methods: {
    showName() {
      alert(this.name);
    },
  },
  mounted() {
    console.log("你好啊!");
  },
};
export const hunhe2 = {
  data() {
    return {
      x: 100,
      y: 200,
    };
  },
};

第二步:使用混入(全局)

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
import {hunhe,hunhe2} from './mixin'
//关闭Vue的生产提示
Vue.config.productionTip = false

Vue.mixin(hunhe)
Vue.mixin(hunhe2)


//创建vm
new Vue({
	el:'#app',
	render: h => h(App)
})

第三步:使用混入(局部)

<template>
  <div>

  </div>
</template>

<script>
import {hunhe,hunhe2} from '../mixin'

export default {
  name: "App",
  data() {
    return {
      name: "lqz",
    };
  },
  mixins:[hunhe,hunhe2]
};
</script>

四 插件

  1. 功能:用于增强Vue

  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  3. 定义插件:plugins/index.js

    import Vue from "vue";
    
    export default {
        install(a) {
            console.log('执行了插件', a)
            // 定义指令
            //定义全局指令:跟v-bind一样,获取焦点
            Vue.directive("fbind", {
                //指令与元素成功绑定时(一上来)
                bind(element, binding) {
                    element.value = binding.value;
                },
                //指令所在元素被插入页面时
                inserted(element, binding) {
                    element.focus();
                },
                //指令所在的模板被重新解析时
                update(element, binding) {
                    element.value = binding.value;
                },
            });
    
            //定义混入,所有vc和vm上都有name和lqz
            Vue.mixin({
                data() {
                    return {
                        name: '彭于晏',
                        age: 19,
                    };
                },
            });
    
            // 原型上放方法,所有vc和vm都可以用hello
            Vue.prototype.hello = () => {
                alert("你好啊");
            };
    
    
        }
    }
    
  4. 使用插件:main.js中

    import plugins from './plugins'
    
    Vue.use(plugins, 1, 2, 3);
    
  5. App.vue中

<template>
  <div>
  {{name}}
    <input type="text" v-fbind:value="v">
    <br>
    <button @click="hello">点我</button>
  </div>
</template>

<script>

export default {
  name: "App",
  data() {
    return {
      v:'xxx'
    };
  },
};
</script>


五 scoped样式

  1. 作用:让样式在局部生效,防止冲突。
  2. 写法:<style scoped>

六 插槽

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件
  2. 分类:默认插槽、具名插槽
  3. 使用方式:
<template v-slot:footer>
    <div>html结构2</div>
</template>

七 Elementui的使用

https://element.eleme.cn/#/zh-CN/component/installation

八 vuex的使用

1.概念

在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

2.何时使用?

多个组件需要共享数据时

image-20221030030358773

img

3.搭建vuex环境

创建文件:src/store/index.js

   //引入Vue核心库
   import Vue from 'vue'
   //引入Vuex
   import Vuex from 'vuex'
   //应用Vuex插件
   Vue.use(Vuex)
   
   //准备actions对象——响应组件中用户的动作
   const actions = {}
   //准备mutations对象——修改state中的数据
   const mutations = {}
   //准备state对象——保存具体的数据
   const state = {}
   
   //创建并暴露store
   export default new Vuex.Store({
   	actions,
   	mutations,
   	state
   })

main.js中创建vm时传入store配置项

   ......
   //引入store
   import store from './store'
   ......
   
   //创建vm
   new Vue({
   	el:'#app',
   	render: h => h(App),
   	store
   })

4.基本使用

初始化数据、配置actions、配置mutations,操作文件store.js

   //引入Vue核心库
   import Vue from 'vue'
   //引入Vuex
   import Vuex from 'vuex'
   //引用Vuex
   Vue.use(Vuex)
   
   const actions = {
       //响应组件中加的动作
   	jia(context,value){
   		// console.log('actions中的jia被调用了',miniStore,value)
   		context.commit('JIA',value)
   	},
   }
   
   const mutations = {
       //执行加
   	JIA(state,value){
   		// console.log('mutations中的JIA被调用了',state,value)
   		state.sum += value
   	}
   }
   
   //初始化数据
   const state = {
      sum:0
   }
   
   //创建并暴露store
   export default new Vuex.Store({
   	actions,
   	mutations,
   	state,
   })

组件中读取vuex中的数据:$store.state.sum

组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

<template>
  <div>

    <hr>
    {{sum}}
    <button @click="handleClick">点我</button>
  </div>
</template>

<script>

export default {
  name: "App",
  data() {
    return {
      v:'xxx',
      sum:this.$store.state.sum
    };
  },
  methods:{
    handleClick(){
      // action中的方法名
      // this.$store.dispatch('jia',2)
      // console.log(this.$store.state.sum)

      //mutations中的方法名
      this.$store.commit('JIA',4)
      console.log(this.$store.state.sum)
    }
  }
};
</script>


备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

九 Router使用

9.1 说明

  1. 官方提供的用来实现SPA 的vue 插件
  2. github: https://github.com/vuejs/vue-router
  3. 中文文档: http://router.vuejs.org/zh-cn/
  4. 下载: npm install vue-router --save
//引入VueRouter
   import VueRouter from 'vue-router'
   //引入Luyou 组件
   import About from '../components/About'
   import Home from '../components/Home'
   
   //创建router实例对象,去管理一组一组的路由规则
   const router = new VueRouter({
   	routes:[
   		{
   			path:'/about',
   			component:About
   		},
   		{
   			path:'/home',
   			component:Home
   		}
   	]
   })
   
   //暴露router
   export default router

9.2 相关API

VueRouter(): 用于创建路由器的构建函数

new VueRouter({
// 多个配置项
})

路由配置

routes: [
	{ // 一般路由
		path: '/about',
		component: About
	},
	{ // 自动跳转路由
		path: '/',
		redirect: '/about'
	}
]

注册路由

import router from './router'
new Vue({
	router
})

使用路由组件标签

<router-link>: 用来生成路由链接

<router-link to="/xxx">Go to XXX</router-link>

<router-view>: 用来显示当前路由组件界面

<router-view></router-view>

9.3 路由嵌套

9.4 向路由组件传递数据

//1 路由配置
children: [
	{
	path: 'mdetail/:id',
	component: MessageDetail
	}
]
// 2 <router-link :to="'/home/message/mdetail/'+m.id">{{m.title}}</router-link>

// 3 this.$route.params.id

9.5 相关API

this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
this.$router.back(): 请求(返回)上一个记录路由
this.$router.go(-1): 请求(返回)上一个记录路由
this.$router.go(1): 请求下一个记录路由

9.6 多级路由

  routes:[
   	{
   		path:'/about',
   		component:About,
   	},
   	{
   		path:'/home',
   		component:Home,
   		children:[ //通过children配置子级路由
   			{
   				path:'news', //此处一定不要写:/news
   				component:News
   			},
   			{
   				path:'message',//此处一定不要写:/message
   				component:Message
   			}
   		]
   	}
   ]

// 跳转
<router-link to="/home/news">News</router-link>

9.7 命名路由(可以简化路由的跳转)

    {
      	path:'/demo',
      	component:Demo,
      	children:[
      		{
      			path:'test',
      			component:Test,
      			children:[
      				{
                name:'hello' //给路由命名
      					path:'welcome',
      					component:Hello,
      				}
      			]
      		}
      	]
      }
 <!--简化前,需要写完整的路径 -->
      <router-link to="/demo/test/welcome">跳转</router-link>
      
      <!--简化后,直接通过名字跳转 -->
      <router-link :to="{name:'hello'}">跳转</router-link>
      
      <!--简化写法配合传递参数 -->
      <router-link 
      	:to="{
      		name:'hello',
      		query:{
      		   id:666,
             title:'你好'
      		}
      	}"
      >跳转</router-link>

9.8 router-link的replace属性

作用:控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
如何开启replace模式:News

9.10 编程式路由导航

//$router的两个API
   this.$router.push({
   	name:'xiangqing',
   		params:{
   			id:xxx,
   			title:xxx
   		}
   })
   
   this.$router.replace({
   	name:'xiangqing',
   		params:{
   			id:xxx,
   			title:xxx
   		}
   })
   this.$router.forward() //前进
   this.$router.back() //后退
   this.$router.go() //可前进也可后退

9.11 路由守卫

  1. 作用:对路由进行权限控制
  2. 分类:全局守卫、独享守卫、组件内守卫

全局守卫

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'

//创建并暴露一个路由器
const router =  new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About,
			meta:{title:'关于'}
		},
		{
			name:'zhuye',
			path:'/home',
			component:Home,
			meta:{title:'主页'},
			children:[
				{
					name:'xinwen',
					path:'news',
					component:News,
					meta:{isAuth:true,title:'新闻'}
				},
				{
					name:'xiaoxi',
					path:'message',
					component:Message,
					meta:{isAuth:true,title:'消息'},
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
							meta:{isAuth:true,title:'详情'},

							//props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给Detail组件。
							// props:{a:1,b:'hello'}

							//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
							// props:true

							//props的第三种写法,值为函数
							props($route){
								return {
									id:$route.query.id,
									title:$route.query.title,
									a:1,
									b:'hello'
								}
							}

						}
					]
				}
			]
		}
	]
})

//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
	console.log('前置路由守卫',to,from)
	if(to.meta.isAuth){ //判断是否需要鉴权
		if(localStorage.getItem('name')==='lqz'){
			next()
		}else{
			alert('名不对,无权限查看!')
		}
	}else{
		next()
	}
})

//全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
	console.log('后置路由守卫',to,from)
	document.title = to.meta.title || 'lqz系统'
})

export default router

独享守卫

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../pages/About'
import Home from '../pages/Home'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'

//创建并暴露一个路由器
const router =  new VueRouter({
	routes:[
		{
			name:'guanyu',
			path:'/about',
			component:About,
			meta:{title:'关于'}
		},
		{
			name:'zhuye',
			path:'/home',
			component:Home,
			meta:{title:'主页'},
			children:[
				{
					name:'xinwen',
					path:'news',
					component:News,
					meta:{isAuth:true,title:'新闻'},
					beforeEnter: (to, from, next) => {
						console.log('独享路由守卫',to,from)
						if(to.meta.isAuth){ //判断是否需要鉴权
							if(localStorage.getItem('name')==='lqz'){
								next()
							}else{
								alert('名不对,无权限查看!')
							}
						}else{
							next()
						}
					}
				},
				{
					name:'xiaoxi',
					path:'message',
					component:Message,
					meta:{isAuth:true,title:'消息'},
					children:[
						{
							name:'xiangqing',
							path:'detail',
							component:Detail,
							meta:{isAuth:true,title:'详情'},

						}
					]
				}
			]
		}
	]
})


export default router

组件内守卫

  //进入守卫:通过路由规则,进入该组件时被调用
   beforeRouteEnter (to, from, next) {
   },
   //离开守卫:通过路由规则,离开该组件时被调用
   beforeRouteLeave (to, from, next) {
   }


	//通过路由规则,进入该组件时被调用
		beforeRouteEnter (to, from, next) {
			console.log('About--beforeRouteEnter',to,from)
			if(to.meta.isAuth){ //判断是否需要鉴权
				if(localStorage.getItem('school')==='atguigu'){
					next()
				}else{
					alert('学校名不对,无权限查看!')
				}
			}else{
				next()
			}
		},

		//通过路由规则,离开该组件时被调用
		beforeRouteLeave (to, from, next) {
			console.log('About--beforeRouteLeave',to,from)
			next()
		}

9.12 路由器的两种工作模式

1 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
2 hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
3 hash模式:
地址中永远带着#号,不美观 。
若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
兼容性较好。
4 history模式:
地址干净,美观 。
兼容性和hash模式相比略差。
应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

10 localStorage和sessionStorage

//存:
    var obj = {"name":"xiaoming","age":"16"}
    localStorage.setItem("userInfo",JSON.stringify(obj));

//取:
    var user = JSON.parse(localStorage.getItem("userInfo"))
//删除:
    localStorage.remove("userInfo);
//清空:
    localStorage.clear();

一 Vue3的变化

1.性能的提升

  • 打包大小减少41%

  • 初次渲染快55%, 更新渲染快133%

  • 内存减少54%

2.源码的升级

  • 使用Proxy代替defineProperty实现响应式

  • 重写虚拟DOM的实现和Tree-Shaking

3.拥抱TypeScript

  • Vue3可以更好的支持TypeScript

4.新的特性

  1. Composition API(组合API)

    • setup配置
    • ref与reactive
    • watch与watchEffect
    • provide与inject
  2. 新的内置组件

    • Fragment
    • Teleport
    • Suspense
  3. 其他改变

    • 新的生命周期钩子
    • data 选项应始终被声明为一个函数
    • 移除keyCode支持作为 v-on 的修饰符

5 组合式API和配置项API

5.1 Options API 存在的问题

使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。

5.2 Composition API 的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

6 项目分析

分析文件目录

main.js

Vue2项目的main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

看看vm是什么

const vm = new Vue({
  render: h => h(App),
})

console.log(vm)

vm.$mount('#app')

我们再来看看Vue3项目中的main.js

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

我们来分析一下吧

// 引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'

// 创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
console.log(app)
// 挂载
app.mount('#app')

这里的app到底是啥,我们输出到控制台看看

App.vue

我们再来看看组件

template标签里可以没有根标签了

<template>
	<!-- Vue3组件中的模板结构可以没有根标签 -->
	<img alt="Vue logo" src="./assets/logo.png">
	<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

二 创建Vue3.0工程

1.使用 vue-cli 创建

官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

2.使用 vite 创建

官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite

vite官网:https://vitejs.cn

介绍:https://cn.vitejs.dev/guide/why.html#the-problems

  • 什么是vite?—— 新一代前端构建工具。
  • 优势如下:
    • 开发环境中,无需打包操作,可快速的冷启动。
    • 轻量快速的热重载(HMR)。
    • 真正的按需编译,不再等待整个应用编译完成。
  • 传统构建 与 vite构建对比图
image-20221030000602003
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
image-20221030001055318
# 确保你安装了最新版本的 Node.js,然后在命令行中运行以下命令 (不要带上 > 符号):
> npm init vue@latest
# 这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示:

✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes

Scaffolding project in ./<your-project-name>...
Done.
# 如果不确定是否要开启某个功能,你可以直接按下回车键选择 No。在项目被创建后,通过以下步骤安装依赖并启动开发服务器:

> cd <your-project-name>
> npm install
> npm run dev
image-20221030001011182

三、常用API

3.1 setup

  1. setup为Vue3.0中一个新的配置项,值为一个函数

  2. setup是所有Composition API(组合API)编写的位置

  3. 组件中所用到的:数据、方法等等,均要配置在setup中

  4. setup函数的返回值:返回一个对象,对象中的属性、方法, 在模板中均可以直接使用

  5. 注意:

    尽量不要与Vue2.x配置混用

    • Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。
    • 但在setup中不能访问到Vue2.x配置(data、methos、computed...)。
    • 如果有重名, setup优先。
<template>
  {{name}}--{{age}}--{{xx}}
</template>

<script>

export default {
  name: 'App',
  data(){
    return {
      xx:this.name
    }
  },
  setup(){
    let name='lqz'
    let age =19
    return {
      name,age
    }
  },
}
</script>

3.2 ref函数

  • 作用: 定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
    • JS中操作数据: xxx.value
    • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
  • 备注:
    • 接收的数据可以是:基本类型、也可以是对象类型。
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的
    • 对象类型的数据:内部 求助 了Vue3.0中的一个新函数—— reactive函数

3.3 reactive函数

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是“深层次的”
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作
<template>
  {{ name }}--{{ age }}--{{ xx }}
  <br>
  {{person}}
  <button @click="handleClick">点我</button>
  <br>

</template>

<script>

import {ref, reactive} from 'vue'

export default {
  name: 'App',
  data() {
    return {
      xx: this.name
    }
  },
  setup() {
    let name = 'lqz'
    let age = ref(19)
    // const person = ref({    // 内部包装成reactive
    const person = reactive({
      name: '彭于晏',
      age: 88
    })

    function handleClick() {
      // console.log(age)
      // age.value++


      console.log(person)
      person.age++

    }
    return {
      name, age, handleClick,person
    }
  },
}
</script>

3.4 reactive对比ref

  • 从定义数据角度对比:
    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value

3.5 setup的两个注意点

  • setup执行的时机

    • 在beforeCreate之前执行一次,this是undefined。
  • setup的参数

    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
      • slots: 收到的插槽内容, 相当于 this.$slots
      • emit: 分发自定义事件的函数, 相当于 this.$emit

3.6 计算属性与监视

1.computed函数

  • 与Vue2.x中computed配置功能一致

  • 写法

    <template>
      <p>姓:<input type="text" v-model="person.firstName"></p>
      <p>名:<input type="text" v-model="person.lastName"></p>
      <p>全名:{{ person.fullName }}</p>
      <p>全名修改:<input type="text" v-model="person.fullName"></p>
    
    </template>
    
    <script>
    
    import {ref, reactive} from 'vue'
    import {computed} from 'vue'
    
    export default {
      name: 'App',
      setup() {
        const person = reactive({
          firstName: '刘',
          lastName: '清政'
        })
    
        // let fullName = computed(() => {
        //   return person.firstName + '-' + person.lastName
        // })
        // 或者,传入箭头函数
        // person.fullName=computed(() => {
        //   return person.firstName + '-' + person.lastName
        // })
        // 修改,传入配置项目
        person.fullName = computed({
          get() {
            return person.firstName + '-' + person.lastName
          },
          set(value) {
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
          }
        })
        return {person}
      },
    }
    </script>
    
    

2.watch函数

  • 与Vue2.x中watch配置功能一致

  • 两个小“坑”:

    • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
    • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
    <template>
      <h2>年龄是:{{ age }}</h2>
      <button @click="age++">点我年龄增加</button>
      <hr>
      <h2>姓名是:{{ person.name }}</h2>
      <button @click="person.name+='?'">点我姓名变化</button>
      <hr>
      <h2>sum是:{{ sum }},msg是:{{ msg }}</h2>
      <button @click="sum++">点我sum变化</button>
      |
      <button @click="msg+='?'">点我msg变化</button>
    
    </template>
    
    <script>
    
    import {ref, reactive} from 'vue'
    import {watch} from 'vue'
    
    export default {
      name: 'App',
      setup() {
        const age = ref(19)
        const person = reactive({
          name: 'lqz',
          age: 20
        })
        //1 监听普通
        watch(age, (newValue, oldValue) => {
          console.log('sum变化了', newValue, oldValue)
        })
        // 2 监听对象
        watch(() => person.name, (newValue, oldValue) => {
          console.log('person.name变化了', newValue, oldValue)
        })
        // 3 监听多个
        const sum = ref(100)
        const msg = ref('很好')
    
        watch([sum, msg], (newValue, oldValue) => {
          console.log('sum或msg变化了', newValue, oldValue)
        })
        return {person, age, sum, msg}
      },
    }
    </script>
    
    

3.watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调。

  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

  • watchEffect有点像computed:

    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
    //watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
    watchEffect(() => {
          const x1 = sum.value
          const x2 = person.age
          console.log('watchEffect配置的回调执行了')
        })
    

3.7 生命周期

组件生命周期图示
  • Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
    • beforeDestroy改名为 beforeUnmount
    • destroyed改名为 unmounted
  • Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
    • beforeCreate===>setup()
    • created=======>setup()
    • beforeMount ===>onBeforeMount
    • mounted=======>onMounted
    • beforeUpdate===>onBeforeUpdate
    • updated =======>onUpdated
    • beforeUnmount ==>onBeforeUnmount
    • unmounted =====>onUnmounted

3.8 自定义hook函数

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。

  • 类似于vue2.x中的mixin。

  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

3.8.1 打点功能

<template>
  <h2>点击的x坐标:{{ point.x }},y坐标:{{ point.y }}</h2>

</template>

<script>

import {reactive} from 'vue'
import {onMounted, onBeforeUnmount} from 'vue'

export default {
  name: 'Point',
  setup() {
    const point = reactive({
      x: 0,
      y: 0
    })

    function getPoint(event) {
      console.log(event.pageX)
      console.log(event.pageY)
      point.x = event.pageX
      point.y = event.pageY
    }

    // 挂在完成开始执行
    onMounted(() => {
      window.addEventListener('click', getPoint)
    })
    // 接除挂载时执行
    onBeforeUnmount(() => {
      console.log('sss')
      window.removeEventListener('click', getPoint)
    })
    return {point}
  },
}
</script>

<template>
  <div>
    <button @click="isShow=!isShow">点我显示隐藏</button>
    <Point v-if="isShow"></Point>

  </div>


</template>

<script>

import {ref, reactive} from 'vue'
import Point from "./components/Point.vue";
import Demo from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {Demo, Point},
  setup() {
    const isShow = ref(true)
    return {isShow}
  },
}
</script>

3.8.2 使用hook实现打点

uesPoint.js

import {onBeforeUnmount, onMounted, reactive} from "vue";

export default function () {
    let point = reactive({
        x: 0,
        y: 0
    })
    function getPoint(event) {
        console.log(event.pageX)
        console.log(event.pageY)
        point.x = event.pageX
        point.y = event.pageY
    }
    // 挂在完成开始执行
    onMounted(() => {
        window.addEventListener('click', getPoint)
    })
    // 接除挂载时执行
    onBeforeUnmount(() => {
        console.log('sss')
        window.removeEventListener('click', getPoint)
    })
    return point
}

Point.vue

<template>
  <h2>点击的x坐标:{{ point.x }},y坐标:{{ point.y }}</h2>

</template>

<script>

import usePoint from '../hooks/usePoint.js'
export default {
  name: 'Point',
  setup() {
    let point = usePoint()
    console.log(point)
    return {point}
  },
}
</script>

10.toRef

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。

  • 语法:const name = toRef(person,'name')

  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。

  • 扩展:toRefstoRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)

<template>
  <div>
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ age }}</h2>
    <button @click="age++">改年龄</button>| <button @click="name+='~'">改姓名</button>

  </div>


</template>

<script>

import {ref, reactive,toRefs} from 'vue'

export default {
  name: 'App',
  setup() {
    const person = reactive({
      name: 'lqz',
      age: 19
    })
    return {
      ...toRefs(person)
    }
  },
}
</script>

//对象展开语法

let obj1 = {foo: 'bar', x: 42};
let obj2 = {foo: 'baz', y: 13};
let mergedObj = {...obj1, ...obj2};
console.log(mergedObj)
posted @ 2022-08-08 15:37  silencio。  阅读(79)  评论(0编辑  收藏  举报