use( )
Vue.use( )(vue2.x 是 Vue.use,vue3.x 是 app.use)在 vue 中使用很多,比如 ElementUI,vantUI,VueI18n,Router,Vuex 等部分第三方库、组件、自定义方法等,在引用后会使用 Vue.use 将他们传入作为参数使用,将他们注册到 Vue。
vue2.x 使用 Vue.use( )
import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store';
// 引入国际化插件
import VueI18n from 'vue-i18n';
// 引入统一配置
import common from './lib/common';
// 引入animate.css
import animated from 'animate.css';
Vue.use(animated);
Vue.use(VueI18n); // 通过插件的形式挂载
Vue.use(common); // 全局配置项
/* eslint-disable no-new */
// 把 vue实例挂载在 window.vm,方便使用 vue的实例
window.vm = new Vue({
el: '#app',
router,
store,
components: {
App,
},
template: '<App/>',
});
vue3.x 使用 app.use( )
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import vant from 'vant';
import 'vant/lib/index.css';
import globalFunction from './utils/globalFunction'; // 引入全局方法
const app = createApp(App);
app.use(vant);
app.use(store);
app.use(router);
// 挂载全局方法
for (const key in globalFunction) {
app.provide(key, globalFunction[key]);
console.log(key);
}
app.mount('#app');
window.vm = app;
注册全局组件/方法
在 vue2 中
vue2.x 利用 Vue.prototype 注册全局方法/变量/组件。
有些时候我们需要在 vue 项目的任意位置都能调用公共方法/变量,那么 vue 如何注册全局的方法/变量/组件呢?请看以下示例:
1.创建 common.js(编写插件),插件内利用 Vue.prototype 全局挂载方法
import { Toast } from 'vant';
import echarts from 'echarts';
const install = (Vue) => {
// toast提示, 通过 this.Toast调用
Vue.prototype.Toast = Toast;
// echarts, 通过 this.$echarts调用
Vue.prototype.$echarts = echarts;
// 获取 url 参数
Vue.prototype.$getUrlParam = (name, href) => {
const _search = href ? href.split('?')[1] : window.location.search.substr(1);
if (_search) {
const _reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
const _regNext = _search.match(_reg);
if (_regNext) {
return decodeURI(_regNext[2]) || '';
}
return '';
}
return '';
};
};
2.main.js(通过Vue.use注册刚刚编写的自定义插件)
import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store';
// 引入自定义插件
import common from './lib/common';
Vue.use(common); // 全局配置项
/* eslint-disable no-new */
// 把 vue实例挂载在 window.vm,方便使用 vue的实例
window.vm = new Vue({
el: '#app',
router,
store,
components: {
App,
},
template: '<App/>',
});
3.在任意组件中使用刚刚通过 Vue.prototype 全局注册的方法,比如使用 $getUrlParam 方法,直接通过 this.$getUrlParam 即可调用。
<template>
<div class="page">
Vue.prototype全局注册的方法测试
</div>
</template>
<script>
export default {
name: 'Test',
data() {},
created() {
console.log(this.$getUrlParam('idCard'))
},
};
</script>
4.在任意js文件中使用刚刚通过Vue.prototype全局注册的方法,比如使用 $getUrlParam 方法。因为我们已经在 main.js 中将 vue 实例挂载在 window.vm,所以在 js 文件中,直接通过 window.vm.$getUrlParam 即可调用。
console.log(window.vm.$getUrlParam('idCard'))
当然,如果不想通过编写组件然后Vue.use的方式引入全局方法,也可以直接在main.js通过Vue.prototype注册全局方法/变量/组件。
// mian.js:
import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store';
// 通过Vue.prototype注册全局方法/变量/组件
// toast提示,通过this.Toast调用
Vue.prototype.Toast = Toast;
// echarts,通过this.$echarts调用
Vue.prototype.$echarts = echarts;
// 获取url参数
Vue.prototype.$getUrlParam = (name, href) => {
const _search = href ? href.split('?')[1] : window.location.search.substr(1);
if (_search) {
const _reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
const _regNext = _search.match(_reg);
if (_regNext) {
return decodeURI(_regNext[2]) || '';
}
return '';
}
return '';
};
// 把 vue实例挂载在 window.vm,方便使用 vue的实例
window.vm = new Vue({
el: '#app',
router,
store,
components: {
App,
},
template: '<App/>',
});
需要注意的是,只有vue2.x支持Vue.prototype注册全局方法/变量/组件,vue3.x不再支持Vue.prototype。
在 vue3 中
vue2.x 利用 Vue.prototype 注册全局组件/方法,然而 vue3.x 是无法通过Vue.prototype 来注册全局组件/方法的,因此需要另辟蹊径。
vue3.x注册全局组件/方法主要有以下两个方式:
1.provide / inject (推荐)
main.js中:通过 provide 将组件或者方法、变量挂载在全局。
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
const app = createApp(App);
app.use(store);
app.use(router);
// 挂载全局方法
const globalFunc = () => {
console.log('要挂载在全局的方法');
};
// 将 globalFunc方法挂载在全局
app.provide('globalFunc', globalFunc);
app.mount('#app');
// 把 vue实例挂载在 window.vm,方便使用 vue的实例
window.vm = app;
组件中:通过 inject 获取全局方法并调用
<template>
<div>组件中通过inject获取全局方法并调用</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, inject } from 'vue';
export default defineComponent({
name: 'JobDetail',
// 注册组件
components: {
Header,
},
setup() {
onMounted(() => {
testFunc();
});
const testFunc = (): void => {
// 通过 inject 获取挂载在全局的 globalFunc方法
const globalFunc: any = inject('globalFunc');
globalFunc();
};
return {
testFunc,
};
},
});
</script>
路由
在 vue3 中
hash路由:
import { createRouter, createWebHashHistory } from 'vue-router';
const routes = [
{
path: '/',
component: () => import('@/views'),
},
{
path: '/test1',
name: 'test1',
component: () => import('@/views/test1'),
},
{
path: '/test2',
name: 'test2',
component: () => import('@/views/test2'),
},
];
export const router = createRouter({
history: createWebHashHistory(),
routes: routes,
});
// 访问页面:
http://192.168.1.3:8080/#/test1
history路由:
import { createRouter, createWebHistory} from 'vue-router';
const routes = [
{
path: '/',
component: () => import('@/views'),
},
{
path: '/test1',
name: 'test1',
component: () => import('@/views/test1'),
},
{
path: '/test2',
name: 'test2',
component: () => import('@/views/test2'),
},
];
export const router = createRouter({
history: createWebHistory(),
routes: routes,
});
// 访问页面:
http://192.168.1.3:8080/test1
Class 与 Style 绑定
对象语法
<template>
<div
class="static"
v-bind:class="{ active: isActive }"></div> // <div class="static active"></div>
</template>
<script>
export default {
data() {
return {
isActive: true
}
}
}
</script>
<template>
<div v-bind:class="classObject"></div> // <div class="active"></div>
</template>
<script>
export default {
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
}
</script>
数组语法
<template>
<div
:class="[ isActive ? activeClass : '', errorClass]"></div> // <div class="active text-danger"></div>
</template>
<script>
export default {
data: {
isActive: true,
activeClass: 'active',
errorClass: 'text-danger'
}
}
</script>
<template>
<div
:class="[{ safe: isSafe }, activeClass, errorClass]"></div> // <div class="safe active text-danger"></div>
</template>
<script>
export default {
data: {
isSafe: true,
activeClass: 'active',
errorClass: 'text-danger'
}
}
</script>
内联样式
<template>
<div
:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> // <div style="color:red;font-size:30px"></div>
</template>
<script>
export default {
data: {
activeColor: 'red',
fontSize: 30
}
}
</script>
<template>
<div
:style="styleObject"></div> // <div style="color:red;font-size:30px"></div>
</template>
<script>
export default {
data: {
styleObject: {
color: 'red',
fontSize: 30
}
}
}
</script>
jsx
① 插槽;
// 父组件:
render() {
{/* 具名插槽 */}
<myComponent>
<header slot="header">header</header>
<header slot="content">content</header>
<footer slot="footer">footer</footer>
</myComponent>
}
// 子组件:
render() {
return (
<div>
{/* 纯文本 */}
<p>我是自定义组件</p>
{this.$slots.header}
{this.$slots.content}
{this.$slots.footer}
</div>
);
}
// 父组件:
render() {
{/* 具名插槽 作用域插槽 */}
<myComponent {
...{
scopedSlots: {
test: ({user}) => (
<div>{user.name}</div>
)
}
}
}>
</myComponent>
}
// 子组件:
render() {
return (
<div>
{this.$scopedSlots.test({
user: this.user
})}
</div>
);
}
// content.js
export default {
name: 'content',
functional: true,
props: {
render: Function,
column: {
type: Object,
default: null
},
params: {
type: Object,
default: () => {}
}
},
render: (h, ctx) => {
const params = ctx.props.params
return ctx.props.render && ctx.props.render(h, params)
}
}
<el-tabs
v-model="activeName">
<el-tab-pane
v-for="item in tabList"
:key="item.code"
:label="item.label">
<span v-if="item.render">
<content
:params="item.params"
:render="item.render"></content>
</span>
</el-tab-pane>
</el-tabs>
<script>
import Content from './content'
components: {
Content
},
</script>
② v-model;
// 在vue中
<el-input
v-model="searchParams.searchVal">
</el-input>
// 对应jsx语法
function _input (value) {
this.searchParams.searchVal = value
},
render(h, params) => {
// 这里也可以从params传入参数
return (
<el-input
value={this.searchParams.searchVal}
onInput={_input}>
</el-input>
)
}
③ v-html;
render(h, params) {
return (
<div> <div domPropsInnerHTML={htmlStr}></div> </div>
)
}
④ v-if;
// 在vue中
<el-button v-if="show"></el-button>
// 对应jsx语法
render(h, params) {
return (
this.show && <el-button></el-button>
)
}
拓展
如何在vue中使用jsx语法
Vue插槽用法,在JSX中的用法
在vue中使用jsx语法
Vue 中使用 JSX
Vue+JSX 使用例子
Vue2 + JSX使用总结
vue2.x 使用JSX 开发
vue2.0如何实现jsx的组件功能
vue中的渲染函数/jsx和插槽slot
使用Vue 2和JSX绑定自定义事件
在vue中如何使用jsx,并使用v-model等自定义指令
为什么 JSX 语法这么香?
自定义指令
// directives.js:
Vue.directive("changePageSize", {
bind(el, binding) {
setPageSizeData(binding);
},
update(vnode, binding) {
// window.onresize = function () {
// setPageSizeData(binding);
// };
},
});
const setPageSizeData = (binding) => {
console.log(binding);
let page = 0;
if (window.innerHeight < 800) {
page = 10;
} else if (window.innerHeight >= 800 && window.innerHeight < 1200) {
page = 20;
} else {
page = 30;
}
binding.value.setPageSize(page);
};
// main.js:
import "./utils/directives";
// demo.vue:
<template>
<el-pagination
v-changePageSize="{ setPageSize: handleSizeChange }"
@size-change="handleSizeChange"
background
@current-change="handleCurrentChange"
:current-page="this.pageData.currentPage"
:page-sizes="[15, 30, 45, 60, 85, 100]"
:page-size="this.pageData.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="this.pageData.total"
></el-pagination>
</template>
<script>
export default {
data(){
return {
pageData: {
pageSize: 10,
}
}
},
methods: {
handleSizeChange(val) {
this.pageData.pageSize = val;
}
}
}
</script>
拓展
vue中如何自定义指令
vue2 自定义指令中怎么改变data中的值
vue 改变 data 值,自定义指令
v-model 指令
<input v-model="text" />
上例不过是一个语法糖,展开来是:
<input :value="text" @Input="e => text = e.target.value" />
.sync 修饰符
.sync 的作用:实现父子组件数据之间的双向绑定, 与 v-model 类似;
区别:在 vue2 中一个组件上只能有一个 v-model,而 .sync 修饰符可以有很多个。
原理
v-model 的原理
<com1 v-model="num"></com1>
// 等价于
<com1 :value="num" @input="(val)=>this.num=val"></com1>
.sync修饰符的原理
// 正常父传子:
<com1 :a="num" :b="num2"></com1>
// 加上sync之后父传子:
<com1 :a.sync="num" .b.sync="num2"></com1>
// 它等价于
<com1
:a="num" @update:a="val=>num=val"
:b="num2" @update:b="val=>num2=val"></com1>
// 相当于多了一个事件监听,事件名是update:a,回调函数中,会把接收到的值赋值给属性绑定的数据项中。
小结
.sync 与 v-model 区别是
① 相同点: 都是语法糖, 都是可以实现父子组件中的数据的双向通信;
② 区别点:
格式不同:
v-model="num" , :num.sync="num"
v-model : @input + value
:num.sync: @update:num
v-model 只能用一次; .sync 可以有多个
拓展
修饰符.sync (Vue2)
vue2 自定义组件—v-model—.sync修饰符—具名插槽—作用域插槽—混入
Vue中.sync的用法
vue2中v-model .sync 和 vue3中v-model的使用
vue2.0中.sync与v-model
Vue 双向绑定语法糖 .sync 和 v-model
插件开发与使用
Vue的插件就是能够给 vue 添加新功能新特性的东西。
插件通常会为 Vue 添加全局功能。
插件能实现的功能没有限制,不过常见以下几种:
① 通过插件添加全局方法或属性;
② 通过插件添加全局资源:指令、过滤器、过渡等;
③ 通过插件(使用全局 mixin 方法),添加一些组件选项;
④ 通过插件,添加 vue 实例方法,通过把它们添加到 Vue.prototype 上实现;
⑤ 一个库,提供自己的 api,同时提供上面提到的一个或多个功能,如 vue-router。
开发插件
所谓 vue 插件其实就是一个简单的 js 对象而已,然后这个插件对象有一个公开属性 install ,他的值为一个函数,此函数接受两个参数。第一个参数是 Vue 构造器 , 第二个参数是一个可选的选项对象。
最后导出此插件对象以供别人使用。
// src/utils/myPlugin.js:
const myPlug = {
install: (Vue, opts={}) => {
// 插件要实现的功能;
}
}
export default myPlug;
// src/main.js:
import Vue from 'vue';
import App from './App';
import myPlug from './utils/myPlugin.js';
Vue.use(myPlug); // 使用插件 myPlug;
插件案例
// src/utils/myPlugin.js:
import MessageBox from 'element-ui/lib/message-box';
import Loading from 'element-ui/lib/loading';
const myPlug = {
install: (Vue, opts={}) => {
Vue.prototype.$testProp = 'Hello World';
Vue.prototype.$testMethod = (msg) => {
alert('我是插件:myPlug,你的信息为:' + msg);
};
Vue.prototype.$loading = Loading.service;
Vue.prototype.$msgbox = MessageBox;
}
}
export default myPlug;
拓展
vue之插件开发及使用
Vue.js 插件开发详解
编写自定义Vue.js插件
选项式 API 与组合式 API
选项式 API
// vue2 中写法:
<template>
<div class="wrapper">
<p>{{ obj.a }}</p>
<p>{{ obj.b }}</p>
<p>{{ obj.c }}</p>
<p>{{ obj.d }}</p>
<button @click="handleA">修改数据 a</button>
<button @click="handleB">修改数据 b</button>
<button @click="handleC">添加数据 c</button>
<button @click="handleD">添加数据 d</button>
</div>
</template>
<script>
export default {
name: 'demo',
data() {
return {
obj: {
a: '原始数据 a',
b: '原始数据 b'
}
};
},
mounted() {
console.log(this);
},
methods: {
handleA() {
this.obj.a = '数据 a 改变了';
console.log(this);
},
handleB() {
this.$set(this.obj, 'b', '数据 b 改变了');
console.log(this);
},
handleC() {
this.obj.c = '数据 c 添加失败';
console.log(this);
},
handleD() {
this.$set(this.obj, 'd', '数据 d 添加成功');
console.log(this);
}
}
};
</script>
修改数据前:
修改数据 a 后:
修改数据 b 后:
添加数据 c 后:
添加数据 d 后:
组合式 API
setup
// vue3 中写法:
<template>
<div class="wrapper">首页</div>
<p>{{ obj.a }}</p>
<p>{{ obj.b }}</p>
<p>{{ obj.c }}</p>
<button @click="handleData">修改数据</button>
</template>
<script>
export default {
setup() {
let obj = {
a: 1,
b: 2,
};
const handleData = () => {
obj.c = 3;
console.log(obj);
};
return {
obj,
handleData,
};
},
};
</script>
修改数据后:
reactive 通常定义复杂类型的数据,定义后的数据具有响应式特性。
// reactive:
<template>
<div class="wrapper">首页</div>
<p>{{ obj.a }}</p>
<p>{{ obj.b }}</p>
<p>{{ obj.c }}</p>
<button @click="handleData">修改数据</button>
</template>
<script>
import { reactive } from '@vue/reactivity';
export default {
setup() {
let obj = reactive({
a: 1,
b: 2,
});
const handleData = () => {
console.log(obj);
obj.c = 3;
console.log(obj);
};
return {
obj,
handleData,
};
},
};
</script>
修改数据后:
ref 通常定义简单类型的数据,当然也可以定义复杂类型的数据,定义后的数据具有响应式特性。
// ref:
<template>
<div class="wrapper">首页</div>
<p>{{ obj }}</p>
<button @click="handleData">修改数据</button>
</template>
<script>
import { ref } from '@vue/reactivity';
export default {
setup() {
let obj = ref({
a: 1,
b: 2,
});
console.log(obj);
const handleData = () => {
obj.value.c = 3;
console.log(obj);
};
return {
obj,
handleData,
};
},
};
</script>
修改数据前:
修改数据后:
vue3 中 data(),setup(),onMounted(),mounted()的执行顺序为:setup(), data(),onMounted(), mounted()。
<template>
<div class="wrapper"></div>
</template>
<script>
import { onMounted } from '@vue/runtime-core';
export default {
data() {
console.log('data');
return {};
},
setup() {
console.log('setup');
onMounted(() => {
console.log('onMounted');
});
return {};
},
mounted() {
console.log('mounted');
},
};
</script>
// 依次打印: setup, data, onMounted, mounted
setup 语法糖写法(推荐该写法)
// setup 语法糖写法:
<template>
<div class="wrapper">
<p>{{ msg }}</p>
<p>{{ obj }}</p>
<button @click="handleData">修改数据 msg</button>
<button @click="handleData2">修改数据 obj</button>
</div>
</template>
<script setup>
const { onMounted, ref, reactive } = require('@vue/runtime-core');
let msg = ref('这是响应式数据');
let obj = reactive({ a: 1, b: 2 });
const handleData = () => {
msg.value = '修改响应式数据成功';
};
const handleData2 = () => {
obj.a = 3;
};
onMounted(() => {
console.log('onMounted');
});
</script>
toRefs 可以完成数据的解构,如果数据具有响应性,可以保持响应性的特性。
// 选项式 API 写法:
<template>
<div class="wrapper">
<p>{{ name }}</p>
<p>{{ age }}</p>
<button @click="handleData">修改数据</button>
</div>
</template>
<script>
const { reactive, toRefs } = require('@vue/reactivity');
export default {
name: 'demo',
setup() {
let obj = reactive({
name: '张三',
age: 18,
});
const handleData = () => {
obj.name = '张三修改了';
};
return {
...toRefs(obj),
handleData,
};
},
};
</script>
// 组合式 API 写法:
<template>
<div class="wrapper">
<p>{{ name }}</p>
<p>{{ age }}</p>
<button @click="handleData">修改数据</button>
</div>
</template>
<script setup>
const { reactive, toRefs } = require('@vue/reactivity');
let obj = reactive({
name: '张三',
age: 18,
});
const { name, age } = toRefs(obj);
const handleData = () => {
console.log(name);
name.value = '张三修改了';
};
</script>
setup 语法糖插件
① setup 语法糖插件:unplugin-auto-import; 解决场景:② 在组建中开发无需每次都引入 const { onMounted, ref, reactive } = require('@vue/runtime-core')。
下载安装:npm i unplugin-auto-import -D
配置:
computed 计算属性
在 vue2 中
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
- get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。 - 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 备注:
(1).计算属性最终会出现在vm上,直接读取使用即可。(data的属性是在vm的_data上)
(2).如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
在使用计算属性的时候,大部分的使用场景只读取不修改,因此可以简写。
// 接受一个 getter 函数:
data() {
return {
firstName: 'Foo',
lastName: 'Bar'
}
},
computed: {
// 简写
fullName: function() { // 该 function 是 getter 函数
return this.firstName + '-' + this.lastName;
}
}
// 接受一个具有 getter 和 setter 函数的对象:
data() {
return {
firstName: 'Foo',
lastName: 'Bar'
}
},
computed: {
// 完整写法
fullName: {
// getter
get() {
return this.firstName + '-' + this.lastName;
}
// setter
set(newVal) {
const arr = newVal.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
// 带参数的计算属性
<template>
<p>{{ getSum(20) }}</p>
</template>
<script>
expoert default {
data() {
return {
x: 10,
}
},
computed: {
getSum() {
return (y) => {
return this.x + y;
}
}
}
}
</script>
// 计算属性的第一个参数指向该 vue实例
<template>
<p>{{ getX }}</p>
</template>
<script>
expoert default {
data() {
return {
x: 10,
}
},
computed: {
getX(app) { // app 指向当前上下文的 this(即当前组件实例)
return app.x;
}
}
}
</script>
在 vue3 中
computed() 用来创建计算属性,computed() 函数的返回值是一个 ref 的实例。使用 computed 之前需要按需导入:import { computed } from 'vue'。
用法:① 接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象(即该 ref 对象只读不可写);② 接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象。
// 接受一个 getter 函数:
const count = ref(1) // 创建一个 ref 响应式数据
const plusOne = computed(
// getter
() => count.value + 1 // 参数为 getter 函数,我们只能读取值,不能修改值
) // 不可变的 ref 对象
console.log(plusOne.value) // 2 (触发 getter 函数)
plusOne.value++ // 错误
// 接受一个具有 getter 和 setter 函数的对象:
const count = ref(1)
const plusOne = computed({
// getter
get: () => count.value + 1,
// setter
set: val => {
count.value = val - 1
}
}) // 可变的 ref 对象
plusOne.value = 1 // 触发 setter 函数
console.log(count.value) // 0 // 触发 getter 函数
// 写法一:
<template>
<div class="wrapper">
<p>{{ changeName }}</p>
</div>
</template>
<script setup>
const { reactive } = require('@vue/reactivity');
const { computed } = require('@vue/runtime-core');
let obj = reactive({
name: '张三',
});
const changeName = computed(() => {
return obj.name.slice(0, 1);
}); // 不可变的 ref 对象
</script>
// 写法二(高级写法):
<template>
<div class="wrapper">
<p>{{ changeName }}</p>
</div>
</template>
<script setup>
const { reactive } = require('@vue/reactivity');
const { computed } = require('@vue/runtime-core');
let obj = reactive({
name: '张三',
});
const changeName = computed({
get() {
return obj.name.slice(0, 1);
},
set() {},
});
</script>
// 写法三(不常见):
<template>
<div class="wrapper">
<p>{{ obj.str }}</p>
</div>
</template>
<script setup>
const { reactive } = require('@vue/reactivity');
const { computed } = require('@vue/runtime-core');
let obj = reactive({
name: '张三',
str: computed(() => {
return obj.name.slice(0, 1);
}),
});
</script>
watch 监听
vue 中 watch 用来监听数据的响应式变化,获取数据变化前后的值。
在 vue2 中
监听属性:
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视!!(在data里或者computed里)
3.深度监视:
(1).Vue中的watch默认不监测对象内部值的改变(一层)。
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以!
(2).使用 watch 时根据数据的具体结构,决定是否采用深度监视。
通常需要对基本数据、复杂数据进行监听,故 watch 有四种情况:① 普通的 watch;② 数组的 watch;③ 对象的 watch;④ 对象中某个属性的 watch。
// 普通的 watch:
data: {
meter: 1000,
kilameter: 1
},
watch: {
meter: function(newVal, oldVal) {
this.kilameter = newVal * 0.1;
},
kilameter: function(newVal, oldVal) {
this.meter = newVal *1000;
}
}
// 数组的 watch:
data: {
arr: [1,2,3]
},
watch: {
arr: function(newVal, oldVal) {
console.log(newVal);
console.log(oldVal);
}
}
// 对象的 watch:
data: {
obj: {
a:111,
b:222
}
},
watch: {
obj: {
handler: function(newVal, oldVal) {
console.log(oldVal);
},
immediate: true, // 初始化时让 handler调用一下
deep: true
}
}
// 对象中某个属性的 watch:
data: {
obj: {
a: 111,
b: 222
}
},
watch: {
'obj.a': {
handler: function(newVal, oldVal) {
console.log(oldVal);
}
}
}
在 vue3 中
vue3的监听跟vue2有点不一样,使用 watch 之前需先按需导入:import { watch } from "vue";,然后直接在setup里面使用,调用方式是以回调函数的方式呈现。
语法:
① watch(监听的数据,副作用函数,配置对象);
② watch(data, (newData, oldData) => {}, {immediate: true, deep: true});
③ immediate:true,初次加载 watch 侦听器立即被执行;deep:true,深度监听。
// 选项式写法:
<template>
<div class="wrapper">
<input type="text" v-model="msg" />
</div>
</template>
<script>
const { ref } = require('@vue/reactivity');
const { watch } = require('@vue/runtime-core');
export default {
setup() {
let msg = ref('这是一个数据');
watch(
msg,
(newVal, oldVal) => {
console.log(newVal, oldVal);
},
{
immediate: true, // 初始化监听
}
);
return {
msg,
};
},
};
</script>
// 初始化监听:
<template>
<div class="wrapper">
<input type="text" v-model="msg" />
</div>
</template>
<script setup>
const { ref } = require('@vue/reactivity');
const { watch } = require('@vue/runtime-core');
let msg = ref('这是一个数据');
watch(
msg,
(newVal, oldVal) => {
console.log(newVal, oldVal);
},
{
immediate: true, // 初始化监听
}
);
</script>
// 监听多个数据:
<template>
<div class="wrapper">
<input type="text" v-model="msg" />
<input type="text" v-model="str" />
</div>
</template>
<script setup>
const { ref } = require('@vue/reactivity');
const { watch } = require('@vue/runtime-core');
let msg = ref('这是一个数据');
let str = ref('这是一个字符串');
watch(
[msg, str], // 同时监听多个
(newVal, oldVal) => {
console.log(newVal, oldVal);
},
{
immediate: true,
}
);
</script>
// 监听对象
<template>
<div class="wrapper">
<input type="text" v-model="obj.a" />
<input type="text" v-model="obj.arr" />
</div>
</template>
<script setup>
const { reactive } = require('@vue/reactivity');
const { watch } = require('@vue/runtime-core');
let obj = reactive({
a: 1,
arr: ['a', 'b', 'c'],
});
watch(obj, (newVal, oldVal) => {
console.log(newVal, oldVal);
});
</script>
// 监听对象中某个对象
<template>
<div class="wrapper">
<input type="text" v-model="obj.a" />
<input type="text" v-model="obj.arr" />
</div>
</template>
<script setup>
const { reactive } = require('@vue/reactivity');
const { watch } = require('@vue/runtime-core');
let obj = reactive({
a: 1,
arr: ['a', 'b', 'c'],
});
watch(
() => obj.arr, // 监听对象中某个对象(此处不能写成 obj.arr,否则无效)
(newVal, oldVal) => {
console.log(newVal, oldVal);
}
);
</script>
// watchEffect 立即执行监听函数:
<template>
<div class="wrapper">
<input type="text" v-model="name" />
<input type="text" v-model="obj.a" />
<input type="text" v-model="obj.arr" />
</div>
</template>
<script setup>
const { ref, reactive } = require('@vue/reactivity');
const { watchEffect } = require('@vue/runtime-core');
let name = ref('张三');
let obj = reactive({
a: 1,
arr: ['a', 'b', 'c'],
});
watchEffect(() => {
console.log(name.value); // name 的值变化就会监听到
console.log(obj.a); // obj.a 的值变化就会监听到
console.log(obj.arr); // obj.arr 的值变化就会监听到
});
</script>
插槽
插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签,父组件填充的内容称为插槽内容。
- 子组件不提供插槽时,父组件填充失效
- 父组件无填充时,
<slot></slot>中的备用内容会启用生效 - 父级模板里的所有内容都是在父级作用域中编译的,子模板里的所有内容都是在子作用域中编译的,互不影响。
匿名插槽
又名默认插槽
// parent.vue:
<template>
<Child>提交按钮</Child>
</template>
<script setup>
import Child from './child.vue';
</script>
// child.vue:
<template>
<button>
<slot>SUBMIT BTN</slot>
</button>
</template>
// parent 组件渲染为:
<button>提交按钮</button>
具名插槽
当有多个插槽时,插槽增加了name属性来正确渲染对应的部分,父组件需要使用<template></template>。可以认为匿名插槽是特殊的具名插槽。
// parent.vue:
<template>
<Child>
匿名插槽的内容
<!-- <template v-slot:default>匿名插槽的内容</template> -->
<!-- <p>匿名插槽的内容</p> -->
<!-- 上面三种方式效果一样 -->
<!-- 填充内容顺序无关 -->
<template #footer>自定义 footer</template><!-- #footer 是 v-slot:footer 的简写 -->
<template v-slot:header>自定义 header</template>
</Child>
</template>
<script setup>
import Child from './child.vue';
</script>
// child.vue:
<template>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot><!-- 其实就是: <slot name="default"></slot> -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</template>
// parent 组件渲染为:
<header>自定义 header</header>
<main>"匿名插槽的内容"</main>
<footer>自定义 footer</footer>
作用域插槽
让父级插槽内容能够访问子组件数据,数据从子组件往父组件流动。子组件通过插槽prop(任意个数)来绑定数据,父组件通过带值(命名随意)的v-slot来获取子组件的数据。
// parent.vue:
<template>
<Child>
<template v-slot:default="slotProps">
<p>{{ slotProps }}</p>
</template>
</Child>
</template>
// 写法二: 只有默认插槽时, v-slot可以写在组件上
//<template>
// <Child v-slot:default="slotProps">
// <p>{{ slotProps }}</p>
// </Child>
//</template>
// 写法三: v-slot有解构的用法
//<template>
// <C v-slot:default="{ data, index }">
// <p>{{ { data, index } }}</p>
// </C>
//</template>
<script setup>
import Child from './child.vue';
</script>
// child.vue:
<template>
<ul>
<li v-for="(item, index) in items" :key="index">
<!-- data 即插槽 prop, 绑定 item, index 也是插槽 prop, 绑定 index,
备用内容为 item.thing1 -->
<slot :data="item" :index="index">{{ item.thing1 }}</slot>
</li>
</ul>
</template>
<script setup>
const items = [
{ thing1: '阅读', thing2: '看电视' },
{ thing1: '背单词', thing2: '玩游戏' },
];
</script>
// parent 组件渲染为:
<ul>
<li>
<p>{ "data": { "thing1": "阅读", "thing2": "看电视" }, "index": 0 }</p>
</li>
<li>
<p>{ "data": { "thing1": "背单词", "thing2": "玩游戏" }, "index": 1 }</p>
</li>
</ul>
动态数据写入插槽
// parent.vue:
<template>
<div class="wrapper">
{{ f }}
<Child>
<template #[f]>
<p>我来自 parent</p>
</template>
</Child>
</div>
</template>
<script setup>
import { ref } from '@vue/reactivity';
import Child from './child.vue';
const f = ref('footer');
</script>
// child.vue:
<template>
<div class="header">
<slot name="header">这是 header</slot>
</div>
<div class="footer">
<slot name="footer">这是 footer</slot>
</div>
</template>
<script setup></script>
// parent 组件渲染为:
<div class="wrapper">
"footer"
<div class="header">这是 header</div>
<div class="footer">我来自 parent</div>
</div>
拓展
el-tooltip内容用插槽展示(slot#content用法)
Teleport 传送
组件间数据进行传递,写法有:class 类、id 、标签。
<teleport to=".main"></teleport>
<teleport to="#cont"></teleport>
<teleport to="body"></teleport>
注意:teleport 必须传递到有这个 dom 的元素中,所以 dom 元素必须在 teleport 的前面,否则无效。
// index.vue:
<template>
<div class="wrapper">
<div class="accept"></div>
</div>
<div id="cont"></div>
<Child></Child>
</template>
<script setup>
import Child from './child-view.vue';
</script>
// child-view.vue:
<template>
<div class="child_wrapper">
<p>子组件</p>
<teleport to=".accept">
<p>这是传送</p>
</teleport>
<teleport to="#cont">
<p>这是传送的 cont</p>
</teleport>
</div>
</template>
// index.vue:
<template>
<div class="wrapper">
<div class="accept"></div>
</div>
<teleport to=".accept">
<p>这是传送</p>
</teleport>
</template>
<script setup></script>
动态组件
语法:<component :is="动态去切换的组件"></component>
<template>
<div class="wrapper">
<ul>
<li v-for="(item, index) in tabList" :key="index" @click="changeTab(index)">{{ item.name }}</li>
</ul>
<keep-alive>
<component :is="currentComp.comp"></component>
</keep-alive>
</div>
</template>
<script setup>
import { markRaw, reactive } from '@vue/reactivity';
import A from './a-view.vue';
import B from './b-view.vue';
let tabList = reactive([
{ name: 'tabA', comp: markRaw(A) }, // markRaw() 包裹组件,组件就不会响应,从而提高性能
{ name: 'tabB', comp: markRaw(B) },
]);
let currentComp = reactive({
comp: tabList[0].comp,
});
const changeTab = (index) => {
currentComp.comp = tabList[index].comp;
};
</script>
异步组件
异步组件主要进行性能提升。
使用场景:① 组件按需引入:当满足条件后再去加载该组件;② 打包分包处理:npm run build 打包完成后,异步组件有单独的 js 文件,是从主体 js 分包出来的。
<template>
<div class="wrapper">
<C v-if="loadBool"></C>
<button @click="handleLoad">加载组件</button>
</div>
</template>
<script setup>
const { defineAsyncComponent, ref } = require('@vue/runtime-core');
const C = defineAsyncComponent(() => import('./c-view.vue'));
let loadBool = ref(false);
const handleLoad = () => {
loadBool.value = true;
};
</script>
组件递归(自调用)
递归:无限套娃,当找到某个娃娃时,就不套了。
// tree.vue:
<template>
<ul>
<li v-for="(item, index) in data" :key="index">
{{ item.name }}
<!-- 在遍历时递归调用组件自身,当然,要有children,有数据的时候才去递归调用自身(递归需要有一个结束条件) -->
<template v-if="item.children">
<tree :data="item.children"></tree>
<!-- 因为组件调用渲染,需要有数据,而这个tree.vue组件是的数据是通过props接收的,
所以需要把子内容数据再传递给子组件,子组件用props接收,就能够一层一层的渲染了 -->
</template>
</li>
</ul>
</template>
<script>
export default {
name: "tree",
props: {
data: {
type: Array,
default() {
return [];
},
},
},
};
</script>
拓展:
vue组件的递归自调用~代码思路分析
vue 组件自调用
Vue组件自我调用,实现递归展示
vue 菜单递归
vue之菜单的递归渲染思想
vue实现点击页面其他地方,隐藏div元素
mounted(){
document.addEventListener('click', (e) => {
let that = this;
if(!this.$el.contains(e.target)){
that.videoShow = false; // 点击其他区域关闭;
} else {
that.videoShow = true;
}
})
}
/**
* 关闭搜索下拉列表;
*/
handleCloseSearchPage() {
document.addEventListener('click', (e) => {
const searchPageBox = this.$refs.searchPageBox;
if (!searchPageBox.contains(e.target)) {
this.showSearchPage = false;
this.keyVal = null;
}
});
},