Vite 简单介绍
- Vite在开发环境下不需要打包,开发模式下vite使用浏览器原生支持的ES Module的方式加载模块
- Vue-Cli 在开发模式下必须对项目打包才可以运行 vite开启的web服务器会开启劫持vue的请求,会把.vue文件解析成js文件,并把响应头的content-type设置成为 application/javascript,告诉浏览器现在发送的是一个js脚本。 服务器会对浏览器不能识别的文件进行特殊处理,如果是单文件组件, 会调用complierSFC编译单文件组件并把编译的结果返回给浏览器
Composition API
了解: createApp : 创建vue对象 setup: compositionApi的入口 reactive: 创建响应式对象的
setup是在props被解析完毕,但是组件被创建之前执行的, 所以setup内部无法通过this获取组件实例, 及data, computed, methods 等。
setup(props, context){
// 第一个参数 props, 是个响应式对象,不能被解构
// 第二个参数 context对,象 包含attrs、emit、slots
}
setup中也可以使用生命周期的钩子函数
- onRenderTrigger 首次调用render不会触发
- onRenderTracked 首次调用render触发
reactive/ref/toRefs
都是用来创建响应式数据的。 具体结合🌰来看:
reactive的使用
function useMousePosition() {
const position = reactive({ //把一个对象转换成响应式对象,并且该对象的嵌套属性也会转换成响应式对象,返回的是一个proxy对象
x: 0,
y: 0
})
const updated = (e) => {
position.x = e.pageX;
position.y = e.pageY;
}
onMounted(() => {
window.addEventListener('mousemove', updated)
});
onUnmounted(() => {
window.remmoveEventListener('mousemove', updated)
});
return toRefs(position)
}
🌰
如果我们想在模版里直接使用x,y来获取鼠标位置,修改代码如下
// 尝试将position解构
setup(props) {
const {x, y} = useMousePosition();
return {
x,
y
}
},
发现x, y 不再是响应式了。
🤔: 在解构前position是个proxy对象,在访问position中的属性时,会调用代理对象中的getter,拦截收集依赖。当属性变化时会调用代理对象中的setter进行拦截触发更新。
那么我们看看解构后发生了什么。
先来看下babel对解构的降级处理:
可以看到在降级之后,其实就是用两个变量来接收了position.x, position.y。 而我们知道,基本类型的赋值是把值在内存中复制一份。而这里的x,y也仅仅只是一个基本类型的变量,与代理对象无关。所以当重新给x,y赋值时,也无法调用代理对象的setter, 无法触发更新的操作。所以是不能对当前的响应式对象进行结构的。
这时,需要用到一个新的Api:toRefs。
toRefs的使用
toRefs 可以把一个响应式对象中的所有属性也转换成响应式。 所以可以利用toRefs来进行结构的操作
function useMousePosition() {
...
return toRefs(position)
}
ref的使用
主要可以把一个基本类型的数据转换成响应式对象。
(ref如果接收一个对象,内部就调用了reactive 返回一个代理对象)
如果接收一个基本数据类型,内部会创建一个只有value属性的对象(模版中使用该对象时可以省略value),该对象的value属性具有getter,setter。 在getter中收集依赖,setter中触发更新。
结合🌰来看
<div id="app">
<button @click='increment'>点击</button>
<span>{{count}}</span>
</div>
function useIncrement(){
const count = ref(0);
return {
count, //此时的count是一个对象
increment:() => {
count.value ++
}
}
}
const app = createApp({
setup(props) {
return {
...useIncrement()
}
}
})
computed用法
🌰
...
<span>{{activeCount}}</span>
<button @click="push">增加</button>
...
const data = [
{ text: "read", completed: false, },
{ text: "excise", completed: true, },
{ text: "sleep", completed: false, },
]
setup(props) {
const todos = reactive(data)
const activeCount = computed(() => {
return todos.filter(it => !it.completed).length
});
return {
activeCount,
push() {
todos.push({
text: "eee",
completed: false
})
}
}
}
watch 用法
Watch 的三个参数
- 第一个参数:要监听的数据
- 第二个参数:监听到数据变化后执行的函数,这个函数有两个参数分别是新值和旧值
- 第三个参数:选项对象,deep 和 immediate
Watch 的返回值
- 取消监听的函数 🌰:
<div id="app">
请输入你的问题:
<input type="text" v-model='question'>
<p>
{{answer}}
</p>
</div>
...
const apiUrl = 'https://www.yesno.wtf/api'
const app = createApp({
setup(props) {
const question = ref('');
const answer = ref('');
watch(question, async(newVl, oldVl) => {
const response = await fetch(apiUrl);
const data = await response.json();
answer.value = data.answer
});
return {
question,
answer
}
}
})
watchEffect
接收一个函数作为参数,监听函数内响应式数据的变化。初始就会执行一次,当监听的数据变化时会再次触发。 并返回一个函数,取消对数据的监听
<div id="app">
{{count}}
<button @click='increment'>count增加</button>
<p>
取消监听: <button @click='stop'>stop</button>
<!-- 点击后控制台不再打印 -->
</p>
</div>
...
const app = createApp({
setup(props) {
const count = ref(0);
const stop = watchEffect(() => {
console.log(count.value);
})
return {
count,
stop,
increment(){
count.value ++
}
}
}
})