Vue2基础
1. MVVM模型
MVVM模型
1. M:模型(Model) :data中的数据
2. V:视图(View) :模板代码
3. VM:视图模型(ViewModel):Vue实例
观察发现:
1.data中所有的属性,最后都出现在了vm身上。
2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
MVVM是vue实现数据驱动视图的和双向数据绑定的核心原理
Model表示渲染数据所依赖的数据源
View表示当前页面所渲染的DOM结构
ViewModel表示vue的实例,是核心他把上面两者绑定在一起
2. vue中的数据代理
- vue数据代理的原理
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
- Object.defineProperty()实现数据代理
<body>
<!-- 数组的响应 -->
<input type="text" id="ipt">
<p id='op'></p>
<!-- 对象的响应 -->
<input type="text" id="iptj">
<p id='opj'></p>
<script>
function Observer(obj) {
const keys = Object.keys(obj)
keys.forEach((key) => {
Object.defineProperty(this,key,{
get() {
console.log('get方法被调用了');
return obj[key]
},
set(val) {
console.log('set方法调用了')
obj[key] = val
}
})
})
}
const obs = new Observer([1,2,3])
const ipt = document.querySelector('#ipt')
ipt.value = obs[1]
document.querySelector('#op').innerHTML = obs[1]
ipt.addEventListener('input',function (e) {
obs[1] = e.target.value
document.querySelector('#op').innerHTML = obs[1]
})
const obj = new Observer({a:1,b:2})
const iptj = document.querySelector('#iptj')
iptj.value = obj.a
document.querySelector('#opj').innerHTML = obj.a
iptj.addEventListener('input',function (e) {
obj.a = e.target.value
document.querySelector('#opj').innerHTML = obj.a
})
</script>
</body>
(1)vue2中数据代理数组
- 代理的数组无法触发响应
<template>
<div>
数组:{{ arr }}
<br />
<button @click="changeArr">改变数组button>
div>
template>
<script>
export default {
name: "School",
data() {
return {
arr: [],
};
},
methods: {
changeArr() {
this.arr[1] = 1;
},
},
};
script>
- 解决办法this.$set
<template>
<div>
数组:{{ arr }}
<br />
<button @click="changeArr">改变数组button>
div>
template>
<script>
export default {
name: "School",
data() {
return {
arr: [],
};
},
methods: {
changeArr() {
this.$set(this.arr, 0, 1);
},
},
};
script>
(2)vue2中数据代理对象
- 代理对象的问题(对象无初始值,vue3常用ref({}))
<template>
<div>
对象:{{ obj }}
<br />
<button @click="changeObj">改变对象button>
div>
template>
<script>
export default {
name: "School",
data() {
return {
obj: {
A: "",
},
};
},
methods: {
changeObj() {
this.obj.B = 2;
},
},
};
script>
- 代理对象的问题(对象有初始值)
<template>
<div>
对象:{{ obj }}
<br />
<button @click="changeObj">改变对象button>
div>
template>
<script>
export default {
name: "School",
data() {
return {
obj: {
A: "",
},
};
},
methods: {
changeObj() {
this.obj.A = 1;
},
},
};
script>
- 使用this.$set
<template>
<div>
对象:{{ obj }}
<br />
<button @click="changeObj">改变对象button>
div>
template>
<script>
export default {
name: "School",
data() {
return {
obj: {},
};
},
methods: {
changeObj() {
this.obj.A = 1;
this.obj.B = 2;
this.obj.C = 3;
this.$set(this.obj, "D", 4);
},
},
};
script>
3. vue中常用的事件处理
- 常用的事件修饰符
<h1>Hello {{name}}h1>
<a href="https://www.baidu.com" @click.prevent="showInfo">点我提示a>
<div class="demo1" @click="showInfo"><button @click.stop="showInfo">点我提示button>div>
<button @click.once="showInfo">点我提示button>
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
div>
div>
<div class="demo1" @click.self="showInfo"><button @click="showInfo">点我提示button>div>
<ul class="list" @wheel.passive="demo">
<li>1li>
<li>2li>
<li>3li>
<li>4li>
ul>
div>
- 常用的按键修饰符
1.Vue中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta(win键)
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
(3).系统修饰键可以组合ctrl.y
4. vue2中选项式属性
(1)计算属性
- 非响应式
computed:{
fullName(){
return this.firstName+'-'+this.lastName;
}
}
- 响应式
computed:{
fullName:{
get(){
console.log("get被调用了");
return this.firstName+'-'+this.lastName;
},
set(value){
console.log("set被调用了");
const arr=value.split('-');
this.firstName=arr[0];
this.lastName=arr[1];
}
}
}
(2)监听属性
- 监听的配置
watch:{
number:{
deep:true,
immediate: true,
handler(){
console.log('number改变了');
}
}
}
- 监听属性的属性方式
watch:{
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
(3)生命周期
- 构造
beforeCreate() {},
created() {console.log(this.n);},
- 挂载
beforeMount() {},
mounted() {console.log(this.$el instanceof HTMLElement);},
- 更新
beforeUpdate() {console.log(this.$el );},
updated() {},
- 卸载
beforeDestroy() {},
destroyed() {},
5. vue2的组合式实现
- 下载插件
yarn add unplugin-vue2-script-setup
yarn add @vue/composition-api
- 使用组合式API插件和setup语法糖配置
import VueCompositionAPI from "@vue/composition-api";
Vue.use(VueCompositionAPI);
const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default
module.exports = {
parallel: false,
configureWebpack: {
plugins: [
ScriptSetup({ }),
],
},
}
- 简单使用
<template>
<div>
数组:{{ arr }} 对象:{{ obj }}
<br />
<button @click="changeObj" ref="a">改变对象button>
<button @click="changeArr">改变数组button>
div>
template>
<script setup>
import { ref } from "@vue/composition-api";
let arr = ref([]);
let obj = ref({});
const changeArr = () => {};
const changeObj = () => {};
script>
Vue2组件
1.常用内置组件
(1)component组件
<template>
<div>
<component :is="component">component>
<button @click="changeFn">切换button>
div>
template>
<script>
import School from "./components/School.vue";
import Stu from "./components/Stu.vue";
export default {
name: "App",
components: {
School,
Stu,
},
data() {
return {
component: "School",
};
},
methods: {
changeFn() {
this.component === "Stu"
? (this.component = "School")
: (this.component = "Stu");
},
},
};
script>
(2)transition组件
- 介绍
v-enter:定义进入过渡的开始状态(开始状态和离开过度的结束状态一致)
v-enter-active:定义进入过渡生效时的状态(写进入的动画)
v-enter-to:2.1.8 版及以上定义进入过渡的结束状态。
v-leave:定义离开过渡的开始状态。
v-leave-active:定义离开过渡生效时的状态(写离开的动画)
v-leave-to:2.1.8 版及以上定义离开过渡的结束状态(开始状态和离开过度的结束状态一致)
- 基本使用
<template>
<div>
<transition name="fade">
<School v-if="flag" />
transition>
<button @click="flag = !flag">togglebutton>
div>
template>
<script>
import { School, Stu } from "./components";
export default {
name: "",
props: {},
data() {
return {
flag: true,
};
},
components: { School, Stu },
};
script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
style>
- 结合第三方动画库
<template>
<div>
<transition
enter-active-class="animate__animated animate__bounceInDown"
leave-active-class="animate__animated animate__bounceOutDown"
>
<School v-if="flag" />
transition>
<button @click="flag = !flag">togglebutton>
div>
template>
<script>
import { School, Stu } from "./components";
import "animate.css";
export default {
name: "",
props: {},
data() {
return {
flag: true,
};
},
components: { School, Stu },
methods: {},
watch: {},
computed: {},
};
script>
- transition的构子函数
<template>
<div>
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<School v-if="flag" />
transition>
<button @click="flag = !flag">togglebutton>
div>
template>
<script>
import { School, Stu } from "./components";
import "animate.css";
export default {
name: "",
props: {},
data() {
return {
flag: true,
};
},
components: { School, Stu },
methods: {
beforeEnter() {},
enter() {},
afterEnter() {},
enterCancelled() {},
beforeLeave() {},
leave() {},
afterLeave() {},
leaveCancelled() {},
},
watch: {},
computed: {},
};
script>
- 使用gsap实现过度
<template>
<div>
<transition
@before-enter="beforeEnter"
@enter="enter"
@before-leave="beforeLeave"
@leave="leave"
>
<div class="box" v-if="flag">div>
transition>
<button @click="flag = !flag">togglebutton>
div>
template>
<script>
import { School, Stu } from "./components";
import { gsap } from "gsap";
export default {
name: "",
props: {},
data() {
return {
flag: true,
};
},
components: { School, Stu },
methods: {
beforeEnter(el) {
gsap.set(el, {
duration: 3,
width: 0,
height: 0,
});
console.log(1);
},
enter(el, done) {
gsap.to(el, {
width: 200,
height: 200,
onComplete: done,
});
console.log(1);
},
beforeLeave(el, done) {
gsap.set(el, {
width: 200,
height: 200,
});
console.log(1);
},
leave(el, done) {
gsap.to(el, {
width: 0,
height: 0,
onComplete: done,
});
console.log(1);
},
},
watch: {},
computed: {},
};
script>
<style>
.box {
background-color: red;
}
style>
(3)transition-group组件
- 平移属性move-class
<template>
<div>
<button @click="random">打乱</button>
<transition-group
tag="div"
class="wraps"
move-class="move"
>
<div
v-for="item in arr"
:key="item.id"
class="item"
>
{{ item.number }}
</div>
</transition-group>
</div>
</template>
<script>
import { School, Stu } from './components'
import _ from 'lodash'
export default {
name: '',
props: {},
data() {
return {
arr: []
}
},
components: { School, Stu },
mounted() {
this.arr = Array.apply(null, { length: 81 }).map((_, index) => {
return {
id: index,
number: (index % 9) + 1
}
})
},
methods: {
random() {
this.arr = _.shuffle(this.arr)
}
},
watch: {},
computed: {}
}
</script>
<style lang="less" scoped>
.wraps {
display: flex;
flex-wrap: wrap;
width: calc(25px * 10 + 9px);
.item {
width: 25px;
height: 25px;
border: 1px solid #ccc;
text-align: center;
}
}
.move {
transition: all 1s;
}
</style>
- 状态过度(数字过度颜色过度)
<template>
<div>
<h1>{{num.tweenedNumber.toFixed(0)}}</h1>
<input
type="text"
v-model="num.current"
>
</div>
</template>
<script>
import { School, Stu } from './components'
import _ from 'lodash'
import gsap from 'gsap'
export default {
name: '',
props: {},
data() {
return {
num: {
current: 0,
tweenedNumber: 0
}
}
},
components: { School, Stu },
methods: {},
watch: {
num: {
deep: true,
handler(newVal) {
gsap.to(this.num, {
duration: 1,
tweenedNumber: newVal.current
})
}
}
},
computed: {}
}
</script>
(4)keep-alive组件
- props
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例。
- 基本使用
<template>
<div>
<keep-alive :include="['A']">
<A v-if="flag"></A>
</keep-alive>
<button @click="flag=!flag">显示/隐藏</button>
</div>
</template>
<script>
import { A } from './components'
export default {
props: {},
data() {
return {
flag: true
}
},
components: { A }
}
</script>
- 生命周期变化
activated() {
console.log('组件被激活了');
},
deactivated() {
console.log('组件被缓存了');
},
2.组件间通讯
1.props父传子
- 父组件传递值
<template>
<div>
<A title="向子组件传值"></A>
</div>
</template>
<script>
import {A,B} from './components'
export default {
components:{A,B},
};
</script>
<style lang="less"></style>
- 子组件接受值
<template>
<div>A组件</div>
</template>
<script>
export default {
props: {
title: {
type: String
}
},
mounted() {
console.log(this.title)
}
}
</script>
2.emit子传父
- 父组件给子组件绑定事件
<template>
<div>
<A @getChild="getChild">A>
div>
template>
<script>
import { A, B } from './components'
export default {
components: { A, B },
methods: {
getChild(value) {
console.log(value)
}
}
}
script>
- 子组件触发父组件的事件
<template>
<div @click="$emit('getChild',100)">A组件div>
template>
(1)props和emit实现简单的双向据绑定
- 父组件
<template>
<div>
父组件{{value}}
<br>
<A :value="value" @setValue="setValue"></A>
</div>
</template>
<script>
import { A, B } from './components'
export default {
components: { A, B },
data() {
return {
value: 1
}
},
methods: {
setValue(value){
this.value=value
}
}
}
</script>
- 子组件
<template>
<div>
子组件:
<input
type="text"
v-model="modelValue"
>
</div>
</template>
<script>
export default {
props: {
value: {
type: Number
}
},
computed: {
modelValue: {
get() {
return this.value
},
set(value) {
this.$emit('setValue', +value)
}
}
}
}
</script>
(2)mixin混入拆分双向数据绑定
- 混入属性
export const mixinModel = {
computed: {
modelValue: {
get() {
return this.value
},
set(value) {
this.$emit('setValue', +value)
}
}
}
}
- 子组件引入混入
<template>
<div>
子组件:
<input
type="text"
v-model="modelValue"
>
</div>
</template>
<script>
import mixinModel from '../mixin'
export default {
props: {
value: {
type: Number
}
},
mixin: [mixinModel]
}
</script>
3.插槽通信
(1)默认插槽
- 父组件
<template>
<div>
<A><h1>默认插槽</h1></A>
</div>
</template>
<script>
import {A} from './components'
export default {
components:{A},
};
</script>
- 子组件
<template>
<div>
<!-- 默认插槽占位 -->
<slot></slot>
</div>
</template>
(2)具名插槽
- 父组件
<template>
<div>
<A>
<template #name>
<h1 >具名插槽</h1>
</template>
</A>
</div>
</template>
<script>
import { A } from './components'
export default {
components: { A }
}
</script>
- 子组件
<template>
<div>
<!-- 具名插槽占位 -->
<slot name="name"></slot>
</div>
</template>
(3)作用域插槽(scope传值重点)
- 理解:数据在子组件的自身,但根据数据生成的结构需要父组件决定。
- 子组件
<template>
<div>
<!-- 作用域插槽占位,slot传的值最后会被#name="scoped" scoped会接受list-->
<slot name="name" :list="list"></slot>
</div>
</template>
<script>
export default {
data() {
return {
list: ['作', '用', '域', '插', '槽']
}
},
}
</script>
- 父组件
<template>
<div>
<!-- 作用域插槽占位,slot传的值最后会被#name="scoped" scoped会接受list-->
<slot name="name" :list="list"></slot>
</div>
</template>
<script>
export default {
data() {
return {
list: ['作', '用', '域', '插', '槽']
}
},
}
</script>
4.全局事件总线
- 挂载$bus
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render(creatElement) {
return creatElement(App)
},
beforeCreate() {
Vue.prototype.$bus=this
}
}).$mount('#app')
- A组件
<template>
<div>
A组件
<button @click="sendB">发生信息给B</button>
</div>
</template>
<script>
export default {
methods: {
sendB() {
this.$bus.$emit('getB', 'A组件发生信息给B组件')
}
}
}
</script>
- B组件
<template>
<div></div>
</template>
<script>
export default {
mounted() {
this.$bus.$on('getB', value => {
console.log(value)
})
},
beforeDestroy() {
this.$bus.$off('getB')
}
}
</script>
(1)实现简单的发布和订阅模式
- 发布订阅的实现
class Bus {
list
constructor() {
this.list = {}
}
on(name, callback) {
const fn = this.list[name] || []
fn.push(callback)
this.list[name] = fn
}
emit(name, ...args) {
const evnentName = this.list[name]
evnentName.forEach(fn => {
fn.apply(this, args)
})
}
off(name) {
delete this.list[name]
}
}
export default new Bus()
- A组件
<template>
<div>
A组件
<button @click="sendB">发生信息给B</button>
</div>
</template>
<script>
import bus from '../utils'
export default {
methods: {
sendB() {
bus.emit('getB', 'A组件发生信息给B组件')
}
}
}
</script>
- B组件
<template>
<div></div>
</template>
<script>
import bus from '../utils'
export default {
mounted() {
bus.on('getB', value => {
console.log(value)
})
},
beforeDestroy() {
bus.off('getB')
}
}
</script>
5.ref调用
- 子组件
<template>
<div>
A组件
</div>
</template>
<script>
export default {
data() {
return {
childVal: '父组件可以通过ref调用它'
}
},
methods: {
childrenFn() {
console.log('父组件可以通过ref调用它')
}
}
}
</script>
- 父组件
<template>
<div>
<A ref="Ac"></A>
</div>
</template>
<script>
import { A } from './components'
export default {
props: {},
components: { A },
mounted() {
console.log(this.$refs.Ac.childVal)
console.log(this.$refs.Ac.childrenFn)
}
}
</script>
自定义的配置
1. 自定义指令
(1)局部自定义指令
- 基本使用
<template>
<div>
<input
type="text"
v-focus
>
</div>
</template>
<script>
import {} from './components'
export default {
directives: {
focus: {
bind(el, binding, vnode) {
console.log('指令第一次绑定到元素时')
},
inserted(el, binding, vnode) {
el.focus()
console.log('被绑定元素插入父节点时调用')
},
update(el, binding, vnode, oldVnode) {
console.log('所在组件的 VNode 更新时调用')
},
componentUpdated(el, binding, vnode) {
console.log('指令所在组件的 VNode 及其子 VNode 全部更新后调用')
},
unbind(el, binding, vnode) {
console.log('只调用一次,指令与元素解绑时调用')
}
}
}
}
</script>
- 自定义托拽指令的实现
<template>
<div>
<div
style="position: absolute;
width: 100px;
height: 100px;
background-color: red;"
v-move
></div>
</div>
</template>
<script>
import {} from './components'
export default {
data() {
return {
value: 1
}
},
directives: {
move: {
inserted: (el, binding, vnode) => {
const move = e => {
console.log(parseInt(el.style.width))
el.style.left = e.clientX - parseInt(el.style.width)/2 + 'px'
el.style.top = e.clientY - parseInt(el.style.height)/2 + 'px'
}
el.addEventListener('mousedown', () => {
document.addEventListener('mousemove', move)
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', move)
})
})
}
}
}
}
</script>
(2)全局自定义指令
- 入口文件中定义
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
Vue.directive('focus', {
inserted(el) {
el.focus()
}
})
export const vm = new Vue({
render(creatElement) {
return creatElement(App);
},
beforeCreate() {
Vue.prototype.$A = '全局变量'
Vue.prototype.$func = () => {
console.log('全局函数');
}
}
}).$mount("#app");
- 组件中使用
<template>
<div>
<input
type="text"
v-focus='value'
>
</div>
</template>
2. 全局变量
- 定义全局变量
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
export const vm = new Vue({
render(creatElement) {
return creatElement(App);
},
beforeCreate() {
Vue.prototype.$A = '全局变量'
Vue.prototype.$func = () => {
console.log('全局函数');
}
}
}).$mount("#app");
- 使用全局变量
<script>
import {} from './components'
export default {
mounted() {
console.log(this.$A)
console.log(this.$func)
}
}
</script>
3. 自定义插件
(1)简单的使用
- 定义组件
<template>
<div v-if="flag">
我是自定的插件组件
</div>
</template>
<script>
export default {
data() {
return {
flag: false
}
},
methods: {
show() {
this.flag = true
},
hide() {
this.flag = false
}
}
}
</script>
- 挂载插件到Vue上(样式挂载到body上,方法挂载到全局)
import C from './components/C.vue'
import Vue from 'vue'
const contructor = Vue.extend(C)
const instance = new contructor()
const div = document.createElement('div')
instance.$mount(document.body.appendChild(div))
export default {
install(Vue) {
Vue.prototype.$C = {
hide: instance.hide,
show: instance.show,
}
},
}
- 入口文件中引入插件
import plugin from './plugin'
Vue.use(plugin)
(2)封装Message提示插件
- 定义组件
<template>
<div
class="xtx-message"
:style="style[type]"
v-if="flag"
>
<!-- 上面绑定的是样式 -->
<!-- 不同提示图标会变 -->
<i
class="iconfont"
:class="[style[type].icon]"
></i>
<span class="text">{{ text }}</span>
</div>
</template>
<script>
export default {
props: {
text: {
type: String,
default: ''
},
type: {
type: String,
default: 'warn'
}
},
data() {
return {
style: {
warn: {
icon: 'icon-warning',
color: '#E6A23C',
backgroundColor: 'rgb(253, 246, 236)',
borderColor: 'rgb(250, 236, 216)'
},
error: {
icon: 'icon-shanchu',
color: '#F56C6C',
backgroundColor: 'rgb(254, 240, 240)',
borderColor: 'rgb(253, 226, 226)'
},
success: {
icon: 'icon-queren2',
color: '#67C23A',
backgroundColor: 'rgb(240, 249, 235)',
borderColor: 'rgb(225, 243, 216)'
}
},
flag: false
}
},
methods: {
show() {
this.flag = true
},
hide() {
this.flag = false
}
}
}
</script>
<style scoped lang="less">
.xtx-message {
width: 300px;
height: 50px;
position: fixed;
z-index: 9999;
left: 50%;
margin-left: -150px;
top: 25px;
line-height: 50px;
padding: 0 25px;
border: 1px solid #e4e4e4;
background: #f5f5f5;
color: #999;
border-radius: 4px;
i {
margin-right: 4px;
vertical-align: middle;
}
.text {
vertical-align: middle;
}
}
</style>
- 挂载插件到Vue上(样式挂载到body上,方法挂载到全局)
import Message from './components/Message.vue'
import Vue from 'vue'
const MessageFn = (message) => {
const contructor = Vue.extend(Message)
const instance = new contructor({propsData:message})
console.log(instance)
instance.$mount(document.createElement('div'))
document.body.appendChild(instance.$el)
const show = instance.show
const hide = instance.hide
show()
setTimeout(() => {
hide()
}, 3000)
}
export default {
install(Vue) {
Vue.prototype.$Message = MessageFn
},
}
- 入口文件中引入插件
import plugin from './plugin'
Vue.use(plugin)
- 使用插件
<script>
import {} from './components'
export default {
mounted() {
console.log(this.$Message({ type: 'success', text: '注册组件成功' }))
}
}
</script>
移动端适配
1.第一种适配方案
- 安装依赖
yarn add amfe-flexible postcss [email protected]
- main.ts引入amfe-flexible
import "amfe-flexible"
- 根目录下创建postcss.config.js文件并配置
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 37.5,
propList: ['*']
}
}
}
2.第二种适配方案
- 安装依赖
yarn add postcss-px-to-viewport -D
- vite.config.ts内置
postcss.config.js
中修改配置
import { fileURLToPath, URL } from 'node:url'
import pxtoViewPort from 'postcss-px-to-viewport'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
css: {
postcss: {
plugins: [
pxtoViewPort({
unitToConvert: 'px',
viewportWidth: 750,
unitPrecision: 6,
propList: ['*'],
viewportUnit: 'vw',
fontViewportUnit: 'vw',
selectorBlackList: ['ignore-'],
minPixelValue: 1,
mediaQuery: true,
replace: true,
landscape: false
})
]
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
- 创建postcss-px-to-viewport.d.ts的声明文件
declare module 'postcss-px-to-viewport' {
type Options = {
unitToConvert: 'px' | 'rem' | 'cm' | 'em'
viewportWidth: number
viewportHeight: number
unitPrecision: number
viewportUnit: string
fontViewportUnit: string
selectorBlackList: string[]
propList: string[]
minPixelValue: number
mediaQuery: boolean
replace: boolean
landscape: boolean
landscapeUnit: string
landscapeWidth: number
}
export default function (options: Partial<Options>): any
}
- 在tsconfig.json中引入声明文件
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "postcss-px-to-viewport.d.ts"],
"compilerOptions": {
"baseUrl": ".",
"types": ["element-plus/global"],
"paths": {
"@/*": ["./src/*"]
}
},
"references": [
{
"path": "./tsconfig.config.json"
}
]
}
- 注意:如果外面用到了
postcss.config.js
,在postcss.config.js
中添加配置文件
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'postcss-px-to-viewport': {
unitToConvert: 'px',
viewportWidth: 320
}
}
}